-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
itest: add fee estimation + input types itest
In this commit, we add a new itest that exercises the minter and freighter when using multiple input types like P2WKH and various fee rates.
- Loading branch information
Showing
3 changed files
with
294 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
package itest | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/btcsuite/btcd/btcutil" | ||
"github.com/btcsuite/btcd/chaincfg/chainhash" | ||
"github.com/btcsuite/btcd/txscript" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/lightninglabs/taproot-assets/fn" | ||
"github.com/lightninglabs/taproot-assets/taprpc" | ||
"github.com/lightningnetwork/lnd/lnrpc" | ||
"github.com/lightningnetwork/lnd/lnwallet/chainfee" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
type UTXORequest struct { | ||
Type lnrpc.AddressType | ||
Amount int64 | ||
} | ||
|
||
// SetAnchorUTXOs sets the wallet state for the sending node to a set of UTXOs | ||
// of a specific type and value. | ||
func SetAnchorUTXO(t *harnessTest, reqs []*UTXORequest) { | ||
// Burn funds that were sent to the first node. | ||
minerAddr := t.lndHarness.Miner.NewMinerAddress() | ||
primaryWallet := t.lndHarness.Alice | ||
// Initial state of Alice wallet is 100 UTXOs of 1 BTC each. | ||
|
||
primaryWallet.RPC.SendCoins(&lnrpc.SendCoinsRequest{ | ||
Addr: minerAddr.EncodeAddress(), | ||
SendAll: true, | ||
}) | ||
|
||
t.lndHarness.MineBlocksAndAssertNumTxes(1, 1) | ||
|
||
// Request a specific number of sats. | ||
makeOutputs := func(req *UTXORequest) *wire.TxOut { | ||
aliceAddrResp := primaryWallet.RPC.NewAddress( | ||
&lnrpc.NewAddressRequest{ | ||
Type: req.Type, | ||
}, | ||
) | ||
|
||
addr, err := btcutil.DecodeAddress( | ||
aliceAddrResp.Address, t.lndHarness.Miner.ActiveNet, | ||
) | ||
require.NoError(t.t, err) | ||
|
||
addrScript, err := txscript.PayToAddrScript(addr) | ||
require.NoError(t.t, err) | ||
|
||
return &wire.TxOut{ | ||
PkScript: addrScript, | ||
Value: req.Amount, | ||
} | ||
} | ||
|
||
aliceOutputs := fn.Map(reqs, makeOutputs) | ||
feeRate := btcutil.Amount(2) | ||
|
||
_ = t.lndHarness.Miner.SendOutputsWithoutChange(aliceOutputs, feeRate) | ||
|
||
t.lndHarness.MineBlocksAndAssertNumTxes(6, 1) | ||
t.lndHarness.WaitForBlockchainSync(primaryWallet) | ||
} | ||
|
||
// testFeeEstimation tests that we're able to spend outputs of various script | ||
// types, and that the fee estimator and TX size estimator used during asset | ||
// transfers are accurate. | ||
func testFeeEstimation(t *harnessTest) { | ||
var ( | ||
// Make a ladder of UTXO values so use order is deterministic. | ||
anchorAmounts = []int64{10000, 9990, 9980, 9970} | ||
|
||
// The default feerate in the itests is 12.5 sat/vB, but we | ||
// define it here explicitly to use for assertions. | ||
defaultFeeRate = chainfee.SatPerKWeight(3125) | ||
higherFeeRate = defaultFeeRate * 2 | ||
excessiveFeeRate = defaultFeeRate * 8 | ||
lowFeeRate = chainfee.SatPerKWeight(500) | ||
|
||
// We will mint assets using the larget NP2WKH output, and then | ||
// use all three output types for transfers. | ||
initialUTXOs = []*UTXORequest{ | ||
{ | ||
Type: lnrpc.AddressType_NESTED_PUBKEY_HASH, | ||
Amount: anchorAmounts[0], | ||
}, | ||
{ | ||
Type: lnrpc.AddressType_NESTED_PUBKEY_HASH, | ||
Amount: anchorAmounts[1], | ||
}, | ||
{ | ||
Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH, | ||
Amount: anchorAmounts[2], | ||
}, | ||
{ | ||
Type: lnrpc.AddressType_TAPROOT_PUBKEY, | ||
Amount: anchorAmounts[3], | ||
}, | ||
} | ||
) | ||
|
||
ctxb := context.Background() | ||
ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) | ||
defer cancel() | ||
|
||
// Reset the on-chain funds of the first node. | ||
SetAnchorUTXO(t, initialUTXOs) | ||
|
||
// Mint some assets with a NP2WPKH input, which will give us an anchor | ||
// output to spend for a transfer. | ||
rpcAssets := MintAssetsConfirmBatch( | ||
t.t, t.lndHarness.Miner.Client, t.tapd, simpleAssets, | ||
) | ||
|
||
// Check the final fee rate of the mint TX. | ||
rpcMintOutpoint := rpcAssets[0].ChainAnchor.AnchorOutpoint | ||
mintOutpoint, err := wire.NewOutPointFromString(rpcMintOutpoint) | ||
require.NoError(t.t, err) | ||
|
||
// We check the minting TX with a rounded fee rate as the minter does | ||
// not adjust the fee rate of the TX after it was funded by our backing | ||
// wallet. | ||
AssertFeeRate( | ||
t.t, t.lndHarness.Miner.Client, anchorAmounts[0], | ||
&mintOutpoint.Hash, defaultFeeRate, true, | ||
) | ||
|
||
// Split the normal asset to create a transfer with two anchor outputs. | ||
normalAssetId := rpcAssets[0].AssetGenesis.AssetId | ||
splitAmount := rpcAssets[0].Amount / 2 | ||
addr, err := t.tapd.NewAddr( | ||
ctxt, &taprpc.NewAddrRequest{ | ||
AssetId: normalAssetId, | ||
Amt: splitAmount, | ||
}, | ||
) | ||
require.NoError(t.t, err) | ||
sendResp := sendAssetsToAddr(t, t.tapd, addr) | ||
|
||
transferIdx := 0 | ||
ConfirmAndAssertOutboundTransfer( | ||
t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, normalAssetId, | ||
[]uint64{splitAmount, splitAmount}, transferIdx, transferIdx+1, | ||
) | ||
transferIdx += 1 | ||
AssertNonInteractiveRecvComplete(t.t, t.tapd, transferIdx) | ||
|
||
sendTxid, err := chainhash.NewHash(sendResp.Transfer.AnchorTxHash) | ||
require.NoError(t.t, err) | ||
|
||
sendInputAmt := anchorAmounts[1] + 1000 | ||
AssertFeeRate( | ||
t.t, t.lndHarness.Miner.Client, sendInputAmt, sendTxid, | ||
defaultFeeRate, false, | ||
) | ||
|
||
// Double the fee rate to 25 sat/vB before performing another transfer. | ||
t.lndHarness.SetFeeEstimateWithConf(higherFeeRate, 6) | ||
|
||
secondSplitAmount := splitAmount / 2 | ||
addr2, err := t.tapd.NewAddr( | ||
ctxt, &taprpc.NewAddrRequest{ | ||
AssetId: normalAssetId, | ||
Amt: secondSplitAmount, | ||
}, | ||
) | ||
require.NoError(t.t, err) | ||
sendResp = sendAssetsToAddr(t, t.tapd, addr2) | ||
|
||
ConfirmAndAssertOutboundTransfer( | ||
t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, normalAssetId, | ||
[]uint64{secondSplitAmount, secondSplitAmount}, | ||
transferIdx, transferIdx+1, | ||
) | ||
transferIdx += 1 | ||
AssertNonInteractiveRecvComplete(t.t, t.tapd, transferIdx) | ||
|
||
sendTxid, err = chainhash.NewHash(sendResp.Transfer.AnchorTxHash) | ||
require.NoError(t.t, err) | ||
|
||
sendInputAmt = anchorAmounts[2] + 1000 | ||
AssertFeeRate( | ||
t.t, t.lndHarness.Miner.Client, sendInputAmt, sendTxid, | ||
higherFeeRate, false, | ||
) | ||
|
||
// TODO(jhb): unit test for hitting dust limit? | ||
// If we quadruple the fee rate, the freighter should fail during input | ||
// input selection. | ||
t.lndHarness.SetFeeEstimateWithConf(excessiveFeeRate, 6) | ||
|
||
thirdSplitAmount := splitAmount / 4 | ||
addr3, err := t.tapd.NewAddr( | ||
ctxt, &taprpc.NewAddrRequest{ | ||
AssetId: normalAssetId, | ||
Amt: thirdSplitAmount, | ||
}, | ||
) | ||
require.NoError(t.t, err) | ||
|
||
_, err = t.tapd.SendAsset(ctxt, &taprpc.SendAssetRequest{ | ||
TapAddrs: []string{addr3.Encoded}, | ||
}) | ||
require.ErrorContains(t.t, err, "insufficient funds available") | ||
|
||
// After failure at the high feerate, we should still be able to make a | ||
// transfer at a very low feerate. | ||
t.lndHarness.SetFeeEstimateWithConf(lowFeeRate, 6) | ||
sendResp = sendAssetsToAddr(t, t.tapd, addr3) | ||
|
||
ConfirmAndAssertOutboundTransfer( | ||
t.t, t.lndHarness.Miner.Client, t.tapd, sendResp, normalAssetId, | ||
[]uint64{thirdSplitAmount, thirdSplitAmount}, | ||
transferIdx, transferIdx+1, | ||
) | ||
transferIdx += 1 | ||
AssertNonInteractiveRecvComplete(t.t, t.tapd, transferIdx) | ||
|
||
sendTxid, err = chainhash.NewHash(sendResp.Transfer.AnchorTxHash) | ||
require.NoError(t.t, err) | ||
|
||
sendInputAmt = anchorAmounts[3] + 1000 | ||
AssertFeeRate( | ||
t.t, t.lndHarness.Miner.Client, sendInputAmt, sendTxid, | ||
lowFeeRate, false, | ||
) | ||
|
||
// TODO(jhb): Reset node balance to not crash later itests | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters