diff --git a/blockmanager.go b/blockmanager.go index 7b1ff6d10..50bc7a2df 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -1473,6 +1473,7 @@ func newBlockManager(s *server, indexManager blockchain.IndexManager) (*blockMan Notifications: bm.handleNotifyMsg, SigCache: s.sigCache, IndexManager: indexManager, + HashCache: s.hashCache, }) if err != nil { return nil, err diff --git a/config.go b/config.go index df47405bc..1266ce48b 100644 --- a/config.go +++ b/config.go @@ -20,13 +20,13 @@ import ( "strings" "time" + "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/connmgr" "github.com/btcsuite/btcd/database" _ "github.com/btcsuite/btcd/database/ffldb" "github.com/btcsuite/btcd/mempool" - "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/btcsuite/go-socks/socks" flags "github.com/jessevdk/go-flags" @@ -49,8 +49,12 @@ const ( defaultFreeTxRelayLimit = 15.0 defaultBlockMinSize = 0 defaultBlockMaxSize = 750000 + defaultBlockMinWeight = 0 + defaultBlockMaxWeight = 3000000 blockMaxSizeMin = 1000 - blockMaxSizeMax = wire.MaxBlockPayload - 1000 + blockMaxSizeMax = blockchain.MaxBlockBaseSize - 1000 + blockMaxWeightMin = 4000 + blockMaxWeightMax = blockchain.MaxBlockWeight - 4000 defaultGenerate = false defaultMaxOrphanTransactions = 100 defaultMaxOrphanTxSize = 100000 @@ -140,6 +144,8 @@ type config struct { MiningAddrs []string `long:"miningaddr" description:"Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set"` BlockMinSize uint32 `long:"blockminsize" description:"Mininum block size in bytes to be used when creating a block"` BlockMaxSize uint32 `long:"blockmaxsize" description:"Maximum block size in bytes to be used when creating a block"` + BlockMinWeight uint32 `long:"blockminweight" description:"Mininum block weight to be used when creating a block"` + BlockMaxWeight uint32 `long:"blockmaxweight" description:"Maximum block weight to be used when creating a block"` BlockPrioritySize uint32 `long:"blockprioritysize" description:"Size in bytes for high-priority/low-fee transactions when creating a block"` UserAgentComments []string `long:"uacomment" description:"Comment to add to the user agent -- See BIP 14 for more information."` NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support"` @@ -407,6 +413,8 @@ func loadConfig() (*config, []string, error) { FreeTxRelayLimit: defaultFreeTxRelayLimit, BlockMinSize: defaultBlockMinSize, BlockMaxSize: defaultBlockMaxSize, + BlockMinWeight: defaultBlockMinWeight, + BlockMaxWeight: defaultBlockMaxWeight, BlockPrioritySize: mempool.DefaultBlockPrioritySize, MaxOrphanTxs: defaultMaxOrphanTransactions, SigCacheMaxSize: defaultSigCacheMaxSize, @@ -531,8 +539,8 @@ func loadConfig() (*config, []string, error) { cfg.DisableDNSSeed = true } if numNets > 1 { - str := "%s: The testnet, regtest, and simnet params can't be " + - "used together -- choose one of the three" + str := "%s: The testnet, regtest, segnet, and simnet params " + + "can't be used together -- choose one of the four" err := fmt.Errorf(str, funcName) fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, usageMessage) @@ -723,7 +731,20 @@ func loadConfig() (*config, []string, error) { return nil, nil, err } - // Limit the max orphan count to a sane value. + // Limit the max block weight to a sane value. + if cfg.BlockMaxWeight < blockMaxWeightMin || + cfg.BlockMaxWeight > blockMaxWeightMax { + + str := "%s: The blockmaxweight option must be in between %d " + + "and %d -- parsed [%d]" + err := fmt.Errorf(str, funcName, blockMaxWeightMin, + blockMaxWeightMax, cfg.BlockMaxWeight) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Limit the max orphan count to a sane vlue. if cfg.MaxOrphanTxs < 0 { str := "%s: The maxorphantx option may not be less than 0 " + "-- parsed [%d]" @@ -736,6 +757,24 @@ func loadConfig() (*config, []string, error) { // Limit the block priority and minimum block sizes to max block size. cfg.BlockPrioritySize = minUint32(cfg.BlockPrioritySize, cfg.BlockMaxSize) cfg.BlockMinSize = minUint32(cfg.BlockMinSize, cfg.BlockMaxSize) + cfg.BlockMinWeight = minUint32(cfg.BlockMinWeight, cfg.BlockMaxWeight) + + switch { + // If the max block size isn't set, but the max weight is, then we'll + // set the limit for the max block size to a safe limit so weight takes + // precedence. + case cfg.BlockMaxSize == defaultBlockMaxSize && + cfg.BlockMaxWeight != defaultBlockMaxWeight: + + cfg.BlockMaxSize = blockchain.MaxBlockBaseSize - 1000 + + // If the max block weight isn't set, but the block size is, then we'll + // scale the set weight accordingly based on the max block size value. + case cfg.BlockMaxSize != defaultBlockMaxSize && + cfg.BlockMaxWeight == defaultBlockMaxWeight: + + cfg.BlockMaxWeight = cfg.BlockMaxSize * blockchain.WitnessScaleFactor + } // Look for illegal characters in the user agent comments. for _, uaComment := range cfg.UserAgentComments { diff --git a/mining/mining.go b/mining/mining.go index be6cecc79..e8a039fed 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -197,9 +197,9 @@ type BlockTemplate struct { // sum of the fees of all other transactions. Fees []int64 - // SigOpCounts contains the number of signature operations each + // SigOpCosts contains the number of signature operations each // transaction in the generated template performs. - SigOpCounts []int64 + SigOpCosts []int64 // Height is the height at which the block template connects to the main // chain. @@ -210,6 +210,12 @@ 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 view to viewA. The result is that @@ -347,6 +353,7 @@ type BlkTmplGenerator struct { chain *blockchain.BlockChain timeSource blockchain.MedianTimeSource sigCache *txscript.SigCache + hashCache *txscript.HashCache } // NewBlkTmplGenerator returns a new block template generator for the given @@ -358,7 +365,8 @@ type BlkTmplGenerator struct { func NewBlkTmplGenerator(policy *Policy, params *chaincfg.Params, txSource TxSource, chain *blockchain.BlockChain, timeSource blockchain.MedianTimeSource, - sigCache *txscript.SigCache) *BlkTmplGenerator { + sigCache *txscript.SigCache, + hashCache *txscript.HashCache) *BlkTmplGenerator { return &BlkTmplGenerator{ policy: policy, @@ -367,6 +375,7 @@ func NewBlkTmplGenerator(policy *Policy, params *chaincfg.Params, chain: chain, timeSource: timeSource, sigCache: sigCache, + hashCache: hashCache, } } @@ -451,12 +460,13 @@ 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 { return nil, err } - numCoinbaseSigOps := int64(blockchain.CountSigOps(coinbaseTx)) + coinbaseSigOpCost := int64(blockchain.CountSigOps(coinbaseTx)) * blockchain.WitnessScaleFactor // Get the current source transactions and create a priority queue to // hold the transactions which are ready for inclusion into a block @@ -490,9 +500,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)) - txSigOpCounts := make([]int64, 0, len(sourceTxns)) + txSigOpCosts := make([]int64, 0, len(sourceTxns)) txFees = append(txFees, -1) // Updated once known - txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps) + txSigOpCosts = append(txSigOpCosts, coinbaseSigOpCost) log.Debugf("Considering %d transactions for inclusion to new block", len(sourceTxns)) @@ -570,6 +580,7 @@ mempoolLoop: nextBlockHeight) // Calculate the fee in Satoshi/kB. + // TODO(roasbeef): cost accounting by weight prioItem.feePerKB = txDesc.FeePerKB prioItem.fee = txDesc.Fee @@ -591,10 +602,14 @@ 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. - blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize()) - blockSigOps := numCoinbaseSigOps + blockWeight := uint32((blockHeaderOverhead * (blockchain.WitnessScaleFactor - 1)) + blockchain.GetTransactionWeight(coinbaseTx)) + blockSigOpCost := coinbaseSigOpCost totalFees := int64(0) + // TODO(roasbeef): should be guarded by version bits state check + var witnessIncluded bool + includeWitness := true + // Choose which transactions make it into the block. for priorityQueue.Len() > 0 { // Grab the highest priority (or highest fee per kilobyte @@ -602,45 +617,73 @@ 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 tx.HasWitness() && !segwitActive: + 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: + // 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 + } + + witnessIncluded = true + } + // Grab any transactions which depend on this one. deps := dependers[*tx.Hash()] // Enforce maximum block size. Also check for overflow. - txSize := uint32(tx.MsgTx().SerializeSize()) - blockPlusTxSize := blockSize + txSize - if blockPlusTxSize < blockSize || - blockPlusTxSize >= g.policy.BlockMaxSize { + txWeight := uint32(blockchain.GetTransactionWeight(tx)) + blockPlusTxWeight := uint32(blockWeight + txWeight) + if blockPlusTxWeight < blockWeight || + blockPlusTxWeight >= g.policy.BlockMaxWeight { log.Tracef("Skipping tx %s because it would exceed "+ - "the max block size", tx.Hash()) + "the max block weight", tx.Hash()) logSkippedDeps(tx, deps) continue } - // 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) + // Enforce maximum signature operation cost per block. Also + // check for overflow. + sigOpCost, err := blockchain.GetSigOpCost(tx, false, + blockUtxos, true, includeWitness) if err != nil { log.Tracef("Skipping tx %s due to error in "+ - "CountP2SHSigOps: %v", tx.Hash(), err) + "GetSigOpCost: %v", tx.Hash(), err) logSkippedDeps(tx, deps) continue } - numSigOps += int64(numP2SHSigOps) - if blockSigOps+numSigOps < blockSigOps || - blockSigOps+numSigOps > blockchain.MaxSigOpsPerBlock { - log.Tracef("Skipping tx %s because it would exceed "+ - "the maximum sigops per block (p2sh)", - tx.Hash()) + if blockSigOpCost+int64(sigOpCost) < blockSigOpCost || + blockSigOpCost+int64(sigOpCost) > blockchain.MaxBlockSigOpsCost { + log.Tracef("Skipping tx %s because it would "+ + "exceed the maximum sigops per block", tx.Hash()) logSkippedDeps(tx, deps) continue } @@ -649,13 +692,13 @@ mempoolLoop: // minimum block size. if sortedByFee && prioItem.feePerKB < int64(g.policy.TxMinFreeFee) && - blockPlusTxSize >= g.policy.BlockMinSize { + 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) + 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) logSkippedDeps(tx, deps) continue } @@ -663,13 +706,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 && (blockPlusTxSize >= g.policy.BlockPrioritySize || + if !sortedByFee && (blockPlusTxWeight >= g.policy.BlockPrioritySize || prioItem.priority <= MinHighPriority) { - log.Tracef("Switching to sort by fees per kilobyte "+ - "blockSize %d >= BlockPrioritySize %d || "+ - "priority %.2f <= minHighPriority %.2f", - blockPlusTxSize, g.policy.BlockPrioritySize, + log.Tracef("Switching to sort by fees per "+ + "kilobyte blockSize %d >= BlockPrioritySize "+ + "%d || priority %.2f <= minHighPriority %.2f", + blockPlusTxWeight, g.policy.BlockPrioritySize, prioItem.priority, MinHighPriority) sortedByFee = true @@ -677,11 +720,11 @@ mempoolLoop: // Put the transaction back into the priority queue and // skip it so it is re-priortized by fees if it won't - // fit into the high-priority section or the priority is - // too low. Otherwise this transaction will be the + // fit into the high-priority section or the priority + // 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 blockPlusTxSize > g.policy.BlockPrioritySize || + if blockPlusTxWeight > g.policy.BlockPrioritySize || prioItem.priority < MinHighPriority { heap.Push(priorityQueue, prioItem) @@ -700,11 +743,11 @@ mempoolLoop: continue } err = blockchain.ValidateTransactionScripts(tx, blockUtxos, - txscript.StandardVerifyFlags, g.sigCache) + txscript.StandardVerifyFlags, g.sigCache, + g.hashCache) if err != nil { log.Tracef("Skipping tx %s due to error in "+ - "ValidateTransactionScripts: %v", tx.Hash(), - err) + "ValidateTransactionScripts: %v", tx.Hash(), err) logSkippedDeps(tx, deps) continue } @@ -719,11 +762,11 @@ mempoolLoop: // save the fees and signature operation counts to the block // template. blockTxns = append(blockTxns, tx) - blockSize += txSize - blockSigOps += numSigOps + blockWeight += txWeight + blockSigOpCost += int64(sigOpCost) totalFees += prioItem.fee txFees = append(txFees, prioItem.fee) - txSigOpCounts = append(txSigOpCounts, numSigOps) + txSigOpCosts = append(txSigOpCosts, int64(sigOpCost)) log.Tracef("Adding tx %s (priority %.2f, feePerKB %.2f)", prioItem.tx.Hash(), prioItem.priority, prioItem.feePerKB) @@ -742,13 +785,18 @@ mempoolLoop: } // Now that the actual transactions have been selected, update the - // block size for the real transaction count and coinbase value with + // block weight for the real transaction count and coinbase value with // the total fees accordingly. - blockSize -= wire.MaxVarIntPayload - - uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) + blockWeight -= wire.MaxVarIntPayload - + (uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) * + (blockchain.WitnessScaleFactor - 1)) coinbaseTx.MsgTx().TxOut[0].Value += totalFees txFees[0] = -totalFees + // TODO(roasbeef): add witness commitment + if witnessIncluded { + } + // 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. @@ -766,7 +814,7 @@ mempoolLoop: } // Create a new block ready to be solved. - merkles := blockchain.BuildMerkleTreeStore(blockTxns) + merkles := blockchain.BuildMerkleTreeStore(blockTxns, false) var msgBlock wire.MsgBlock msgBlock.Header = wire.BlockHeader{ Version: nextBlockVersion, @@ -790,15 +838,15 @@ mempoolLoop: return nil, err } - 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)) + 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)) return &BlockTemplate{ Block: &msgBlock, Fees: txFees, - SigOpCounts: txSigOpCounts, + SigOpCosts: txSigOpCosts, Height: nextBlockHeight, ValidPayAddress: payToAddress != nil, }, nil @@ -852,7 +900,7 @@ func (g *BlkTmplGenerator) UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight // Recalculate the merkle root with the updated extra nonce. block := btcutil.NewBlock(msgBlock) - merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) + merkles := blockchain.BuildMerkleTreeStore(block.Transactions(), false) msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1] return nil } diff --git a/mining/policy.go b/mining/policy.go index 5b4043515..54b5305f0 100644 --- a/mining/policy.go +++ b/mining/policy.go @@ -21,12 +21,20 @@ const ( // the generation of block templates. See the documentation for // NewBlockTemplate for more details on each of these parameters are used. type Policy struct { - // BlockMinSize is the minimum block size in bytes to be used when + // 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 + // a block template. BlockMinSize uint32 - // BlockMaxSize is the maximum block size in bytes to be used when - // generating a block template. + // BlockMaxSize is the maximum block size to be used when generating a + // block template. BlockMaxSize uint32 // BlockPrioritySize is the size in bytes for high-priority / low-fee diff --git a/server.go b/server.go index 86ee58522..cf8db2640 100644 --- a/server.go +++ b/server.go @@ -173,6 +173,7 @@ type server struct { addrManager *addrmgr.AddrManager connManager *connmgr.ConnManager sigCache *txscript.SigCache + hashCache *txscript.HashCache rpcServer *rpcServer blockManager *blockManager txMemPool *mempool.TxPool @@ -2412,6 +2413,7 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param timeSource: blockchain.NewMedianTime(), services: services, sigCache: txscript.NewSigCache(cfg.SigCacheMaxSize), + hashCache: txscript.NewHashCache(cfg.SigCacheMaxSize), } // Create the transaction and address indexes if needed. @@ -2459,7 +2461,7 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param FreeTxRelayLimit: cfg.FreeTxRelayLimit, MaxOrphanTxs: cfg.MaxOrphanTxs, MaxOrphanTxSize: defaultMaxOrphanTxSize, - MaxSigOpsPerTx: blockchain.MaxSigOpsPerBlock / 5, + MaxSigOpCostPerTx: blockchain.MaxBlockSigOpsCost / 4, MinRelayTxFee: cfg.minRelayTxFee, MaxTxVersion: 2, }, @@ -2483,6 +2485,8 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param // NOTE: The CPU miner relies on the mempool, so the mempool has to be // created before calling the function to create the CPU miner. policy := mining.Policy{ + BlockMinWeight: cfg.BlockMinWeight, + BlockMaxWeight: cfg.BlockMaxWeight, BlockMinSize: cfg.BlockMinSize, BlockMaxSize: cfg.BlockMaxSize, BlockPrioritySize: cfg.BlockPrioritySize, @@ -2490,7 +2494,7 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param } blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, s.chainParams, s.txMemPool, s.blockManager.chain, s.timeSource, - s.sigCache) + s.sigCache, s.hashCache) s.cpuMiner = cpuminer.New(&cpuminer.Config{ ChainParams: chainParams, BlockTemplateGenerator: blockTemplateGenerator,