比特币族系的交易构造
比特币及相关币种使用的学习笔记(二)
比特币交易
交易
完成一次比特币交易分三步,首先我们要构造一个交易,这涉及到给交易签名;然后将它广播到比特币网络; 最终我们要等待某个矿工将它打包。它才算成为了被确认的交易,变成了区块链记录的一部分,接下来如果 一切顺利,一个个后继区块基于它被计算出来后,它也就越来越可靠的成为可信记录的一部分。一般来说, 我们在主网络(Main Chain)上采用的信任策略不会像 regtest 环境这么保守,6 次确认(即有6个以 上区块的后继链被挖出),我们就可以相信它是非常可靠的了。
为了构造交易,我们先从用户地址开始。
# 为了省力气,先定一个 cli 封装
def cli(*args):
cmd = ["bitcoin-cli", "-regtest", "-rpcport=8432", "-rpcuser=bitcoin", "-rpcpassword=bitcoin"]
cmd += list(args)
process = command(*cmd)
process.wait()
return process.stdout.read().strip()
addr = cli("getnewaddress")
print(addr)
……
简单交易
现在我们向构造出来的这个新地址发送一笔钱。通过最简操作对比特币交易有个初步了解。
txid = cli("sendtoaddress", addr, "10.00")
print(txid)
这步操作得到了一个交易 hash 。 这个 sendtoaddress 操作封装了很多细节。它从 UTXO 中选择 足够多的金额,生成交易,其中扣除交易费和 output 后,还设置了找零。
tx = cli("getrawtransaction", txid, "1")
print(tx)
{
"hex": "0200000001abc593e33f10678f302af06a12871c32a5252c115de0f3ee61505be88aaf3d270000000049483045022100f4984d39a301a3bcd394e46b7fa135be076987a6490ff430a96ede6d0968dfa80220287b93d4d0cb43fd8c977ace3d00d35e9563f5ebc9e2551dc0e4945ca4586ad901feffffff0200ca9a3b000000001976a914646731518e9bcc7b28f8a46f4e484b735d56575b88ac00196bee000000001976a914ba750b1ea99c17395c97e933d9329a407eeb985588ac81000000",
"txid": "ff9b7b3e511e4cbcaf2c264896b128211564a6ee033bed983955afbefd3b59c9",
"hash": "ff9b7b3e511e4cbcaf2c264896b128211564a6ee033bed983955afbefd3b59c9",
"size": 192,
"vsize": 192,
"version": 2,
"locktime": 129,
"vin": [
{
"txid": "273daf8ae85b5061eef3e05d112c25a5321c87126af02a308f67103fe393c5ab",
"vout": 0,
"scriptSig": {
"asm": "3045022100f4984d39a301a3bcd394e46b7fa135be076987a6490ff430a96ede6d0968dfa80220287b93d4d0cb43fd8c977ace3d00d35e9563f5ebc9e2551dc0e4945ca4586ad9[ALL]",
"hex": "483045022100f4984d39a301a3bcd394e46b7fa135be076987a6490ff430a96ede6d0968dfa80220287b93d4d0cb43fd8c977ace3d00d35e9563f5ebc9e2551dc0e4945ca4586ad901"
},
"sequence": 4294967294
}
],
"vout": [
{
"value": 10.00000000,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 646731518e9bcc7b28f8a46f4e484b735d56575b OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914646731518e9bcc7b28f8a46f4e484b735d56575b88ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"mpfqVBs8W1NbMqPk2oV16R6MLQTFuypaK5"
]
}
},
{
"value": 39.99996160,
"n": 1,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 ba750b1ea99c17395c97e933d9329a407eeb9855 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914ba750b1ea99c17395c97e933d9329a407eeb985588ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"mxWrC49MfPdu41gW83BNi7TmhQqKCX1Do5"
]
}
}
]
}
当然我们可以,也应该可以更精细的构造一个交易。
构造确定交易
下面我们利用 json 库获取 unspend 列表,然后从中拿出一条用来构造交易。
import json
result = json.loads(cli("listunspent"))
utxo = result[-6]
print(utxo)
我们还需要一个新地址:
addr0 = cli("getnewaddress")
print(addr0)
n3pzWqJHW4y1BCdPKv4VnB3Qtnq5X745wa
现在定义 vin 和 vout 列表:
vin = json.dumps([{"txid":utxo["txid"], "vout":utxo["vout"]}])
vout = json.dumps({addr0:49.9999})
print(vin)
print(vout)
[{"txid": "526b12b007aaa7ed48939488109603d9925b3b6ff8931d90c104972b521ad1e3", "vout": 0}]
{"n3pzWqJHW4y1BCdPKv4VnB3Qtnq5X745wa": 49.9999}
然后生成 rawtransaction :
rawtx = cli("createrawtransaction", vin, vout)
print(rawtx)
0200000001e3d11a522b9704c1901d93f86f3b5b92d903961088949348eda7aa07b0126b520000000000ffffffff01f0ca052a010000001976a914f4bc1ba5d157600b5338bca440903b513be1f6a288ac00000000
当然简单归简单,这个接口不能设置找零,如果只是想转 inputs 中的一小部分, 剩余部分会都变成交易费。这应该是个很大的问题。我们可以将这个交易解码,一看便知:
tx = cli("decoderawtransaction", rawtx)
print(tx)
{
"txid": "aab9113d365f3693336f5a7696042d28b799964827fa824cfd0e510d9c609fd0",
"hash": "aab9113d365f3693336f5a7696042d28b799964827fa824cfd0e510d9c609fd0",
"size": 85,
"vsize": 85,
"version": 2,
"locktime": 0,
"vin": [
{
"txid": "526b12b007aaa7ed48939488109603d9925b3b6ff8931d90c104972b521ad1e3",
"vout": 0,
"scriptSig": {
"asm": "",
"hex": ""
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 49.99990000,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 f4bc1ba5d157600b5338bca440903b513be1f6a2 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914f4bc1ba5d157600b5338bca440903b513be1f6a288ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"n3pzWqJHW4y1BCdPKv4VnB3Qtnq5X745wa"
]
}
}
]
}
后面我们再讨论更复杂和完备的交易构造,现在先接着交易构造流程讨论如何给它签名:
signed = json.loads(cli("signrawtransaction", rawtx))
print(signed)
{'hex': '0200000001e3d11a522b9704c1901d93f86f3b5b92d903961088949348eda7aa07b0126b520000000049483045022100b16f6fc8fc6003031e833375f15bf6525102c7e052be59bf1010baaa9729f1530220720bdf21fd50a4ff4751689e6ef59fcb1e32f04f4dcfa208a99765c2f5cf4fff01ffffffff01f0ca052a010000001976a914f4bc1ba5d157600b5338bca440903b513be1f6a288ac00000000', 'complete': True}
到此为止,我们终于构造了一个交易,现在我们把它广播出去:
new_txid = cli("sendrawtransaction", signed["hex"])
print(new_txid)
……
result = cli("generate", "1")
print(result)
[
"65139873e571227c9068b727fe50b6f3e8520a6dbc150bd603c28a9e3d168184"
]
片刻之后,我们得到了第一个手工打包的交易,现在用 getrawmempool 查看节点,应该会看到 mempool 已经被清空。 它们都进入了最新的区块。
构造更复杂的交易
像上一次交易一样,我们先找到两个可用的 utxo :
import json
result = json.loads(cli("listunspent"))
utxo0 = result[-6]
utxo1 = result[5]
print(utxo0)
print(utxo1)
{'txid': '26b6962e76f60847a7e6952e93e7b1bedefeb55ab81d96bd52891b5e5a0b0be0', 'vout': 0, 'address': 'mx17ravt8fiSJNHJrES2HiXxzTpfGnwW2e', 'scriptPubKey': '2102b331518051765afca186494922039763b0a0ffed41be17f26788724186fbf993ac', 'amount': 50.0, 'confirmations': 178, 'spendable': True, 'solvable': True}
{'txid': '5a78f2b9ee063c3e8f8ae2126587339e0e8a55ab3a740cf7ede2c4cf4e7b8908', 'vout': 0, 'address': 'n3pzWqJHW4y1BCdPKv4VnB3Qtnq5X745wa', 'account': '', 'scriptPubKey': '76a914f4bc1ba5d157600b5338bca440903b513be1f6a288ac', 'amount': 49.9999, 'confirmations': 2, 'spendable': True, 'solvable': True}
然后生成两个收款地址:
addr0 = cli("getnewaddress")
addr1 = cli("getnewaddress")
print(addr0)
print(addr1)
mwkfrosPqs4FbkuJyNehMY1eiDoYpFkGPR
mkxaaPvfpbGsM9BFdhUMdmXFppqAoa2dTd
现在我们取出这两个 utxo 的地址所对应的私钥:
priv0 = cli("dumpprivkey", utxo0["address"])
print(priv0)
priv1 = cli("dumpprivkey", utxo1["address"])
print(priv1)
cVEkKgsphy8Txkg44Tvd8snhWdbB9c47DKQuZbhdM7Pa4gX47dbc
cNDqbGYYHK8sJ3WJNbMQcq1t2o8mxGdJvEQWHsEjrroyRxjzCMbT
我们现在构造交易的 vout 和 vin :
vin = [{"txid":utxo0["txid"], "vout":utxo0["vout"]}, {"txid":utxo1["txid"], "vout":utxo1["vout"]}]
vout = {utxo0["address"]:79.9999, utxo1["address"]:10}
现在构造出新交易:
rawtx = cli("createrawtransaction", json.dumps(vin), json.dumps(vout))
print(rawtx)
0200000002e00b0b5a5e1b8952bd961db85ab5fedebeb1e7932e95e6a74708f6762e96b6260000000000ffffffff08897b4ecfc4e2edf70c743aab558a0e9e33876512e28a8f3e3c06eeb9f2785a0000000000ffffffff02f028d6dc010000001976a914b4d5a3f7ee367fb12fd7592e907f286fb6c71e2d88ac00ca9a3b000000001976a914f4bc1ba5d157600b5338bca440903b513be1f6a288ac00000000
现在做第一次签名:
signed0 = json.loads(cli("signrawtransaction", rawtx, "[]", json.dumps([priv0])))
print(signed0)
{'hex': '0200000002e00b0b5a5e1b8952bd961db85ab5fedebeb1e7932e95e6a74708f6762e96b6260000000049483045022100b77c54e298ed208729e661ed6bc46a0c3b73d9cb9b4dd567212ba0f2ef9f7cde02202e3dd46433c2e941a6dda53d9e7c734e957097f3fad5b55a41993f7008e7c16501ffffffff08897b4ecfc4e2edf70c743aab558a0e9e33876512e28a8f3e3c06eeb9f2785a0000000000ffffffff02f028d6dc010000001976a914b4d5a3f7ee367fb12fd7592e907f286fb6c71e2d88ac00ca9a3b000000001976a914f4bc1ba5d157600b5338bca440903b513be1f6a288ac00000000', 'complete': False, 'errors': [{'txid': '5a78f2b9ee063c3e8f8ae2126587339e0e8a55ab3a740cf7ede2c4cf4e7b8908', 'vout': 0, 'scriptSig': '', 'sequence': 4294967295, 'error': 'Operation not valid with the current stack size'}]}
用同样的方式,我们以第二个私钥给第一次签名结果再签一次:
signed1 = json.loads(cli("signrawtransaction", signed0["hex"], "[]", json.dumps([priv1])))
print(signed1)
{'hex': '0200000002e00b0b5a5e1b8952bd961db85ab5fedebeb1e7932e95e6a74708f6762e96b6260000000049483045022100b77c54e298ed208729e661ed6bc46a0c3b73d9cb9b4dd567212ba0f2ef9f7cde02202e3dd46433c2e941a6dda53d9e7c734e957097f3fad5b55a41993f7008e7c16501ffffffff08897b4ecfc4e2edf70c743aab558a0e9e33876512e28a8f3e3c06eeb9f2785a000000006a473044022011c23a8bdd0278ed73c312b7a84008df877241da1ca40617f9ea1fd5f6fae6ad02201bd3095ded770eac3bb25c93dc6f0c26eba037f6332f455e14d0e33889671daa012102dfaabd6c406491fd337c7aedc923c984f8b53aedf016fc470dbe60f931b3ce11ffffffff02f028d6dc010000001976a914b4d5a3f7ee367fb12fd7592e907f286fb6c71e2d88ac00ca9a3b000000001976a914f4bc1ba5d157600b5338bca440903b513be1f6a288ac00000000', 'complete': True}
现在,我们可以将这个签名后的交易如前面章节中的简单交易一样,广播并确认:
new_txid = cli("sendrawtransaction", signed1["hex"])
print(new_txid)
result = cli("generate", "1")
print(result)
::...
免责声明:
当前网页内容, 由
大妈 ZoomQuiet
使用工具:
ScrapBook :: Firefox Extension
人工从互联网中收集并分享;
内容版权归原作者所有;
本人对内容的有效性/合法性不承担任何强制性责任.
若有不妥, 欢迎评注提醒:
或是邮件反馈可也:
askdama[AT]googlegroups.com
订阅 substack 体验古早写作:
点击注册~> 获得 100$ 体验券:
关注公众号, 持续获得相关各种嗯哼:
自怼圈/年度番新
关于 ~ DebugUself with DAMA ;-)
粤ICP备18025058号-1
公安备案号:
44049002000656
...::