Skip to content

Commit 4165047

Browse files
committed
taprpc+rpcserver: add group key to DecodeAssetPayReq
Since we support group keys in all other payment related commands, we now add the same argument for the invoice decoding RPC.
1 parent acfb20a commit 4165047

File tree

4 files changed

+239
-70
lines changed

4 files changed

+239
-70
lines changed

rpcserver.go

Lines changed: 157 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8581,29 +8581,73 @@ func (r *rpcServer) assetInvoiceAmt(ctx context.Context,
85818581
func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
85828582
payReq *tchrpc.AssetPayReq) (*tchrpc.AssetPayReqResponse, error) {
85838583

8584+
tapdLog.Debugf("Decoding asset pay req, asset_id=%x, group_key=%x,"+
8585+
"pay_req=%v", payReq.AssetId, payReq.GroupKey,
8586+
payReq.PayReqString)
8587+
85848588
if r.cfg.PriceOracle == nil {
85858589
return nil, fmt.Errorf("price oracle is not set")
85868590
}
85878591

85888592
// First, we'll perform some basic input validation.
8593+
var assetID asset.ID
85898594
switch {
8590-
case len(payReq.AssetId) == 0:
8591-
return nil, fmt.Errorf("asset ID must be specified")
8595+
case len(payReq.AssetId) == 0 && len(payReq.GroupKey) == 0:
8596+
return nil, fmt.Errorf("either asset ID or group key must be " +
8597+
"specified")
85928598

8593-
case len(payReq.AssetId) != 32:
8599+
case len(payReq.AssetId) != 0 && len(payReq.GroupKey) != 0:
8600+
return nil, fmt.Errorf("cannot set both asset ID and group key")
8601+
8602+
case len(payReq.AssetId) != 0 && len(payReq.AssetId) != 32:
85948603
return nil, fmt.Errorf("asset ID must be 32 bytes, "+
85958604
"was %d", len(payReq.AssetId))
85968605

8606+
case len(payReq.GroupKey) != 0 && len(payReq.GroupKey) != 33 &&
8607+
len(payReq.GroupKey) != 32:
8608+
8609+
return nil, fmt.Errorf("group key must be 32 or 33 bytes, "+
8610+
"was %d", len(payReq.GroupKey))
8611+
85978612
case len(payReq.PayReqString) == 0:
85988613
return nil, fmt.Errorf("payment request must be specified")
85998614
}
86008615

8601-
var (
8602-
resp tchrpc.AssetPayReqResponse
8603-
assetID asset.ID
8604-
)
8616+
// We made sure that only one is set, so let's now use the asset ID
8617+
// or group key.
8618+
switch {
8619+
// The asset ID is easy, we can just copy the bytes.
8620+
case len(payReq.AssetId) != 0:
8621+
copy(assetID[:], payReq.AssetId)
8622+
8623+
// The group key is a bit more involved. We first need to sync the asset
8624+
// group, then fetch the leaves that are associated with this group key.
8625+
// From there, we can look up the asset ID of one of the group's
8626+
// tranches.
8627+
case len(payReq.GroupKey) != 0:
8628+
var (
8629+
groupKey *btcec.PublicKey
8630+
err error
8631+
)
8632+
if len(payReq.GroupKey) == 32 {
8633+
groupKey, err = schnorr.ParsePubKey(payReq.GroupKey)
8634+
} else {
8635+
groupKey, err = btcec.ParsePubKey(payReq.GroupKey)
8636+
}
8637+
if err != nil {
8638+
return nil, fmt.Errorf("error parsing group "+
8639+
"key: %w", err)
8640+
}
8641+
8642+
assetID, err = r.syncAssetGroup(ctx, *groupKey)
8643+
if err != nil {
8644+
return nil, fmt.Errorf("error syncing asset group: %w",
8645+
err)
8646+
}
86058647

8606-
copy(assetID[:], payReq.AssetId)
8648+
tapdLog.Debugf("Resolved asset ID %v for group key %x",
8649+
assetID.String(), groupKey.SerializeCompressed())
8650+
}
86078651

86088652
// With the inputs validated, we'll first call out to lnd to decode the
86098653
// payment request.
@@ -8615,7 +8659,9 @@ func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
86158659
return nil, fmt.Errorf("unable to fetch channel: %w", err)
86168660
}
86178661

8618-
resp.PayReq = payReqInfo
8662+
resp := tchrpc.AssetPayReqResponse{
8663+
PayReq: payReqInfo,
8664+
}
86198665

86208666
// Next, we'll fetch the information for this asset ID through the addr
86218667
// book. This'll automatically fetch the asset if needed.
@@ -8625,12 +8671,17 @@ func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
86258671
"asset_id=%x: %w", assetID[:], err)
86268672
}
86278673

8628-
resp.GenesisInfo = &taprpc.GenesisInfo{
8629-
GenesisPoint: assetGroup.FirstPrevOut.String(),
8630-
AssetType: taprpc.AssetType(assetGroup.Type),
8631-
Name: assetGroup.Tag,
8632-
MetaHash: assetGroup.MetaHash[:],
8633-
AssetId: assetID[:],
8674+
// The genesis info makes no sense in the case where we have decoded the
8675+
// invoice for a group key, since we just pick the first asset ID we
8676+
// found for a group key.
8677+
if len(payReq.GroupKey) == 0 {
8678+
resp.GenesisInfo = &taprpc.GenesisInfo{
8679+
GenesisPoint: assetGroup.FirstPrevOut.String(),
8680+
AssetType: taprpc.AssetType(assetGroup.Type),
8681+
Name: assetGroup.Tag,
8682+
MetaHash: assetGroup.MetaHash[:],
8683+
AssetId: assetID[:],
8684+
}
86348685
}
86358686

86368687
// If this asset ID belongs to an asset group, then we'll display that
@@ -8690,6 +8741,97 @@ func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
86908741
return &resp, nil
86918742
}
86928743

8744+
// syncAssetGroup checks if we already know any asset leaves associated with
8745+
// this group key. If not, it will sync the asset group and return the asset
8746+
// ID of the first leaf found. If there are no leaves found after syncing, an
8747+
// error is returned.
8748+
func (r *rpcServer) syncAssetGroup(ctx context.Context,
8749+
groupKey btcec.PublicKey) (asset.ID, error) {
8750+
8751+
// We first check if we already know any asset leaves associated with
8752+
// this group key.
8753+
leaf := fn.NewRight[asset.ID](groupKey)
8754+
leaves, err := r.cfg.Multiverse.FetchLeaves(
8755+
ctx, []universe.MultiverseLeafDesc{leaf},
8756+
universe.ProofTypeIssuance,
8757+
)
8758+
if err != nil {
8759+
return asset.ID{}, fmt.Errorf("error fetching leaves: %w", err)
8760+
}
8761+
8762+
tapdLog.Tracef("Found %d leaves for group key %x",
8763+
len(leaves), groupKey.SerializeCompressed())
8764+
8765+
// If there are no leaves, then we need to sync the asset group.
8766+
if len(leaves) == 0 {
8767+
tapdLog.Debugf("No leaves found for group key %x, "+
8768+
"syncing asset group", groupKey.SerializeCompressed())
8769+
err = r.cfg.AddrBook.SyncAssetGroup(ctx, groupKey)
8770+
if err != nil {
8771+
return asset.ID{}, fmt.Errorf("error syncing asset "+
8772+
"group: %w", err)
8773+
}
8774+
8775+
// Now we can try again.
8776+
leaves, err = r.cfg.Multiverse.FetchLeaves(
8777+
ctx, []universe.MultiverseLeafDesc{leaf},
8778+
universe.ProofTypeIssuance,
8779+
)
8780+
if err != nil {
8781+
return asset.ID{}, fmt.Errorf("error fetching leaves: "+
8782+
"%w", err)
8783+
}
8784+
8785+
tapdLog.Tracef("Found %d leaves for group key %x",
8786+
len(leaves), groupKey.SerializeCompressed())
8787+
8788+
if len(leaves) == 0 {
8789+
return asset.ID{}, fmt.Errorf("no asset leaves found "+
8790+
"for group %x after sync",
8791+
groupKey.SerializeCompressed())
8792+
}
8793+
}
8794+
8795+
// Since we know we have at least one leaf, we can just take leaf ID and
8796+
// query the keys with it. We just need one so we can fetch the actual
8797+
// proof to find out the asset ID. We don't really care about the order,
8798+
// so we just use the natural database order.
8799+
leafID := leaves[0]
8800+
leafKeys, err := r.cfg.Multiverse.UniverseLeafKeys(
8801+
ctx, universe.UniverseLeafKeysQuery{
8802+
Id: leafID.ID,
8803+
Limit: 1,
8804+
},
8805+
)
8806+
if err != nil {
8807+
return asset.ID{}, fmt.Errorf("error fetching leaf keys: %w",
8808+
err)
8809+
}
8810+
8811+
// We know we have a leaf, so this shouldn't happen.
8812+
if len(leafKeys) != 1 {
8813+
return asset.ID{}, fmt.Errorf("expected 1 leaf key, got %d",
8814+
len(leafKeys))
8815+
}
8816+
8817+
proofs, err := r.cfg.Multiverse.FetchProofLeaf(
8818+
ctx, leafID.ID, leafKeys[0],
8819+
)
8820+
if err != nil {
8821+
return asset.ID{}, fmt.Errorf("error fetching proof leaf: %w",
8822+
err)
8823+
}
8824+
8825+
// We should have a proof for the asset ID now.
8826+
if len(proofs) != 1 {
8827+
return asset.ID{}, fmt.Errorf("expected 1 proof, got %d",
8828+
len(proofs))
8829+
}
8830+
8831+
// We can now extract the asset ID from the proof.
8832+
return proofs[0].Leaf.ID(), nil
8833+
}
8834+
86938835
// RegisterTransfer informs the daemon about a new inbound transfer that has
86948836
// happened. This is used for interactive transfers where no TAP address is
86958837
// involved and the recipient is aware of the transfer through an out-of-band

taprpc/tapchannelrpc/tapchannel.pb.go

Lines changed: 67 additions & 52 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)