itest: add integration test for scbforceclose

This commit is contained in:
Oli 2025-11-06 15:03:53 +01:00
parent 7fa66ac0d5
commit 0c4693ca58
No known key found for this signature in database
GPG key ID: 8E4256593F177720
5 changed files with 119 additions and 16 deletions

View file

@ -11,10 +11,10 @@ around the network).
The network is set up as follows:
```
Alice ◄──► Bob ◄──► Charlie ◄──► Dave
└───────►└──► Rusty ◄──┘
| └► Nifty
└► Snyke
Alice ◄──► Bob ◄──► Charlie ◄──► Dave
└───────►└──► Rusty ◄──┘
| Nifty ◄──┘ |
└► Snyke ◄─────────────┘
```
- Channel **Alice** - **Bob**: Remains open, used by `runZombieRecoveryLndLnd`.
@ -26,6 +26,7 @@ Alice ◄──► Bob ◄──► Charlie ◄──► Dave
`runSweepRemoteClosedLnd`.
- Channel **Charlie** - **Dave**: Remains open, used by
`runTriggerForceCloseLnd`.
- Channel **Charlie** - **Snyke**: Remains open, used by `runSCBForceClose`.
- Channel **Bob** - **Rusty**: Remains open, used by `runZombieRecoveryLndCln`.
- Channel **Rusty** - **Charlie**: Remains open, used by
`runZombieRecoveryClnLnd`.

View file

@ -0,0 +1,43 @@
package itest
import (
"fmt"
"testing"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/stretchr/testify/require"
)
func runScbForceClose(t *testing.T) {
charlieChannels := readChannelsJSON(t, "charlie")
snykeIdentity := getNodeIdentityKeyCln(t, "snyke")
var charlieSnykeChannel *lnrpc.Channel
for _, c := range charlieChannels {
if c.RemotePubkey == snykeIdentity {
charlieSnykeChannel = c
}
}
require.NotNil(
t, charlieSnykeChannel, "charlie-snyke channel not found",
)
scbFile := fmt.Sprintf(scbFilePattern, "charlie")
txHex, fullOutput := getScbForceClose(
t, "charlie", tempDir, scbFile,
charlieSnykeChannel.ChannelPoint,
)
// Outputs on a force-close transaction are always ordered by amount.
require.Contains(
t, fullOutput, "Possible anchor: idx=0 amount=330 sat",
)
require.Contains(
t, fullOutput, "Possible anchor: idx=1 amount=330 sat",
)
require.Contains(t, fullOutput, "Output to_remote: idx=2 amount=")
require.Contains(t, fullOutput, "Possible to_local/htlc: idx=3 amount=")
backend := connectBitcoind(t)
publishTx(t, txHex, backend)
}

View file

@ -26,9 +26,7 @@ done
# Spin up the network in detached mode.
compose_up
# Set up the basic A ◄─► B ◄─► C ◄─► D network.
# └────►└► R ◄┘
# └► N
# Set up the basic network.
setup_bitcoin
wait_for_nodes alice bob charlie dave rusty nifty snyke
@ -36,7 +34,7 @@ wait_for_nodes alice bob charlie dave rusty nifty snyke
do_for fund_node alice bob charlie dave rusty nifty snyke
# Alice, Bob and Charlie will open more than one channel each.
do_for fund_node alice alice bob charlie
do_for fund_node alice alice bob charlie charlie
mine 6
@ -51,6 +49,7 @@ connect_nodes bob rusty
connect_nodes bob nifty
connect_nodes charlie dave
connect_nodes charlie rusty
connect_nodes charlie snyke
connect_nodes rusty nifty
open_channel alice bob
@ -59,17 +58,21 @@ open_channel bob charlie
open_channel charlie dave
open_channel bob rusty
open_channel charlie rusty
open_channel charlie snyke
open_channel rusty nifty
open_channel alice snyke
echo "🔗 Set up network: Alice ◄─► Bob ◄─► Charlie ◄─► Dave network."
echo " └────────►└► Rusty ◄┘ "
echo " | └► Nifty"
echo " └► Snyke"
echo "🔗 Set up network:"
cat << EOF
Alice ◄──► Bob ◄──► Charlie ◄──► Dave
└───────►└──► Rusty ◄──┘
| Nifty ◄──┘ |
└► Snyke ◄─────────────┘
EOF
mine 12
num_channels=8
num_channels=9
wait_graph_sync alice $num_channels
wait_graph_sync bob $num_channels
@ -83,6 +86,7 @@ send_payment alice dave
send_payment alice rusty
send_payment dave rusty
send_payment alice snyke
send_payment charlie snyke
# Repeat the basic tests.
send_payment bob dave
@ -91,6 +95,7 @@ send_payment alice dave
send_payment alice rusty
send_payment dave rusty
send_payment alice snyke
send_payment charlie snyke
# Store all the channel information in separate JSON files.
alice listchannels > "$DIR/node-data/chantools/alice-channels.json"

View file

@ -33,6 +33,8 @@ const (
channelsFilePattern = "docker/node-data/chantools/%s-channels.json"
walletFilePattern = "docker/node-data/%s/data/chain/bitcoin/" +
"regtest/wallet.db"
scbFilePattern = "docker/node-data/%s/data/chain/bitcoin/" +
"regtest/channel.backup"
hsmSecretFilePattern = "docker/node-data/%s/regtest/hsm_secret"
nodeIdentityFilePattern = "docker/node-data/chantools/identities.txt"
nodeURIPattern = "%s@%s"
@ -48,9 +50,10 @@ const (
transactionHexIdent = "02000000"
rowSign = "Press <enter> to continue and sign the " +
"transaction or <ctrl+c> to abort:"
rowPublish = "Please publish this using any bitcoin node:"
rowTransaction = "Transaction:"
rowForceClose = "Found force close transaction"
rowPublish = "Please publish this using any bitcoin node:"
rowTransaction = "Transaction:"
rowForceClose = "Found force close transaction"
rowRawTransaction = "Raw transaction hex:"
)
var (
@ -321,6 +324,36 @@ func invokeCmdTriggerForceClose(t *testing.T, walletPassword *string,
return output
}
func invokeCmdScbForceClose(t *testing.T, walletPassword *string,
resultsDir string, args ...string) string {
t.Helper()
fullArgs := append([]string{
"--regtest", "--resultsdir", resultsDir, "scbforceclose",
}, args...)
proc := StartChantools(t, fullArgs...)
defer proc.Wait(t)
if walletPassword != nil {
pwPrompt := proc.ReadAvailableOutput(t, readTimeout)
require.Contains(t, pwPrompt, "Input wallet password:")
proc.WriteInput(t, *walletPassword+"\n")
proc.AssertNoStderr(t)
}
warnPrompt := proc.ReadAvailableOutput(t, defaultTimeout)
require.Contains(t, warnPrompt, "Type YES to proceed: ")
proc.WriteInput(t, "YES\n")
proc.AssertNoStderr(t)
output := proc.ReadAvailableOutput(t, longTimeout)
return output
}
func getNodeIdentityKey(t *testing.T, node string) string {
t.Helper()
@ -503,3 +536,20 @@ func getTriggerForceClose(t *testing.T, node, tempDir, apiURL, peerURI,
return txid
}
func getScbForceClose(t *testing.T, node, tempDir, multiBackup,
channelPoint string) (string, string) {
t.Helper()
walletDbPath := fmt.Sprintf(walletFilePattern, node)
cmdOutput := invokeCmdScbForceClose(
t, &emptyPassword, tempDir,
"--channel_point", channelPoint, "--walletdb", walletDbPath,
"--multi_file", multiBackup,
)
txHex := extractRowContent(cmdOutput, rowRawTransaction)
require.Contains(t, txHex, transactionHexIdent)
return txHex, cmdOutput
}

View file

@ -42,6 +42,10 @@ var testCases = []testCase{
name: "trigger force close cln",
fn: runTriggerForceCloseCln,
},
{
name: "scb force close",
fn: runScbForceClose,
},
}
// TestIntegration runs all integration test cases.