diff --git a/blockchain/validate.go b/blockchain/validate.go index 53d1ad80f..45e99e89a 100644 --- a/blockchain/validate.go +++ b/blockchain/validate.go @@ -19,6 +19,10 @@ import ( ) const ( + // MaxSigOpsPerBlock is the maximum number of signature operations + // allowed for a block. It is a fraction of the max block payload size. + MaxSigOpsPerBlock = wire.MaxBlockPayload / 50 + // MaxTimeOffsetSeconds is the maximum number of seconds a block time // is allowed to be ahead of the current time. This is currently 2 // hours. diff --git a/mining/mining.go b/mining/mining.go index dbf9b961b..ccde70223 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -5,7 +5,6 @@ package mining import ( - "bytes" "container/heap" "fmt" "time" @@ -198,9 +197,9 @@ type BlockTemplate struct { // sum of the fees of all other transactions. Fees []int64 - // SigOpCosts contains the number of signature operations each + // SigOpCounts contains the number of signature operations each // transaction in the generated template performs. - SigOpCosts []int64 + SigOpCounts []int64 // Height is the height at which the block template connects to the main // chain. @@ -211,12 +210,6 @@ type BlockTemplate struct { // NewBlockTemplate for details on which this can be useful to generate // templates without a coinbase payment address. ValidPayAddress bool - - // WitnessCommitment is a commitment to the witness data (if any) - // within the block. This field will only be populted once segregated - // witness has been activated, and the block contains a transaction - // which has witness data. - WitnessCommitment []byte } // mergeUtxoView adds all of the entries in viewB to viewA. The result is that @@ -463,7 +456,7 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress btcutil.Address) (*Bloc if err != nil { return nil, err } - coinbaseSigOpCost := int64(blockchain.CountSigOps(coinbaseTx)) * blockchain.WitnessScaleFactor + numCoinbaseSigOps := int64(blockchain.CountSigOps(coinbaseTx)) // Get the current source transactions and create a priority queue to // hold the transactions which are ready for inclusion into a block @@ -497,9 +490,9 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress btcutil.Address) (*Bloc // However, since the total fees aren't known yet, use a dummy value for // the coinbase fee which will be updated later. txFees := make([]int64, 0, len(sourceTxns)) - txSigOpCosts := make([]int64, 0, len(sourceTxns)) + txSigOpCounts := make([]int64, 0, len(sourceTxns)) txFees = append(txFees, -1) // Updated once known - txSigOpCosts = append(txSigOpCosts, coinbaseSigOpCost) + txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps) log.Debugf("Considering %d transactions for inclusion to new block", len(sourceTxns)) @@ -597,23 +590,10 @@ 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) + - blockchain.GetTransactionWeight(coinbaseTx)) - blockSigOpCost := coinbaseSigOpCost + blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize()) + blockSigOps := numCoinbaseSigOps totalFees := int64(0) - // 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 { // Grab the highest priority (or highest fee per kilobyte @@ -621,72 +601,42 @@ mempoolLoop: prioItem := heap.Pop(priorityQueue).(*txPrioItem) tx := prioItem.tx - switch { - // If segregated witness has not been activated yet, then we - // shouldn't include any witness transactions in the block. - 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 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 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 - } - // Grab any transactions which depend on this one. deps := dependers[*tx.Hash()] // Enforce maximum block size. Also check for overflow. - txWeight := uint32(blockchain.GetTransactionWeight(tx)) - blockPlusTxWeight := blockWeight + txWeight - if blockPlusTxWeight < blockWeight || - blockPlusTxWeight >= g.policy.BlockMaxWeight { + txSize := uint32(tx.MsgTx().SerializeSize()) + blockPlusTxSize := blockSize + txSize + if blockPlusTxSize < blockSize || + blockPlusTxSize >= g.policy.BlockMaxSize { log.Tracef("Skipping tx %s because it would exceed "+ - "the max block weight", tx.Hash()) + "the max block size", tx.Hash()) logSkippedDeps(tx, deps) continue } - // Enforce maximum signature operation cost per block. Also - // check for overflow. - sigOpCost, err := blockchain.GetSigOpCost(tx, false, - blockUtxos, true, segwitActive) + // Enforce maximum signature operations per block. Also check + // for overflow. + numSigOps := int64(blockchain.CountSigOps(tx)) + if blockSigOps+numSigOps < blockSigOps || + blockSigOps+numSigOps > blockchain.MaxSigOpsPerBlock { + log.Tracef("Skipping tx %s because it would exceed "+ + "the maximum sigops per block", tx.Hash()) + logSkippedDeps(tx, deps) + continue + } + numP2SHSigOps, err := blockchain.CountP2SHSigOps(tx, false, + blockUtxos) if err != nil { log.Tracef("Skipping tx %s due to error in "+ "GetSigOpCost: %v", tx.Hash(), err) logSkippedDeps(tx, deps) continue } - if blockSigOpCost+int64(sigOpCost) < blockSigOpCost || - blockSigOpCost+int64(sigOpCost) > blockchain.MaxBlockSigOpsCost { + numSigOps += int64(numP2SHSigOps) + if blockSigOps+numSigOps < blockSigOps || + blockSigOps+numSigOps > blockchain.MaxBlockSigOpsCost { log.Tracef("Skipping tx %s because it would "+ "exceed the maximum sigops per block", tx.Hash()) logSkippedDeps(tx, deps) @@ -697,13 +647,13 @@ mempoolLoop: // minimum block size. if sortedByFee && prioItem.feePerKB < int64(g.policy.TxMinFreeFee) && - blockPlusTxWeight >= g.policy.BlockMinWeight { + blockPlusTxSize >= g.policy.BlockMinSize { - log.Tracef("Skipping tx %s with feePerKB %d "+ - "< TxMinFreeFee %d and block weight %d >= "+ - "minBlockWeight %d", tx.Hash(), prioItem.feePerKB, - g.policy.TxMinFreeFee, blockPlusTxWeight, - g.policy.BlockMinWeight) + log.Tracef("Skipping tx %s with feePerKB %.2f "+ + "< TxMinFreeFee %d and block size %d >= "+ + "minBlockSize %d", tx.Hash(), prioItem.feePerKB, + g.policy.TxMinFreeFee, blockPlusTxSize, + g.policy.BlockMinSize) logSkippedDeps(tx, deps) continue } @@ -711,13 +661,13 @@ mempoolLoop: // Prioritize by fee per kilobyte once the block is larger than // the priority size or there are no more high-priority // transactions. - if !sortedByFee && (blockPlusTxWeight >= g.policy.BlockPrioritySize || + if !sortedByFee && (blockPlusTxSize >= g.policy.BlockPrioritySize || prioItem.priority <= MinHighPriority) { - log.Tracef("Switching to sort by fees per "+ - "kilobyte blockSize %d >= BlockPrioritySize "+ - "%d || priority %.2f <= minHighPriority %.2f", - blockPlusTxWeight, g.policy.BlockPrioritySize, + log.Tracef("Switching to sort by fees per kilobyte "+ + "blockSize %d >= BlockPrioritySize %d || "+ + "priority %.2f <= minHighPriority %.2f", + blockPlusTxSize, g.policy.BlockPrioritySize, prioItem.priority, MinHighPriority) sortedByFee = true @@ -729,7 +679,7 @@ mempoolLoop: // is too low. Otherwise this transaction will be the // final one in the high-priority section, so just fall // though to the code below so it is added now. - if blockPlusTxWeight > g.policy.BlockPrioritySize || + if blockPlusTxSize > g.policy.BlockPrioritySize || prioItem.priority < MinHighPriority { heap.Push(priorityQueue, prioItem) @@ -767,11 +717,11 @@ mempoolLoop: // save the fees and signature operation counts to the block // template. blockTxns = append(blockTxns, tx) - blockWeight += txWeight - blockSigOpCost += int64(sigOpCost) + blockSize += txSize + blockSigOps += int64(numSigOps) totalFees += prioItem.fee txFees = append(txFees, prioItem.fee) - txSigOpCosts = append(txSigOpCosts, int64(sigOpCost)) + txSigOpCounts = append(txSigOpCounts, numSigOps) log.Tracef("Adding tx %s (priority %.2f, feePerKB %.2f)", prioItem.tx.Hash(), prioItem.priority, prioItem.feePerKB) @@ -790,55 +740,13 @@ mempoolLoop: } // Now that the actual transactions have been selected, update the - // block weight for the real transaction count and coinbase value with + // block size for the real transaction count and coinbase value with // the total fees accordingly. - blockWeight -= wire.MaxVarIntPayload - - (uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) * - blockchain.WitnessScaleFactor) + blockSize -= wire.MaxVarIntPayload - + uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) coinbaseTx.MsgTx().TxOut[0].Value += totalFees txFees[0] = -totalFees - // 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 referred 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 // is potentially adjusted to ensure it comes after the median time of // the last several blocks per the chain consensus rules. @@ -880,18 +788,17 @@ mempoolLoop: return nil, err } - log.Debugf("Created new block template (%d transactions, %d in "+ - "fees, %d signature operations cost, %d weight, target difficulty "+ - "%064x)", len(msgBlock.Transactions), totalFees, blockSigOpCost, - blockWeight, blockchain.CompactToBig(msgBlock.Header.Bits)) + log.Debugf("Created new block template (%d transactions, %d in fees, "+ + "%d signature operations, %d bytes, target difficulty %064x)", + len(msgBlock.Transactions), totalFees, blockSigOps, blockSize, + blockchain.CompactToBig(msgBlock.Header.Bits)) return &BlockTemplate{ - Block: &msgBlock, - Fees: txFees, - SigOpCosts: txSigOpCosts, - Height: nextBlockHeight, - ValidPayAddress: payToAddress != nil, - WitnessCommitment: witnessCommitment, + Block: &msgBlock, + Fees: txFees, + SigOpCounts: txSigOpCounts, + Height: nextBlockHeight, + ValidPayAddress: payToAddress != nil, }, nil } diff --git a/mining/policy.go b/mining/policy.go index 78adf6a94..fddb7381d 100644 --- a/mining/policy.go +++ b/mining/policy.go @@ -21,15 +21,7 @@ const ( // the generation of block templates. See the documentation for // NewBlockTemplate for more details on each of these parameters are used. type Policy struct { - // BlockMinWeight is the minimum block weight to be used when - // generating a block template. - BlockMinWeight uint32 - - // BlockMaxWeight is the maximum block weight to be used when - // generating a block template. - BlockMaxWeight uint32 - - // BlockMinWeight is the minimum block size to be used when generating + // BlockMinSize is the minimum block size to be used when generating // a block template. BlockMinSize uint32 diff --git a/rpcserver.go b/rpcserver.go index 718980e02..cbd30fdcb 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1695,7 +1695,7 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld Hash: txHash.String(), Depends: depends, Fee: template.Fees[i], - SigOps: template.SigOpCosts[i], + SigOps: template.SigOpCounts[i], } transactions = append(transactions, resultTx) } @@ -1754,7 +1754,7 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld Hash: tx.TxHash().String(), Depends: []int64{}, Fee: template.Fees[0], - SigOps: template.SigOpCosts[0], + SigOps: template.SigOpCounts[0], } reply.CoinbaseTxn = &resultTx