Implement BIP0023 getblocktemplate block proposals.
This commit implements block proposals as defined by BIP0023. This is work towards #124.
This commit is contained in:
parent
db20f25ff7
commit
21050b4751
1 changed files with 167 additions and 4 deletions
171
rpcserver.go
171
rpcserver.go
|
@ -81,6 +81,12 @@ var (
|
|||
Flags: hex.EncodeToString(btcscript.NewScriptBuilder().
|
||||
AddData([]byte(coinbaseFlags)).Script()),
|
||||
}
|
||||
|
||||
// gbtCapabilities describes additional capabilities returned with a
|
||||
// block template generated by the getblocktemplate RPC. It is
|
||||
// declared here to avoid the overhead of creating the slice on every
|
||||
// invocation for constant data.
|
||||
gbtCapabilities = []string{"proposal"}
|
||||
)
|
||||
|
||||
// Errors
|
||||
|
@ -1609,8 +1615,8 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld
|
|||
Version: header.Version,
|
||||
LongPollID: templateID,
|
||||
SubmitOld: submitOld,
|
||||
MinTime: state.minTimestamp.Unix(),
|
||||
Target: targetDifficulty,
|
||||
Capabilities: gbtCapabilities,
|
||||
}
|
||||
if useCoinbaseValue {
|
||||
reply.CoinbaseAux = gbtCoinbaseAux
|
||||
|
@ -1827,9 +1833,166 @@ func handleGetBlockTemplateRequest(s *rpcServer, request *btcjson.TemplateReques
|
|||
return state.blockTemplateResult(useCoinbaseValue, nil)
|
||||
}
|
||||
|
||||
// chainErrToGBTErrString converts an error returned from btcchain to a string
|
||||
// which matches the reasons and format described in BIP0022 for rejection
|
||||
// reasons.
|
||||
func chainErrToGBTErrString(err error) string {
|
||||
// When the passed error is not a RuleError, just return a generic
|
||||
// rejected string with the error text.
|
||||
ruleErr, ok := err.(btcchain.RuleError)
|
||||
if !ok {
|
||||
return "rejected: " + err.Error()
|
||||
}
|
||||
|
||||
switch ruleErr.ErrorCode {
|
||||
case btcchain.ErrDuplicateBlock:
|
||||
return "duplicate"
|
||||
case btcchain.ErrBlockTooBig:
|
||||
return "bad-block-size"
|
||||
case btcchain.ErrBlockVersionTooOld:
|
||||
return "bad-version"
|
||||
case btcchain.ErrInvalidTime:
|
||||
return "bad-time"
|
||||
case btcchain.ErrTimeTooOld:
|
||||
return "time-too-old"
|
||||
case btcchain.ErrTimeTooNew:
|
||||
return "time-too-new"
|
||||
case btcchain.ErrDifficultyTooLow:
|
||||
return "bad-diffbits"
|
||||
case btcchain.ErrUnexpectedDifficulty:
|
||||
return "bad-diffbits"
|
||||
case btcchain.ErrHighHash:
|
||||
return "high-hash"
|
||||
case btcchain.ErrBadMerkleRoot:
|
||||
return "bad-txnmrklroot"
|
||||
case btcchain.ErrBadCheckpoint:
|
||||
return "bad-checkpoint"
|
||||
case btcchain.ErrForkTooOld:
|
||||
return "fork-too-old"
|
||||
case btcchain.ErrNoTransactions:
|
||||
return "bad-txns-none"
|
||||
case btcchain.ErrTooManyTransactions:
|
||||
return "bad-txns-toomany"
|
||||
case btcchain.ErrNoTxInputs:
|
||||
return "bad-txns-noinputs"
|
||||
case btcchain.ErrNoTxOutputs:
|
||||
return "bad-txns-nooutputs"
|
||||
case btcchain.ErrTxTooBig:
|
||||
return "bad-txns-size"
|
||||
case btcchain.ErrBadTxOutValue:
|
||||
return "bad-txns-outputvalue"
|
||||
case btcchain.ErrDuplicateTxInputs:
|
||||
return "bad-txns-dupinputs"
|
||||
case btcchain.ErrBadTxInput:
|
||||
return "bad-txns-badinput"
|
||||
case btcchain.ErrMissingTx:
|
||||
return "bad-txns-missinginput"
|
||||
case btcchain.ErrUnfinalizedTx:
|
||||
return "bad-txns-unfinalizedtx"
|
||||
case btcchain.ErrDuplicateTx:
|
||||
return "bad-txns-duplicate"
|
||||
case btcchain.ErrOverwriteTx:
|
||||
return "bad-txns-overwrite"
|
||||
case btcchain.ErrImmatureSpend:
|
||||
return "bad-txns-maturity"
|
||||
case btcchain.ErrDoubleSpend:
|
||||
return "bad-txns-dblspend"
|
||||
case btcchain.ErrSpendTooHigh:
|
||||
return "bad-txns-highspend"
|
||||
case btcchain.ErrBadFees:
|
||||
return "bad-txns-fees"
|
||||
case btcchain.ErrTooManySigOps:
|
||||
return "high-sigops"
|
||||
case btcchain.ErrFirstTxNotCoinbase:
|
||||
return "bad-txns-nocoinbase"
|
||||
case btcchain.ErrMultipleCoinbases:
|
||||
return "bad-txns-multicoinbase"
|
||||
case btcchain.ErrBadCoinbaseScriptLen:
|
||||
return "bad-cb-length"
|
||||
case btcchain.ErrBadCoinbaseValue:
|
||||
return "bad-cb-value"
|
||||
case btcchain.ErrMissingCoinbaseHeight:
|
||||
return "bad-cb-height"
|
||||
case btcchain.ErrBadCoinbaseHeight:
|
||||
return "bad-cb-height"
|
||||
case btcchain.ErrScriptMalformed:
|
||||
return "bad-script-malformed"
|
||||
case btcchain.ErrScriptValidation:
|
||||
return "bad-script-validate"
|
||||
}
|
||||
|
||||
return "rejected: " + err.Error()
|
||||
}
|
||||
|
||||
// handleGetBlockTemplateProposal is a helper for handleGetBlockTemplate which
|
||||
// deals with block proposals.
|
||||
//
|
||||
// See https://en.bitcoin.it/wiki/BIP_0023 for more details.
|
||||
func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateRequest) (interface{}, error) {
|
||||
hexData := request.Data
|
||||
if hexData == "" {
|
||||
return false, btcjson.Error{
|
||||
Code: btcjson.ErrType.Code,
|
||||
Message: fmt.Sprintf("data must contain the " +
|
||||
"hex-encoded serialized block that is being " +
|
||||
"proposed"),
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the provided data is sane and deserialize the proposed block.
|
||||
if len(hexData)%2 != 0 {
|
||||
hexData = "0" + hexData
|
||||
}
|
||||
dataBytes, err := hex.DecodeString(hexData)
|
||||
if err != nil {
|
||||
return false, btcjson.Error{
|
||||
Code: btcjson.ErrDeserialization.Code,
|
||||
Message: fmt.Sprintf("data must be "+
|
||||
"hexadecimal string (not %q)", hexData),
|
||||
}
|
||||
}
|
||||
var msgBlock btcwire.MsgBlock
|
||||
if err := msgBlock.Deserialize(bytes.NewReader(dataBytes)); err != nil {
|
||||
return nil, btcjson.Error{
|
||||
Code: btcjson.ErrDeserialization.Code,
|
||||
Message: "Block decode failed: " + err.Error(),
|
||||
}
|
||||
}
|
||||
block := btcutil.NewBlock(&msgBlock)
|
||||
|
||||
// Ensure the block is building from the expected previous block.
|
||||
expectedPrevHash, _ := s.server.blockManager.chainState.Best()
|
||||
prevHash := &block.MsgBlock().Header.PrevBlock
|
||||
if expectedPrevHash == nil || !expectedPrevHash.IsEqual(prevHash) {
|
||||
return "bad-prevblk", nil
|
||||
}
|
||||
|
||||
flags := btcchain.BFDryRun | btcchain.BFNoPoWCheck
|
||||
isOrphan, err := s.server.blockManager.ProcessBlock(block, flags)
|
||||
if err != nil {
|
||||
if _, ok := err.(btcchain.RuleError); !ok {
|
||||
rpcsLog.Errorf("Failed to process block proposal: %v",
|
||||
err)
|
||||
return nil, btcjson.Error{
|
||||
Code: -25, // ErrRpcVerify
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
rpcsLog.Infof("Rejected block proposal: %v", err)
|
||||
return chainErrToGBTErrString(err), nil
|
||||
}
|
||||
if isOrphan {
|
||||
return "orphan", nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleGetBlockTemplate implements the getblocktemplate command.
|
||||
//
|
||||
// See https://en.bitcoin.it/wiki/BIP_0022 for more details.
|
||||
// See https://en.bitcoin.it/wiki/BIP_0022 and
|
||||
// https://en.bitcoin.it/wiki/BIP_0023 for more details.
|
||||
func handleGetBlockTemplate(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
|
||||
c := cmd.(*btcjson.GetBlockTemplateCmd)
|
||||
request := c.Request
|
||||
|
@ -1840,11 +2003,11 @@ func handleGetBlockTemplate(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan stru
|
|||
mode = request.Mode
|
||||
}
|
||||
|
||||
// The only supported mode is currently "template". Use a switch to
|
||||
// make other modes easier to implement.
|
||||
switch mode {
|
||||
case "template":
|
||||
return handleGetBlockTemplateRequest(s, request, closeChan)
|
||||
case "proposal":
|
||||
return handleGetBlockTemplateProposal(s, request)
|
||||
}
|
||||
|
||||
return nil, btcjson.Error{
|
||||
|
|
Loading…
Reference in a new issue