From eb166a7f9b0a0020695a78162111e4654eea0002 Mon Sep 17 00:00:00 2001 From: Jamie Holdstock Date: Fri, 22 Dec 2023 11:24:27 +0000 Subject: [PATCH] pool: Handle (nil,nil) response from dcrd.GetTxOut --- pool/hub.go | 6 ++++-- pool/paymentmgr.go | 10 ++++++++-- pool/paymentmgr_test.go | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/pool/hub.go b/pool/hub.go index b7df9107..88468cd8 100644 --- a/pool/hub.go +++ b/pool/hub.go @@ -108,9 +108,11 @@ type WalletConnection interface { Rescan(ctx context.Context, in *walletrpc.RescanRequest, opts ...grpc.CallOption) (walletrpc.WalletService_RescanClient, error) } -// NodeConnection defines the functionality needed by a mining node -// connection for the pool. +// NodeConnection defines the functionality needed by a mining node connection +// for the pool. Typically a dcrd client but can be stubbed for testing. type NodeConnection interface { + // GetTxOut fetches the output referenced by the provided txHash and index. + // WARNING: dcrd can return (nil, nil). GetTxOut(context.Context, *chainhash.Hash, uint32, int8, bool) (*chainjson.GetTxOutResult, error) CreateRawTransaction(context.Context, []chainjson.TransactionInput, map[stdaddr.Address]dcrutil.Amount, *int64, *int64) (*wire.MsgTx, error) GetWorkSubmit(context.Context, string) (bool, error) diff --git a/pool/paymentmgr.go b/pool/paymentmgr.go index f95c4e95..47c67b3b 100644 --- a/pool/paymentmgr.go +++ b/pool/paymentmgr.go @@ -55,6 +55,7 @@ const ( // pool. Typically a dcrd client but can be stubbed for testing. type txCreator interface { // GetTxOut fetches the output referenced by the provided txHash and index. + // WARNING: dcrd can return (nil, nil). GetTxOut(context.Context, *chainhash.Hash, uint32, int8, bool) (*chainjson.GetTxOutResult, error) // CreateRawTransaction generates a transaction from the provided // inputs and payouts. @@ -641,8 +642,13 @@ func (pm *PaymentMgr) generatePayoutTxDetails(ctx context.Context, txC txCreator // the current height. txOutResult, err := txC.GetTxOut(ctx, txHash, coinbaseIndex, wire.TxTreeRegular, false) if err != nil { - desc := fmt.Sprintf("%s: unable to find tx output: %v", - funcName, err) + desc := fmt.Sprintf("%s: unable to find tx output %s:%d: %v", + funcName, txHash, coinbaseIndex, err) + return nil, nil, nil, 0, errs.PoolError(errs.TxOut, desc) + } + if txOutResult == nil { + desc := fmt.Sprintf("%s: unable to find tx output %s:%d", + funcName, txHash, coinbaseIndex) return nil, nil, nil, 0, errs.PoolError(errs.TxOut, desc) } if txOutResult.Confirmations < int64(pm.cfg.ActiveNet.CoinbaseMaturity+1) { diff --git a/pool/paymentmgr_test.go b/pool/paymentmgr_test.go index 1250105e..7155094b 100644 --- a/pool/paymentmgr_test.go +++ b/pool/paymentmgr_test.go @@ -840,6 +840,21 @@ func testPaymentMgrPayment(t *testing.T) { t.Fatalf("expected a fetch txOut error, got %v", err) } + // Ensure a (nil, nil) response from dcrd is handled gracefully. + // generatePayoutTxDetails should return a TxOut error rather than + // generating a panic. + txC = &txCreatorImpl{ + getTxOut: func(ctx context.Context, txHash *chainhash.Hash, index uint32, tree int8, mempool bool) (*chainjson.GetTxOutResult, error) { + return nil, nil + }, + } + _, _, _, _, err = mgr.generatePayoutTxDetails(ctx, txC, poolFeeAddrs, + mPmts, coinbaseIndex) + if !errors.Is(err, errs.TxOut) { + cancel() + t.Fatalf("expected a fetch txOut error, got %v", err) + } + // Ensure generating payout tx details returns an error if the returned // output is not spendable. txC = &txCreatorImpl{