交易
{
"version": 1,
"locktime": 0,
"vin": [
{
"txid":"7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
"vout": 0,
"scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.01500000,
"scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG"
},
{
"value": 0.08450000,
"scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG",
}
]
}
// - 7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18交易的vout
"vout": [
{
"value": 0.10000000,
"scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG"
}
]
-
交易截图(代码中的正在进行的交易)
iShot2022-04-06 18.04.52.png -
交易截图(代码中的 7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18指定的交易)
iShot2022-04-06 18.05.26.png
上边的input中的address 表示的是input中的交易中付款方的地址, output中的address 表示的是output中的交易中收款方的地址
-
交易的输入输出
- UTXO(unspent transaction outputs, 未花费的交易输出) : 所有UTXO的集合被称为UTXO集,目前有数百万个UTXO。 当新的UTXO被创建,UTXO集就会变大,当UTXO被消耗时,UTXO集会随着缩小。每一个交易都代表UTXO集的变化(状态转换)
- 用户收到比特币是指的钱包检测到可用的UTXO, 通过钱包所控制的密钥我们可以花费这些UTXO, 所以余额指的是所有UTXO的总和(可能分布在数百个交易和区块中).
- 一个用户的比特币余额: 比特币钱包扫描区块链并聚集所有属于该用户的UTXO来计算用户的余额, 大多数钱包维护一个数据库或使用数据库服务来存储所有UTXO的快速参考集,这些UTXO由用户所有的密钥来控制花费行为。
- 一个UTXO只能在一次交易中作为一个整体被消耗。这样的话,由于UTXO(或交易输出)的不可分割特性,大部分比特币交易都会产生找零.
- 每一笔交易都会消耗之前被记录的UTXO, 并创建新的UTXO以备未来的交易的消耗, 比特币的交易就是在交易链中消耗和创建UTXO, 一笔比特币的交易通过使用所有者的签名来解锁UTXO, 通过使用新的所有者的比特币地址来锁定并创建UTXO;
-
交易输出
- 每一笔比特币交易都会创建输出,并被比特币账簿记录下来(OP_RETURN除外), 几乎所有的输出都能创造一定数量的可用于支付的比特币.
- 交易的输出包含
- 一定数量的比特币(value)
- 确定花费这些输出所需的条件(scriptPubKey)
- 序列化和反序列化
- 序列化
- 将对象类型转换成字节流的过程
- 反序列化
- 将字节流转换成对象类型的过程
- 序列化
-
交易输入
- 将UTXO标记为被消费并通过解锁脚本提供所有权证明.
- 当构建一个交易的时候, 钱包通过控制地址对应的UTXO中选择足够的价值来执行被请求的付款(有时一个UTXO就足够, 有时需要很多个), 对于将用于进行此次付款的每个UTXO, 钱包都会创建一个指向UTXO的输入, 并使用解锁脚本解锁它.
- 交易的输出包含
- UTXO指针: 一个交易ID,引用包含正在使用的UTXO的交易(通过指向UTXO被记录在区块链中所在的交易的哈希值和序列号来实现)
- 索引: 用于标识来自该交易vout的索引(从0开始)
- 解锁脚本: 用于解锁支出
- 序列号:
- 序列化和反序列化
- 序列化
- 将对象类型转换成字节流的过程
- 反序列化
- 将字节流转换成对象类型的过程
- 序列化
- 交易费
- 大多数交易包含交易费(矿工费). 矿工会根据不同的标准(包括交易费)对交易进行优先级排序, 交易费会影响处理优先级. 交易费不足或者没有交易费的交易可能会被推迟甚至可能会不被处理. 尽管交易费不是强制的, 而且没有交易费最终也有可能会被处理, 但是交易费将提高处理的优先级.
- 现在任何创建交易的比特币服务都必须动态实现收费, 动态费用通过费用估算服务/费用估算算法实现. 费用化估算包括简单的(计算最后一个块中矿工费的平均值的中位数) / 复杂的(统计分析), 估计必要费用(以字节为单位), 是交易具有很高的可能性被选择并打包进一定数量的块内.
- 交易的数据结构没有交易费的字段。相替代地,交易费是指输入和输出之间的差值。从所有输入中扣掉所有输出之后的多余的量会被矿工作为矿工费收集走(交易费即输入总和减输出总和的余量:交易费 = 求和(所有输入) - 求和(所有输出);
-
比特币交易脚本
- 锁定脚本
- 一个放置在输出上面的花费条件:它指定了今后花费这笔输出必须要满足的条件。
- 解锁脚本
- 一个满足锁定脚本设定的花费条件的脚本, 通过解锁脚本可以允许一个vout被消费, 解锁脚本是每一笔比特币交易输入的一部分, 往往含有一个用户的比特币钱包(用户的私钥)生成的数字签名, 由于解锁脚本通常包含一个数字签名, 所以也被称为ScriptSig.
- 流程
- 每个比特币节点都会通过同时执行锁定和解锁脚本来验证一笔交易, 每个输入都包含一个解锁脚本, 并引用之前的UTXO, 验证软件将复制解锁脚本, 检索输入所引用的UTXO, 并复制锁定脚本, 然后依次执行锁定脚本和解锁脚本, 如果解锁脚本满足锁定脚本条件, 则输入有效.
- P2PK(Pay-to-Public-Key)付款至公钥
- 脚本
- 锁定脚本
PUSHDATA(PubKey)
CHECKSIG - 解锁脚本
PUSHDATA(Sig)
- 锁定脚本
- 拼接
PUSHDATA(Sig)
PUSHDATA(PubKey)
CHECKSIG
- 脚本
- P2PKH(Pay-to-Public-Key-Hash)付款至公钥hash
- A->B : scriptPubKey: OP_DUP OP_HASH160 <B pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG B->C :scriptSig: <B 私钥对交易的签名> <B 公钥>
- 将输入锁定为一个公钥哈希值,即我们常说的比特币地址.
- 脚本
- 锁定脚本
OP_DUP
OP_HASH160
PUSHDATA(PubKey)
OP_EQUALVERIFY
OP_CHECKSIG - 解锁脚本
PUSHDATA(Sig)
PUSHDATA(PubKey)
- 锁定脚本
- 拼接
PUSHDATA(Sig)
PUSHDATA(PubKey)
OP_DUP
OP_HASH160
PUSHDATA(PubKey)
OP_EQUALVERIFY
OP_CHECKSIG - 上述交易的脚本执行流程(先执行解锁脚本,在执行锁定脚本, 因为是个栈结构,所以会先锁定然后再解锁).
-
上述的<Cafe Public Key>地址是长度为520位(65字节)的全公钥(04前缀 || x坐标 || y坐标);
iShot2022-04-06 18.18.28.png
- 多重签名
- 其中N个公钥被记录在脚本中,并且至少有M个必须提供签名来解锁资金
- 锁定脚本
M <Public Key 1> <Public Key 2> ... <Public Key N> N CHECKMULTISIG - 解锁脚本, 解锁脚本中有个0的原因是因为 CHECKMULTISIG的执行中有一个bug, CHECKMULTISIG弹出最上面的项目,这是N(在这个例子中N是“3”)。然后它弹出N个项目,这是可以签名的公钥。在这个例子中,公钥A,B和C.然后,它弹出一个项目,即M,仲裁(需要多少个签名)。这里M = 2。此时,CHECKMULTISIG应弹出最终的M个项目,这些是签名,并查看它们是否有效。然而,不幸的是,实施中的错误导致CHECKMULTISIG再弹出一个项目(总共M + 1个)。检查签名时,不考虑额外的项目,因此它对CHECKMULTISIG本身没有直接影响。但是,必须存在额外的值,因为如果不存在,则当CHECKMULTISIG尝试弹出空堆栈时,会导致堆栈错误和脚本失败(将交易标记为无效)。因为额外的项目被忽略,它可以是任何东西,但通常使用0。所以正确的解锁脚本中前边多了个0;
0 <Signature B> <Signature C> - 拼接
0
<Signature B>
<Signature C>
2
<Public Key A>
<Public Key B>
<Public Key C>
3
CHECKMULTISIG
- P2SH(Pay-to-Script-Hash)付款至脚本hash
-
scriptPubKey: OP_HASH 6fe28c0ab6f1b372c1a6 OP_EQUAL 这个脚本, hash(x) = 6fe28c0ab6f1b372c1a6 我们只是提供⼀一个x满⾜足这个条件,就可以证明我可以花费这笔钱了了。scriptPubKey: OP_HASH 6fe28c0ab6f1b372c1a6 OPEQUAL 这⾥6fe28c0ab6f1b372c1a6不不是简单的⼀一个数字的hash,⽽而是⼀一段script的hash(简称redeemScript)。 OP_HASH hash(redeemScript) OPEQUAL 我们可以理理解成只要你提供了了⼀段script,它的二进制hash和目标匹配,那么你就可以花费这笔钱了.
-
解决多重签名中使用不方便的问题(花费这笔UTXO时: 在客户付款前将该脚本发送给每一位客户,而每一位顾客也必须使用特制的能产生客户交易脚本的比特币钱包软件,每位顾客还得学会如何利用脚本来完成交易。).
image.png -
可以看到赎回脚本不是在锁定脚本中显示, 并且赎回脚本值是展示在解锁脚本中,也就是复杂的计算工作从(企业收款的那笔交易中的)发送方转移到收款方. 同时矿工的交易费用从发送方转移到收款方.
-
当有人试图花费这笔UTXO时,附上原始赎回脚本(与UTXO锁定的哈希)和必要的解锁签名即可, 即:
-
redeemScript
-
非多重支付类型
PUSHDATA(PubKey)
CHECKSIG -
多重支付类型
2
PUSHDATA(PubKey_1)
PUSHDATA(PubKey_2)
PUSHDATA(PubKey_3)
3
CHECKMULTISIG
-
-
解锁脚本就是:
-
非多重支付类型
PUSHDATA(Sig)
PUSHDATA(序列化的redeemScript) -
多重支付类型
0
PUSHDATA(Sig_1)
PUSHDATA(Sig_2)
PUSHDATA(序列化的redeemScript)
-
-
锁定脚本变成:
HASH160
PUSHDATA(redeemScriptHash)
EQUAL -
验证
-
验证序列化的redeemScript是否与output script中的hash值是否匹配
-
非多重支付
PUSHDATA(Sig)
PUSHDATA(序列化的redeemScript)
HASH160
PUSHDATA(redeemScriptHash)
EQUAL -
多重支付
0
PUSHDATA(Sig_1)
PUSHDATA(Sig_2)
PUSHDATA(序列化的redeemScript)
HASH160
PUSHDATA(redeemScriptHash)
EQUAL
-
-
反序列化执行redeemScript, 验证input script中给出的签名是否正确
- 非多重支付
PUSHDATA(Sig) // - 这行是执行完第一步验证后栈中剩余的
PUSHDATA(PubKey) // - redeemScript中的代码
CHECKSIG // - redeemScript中的代码 - 多重支付
0 // - 这行是执行完第一步验证后栈中剩余的
PUSHDATA(Sig_1) // - 这行是执行完第一步验证后栈中剩余的
PUSHDATA(Sig_2) // - 这行是执行完第一步验证后栈中剩余的
2 // - redeemScript中的代码
PUSHDATA(PubKey_1) // - redeemScript中的代码
PUSHDATA(PubKey_2) // - redeemScript中的代码
PUSHDATA(PubKey_3) // - redeemScript中的代码
3 // - redeemScript中的代码
CHECKMULTISIG // - redeemScript中的代码
- 非多重支付
-
-
同时可以将脚本哈希编译为一个地址, 即P2SH地址是基于Base58编码的一 个含有20个字节哈希的脚本(像比特币地址是基于Base58编码的一个含有20个字节的公钥。由于P2SH地址采用5作为前缀,这导致基于Base58编码的地址以“3”开头);
-
- 锁定脚本
-
数字签名
- 比特币中使用的数字签名算法是ECDSA(Elliptic Curve Digital Signature Algorithm, 椭圆曲线数字签名算法), 用于基于椭圆曲线私钥/公钥数字签名的算法, 该签名算法用于 OP_CHECKSIG,OP_CHECKSIGVERIFY,OP_CHECKMULTISIG和OP_CHECKMULTISIGVERIFY, 当锁定脚本看到这些操作时候, 解锁脚本必须包含一个 ECDSA签名.
- 用途
- 资金所有者已经授权了支出这些资金.
- 授权不可否认.
- 交易在签名之后没有任何人篡改.
- 创建数字签名
-
((Sig = F{sig}(F{hash}(m), dA))) : 使用私钥对交易的hash做签名运算,得到签名结果
- dA 是签名私钥
- m : 交易
- F{hash} : hash 函数
4.F{sig} : 签名算法; - Sig: 结果签名
-
Sig是有两个值组成的 即:Sig = (R, S), 得到R和S之后序列化为字节流,使用一种称为DER(Distinguished Encoding Rules, 分辨编码规则)的国际标准编码方案.
例如 : 3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301
- 0x30表示DER序列的开始
- 0x45 - 序列的长度(69字节)
- 0x02 - 接下来是一个整数
- 0x21 - 整数的长度(33字节)
- R-00884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb
- 0x02 - 接下来是一个整数
- 0x20 - 整数的长度(32字节)
- S-4b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813
- 后缀(0x01)指示使用的哈希的类型(SIGHASH_ALL)
-
-
验证签名
- 要验证签名, 必须有签名(R和S), 序列化交易和公钥.
-
签名可以指示交易数据的哪一部分包含在私钥签名的hash中(使用后缀表示), input中的不同的交易可以使用不同后缀类型,使每个交易拥有自己的后缀类型.
image.png
image.png
-
ECDSA算法
-
签名
image.png
- k是临时私钥, K = k*G, 得到公钥K, 其中Kx记作点的x坐标, Ky 记作点的y坐标.
- R = Kx
- dA是签名私钥
- m是交易数据, Hash(m)是对交易做hash运算.
- p是椭圆曲线的素数阶, 在 secp256k1 曲线中, p = 2^256 – 2^32 – 2^9 – 2^8 – 2^7 – 2^6 – 2^4 – 1
- 计算的到S, 如果, s==0, 重新执行第一步. 从而得到 R和S;
-
验证签名
image.png- R和S是签名值
- Qa是公钥
- m是签署的交易数据
- G是椭圆曲线发生器点
当且仅当 计算的点Px == R, 验证签名通过
-
网友评论