Skip to content

Commit 95faab7

Browse files
authored
Merge pull request #923 from lightninglabs/decode-asset-pay-req-itest
tapd: add new CLI command for `decodeassetinvoice` along with corresponding itest for `DecodeAssetPayReq`
2 parents 411f881 + 002479e commit 95faab7

File tree

5 files changed

+201
-1
lines changed

5 files changed

+201
-1
lines changed

cmd/litcli/ln.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ var lnCommands = []cli.Command{
4343
sendPaymentCommand,
4444
payInvoiceCommand,
4545
addInvoiceCommand,
46+
decodeAssetInvoiceCommand,
4647
},
4748
},
4849
}
@@ -650,3 +651,65 @@ func addInvoice(ctx *cli.Context) error {
650651

651652
return nil
652653
}
654+
655+
var decodeAssetInvoiceCommand = cli.Command{
656+
Name: "decodeassetinvoice",
657+
Category: "Payments",
658+
Usage: "Decodes an LN invoice and displays the invoice's amount in asset " +
659+
"units specified by an asset ID",
660+
Description: `
661+
This command can be used to display the information encoded in an invoice.
662+
Given a chosen asset_id, the invoice's amount expressed in units of the asset
663+
will be displayed.
664+
665+
Other information such as the decimal display of an asset, and the asset
666+
group information (if applicable) are also shown.
667+
`,
668+
ArgsUsage: "--pay_req=X --asset_id=X",
669+
Flags: []cli.Flag{
670+
cli.StringFlag{
671+
Name: "pay_req",
672+
Usage: "a zpay32 encoded payment request to fulfill",
673+
},
674+
assetIDFlag,
675+
},
676+
Action: decodeAssetInvoice,
677+
}
678+
679+
func decodeAssetInvoice(ctx *cli.Context) error {
680+
ctxb := context.Background()
681+
682+
switch {
683+
case !ctx.IsSet("pay_req"):
684+
return fmt.Errorf("pay_req argument missing")
685+
case !ctx.IsSet(assetIDFlag.Name):
686+
return fmt.Errorf("the --asset_id flag must be set")
687+
}
688+
689+
payReq := ctx.String("pay_req")
690+
691+
assetIDStr := ctx.String(assetIDFlag.Name)
692+
assetIDBytes, err := hex.DecodeString(assetIDStr)
693+
if err != nil {
694+
return fmt.Errorf("unable to decode assetID: %v", err)
695+
}
696+
697+
tapdConn, cleanup, err := connectSuperMacClient(ctx)
698+
if err != nil {
699+
return fmt.Errorf("unable to make rpc con: %w", err)
700+
}
701+
defer cleanup()
702+
703+
channelsClient := tchrpc.NewTaprootAssetChannelsClient(tapdConn)
704+
resp, err := channelsClient.DecodeAssetPayReq(ctxb, &tchrpc.AssetPayReq{
705+
AssetId: assetIDBytes,
706+
PayReqString: payReq,
707+
})
708+
if err != nil {
709+
return fmt.Errorf("error adding invoice: %w", err)
710+
}
711+
712+
printRespJSON(resp)
713+
714+
return nil
715+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
github.com/lightninglabs/pool v0.6.5-beta.0.20241015105339-044cb451b5df
2424
github.com/lightninglabs/pool/auctioneerrpc v1.1.2
2525
github.com/lightninglabs/pool/poolrpc v1.0.0
26-
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241217185126-a9a2744061f1
26+
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241219112156-ecba8f87eb24
2727
github.com/lightningnetwork/lnd v0.18.4-beta.rc2
2828
github.com/lightningnetwork/lnd/cert v1.2.2
2929
github.com/lightningnetwork/lnd/fn v1.2.3

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,12 @@ github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display h1:w7FM5LH9
11791179
github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
11801180
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241217185126-a9a2744061f1 h1:YAiqidrUkbWLtCVMdW/j7WM1gOP7CXHqLNKIEowVAsA=
11811181
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241217185126-a9a2744061f1/go.mod h1:rkSWHSkPXX2k+PBOkEE1BA3L3qq5+Yv3m6LGkoH3tQk=
1182+
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241218162444-57c2454c050e h1:9YWq4/tGgrhRFKg7eyAtahxTMD6dQrl91xLFmd8KgmA=
1183+
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241218162444-57c2454c050e/go.mod h1:rkSWHSkPXX2k+PBOkEE1BA3L3qq5+Yv3m6LGkoH3tQk=
1184+
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241218173823-610300af2da1 h1:MaHjknFlcS4cosKsx3LSgPG4FoYQxXU84l5EFQTvP0w=
1185+
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241218173823-610300af2da1/go.mod h1:rkSWHSkPXX2k+PBOkEE1BA3L3qq5+Yv3m6LGkoH3tQk=
1186+
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241219112156-ecba8f87eb24 h1:dTVZCgUZ3CeQAKHLY+jyvMfhHQtutXT93hoKwgTsv2M=
1187+
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241219112156-ecba8f87eb24/go.mod h1:rkSWHSkPXX2k+PBOkEE1BA3L3qq5+Yv3m6LGkoH3tQk=
11821188
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY=
11831189
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI=
11841190
github.com/lightningnetwork/lnd v0.18.4-beta h1:4pGmIjIMisrs4TMDYp4fk8NeI1YFpcuqwaSiFwLcd1g=

itest/litd_custom_channels_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
2222
oraclerpc "github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
2323
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
24+
"github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
2425
tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
2526
"github.com/lightninglabs/taproot-assets/taprpc/universerpc"
2627
"github.com/lightninglabs/taproot-assets/tapscript"
@@ -3678,3 +3679,129 @@ func testCustomChannelsForwardBandwidth(ctxb context.Context,
36783679
universeTap, noOpCoOpCloseBalanceCheck,
36793680
)
36803681
}
3682+
3683+
// testCustomChannelsDecodeAssetInvoice tests that we're able to properly
3684+
// decode and display asset invoice related information.
3685+
//
3686+
// TODO(roasbeef): just move to tapd repo due to new version that doesn't req a
3687+
// chan?
3688+
func testCustomChannelsDecodeAssetInvoice(ctx context.Context,
3689+
net *NetworkHarness, t *harnessTest) {
3690+
3691+
// First, we'll set up some information for our custom oracle that we'll use
3692+
// to feed in price information.
3693+
oracleAddr := fmt.Sprintf("localhost:%d", port.NextAvailablePort())
3694+
oracle := newOracleHarness(oracleAddr)
3695+
oracle.start(t.t)
3696+
t.t.Cleanup(oracle.stop)
3697+
3698+
ctxb := context.Background()
3699+
lndArgs := slices.Clone(lndArgsTemplate)
3700+
litdArgs := slices.Clone(litdArgsTemplateNoOracle)
3701+
litdArgs = append(litdArgs, fmt.Sprintf(
3702+
"--taproot-assets.experimental.rfq.priceoracleaddress="+
3703+
"rfqrpc://%s", oracleAddr,
3704+
))
3705+
3706+
// For this test, Zane will be our dedicated Universe server for all parties.
3707+
zane, err := net.NewNode(
3708+
t.t, "Zane", lndArgs, false, true, litdArgs...,
3709+
)
3710+
require.NoError(t.t, err)
3711+
3712+
litdArgs = append(litdArgs, fmt.Sprintf(
3713+
"--taproot-assets.proofcourieraddr=%s://%s",
3714+
proof.UniverseRpcCourierType, zane.Cfg.LitAddr(),
3715+
))
3716+
3717+
// We'll just make a single node here, as this doesn't actually rely on a set
3718+
// of active channels.
3719+
alice, err := net.NewNode(t.t, "Alice", lndArgs, false, true, litdArgs...)
3720+
require.NoError(t.t, err)
3721+
aliceTap := newTapClient(t.t, alice)
3722+
3723+
// Fund Alice so she'll have enough funds to mint the asset.
3724+
fundAllNodes(t.t, net, []*HarnessNode{alice})
3725+
3726+
// Next, we'll make a new asset with a specified decimal display. We'll also
3727+
// make grouped asset as well.
3728+
usdMetaData := &taprpc.AssetMeta{
3729+
Data: []byte(`{
3730+
"description":"this is a USD stablecoin with decimal display of 6"
3731+
}`),
3732+
Type: taprpc.AssetMetaType_META_TYPE_JSON,
3733+
}
3734+
3735+
const decimalDisplay = 6
3736+
itestAsset = &mintrpc.MintAsset{
3737+
AssetType: taprpc.AssetType_NORMAL,
3738+
Name: "USD",
3739+
AssetMeta: usdMetaData,
3740+
// We mint 1 million USD with a decimal display of 6, which
3741+
// results in 1 trillion asset units.
3742+
Amount: 1_000_000_000_000,
3743+
DecimalDisplay: decimalDisplay,
3744+
NewGroupedAsset: true,
3745+
}
3746+
3747+
// Mint an asset on Charlie and sync Dave to Charlie as the universe.
3748+
mintedAssets := itest.MintAssetsConfirmBatch(
3749+
t.t, t.lndHarness.Miner.Client, aliceTap,
3750+
[]*mintrpc.MintAssetRequest{
3751+
{
3752+
Asset: itestAsset,
3753+
},
3754+
},
3755+
)
3756+
usdAsset := mintedAssets[0]
3757+
assetID := usdAsset.AssetGenesis.AssetId
3758+
3759+
// Now that we've minted the asset, we can set the price in the oracle.
3760+
var id asset.ID
3761+
copy(id[:], assetID)
3762+
3763+
// We'll assume a price of $100,000.00 USD for a single BTC. This is just the
3764+
// current subjective price our oracle will use. From this BTC price, we'll
3765+
// scale things up to be in the precision of the asset we minted above.
3766+
btcPrice := rfqmath.NewBigIntFixedPoint(
3767+
100_000_00, 2,
3768+
)
3769+
factor := rfqmath.NewBigInt(
3770+
big.NewInt(int64(math.Pow10(decimalDisplay))),
3771+
)
3772+
btcPrice.Coefficient = btcPrice.Coefficient.Mul(factor)
3773+
oracle.setPrice(id, btcPrice, btcPrice)
3774+
3775+
// Now we'll make a normal invoice for 1 BTC using Alice.
3776+
expirySeconds := 10
3777+
amountSat := 100_000_000
3778+
invoiceResp, err := alice.AddInvoice(ctxb, &lnrpc.Invoice{
3779+
Value: int64(amountSat),
3780+
Memo: "normal invoice",
3781+
Expiry: int64(expirySeconds),
3782+
})
3783+
require.NoError(t.t, err)
3784+
3785+
payReq := invoiceResp.PaymentRequest
3786+
3787+
// Now that we have our payment request, we'll call into the new decode asset
3788+
// pay req call.
3789+
decodeResp, err := aliceTap.DecodeAssetPayReq(ctxb, &tapchannelrpc.AssetPayReq{
3790+
AssetId: assetID,
3791+
PayReqString: payReq,
3792+
})
3793+
require.NoError(t.t, err)
3794+
3795+
// The decimal display information, genesis, and asset group information
3796+
// should all match.
3797+
require.Equal(
3798+
t.t, int64(decimalDisplay), int64(decodeResp.DecimalDisplay.DecimalDisplay),
3799+
)
3800+
require.Equal(t.t, usdAsset.AssetGenesis, decodeResp.GenesisInfo)
3801+
require.Equal(t.t, usdAsset.AssetGroup, decodeResp.AssetGroup)
3802+
3803+
// The 1 BTC invoice should map to 100k asset units, with decimal display 6
3804+
// that's 100 billion asset units.
3805+
const expectedUnits = 100_000_000_000
3806+
require.Equal(t.t, int64(expectedUnits), int64(decodeResp.AssetAmount))
3807+
}

itest/litd_test_list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,8 @@ var allTestCases = []*testCase{
7272
name: "test custom channels forward bandwidth",
7373
test: testCustomChannelsForwardBandwidth,
7474
},
75+
{
76+
name: "test custom channels decode payreq",
77+
test: testCustomChannelsDecodeAssetInvoice,
78+
},
7579
}

0 commit comments

Comments
 (0)