From 5e29dc73939b50b382e48560f63fe50b7c8c2a9f Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 3 Mar 2018 17:17:32 -0600 Subject: [PATCH] mining: Use single uint64 coinbase extra nonce. The current code creates block templates with coinbase transactions that have 4 uint64 extra nonces, but only ever uses one of them. Rather than wasting the extra space, this modifies the code so it only uses a single uint64 extra nonce. It should be noted that realistically there isn't even a real need for an extra nonce in the coinbase at all because there is extra space in the header specifically for that purpose and miners can't modify the coinbase via getwork anyways. However, it is still necessary to insert for the current code in order to ensure every block template has a unique merkle root since that is what is used to uniquely identify the block template. --- blockmanager.go | 5 +-- cpuminer.go | 8 +--- mining.go | 100 ++++++++++++++++-------------------------------- rpcserver.go | 5 +-- 4 files changed, 39 insertions(+), 79 deletions(-) diff --git a/blockmanager.go b/blockmanager.go index 55f7ea6a..6a569bf7 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2016 The btcsuite developers -// Copyright (c) 2015-2016 The Decred developers +// Copyright (c) 2015-2018 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -898,8 +898,7 @@ func (b *blockManager) checkBlockForHiddenVotes(block *dcrutil.Block) { return } height := block.MsgBlock().Header.Height - opReturnPkScript, err := standardCoinbaseOpReturn(height, - []uint64{0, 0, 0, random}) + opReturnPkScript, err := standardCoinbaseOpReturn(height, random) if err != nil { // Stopping at this step will lead to a corrupted block template // because the stake tree has already been manipulated, so throw diff --git a/cpuminer.go b/cpuminer.go index 383bc6f8..fcab9a25 100644 --- a/cpuminer.go +++ b/cpuminer.go @@ -212,14 +212,10 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit // added relying on the fact that overflow will wrap around 0 as // provided by the Go spec. for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ { - // Get the old nonce values. - ens := getCoinbaseExtranonces(msgBlock) - ens[2] = extraNonce + enOffset - // Update the extra nonce in the block template with the // new value by regenerating the coinbase script and - // setting the merkle root to the new value. The - err := UpdateExtraNonce(msgBlock, blockHeight, ens) + // setting the merkle root to the new value. + err := UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset) if err != nil { minrLog.Warnf("Unable to update CPU miner extranonce: %v", err) diff --git a/mining.go b/mining.go index 878c2ac5..f7483fcd 100644 --- a/mining.go +++ b/mining.go @@ -418,17 +418,10 @@ func txIndexFromTxList(hash chainhash.Hash, list []*dcrutil.Tx) int { // standardCoinbaseOpReturn creates a standard OP_RETURN output to insert into // coinbase to use as extranonces. The OP_RETURN pushes 32 bytes. -func standardCoinbaseOpReturn(height uint32, extraNonces []uint64) ([]byte, error) { - if len(extraNonces) != 4 { - return nil, fmt.Errorf("extranonces has wrong num uint64s") - } - - enData := make([]byte, 36) +func standardCoinbaseOpReturn(height uint32, extraNonce uint64) ([]byte, error) { + enData := make([]byte, 12) binary.LittleEndian.PutUint32(enData[0:4], height) - binary.LittleEndian.PutUint64(enData[4:12], extraNonces[0]) - binary.LittleEndian.PutUint64(enData[12:20], extraNonces[1]) - binary.LittleEndian.PutUint64(enData[20:28], extraNonces[2]) - binary.LittleEndian.PutUint64(enData[28:36], extraNonces[3]) + binary.LittleEndian.PutUint64(enData[4:12], extraNonce) extraNonceScript, err := txscript.GenerateProvablyPruneableOut(enData) if err != nil { return nil, err @@ -437,69 +430,45 @@ func standardCoinbaseOpReturn(height uint32, extraNonces []uint64) ([]byte, erro return extraNonceScript, nil } -// getCoinbaseExtranonce extracts the extranonce from a block template's -// coinbase transaction. -func (bt *BlockTemplate) getCoinbaseExtranonces() []uint64 { - if len(bt.Block.Transactions[0].TxOut) < 2 { - return []uint64{0, 0, 0, 0} +// extractCoinbaseTxExtraNonce extracts the extra nonce from a standard coinbase +// OP_RETURN output. It will return 0 if either the provided transaction does +// not have the relevant output or the script is not large enough to perform the +// extraction. +func extractCoinbaseTxExtraNonce(coinbaseTx *wire.MsgTx) uint64 { + if len(coinbaseTx.TxOut) < 2 { + return 0 } - - if len(bt.Block.Transactions[0].TxOut[1].PkScript) < 38 { - return []uint64{0, 0, 0, 0} + script := coinbaseTx.TxOut[1].PkScript + if len(script) < 14 { + return 0 } - - ens := make([]uint64, 4) // 32-bytes - ens[0] = binary.LittleEndian.Uint64( - bt.Block.Transactions[0].TxOut[1].PkScript[6:14]) - ens[1] = binary.LittleEndian.Uint64( - bt.Block.Transactions[0].TxOut[1].PkScript[14:22]) - ens[2] = binary.LittleEndian.Uint64( - bt.Block.Transactions[0].TxOut[1].PkScript[22:30]) - ens[3] = binary.LittleEndian.Uint64( - bt.Block.Transactions[0].TxOut[1].PkScript[30:38]) - - return ens + return binary.LittleEndian.Uint64(script[6:14]) } -// getCoinbaseExtranonce extracts the extranonce from a block template's +// extractCoinbaseExtraNonce extracts the extra nonce from a block template's // coinbase transaction. -func getCoinbaseExtranonces(msgBlock *wire.MsgBlock) []uint64 { - if len(msgBlock.Transactions[0].TxOut) < 2 { - return []uint64{0, 0, 0, 0} - } +func (bt *BlockTemplate) extractCoinbaseExtraNonce() uint64 { + return extractCoinbaseTxExtraNonce(bt.Block.Transactions[0]) +} - if len(msgBlock.Transactions[0].TxOut[1].PkScript) < 38 { - return []uint64{0, 0, 0, 0} - } - - ens := make([]uint64, 4) // 32-bytes - ens[0] = binary.LittleEndian.Uint64( - msgBlock.Transactions[0].TxOut[1].PkScript[6:14]) - ens[1] = binary.LittleEndian.Uint64( - msgBlock.Transactions[0].TxOut[1].PkScript[14:22]) - ens[2] = binary.LittleEndian.Uint64( - msgBlock.Transactions[0].TxOut[1].PkScript[22:30]) - ens[3] = binary.LittleEndian.Uint64( - msgBlock.Transactions[0].TxOut[1].PkScript[30:38]) - - return ens +// extractCoinbaseExtraNonce extracts the extra nonce from a block template's +// coinbase transaction. +func extractCoinbaseExtraNonce(msgBlock *wire.MsgBlock) uint64 { + return extractCoinbaseTxExtraNonce(msgBlock.Transactions[0]) } // UpdateExtraNonce updates the extra nonce in the coinbase script of the passed // block by regenerating the coinbase script with the passed value and block // height. It also recalculates and updates the new merkle root that results // from changing the coinbase script. -func UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight int64, extraNonces []uint64) error { +func UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight int64, extraNonce uint64) error { // First block has no extranonce. if blockHeight == 1 { return nil } - if len(extraNonces) != 4 { - return fmt.Errorf("not enough nonce information passed") - } coinbaseOpReturn, err := standardCoinbaseOpReturn(uint32(blockHeight), - extraNonces) + extraNonce) if err != nil { return err } @@ -841,19 +810,18 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, cptCopy.Block.Header.Bits = requiredDifficulty } - // Choose a new extranonce value that is one greater - // than the previous extranonce, so we don't remine the + // Choose a new extra nonce value that is one greater + // than the previous extra nonce, so we don't remine the // same block and choose the same winners as before. - ens := cptCopy.getCoinbaseExtranonces() - ens[0]++ - err = UpdateExtraNonce(cptCopy.Block, cptCopy.Height, ens) + en := cptCopy.extractCoinbaseExtraNonce() + 1 + err = UpdateExtraNonce(cptCopy.Block, cptCopy.Height, en) if err != nil { return nil, err } // Update extranonce of the original template too, so // we keep getting unique numbers. - err = UpdateExtraNonce(curTemplate.Block, curTemplate.Height, ens) + err = UpdateExtraNonce(curTemplate.Block, curTemplate.Height, en) if err != nil { return nil, err } @@ -892,7 +860,7 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, } opReturnPkScript, err := standardCoinbaseOpReturn(topBlock.MsgBlock().Header.Height, - []uint64{0, 0, 0, rand}) + rand) if err != nil { return nil, err } @@ -994,8 +962,7 @@ func handleCreatedBlockTemplate(blockTemplate *BlockTemplate, bm *blockManager) // This is where we begin storing block templates, when either the // program is freshly started or the chain is matured to stake // validation height. - if curTemplate == nil && - nextBlockHeight >= stakeValidationHeight-2 { + if curTemplate == nil && nextBlockHeight >= stakeValidationHeight-2 { bm.SetCurrentTemplate(blockTemplate) } @@ -1003,8 +970,7 @@ func handleCreatedBlockTemplate(blockTemplate *BlockTemplate, bm *blockManager) // so we check to if CachedCurrentTemplate is out of date. If it is, // we store it as the cached parent template, and store the new block // template as the currenct template. - if curTemplate != nil && - nextBlockHeight >= stakeValidationHeight-1 { + if curTemplate != nil && nextBlockHeight >= stakeValidationHeight-1 { if curTemplate.Height < nextBlockHeight { bm.SetParentTemplate(curTemplate) bm.SetCurrentTemplate(blockTemplate) @@ -1801,7 +1767,7 @@ mempoolLoop: return nil, err } opReturnPkScript, err := standardCoinbaseOpReturn(uint32(nextBlockHeight), - []uint64{0, 0, 0, rand}) + rand) if err != nil { return nil, err } diff --git a/rpcserver.go b/rpcserver.go index bdb43564..33dddad3 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -4033,10 +4033,9 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) { // Increment the extra nonce and update the block template // with the new value by regenerating the coinbase script and // setting the merkle root to the new value. - ens := getCoinbaseExtranonces(msgBlock) + en := extractCoinbaseExtraNonce(msgBlock) + 1 state.extraNonce++ - ens[0]++ - err := UpdateExtraNonce(msgBlock, latestHeight+1, ens) + err := UpdateExtraNonce(msgBlock, latestHeight+1, en) if err != nil { errStr := fmt.Sprintf("Failed to update extra nonce: "+ "%v", err)