比特币源码分析之三:交易脚本

比特币源码分析之三:交易脚本

本篇默认读者了解两个基本的概念

1、hash算法,比特币使用的是sha-256,如果不了解,google一下

2、非对称加密算法,比特币使用的是椭圆曲线加密算法后文用ecc代替

这两个概念不需要了解详细的数学实现,只是了解大致的工作原理即可,相信码农应该都有这个基本功

地址

如果使用过比特币,可能会有一个类似下面格式的一个地址

1QAc9S5EmycqjzzWDc1yiWzr9jJLC8sLiY

别人想给你转账,你必须提供一个这样的地址,那么这个地址是什么呢

如果想看这个地址是怎么生成的可以尝试自己调试一把

1、gdb bitcoind

set arg -regtest(注意这里不需要-daemon,因为如果加上这个参数会新fork一个进程不受gdb管控)

b getnewaddress

r

2、新启动一个shell输入bitcoin-cli -regtest getnewaddress

这时候断点到了getnewaddress函数,堆栈如下

3、继续细跟下去就是具体的生成地址流程

关键过程(函数)如下

1、CKey::MakeNewKey(key.cpp),这里的CKey代表的是一个ecc算法的私钥

这个函数很简单,调用GetStrongRandBytes生成了一个大小为32字节随机数(check函数就是验证随机数是否符合私钥的格式)fCompressedIn表示是否压缩,这个涉及到ecc的公钥地址的一个梗,这个先不用关注

2、CKey::GetPubKey(key.cpp) 生成公钥,返回的是CPubKey(代表ecc公钥的类)

这个函数也比较简单,就是利用生成的私钥(32字节的keydata)生成了对应的公钥,公钥长度是65个字节大小(不压缩的情况,fCompressedIn为false)

总结下:

使用随机数生成一个私钥,再用私钥生成一个公钥,这里好奇心比较强的读者可能会疑问,私钥是一个随机数?对应的公钥不怕和别人重复了?这个完全不用担心,首先这个随机数参考了机器上的硬件信息,其次据说在大小在32字节的随机数生成出来的数字,如果和别人重复了,你就相当于一年365天,天天中彩票了。

生成公钥以后会把这个公钥经过加工变成大家看到的一个字符串地址,大概会经过以下步骤

1、先对公钥做一次hash160(内部实现是一个先对公钥sha256然后ripemd160)构造了一个CKeyID对象

这个keyid比较重要后文中介绍脚本的时候还要介绍

2、在keyid数据前加了一个字节的前缀,这个前缀根据网络不同,前缀不同

a)Mainnet是0x00

b)Tesstnet是0x6f

c)Regnet也是0x6f

3、对ver+keyid做两次sha256,并且取hash后结果的前四字节作为checksum(校验)

4、最后对ver+keyid+ checksum整体做一次base58(base64算法的修改版本,去掉了一些生僻字符)

3、4两步的代码如下(base58.cpp)

交易脚本

讲交易那一篇中有提到,交易脚本其实有两部分组成

1、输入脚本 (锁)

2、输出脚本(钥匙)

对这个概念不清楚的可以参看《比特币源码分析交易》

为了便于理解,笔者先从一个最为简单的脚本出发来讲解

输入脚本:<sig> <pubkey>

输出脚本:OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

首先先解释以上脚本指令

OP_DUP 复制指令,将虚拟机栈顶的数据复制一份放到栈顶

OP_HASH160 hash160指令,和计算keyid的hash160是一个算法

OP_EQUALVERIFY 比对指令,比对栈顶两个数据是否相等,如果不等执行失败

OP_CHECKSIG 签名验证指令,取栈顶的两个元素 第一个是pubkey 第二个是签名,该指令比较复杂,简单来说就是通过公钥和签名对交易数据做验证,后续会做详细介绍。

另外除了指令外还有几个<>内的表示一些关键数据

Sig,对交易数据,通过私钥计算出来的签名,签名验证,就是使用公钥对交易数据做运算,然后和签名校对,如果一致表明验证通过

Pubkey,上文中提到的由私钥生成出来的65字节大小的数据(压缩的是33字节)

PubKeyHash,对应上文中的keyid,是通过对pubkey做hash160计算出来的结果

虚拟机的通过栈执行,有数据结构基础的可能会理解,栈是一个先入先出的数据结构。虚拟机执行时会先执行输入脚本,再执行输出脚本,如果出错就验证失败。

1、先入栈sig

2、再入栈pubkey

3、复制栈顶的pubkey

4、做hash160运算

5、入栈PubKeyHash

6、对比栈顶的两个数据是否相等,不相等验证失败

7、取栈顶的pubkey和sig,验证签名,签名验证失败,脚本验证就失败

这里需要结合上一篇一起看,虽然输入脚本是先执行,但是按照出现顺序是先有输出脚本,也就是出钱的交易先出来(锁先出来),然后花钱的交易(也就是钥匙)才出来。这里很容易迷糊

虚拟机执行主逻辑在interpreter.cpp的EvalScript函数中,而调用是通过VerifyScript

下一篇会介绍下签名验证的细节

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

3 条评论
登录 后参与评论

相关文章

来自专栏c#开发者

Ext.net RecordField SubmitEmptyValue invalid 无效问题有解决方案吗

<ext:Store              ID="Store1"              runat="server"              Aut...

3355
来自专栏信安之路

记一次详细的勒索病毒分析

第一次写病毒分析的文章,之前表哥丢给我一个样本断断续续分析了好几天才搞明白,如有任何错误,还请各位多加指点

640
来自专栏极客编程

以太坊智能合约部署与交互

我们再打开一个终端,打开cluster1的peer02的控制台,直接at到上一个终端部署的智能合约地址并进行set操作

812
来自专栏MasiMaro 的技术博文

windows 驱动开发入门——驱动中的数据结构

最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书——《独钓寒江 windows安全编程》 和...

632
来自专栏林冠宏的技术文章

以太坊: ETH 发送交易 sendRawTransaction 方法数据的签名 和 验证过程

它们都将会被 from 所对应的密钥 进行签名而得出三个量:V,R,S。同时,各个入参依然以原来的可见的形式进入序列化步骤。

962
来自专栏汇智网教程

web3.eth.estimateGas

2175
来自专栏NetCore

Asp.Net Core SignalR 用泛型Hub优雅的调用前端方法及传参

最近一直在使用Asp.Net Core SignalR(下面成SignalR Core)为小程序提供websocket支持,前端时间也发了一个学习笔记,在使用过...

1054
来自专栏乐沙弥的世界

Oracle 表缓存(caching table)的使用

在通常的情况下,应用程序访问在cache中的数据块将按照LRU算法来进行处理。然而对于小表的访问,当使用全表扫描时,则该表

864
来自专栏chafezhou

程序员探案之被吃掉的串口数据

1204
来自专栏SAP最佳业务实践

想学FM系列(18)-SAP FM模块:派生规则推导策略(1)-派生规则推导步骤-初始化

4 派生规则推导策略 派生规则推导,是SAP提供由数据源推导到目标数据的一种工具,它提供了一系列面向用户开放使用的方法来使数据源经过逻辑推理后生成了有效目标数据...

3747

扫码关注云+社区