diff --git a/cpuminer.go b/cpuminer.go index 901285ef..f1150c51 100644 --- a/cpuminer.go +++ b/cpuminer.go @@ -60,6 +60,7 @@ var ( type CPUMiner struct { sync.Mutex policy *miningPolicy + txSource TxSource server *server numWorkers uint32 started bool @@ -212,7 +213,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, // Initial state. lastGenerated := time.Now() - lastTxUpdate := m.server.txMemPool.LastUpdated() + lastTxUpdate := m.txSource.LastUpdated() hashesCompleted := uint64(0) // Note that the entire extra nonce range is iterated and the offset is @@ -244,9 +245,10 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, // has been updated since the block template was // generated and it has been at least 3 seconds, // or if it's been one minute. - if (lastTxUpdate != m.server.txMemPool.LastUpdated() && + if (lastTxUpdate != m.txSource.LastUpdated() && time.Now().After(lastGenerated.Add(3*time.Second))) || time.Now().After(lastGenerated.Add(60*time.Second)) { + return false } @@ -327,8 +329,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.policy, m.server.txMemPool, - payToAddr) + template, err := NewBlockTemplate(m.policy, m.server, payToAddr) m.submitBlockLock.Unlock() if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ @@ -607,8 +608,7 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, 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.policy, m.server.txMemPool, - payToAddr) + template, err := NewBlockTemplate(m.policy, m.server, payToAddr) m.submitBlockLock.Unlock() if err != nil { errStr := fmt.Sprintf("Failed to create new block "+ @@ -652,6 +652,7 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) { func newCPUMiner(policy *miningPolicy, s *server) *CPUMiner { return &CPUMiner{ policy: policy, + txSource: s.txMemPool, server: s, numWorkers: defaultNumWorkers, updateNumWorkers: make(chan struct{}), diff --git a/mempool.go b/mempool.go index cd0e3921..eec359ea 100644 --- a/mempool.go +++ b/mempool.go @@ -74,22 +74,6 @@ const ( maxNullDataOutputs = 4 ) -// TxDesc is a descriptor containing a transaction in the mempool and the -// metadata we store about it. -type TxDesc struct { - Tx *dcrutil.Tx // Transaction. - Type stake.TxType // Transcation type. - Added time.Time // Time when added to pool. - Height int64 // Blockheight when added to pool. - Fee int64 // Transaction fees. - StartingPriority float64 // Priority when added to the pool. -} - -// GetType returns what TxType a given TxDesc is. -func (td *TxDesc) GetType() stake.TxType { - return td.Type -} - // VoteTx is a struct describing a block vote (SSGen). type VoteTx struct { SsgenHash chainhash.Hash // Vote @@ -97,6 +81,16 @@ type VoteTx struct { Vote bool } +// mempoolTxDesc is a descriptor containing a transaction in the mempool along +// with additional metadata. +type mempoolTxDesc struct { + miningTxDesc + + // StartingPriority is the priority of the transaction when it was added + // to the pool. + StartingPriority float64 +} + // txMemPool is used as a source of transactions that need to be mined into // blocks and relayed to other peers. It is safe for concurrent access from // multiple peers. @@ -106,7 +100,7 @@ type txMemPool struct { sync.RWMutex server *server - pool map[chainhash.Hash]*TxDesc + pool map[chainhash.Hash]*mempoolTxDesc orphans map[chainhash.Hash]*dcrutil.Tx orphansByPrev map[chainhash.Hash]map[chainhash.Hash]*dcrutil.Tx addrindex map[string]map[chainhash.Hash]struct{} // maps address to txs @@ -358,6 +352,9 @@ func (mp *txMemPool) SortParentsByVotes(currentTopBlock chainhash.Hash, return sortedBlocks, err } +// Ensure the txMemPool type implements the mining.TxSource interface. +var _ TxSource = (*txMemPool)(nil) + // removeOrphan is the internal function which implements the public // RemoveOrphan. See the comment for RemoveOrphan for more details. // @@ -661,12 +658,14 @@ func (mp *txMemPool) addTransaction(txStore blockchain.TxStore, tx *dcrutil.Tx, // Add the transaction to the pool and mark the referenced outpoints // as spent by the pool. - mp.pool[*tx.Sha()] = &TxDesc{ - Tx: tx, - Type: txType, - Added: time.Now(), - Height: height, - Fee: fee, + mp.pool[*tx.Sha()] = &mempoolTxDesc{ + miningTxDesc: miningTxDesc{ + Tx: tx, + Type: txType, + Added: time.Now(), + Height: height, + Fee: fee, + }, StartingPriority: calcPriority(tx.MsgTx(), txStore, height), } for _, txIn := range tx.MsgTx().TxIn { @@ -952,8 +951,8 @@ func (mp *txMemPool) FilterTransactionsByAddress( if txs, exists := mp.addrindex[addr.EncodeAddress()]; exists { addressTxs := make([]*dcrutil.Tx, 0, len(txs)) for txHash := range txs { - if tx, exists := mp.pool[txHash]; exists { - addressTxs = append(addressTxs, tx.Tx) + if txD, exists := mp.pool[txHash]; exists { + addressTxs = append(addressTxs, txD.Tx) } } return addressTxs, nil @@ -1072,7 +1071,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, if txType == stake.TxTypeSSGen { ssGenAlreadyFound := 0 for _, mpTx := range mp.pool { - if mpTx.GetType() == stake.TxTypeSSGen { + if mpTx.Type == stake.TxTypeSSGen { if mpTx.Tx.MsgTx().TxIn[1].PreviousOutPoint == tx.MsgTx().TxIn[1].PreviousOutPoint { ssGenAlreadyFound++ @@ -1090,7 +1089,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, if txType == stake.TxTypeSSRtx { for _, mpTx := range mp.pool { - if mpTx.GetType() == stake.TxTypeSSRtx { + if mpTx.Type == stake.TxTypeSSRtx { if mpTx.Tx.MsgTx().TxIn[0].PreviousOutPoint == tx.MsgTx().TxIn[0].PreviousOutPoint { str := fmt.Sprintf("transaction %v in the pool "+ @@ -1645,11 +1644,11 @@ func (mp *txMemPool) TxShas() []*chainhash.Hash { // The descriptors are to be treated as read only. // // This function is safe for concurrent access. -func (mp *txMemPool) TxDescs() []*TxDesc { +func (mp *txMemPool) TxDescs() []*mempoolTxDesc { mp.RLock() defer mp.RUnlock() - descs := make([]*TxDesc, len(mp.pool)) + descs := make([]*mempoolTxDesc, len(mp.pool)) i := 0 for _, desc := range mp.pool { descs[i] = desc @@ -1659,6 +1658,25 @@ func (mp *txMemPool) TxDescs() []*TxDesc { return descs } +// MiningDescs returns a slice of mining descriptors for all the transactions +// in the pool. +// +// This is part of the TxSource interface implementation and is safe for +// concurrent access as required by the interface contract. +func (mp *txMemPool) MiningDescs() []*miningTxDesc { + mp.RLock() + defer mp.RUnlock() + + descs := make([]*miningTxDesc, len(mp.pool)) + i := 0 + for _, desc := range mp.pool { + descs[i] = &desc.miningTxDesc + i++ + } + + return descs +} + // LastUpdated returns the last time a transaction was added to or removed from // the main pool. It does not include the orphan pool. // @@ -1691,7 +1709,7 @@ func (mp *txMemPool) CheckIfTxsExist(hashes []chainhash.Hash) bool { func newTxMemPool(server *server) *txMemPool { memPool := &txMemPool{ server: server, - pool: make(map[chainhash.Hash]*TxDesc), + pool: make(map[chainhash.Hash]*mempoolTxDesc), orphans: make(map[chainhash.Hash]*dcrutil.Tx), orphansByPrev: make(map[chainhash.Hash]map[chainhash.Hash]*dcrutil.Tx), outpoints: make(map[wire.OutPoint]*dcrutil.Tx), diff --git a/mining.go b/mining.go index 3f820fa4..2cbbd8b2 100644 --- a/mining.go +++ b/mining.go @@ -40,6 +40,45 @@ const ( kilobyte = 1000 ) +// miningTxDesc is a descriptor about a transaction in a transaction source +// along with additional metadata. +type miningTxDesc struct { + // Tx is the transaction associated with the entry. + Tx *dcrutil.Tx + + // Type is the type of the transaction associated with the entry. + Type stake.TxType + + // Added is the time when the entry was added to the source pool. + Added time.Time + + // Height is the block height when the entry was added to the the source + // pool. + Height int64 + + // Fee is the total fee the transaction associated with the entry pays. + Fee int64 +} + +// TxSource represents a source of transactions to consider for inclusion in +// new blocks. +// +// The interface contract requires that all of these methods are safe for +// concurrent access with respect to the source. +type TxSource interface { + // LastUpdated returns the last time a transaction was added to or + // removed from the source pool. + LastUpdated() time.Time + + // MiningDescs returns a slice of mining descriptors for all the + // transactions in the source pool. + MiningDescs() []*miningTxDesc + + // HaveTransaction returns whether or not the passed transaction hash + // exists in the source pool. + HaveTransaction(hash *chainhash.Hash) bool +} + // 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. @@ -74,7 +113,7 @@ type txPrioItem struct { // dependsOn holds a map of transaction hashes which this one depends // on. It will only be set when the transaction references other - // transactions in the memory pool and hence must come after them in + // transactions in the source pool and hence must come after them in // a block. dependsOn map[chainhash.Hash]struct{} } @@ -1049,7 +1088,7 @@ func handleCreatedBlockTemplate(blockTemplate *BlockTemplate, } // NewBlockTemplate returns a new block template that is ready to be solved -// using the transactions from the passed transaction memory pool and a coinbase +// using the transactions from the passed transaction source pool and a coinbase // that either pays to the passed address if it is not nil, or a coinbase that // is redeemable by anyone if the passed address is nil. The nil address // functionality is useful since there are cases such as the getblocktemplate @@ -1070,7 +1109,7 @@ func handleCreatedBlockTemplate(blockTemplate *BlockTemplate, // prioritizes based on the priority (then fee per kilobyte) or the fee per // kilobyte (then priority) depending on whether or not the BlockPrioritySize // policy setting allots space for high-priority transactions. Transactions -// which spend outputs from other transactions in the memory pool are added to a +// which spend outputs from other transactions in the source 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. // @@ -1130,11 +1169,16 @@ func handleCreatedBlockTemplate(blockTemplate *BlockTemplate, // // This function returns nil, nil if there are not enough voters on any of // the current top blocks to create a new block template. -func NewBlockTemplate(policy *miningPolicy, mempool *txMemPool, +func NewBlockTemplate(policy *miningPolicy, server *server, payToAddress dcrutil.Address) (*BlockTemplate, error) { - blockManager := mempool.server.blockManager - timeSource := mempool.server.timeSource + // TODO: The mempool should be completely separated via the TxSource + // interface so this function is fully decoupled. + mempool := server.txMemPool + + var txSource TxSource = server.txMemPool + blockManager := server.blockManager + timeSource := server.timeSource chainState := &blockManager.chainState // Extend the most recently known best block. @@ -1177,7 +1221,7 @@ func NewBlockTemplate(policy *miningPolicy, mempool *txMemPool, chainState.Unlock() // Calculate the stake enabled height. - stakeValidationHeight := mempool.server.chainParams.StakeValidationHeight + stakeValidationHeight := server.chainParams.StakeValidationHeight if nextBlockHeight >= stakeValidationHeight { // Obtain the entire generation of blocks stemming from this parent. @@ -1195,7 +1239,7 @@ func NewBlockTemplate(policy *miningPolicy, mempool *txMemPool, minrLog.Debugf("Too few voters found on any HEAD block, " + "recycling a parent block to mine on") return handleTooFewVoters(nextBlockHeight, payToAddress, - mempool.server.blockManager) + server.blockManager) } minrLog.Errorf("unexpected error while sorting eligible "+ "parents: %v", err.Error()) @@ -1233,30 +1277,29 @@ func NewBlockTemplate(policy *miningPolicy, mempool *txMemPool, } } - // Get the current memory pool transactions and create a priority queue - // to hold the transactions which are ready for inclusion into a block + // Get the current source transactions and create a priority queue to + // hold the transactions which are ready for inclusion into a block // along with some priority related and fee metadata. Reserve the same - // number of items that are in the memory pool for the priority queue. - // Also, choose the initial sort order for the priority queue based on - // whether or not there is an area allocated for high-priority - // transactions. - mempoolTxns := mempool.TxDescs() + // number of items that are available for the priority queue. Also, + // choose the initial sort order for the priority queue based on whether + // or not there is an area allocated for high-priority transactions. + sourceTxns := txSource.MiningDescs() sortedByFee := policy.BlockPrioritySize == 0 lessFunc := txPQByStakeAndFeeAndThenPriority if sortedByFee { lessFunc = txPQByStakeAndFee } - priorityQueue := newTxPriorityQueue(len(mempoolTxns), lessFunc) + priorityQueue := newTxPriorityQueue(len(sourceTxns), lessFunc) // Create a slice to hold the transactions to be included in the // generated block with reserved space. Also create a transaction // store to house all of the input transactions so multiple lookups // can be avoided. - blockTxns := make([]*dcrutil.Tx, 0, len(mempoolTxns)) + blockTxns := make([]*dcrutil.Tx, 0, len(sourceTxns)) blockTxStore := make(blockchain.TxStore) // dependers is used to track transactions which depend on another - // transaction in the memory pool. This, in conjunction with the + // transaction in the source pool. This, in conjunction with the // dependsOn map kept with each dependent transaction helps quickly // determine which dependent transactions are now eligible for inclusion // in the block once each transaction has been included. @@ -1268,18 +1311,18 @@ func NewBlockTemplate(policy *miningPolicy, mempool *txMemPool, // a transaction as it is selected for inclusion in the final block. // 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(mempoolTxns)) + txFees := make([]int64, 0, len(sourceTxns)) txFeesMap := make(map[chainhash.Hash]int64) - txSigOpCounts := make([]int64, 0, len(mempoolTxns)) + txSigOpCounts := make([]int64, 0, len(sourceTxns)) txSigOpCountsMap := make(map[chainhash.Hash]int64) txFees = append(txFees, -1) // Updated once known - minrLog.Debugf("Considering %d mempool transactions for inclusion to "+ - "new block", len(mempoolTxns)) + minrLog.Debugf("Considering %d transactions for inclusion to new block", + len(sourceTxns)) treeValid := mempool.IsTxTreeValid(prevHash) mempoolLoop: - for _, txDesc := range mempoolTxns { + for _, txDesc := range sourceTxns { // A block can't have more than one coinbase or contain // non-finalized transactions. tx := txDesc.Tx @@ -1325,15 +1368,10 @@ mempoolLoop: continue } - // Calculate the input value age sum for the transaction. This - // is comprised of the sum all of input amounts multiplied by - // their respective age (number of confirmations since the - // referenced input transaction). While doing the above, also - // setup dependencies for any transactions which reference other - // transactions in the mempool so they can be properly ordered - // below. + // Setup dependencies for any transactions which reference + // other transactions in the mempool so they can be properly + // ordered below. prioItem := &txPrioItem{tx: txDesc.Tx, txType: txDesc.Type} - inputValueAge := float64(0.0) for i, txIn := range tx.MsgTx().TxIn { // Evaluate if this is a stakebase input or not. If it is, continue // without evaluation of the input. @@ -1346,7 +1384,7 @@ mempoolLoop: originIndex := txIn.PreviousOutPoint.Index txData, exists := txStore[*originHash] if !exists || txData.Err != nil || txData.Tx == nil { - if !mempool.HaveTransaction(originHash) { + if !txSource.HaveTransaction(originHash) { minrLog.Tracef("Skipping tx %s because "+ "it references tx %s which is "+ "not available", tx.Sha, @@ -1355,7 +1393,7 @@ mempoolLoop: } // The transaction is referencing another - // transaction in the memory pool, so setup an + // transaction in the source pool, so setup an // ordering dependency. depList, exists := dependers[*originHash] if !exists { @@ -1369,9 +1407,8 @@ mempoolLoop: } prioItem.dependsOn[*originHash] = struct{}{} - // No need to calculate or sum input value age - // for this input since it's zero due to - // the input age multiplier of 0. + // Skip the check below. We already know the + // referenced transaction is available. continue } @@ -1385,12 +1422,6 @@ mempoolLoop: originIndex, originHash) continue mempoolLoop } - - // Sum the input value times age. - originTxOut := txData.Tx.MsgTx().TxOut[originIndex] - inputValue := originTxOut.Value - inputAge := nextBlockHeight - txData.BlockHeight - inputValueAge += float64(inputValue * inputAge) } // Calculate the final transaction priority using the input @@ -1469,7 +1500,7 @@ mempoolLoop: // Skip if we already have too many SStx. if isSStx && (numSStx >= - int(mempool.server.chainParams.MaxFreshStakePerBlock)) { + int(server.chainParams.MaxFreshStakePerBlock)) { minrLog.Tracef("Skipping sstx %s because it would exceed "+ "the max number of sstx allowed in a block", tx.Sha()) logSkippedDeps(tx, deps) @@ -1603,7 +1634,7 @@ mempoolLoop: nextBlockHeight, blockTxStore, false, // Don't check fraud proofs; missing ones are filled out below - mempool.server.chainParams) + server.chainParams) if err != nil { minrLog.Tracef("Skipping tx %s due to error in "+ "CheckTransactionInputs: %v", tx.Sha(), err) @@ -1611,7 +1642,7 @@ mempoolLoop: continue } err = blockchain.ValidateTransactionScripts(tx, blockTxStore, - txscript.StandardVerifyFlags, mempool.server.sigCache) + txscript.StandardVerifyFlags, server.sigCache) if err != nil { minrLog.Tracef("Skipping tx %s due to error in "+ "ValidateTransactionScripts: %v", tx.Sha(), err) @@ -1744,7 +1775,7 @@ mempoolLoop: } topBlockRegTx := topBlock.Transactions() - tempBlockTxns := make([]*dcrutil.Tx, 0, len(mempoolTxns)) + tempBlockTxns := make([]*dcrutil.Tx, 0, len(sourceTxns)) for _, tx := range blockTxns { if tx.Tree() == dcrutil.TxTreeRegular { // Go through all the inputs and check to see if this mempool @@ -1797,7 +1828,7 @@ mempoolLoop: } // Don't let this overflow. - if freshStake >= int(mempool.server.chainParams.MaxFreshStakePerBlock) { + if freshStake >= int(server.chainParams.MaxFreshStakePerBlock) { break } } @@ -1854,7 +1885,7 @@ mempoolLoop: nextBlockHeight, payToAddress, uint16(voters), - mempool.server.chainParams) + server.chainParams) if err != nil { return nil, err } @@ -1926,7 +1957,7 @@ mempoolLoop: // If we're greater than or equal to stake validation height, scale the // fees according to the number of voters. totalFees *= int64(voters) - totalFees /= int64(mempool.server.chainParams.TicketsPerBlock) + totalFees /= int64(server.chainParams.TicketsPerBlock) // Now that the actual transactions have been selected, update the // block size for the real transaction count and coinbase value with @@ -1956,13 +1987,13 @@ mempoolLoop: // bit for the mempool to sync with the votes map and we end up down // here despite having the relevant votes available in the votes map. minimumVotesRequired := - int((mempool.server.chainParams.TicketsPerBlock / 2) + 1) + int((server.chainParams.TicketsPerBlock / 2) + 1) if nextBlockHeight >= stakeValidationHeight && voters < minimumVotesRequired { minrLog.Warnf("incongruent number of voters in mempool " + "vs mempool.voters; not enough voters found") return handleTooFewVoters(nextBlockHeight, payToAddress, - mempool.server.blockManager) + server.blockManager) } // Correct transaction index fraud proofs for any transactions that @@ -2039,7 +2070,7 @@ mempoolLoop: var msgBlock wire.MsgBlock msgBlock.Header = wire.BlockHeader{ - Version: mempool.server.chainParams.CurrentBlockVersion, + Version: server.chainParams.CurrentBlockVersion, PrevBlock: *prevHash, MerkleRoot: *merkles[len(merkles)-1], StakeRoot: *merklesStake[len(merklesStake)-1], @@ -2077,8 +2108,8 @@ mempoolLoop: block.SetHeight(nextBlockHeight) if err := blockchain.CheckWorklessBlockSanity(block, - mempool.server.timeSource, - mempool.server.chainParams); err != nil { + server.timeSource, + server.chainParams); err != nil { str := fmt.Sprintf("failed to do final check for block workless "+ "sanity when making new block template: %v", err.Error()) @@ -2108,7 +2139,7 @@ mempoolLoop: validPayAddress: payToAddress != nil, } - return handleCreatedBlockTemplate(blockTemplate, mempool.server.blockManager) + return handleCreatedBlockTemplate(blockTemplate, server.blockManager) } // UpdateBlockTime updates the timestamp in the header of the passed block to diff --git a/rpcserver.go b/rpcserver.go index 4a3013cf..5d552b67 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2652,8 +2652,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.policy, s.server.txMemPool, - payAddr) + blkTemplate, err := NewBlockTemplate(s.policy, s.server, payAddr) if err != nil { return internalRPCError("Failed to create new block "+ "template: "+err.Error(), "") @@ -3610,15 +3609,15 @@ func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (in // handleGetMempoolInfo implements the getmempoolinfo command. func handleGetMempoolInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - txD := s.server.txMemPool.TxDescs() + mempoolTxns := s.server.txMemPool.TxDescs() var numBytes int64 - for _, desc := range txD { - numBytes += int64(desc.Tx.MsgTx().SerializeSize()) + for _, txD := range mempoolTxns { + numBytes += int64(txD.Tx.MsgTx().SerializeSize()) } ret := &dcrjson.GetMempoolInfoResult{ - Size: int64(len(txD)), + Size: int64(len(mempoolTxns)), Bytes: numBytes, } @@ -3874,7 +3873,7 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{ } mpd := &dcrjson.GetRawMempoolVerboseResult{ - Size: int32(desc.Tx.MsgTx().SerializeSize()), + Size: int32(tx.MsgTx().SerializeSize()), Fee: dcrutil.Amount(desc.Fee).ToCoin(), Time: desc.Added.Unix(), Height: desc.Height, @@ -3882,7 +3881,7 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{ CurrentPriority: currentPriority, Depends: make([]string, 0), } - for _, txIn := range desc.Tx.MsgTx().TxIn { + for _, txIn := range tx.MsgTx().TxIn { hash := &txIn.PreviousOutPoint.Hash if s.server.txMemPool.haveTransaction(hash) { mpd.Depends = append(mpd.Depends, @@ -3890,7 +3889,7 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{ } } - result[desc.Tx.Sha().String()] = mpd + result[tx.Sha().String()] = mpd } return result, nil @@ -4267,8 +4266,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.policy, s.server.txMemPool, - 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)