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.
This commit is contained in:
Dave Collins 2015-11-22 01:02:28 -06:00
parent 2799ddf538
commit a4aa131dd5
4 changed files with 72 additions and 38 deletions

View file

@ -52,6 +52,7 @@ var (
// system which is typically sufficient. // system which is typically sufficient.
type CPUMiner struct { type CPUMiner struct {
sync.Mutex sync.Mutex
policy *miningPolicy
server *server server *server
numWorkers uint32 numWorkers uint32
started bool started bool
@ -302,7 +303,7 @@ out:
// Create a new block template using the available transactions // Create a new block template using the available transactions
// in the memory pool as a source of transactions to potentially // in the memory pool as a source of transactions to potentially
// include in the block. // include in the block.
template, err := NewBlockTemplate(m.server, payToAddr) template, err := NewBlockTemplate(m.policy, m.server, payToAddr)
m.submitBlockLock.Unlock() m.submitBlockLock.Unlock()
if err != nil { if err != nil {
errStr := fmt.Sprintf("Failed to create new block "+ 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 // Create a new block template using the available transactions
// in the memory pool as a source of transactions to potentially // in the memory pool as a source of transactions to potentially
// include in the block. // include in the block.
template, err := NewBlockTemplate(m.server, payToAddr) template, err := NewBlockTemplate(m.policy, m.server, payToAddr)
m.submitBlockLock.Unlock() m.submitBlockLock.Unlock()
if err != nil { if err != nil {
errStr := fmt.Sprintf("Failed to create new block "+ 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. // 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 // Use Start to begin the mining process. See the documentation for CPUMiner
// type for more details. // type for more details.
func newCPUMiner(s *server) *CPUMiner { func newCPUMiner(policy *miningPolicy, s *server) *CPUMiner {
return &CPUMiner{ return &CPUMiner{
policy: policy,
server: s, server: s,
numWorkers: defaultNumWorkers, numWorkers: defaultNumWorkers,
updateNumWorkers: make(chan struct{}), updateNumWorkers: make(chan struct{}),

View file

@ -40,6 +40,28 @@ const (
coinbaseFlags = "/P2SH/btcd/" 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 // txPrioItem houses a transaction along with extra information that allows the
// transaction to be prioritized and track dependencies on other transactions // transaction to be prioritized and track dependencies on other transactions
// which have not been mined into a block yet. // 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 // amounts, older inputs, and small sizes have the highest priority. Second, a
// fee per kilobyte is calculated for each transaction. Transactions with a // fee per kilobyte is calculated for each transaction. Transactions with a
// higher fee per kilobyte are preferred. Finally, the block generation related // 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 // Transactions which only spend outputs from other transactions already in the
// block chain are immediately added to a priority queue which either // block chain are immediately added to a priority queue which either
// prioritizes based on the priority (then fee per kilobyte) or the fee per // prioritizes based on the priority (then fee per kilobyte) or the fee per
// kilobyte (then priority) depending on whether or not the BlockPrioritySize // kilobyte (then priority) depending on whether or not the BlockPrioritySize
// configuration option allots space for high-priority transactions. // policy setting allots space for high-priority transactions. Transactions
// Transactions which spend outputs from other transactions in the memory pool // which spend outputs from other transactions in the memory pool are added to a
// are added to a dependency map so they can be added to the priority queue once // dependency map so they can be added to the priority queue once the
// the transactions they depend on have been included. // transactions they depend on have been included.
// //
// Once the high-priority area (if configured) has been filled with transactions, // Once the high-priority area (if configured) has been filled with
// or the priority falls below what is considered high-priority, the priority // transactions, or the priority falls below what is considered high-priority,
// queue is updated to prioritize by fees per kilobyte (then 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, // When the fees per kilobyte drop below the TxMinFreeFee policy setting, the
// the transaction will be skipped unless there is a BlockMinSize set, in which // transaction will be skipped unless the BlockMinSize policy setting is
// case the block will be filled with the low-fee/free transactions until the // nonzero, in which case the block will be filled with the low-fee/free
// block size reaches that minimum size. // transactions until the block size reaches that minimum size.
// //
// Any transactions which would cause the block to exceed the BlockMaxSize // Any transactions which would cause the block to exceed the BlockMaxSize
// configuration option, exceed the maximum allowed signature operations per // policy setting, exceed the maximum allowed signature operations per block, or
// block, or otherwise cause the block to be invalid are skipped. // otherwise cause the block to be invalid are skipped.
// //
// Given the above, a block generated by this function is of the following form: // Given the above, a block generated by this function is of the following form:
// //
// ----------------------------------- -- -- // ----------------------------------- -- --
// | Coinbase Transaction | | | // | Coinbase Transaction | | |
// |-----------------------------------| | | // |-----------------------------------| | |
// | | | | ----- cfg.BlockPrioritySize // | | | | ----- policy.BlockPrioritySize
// | High-priority Transactions | | | // | High-priority Transactions | | |
// | | | | // | | | |
// |-----------------------------------| | -- // |-----------------------------------| | --
// | | | // | | |
// | | | // | | |
// | | |--- cfg.BlockMaxSize // | | |--- policy.BlockMaxSize
// | Transactions prioritized by fee | | // | Transactions prioritized by fee | |
// | until <= cfg.TxMinFreeFee | | // | until <= policy.TxMinFreeFee | |
// | | | // | | |
// | | | // | | |
// | | | // | | |
// |-----------------------------------| | // |-----------------------------------| |
// | Low-fee/Non high-priority (free) | | // | Low-fee/Non high-priority (free) | |
// | transactions (while block size | | // | 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 blockManager := server.blockManager
timeSource := server.timeSource timeSource := server.timeSource
chainState := &blockManager.chainState 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 // whether or not there is an area allocated for high-priority
// transactions. // transactions.
mempoolTxns := server.txMemPool.TxDescs() mempoolTxns := server.txMemPool.TxDescs()
sortedByFee := cfg.BlockPrioritySize == 0 sortedByFee := policy.BlockPrioritySize == 0
priorityQueue := newTxPriorityQueue(len(mempoolTxns), sortedByFee) priorityQueue := newTxPriorityQueue(len(mempoolTxns), sortedByFee)
// Create a slice to hold the transactions to be included in the // 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. // Enforce maximum block size. Also check for overflow.
txSize := uint32(tx.MsgTx().SerializeSize()) txSize := uint32(tx.MsgTx().SerializeSize())
blockPlusTxSize := blockSize + txSize blockPlusTxSize := blockSize + txSize
if blockPlusTxSize < blockSize || blockPlusTxSize >= cfg.BlockMaxSize { if blockPlusTxSize < blockSize || blockPlusTxSize >= policy.BlockMaxSize {
minrLog.Tracef("Skipping tx %s because it would exceed "+ minrLog.Tracef("Skipping tx %s because it would exceed "+
"the max block size", tx.Sha()) "the max block size", tx.Sha())
logSkippedDeps(tx, deps) logSkippedDeps(tx, deps)
@ -601,14 +624,14 @@ mempoolLoop:
// Skip free transactions once the block is larger than the // Skip free transactions once the block is larger than the
// minimum block size. // minimum block size.
if sortedByFee && if sortedByFee &&
prioItem.feePerKB < int64(cfg.minRelayTxFee) && prioItem.feePerKB < int64(policy.TxMinFreeFee) &&
blockPlusTxSize >= cfg.BlockMinSize { blockPlusTxSize >= policy.BlockMinSize {
minrLog.Tracef("Skipping tx %s with feePerKB %.2f "+ 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, "minBlockSize %d", tx.Sha(), prioItem.feePerKB,
cfg.minRelayTxFee, blockPlusTxSize, policy.TxMinFreeFee, blockPlusTxSize,
cfg.BlockMinSize) policy.BlockMinSize)
logSkippedDeps(tx, deps) logSkippedDeps(tx, deps)
continue continue
} }
@ -616,13 +639,13 @@ mempoolLoop:
// Prioritize by fee per kilobyte once the block is larger than // Prioritize by fee per kilobyte once the block is larger than
// the priority size or there are no more high-priority // the priority size or there are no more high-priority
// transactions. // transactions.
if !sortedByFee && (blockPlusTxSize >= cfg.BlockPrioritySize || if !sortedByFee && (blockPlusTxSize >= policy.BlockPrioritySize ||
prioItem.priority <= minHighPriority) { prioItem.priority <= minHighPriority) {
minrLog.Tracef("Switching to sort by fees per "+ minrLog.Tracef("Switching to sort by fees per "+
"kilobyte blockSize %d >= BlockPrioritySize "+ "kilobyte blockSize %d >= BlockPrioritySize "+
"%d || priority %.2f <= minHighPriority %.2f", "%d || priority %.2f <= minHighPriority %.2f",
blockPlusTxSize, cfg.BlockPrioritySize, blockPlusTxSize, policy.BlockPrioritySize,
prioItem.priority, minHighPriority) prioItem.priority, minHighPriority)
sortedByFee = true sortedByFee = true
@ -634,7 +657,7 @@ mempoolLoop:
// too low. Otherwise this transaction will be the // too low. Otherwise this transaction will be the
// final one in the high-priority section, so just fall // final one in the high-priority section, so just fall
// though to the code below so it is added now. // though to the code below so it is added now.
if blockPlusTxSize > cfg.BlockPrioritySize || if blockPlusTxSize > policy.BlockPrioritySize ||
prioItem.priority < minHighPriority { prioItem.priority < minHighPriority {
heap.Push(priorityQueue, prioItem) heap.Push(priorityQueue, prioItem)

View file

@ -1518,7 +1518,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo
// block template doesn't include the coinbase, so the caller // block template doesn't include the coinbase, so the caller
// will ultimately create their own coinbase which pays to the // will ultimately create their own coinbase which pays to the
// appropriate address(es). // appropriate address(es).
blkTemplate, err := NewBlockTemplate(s.server, payAddr) blkTemplate, err := NewBlockTemplate(s.policy, s.server, payAddr)
if err != nil { if err != nil {
return internalRPCError("Failed to create new block "+ return internalRPCError("Failed to create new block "+
"template: "+err.Error(), "") "template: "+err.Error(), "")
@ -2708,8 +2708,7 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) {
// Choose a payment address at random. // Choose a payment address at random.
payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))]
template, err := NewBlockTemplate(s.policy, s.server, payToAddr)
template, err := NewBlockTemplate(s.server, payToAddr)
if err != nil { if err != nil {
context := "Failed to create new block template" context := "Failed to create new block template"
return nil, internalRPCError(err.Error(), context) return nil, internalRPCError(err.Error(), context)
@ -3518,6 +3517,7 @@ func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{
type rpcServer struct { type rpcServer struct {
started int32 started int32
shutdown int32 shutdown int32
policy *miningPolicy
server *server server *server
authsha [fastsha256.Size]byte authsha [fastsha256.Size]byte
limitauthsha [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. // 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{ rpc := rpcServer{
policy: policy,
server: s, server: s,
statusLines: make(map[int]string), statusLines: make(map[int]string),
workState: newWorkState(), workState: newWorkState(),

View file

@ -2322,7 +2322,15 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param
} }
s.blockManager = bm s.blockManager = bm
s.txMemPool = newTxMemPool(&s) 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 { if cfg.AddrIndex {
ai, err := newAddrIndexer(&s) ai, err := newAddrIndexer(&s)
@ -2333,7 +2341,7 @@ func newServer(listenAddrs []string, db database.Db, chainParams *chaincfg.Param
} }
if !cfg.DisableRPC { if !cfg.DisableRPC {
s.rpcServer, err = newRPCServer(cfg.RPCListeners, &s) s.rpcServer, err = newRPCServer(cfg.RPCListeners, &policy, &s)
if err != nil { if err != nil {
return nil, err return nil, err
} }