@@ -3,14 +3,18 @@ package itest
33import  (
44	"context" 
55	"fmt" 
6+ 	"math" 
7+ 	"math/big" 
68	"slices" 
79	"time" 
810
911	"github.com/btcsuite/btcd/btcec/v2/schnorr" 
1012	"github.com/btcsuite/btcd/btcutil" 
1113	"github.com/btcsuite/btcd/chaincfg/chainhash" 
14+ 	"github.com/lightninglabs/taproot-assets/asset" 
1215	"github.com/lightninglabs/taproot-assets/itest" 
1316	"github.com/lightninglabs/taproot-assets/proof" 
17+ 	"github.com/lightninglabs/taproot-assets/rfqmath" 
1418	"github.com/lightninglabs/taproot-assets/taprpc" 
1519	"github.com/lightninglabs/taproot-assets/taprpc/mintrpc" 
1620	tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc" 
@@ -19,6 +23,7 @@ import (
1923	"github.com/lightningnetwork/lnd/fn" 
2024	"github.com/lightningnetwork/lnd/lnrpc" 
2125	"github.com/lightningnetwork/lnd/lntest" 
26+ 	"github.com/lightningnetwork/lnd/lntest/port" 
2227	"github.com/lightningnetwork/lnd/lntest/wait" 
2328	"github.com/stretchr/testify/require" 
2429)
5560		"--accept-keysend" ,
5661		"--debuglevel=trace,GRPC=error,BTCN=info" ,
5762	}
58- 	litdArgsTemplate  =  []string {
63+ 	litdArgsTemplateNoOracle  =  []string {
5964		"--taproot-assets.allow-public-uni-proof-courier" ,
6065		"--taproot-assets.universe.public-access=rw" ,
6166		"--taproot-assets.universe.sync-all-assets" ,
@@ -64,17 +69,19 @@ var (
6469		"--taproot-assets.universerpccourier.numtries=5" ,
6570		"--taproot-assets.universerpccourier.initialbackoff=300ms" ,
6671		"--taproot-assets.universerpccourier.maxbackoff=600ms" ,
67- 		"--taproot-assets.experimental.rfq.priceoracleaddress="  + 
68- 			"use_mock_price_oracle_service_promise_to_"  + 
69- 			"not_use_on_mainnet" ,
70- 		"--taproot-assets.experimental.rfq.mockoracleassetsperbtc="  + 
71- 			"5820600" ,
7272		"--taproot-assets.universerpccourier.skipinitdelay" ,
7373		"--taproot-assets.universerpccourier.backoffresetwait=100ms" ,
7474		"--taproot-assets.universerpccourier.initialbackoff=300ms" ,
7575		"--taproot-assets.universerpccourier.maxbackoff=600ms" ,
7676		"--taproot-assets.custodianproofretrievaldelay=500ms" ,
7777	}
78+ 	litdArgsTemplate  =  append (litdArgsTemplateNoOracle , []string {
79+ 		"--taproot-assets.experimental.rfq.priceoracleaddress="  + 
80+ 			"use_mock_price_oracle_service_promise_to_"  + 
81+ 			"not_use_on_mainnet" ,
82+ 		"--taproot-assets.experimental.rfq.mockoracleassetsperbtc="  + 
83+ 			"5820600" ,
84+ 	}... )
7885)
7986
8087const  (
@@ -90,7 +97,7 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness,
9097	lndArgs  :=  slices .Clone (lndArgsTemplate )
9198	litdArgs  :=  slices .Clone (litdArgsTemplate )
9299
93- 	// Explicitly set the proof courier as Alice (how  has no other role 
100+ 	// Explicitly set the proof courier as Zane (now  has no other role 
94101	// other than proof shuffling), otherwise a hashmail courier will be 
95102	// used. For the funding transaction, we're just posting it and don't 
96103	// expect a true receiver. 
@@ -269,7 +276,7 @@ func testCustomChannels(_ context.Context, net *NetworkHarness,
269276	lndArgs  :=  slices .Clone (lndArgsTemplate )
270277	litdArgs  :=  slices .Clone (litdArgsTemplate )
271278
272- 	// Explicitly set the proof courier as Alice (how  has no other role 
279+ 	// Explicitly set the proof courier as Zane (now  has no other role 
273280	// other than proof shuffling), otherwise a hashmail courier will be 
274281	// used. For the funding transaction, we're just posting it and don't 
275282	// expect a true receiver. 
@@ -726,10 +733,10 @@ func testCustomChannelsGroupedAsset(_ context.Context, net *NetworkHarness,
726733	lndArgs  :=  slices .Clone (lndArgsTemplate )
727734	litdArgs  :=  slices .Clone (litdArgsTemplate )
728735
729- 	// Explicitly set the proof courier as Alice ( has no other role other  
730- 	// than proof shuffling), otherwise a hashmail courier will be used.  
731- 	// For the funding transaction, we're just posting it and don't expect a  
732- 	// true receiver. 
736+ 	// Explicitly set the proof courier as Zane (now  has no other role 
737+ 	// other  than proof shuffling), otherwise a hashmail courier will be 
738+ 	// used.  For the funding transaction, we're just posting it and don't 
739+ 	// expect a  true receiver. 
733740	zane , err  :=  net .NewNode (
734741		t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
735742	)
@@ -1118,7 +1125,10 @@ func testCustomChannelsForceClose(_ context.Context, net *NetworkHarness,
11181125	lndArgs  :=  slices .Clone (lndArgsTemplate )
11191126	litdArgs  :=  slices .Clone (litdArgsTemplate )
11201127
1121- 	// Zane will act as our Universe server for the duration of the test. 
1128+ 	// Explicitly set the proof courier as Zane (now has no other role 
1129+ 	// other than proof shuffling), otherwise a hashmail courier will be 
1130+ 	// used. For the funding transaction, we're just posting it and don't 
1131+ 	// expect a true receiver. 
11221132	zane , err  :=  net .NewNode (
11231133		t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
11241134	)
@@ -1479,7 +1489,10 @@ func testCustomChannelsBreach(_ context.Context, net *NetworkHarness,
14791489	lndArgs  :=  slices .Clone (lndArgsTemplate )
14801490	litdArgs  :=  slices .Clone (litdArgsTemplate )
14811491
1482- 	// Zane will act as our Universe server for the duration of the test. 
1492+ 	// Explicitly set the proof courier as Zane (now has no other role 
1493+ 	// other than proof shuffling), otherwise a hashmail courier will be 
1494+ 	// used. For the funding transaction, we're just posting it and don't 
1495+ 	// expect a true receiver. 
14831496	zane , err  :=  net .NewNode (
14841497		t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
14851498	)
@@ -1700,7 +1713,7 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
17001713	lndArgs  :=  slices .Clone (lndArgsTemplate )
17011714	litdArgs  :=  slices .Clone (litdArgsTemplate )
17021715
1703- 	// Explicitly set the proof courier as Alice (how  has no other role 
1716+ 	// Explicitly set the proof courier as Zane (now  has no other role 
17041717	// other than proof shuffling), otherwise a hashmail courier will be 
17051718	// used. For the funding transaction, we're just posting it and don't 
17061719	// expect a true receiver. 
@@ -1957,6 +1970,10 @@ func testCustomChannelsBalanceConsistency(_ context.Context,
19571970	lndArgs  :=  slices .Clone (lndArgsTemplate )
19581971	litdArgs  :=  slices .Clone (litdArgsTemplate )
19591972
1973+ 	// Explicitly set the proof courier as Zane (now has no other role 
1974+ 	// other than proof shuffling), otherwise a hashmail courier will be 
1975+ 	// used. For the funding transaction, we're just posting it and don't 
1976+ 	// expect a true receiver. 
19601977	zane , err  :=  net .NewNode (
19611978		t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
19621979	)
@@ -2147,3 +2164,201 @@ func testCustomChannelsBalanceConsistency(_ context.Context,
21472164	assertNumAssetOutputs (t .t , charlieTap , assetID , 1 )
21482165	assertNumAssetOutputs (t .t , daveTap , assetID , 1 )
21492166}
2167+ 
2168+ // testCustomChannelsOraclePricing tests that all asset transfers are correctly 
2169+ // priced when using an oracle that isn't tapd's mock oracle. 
2170+ func  testCustomChannelsOraclePricing (_  context.Context ,
2171+ 	net  * NetworkHarness , t  * harnessTest ) {
2172+ 
2173+ 	usdMetaData  :=  & taprpc.AssetMeta {
2174+ 		Data : []byte (`{ 
2175+ "description":"this is a USD stablecoin with decimal display of 6" 
2176+ }` ),
2177+ 		Type : taprpc .AssetMetaType_META_TYPE_JSON ,
2178+ 	}
2179+ 
2180+ 	const  decimalDisplay  =  6 
2181+ 	itestAsset  =  & mintrpc.MintAsset {
2182+ 		AssetType : taprpc .AssetType_NORMAL ,
2183+ 		Name :      "USD" ,
2184+ 		AssetMeta : usdMetaData ,
2185+ 		// We mint 1 million USD with a decimal display of 6, which 
2186+ 		// results in 1 trillion asset units. 
2187+ 		Amount :         1_000_000_000_000 ,
2188+ 		DecimalDisplay : decimalDisplay ,
2189+ 	}
2190+ 
2191+ 	oracleAddr  :=  fmt .Sprintf ("localhost:%d" , port .NextAvailablePort ())
2192+ 	oracle  :=  newOracleHarness (oracleAddr )
2193+ 	oracle .start (t .t )
2194+ 	t .t .Cleanup (oracle .stop )
2195+ 
2196+ 	ctxb  :=  context .Background ()
2197+ 	lndArgs  :=  slices .Clone (lndArgsTemplate )
2198+ 	litdArgs  :=  slices .Clone (litdArgsTemplateNoOracle )
2199+ 	litdArgs  =  append (litdArgs , fmt .Sprintf (
2200+ 		"--taproot-assets.experimental.rfq.priceoracleaddress=" + 
2201+ 			"rfqrpc://%s" , oracleAddr ,
2202+ 	))
2203+ 
2204+ 	// Explicitly set the proof courier as Zane (now has no other role 
2205+ 	// other than proof shuffling), otherwise a hashmail courier will be 
2206+ 	// used. For the funding transaction, we're just posting it and don't 
2207+ 	// expect a true receiver. 
2208+ 	zane , err  :=  net .NewNode (
2209+ 		t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
2210+ 	)
2211+ 	require .NoError (t .t , err )
2212+ 
2213+ 	litdArgs  =  append (litdArgs , fmt .Sprintf (
2214+ 		"--taproot-assets.proofcourieraddr=%s://%s" ,
2215+ 		proof .UniverseRpcCourierType , zane .Cfg .LitAddr (),
2216+ 	))
2217+ 
2218+ 	// The topology we are going for looks like the following: 
2219+ 	// 
2220+ 	// Charlie  --[assets]-->  Dave  --[sats]-->  Erin  --[assets]-->  Fabia 
2221+ 	//                          | 
2222+ 	//                          | 
2223+ 	//                       [assets] 
2224+ 	//                          | 
2225+ 	//                          v 
2226+ 	//                        Yara 
2227+ 	// 
2228+ 	// With [assets] being a custom channel and [sats] being a normal, BTC 
2229+ 	// only channel. 
2230+ 	// All 5 nodes need to be full litd nodes running in integrated mode 
2231+ 	// with tapd included. We also need specific flags to be enabled, so we 
2232+ 	// create 5 completely new nodes, ignoring the two default nodes that 
2233+ 	// are created by the harness. 
2234+ 	charlie , err  :=  net .NewNode (
2235+ 		t .t , "Charlie" , lndArgs , false , true , litdArgs ... ,
2236+ 	)
2237+ 	require .NoError (t .t , err )
2238+ 
2239+ 	dave , err  :=  net .NewNode (t .t , "Dave" , lndArgs , false , true , litdArgs ... )
2240+ 	require .NoError (t .t , err )
2241+ 	erin , err  :=  net .NewNode (t .t , "Erin" , lndArgs , false , true , litdArgs ... )
2242+ 	require .NoError (t .t , err )
2243+ 	fabia , err  :=  net .NewNode (
2244+ 		t .t , "Fabia" , lndArgs , false , true , litdArgs ... ,
2245+ 	)
2246+ 	require .NoError (t .t , err )
2247+ 	yara , err  :=  net .NewNode (
2248+ 		t .t , "Yara" , lndArgs , false , true , litdArgs ... ,
2249+ 	)
2250+ 	require .NoError (t .t , err )
2251+ 
2252+ 	nodes  :=  []* HarnessNode {charlie , dave , erin , fabia , yara }
2253+ 	connectAllNodes (t .t , net , nodes )
2254+ 	fundAllNodes (t .t , net , nodes )
2255+ 
2256+ 	// Create the normal channel between Dave and Erin. 
2257+ 	t .Logf ("Opening normal channel between Dave and Erin..." )
2258+ 	channelOp  :=  openChannelAndAssert (
2259+ 		t , net , dave , erin , lntest.OpenChannelParams {
2260+ 			Amt :         10_000_000 ,
2261+ 			SatPerVByte : 5 ,
2262+ 		},
2263+ 	)
2264+ 	defer  closeChannelAndAssert (t , net , dave , channelOp , false )
2265+ 
2266+ 	// This is the only public channel, we need everyone to be aware of it. 
2267+ 	assertChannelKnown (t .t , charlie , channelOp )
2268+ 	assertChannelKnown (t .t , fabia , channelOp )
2269+ 
2270+ 	universeTap  :=  newTapClient (t .t , zane )
2271+ 	charlieTap  :=  newTapClient (t .t , charlie )
2272+ 	daveTap  :=  newTapClient (t .t , dave )
2273+ 	erinTap  :=  newTapClient (t .t , erin )
2274+ 	fabiaTap  :=  newTapClient (t .t , fabia )
2275+ 	yaraTap  :=  newTapClient (t .t , yara )
2276+ 
2277+ 	// Mint an asset on Charlie and sync Dave to Charlie as the universe. 
2278+ 	mintedAssets  :=  itest .MintAssetsConfirmBatch (
2279+ 		t .t , t .lndHarness .Miner .Client , charlieTap ,
2280+ 		[]* mintrpc.MintAssetRequest {
2281+ 			{
2282+ 				Asset : itestAsset ,
2283+ 			},
2284+ 		},
2285+ 	)
2286+ 	usdAsset  :=  mintedAssets [0 ]
2287+ 	assetID  :=  usdAsset .AssetGenesis .AssetId 
2288+ 
2289+ 	// Now that we've minted the asset, we can set the price in the oracle. 
2290+ 	var  id  asset.ID 
2291+ 	copy (id [:], assetID )
2292+ 
2293+ 	// Let's assume the current USD price for 1 BTC is 66,548.40. We'll take 
2294+ 	// that price and add a 4% spread, 2% on each side (buy/sell) to earn 
2295+ 	// money as the oracle. 2% is 1,330.97, so we'll set the sell price to 
2296+ 	// 65,217.43 and the purchase price to 67,879.37. 
2297+ 	// The following numbers are to help understand the magic numbers below. 
2298+ 	// They're the price in USD/BTC, the price of 1 USD in sats and the 
2299+ 	// expected price in asset units per BTC. 
2300+ 	// 65,217.43 => 1533.332 => 65_217_430_000 
2301+ 	// 66,548.40 => 1502.666 => 66_548_400_000 
2302+ 	// 67,879.37 => 1473.202 => 67_879_370_000 
2303+ 	salePrice  :=  rfqmath .NewBigIntFixedPoint (65_217_43 , 2 )
2304+ 	purchasePrice  :=  rfqmath .NewBigIntFixedPoint (67_879_37 , 2 )
2305+ 
2306+ 	// We now have the prices defined in USD. But the asset has a decimal 
2307+ 	// display of 6, so we need to multiply them by 10^6. 
2308+ 	factor  :=  rfqmath .NewBigInt (
2309+ 		big .NewInt (int64 (math .Pow10 (decimalDisplay ))),
2310+ 	)
2311+ 	salePrice .Coefficient  =  salePrice .Coefficient .Mul (factor )
2312+ 	purchasePrice .Coefficient  =  purchasePrice .Coefficient .Mul (factor )
2313+ 	oracle .setPrice (id , purchasePrice , salePrice )
2314+ 
2315+ 	t .Logf ("Minted %d USD assets, syncing universes..." , usdAsset .Amount )
2316+ 	syncUniverses (t .t , charlieTap , dave , erin , fabia , yara )
2317+ 	t .Logf ("Universes synced between all nodes, distributing assets..." )
2318+ 
2319+ 	const  (
2320+ 		daveFundingAmount  =  uint64 (400_000_000 )
2321+ 		erinFundingAmount  =  uint64 (200_000_000 )
2322+ 	)
2323+ 	charlieFundingAmount  :=  usdAsset .Amount  -  uint64 (2 * 400_000_000 )
2324+ 
2325+ 	_ , _ , _  =  createTestAssetNetwork (
2326+ 		t , net , charlieTap , daveTap , erinTap , fabiaTap , yaraTap ,
2327+ 		universeTap , usdAsset , daveFundingAmount , charlieFundingAmount ,
2328+ 		daveFundingAmount , erinFundingAmount , 0 ,
2329+ 	)
2330+ 
2331+ 	// Before we start sending out payments, let's make sure each node can 
2332+ 	// see the other one in the graph and has all required features. 
2333+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (charlie , dave ))
2334+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (dave , charlie ))
2335+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (dave , yara ))
2336+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (yara , dave ))
2337+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (erin , fabia ))
2338+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (fabia , erin ))
2339+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (charlie , erin ))
2340+ 
2341+ 	// We now create an invoice at Fabia for 100 USD, which is 100_000_000 
2342+ 	// asset units with decimal display of 6. 
2343+ 	const  fabiaInvoiceAssetAmount  =  100_000_000 
2344+ 	invoiceResp  :=  createAssetInvoice (
2345+ 		t .t , erin , fabia , fabiaInvoiceAssetAmount , assetID ,
2346+ 	)
2347+ 	decodedInvoice , err  :=  fabia .DecodePayReq (ctxb , & lnrpc.PayReqString {
2348+ 		PayReq : invoiceResp .PaymentRequest ,
2349+ 	})
2350+ 	require .NoError (t .t , err )
2351+ 
2352+ 	// The invoice amount should come out as 100 * 1533.332. 
2353+ 	require .EqualValues (t .t , 153_333_242 , decodedInvoice .NumMsat )
2354+ 
2355+ 	numUnits  :=  payInvoiceWithAssets (
2356+ 		t .t , charlie , dave , invoiceResp , assetID , false ,
2357+ 	)
2358+ 	logBalance (t .t , nodes , assetID , "after invoice" )
2359+ 
2360+ 	// The paid amount should come out as 153_333_242 / 1473.202, which is 
2361+ 	// quite exactly 4% more than will arrive at the destination (which is 
2362+ 	// the oracle's configured spread). 
2363+ 	require .EqualValues (t .t , 104_081_638 , numUnits )
2364+ }
0 commit comments