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
import (
"bytes"
"container/heap"
"fmt"
"time"
@ -460,7 +461,6 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress btcutil.Address) (*Bloc
if err != nil {
return nil, err
}
// TODO(roasbeef): add witnesss commitment output
coinbaseTx, err := createCoinbaseTx(g.chainParams, coinbaseScript,
nextBlockHeight, payToAddress)
if err != nil {
@ -580,7 +580,6 @@ mempoolLoop:
nextBlockHeight)
// Calculate the fee in Satoshi/kB.
// TODO(roasbeef): cost accounting by weight
prioItem.feePerKB = txDesc.FeePerKB
prioItem.fee = txDesc.Fee
@ -602,13 +601,22 @@ mempoolLoop:
// The starting block size is the size of the block header plus the max
// possible transaction count size, plus the size of the coinbase
// transaction.
blockWeight := uint32((blockHeaderOverhead * (blockchain.WitnessScaleFactor - 1)) + blockchain.GetTransactionWeight(coinbaseTx))
blockWeight := uint32((blockHeaderOverhead * blockchain.WitnessScaleFactor) +
blockchain.GetTransactionWeight(coinbaseTx))
blockSigOpCost := coinbaseSigOpCost
totalFees := int64(0)
// TODO(roasbeef): should be guarded by version bits state check
var witnessIncluded bool
includeWitness := true
// Query the version bits state to see if segwit has been activated, if
// so then this means that we'll include any transactions with witness
// 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.
for priorityQueue.Len() > 0 {
@ -620,37 +628,38 @@ mempoolLoop:
switch {
// If segregated witness has not been activated yet, then we
// shouldn't include any witness transactions in the block.
case tx.HasWitness() && !segwitActive:
case !segwitActive && tx.HasWitness():
continue
// Otherwise, Keep track of if we've included a transaction
// with witness data or not. If so, then we'll need to include
// the witness commitment as the last output in the coinbase
// transaction.
case tx.HasWitness() && segwitActive:
case segwitActive && !witnessIncluded && tx.HasWitness():
// If we're about to include a transaction bearing
// witness data, then we'll also need to include a
// witness commitment in the coinbase transaction.
// Therefore, we account for the additional weight
// within the block.
if !witnessIncluded {
// First we account for the additional witness
// data in the witness nonce of the coinbaes
// transaction: 32-bytes of zeroes.
blockWeight += 2 + 32
// 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
// within the block with a model coinbase tx with a
// witness commitment.
coinbaseCopy := btcutil.NewTx(coinbaseTx.MsgTx().Copy())
coinbaseCopy.MsgTx().TxIn[0].Witness = [][]byte{
bytes.Repeat([]byte("a"),
blockchain.CoinbaseWitnessDataLen),
}
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
}
@ -660,7 +669,7 @@ mempoolLoop:
// Enforce maximum block size. Also check for overflow.
txWeight := uint32(blockchain.GetTransactionWeight(tx))
blockPlusTxWeight := uint32(blockWeight + txWeight)
blockPlusTxWeight := blockWeight + txWeight
if blockPlusTxWeight < blockWeight ||
blockPlusTxWeight >= g.policy.BlockMaxWeight {
@ -673,7 +682,7 @@ mempoolLoop:
// Enforce maximum signature operation cost per block. Also
// check for overflow.
sigOpCost, err := blockchain.GetSigOpCost(tx, false,
blockUtxos, true, includeWitness)
blockUtxos, true, segwitActive)
if err != nil {
log.Tracef("Skipping tx %s due to error in "+
"GetSigOpCost: %v", tx.Hash(), err)
@ -789,12 +798,49 @@ mempoolLoop:
// the total fees accordingly.
blockWeight -= wire.MaxVarIntPayload -
(uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) *
(blockchain.WitnessScaleFactor - 1))
blockchain.WitnessScaleFactor)
coinbaseTx.MsgTx().TxOut[0].Value += 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 {
// 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
@ -844,11 +890,12 @@ mempoolLoop:
blockWeight, blockchain.CompactToBig(msgBlock.Header.Bits))
return &BlockTemplate{
Block: &msgBlock,
Fees: txFees,
SigOpCosts: txSigOpCosts,
Height: nextBlockHeight,
ValidPayAddress: payToAddress != nil,
Block: &msgBlock,
Fees: txFees,
SigOpCosts: txSigOpCosts,
Height: nextBlockHeight,
ValidPayAddress: payToAddress != nil,
WitnessCommitment: witnessCommitment,
}, nil
}