mining: update GBT generation to include witness commitment

This commit updates the block template generation logic to only include
witness transactions once the soft-fork has activated and to also
include the OP_RETURN witness commitment (with additional block weight
accounting).
This commit is contained in:
Olaoluwa Osuntokun 2017-01-03 21:15:49 -08:00 committed by Dave Collins
parent 728c0a4398
commit f5dec67086

View file

@ -5,6 +5,7 @@
package mining package mining
import ( import (
"bytes"
"container/heap" "container/heap"
"fmt" "fmt"
"time" "time"
@ -460,7 +461,6 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress btcutil.Address) (*Bloc
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO(roasbeef): add witnesss commitment output
coinbaseTx, err := createCoinbaseTx(g.chainParams, coinbaseScript, coinbaseTx, err := createCoinbaseTx(g.chainParams, coinbaseScript,
nextBlockHeight, payToAddress) nextBlockHeight, payToAddress)
if err != nil { if err != nil {
@ -580,7 +580,6 @@ mempoolLoop:
nextBlockHeight) nextBlockHeight)
// Calculate the fee in Satoshi/kB. // Calculate the fee in Satoshi/kB.
// TODO(roasbeef): cost accounting by weight
prioItem.feePerKB = txDesc.FeePerKB prioItem.feePerKB = txDesc.FeePerKB
prioItem.fee = txDesc.Fee prioItem.fee = txDesc.Fee
@ -602,13 +601,22 @@ mempoolLoop:
// The starting block size is the size of the block header plus the max // The starting block size is the size of the block header plus the max
// possible transaction count size, plus the size of the coinbase // possible transaction count size, plus the size of the coinbase
// transaction. // transaction.
blockWeight := uint32((blockHeaderOverhead * (blockchain.WitnessScaleFactor - 1)) + blockchain.GetTransactionWeight(coinbaseTx)) blockWeight := uint32((blockHeaderOverhead * blockchain.WitnessScaleFactor) +
blockchain.GetTransactionWeight(coinbaseTx))
blockSigOpCost := coinbaseSigOpCost blockSigOpCost := coinbaseSigOpCost
totalFees := int64(0) totalFees := int64(0)
// TODO(roasbeef): should be guarded by version bits state check // Query the version bits state to see if segwit has been activated, if
var witnessIncluded bool // so then this means that we'll include any transactions with witness
includeWitness := true // data in the mempool, and also add the witness commitment as an
// OP_RETURN output in the coinbase transaction.
segwitState, err := g.chain.ThresholdState(chaincfg.DeploymentSegwit)
if err != nil {
return nil, err
}
segwitActive := segwitState == blockchain.ThresholdActive
witnessIncluded := false
// Choose which transactions make it into the block. // Choose which transactions make it into the block.
for priorityQueue.Len() > 0 { for priorityQueue.Len() > 0 {
@ -620,37 +628,38 @@ mempoolLoop:
switch { switch {
// If segregated witness has not been activated yet, then we // If segregated witness has not been activated yet, then we
// shouldn't include any witness transactions in the block. // shouldn't include any witness transactions in the block.
case tx.HasWitness() && !segwitActive: case !segwitActive && tx.HasWitness():
continue continue
// Otherwise, Keep track of if we've included a transaction // Otherwise, Keep track of if we've included a transaction
// with witness data or not. If so, then we'll need to include // with witness data or not. If so, then we'll need to include
// the witness commitment as the last output in the coinbase // the witness commitment as the last output in the coinbase
// transaction. // transaction.
case tx.HasWitness() && segwitActive: case segwitActive && !witnessIncluded && tx.HasWitness():
// If we're about to include a transaction bearing // If we're about to include a transaction bearing
// witness data, then we'll also need to include a // witness data, then we'll also need to include a
// witness commitment in the coinbase transaction. // witness commitment in the coinbase transaction.
// Therefore, we account for the additional weight // Therefore, we account for the additional weight
// within the block. // within the block with a model coinbase tx with a
if !witnessIncluded { // witness commitment.
// First we account for the additional witness coinbaseCopy := btcutil.NewTx(coinbaseTx.MsgTx().Copy())
// data in the witness nonce of the coinbaes coinbaseCopy.MsgTx().TxIn[0].Witness = [][]byte{
// transaction: 32-bytes of zeroes. bytes.Repeat([]byte("a"),
blockWeight += 2 + 32 blockchain.CoinbaseWitnessDataLen),
// Next we account for the additional flag and
// marker bytes in the transaction
// serialization.
blockWeight += (1 + 1) * blockchain.WitnessScaleFactor
// Finally we account for the weight of the
// additional OP_RETURN output: 8-bytes (value)
// + 1-byte (var-int) + 38-bytes (pkScript),
// scaling up the weight as it's non-witness
// data.
blockWeight += (8 + 1 + 38) * blockchain.WitnessScaleFactor
} }
coinbaseCopy.MsgTx().AddTxOut(&wire.TxOut{
PkScript: bytes.Repeat([]byte("a"),
blockchain.CoinbaseWitnessPkScriptLength),
})
// In order to accurately account for the weight
// addition due to this coinbase transaction, we'll add
// the difference of the transaction before and after
// the addition of the commitment to the block weight.
weightDiff := blockchain.GetTransactionWeight(coinbaseCopy) -
blockchain.GetTransactionWeight(coinbaseTx)
blockWeight += uint32(weightDiff)
witnessIncluded = true witnessIncluded = true
} }
@ -660,7 +669,7 @@ mempoolLoop:
// Enforce maximum block size. Also check for overflow. // Enforce maximum block size. Also check for overflow.
txWeight := uint32(blockchain.GetTransactionWeight(tx)) txWeight := uint32(blockchain.GetTransactionWeight(tx))
blockPlusTxWeight := uint32(blockWeight + txWeight) blockPlusTxWeight := blockWeight + txWeight
if blockPlusTxWeight < blockWeight || if blockPlusTxWeight < blockWeight ||
blockPlusTxWeight >= g.policy.BlockMaxWeight { blockPlusTxWeight >= g.policy.BlockMaxWeight {
@ -673,7 +682,7 @@ mempoolLoop:
// Enforce maximum signature operation cost per block. Also // Enforce maximum signature operation cost per block. Also
// check for overflow. // check for overflow.
sigOpCost, err := blockchain.GetSigOpCost(tx, false, sigOpCost, err := blockchain.GetSigOpCost(tx, false,
blockUtxos, true, includeWitness) blockUtxos, true, segwitActive)
if err != nil { if err != nil {
log.Tracef("Skipping tx %s due to error in "+ log.Tracef("Skipping tx %s due to error in "+
"GetSigOpCost: %v", tx.Hash(), err) "GetSigOpCost: %v", tx.Hash(), err)
@ -789,12 +798,49 @@ mempoolLoop:
// the total fees accordingly. // the total fees accordingly.
blockWeight -= wire.MaxVarIntPayload - blockWeight -= wire.MaxVarIntPayload -
(uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) * (uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) *
(blockchain.WitnessScaleFactor - 1)) blockchain.WitnessScaleFactor)
coinbaseTx.MsgTx().TxOut[0].Value += totalFees coinbaseTx.MsgTx().TxOut[0].Value += totalFees
txFees[0] = -totalFees txFees[0] = -totalFees
// TODO(roasbeef): add witness commitment // If segwit is active and we included transactions with witness data,
// then we'll need to include a commitment to the witness data in an
// OP_RETURN output within the coinbase transaction.
var witnessCommitment []byte
if witnessIncluded { if witnessIncluded {
// The witness of the coinbase transaction MUST be exactly 32-bytes
// of all zeroes.
var witnessNonce [blockchain.CoinbaseWitnessDataLen]byte
coinbaseTx.MsgTx().TxIn[0].Witness = wire.TxWitness{witnessNonce[:]}
// Next, obtain the merkle root of a tree which consists of the
// wtxid of all transactions in the block. The coinbase
// transaction will have a special wtxid of all zeroes.
witnessMerkleTree := blockchain.BuildMerkleTreeStore(blockTxns,
true)
witnessMerkleRoot := witnessMerkleTree[len(witnessMerkleTree)-1]
// The preimage to the witness commitment is:
// witnessRoot || coinbaseWitness
var witnessPreimage [64]byte
copy(witnessPreimage[:32], witnessMerkleRoot[:])
copy(witnessPreimage[32:], witnessNonce[:])
// The witness commitment itself is the double-sha256 of the
// witness preimage generated above. With the commitment
// generated, the witness script for the output is: OP_RETURN
// OP_DATA_36 {0xaa21a9ed || witnessCommitment}. The leading
// prefix is refered to as the "witness magic bytes".
witnessCommitment = chainhash.DoubleHashB(witnessPreimage[:])
witnessScript := append(blockchain.WitnessMagicBytes, witnessCommitment...)
// Finally, create the OP_RETURN carrying witness commitment
// output as an additional output within the coinbase.
commitmentOutput := &wire.TxOut{
Value: 0,
PkScript: witnessScript,
}
coinbaseTx.MsgTx().TxOut = append(coinbaseTx.MsgTx().TxOut,
commitmentOutput)
} }
// Calculate the required difficulty for the block. The timestamp // Calculate the required difficulty for the block. The timestamp
@ -849,6 +895,7 @@ mempoolLoop:
SigOpCosts: txSigOpCosts, SigOpCosts: txSigOpCosts,
Height: nextBlockHeight, Height: nextBlockHeight,
ValidPayAddress: payToAddress != nil, ValidPayAddress: payToAddress != nil,
WitnessCommitment: witnessCommitment,
}, nil }, nil
} }