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.
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{}),

View file

@ -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)

View file

@ -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(),

View file

@ -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
}