From a4aa131dd5459017bfe329481b3cb9cc8bb01e42 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 22 Nov 2015 01:02:28 -0600 Subject: [PATCH] mining: Refactor policy into its own struct. This introduces the concept of a mining policy struct which is used to control block template generation instead of directly accessing the config struct. This is a step toward decoupling the mining code from the internals of btcd. Ultimately the intent is to create a separate mining package. --- cpuminer.go | 8 ++++-- mining.go | 81 +++++++++++++++++++++++++++++++++------------------- rpcserver.go | 9 +++--- server.go | 12 ++++++-- 4 files changed, 72 insertions(+), 38 deletions(-) diff --git a/cpuminer.go b/cpuminer.go index e6fc08396..4f3f67e9f 100644 --- a/cpuminer.go +++ b/cpuminer.go @@ -52,6 +52,7 @@ var ( // system which is typically sufficient. type CPUMiner struct { sync.Mutex + policy *miningPolicy server *server numWorkers uint32 started bool @@ -302,7 +303,7 @@ out: // Create a new block template using the available transactions // in the memory pool as a source of transactions to potentially // include in the block. - template, err := NewBlockTemplate(m.server, payToAddr) + template, err := NewBlockTemplate(m.policy, m.server, payToAddr) m.submitBlockLock.Unlock() if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ @@ -564,7 +565,7 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*wire.ShaHash, error) { // Create a new block template using the available transactions // in the memory pool as a source of transactions to potentially // include in the block. - template, err := NewBlockTemplate(m.server, payToAddr) + template, err := NewBlockTemplate(m.policy, m.server, payToAddr) m.submitBlockLock.Unlock() if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ @@ -599,8 +600,9 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*wire.ShaHash, error) { // newCPUMiner returns a new instance of a CPU miner for the provided server. // Use Start to begin the mining process. See the documentation for CPUMiner // type for more details. -func newCPUMiner(s *server) *CPUMiner { +func newCPUMiner(policy *miningPolicy, s *server) *CPUMiner { return &CPUMiner{ + policy: policy, server: s, numWorkers: defaultNumWorkers, updateNumWorkers: make(chan struct{}), diff --git a/mining.go b/mining.go index c06b87728..b5445e68c 100644 --- a/mining.go +++ b/mining.go @@ -40,6 +40,28 @@ const ( coinbaseFlags = "/P2SH/btcd/" ) +// miningPolicy houses the policy (configuration parameters) which is used to +// control the generation of block templates. See the documentation for +// NewBlockTemplate for more details on each of these parameters are used. +type miningPolicy struct { + // BlockMinSize is the minimum block size in bytes 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 uint32 + + // BlockPrioritySize is the size in bytes for high-priority / low-fee + // transactions to be used when generating a block template. + BlockPrioritySize uint32 + + // TxMinFreeFee is the minimum fee in Satoshi/kB that is required for a + // transaction to be treated as free for mining purposes (block template + // generation). This value is in Satoshi/1000 bytes. + TxMinFreeFee btcutil.Amount +} + // txPrioItem houses a transaction along with extra information that allows the // transaction to be prioritized and track dependencies on other transactions // which have not been mined into a block yet. @@ -320,53 +342,54 @@ func medianAdjustedTime(chainState *chainState, timeSource blockchain.MedianTime // amounts, older inputs, and small sizes have the highest priority. Second, a // fee per kilobyte is calculated for each transaction. Transactions with a // higher fee per kilobyte are preferred. Finally, the block generation related -// configuration options are all taken into account. +// policy settings are all taken into account. // // Transactions which only spend outputs from other transactions already in the // block chain are immediately added to a priority queue which either // prioritizes based on the priority (then fee per kilobyte) or the fee per // kilobyte (then priority) depending on whether or not the BlockPrioritySize -// configuration option allots space for high-priority transactions. -// Transactions which spend outputs from other transactions in the memory pool -// are added to a dependency map so they can be added to the priority queue once -// the transactions they depend on have been included. +// policy setting allots space for high-priority transactions. Transactions +// which spend outputs from other transactions in the memory pool are added to a +// dependency map so they can be added to the priority queue once the +// transactions they depend on have been included. // -// Once the high-priority area (if configured) has been filled with transactions, -// or the priority falls below what is considered high-priority, the priority -// queue is updated to prioritize by fees per kilobyte (then priority). +// Once the high-priority area (if configured) has been filled with +// transactions, or the priority falls below what is considered high-priority, +// the priority queue is updated to prioritize by fees per kilobyte (then +// priority). // -// When the fees per kilobyte drop below the TxMinFreeFee configuration option, -// the transaction will be skipped unless there is a BlockMinSize set, in which -// case the block will be filled with the low-fee/free transactions until the -// block size reaches that minimum size. +// When the fees per kilobyte drop below the TxMinFreeFee policy setting, the +// transaction will be skipped unless the BlockMinSize policy setting is +// nonzero, in which case the block will be filled with the low-fee/free +// transactions until the block size reaches that minimum size. // // Any transactions which would cause the block to exceed the BlockMaxSize -// configuration option, exceed the maximum allowed signature operations per -// block, or otherwise cause the block to be invalid are skipped. +// policy setting, exceed the maximum allowed signature operations per block, or +// otherwise cause the block to be invalid are skipped. // // Given the above, a block generated by this function is of the following form: // // ----------------------------------- -- -- // | Coinbase Transaction | | | // |-----------------------------------| | | -// | | | | ----- cfg.BlockPrioritySize +// | | | | ----- policy.BlockPrioritySize // | High-priority Transactions | | | // | | | | // |-----------------------------------| | -- // | | | // | | | -// | | |--- cfg.BlockMaxSize +// | | |--- policy.BlockMaxSize // | Transactions prioritized by fee | | -// | until <= cfg.TxMinFreeFee | | +// | until <= policy.TxMinFreeFee | | // | | | // | | | // | | | // |-----------------------------------| | // | Low-fee/Non high-priority (free) | | // | transactions (while block size | | -// | <= cfg.BlockMinSize) | | +// | <= policy.BlockMinSize) | | // ----------------------------------- -- -func NewBlockTemplate(server *server, payToAddress btcutil.Address) (*BlockTemplate, error) { +func NewBlockTemplate(policy *miningPolicy, server *server, payToAddress btcutil.Address) (*BlockTemplate, error) { blockManager := server.blockManager timeSource := server.timeSource chainState := &blockManager.chainState @@ -405,7 +428,7 @@ func NewBlockTemplate(server *server, payToAddress btcutil.Address) (*BlockTempl // whether or not there is an area allocated for high-priority // transactions. mempoolTxns := server.txMemPool.TxDescs() - sortedByFee := cfg.BlockPrioritySize == 0 + sortedByFee := policy.BlockPrioritySize == 0 priorityQueue := newTxPriorityQueue(len(mempoolTxns), sortedByFee) // Create a slice to hold the transactions to be included in the @@ -563,7 +586,7 @@ mempoolLoop: // Enforce maximum block size. Also check for overflow. txSize := uint32(tx.MsgTx().SerializeSize()) blockPlusTxSize := blockSize + txSize - if blockPlusTxSize < blockSize || blockPlusTxSize >= cfg.BlockMaxSize { + if blockPlusTxSize < blockSize || blockPlusTxSize >= policy.BlockMaxSize { minrLog.Tracef("Skipping tx %s because it would exceed "+ "the max block size", tx.Sha()) logSkippedDeps(tx, deps) @@ -601,14 +624,14 @@ mempoolLoop: // Skip free transactions once the block is larger than the // minimum block size. if sortedByFee && - prioItem.feePerKB < int64(cfg.minRelayTxFee) && - blockPlusTxSize >= cfg.BlockMinSize { + prioItem.feePerKB < int64(policy.TxMinFreeFee) && + blockPlusTxSize >= policy.BlockMinSize { minrLog.Tracef("Skipping tx %s with feePerKB %.2f "+ - "< minTxRelayFee %d and block size %d >= "+ + "< TxMinFreeFee %d and block size %d >= "+ "minBlockSize %d", tx.Sha(), prioItem.feePerKB, - cfg.minRelayTxFee, blockPlusTxSize, - cfg.BlockMinSize) + policy.TxMinFreeFee, blockPlusTxSize, + policy.BlockMinSize) logSkippedDeps(tx, deps) continue } @@ -616,13 +639,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 >= cfg.BlockPrioritySize || + if !sortedByFee && (blockPlusTxSize >= policy.BlockPrioritySize || prioItem.priority <= minHighPriority) { minrLog.Tracef("Switching to sort by fees per "+ "kilobyte blockSize %d >= BlockPrioritySize "+ "%d || priority %.2f <= minHighPriority %.2f", - blockPlusTxSize, cfg.BlockPrioritySize, + blockPlusTxSize, policy.BlockPrioritySize, prioItem.priority, minHighPriority) sortedByFee = true @@ -634,7 +657,7 @@ mempoolLoop: // 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 > cfg.BlockPrioritySize || + if blockPlusTxSize > policy.BlockPrioritySize || prioItem.priority < minHighPriority { heap.Push(priorityQueue, prioItem) diff --git a/rpcserver.go b/rpcserver.go index 60a758e46..192523fc1 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1518,7 +1518,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo // block template doesn't include the coinbase, so the caller // will ultimately create their own coinbase which pays to the // appropriate address(es). - blkTemplate, err := NewBlockTemplate(s.server, payAddr) + blkTemplate, err := NewBlockTemplate(s.policy, s.server, payAddr) if err != nil { return internalRPCError("Failed to create new block "+ "template: "+err.Error(), "") @@ -2708,8 +2708,7 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) { // Choose a payment address at random. payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] - - template, err := NewBlockTemplate(s.server, payToAddr) + template, err := NewBlockTemplate(s.policy, s.server, payToAddr) if err != nil { context := "Failed to create new block template" return nil, internalRPCError(err.Error(), context) @@ -3518,6 +3517,7 @@ func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{ type rpcServer struct { started int32 shutdown int32 + policy *miningPolicy server *server authsha [fastsha256.Size]byte limitauthsha [fastsha256.Size]byte @@ -3995,8 +3995,9 @@ func genCertPair(certFile, keyFile string) error { } // newRPCServer returns a new instance of the rpcServer struct. -func newRPCServer(listenAddrs []string, s *server) (*rpcServer, error) { +func newRPCServer(listenAddrs []string, policy *miningPolicy, s *server) (*rpcServer, error) { rpc := rpcServer{ + policy: policy, server: s, statusLines: make(map[int]string), workState: newWorkState(), diff --git a/server.go b/server.go index 2346d4de3..69e1269b6 100644 --- a/server.go +++ b/server.go @@ -2322,7 +2322,15 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param } s.blockManager = bm s.txMemPool = newTxMemPool(&s) - s.cpuMiner = newCPUMiner(&s) + + // Create the mining policy based on the configuration options. + policy := miningPolicy{ + BlockMinSize: cfg.BlockMinSize, + BlockMaxSize: cfg.BlockMaxSize, + BlockPrioritySize: cfg.BlockPrioritySize, + TxMinFreeFee: cfg.minRelayTxFee, + } + s.cpuMiner = newCPUMiner(&policy, &s) if cfg.AddrIndex { ai, err := newAddrIndexer(&s) @@ -2333,7 +2341,7 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param } if !cfg.DisableRPC { - s.rpcServer, err = newRPCServer(cfg.RPCListeners, &s) + s.rpcServer, err = newRPCServer(cfg.RPCListeners, &policy, &s) if err != nil { return nil, err }