现有四种ECDSA签名的验证OP码: OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY。
还有四种签名的种类:ALL, NONE, SINGLE, ANYONECANPAY。
但是存在至少两个缺陷:
- 现在的验证交易存在潜在过于复杂的问题,验证过程的时间度复杂是O(n^2)。构造一个1MB大小的交易,则需要花费2秒左右才能完成验证。
- 交易签名时,在不访问前向输出的情况下,是无法知道输入的金额的。因为交易输入里记录的前向交易的哈希值以及位于前向交易的第几个输出位置,其中并不包含金额。导致冷钱包或者分离式的签名器无法安全的进行签名操作,被迫信任传入的交易数据金额。
在原有的脚本上解决这个问题,并非易事。不过,在Segwit软分叉基础上,可以做到。
验证交易的时间复杂度缺陷
现有交易验证过程:
- 剔除交易其他输入的签名数据
- 替换当前输入的脚本
scriptSig为前向交易对应输出的脚本scriptPubKey - 对当前交易做哈希运算,double-SHA256后得到32字节的结果
- 对32字节结果进行签名
- 循环这个过程,直到遍历处理完所有输入
为何上述过程的时间复杂度是O(n^2)呢?很容易,两个线性叠加了:1. 交易越大,输入数量越多 2. 交易越大,单次交易哈希计算过程越长。
构建一个典型的交易:
- 一个1MB的交易,输入3,300个,输出406,000 bytes,仅哈希计算则需要:3300 * 406,000,约1.34GB,需要时间10.9秒。
- 一个8MB的交易,输入22,500个,输出3.95MB,仅哈希计算则需要:22500 * 3.95,大约是88.8GB,需要时间超过11分钟。
也就是说随着交易体积的线性增大,验证交易的时间呈指数级增长。
实施细节
重新定义一个交易哈希摘要算法,但新摘要算法仅与version为零的witness program一起工作:
1 | Double SHA256一下字段序列化后的数据: |
若只有1,4,7,9,10,则与旧签名算法中的含义一致。
对于解决复杂度问题,新摘要算法的解决办法很简单,把重复计算的数据先预计算为32字节的哈希值,那么整个新算法的序列化后数据长度则与交易大小基本没关系。
其中5,6保证了签名时,签名者可确认签名金额和脚本,解决了输入金额盲签的问题。
参考: