Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement PrevId[] UTXO selection for FundVirtualPSBT RPC method; valid for single PrevId at first #1172

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2049,9 +2049,31 @@ func (r *rpcServer) FundVirtualPsbt(ctx context.Context,

case req.GetRaw() != nil:
raw := req.GetRaw()
prevIDs := []asset.PrevID{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: var prevIDs []asset.PrevID style is preferred over empty slice initialization.

if len(raw.Inputs) > 0 {
return nil, fmt.Errorf("template inputs not yet " +
"supported")
for _, input := range raw.Inputs {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove a whole level of indentation by removing the if len(raw.Inputs) > 0 { since for ... range loops are nil safe.

// Create a new chainhash.Hash and set its bytes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: comments should always be full sentences and end with punctuation.

var hash chainhash.Hash
err := hash.SetBytes(input.Outpoint.Txid)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Should use chainhash.NewHash() instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably check that input.Outpoint is not nil.

if err != nil {
return nil, fmt.Errorf(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Multi-line calls to formatting function should follow the different form to more easily distinguish them from non-error/non-logging calls. See https://github.com/lightningnetwork/lnd/blob/master/docs/code_formatting_rules.md#exception-for-log-and-error-message-formatting.

"invalid Txid length: %w", err,
)
}
// Decode the input into an asset.PrevID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if a comment isn't at the beginning of a new block/indentation, we usually add an empty line before to give some breathing space.

outpoint := wire.OutPoint{
Hash: hash,
Index: input.Outpoint.OutputIndex,
}
prevID := asset.PrevID{
OutPoint: outpoint,
ID: asset.ID(input.Id),
ScriptKey: asset.SerializedKey(
input.ScriptKey,
),
}
prevIDs = append(prevIDs, prevID)
}
}
if len(raw.Recipients) > 1 {
return nil, fmt.Errorf("only one recipient supported")
Expand All @@ -2074,7 +2096,7 @@ func (r *rpcServer) FundVirtualPsbt(ctx context.Context,
}

fundedVPkt, err = r.cfg.AssetWallet.FundAddressSend(
ctx, coinSelectType, addr,
ctx, coinSelectType, prevIDs, addr,
)
if err != nil {
return nil, fmt.Errorf("error funding address send: "+
Expand Down
43 changes: 42 additions & 1 deletion tapdb/assets_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -1991,7 +1991,48 @@ func (a *AssetStore) ListEligibleCoins(ctx context.Context,
// have a block height of 0, so we set the minimum block height to 1.
assetFilter.MinAnchorHeight = sqlInt32(1)

return a.queryCommitments(ctx, assetFilter)
selectedCommitments, err := a.queryCommitments(ctx, assetFilter)
if err != nil {
return nil, fmt.Errorf("unable to query commitments: %w", err)
}

// If we want to restrict on specific inputs, we do the filtering now.
if len(constraints.PrevIDs) > 0 {
selectedCommitments = filterCommitmentsByPrevIDs(
selectedCommitments, constraints.PrevIDs,
)

// If this results in an empty list, we return the same error we
// would if there were no coins found without the filter.
if len(selectedCommitments) == 0 {
return nil, tapfreighter.ErrMatchingAssetsNotFound
}
}

return selectedCommitments, nil
}

// filterCommitmentsByPrevIDs filters the given commitments by the previous IDs
// given.
func filterCommitmentsByPrevIDs(commitments []*tapfreighter.AnchoredCommitment,
prevIDs []asset.PrevID) []*tapfreighter.AnchoredCommitment {

prevIDMatches := func(p asset.PrevID,
c *tapfreighter.AnchoredCommitment) bool {

return p.OutPoint == c.AnchorPoint && p.ID == c.Asset.ID() &&
p.ScriptKey == asset.ToSerialized(
c.Asset.ScriptKey.PubKey,
)
}

commitmentInList := func(c *tapfreighter.AnchoredCommitment) bool {
return fn.Any(prevIDs, func(p asset.PrevID) bool {
return prevIDMatches(p, c)
})
}

return fn.Filter(commitments, commitmentInList)
}

// LeaseCoins leases/locks/reserves coins for the given lease owner until the
Expand Down
2 changes: 1 addition & 1 deletion tapfreighter/chain_porter.go
Original file line number Diff line number Diff line change
Expand Up @@ -994,7 +994,7 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
"address parcel")
}
fundSendRes, err := p.cfg.AssetWallet.FundAddressSend(
ctx, tapsend.Bip86Only, addrParcel.destAddrs...,
ctx, tapsend.Bip86Only, nil, addrParcel.destAddrs...,
)
if err != nil {
return nil, fmt.Errorf("unable to fund address send: "+
Expand Down
1 change: 1 addition & 0 deletions tapfreighter/coin_select.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func (s *CoinSelect) SelectCoins(ctx context.Context,
AssetSpecifier: constraints.AssetSpecifier,
MinAmt: 1,
CoinSelectType: constraints.CoinSelectType,
PrevIDs: constraints.PrevIDs,
}
eligibleCommitments, err := s.coinLister.ListEligibleCoins(
ctx, listConstraints,
Expand Down
3 changes: 3 additions & 0 deletions tapfreighter/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type CommitmentConstraints struct {
// to satisfy the constraints.
MinAmt uint64

// PrevIDs are the set of inputs allowed to be used
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: missing full stop.

PrevIDs []asset.PrevID

// CoinSelectType is the type of coins that should be selected.
CoinSelectType tapsend.CoinSelectType
}
Expand Down
8 changes: 8 additions & 0 deletions tapfreighter/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type Wallet interface {
// selected assets.
FundAddressSend(ctx context.Context,
coinSelectType tapsend.CoinSelectType,
prevIDs []asset.PrevID,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: seems to fit on previous line, here and below.

receiverAddrs ...*address.Tap) (*FundedVPacket, error)

// FundPacket funds a virtual transaction, selecting assets to spend
Expand Down Expand Up @@ -236,6 +237,7 @@ type FundedVPacket struct {
// NOTE: This is part of the Wallet interface.
func (f *AssetWallet) FundAddressSend(ctx context.Context,
coinSelectType tapsend.CoinSelectType,
prevIDs []asset.PrevID,
receiverAddrs ...*address.Tap) (*FundedVPacket, error) {

// We start by creating a new virtual transaction that will be used to
Expand All @@ -253,6 +255,11 @@ func (f *AssetWallet) FundAddressSend(ctx context.Context,
return nil, fmt.Errorf("unable to describe recipients: %w", err)
}

// We need to constrain the prevIDs if they are provided.
if len(prevIDs) > 0 {
fundDesc.PrevIDs = prevIDs
}

fundDesc.CoinSelectType = coinSelectType
fundedVPkt, err := f.FundPacket(ctx, fundDesc, vPkt)
if err != nil {
Expand Down Expand Up @@ -371,6 +378,7 @@ func (f *AssetWallet) FundPacket(ctx context.Context,
AssetSpecifier: fundDesc.AssetSpecifier,
MinAmt: fundDesc.Amount,
CoinSelectType: fundDesc.CoinSelectType,
PrevIDs: fundDesc.PrevIDs,
}

anchorVersion, err := tappsbt.CommitmentVersion(vPkt.Version)
Expand Down
3 changes: 3 additions & 0 deletions tapsend/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ type FundingDescriptor struct {
// Amount is the amount of the asset to transfer.
Amount uint64

// PrevIDs is the set of inputs that can be used to fund the transfer.
PrevIDs []asset.PrevID

// CoinSelectType specifies the type of coins that should be selected.
CoinSelectType CoinSelectType
}
Expand Down
Loading