-
Notifications
You must be signed in to change notification settings - Fork 103
custom channels: add more itest coverage, fix pending channel bug #797
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ import ( | |
"github.com/btcsuite/btcd/btcec/v2/schnorr" | ||
"github.com/btcsuite/btcd/btcutil" | ||
"github.com/btcsuite/btcd/chaincfg/chainhash" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/davecgh/go-spew/spew" | ||
"github.com/lightninglabs/taproot-assets/itest" | ||
"github.com/lightninglabs/taproot-assets/proof" | ||
|
@@ -916,10 +917,19 @@ func waitForSendEvent(t *testing.T, | |
} | ||
} | ||
|
||
// coOpCloseBalanceCheck is a function type that can be passed into | ||
// closeAssetChannelAndAsset to asset the final balance of the closing | ||
// transaction. | ||
type coOpCloseBalanceCheck func(t *testing.T, local, remote *HarnessNode, | ||
closeTx *wire.MsgTx, closeUpdate *lnrpc.ChannelCloseUpdate, | ||
assetID, groupKey []byte, universeTap *tapClient) | ||
Comment on lines
+923
to
+925
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be a good idea to put all of these arguments in a single struct. Or the option pattern. There are already many arguments so a struct could help organise them. And it would minimise the diff if adding another argument in the future. |
||
|
||
// closeAssetChannelAndAssert closes the channel between the local and remote | ||
// node and asserts the final balances of the closing transaction. | ||
func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness, | ||
local, remote *HarnessNode, chanPoint *lnrpc.ChannelPoint, | ||
assetID, groupKey []byte, universeTap *tapClient, remoteBtcBalance, | ||
remoteAssetBalance bool) { | ||
assetID, groupKey []byte, universeTap *tapClient, | ||
balanceCheck coOpCloseBalanceCheck) { | ||
|
||
t.t.Helper() | ||
|
||
|
@@ -953,6 +963,41 @@ func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness, | |
|
||
waitForSendEvent(t.t, sendEvents, tapfreighter.SendStateComplete) | ||
|
||
// Check the final balance of the closing transaction. | ||
balanceCheck( | ||
t.t, local, remote, closeTx, closeUpdate, assetID, groupKey, | ||
universeTap, | ||
) | ||
} | ||
|
||
// assertDefaultCoOpCloseBalance returns a default implementation of the co-op | ||
// close balance check that can be used in tests. It assumes the initiator has | ||
// both an asset and BTC balance left, while the responder's balance can be | ||
// specified with the boolean variables. | ||
func assertDefaultCoOpCloseBalance(remoteBtcBalance, | ||
remoteAssetBalance bool) coOpCloseBalanceCheck { | ||
|
||
return func(t *testing.T, local, remote *HarnessNode, | ||
closeTx *wire.MsgTx, closeUpdate *lnrpc.ChannelCloseUpdate, | ||
assetID, groupKey []byte, universeTap *tapClient) { | ||
|
||
defaultCoOpCloseBalanceCheck( | ||
t, local, remote, closeTx, closeUpdate, assetID, | ||
groupKey, universeTap, remoteBtcBalance, | ||
remoteAssetBalance, | ||
) | ||
} | ||
} | ||
|
||
// defaultCoOpCloseBalanceCheck is a default implementation of the co-op close | ||
// balance check that can be used in tests. It assumes the initiator has both | ||
// an asset and BTC balance left, while the responder's balance can be specified | ||
// with the boolean variables. | ||
func defaultCoOpCloseBalanceCheck(t *testing.T, local, remote *HarnessNode, | ||
closeTx *wire.MsgTx, closeUpdate *lnrpc.ChannelCloseUpdate, | ||
assetID, groupKey []byte, universeTap *tapClient, remoteBtcBalance, | ||
remoteAssetBalance bool) { | ||
|
||
// With the channel closed, we'll now assert that the co-op close | ||
// transaction was inserted into the local universe. | ||
// | ||
|
@@ -972,49 +1017,50 @@ func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness, | |
additionalOutputs++ | ||
} | ||
|
||
require.Len(t.t, closeTx.TxOut, numOutputs) | ||
closeTxid := closeTx.TxHash() | ||
require.Len(t, closeTx.TxOut, numOutputs) | ||
|
||
outIdx := 0 | ||
dummyAmt := int64(1000) | ||
require.LessOrEqual(t.t, closeTx.TxOut[outIdx].Value, dummyAmt) | ||
require.LessOrEqual(t, closeTx.TxOut[outIdx].Value, dummyAmt) | ||
|
||
if remoteAssetBalance { | ||
outIdx++ | ||
require.LessOrEqual(t.t, closeTx.TxOut[outIdx].Value, dummyAmt) | ||
require.LessOrEqual(t, closeTx.TxOut[outIdx].Value, dummyAmt) | ||
} | ||
|
||
// We also require there to be at most two additional outputs, one for | ||
// each of the asset outputs with balance. | ||
require.Len(t.t, closeUpdate.AdditionalOutputs, additionalOutputs) | ||
require.Len(t, closeUpdate.AdditionalOutputs, additionalOutputs) | ||
|
||
var remoteCloseOut *lnrpc.CloseOutput | ||
if remoteBtcBalance { | ||
// The remote node has received a couple of HTLCs with an above | ||
// dust value, so it should also have accumulated a non-dust | ||
// balance, even after subtracting 1k sats for the asset output. | ||
remoteCloseOut = closeUpdate.RemoteCloseOutput | ||
require.NotNil(t.t, remoteCloseOut) | ||
require.NotNil(t, remoteCloseOut) | ||
|
||
outIdx++ | ||
require.EqualValues( | ||
t.t, remoteCloseOut.AmountSat-dummyAmt, | ||
t, remoteCloseOut.AmountSat-dummyAmt, | ||
closeTx.TxOut[outIdx].Value, | ||
) | ||
} else if remoteAssetBalance { | ||
// The remote node has received a couple of HTLCs but not enough | ||
// to go above dust. So it should still have an asset balance | ||
// that we can verify. | ||
remoteCloseOut = closeUpdate.RemoteCloseOutput | ||
require.NotNil(t.t, remoteCloseOut) | ||
require.NotNil(t, remoteCloseOut) | ||
} | ||
|
||
// The local node should have received the local BTC balance minus the | ||
// TX fees and 1k sats for the asset output. | ||
localCloseOut := closeUpdate.LocalCloseOutput | ||
require.NotNil(t.t, localCloseOut) | ||
require.NotNil(t, localCloseOut) | ||
outIdx++ | ||
require.Greater( | ||
t.t, closeTx.TxOut[outIdx].Value, | ||
t, closeTx.TxOut[outIdx].Value, | ||
localCloseOut.AmountSat-dummyAmt, | ||
) | ||
|
||
|
@@ -1039,39 +1085,41 @@ func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness, | |
|
||
if remoteAuxOut != nil { | ||
require.Equal( | ||
t.t, remoteAuxOut.PkScript, | ||
t, remoteAuxOut.PkScript, | ||
closeTx.TxOut[remoteAssetIndex].PkScript, | ||
) | ||
} | ||
|
||
require.Equal( | ||
t.t, localAuxOut.PkScript, | ||
t, localAuxOut.PkScript, | ||
closeTx.TxOut[localAssetIndex].PkScript, | ||
) | ||
|
||
// We now verify the arrival of the local balance asset proof at the | ||
// universe server. | ||
var localAssetCloseOut rfqmsg.JsonCloseOutput | ||
err = json.Unmarshal( | ||
err := json.Unmarshal( | ||
localCloseOut.CustomChannelData, &localAssetCloseOut, | ||
) | ||
require.NoError(t.t, err) | ||
require.NoError(t, err) | ||
|
||
for assetIDStr, scriptKeyStr := range localAssetCloseOut.ScriptKeys { | ||
scriptKeyBytes, err := hex.DecodeString(scriptKeyStr) | ||
require.NoError(t.t, err) | ||
require.NoError(t, err) | ||
|
||
require.Equal(t.t, hex.EncodeToString(assetID), assetIDStr) | ||
require.Equal(t, hex.EncodeToString(assetID), assetIDStr) | ||
|
||
a := assertUniverseProofExists( | ||
t.t, universeTap, assetID, groupKey, scriptKeyBytes, | ||
t, universeTap, assetID, groupKey, scriptKeyBytes, | ||
fmt.Sprintf("%v:%v", closeTxid, localAssetIndex), | ||
) | ||
|
||
localTapd := newTapClient(t, local) | ||
|
||
scriptKey, err := btcec.ParsePubKey(scriptKeyBytes) | ||
require.NoError(t.t, err) | ||
require.NoError(t, err) | ||
assertAssetExists( | ||
t.t, localTapd, assetID, a.Amount, scriptKey, true, | ||
t, localTapd, assetID, a.Amount, scriptKey, true, | ||
true, false, | ||
) | ||
} | ||
|
@@ -1083,38 +1131,145 @@ func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness, | |
|
||
// At this point the remote close output should be defined, otherwise | ||
// something went wrong. | ||
require.NotNil(t.t, remoteCloseOut) | ||
require.NotNil(t, remoteCloseOut) | ||
|
||
// And then we verify the arrival of the remote balance asset proof at | ||
// the universe server as well. | ||
var remoteAssetCloseOut rfqmsg.JsonCloseOutput | ||
err = json.Unmarshal( | ||
remoteCloseOut.CustomChannelData, &remoteAssetCloseOut, | ||
) | ||
require.NoError(t.t, err) | ||
require.NoError(t, err) | ||
|
||
for assetIDStr, scriptKeyStr := range remoteAssetCloseOut.ScriptKeys { | ||
scriptKeyBytes, err := hex.DecodeString(scriptKeyStr) | ||
require.NoError(t.t, err) | ||
require.NoError(t, err) | ||
|
||
require.Equal(t.t, hex.EncodeToString(assetID), assetIDStr) | ||
require.Equal(t, hex.EncodeToString(assetID), assetIDStr) | ||
|
||
a := assertUniverseProofExists( | ||
t.t, universeTap, assetID, groupKey, scriptKeyBytes, | ||
t, universeTap, assetID, groupKey, scriptKeyBytes, | ||
fmt.Sprintf("%v:%v", closeTxid, remoteAssetIndex), | ||
) | ||
|
||
remoteTapd := newTapClient(t.t, remote) | ||
remoteTapd := newTapClient(t, remote) | ||
|
||
scriptKey, err := btcec.ParsePubKey(scriptKeyBytes) | ||
require.NoError(t, err) | ||
assertAssetExists( | ||
t, remoteTapd, assetID, a.Amount, scriptKey, true, | ||
true, false, | ||
) | ||
} | ||
} | ||
|
||
// initiatorZeroAssetBalanceCoOpBalanceCheck is a co-op close balance check | ||
// function that can be used when the initiator has a zero asset balance. | ||
func initiatorZeroAssetBalanceCoOpBalanceCheck(t *testing.T, _, | ||
remote *HarnessNode, closeTx *wire.MsgTx, | ||
closeUpdate *lnrpc.ChannelCloseUpdate, assetID, groupKey []byte, | ||
universeTap *tapClient) { | ||
|
||
// With the channel closed, we'll now assert that the co-op close | ||
// transaction was inserted into the local universe. | ||
// | ||
// Since the initiator has a zero asset balance, we expect that at most | ||
// three outputs exist: one for the remote asset output, one for the | ||
// remote BTC channel balance and one for the initiator's BTC channel | ||
// balance (which cannot be zero or below dust due to the mandatory | ||
// channel reserve). | ||
numOutputs := 3 | ||
|
||
closeTxid := closeTx.TxHash() | ||
require.Len(t, closeTx.TxOut, numOutputs) | ||
|
||
// We assume that the local node has a non-zero BTC balance left. | ||
localOut, _ := closeTxOut(t, closeTx, closeUpdate, true) | ||
require.Greater(t, localOut.Value, int64(1000)) | ||
|
||
// We also require there to be exactly one additional output, which is | ||
// the remote asset output. | ||
require.Len(t, closeUpdate.AdditionalOutputs, 1) | ||
assetTxOut, assetOutputIndex := findTxOut( | ||
t, closeTx, closeUpdate.AdditionalOutputs[0].PkScript, | ||
) | ||
require.LessOrEqual(t, assetTxOut.Value, int64(1000)) | ||
|
||
// The remote node has received a couple of HTLCs with an above | ||
// dust value, so it should also have accumulated a non-dust | ||
// balance, even after subtracting 1k sats for the asset output. | ||
remoteCloseOut := closeUpdate.RemoteCloseOutput | ||
require.NotNil(t, remoteCloseOut) | ||
|
||
// Find out which of the additional outputs is the local one and which | ||
// is the remote. | ||
remoteAuxOut := closeUpdate.AdditionalOutputs[0] | ||
require.False(t, remoteAuxOut.IsLocal) | ||
|
||
// And then we verify the arrival of the remote balance asset proof at | ||
// the universe server as well. | ||
var remoteAssetCloseOut rfqmsg.JsonCloseOutput | ||
err := json.Unmarshal( | ||
remoteCloseOut.CustomChannelData, &remoteAssetCloseOut, | ||
) | ||
require.NoError(t, err) | ||
|
||
for assetIDStr, scriptKeyStr := range remoteAssetCloseOut.ScriptKeys { | ||
scriptKeyBytes, err := hex.DecodeString(scriptKeyStr) | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, hex.EncodeToString(assetID), assetIDStr) | ||
|
||
a := assertUniverseProofExists( | ||
t, universeTap, assetID, groupKey, scriptKeyBytes, | ||
fmt.Sprintf("%v:%v", closeTxid, assetOutputIndex), | ||
) | ||
|
||
remoteTapd := newTapClient(t, remote) | ||
|
||
scriptKey, err := btcec.ParsePubKey(scriptKeyBytes) | ||
require.NoError(t.t, err) | ||
require.NoError(t, err) | ||
assertAssetExists( | ||
t.t, remoteTapd, assetID, a.Amount, scriptKey, true, | ||
t, remoteTapd, assetID, a.Amount, scriptKey, true, | ||
true, false, | ||
) | ||
} | ||
} | ||
|
||
// closeTxOut returns either the local or remote output from the close | ||
// transaction, based on the information given in the close update. | ||
func closeTxOut(t *testing.T, closeTx *wire.MsgTx, | ||
closeUpdate *lnrpc.ChannelCloseUpdate, local bool) (*wire.TxOut, int) { | ||
|
||
var targetPkScript []byte | ||
if local { | ||
require.NotNil(t, closeUpdate.LocalCloseOutput) | ||
targetPkScript = closeUpdate.LocalCloseOutput.PkScript | ||
} else { | ||
require.NotNil(t, closeUpdate.RemoteCloseOutput) | ||
targetPkScript = closeUpdate.RemoteCloseOutput.PkScript | ||
} | ||
|
||
return findTxOut(t, closeTx, targetPkScript) | ||
} | ||
|
||
// findTxOut returns the transaction output with the target pk script from the | ||
// given transaction. | ||
func findTxOut(t *testing.T, tx *wire.MsgTx, targetPkScript []byte) ( | ||
*wire.TxOut, int) { | ||
|
||
for i, txOut := range tx.TxOut { | ||
if bytes.Equal(txOut.PkScript, targetPkScript) { | ||
return txOut, i | ||
} | ||
} | ||
|
||
t.Fatalf("close output (targetPkScript=%x) not found in close "+ | ||
"transaction", targetPkScript) | ||
|
||
return &wire.TxOut{}, 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
type tapClient struct { | ||
node *HarnessNode | ||
lnd *rpc.HarnessRPC | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
asset
->assert