区块链课程作业
- 操作系统:Windows 10 WSL2 + Ubuntu 20.04
- Python版本:3.9.6
$ git clone https://github.com/Zebartin/hello-bitcoin.git
配置好Python虚拟环境后,运行以下命令安装脚本:
$ pip install --editable .
安装完成后运行脚本:
$ hello-bitcoin --help
Usage: hello-bitcoin [OPTIONS]
Options:
-a, --account INTEGER 生成账户的数量 [default: 100]
-t, --transaction INTEGER 生成交易的数量 [default: 1000]
-b, --block INTEGER 生成区块的数量 [default: 10]
-o, --output PATH 结果输出路径 [default: /home/yourname/hello-bitcoin]
--help Show this message and exit.
运行以下命令(可以不带命令行参数),生成2个账户、1笔交易和1个区块,并保存在输出路径中:
$ hello-bitcoin -a 2 -t 1 -b 1 -o ../output
生成accounts.json
和blocks.json
两个文件:
// accounts.json
// 以地址作为索引
{
"15natoEM1kLXDrGDRbGV3xVP22MhJK18ML": {
"private key": "7c500f3d28d19f8849313b154b117ac8b2d9c24ee10030302fdf713018cae24d",
"public key": "030814c477c32577eb767c5f13504bcc8e8f507aee536c8e24dda50e3f3506dd49"
},
"1E11jBYo2BGBqoaGpUQqifAHsS2QcpkKqs": {
"private key": "d5e7d34525d4570a8d0543b62c0fb2a8c88ea1ca3693664446d8979f5c3332c9",
"public key": "03bfb66dcab935fff3e331adab7d553c0e626bdd2519215a550003913db422d9d4"
}
}
// blocks.json
// 以区块hash作为索引
{
"af7df63c74e8a122fa7cd344af3a6a5f5822f4afd90ef3a5e3933067b769b6cc": {
"version": 1,
"hash": "af7df63c74e8a122fa7cd344af3a6a5f5822f4afd90ef3a5e3933067b769b6cc",
"previous block hash": "0000000000000000000000000000000000000000000000000000000000000000",
"merkle root": "167e07b8f25d3cfa585f9b685d35b1cf1b101021261bc34bf476ec5b20c4297b",
"target": 0,
"timestamp": 0,
"nonce": 0,
"tx": [
{
"hash": "167e07b8f25d3cfa585f9b685d35b1cf1b101021261bc34bf476ec5b20c4297b",
"version": 1,
"vin": [
{
"txid": "b7359e0ae5611b3034f87081fbe32f77558072d9f85a9a6853cae08675496caa",
"vout": 832961847,
"scriptSig": "483045022100a22cd7ae7b501d33f556c763c811bb71dcc85d145aa227d198e74e429c42e7940220185cad004fd6ff7d568e24599eb4ef43e309ee93d3876e9d0f648d8f1aa39303012103bfb66dcab935fff3e331adab7d553c0e626bdd2519215a550003913db422d9d4",
"sequence": "ffffffff"
}
],
"vout": [
{
"value": "4.17215855",
"scriptPubKey": "76a914347f838fb2ea6dc3cc562f14eafa465b990641f988ac"
}
],
"locktime": 0
}
]
}
}
在检查时,可以将blocks.json
中的区块信息序列化后进行hash,与文件中的hash值进行比对。
包含MerkleNode
和MerkleTree
两个类,调用MerkleTree.make_merkle_tree(data)
即可生成一棵merkle树。merkle树构建算法参照了Mastering Bitcoin 2nd Edition第205页的C++实现。
包含Account
一个类,表示比特币账户,其中包含了账户的公钥、私钥和地址。Account
类提供了两种生成方式,一种是随机生成from_random_key
,另一种是从给定私钥生成from_private_key
,后者目前只在测试中有使用。使用Account
能对消息进行签名sign
和验证verify
。
这里使用了开源库python-ecdsa来生成密钥对,并进行签名和验证。另外还使用了开源库base58来生成账户地址。由于base58库提供的base58check编码中没有添加version前缀,需自行在公钥的哈希前添加0x00
。
包含三个类:TxIn
、TxOut
和Transaction
。每个类都提供相应的序列化和反序列化方法,这部分参照了Mastering Bitcoin 2nd Edition相关内容,以及比特币官方项目测试代码中的实现。另外为了便于阅读,还提供了字典转换的方法。
另外,Transaction
类提供了generate
方法,用于随机生成一笔交易,其中input中的签名和output中的公钥哈希都由参数指定:
- 在随机生成input时,会将随机字符串哈希后作为伪txid,并根据参数提供的账户生成签名和脚本;
- 在随机生成output时,会由参数提供的账户地址decode得到公钥的哈希,并组合成P2PKH脚本。
此外,文件中提供了make_P2PKH_scriptPubKey
方法来生成公钥对应的P2PKH脚本,和make_scriptSig
方法来将签名和公钥拼接成脚本。
包含BlockHeader
和Block
两个类。其中的序列化和反序列化方法参考了Bitcoin Developer Reference和比特币官方项目测试代码中的实现。与Transaction
一样,也提供了字典转换的方法。
Block
和Transaction
的反序列化方法事实上在作业中并不会用到,只会在测试时会用到。另外,Block
提供的is_valid
方法也只会在测试中用到,用来检查反序列化后的区块merkle根是否与真实区块一致。
包含了一些实用方法,具体见代码注释。
其中ser_compact_size
和deser_compact_size
的实现直接参照了比特币官方项目测试代码中的实现,用于按照CompactSize Unsigned Integer编码格式序列化和反序列化。
包含了一个cli
方法,按照实验作业的要求,根据输入参数,生成若干个账户和若干个交易,用生成的账户对这些交易进行签名,再生成若干个区块将这些交易打包。最后把生成的结果输出到文件中。
用于配置Python Click
模块。
hello-bitcon/test/
包含了本次作业的测试代码。测试只覆盖了一部分,不包括随机生成交易等等。
test_merkletree.py
:用随机字符串生成merkle树并验证父子节点之间的关系;test_accout.py
:根据Mastering Bitcoin 2nd Edition中提供的数据,测试密钥对、地址生成算法的正确性,并测试了签名和验证的流程;test_transaction.py
:根据Mastering Bitcoin 2nd Edition中提供的数据,测试交易的序列化和反序列化。因为无从知晓签名时的私钥,没有测试生成签名脚本的算法的正确性;test_block.py
:利用Blockchain Data API获取真实区块信息,随机取高度在[0, 200000]内的区块来验证区块反序列化算法的正确性。因为API提供的交易信息不完全,只提供了交易在其数据库中的tx_index而非txid,无法测试区块的序列化算法。
想要进行测试,需要安装pytest
模块:pip install pytest
。安装完成后在代码目录下运行pytest
即可。
- 字节、16进制字符串、整数之间的相互转换有点混乱,花了很多时间理清它们。
- 教材Mastering Bitcoin 2nd Edition中对脚本的编码格式不够清晰,有一些字节的含义没有讲清楚,需要额外查阅资料。
- 比特币的字节表示方式有点混乱,交易hash得到的txid值使用大端序的16进制表示,而交易序列化时,输入部分的txid后则又反过来,造成了不小的困扰。