From 61493a5f29f4f52b83ea5aa4f94e5e6452e6221a Mon Sep 17 00:00:00 2001 From: priyanshiiit Date: Mon, 27 Jun 2022 15:45:49 +0530 Subject: [PATCH] lnwallet: add previous_outpoints to ListTransactionDetails --- lnwallet/btcwallet/btcwallet.go | 63 ++++++++++--- lnwallet/btcwallet/btcwallet_test.go | 134 +++++++++++++++++++++++++++ lnwallet/interface.go | 14 +++ 3 files changed, 196 insertions(+), 15 deletions(-) create mode 100644 lnwallet/btcwallet/btcwallet_test.go diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index 1b0464d88..40e4a9867 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -18,6 +18,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/chain" "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/btcsuite/btcwallet/wallet" base "github.com/btcsuite/btcwallet/wallet" "github.com/btcsuite/btcwallet/wallet/txauthor" "github.com/btcsuite/btcwallet/wallet/txrules" @@ -1104,6 +1105,32 @@ func extractBalanceDelta( return balanceDelta, nil } +// getPreviousOutpoints is a helper function which gets the previous +// outpoints of a transaction. +func getPreviousOutpoints(wireTx *wire.MsgTx, + myInputs []wallet.TransactionSummaryInput) []lnwallet.PreviousOutPoint { + + // isOurOutput is a map containing the output indices + // controlled by the wallet. + // Note: We make use of the information in `myInputs` provided + // by the `wallet.TransactionSummary` structure that holds + // information only if the input/previous_output is controlled by the wallet. + isOurOutput := make(map[uint32]bool, len(myInputs)) + for _, myInput := range myInputs { + isOurOutput[myInput.Index] = true + } + + previousOutpoints := make([]lnwallet.PreviousOutPoint, len(wireTx.TxIn)) + for idx, txIn := range wireTx.TxIn { + previousOutpoints[idx] = lnwallet.PreviousOutPoint{ + OutPoint: txIn.PreviousOutPoint.String(), + IsOurOutput: isOurOutput[uint32(idx)], + } + } + + return previousOutpoints +} + // minedTransactionsToDetails is a helper function which converts a summary // information about mined transactions to a TransactionDetail. func minedTransactionsToDetails( @@ -1152,16 +1179,19 @@ func minedTransactionsToDetails( }) } + previousOutpoints := getPreviousOutpoints(wireTx, tx.MyInputs) + txDetail := &lnwallet.TransactionDetail{ - Hash: *tx.Hash, - NumConfirmations: currentHeight - block.Height + 1, - BlockHash: block.Hash, - BlockHeight: block.Height, - Timestamp: block.Timestamp, - TotalFees: int64(tx.Fee), - OutputDetails: outputDetails, - RawTx: tx.Transaction, - Label: tx.Label, + Hash: *tx.Hash, + NumConfirmations: currentHeight - block.Height + 1, + BlockHash: block.Hash, + BlockHeight: block.Height, + Timestamp: block.Timestamp, + TotalFees: int64(tx.Fee), + OutputDetails: outputDetails, + RawTx: tx.Transaction, + Label: tx.Label, + PreviousOutpoints: previousOutpoints, } balanceDelta, err := extractBalanceDelta(tx, wireTx) @@ -1221,13 +1251,16 @@ func unminedTransactionsToDetail( }) } + previousOutpoints := getPreviousOutpoints(wireTx, summary.MyInputs) + txDetail := &lnwallet.TransactionDetail{ - Hash: *summary.Hash, - TotalFees: int64(summary.Fee), - Timestamp: summary.Timestamp, - OutputDetails: outputDetails, - RawTx: summary.Transaction, - Label: summary.Label, + Hash: *summary.Hash, + TotalFees: int64(summary.Fee), + Timestamp: summary.Timestamp, + OutputDetails: outputDetails, + RawTx: summary.Transaction, + Label: summary.Label, + PreviousOutpoints: previousOutpoints, } balanceDelta, err := extractBalanceDelta(summary, wireTx) diff --git a/lnwallet/btcwallet/btcwallet_test.go b/lnwallet/btcwallet/btcwallet_test.go new file mode 100644 index 000000000..28b783acc --- /dev/null +++ b/lnwallet/btcwallet/btcwallet_test.go @@ -0,0 +1,134 @@ +package btcwallet + +import ( + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcwallet/wallet" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/stretchr/testify/require" +) + +type previousOutpointsTest struct { + name string + tx *wire.MsgTx + myInputs []wallet.TransactionSummaryInput + expRes []lnwallet.PreviousOutPoint +} + +var previousOutpointsTests = []previousOutpointsTest{{ + name: "both outpoints are wallet controlled", + tx: &wire.MsgTx{ + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{Index: 0}, + }, { + PreviousOutPoint: wire.OutPoint{Index: 1}, + }}, + }, + myInputs: []wallet.TransactionSummaryInput{{ + Index: 0, + }, { + Index: 1, + }}, + expRes: []lnwallet.PreviousOutPoint{{ + OutPoint: wire.OutPoint{Index: 0}.String(), + IsOurOutput: true, + }, { + OutPoint: wire.OutPoint{Index: 1}.String(), + IsOurOutput: true, + }}, +}, { + name: "only one outpoint is wallet controlled", + tx: &wire.MsgTx{ + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{Index: 0}, + }, { + PreviousOutPoint: wire.OutPoint{Index: 1}, + }}, + }, + myInputs: []wallet.TransactionSummaryInput{{ + Index: 0, + }, { + Index: 2, + }}, + expRes: []lnwallet.PreviousOutPoint{{ + OutPoint: wire.OutPoint{Index: 0}.String(), + IsOurOutput: true, + }, { + OutPoint: wire.OutPoint{Index: 1}.String(), + IsOurOutput: false, + }}, +}, { + name: "no outpoint is wallet controlled", + tx: &wire.MsgTx{ + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{Index: 0}, + }, { + PreviousOutPoint: wire.OutPoint{Index: 1}, + }}, + }, + myInputs: []wallet.TransactionSummaryInput{{ + Index: 2, + }, { + Index: 3, + }}, + expRes: []lnwallet.PreviousOutPoint{{ + OutPoint: wire.OutPoint{Index: 0}.String(), + IsOurOutput: false, + }, { + OutPoint: wire.OutPoint{Index: 1}.String(), + IsOurOutput: false, + }}, +}, { + name: "tx is empty", + tx: &wire.MsgTx{ + TxIn: []*wire.TxIn{}, + }, + myInputs: []wallet.TransactionSummaryInput{{ + Index: 2, + }, { + Index: 3, + }}, + expRes: []lnwallet.PreviousOutPoint{}, +}, { + name: "wallet controlled input set is empty", + tx: &wire.MsgTx{ + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{Index: 0}, + }, { + PreviousOutPoint: wire.OutPoint{Index: 1}, + }}, + }, + myInputs: []wallet.TransactionSummaryInput{}, + expRes: []lnwallet.PreviousOutPoint{{ + OutPoint: wire.OutPoint{Index: 0}.String(), + IsOurOutput: false, + }, { + OutPoint: wire.OutPoint{Index: 1}.String(), + IsOurOutput: false, + }}, +}} + +// TestPreviousOutpoints tests if we are able to get the previous +// outpoints correctly. +func TestPreviousOutpoints(t *testing.T) { + for _, test := range previousOutpointsTests { + t.Run(test.name, func(t *testing.T) { + respOutpoints := getPreviousOutpoints( + test.tx, test.myInputs, + ) + + for idx, respOutpoint := range respOutpoints { + expRes := test.expRes[idx] + require.Equal( + t, expRes.OutPoint, + respOutpoint.OutPoint, + ) + require.Equal( + t, expRes.IsOurOutput, + respOutpoint.IsOurOutput, + ) + } + }) + } +} diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 309e0a4b8..71602789e 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -98,6 +98,17 @@ type OutputDetail struct { IsOurAddress bool } +// PreviousOutPoint contains information about the previous outpoint. +type PreviousOutPoint struct { + // OutPoint is the transaction out point in the format txid:n. + OutPoint string + + // IsOurOutput denotes if the previous output is controlled by the + // internal wallet. The flag will only detect p2wkh, np2wkh and p2tr + // inputs as its own. + IsOurOutput bool +} + // TransactionDetail describes a transaction with either inputs which belong to // the wallet, or has outputs that pay to the wallet. type TransactionDetail struct { @@ -141,6 +152,9 @@ type TransactionDetail struct { // Label is an optional transaction label. Label string + + // PreviousOutpoints are the inputs for a transaction. + PreviousOutpoints []PreviousOutPoint } // TransactionSubscription is an interface which describes an object capable of