diff --git a/client/asset/dcr/dcr.go b/client/asset/dcr/dcr.go index 091df23ca7..bfb62c1098 100644 --- a/client/asset/dcr/dcr.go +++ b/client/asset/dcr/dcr.go @@ -3539,15 +3539,11 @@ func (dcr *ExchangeWallet) lookupTxOutput(ctx context.Context, txHash *chainhash if err != nil { return nil, 0, 0, err // asset.CoinNotFoundError if not found } - msgTx, err := msgTxFromHex(tx.Hex) - if err != nil { - return nil, 0, 0, fmt.Errorf("invalid hex for tx %s: %v", txHash, err) - } - if int(vout) >= len(msgTx.TxOut) { + if int(vout) >= len(tx.MsgTx.TxOut) { return nil, 0, 0, fmt.Errorf("tx %s has no output at %d", txHash, vout) } - txOut = msgTx.TxOut[vout] + txOut = tx.MsgTx.TxOut[vout] confs = uint32(tx.Confirmations) // We have the requested output. Check if it is spent. @@ -3716,15 +3712,11 @@ func (dcr *ExchangeWallet) queueFindRedemptionRequest(ctx context.Context, contr if err != nil { return nil, nil, err } - msgTx, err := msgTxFromHex(tx.Hex) - if err != nil { - return nil, nil, fmt.Errorf("invalid contract tx hex %s: %w", tx.Hex, err) - } - if int(vout) > len(msgTx.TxOut)-1 { + if int(vout) > len(tx.MsgTx.TxOut)-1 { return nil, nil, fmt.Errorf("vout index %d out of range for transaction %s", vout, txHash) } - contractScript := msgTx.TxOut[vout].PkScript - contractScriptVer := msgTx.TxOut[vout].Version + contractScript := tx.MsgTx.TxOut[vout].PkScript + contractScriptVer := tx.MsgTx.TxOut[vout].Version if !stdscript.IsScriptHashScript(contractScriptVer, contractScript) { return nil, nil, fmt.Errorf("coin %s not a valid contract", contractOutpoint.String()) } @@ -3787,12 +3779,12 @@ func (dcr *ExchangeWallet) findRedemptionsInMempool(contractOutpoints []outPoint } for _, txHash := range mempoolTxs { - tx, err := dcr.wallet.GetRawTransaction(dcr.ctx, txHash) + tx, err := dcr.wallet.GetTransaction(dcr.ctx, txHash) if err != nil { logAbandon(fmt.Sprintf("getrawtransaction error for tx hash %v: %v", txHash, err)) return } - found, canceled := dcr.findRedemptionsInTx("mempool", tx, contractOutpoints) + found, canceled := dcr.findRedemptionsInTx("mempool", tx.MsgTx, contractOutpoints) totalFound += found totalCanceled += canceled if totalFound+totalCanceled == contractsCount { @@ -4588,11 +4580,7 @@ func (dcr *ExchangeWallet) FindBond(ctx context.Context, coinID []byte, searchUn // to this wallet, it should be found here. tx, err := dcr.wallet.GetTransaction(ctx, txHash) if err == nil { - msgTx, err := msgTxFromHex(tx.Hex) - if err != nil { - return nil, fmt.Errorf("invalid hex for tx %s: %v", txHash, err) - } - return decodeV0BondTx(msgTx) + return decodeV0BondTx(tx.MsgTx) } if !errors.Is(err, asset.CoinNotFoundError) { dcr.log.Warnf("Unexpected error looking up bond output %v:%d", txHash, vout) @@ -5937,52 +5925,52 @@ func isMixTx(tx *wire.MsgTx) (isMix bool, mixDenom int64) { // idUnknownTx identifies the type and details of a transaction either made // or received by the wallet. -func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactionsResult) (*asset.WalletTransaction, error) { - txHash, err := chainhash.NewHashFromStr(tx.TxID) +func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, ltxr *ListTransactionsResult) (*asset.WalletTransaction, error) { + txHash, err := chainhash.NewHashFromStr(ltxr.TxID) if err != nil { - return nil, fmt.Errorf("error decoding tx hash %s: %v", tx.TxID, err) + return nil, fmt.Errorf("error decoding tx hash %s: %v", ltxr.TxID, err) } - msgTx, err := dcr.wallet.GetRawTransaction(ctx, txHash) + tx, err := dcr.wallet.GetTransaction(ctx, txHash) if err != nil { - return nil, fmt.Errorf("GetRawTransaction error: %v", err) + return nil, fmt.Errorf("GetTransaction error: %v", err) } var totalIn uint64 - for _, txIn := range msgTx.TxIn { + for _, txIn := range tx.MsgTx.TxIn { if txIn.ValueIn > 0 { totalIn += uint64(txIn.ValueIn) } } var totalOut uint64 - for _, txOut := range msgTx.TxOut { + for _, txOut := range tx.MsgTx.TxOut { totalOut += uint64(txOut.Value) } - fee := rpcTxFee(tx) + fee := rpcTxFee(ltxr) if fee == 0 && totalIn > totalOut { fee = totalIn - totalOut } - switch *tx.TxType { + switch *ltxr.TxType { case walletjson.LTTTVote: return &asset.WalletTransaction{ Type: asset.TicketVote, - ID: tx.TxID, + ID: ltxr.TxID, Amount: totalOut, Fees: fee, }, nil case walletjson.LTTTRevocation: return &asset.WalletTransaction{ Type: asset.TicketRevocation, - ID: tx.TxID, + ID: ltxr.TxID, Amount: totalOut, Fees: fee, }, nil case walletjson.LTTTTicket: return &asset.WalletTransaction{ Type: asset.TicketPurchase, - ID: tx.TxID, + ID: ltxr.TxID, Amount: totalOut, Fees: fee, }, nil @@ -6003,11 +5991,11 @@ func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactions BondID: pkHash[:], } } - if isBond, bondInfo := txIsBond(msgTx); isBond { + if isBond, bondInfo := txIsBond(tx.MsgTx); isBond { return &asset.WalletTransaction{ Type: asset.CreateBond, - ID: tx.TxID, - Amount: uint64(msgTx.TxOut[0].Value), + ID: ltxr.TxID, + Amount: uint64(tx.MsgTx.TxOut[0].Value), Fees: fee, BondInfo: bondInfo, }, nil @@ -6023,10 +6011,10 @@ func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactions } return } - if v := txPaysToScriptHash(msgTx); v > 0 { + if v := txPaysToScriptHash(tx.MsgTx); v > 0 { return &asset.WalletTransaction{ Type: asset.SwapOrSend, - ID: tx.TxID, + ID: ltxr.TxID, Amount: v, Fees: fee, }, nil @@ -6059,10 +6047,10 @@ func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactions redeemsSwap := func(msgTx *wire.MsgTx) bool { return containsContractAtPushIndex(msgTx, 4, contractIsSwap) } - if redeemsSwap(msgTx) { + if redeemsSwap(tx.MsgTx) { return &asset.WalletTransaction{ Type: asset.Redeem, - ID: tx.TxID, + ID: ltxr.TxID, Amount: totalOut + fee, Fees: fee, }, nil @@ -6070,10 +6058,10 @@ func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactions refundsSwap := func(msgTx *wire.MsgTx) bool { return containsContractAtPushIndex(msgTx, 3, contractIsSwap) } - if refundsSwap(msgTx) { + if refundsSwap(tx.MsgTx) { return &asset.WalletTransaction{ Type: asset.Refund, - ID: tx.TxID, + ID: ltxr.TxID, Amount: totalOut + fee, Fees: fee, }, nil @@ -6097,10 +6085,10 @@ func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactions } return containsContractAtPushIndex(msgTx, 2, isBond), bondInfo } - if isBondRedemption, bondInfo := redeemsBond(msgTx); isBondRedemption { + if isBondRedemption, bondInfo := redeemsBond(tx.MsgTx); isBondRedemption { return &asset.WalletTransaction{ Type: asset.RedeemBond, - ID: tx.TxID, + ID: ltxr.TxID, Amount: totalOut, Fees: fee, BondInfo: bondInfo, @@ -6130,17 +6118,17 @@ func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactions return true } - if tx.Send && allOutputsPayUs(msgTx) && len(msgTx.TxIn) == 1 { + if ltxr.Send && allOutputsPayUs(tx.MsgTx) && len(tx.MsgTx.TxIn) == 1 { return &asset.WalletTransaction{ Type: asset.Split, - ID: tx.TxID, + ID: ltxr.TxID, Fees: fee, }, nil } - if isMix, mixDenom := isMixTx(msgTx); isMix { + if isMix, mixDenom := isMixTx(tx.MsgTx); isMix { var mixedAmount uint64 - for _, txOut := range msgTx.TxOut { + for _, txOut := range tx.MsgTx.TxOut { if txOut.Value == mixDenom { _, addrs := stdscript.ExtractAddrs(scriptVersion, txOut.PkScript, dcr.chainParams) if err != nil { @@ -6166,7 +6154,7 @@ func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactions return &asset.WalletTransaction{ Type: asset.Mix, - ID: tx.TxID, + ID: ltxr.TxID, Amount: mixedAmount, Fees: fee, }, nil @@ -6224,30 +6212,30 @@ func (dcr *ExchangeWallet) idUnknownTx(ctx context.Context, tx *ListTransactions return } - in, out := txOutDirection(msgTx) + in, out := txOutDirection(tx.MsgTx) - if tx.Send { + if ltxr.Send { txType := asset.Send amt := out - if allOutputsPayUs(msgTx) { + if allOutputsPayUs(tx.MsgTx) { txType = asset.SelfSend amt = in } return &asset.WalletTransaction{ Type: txType, - ID: tx.TxID, + ID: ltxr.TxID, Amount: amt, Fees: fee, - Recipient: getRecipient(msgTx, false), + Recipient: getRecipient(tx.MsgTx, false), }, nil } return &asset.WalletTransaction{ Type: asset.Receive, - ID: tx.TxID, + ID: ltxr.TxID, Amount: in, Fees: fee, - Recipient: getRecipient(msgTx, true), + Recipient: getRecipient(tx.MsgTx, true), }, nil } diff --git a/client/asset/dcr/dcr_test.go b/client/asset/dcr/dcr_test.go index 119e7c632b..7caf8e9dd9 100644 --- a/client/asset/dcr/dcr_test.go +++ b/client/asset/dcr/dcr_test.go @@ -450,17 +450,6 @@ func (c *tRPCClient) GetRawMempool(_ context.Context, txType chainjson.GetRawMem return txHashes, nil } -func (c *tRPCClient) GetRawTransaction(_ context.Context, txHash *chainhash.Hash) (*dcrutil.Tx, error) { - if c.rawTxErr != nil { - return nil, c.rawTxErr - } - tx, found := c.blockchain.rawTxs[*txHash] - if !found { - return nil, dcrjson.NewRPCError(dcrjson.ErrRPCNoTxInfo, "no test raw tx "+txHash.String()) - } - return dcrutil.NewTx(tx.tx), nil -} - func (c *tRPCClient) GetBalanceMinConf(_ context.Context, account string, minConfirms int) (*walletjson.GetBalanceResult, error) { return c.balanceResult, c.balanceErr } @@ -492,6 +481,18 @@ func (c *tRPCClient) GetTransaction(_ context.Context, txHash *chainhash.Hash) ( if c.walletTxFn != nil { return c.walletTxFn() } + c.blockchain.mtx.RLock() + defer c.blockchain.mtx.RUnlock() + if rawTx, has := c.blockchain.rawTxs[*txHash]; has { + b, err := rawTx.tx.Bytes() + if err != nil { + return nil, err + } + walletTx := &walletjson.GetTransactionResult{ + Hex: hex.EncodeToString(b), + } + return walletTx, nil + } return nil, dcrjson.NewRPCError(dcrjson.ErrRPCNoTxInfo, "no test transaction") } @@ -3200,10 +3201,6 @@ func TestFindRedemption(t *testing.T) { Hex: txHex, } - node.walletTxFn = func() (*walletjson.GetTransactionResult, error) { - return walletTx, nil - } - // Add an intermediate block for good measure. node.blockchain.addRawTx(contractHeight+1, dummyTx()) @@ -3221,6 +3218,10 @@ func TestFindRedemption(t *testing.T) { t.Fatalf("wrong secret. expected %x, got %x", secret, checkSecret) } + node.walletTxFn = func() (*walletjson.GetTransactionResult, error) { + return walletTx, nil + } + // Move the redemption to a new block and check if wallet.FindRedemption finds it. _, redeemBlock := node.blockchain.addRawTx(contractHeight+2, makeRawTx(inputs, []dex.Bytes{otherScript})) _, checkSecret, err = wallet.FindRedemption(tCtx, coinID, nil) @@ -4654,7 +4655,7 @@ func TestFindBond(t *testing.T) { }, { name: "bad msgtx", coinID: bond.CoinID, - txRes: txFn(nil, bond.SignedTx[1:]), + txRes: txFn(nil, bond.SignedTx[100:]), wantErr: true, }, { name: "get best block error", diff --git a/client/asset/dcr/rpcwallet.go b/client/asset/dcr/rpcwallet.go index c714bffd03..4cf81d1e12 100644 --- a/client/asset/dcr/rpcwallet.go +++ b/client/asset/dcr/rpcwallet.go @@ -134,7 +134,6 @@ type rpcClient interface { GetBlockHeaderVerbose(ctx context.Context, blockHash *chainhash.Hash) (*chainjson.GetBlockHeaderVerboseResult, error) GetBlockHeader(ctx context.Context, blockHash *chainhash.Hash) (*wire.BlockHeader, error) GetRawMempool(ctx context.Context, txType chainjson.GetRawMempoolTxTypeCmd) ([]*chainhash.Hash, error) - GetRawTransaction(ctx context.Context, txHash *chainhash.Hash) (*dcrutil.Tx, error) LockUnspent(ctx context.Context, unlock bool, ops []*wire.OutPoint) error GetRawChangeAddress(ctx context.Context, account string, net stdaddr.AddressParams) (stdaddr.Address, error) GetNewAddressGapPolicy(ctx context.Context, account string, gap dcrwallet.GapPolicy) (stdaddr.Address, error) @@ -755,40 +754,18 @@ func (w *rpcWallet) GetTransaction(ctx context.Context, txHash *chainhash.Hash) } return nil, fmt.Errorf("error finding transaction %s in wallet: %w", txHash, translateRPCCancelErr(err)) } + msgTx, err := msgTxFromHex(tx.Hex) + if err != nil { + return nil, fmt.Errorf("invalid tx hex %s: %w", tx.Hex, err) + } return &WalletTransaction{ Confirmations: tx.Confirmations, BlockHash: tx.BlockHash, Details: tx.Details, - Hex: tx.Hex, + MsgTx: msgTx, }, nil } -// GetRawTransaction returns details of the tx with the provided hash. Returns -// asset.CoinNotFoundError if the tx is not found. -// Part of the Wallet interface. -func (w *rpcWallet) GetRawTransaction(ctx context.Context, txHash *chainhash.Hash) (*wire.MsgTx, error) { - if w.spvMode { - gtr, err := w.rpcClient.GetTransaction(ctx, txHash) - if err != nil { - return nil, err - } - - txB, err := hex.DecodeString(gtr.Hex) - if err != nil { - return nil, err - } - - return msgTxFromBytes(txB) - } - - utilTx, err := w.rpcClient.GetRawTransaction(ctx, txHash) - if err != nil { - return nil, err - } - - return utilTx.MsgTx(), nil -} - func (w *rpcWallet) ListSinceBlock(ctx context.Context, start int32) ([]ListTransactionsResult, error) { hash, err := w.GetBlockHash(ctx, int64(start)) if err != nil { diff --git a/client/asset/dcr/simnet_test.go b/client/asset/dcr/simnet_test.go index 1358f48a4b..e2cceb3cb9 100644 --- a/client/asset/dcr/simnet_test.go +++ b/client/asset/dcr/simnet_test.go @@ -460,11 +460,7 @@ func runTest(t *testing.T, splitTx bool) { if err != nil || tx == nil { t.Fatalf("GetTransaction: %v", err) } - msgTx, err := msgTxFromHex(tx.Hex) - if err != nil { - t.Fatalf("msgTxFromHex: %v", err) - } - txData, err := msgTx.Bytes() + txData, err := tx.MsgTx.Bytes() if err != nil { t.Fatalf("msgTx.Bytes: %v", err) } diff --git a/client/asset/dcr/spv.go b/client/asset/dcr/spv.go index 31f4c14d63..7861a53e90 100644 --- a/client/asset/dcr/spv.go +++ b/client/asset/dcr/spv.go @@ -14,7 +14,6 @@ import ( "os" "path/filepath" "sort" - "strings" "sync" "sync/atomic" "time" @@ -84,7 +83,6 @@ type dcrWallet interface { UnlockAccount(ctx context.Context, account uint32, passphrase []byte) error LoadPrivateKey(ctx context.Context, addr stdaddr.Address) (key *secp256k1.PrivateKey, zero func(), err error) TxDetails(ctx context.Context, txHash *chainhash.Hash) (*udb.TxDetails, error) - GetTransactionsByHashes(ctx context.Context, txHashes []*chainhash.Hash) (txs []*wire.MsgTx, notFound []*wire.InvVect, err error) StakeInfo(ctx context.Context) (*wallet.StakeInfoData, error) PurchaseTickets(ctx context.Context, n wallet.NetworkBackend, req *wallet.PurchaseTicketsRequest) (*wallet.PurchaseTicketsResponse, error) ForUnspentUnexpiredTickets(ctx context.Context, f func(hash *chainhash.Hash) error) error @@ -859,15 +857,8 @@ func (w *spvWallet) GetTransaction(ctx context.Context, txHash *chainhash.Hash) _, tipHeight := w.MainChainTip(ctx) - var b strings.Builder - b.Grow(2 * txd.MsgTx.SerializeSize()) - err = txd.MsgTx.Serialize(hex.NewEncoder(&b)) - if err != nil { - return nil, err - } - ret := WalletTransaction{ - Hex: b.String(), + MsgTx: &txd.MsgTx, } if txd.Block.Height != -1 { @@ -910,20 +901,6 @@ func (w *spvWallet) MatchAnyScript(ctx context.Context, blockHash *chainhash.Has } -// GetRawTransaction returns details of the tx with the provided hash. Returns -// asset.CoinNotFoundError if the tx is not found. -// Part of the Wallet interface. -func (w *spvWallet) GetRawTransaction(ctx context.Context, txHash *chainhash.Hash) (*wire.MsgTx, error) { - txs, _, err := w.dcrWallet.GetTransactionsByHashes(ctx, []*chainhash.Hash{txHash}) - if err != nil { - return nil, err - } - if len(txs) != 1 { - return nil, asset.CoinNotFoundError - } - return txs[0], nil -} - // GetBestBlock returns the hash and height of the wallet's best block. // Part of the Wallet interface. func (w *spvWallet) GetBestBlock(ctx context.Context) (*chainhash.Hash, int64, error) { diff --git a/client/asset/dcr/spv_test.go b/client/asset/dcr/spv_test.go index d8ea4dc1f8..1ff3b4d5c9 100644 --- a/client/asset/dcr/spv_test.go +++ b/client/asset/dcr/spv_test.go @@ -949,30 +949,6 @@ func TestMatchAnyScript(t *testing.T) { } } -func TestGetRawTransaction(t *testing.T) { - w, dcrw := tNewSpvWallet() - - dcrw.txsByHash = []*wire.MsgTx{{}} - - if _, err := w.GetRawTransaction(tCtx, nil); err != nil { - t.Fatalf("intitial GetRawTransaction error: %v", err) - } - - // TxDetails NotExist error - dcrw.txsByHash = []*wire.MsgTx{} - if _, err := w.GetRawTransaction(tCtx, nil); !errors.Is(err, asset.CoinNotFoundError) { - t.Fatalf("expected asset.CoinNotFoundError, got %v", err) - } - dcrw.txsByHash = []*wire.MsgTx{{}} - - // TxDetails generic error - dcrw.txsByHashErr = tErr - if _, err := w.GetRawTransaction(tCtx, nil); err == nil { - t.Fatalf("expected TxDetail generic error to propagate") - } - dcrw.txsByHashErr = nil -} - func TestBirthdayBlockHeight(t *testing.T) { spvw, dcrw := tNewSpvWallet() diff --git a/client/asset/dcr/wallet.go b/client/asset/dcr/wallet.go index 3e7baee987..45aa18a612 100644 --- a/client/asset/dcr/wallet.go +++ b/client/asset/dcr/wallet.go @@ -139,9 +139,6 @@ type Wallet interface { // tx with the provided hash. Returns asset.CoinNotFoundError if the tx is not // found in the wallet. GetTransaction(ctx context.Context, txHash *chainhash.Hash) (*WalletTransaction, error) - // GetRawTransaction returns details of the tx with the provided hash. - // Returns asset.CoinNotFoundError if the tx is not found. - GetRawTransaction(ctx context.Context, txHash *chainhash.Hash) (*wire.MsgTx, error) // GetBestBlock returns the hash and height of the wallet's best block. GetBestBlock(ctx context.Context) (*chainhash.Hash, int64, error) // GetBlockHash returns the hash of the mainchain block at the specified height. @@ -186,7 +183,7 @@ type WalletTransaction struct { Confirmations int64 BlockHash string Details []walletjson.GetTransactionDetailsResult - Hex string + MsgTx *wire.MsgTx } // tipNotifier can be implemented if the Wallet is able to provide a stream of