Implement some BIP0023 getblocktemplate mutations.

This commit implements a portion of the mutations section of BIP0023.
In particular, it adds the mutable, mintime, maxtime, and noncerange keys
to the returned block template along with indicating support for the time,
transactions/add, prevblock, and coinbase/append mutations.  Also, the
addition of the mintime and maxtime fields imply support for the
time/decrement and time/increment mutations.  Further, if the caller
indicates the coinbasevalue capability, the coinbasetxn field will be
omitted thereby implying support for the coinbase and generation
mutations.

Closes #124.
This commit is contained in:
Dave Collins 2014-06-29 20:00:51 -05:00
parent 21050b4751
commit 180f4ac0a2

View file

@ -65,6 +65,11 @@ const (
hash1Len = (1 + ((btcwire.HashSize + 8) / fastsha256.BlockSize)) * hash1Len = (1 + ((btcwire.HashSize + 8) / fastsha256.BlockSize)) *
fastsha256.BlockSize fastsha256.BlockSize
// gbtNonceRange is two 32-bit big-endian hexadecimal integers which
// represent the valid ranges of nonces returned by the getblocktemplate
// RPC.
gbtNonceRange = "00000000ffffffff"
// gbtRegenerateSeconds is the number of seconds that must pass before // gbtRegenerateSeconds is the number of seconds that must pass before
// a new template is generated when the previous block hash has not // a new template is generated when the previous block hash has not
// changed and there have been changes to the available transactions // changed and there have been changes to the available transactions
@ -73,6 +78,14 @@ const (
) )
var ( var (
// gbtMutableFields are the manipulations the server allows to be made
// to block templates generated by the getblocktemplate RPC. It is
// declared here to avoid the overhead of creating the slice on every
// invocation for constant data.
gbtMutableFields = []string{
"time", "transactions/add", "prevblock", "coinbase/append",
}
// gbtCoinbaseAux describes additional data that miners should include // gbtCoinbaseAux describes additional data that miners should include
// in the coinbase signature script. It is declared here to avoid the // in the coinbase signature script. It is declared here to avoid the
// overhead of creating a new object on every invocation for constant // overhead of creating a new object on every invocation for constant
@ -1547,11 +1560,36 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo
// //
// This function MUST be called with the state locked. // This function MUST be called with the state locked.
func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld *bool) (*btcjson.GetBlockTemplateResult, error) { func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld *bool) (*btcjson.GetBlockTemplateResult, error) {
// Ensure the timestamps are still in valid range for the template.
// This should really only ever happen if the local clock is changed
// after the template is generated, but it's important to avoid serving
// invalid block templates.
template := state.template
msgBlock := template.block
header := &msgBlock.Header
curTime := time.Now()
if curTime.Before(state.minTimestamp) {
return nil, btcjson.Error{
Code: btcjson.ErrOutOfRange.Code,
Message: fmt.Sprintf("The local time is before the "+
"minimum allowed time for a block - current "+
"time %v, minimum time %v", curTime,
state.minTimestamp),
}
}
maxTime := curTime.Add(time.Second * btcchain.MaxTimeOffsetSeconds)
if header.Timestamp.After(maxTime) {
return nil, btcjson.Error{
Code: btcjson.ErrOutOfRange.Code,
Message: fmt.Sprintf("The template time is after the "+
"maximum allowed time for a block - template "+
"time %v, maximum time %v", curTime, maxTime),
}
}
// Convert each transaction in the block template to a template result // Convert each transaction in the block template to a template result
// transaction. The result does not include the coinbase, so notice // transaction. The result does not include the coinbase, so notice
// the adjustments to the various lengths and indices. // the adjustments to the various lengths and indices.
template := state.template
msgBlock := template.block
numTx := len(msgBlock.Transactions) numTx := len(msgBlock.Transactions)
transactions := make([]btcjson.GetBlockTemplateResultTx, 0, numTx-1) transactions := make([]btcjson.GetBlockTemplateResultTx, 0, numTx-1)
txIndex := make(map[btcwire.ShaHash]int64, numTx) txIndex := make(map[btcwire.ShaHash]int64, numTx)
@ -1600,13 +1638,15 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld
transactions = append(transactions, resultTx) transactions = append(transactions, resultTx)
} }
// Generate the block template reply. // Generate the block template reply. Note that following mutations are
header := &msgBlock.Header // implied by the included or omission of fields:
// Including MinTime -> time/decrement
// Omitting CoinbaseTxn -> coinbase, generation
targetDifficulty := fmt.Sprintf("%064x", btcchain.CompactToBig(header.Bits)) targetDifficulty := fmt.Sprintf("%064x", btcchain.CompactToBig(header.Bits))
templateID := encodeTemplateID(state.prevHash, state.lastGenerated) templateID := encodeTemplateID(state.prevHash, state.lastGenerated)
reply := btcjson.GetBlockTemplateResult{ reply := btcjson.GetBlockTemplateResult{
Bits: strconv.FormatInt(int64(header.Bits), 16), Bits: strconv.FormatInt(int64(header.Bits), 16),
CurTime: time.Now().Unix(), CurTime: curTime.Unix(),
Height: template.height, Height: template.height,
PreviousHash: header.PrevBlock.String(), PreviousHash: header.PrevBlock.String(),
SigOpLimit: btcchain.MaxSigOpsPerBlock, SigOpLimit: btcchain.MaxSigOpsPerBlock,
@ -1616,6 +1656,10 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld
LongPollID: templateID, LongPollID: templateID,
SubmitOld: submitOld, SubmitOld: submitOld,
Target: targetDifficulty, Target: targetDifficulty,
MinTime: state.minTimestamp.Unix(),
MaxTime: maxTime.Unix(),
Mutable: gbtMutableFields,
NonceRange: gbtNonceRange,
Capabilities: gbtCapabilities, Capabilities: gbtCapabilities,
} }
if useCoinbaseValue { if useCoinbaseValue {