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.
This commit is contained in:
Dave Collins 2022-08-02 06:02:45 -05:00
parent 659b7fed1f
commit bccbb9a722
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
3 changed files with 25 additions and 10 deletions

View File

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

View File

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

View File

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