Merge pull request #6321 from priyanshiiit/walletrpc

lnrpc+lnwallet: add previous_outpoints to listchaintxns
This commit is contained in:
Oliver Gugger 2022-06-27 18:15:32 +02:00 committed by GitHub
commit 0df880139f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 4229 additions and 3876 deletions

View File

@ -9,7 +9,14 @@
* [Add minor comment](https://github.com/lightningnetwork/lnd/pull/6559) on
subscribe/cancel/lookup invoice parameter encoding.
## RPC Server
* [Add previous_outpoints to listchaintxns](https://github.com/lightningnetwork/lnd/pull/6321)
# Contributors (Alphabetical Order)
* Elle Mouton
* ErikEk
* Priyansh Rastogi

File diff suppressed because it is too large Load Diff

View File

@ -686,7 +686,11 @@ message Transaction {
// A label that was optionally set on transaction broadcast.
string label = 10;
// PreviousOutpoints/Inputs of this transaction.
repeated PreviousOutPoint previous_outpoints = 12;
}
message GetTransactionsRequest {
/*
The height from which to list transactions, inclusive. If this value is
@ -1007,6 +1011,15 @@ message OutPoint {
uint32 output_index = 3;
}
message PreviousOutPoint {
// The outpoint in format txid:n.
string outpoint = 1;
// Denotes if the outpoint is controlled by the internal wallet.
// The flag will only detect p2wkh, np2wkh and p2tr inputs as its own.
bool is_our_output = 2;
}
message LightningAddress {
// The identity pubkey of the Lightning node.
string pubkey = 1;

View File

@ -5879,6 +5879,19 @@
}
}
},
"lnrpcPreviousOutPoint": {
"type": "object",
"properties": {
"outpoint": {
"type": "string",
"description": "The outpoint in format txid:n."
},
"is_our_output": {
"type": "boolean",
"description": "Denotes if the outpoint is controlled by the internal wallet.\nThe flag will only detect p2wkh, np2wkh and p2tr inputs as its own."
}
}
},
"lnrpcPsbtShim": {
"type": "object",
"properties": {
@ -6501,6 +6514,13 @@
"label": {
"type": "string",
"description": "A label that was optionally set on transaction broadcast."
},
"previous_outpoints": {
"type": "array",
"items": {
"$ref": "#/definitions/lnrpcPreviousOutPoint"
},
"description": "PreviousOutpoints/Inputs of this transaction."
}
}
},

View File

@ -45,6 +45,14 @@ func RPCTransaction(tx *lnwallet.TransactionDetail) *Transaction {
})
}
previousOutpoints := make([]*PreviousOutPoint, len(tx.PreviousOutpoints))
for idx, previousOutPoint := range tx.PreviousOutpoints {
previousOutpoints[idx] = &PreviousOutPoint{
Outpoint: previousOutPoint.OutPoint,
IsOurOutput: previousOutPoint.IsOurOutput,
}
}
// We also get unconfirmed transactions, so BlockHash can be nil.
blockHash := ""
if tx.BlockHash != nil {
@ -52,17 +60,18 @@ func RPCTransaction(tx *lnwallet.TransactionDetail) *Transaction {
}
return &Transaction{
TxHash: tx.Hash.String(),
Amount: int64(tx.Value),
NumConfirmations: tx.NumConfirmations,
BlockHash: blockHash,
BlockHeight: tx.BlockHeight,
TimeStamp: tx.Timestamp,
TotalFees: tx.TotalFees,
DestAddresses: destAddresses,
OutputDetails: outputDetails,
RawTxHex: hex.EncodeToString(tx.RawTx),
Label: tx.Label,
TxHash: tx.Hash.String(),
Amount: int64(tx.Value),
NumConfirmations: tx.NumConfirmations,
BlockHash: blockHash,
BlockHeight: tx.BlockHeight,
TimeStamp: tx.Timestamp,
TotalFees: tx.TotalFees,
DestAddresses: destAddresses,
OutputDetails: outputDetails,
RawTxHex: hex.EncodeToString(tx.RawTx),
Label: tx.Label,
PreviousOutpoints: previousOutpoints,
}
}

View File

@ -769,6 +769,19 @@
],
"default": "SCRIPT_TYPE_PUBKEY_HASH"
},
"lnrpcPreviousOutPoint": {
"type": "object",
"properties": {
"outpoint": {
"type": "string",
"description": "The outpoint in format txid:n."
},
"is_our_output": {
"type": "boolean",
"description": "Denotes if the outpoint is controlled by the internal wallet.\nThe flag will only detect p2wkh, np2wkh and p2tr inputs as its own."
}
}
},
"lnrpcTransaction": {
"type": "object",
"properties": {
@ -826,6 +839,13 @@
"label": {
"type": "string",
"description": "A label that was optionally set on transaction broadcast."
},
"previous_outpoints": {
"type": "array",
"items": {
"$ref": "#/definitions/lnrpcPreviousOutPoint"
},
"description": "PreviousOutpoints/Inputs of this transaction."
}
}
},

View File

@ -199,6 +199,18 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
fn(node)
}
// Check if the previous outpoints are set correctly.
req := &lnrpc.GetTransactionsRequest{
StartHeight: 0,
EndHeight: -1,
}
txDetails, err := node.GetTransactions(ctxb, req)
require.NoError(t.t, err)
for _, tx := range txDetails.Transactions {
require.Greater(t.t, len(tx.PreviousOutpoints), 0)
}
// Lastly, shutdown this Carol so we can move on to the next
// restoration.
shutdownAndAssert(net, t, node)

View File

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

View File

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

View File

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