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 valueSHA256S双哈希得到。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做比较,如果相同则认为是合法的区块

 

posted on 2018-05-31 23:07  xgcode  阅读(474)  评论(0编辑  收藏  举报