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

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

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

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 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术博客

win8 数据加密和解密

在win8中有时候需要对数据进行加密和解密的话,就可以用Windows.Security.Cryptography.DataProtection命名空间下的Da...

902
来自专栏有趣的django

37.Django1.11.6文档

第一步 入门 检查版本 python -m django --version 创建第一个项目 django-admin startproject mysite ...

4048
来自专栏偏前端工程师的驿站

理解cookie的path和domain属性

  今天在做验证码时发现一个问题:A、B窗口都打开同一个页面,A先生成一个验证码,B再生成验证码,这时A所生成的验证码被B覆盖掉了。原因是使用了同名的cooki...

1759
来自专栏ChaMd5安全团队

第二届红帽杯4Re + 1Pwn的writeup

通过加密函数的分组、及具体算法里的swap,mul,add我操作,看出是idea算法。

1062
来自专栏iOS开发笔记

cordova插件-Device Orientation

添加插件 $ cordova plugin addcordova-plugin-device-orientation ? 图 13如上则插入成功 插件的使用...

2816
来自专栏三流程序员的挣扎

RxJava Flowable Processor

同一个线程生产一个就消费了,不会产生问题,在异步线程中,如果生产者的速度大于消费者的速度,就会产生 Backpressure 问题。比如子线程的被观察者 1 秒...

2222
来自专栏Grace development

冷门PHP函数汇总

整理一些日常生活中基本用不到的PHP函数,也可以说在框架内基本都内置了,无需我们去自行使用的函数。量不多。后续在日常开发中如遇到更多的冷门,会更新本文章

671
来自专栏红色石头的机器学习之路

Jupyter notebook入门教程(上)

本文将分上下两部分简单介绍Jupyter notebook的入门教程,英文原文出处: Getting started with the Jupyter note...

3510
来自专栏XAI

微信企业号回调模式配置讲解 Java Servlet+Struts2版本 echostr校验失败解决

异常java.security.InvalidKeyException:illegal Key Size 也就是 echostr校验失败,请您检查是否正确解密并...

21510
来自专栏游戏杂谈

fabrication的拦截器Interceptors简介

Interceptors(拦截器),主要目的是为了改变PureMVC的消息通知在到达Commands和Mediators的正常执行顺序。 在拦截器里可以:

752

扫码关注云+社区