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.
This commit is contained in:
Dave Collins 2018-03-03 17:17:32 -06:00
parent 1275d1ffe9
commit 5e29dc7393
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
4 changed files with 39 additions and 79 deletions

View File

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

View File

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

100
mining.go
View File

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

View File

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