Skip to content

Commit

Permalink
Merge pull request #923 from lightninglabs/decode-asset-pay-req-itest
Browse files Browse the repository at this point in the history
tapd: add new CLI command for `decodeassetinvoice` along with corresponding itest for `DecodeAssetPayReq`
  • Loading branch information
guggero authored Dec 19, 2024
2 parents 411f881 + 002479e commit 95faab7
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 1 deletion.
63 changes: 63 additions & 0 deletions cmd/litcli/ln.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ var lnCommands = []cli.Command{
sendPaymentCommand,
payInvoiceCommand,
addInvoiceCommand,
decodeAssetInvoiceCommand,
},
},
}
Expand Down Expand Up @@ -650,3 +651,65 @@ func addInvoice(ctx *cli.Context) error {

return nil
}

var decodeAssetInvoiceCommand = cli.Command{
Name: "decodeassetinvoice",
Category: "Payments",
Usage: "Decodes an LN invoice and displays the invoice's amount in asset " +
"units specified by an asset ID",
Description: `
This command can be used to display the information encoded in an invoice.
Given a chosen asset_id, the invoice's amount expressed in units of the asset
will be displayed.
Other information such as the decimal display of an asset, and the asset
group information (if applicable) are also shown.
`,
ArgsUsage: "--pay_req=X --asset_id=X",
Flags: []cli.Flag{
cli.StringFlag{
Name: "pay_req",
Usage: "a zpay32 encoded payment request to fulfill",
},
assetIDFlag,
},
Action: decodeAssetInvoice,
}

func decodeAssetInvoice(ctx *cli.Context) error {
ctxb := context.Background()

switch {
case !ctx.IsSet("pay_req"):
return fmt.Errorf("pay_req argument missing")
case !ctx.IsSet(assetIDFlag.Name):
return fmt.Errorf("the --asset_id flag must be set")
}

payReq := ctx.String("pay_req")

assetIDStr := ctx.String(assetIDFlag.Name)
assetIDBytes, err := hex.DecodeString(assetIDStr)
if err != nil {
return fmt.Errorf("unable to decode assetID: %v", err)
}

tapdConn, cleanup, err := connectSuperMacClient(ctx)
if err != nil {
return fmt.Errorf("unable to make rpc con: %w", err)
}
defer cleanup()

channelsClient := tchrpc.NewTaprootAssetChannelsClient(tapdConn)
resp, err := channelsClient.DecodeAssetPayReq(ctxb, &tchrpc.AssetPayReq{
AssetId: assetIDBytes,
PayReqString: payReq,
})
if err != nil {
return fmt.Errorf("error adding invoice: %w", err)
}

printRespJSON(resp)

return nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
github.com/lightninglabs/pool v0.6.5-beta.0.20241015105339-044cb451b5df
github.com/lightninglabs/pool/auctioneerrpc v1.1.2
github.com/lightninglabs/pool/poolrpc v1.0.0
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241217185126-a9a2744061f1
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241219112156-ecba8f87eb24
github.com/lightningnetwork/lnd v0.18.4-beta.rc2
github.com/lightningnetwork/lnd/cert v1.2.2
github.com/lightningnetwork/lnd/fn v1.2.3
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,12 @@ github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display h1:w7FM5LH9
github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241217185126-a9a2744061f1 h1:YAiqidrUkbWLtCVMdW/j7WM1gOP7CXHqLNKIEowVAsA=
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241217185126-a9a2744061f1/go.mod h1:rkSWHSkPXX2k+PBOkEE1BA3L3qq5+Yv3m6LGkoH3tQk=
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241218162444-57c2454c050e h1:9YWq4/tGgrhRFKg7eyAtahxTMD6dQrl91xLFmd8KgmA=
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241218162444-57c2454c050e/go.mod h1:rkSWHSkPXX2k+PBOkEE1BA3L3qq5+Yv3m6LGkoH3tQk=
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241218173823-610300af2da1 h1:MaHjknFlcS4cosKsx3LSgPG4FoYQxXU84l5EFQTvP0w=
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241218173823-610300af2da1/go.mod h1:rkSWHSkPXX2k+PBOkEE1BA3L3qq5+Yv3m6LGkoH3tQk=
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241219112156-ecba8f87eb24 h1:dTVZCgUZ3CeQAKHLY+jyvMfhHQtutXT93hoKwgTsv2M=
github.com/lightninglabs/taproot-assets v0.5.0-rc2.0.20241219112156-ecba8f87eb24/go.mod h1:rkSWHSkPXX2k+PBOkEE1BA3L3qq5+Yv3m6LGkoH3tQk=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI=
github.com/lightningnetwork/lnd v0.18.4-beta h1:4pGmIjIMisrs4TMDYp4fk8NeI1YFpcuqwaSiFwLcd1g=
Expand Down
127 changes: 127 additions & 0 deletions itest/litd_custom_channels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
oraclerpc "github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
"github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
"github.com/lightninglabs/taproot-assets/taprpc/universerpc"
"github.com/lightninglabs/taproot-assets/tapscript"
Expand Down Expand Up @@ -3678,3 +3679,129 @@ func testCustomChannelsForwardBandwidth(ctxb context.Context,
universeTap, noOpCoOpCloseBalanceCheck,
)
}

// testCustomChannelsDecodeAssetInvoice tests that we're able to properly
// decode and display asset invoice related information.
//
// TODO(roasbeef): just move to tapd repo due to new version that doesn't req a
// chan?
func testCustomChannelsDecodeAssetInvoice(ctx context.Context,
net *NetworkHarness, t *harnessTest) {

// First, we'll set up some information for our custom oracle that we'll use
// to feed in price information.
oracleAddr := fmt.Sprintf("localhost:%d", port.NextAvailablePort())
oracle := newOracleHarness(oracleAddr)
oracle.start(t.t)
t.t.Cleanup(oracle.stop)

ctxb := context.Background()
lndArgs := slices.Clone(lndArgsTemplate)
litdArgs := slices.Clone(litdArgsTemplateNoOracle)
litdArgs = append(litdArgs, fmt.Sprintf(
"--taproot-assets.experimental.rfq.priceoracleaddress="+
"rfqrpc://%s", oracleAddr,
))

// For this test, Zane will be our dedicated Universe server for all parties.
zane, err := net.NewNode(
t.t, "Zane", lndArgs, false, true, litdArgs...,
)
require.NoError(t.t, err)

litdArgs = append(litdArgs, fmt.Sprintf(
"--taproot-assets.proofcourieraddr=%s://%s",
proof.UniverseRpcCourierType, zane.Cfg.LitAddr(),
))

// We'll just make a single node here, as this doesn't actually rely on a set
// of active channels.
alice, err := net.NewNode(t.t, "Alice", lndArgs, false, true, litdArgs...)
require.NoError(t.t, err)
aliceTap := newTapClient(t.t, alice)

// Fund Alice so she'll have enough funds to mint the asset.
fundAllNodes(t.t, net, []*HarnessNode{alice})

// Next, we'll make a new asset with a specified decimal display. We'll also
// make grouped asset as well.
usdMetaData := &taprpc.AssetMeta{
Data: []byte(`{
"description":"this is a USD stablecoin with decimal display of 6"
}`),
Type: taprpc.AssetMetaType_META_TYPE_JSON,
}

const decimalDisplay = 6
itestAsset = &mintrpc.MintAsset{
AssetType: taprpc.AssetType_NORMAL,
Name: "USD",
AssetMeta: usdMetaData,
// We mint 1 million USD with a decimal display of 6, which
// results in 1 trillion asset units.
Amount: 1_000_000_000_000,
DecimalDisplay: decimalDisplay,
NewGroupedAsset: true,
}

// Mint an asset on Charlie and sync Dave to Charlie as the universe.
mintedAssets := itest.MintAssetsConfirmBatch(
t.t, t.lndHarness.Miner.Client, aliceTap,
[]*mintrpc.MintAssetRequest{
{
Asset: itestAsset,
},
},
)
usdAsset := mintedAssets[0]
assetID := usdAsset.AssetGenesis.AssetId

// Now that we've minted the asset, we can set the price in the oracle.
var id asset.ID
copy(id[:], assetID)

// We'll assume a price of $100,000.00 USD for a single BTC. This is just the
// current subjective price our oracle will use. From this BTC price, we'll
// scale things up to be in the precision of the asset we minted above.
btcPrice := rfqmath.NewBigIntFixedPoint(
100_000_00, 2,
)
factor := rfqmath.NewBigInt(
big.NewInt(int64(math.Pow10(decimalDisplay))),
)
btcPrice.Coefficient = btcPrice.Coefficient.Mul(factor)
oracle.setPrice(id, btcPrice, btcPrice)

// Now we'll make a normal invoice for 1 BTC using Alice.
expirySeconds := 10
amountSat := 100_000_000
invoiceResp, err := alice.AddInvoice(ctxb, &lnrpc.Invoice{
Value: int64(amountSat),
Memo: "normal invoice",
Expiry: int64(expirySeconds),
})
require.NoError(t.t, err)

payReq := invoiceResp.PaymentRequest

// Now that we have our payment request, we'll call into the new decode asset
// pay req call.
decodeResp, err := aliceTap.DecodeAssetPayReq(ctxb, &tapchannelrpc.AssetPayReq{
AssetId: assetID,
PayReqString: payReq,
})
require.NoError(t.t, err)

// The decimal display information, genesis, and asset group information
// should all match.
require.Equal(
t.t, int64(decimalDisplay), int64(decodeResp.DecimalDisplay.DecimalDisplay),
)
require.Equal(t.t, usdAsset.AssetGenesis, decodeResp.GenesisInfo)
require.Equal(t.t, usdAsset.AssetGroup, decodeResp.AssetGroup)

// The 1 BTC invoice should map to 100k asset units, with decimal display 6
// that's 100 billion asset units.
const expectedUnits = 100_000_000_000
require.Equal(t.t, int64(expectedUnits), int64(decodeResp.AssetAmount))
}
4 changes: 4 additions & 0 deletions itest/litd_test_list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,8 @@ var allTestCases = []*testCase{
name: "test custom channels forward bandwidth",
test: testCustomChannelsForwardBandwidth,
},
{
name: "test custom channels decode payreq",
test: testCustomChannelsDecodeAssetInvoice,
},
}

0 comments on commit 95faab7

Please sign in to comment.