Segwitness 中btcd 代码实现详解
从btc源代码看segwitness的实现,以btcd代码为例:函数ExtractWitnessCommitment从块的coinbase交易中提取commitment。
func ExtractWitnessCommitment(tx *btcutil.Tx) ([]byte, bool) {
// The witness commitment *must* be located within one of the coinbase
// transaction's outputs.
if !IsCoinBase(tx) {
return nil, false
}
msgTx := tx.MsgTx()
for i := len(msgTx.TxOut) - 1; i >= 0; i-- {
// The public key script that contains the witness commitment
// must shared a prefix with the WitnessMagicBytes, and be at
// least 38 bytes.
pkScript := msgTx.TxOut[i].PkScript
if len(pkScript) >= CoinbaseWitnessPkScriptLength &&
bytes.HasPrefix(pkScript, WitnessMagicBytes) {
// The witness commitment itself is a 32-byte hash
// directly after the WitnessMagicBytes. The remaining
// bytes beyond the 38th byte currently have no consensus
// meaning.
start := len(WitnessMagicBytes)
end := CoinbaseWitnessPkScriptLength
return msgTx.TxOut[i].PkScript[start:end], true
}
}
return nil, false
}
Commitment是 BIP:141中规定的一个结构,该结构包含一个WitnessMagicBytes :0x6a24aa21a9ed。如果coinbase交易的输出中包含这个头,即表示有采用segwitness协议。
1-byte - OP_RETURN (0x6a)
1-byte - Push the following 36 bytes (0x24)
4-byte - Commitment header (0xaa21a9ed)
32-byte - Commitment hash: Double-SHA256(witness root hash|witness reserved value)
Witness的结构定义如上所示,其中前六个字节是固定值,commitment hash是对witness root hash|witness reserved value求SHA256S双哈希得到。而witness root hash 是以wtxid为叶子节点的merke tree的根节点。witness reserved value是coinbase交易中交易输入中的一个值具体,计算merke root节点的代码如下:
func BuildMerkleTreeStore(transactions []*btcutil.Tx, witness bool) []*chainhash.Hash {
// Calculate how many entries are required to hold the binary merkle
// tree as a linear array and create an array of that size.
nextPoT := nextPowerOfTwo(len(transactions))
arraySize := nextPoT*2 - 1
merkles := make([]*chainhash.Hash, arraySize)
// Create the base transaction hashes and populate the array with them.
for i, tx := range transactions {
// If we're computing a witness merkle root, instead of the
// regular txid, we use the modified wtxid which includes a
// transaction's witness data within the digest. Additionally,
// the coinbase's wtxid is all zeroes.
switch {
case witness && i == 0:
var zeroHash chainhash.Hash
merkles[i] = &zeroHash
case witness:
wSha := tx.MsgTx().WitnessHash()
merkles[i] = &wSha
default:
merkles[i] = tx.Hash()
}
}
// Start the array offset after the last transaction and adjusted to the
// next power of two.
offset := nextPoT
for i := 0; i < arraySize-1; i += 2 {
switch {
// When there is no left child node, the parent is nil too.
case merkles[i] == nil:
merkles[offset] = nil
// When there is no right child, the parent is generated by
// hashing the concatenation of the left child with itself.
case merkles[i+1] == nil:
newHash := HashMerkleBranches(merkles[i], merkles[i])
merkles[offset] = newHash
// The normal case sets the parent node to the double sha256
// of the concatentation of the left and right children.
default:
newHash := HashMerkleBranches(merkles[i], merkles[i+1])
merkles[offset] = newHash
}
offset++
}
return merkles
}
Segwitness定义了新的transactionid,每一笔交易都有两个交易id,一个是原来的txid,一个是新定义的Wtxid,对这个[nVersion][txins][txouts][nLockTime]序列化输出做SHA256双哈希,可以得到txid。而对[nVersion][marker][flag][txins][txouts][witness][nLockTime]的序列化输出做SHA256双哈希得到wtxid。函数ValidateWitnessCommitment是对winess的验证,即根据各个transaction计算的merkroot和上文提到的commitment hash做比较,如果相同则认为是合法的区块
浙公网安备 33010602011771号