From bccbb9a7228ea12e19c18d8aa90255286977ce26 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Tue, 2 Aug 2022 06:02:45 -0500 Subject: [PATCH] rpcserver: Return template errors from getwork RPC. This modifies the RPC server getwork method to return an error in the case of a failed background template. Callers generally expect an error as opposed to long blocking behavior in the case there is a temporary reason block templates are unable to be generated. Also, correct the offsets in the comments regarding the serialized locations of the fields while here. --- internal/rpcserver/rpcserver.go | 19 ++++++++++++++----- internal/rpcserver/rpcserverhandlers_test.go | 2 +- rpctest/simnet_miner.go | 14 ++++++++++---- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/internal/rpcserver/rpcserver.go b/internal/rpcserver/rpcserver.go index 16cd9cb4..37d746d6 100644 --- a/internal/rpcserver/rpcserver.go +++ b/internal/rpcserver/rpcserver.go @@ -3660,11 +3660,20 @@ func getWorkTemplateKey(header *wire.BlockHeader) [merkleRootPairSize]byte { // // This function MUST be called with the RPC workstate locked. func handleGetWorkRequest(s *Server) (interface{}, error) { + // Return an error immediately in the case of a failed background template. + // + // The only time this is expected is due to imposition of certain additional + // time-based rules such as when the maximum allowed difficulty on testnet + // is reached and not enough time has passed since the last block. + bt := s.cfg.BlockTemplater + if _, err := bt.CurrentTemplate(); err != nil { + return nil, rpcMiscError(fmt.Sprintf("no work is available: %v", err)) + } + // Prune old templates and wait for updated templates when the current best // chain changes. state := s.workState best := s.cfg.Chain.BestSnapshot() - bt := s.cfg.BlockTemplater var template *mining.BlockTemplate if state.prevHash == nil || *state.prevHash != best.Hash { // Prune old templates from the pool when the best block changes. @@ -3726,10 +3735,10 @@ func handleGetWorkRequest(s *Server) (interface{}, error) { // retuned as part of the data below. // // For reference (0-index based, end value is exclusive): - // data[115:119] --> Bits - // data[135:139] --> Timestamp - // data[139:143] --> Nonce - // data[143:151] --> ExtraNonce + // data[116:120] --> Bits + // data[136:140] --> Timestamp + // data[140:144] --> Nonce + // data[144:152] --> ExtraNonce data := make([]byte, 0, getworkDataLen) buf := bytes.NewBuffer(data) err = headerCopy.Serialize(buf) diff --git a/internal/rpcserver/rpcserverhandlers_test.go b/internal/rpcserver/rpcserverhandlers_test.go index 6e8975ee..a66e3b1b 100644 --- a/internal/rpcserver/rpcserverhandlers_test.go +++ b/internal/rpcserver/rpcserverhandlers_test.go @@ -5756,7 +5756,7 @@ func TestHandleGetWork(t *testing.T) { return templater }(), wantErr: true, - errCode: dcrjson.ErrRPCInternal.Code, + errCode: dcrjson.ErrRPCMisc, }, { name: "handleGetWork: no work during chain reorg", handler: handleGetWork, diff --git a/rpctest/simnet_miner.go b/rpctest/simnet_miner.go index 5e6d3409..21f77692 100644 --- a/rpctest/simnet_miner.go +++ b/rpctest/simnet_miner.go @@ -15,6 +15,7 @@ import ( "github.com/decred/dcrd/blockchain/standalone/v2" "github.com/decred/dcrd/chaincfg/chainhash" + dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v4" "github.com/decred/dcrd/rpcclient/v8" "github.com/decred/dcrd/wire" ) @@ -110,10 +111,14 @@ func waitPredicate(pred func() bool, timeout time.Duration) error { // This is only applicable for tests that run on simnet or other networks that // have a target block per count of 1 second. func AdjustedSimnetMiner(ctx context.Context, client *rpcclient.Client, nb uint32) ([]*chainhash.Hash, error) { - - hashes := make([]*chainhash.Hash, nb) - - prevWork, err := client.GetWork(ctx) + // Fetch the current template. This might fail if there aren't enough + // tickets in the mempool yet, so perform a few tries. + var prevWork *dcrdtypes.GetWorkResult + err := waitPredicate(func() bool { + var err error + prevWork, err = client.GetWork(ctx) + return err == nil + }, time.Second*10) if err != nil { return nil, err } @@ -135,6 +140,7 @@ func AdjustedSimnetMiner(ctx context.Context, client *rpcclient.Client, nb uint3 return work.Data != prevWork.Data }, time.Second) + hashes := make([]*chainhash.Hash, nb) for i := uint32(0); i < nb; i++ { work, err := client.GetWork(ctx) if err != nil {