multi: add backward compatibility test

This commit is contained in:
Oliver Gugger 2025-05-15 11:17:08 +02:00
parent c99f2dc07c
commit 03c80cfeab
No known key found for this signature in database
GPG key ID: 8E4256593F177720
11 changed files with 270 additions and 46 deletions

View file

@ -279,11 +279,11 @@ jobs:
matrix:
include:
- name: bbolt
args: itest icase=terminal dbbackend=bbolt
args: itest-no-backward-compat icase=terminal dbbackend=bbolt
- name: sqlite
args: itest icase=terminal dbbackend=sqlite
args: itest-no-backward-compat icase=terminal dbbackend=sqlite
- name: postgres
args: itest icase=terminal dbbackend=postgres
args: itest-no-backward-compat icase=terminal dbbackend=postgres
- name: custom-channels
args: itest-only icase=custom_channels

3
.gitignore vendored
View file

@ -9,11 +9,12 @@ litcli-debug
.DS_Store
itest/btcd-itest
itest/litd-itest
itest/litd-itest*
itest/lnd-itest
itest/itest.test
itest/.logs
itest/*.log
itest/backward-compat
vendor
*.idea

View file

@ -24,6 +24,12 @@ PUBLIC_URL :=
# versions are checked against this version.
GO_VERSION = 1.23.6
# LITD_COMPAT_VERSIONS is a space-separated list of litd versions that are
# installed before running the integration tests which include backward
# compatibility tests. The list of versions must be in sync with any version
# used in the backwardCompat map in itest/litd_test_list_on_test.go.
LITD_COMPAT_VERSIONS = v0.14.1-alpha
LOOP_COMMIT := $(shell cat go.mod | \
grep $(LOOP_PKG) | \
head -n1 | \
@ -233,7 +239,11 @@ build-itest:
CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o itest/btcd-itest -ldflags "$(ITEST_LDFLAGS)" $(BTCD_PKG)
CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o itest/lnd-itest -ldflags "$(ITEST_LDFLAGS)" $(LND_PKG)/cmd/lnd
itest-only: build-itest
install-backward-compat-versions:
@$(call print, "Installing old versions of litd for backward compatibility tests.")
scripts/install-backward-compat-versions.sh '$(LITD_COMPAT_VERSIONS)'
run-itest-only:
@$(call print, "Building itest binary.")
CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o itest/litd-itest -ldflags "$(ITEST_LDFLAGS)" $(PKG)/cmd/litd
CGO_ENABLED=0 $(GOTEST) -v ./itest -tags="$(DEV_TAGS) $(ITEST_TAGS)" -c -o itest/itest.test
@ -242,8 +252,12 @@ itest-only: build-itest
rm -rf itest/*.log itest/.logs*; date
scripts/itest_part.sh $(ITEST_FLAGS)
itest-only: build-itest install-backward-compat-versions run-itest-only
itest: app-build build-itest itest-only
itest-no-backward-compat: app-build build-itest build-itest run-itest-only
# =============
# FLAKE HUNTING
# =============

View file

@ -207,20 +207,20 @@ func createTestAssetNetwork(t *harnessTest, net *NetworkHarness, charlieTap,
erinAssetBalance := assetSendAmount - erinFundingAmount
// Assert that we see the funding outputs in the wallet.
itest.AssertBalances(
assertBalance(
t.t, charlieTap, charlieFundingAmount,
itest.WithAssetID(assetID),
itest.WithScriptKeyType(asset.ScriptKeyScriptPathChannel),
itest.WithNumUtxos(1),
itest.WithScriptKey(fundingScriptKeyBytes),
)
itest.AssertBalances(
assertBalance(
t.t, daveTap, daveFundingAmount, itest.WithAssetID(assetID),
itest.WithScriptKeyType(asset.ScriptKeyScriptPathChannel),
itest.WithNumUtxos(1),
itest.WithScriptKey(fundingScriptKeyBytes),
)
itest.AssertBalances(
assertBalance(
t.t, erinTap, erinFundingAmount, itest.WithAssetID(assetID),
itest.WithScriptKeyType(asset.ScriptKeyScriptPathChannel),
itest.WithNumUtxos(1),
@ -229,14 +229,13 @@ func createTestAssetNetwork(t *harnessTest, net *NetworkHarness, charlieTap,
// After opening the channels, the asset balance of the funding nodes
// should have been decreased with the funding amount.
itest.AssertBalances(
t.t, charlieTap, charlieAssetBalance,
itest.WithAssetID(assetID),
assertBalance(
t.t, charlieTap, charlieAssetBalance, itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, daveTap, daveAssetBalance, itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, erinTap, erinAssetBalance, itest.WithAssetID(assetID),
)
@ -527,6 +526,18 @@ func createTestAssetNetworkGroupKey(ctx context.Context, t *harnessTest,
return chanPointCD, chanPointEF
}
// assertBalance is a thin wrapper around itest.AssertBalances that skips the
// balance check for old versions during the backward compatibility test.
func assertBalance(t *testing.T, client *tapClient, balance uint64,
opts ...itest.BalanceOption) {
if client.node.Cfg.SkipBalanceChecks {
return
}
itest.AssertBalances(t, client, balance, opts...)
}
// sendAssetsAndAssert sends the given amount of assets to the recipient and
// asserts that the transfer was successful. It also checks that the asset
// balance of the sender and recipient is as expected.
@ -718,6 +729,12 @@ func assertPendingChannels(t *testing.T, node *HarnessNode,
require.NoError(t, err)
require.Len(t, pendingChannelsResp.PendingOpenChannels, numChannels)
// For older versions (during the backward compatibility test), if the
// channel custom data is in the old format, we can't do further checks.
if node.Cfg.OldChannelFormat {
return
}
pendingChan := pendingChannelsResp.PendingOpenChannels[0]
var pendingJSON rfqmsg.JsonAssetChannel
err = json.Unmarshal(
@ -768,6 +785,12 @@ func haveFundingAsset(assetChannel *rfqmsg.JsonAssetChannel,
func assertAssetChan(t *testing.T, src, dst *HarnessNode, fundingAmount uint64,
channelAssets []*taprpc.Asset) {
if src.Cfg.OldChannelFormat {
t.Logf("Skipping asset channel check for %s->%s, old format",
src.Cfg.Name, dst.Cfg.Name)
return
}
err := wait.NoError(func() error {
a, err := getChannelCustomData(src, dst)
if err != nil {
@ -1822,8 +1845,12 @@ func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness,
)
require.NoError(t.t, err)
assertWaitingCloseChannelAssetData(t.t, local, chanPoint)
assertWaitingCloseChannelAssetData(t.t, remote, chanPoint)
if !local.Cfg.OldChannelFormat {
assertWaitingCloseChannelAssetData(t.t, local, chanPoint)
}
if !remote.Cfg.OldChannelFormat {
assertWaitingCloseChannelAssetData(t.t, remote, chanPoint)
}
mineBlocks(t, net, 1, 1)
@ -1846,8 +1873,12 @@ func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness,
universeTap,
)
assertClosedChannelAssetData(t.t, local, chanPoint)
assertClosedChannelAssetData(t.t, remote, chanPoint)
if !local.Cfg.OldChannelFormat {
assertClosedChannelAssetData(t.t, local, chanPoint)
}
if !remote.Cfg.OldChannelFormat {
assertClosedChannelAssetData(t.t, remote, chanPoint)
}
}
// assertDefaultCoOpCloseBalance returns a default implementation of the co-op

View file

@ -639,20 +639,20 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness,
erinAssetBalance += 3
fabiaAssetBalance -= 3
yaraAssetBalance -= 1
itest.AssertBalances(
assertBalance(
t.t, charlieTap, charlieAssetBalance,
itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, daveTap, daveAssetBalance, itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, erinTap, erinAssetBalance, itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, fabiaTap, fabiaAssetBalance, itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, yaraTap, yaraAssetBalance, itest.WithAssetID(assetID),
)
@ -698,7 +698,7 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness,
)
// Charlie should still have four asset pieces, two with the same size.
itest.AssertBalances(
assertBalance(
t.t, charlieTap, charlieAssetBalance,
itest.WithAssetID(assetID), itest.WithNumUtxos(2),
itest.WithScriptKeyType(asset.ScriptKeyBip86),
@ -706,7 +706,7 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness,
// Dave should have two outputs, one from the initial channel with Yara
// and one from the remaining amount of the channel with Charlie.
itest.AssertBalances(
assertBalance(
t.t, daveTap, daveAssetBalance, itest.WithAssetID(assetID),
itest.WithNumUtxos(2),
itest.WithScriptKeyType(asset.ScriptKeyBip86),
@ -714,12 +714,12 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness,
// Fabia and Yara should all have a single output each, just what was
// left over from the initial channel.
itest.AssertBalances(
assertBalance(
t.t, fabiaTap, fabiaAssetBalance, itest.WithAssetID(assetID),
itest.WithNumUtxos(1),
itest.WithScriptKeyType(asset.ScriptKeyBip86),
)
itest.AssertBalances(
assertBalance(
t.t, yaraTap, yaraAssetBalance, itest.WithAssetID(assetID),
itest.WithNumUtxos(1),
itest.WithScriptKeyType(asset.ScriptKeyBip86),
@ -728,24 +728,24 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness,
// Erin didn't use all of his assets when opening the channel, so he
// should have two outputs, the change from the channel opening and the
// remaining amount after closing the channel.
itest.AssertBalances(
assertBalance(
t.t, erinTap, erinAssetBalance, itest.WithAssetID(assetID),
itest.WithNumUtxos(2),
itest.WithScriptKeyType(asset.ScriptKeyBip86),
)
// The asset balances should still remain unchanged.
itest.AssertBalances(
assertBalance(
t.t, charlieTap, charlieAssetBalance,
itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, daveTap, daveAssetBalance, itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, erinTap, erinAssetBalance, itest.WithAssetID(assetID),
)
itest.AssertBalances(
assertBalance(
t.t, fabiaTap, fabiaAssetBalance, itest.WithAssetID(assetID),
)
}

View file

@ -68,6 +68,26 @@ var (
"litdbbackend", terminal.DatabaseBackendBbolt, "Set the "+
"database backend to use when starting a LiT daemon.",
)
// backwardCompatFlagOverride is a map of LiT versions to a map of
// config file flags that need to be overwritten or added for the target
// version to work. If a flag in the map is empty, it means it will be
// removed from the config file of that version.
backwardCompatFlagOverride = map[string]map[string]string{
"v0.14.1-alpha": {
"databasebackend": "",
"lnd.logging.no-commit-hash": "",
},
}
// backwardCompatConfigOverride is a map of LiT versions to a map of
// config file overriding functions.
backwardCompatConfigOverride = map[string]func(*LitNodeConfig){
"v0.14.1-alpha": func(cfg *LitNodeConfig) {
cfg.OldChannelFormat = true
cfg.SkipBalanceChecks = true
},
}
)
// Option is a function for updating a node's configuration.
@ -103,6 +123,14 @@ type LitNodeConfig struct {
LitPort int
LitRESTPort int
// OldChannelFormat is a flag for turning off certain checks for old
// versions of litd during the backward compatibility test.
OldChannelFormat bool
// SkipBalanceChecks is a flag for turning off certain checks for old
// versions of litd during the backward compatibility test.
SkipBalanceChecks bool
// backupDBDir is the path where a database backup is stored, if any.
backupDBDir string
}
@ -616,17 +644,60 @@ func renameFile(fromFileName, toFileName string) {
}
}
// overrideFlagsAndBinary is a helper function that checks if the passed node
// name needs a version downgrade and if so, it will return the new binary
// name and the new arguments.
func (hn *HarnessNode) overrideFlagsAndBinary(backwardCompat map[string]string,
binary string, args []LitArgOption) (string, []LitArgOption) {
if backwardCompat == nil {
return binary, args
}
downgradeVersion, ok := backwardCompat[hn.Cfg.Name]
if !ok {
return binary, args
}
newBinary := fmt.Sprintf("%s-%s", binary, downgradeVersion)
flagOverride, ok := backwardCompatFlagOverride[downgradeVersion]
if !ok {
return newBinary, args
}
for k, v := range flagOverride {
if v == "" {
args = append(args, WithoutLitArg(k))
} else {
args = append(args, WithLitArg(k, v))
}
}
cfgOverride, ok := backwardCompatConfigOverride[downgradeVersion]
if ok && cfgOverride != nil {
cfgOverride(hn.Cfg)
}
return newBinary, args
}
// Start launches a new process running lnd. Additionally, the PID of the
// launched process is saved in order to possibly kill the process forcibly
// later.
//
// This may not clean up properly if an error is returned, so the caller should
// call shutdown() regardless of the return value.
func (hn *HarnessNode) Start(litdBinary string, litdError chan<- error,
func (hn *HarnessNode) Start(litdBinary string,
backwardCompat map[string]string, litdError chan<- error,
waitForStart bool, litArgOpts ...LitArgOption) error {
hn.quit = make(chan struct{})
litdBinary, litArgOpts = hn.overrideFlagsAndBinary(
backwardCompat, litdBinary, litArgOpts,
)
args := hn.Cfg.GenArgs(litArgOpts...)
hn.cmd = exec.Command(litdBinary, args...)

View file

@ -71,6 +71,7 @@ func TestLightningTerminal(t *testing.T) {
lndSubTest := lndHarness.Subtest(t1)
litdHarness, err := NewNetworkHarness(
lndSubTest, chainBackend, binary, feeService,
testCase.backwardCompat,
)
require.NoError(t1, err)

View file

@ -29,6 +29,15 @@ var allTestCases = []*testCase{
test: testCustomChannels,
noAliceBob: true,
},
{
name: "custom channels backward compatibility",
test: testCustomChannels,
noAliceBob: true,
backwardCompat: map[string]string{
"Dave": "v0.14.1-alpha",
"Fabia": "v0.14.1-alpha",
},
},
{
name: "custom channels large",
test: testCustomChannelsLarge,

View file

@ -69,6 +69,11 @@ type NetworkHarness struct {
Alice *HarnessNode
Bob *HarnessNode
// backwardCompat is a map of node names to the version of litd that
// should be used for them. If the map is empty, then the latest/current
// version will be used for all nodes.
backwardCompat map[string]string
// Channel for transmitting stderr output from failed lightning node
// to main process.
lndErrorChan chan error
@ -78,19 +83,20 @@ type NetworkHarness struct {
// NewNetworkHarness creates a new network test harness.
func NewNetworkHarness(lndHarness *lntest.HarnessTest, b node.BackendConfig,
litdBinary string, feeService lntest.WebFeeService) (*NetworkHarness,
error) {
litdBinary string, feeService lntest.WebFeeService,
backwardCompat map[string]string) (*NetworkHarness, error) {
n := NetworkHarness{
activeNodes: make(map[int]*HarnessNode),
nodesByPub: make(map[string]*HarnessNode),
lndErrorChan: make(chan error),
netParams: lndHarness.Miner().ActiveNet,
Miner: lndHarness.Miner(),
LNDHarness: lndHarness,
BackendCfg: b,
litdBinary: litdBinary,
feeService: feeService,
activeNodes: make(map[int]*HarnessNode),
nodesByPub: make(map[string]*HarnessNode),
lndErrorChan: make(chan error),
netParams: lndHarness.Miner().ActiveNet,
Miner: lndHarness.Miner(),
LNDHarness: lndHarness,
BackendCfg: b,
litdBinary: litdBinary,
feeService: feeService,
backwardCompat: backwardCompat,
}
return &n, nil
}
@ -373,7 +379,7 @@ func (n *NetworkHarness) newNode(t *testing.T, name string, extraArgs,
n.activeNodes[node.NodeID] = node
n.mtx.Unlock()
err = node.Start(n.litdBinary, n.lndErrorChan, wait)
err = node.Start(n.litdBinary, n.backwardCompat, n.lndErrorChan, wait)
if err != nil {
return nil, err
}
@ -767,7 +773,10 @@ func (n *NetworkHarness) RestartNodeNoUnlock(node *HarnessNode,
}
}
return node.Start(n.litdBinary, n.lndErrorChan, wait, litArgOpts...)
return node.Start(
n.litdBinary, n.backwardCompat, n.lndErrorChan, wait,
litArgOpts...,
)
}
// SuspendNode stops the given node and returns a callback that can be used to
@ -778,7 +787,9 @@ func (n *NetworkHarness) SuspendNode(node *HarnessNode) (func() error, error) {
}
restart := func() error {
return node.Start(n.litdBinary, n.lndErrorChan, true)
return node.Start(
n.litdBinary, n.backwardCompat, n.lndErrorChan, true,
)
}
return restart, nil

View file

@ -129,6 +129,12 @@ type testCase struct {
test func(ctx context.Context, net *NetworkHarness,
t *harnessTest)
noAliceBob bool
// backwardCompat is a map of node names to the version they are going
// to be downgraded to during the specific test. Make sure to update the
// LITD_COMPAT_VERSIONS variable in the Makefile to include all versions
// used for any backward compatibility tests.
backwardCompat map[string]string
}
// waitForNTxsInMempool polls until finding the desired number of transactions

View file

@ -0,0 +1,80 @@
#!/bin/bash
# Build and install older versions of `litd` for backward compatibility
# integration tests.
#
# Usage:
# ./scripts/install-backward-compat-versions.sh "<version1> <version2> ..."
#
# Example:
# ./scripts/install-backward-compat-versions.sh "v0.12.0-beta v0.14.1-alpha"
#
# Parameters:
# $1 Space-separated list of `litd` versions to install (required).
#
# Environment Variables:
# REPO_URL URL of the litd repository (defaults to:
# https://github.com/lightninglabs/lightning-terminal.git)
#
# Notes:
# - Each version will be installed under a version-specific path.
set -ex
VERSIONS="$1"
REPO_URL="${REPO_URL:-https://github.com/lightninglabs/lightning-terminal.git}"
# Make sure at least one version is set.
if [[ -z "${VERSIONS}" ]]; then
echo "Usage: $0 '<version1> <version2> ...'"
echo "Please provide at least one version to install."
exit 1
fi
# Directory of the script file, independent of where it's called from.
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ITEST_DIR="${DIR}/../itest"
TARGET_DIR="${ITEST_DIR}/backward-compat"
mkdir -p "${TARGET_DIR}" && cd "${TARGET_DIR}"
# First, make sure we've cloned each version of litd.
for VERSION in ${VERSIONS}; do
VERSION_DIR="${TARGET_DIR}/litd-${VERSION}"
# Check if the version is already installed.
if [[ -f "${VERSION_DIR}/Makefile" ]]; then
echo "Version ${VERSION} is already installed."
continue
fi
# Clone the specified version of litd.
echo "Installing litd ${VERSION} to ${VERSION_DIR}..."
git clone ${REPO_URL} --depth 1 -b "$VERSION" "${VERSION_DIR}"
done
# Build the itest binary for each version.
for VERSION in ${VERSIONS}; do
VERSION_DIR="${TARGET_DIR}/litd-${VERSION}"
SRC="${VERSION_DIR}/itest/litd-itest"
DST="${ITEST_DIR}/litd-itest-${VERSION}"
if [[ -f "${DST}" ]]; then
echo "Binary ${DST} is already available, skipping build."
continue
fi
echo "Building litd ${VERSION}..."
cd "${VERSION_DIR}"
# The Makefile was changed a bit, but the "itest-only" target that skips the
# frontend build has existed for a while. We use it to build the itest binary
# (but don't actually run the tests).
make itest-only icase=nothing
# Copy the resulting binary to the main itest directory.
echo "Copying ${SRC} to ${DST}"
cp "${SRC}" "${DST}"
done