From ddeb63ef50e39568c77ad2ea6326578fd463f7ac Mon Sep 17 00:00:00 2001 From: Dario Anongba Varela Date: Tue, 17 Mar 2026 19:14:05 +0100 Subject: [PATCH 1/6] itest: add NewNodeWithBinary and UpgradeNode to harness Add two methods to IntegratedNetworkHarness for backward compatibility testing. NewNodeWithBinary starts a node with a specific binary path (e.g. an older release), while UpgradeNode stops a node and restarts it with the harness's default (current) binary, preserving data directories. --- itest/integrated_harness.go | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/itest/integrated_harness.go b/itest/integrated_harness.go index c1e3d1b15a..2bdc0d993c 100644 --- a/itest/integrated_harness.go +++ b/itest/integrated_harness.go @@ -100,6 +100,53 @@ func (h *IntegratedNetworkHarness) NewNode(name string, return n } +// NewNodeWithBinary creates, starts, and returns a new IntegratedNode that +// runs the specified binary instead of the harness's default binary. This is +// used for backward compatibility testing where some nodes run an older +// release while others run the current build. +func (h *IntegratedNetworkHarness) NewNodeWithBinary(name, binaryPath string, + extraLndArgs, extraTapdArgs []string) *IntegratedNode { + + h.t.Helper() + + chainArgs := h.chainBackend.GenArgs() + lndArgs := append(chainArgs, extraLndArgs...) + + if h.FeeServiceURL != "" { + lndArgs = append(lndArgs, "--fee.url="+h.FeeServiceURL) + } + + n := NewIntegratedNode( + h.t, name, binaryPath, h.netParams, lndArgs, extraTapdArgs, + ) + n.Start() + + h.activeNodes[name] = n + + return n +} + +// UpgradeNode stops the given node and restarts it using the harness's +// default (current) binary, preserving its data directories. This simulates +// a node operator upgrading their software mid-operation. +func (h *IntegratedNetworkHarness) UpgradeNode( + node *IntegratedNode) { + + h.t.Helper() + + node.Stop() + + // Switch the binary path to the harness's current build. + node.Cfg.BinaryPath = h.binary + + // Remove the ready file so Start() waits for the new instance. + if node.readyFile != "" { + _ = os.Remove(node.readyFile) + } + + node.Start() +} + // TearDown stops all active nodes managed by this harness. func (h *IntegratedNetworkHarness) TearDown() { h.t.Helper() From d6b85db1cf35c8f83302b2534e8b598f5f8c9d97 Mon Sep 17 00:00:00 2001 From: Dario Anongba Varela Date: Tue, 17 Mar 2026 19:15:04 +0100 Subject: [PATCH 2/6] scripts: add build-compat-binary for historical releases Add a script that builds the tapd-integrated binary from a specific git tag using a temporary worktree. Built binaries are cached locally so subsequent invocations are instant. This supports backward compatibility testing without requiring release artifact publishing. --- scripts/build-compat-binary.sh | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100755 scripts/build-compat-binary.sh diff --git a/scripts/build-compat-binary.sh b/scripts/build-compat-binary.sh new file mode 100755 index 0000000000..247acbb6ef --- /dev/null +++ b/scripts/build-compat-binary.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +# build-compat-binary.sh builds the tapd-integrated binary from a specific +# git tag for backward compatibility testing. Built binaries are cached in +# a local directory so subsequent invocations are instant. +# +# Usage: +# ./scripts/build-compat-binary.sh [cache-dir] +# +# Examples: +# ./scripts/build-compat-binary.sh v0.8.0 +# ./scripts/build-compat-binary.sh v0.8.0 /tmp/compat-bins +# +# The script prints the absolute path to the built binary on success. + +set -euo pipefail + +if [ $# -lt 1 ]; then + echo "Usage: $0 [cache-dir]" >&2 + exit 1 +fi + +VERSION_TAG="$1" +CACHE_DIR="${2:-${HOME}/.tapd-compat-bins}" +BINARY_NAME="tapd-integrated-${VERSION_TAG}" +BINARY_PATH="${CACHE_DIR}/${BINARY_NAME}" + +# If the binary already exists in the cache, skip the build. +if [ -x "${BINARY_PATH}" ]; then + echo "${BINARY_PATH}" + exit 0 +fi + +mkdir -p "${CACHE_DIR}" + +# We need the repo root to create a temporary worktree. +REPO_ROOT="$(git rev-parse --show-toplevel)" +WORK_DIR="$(mktemp -d)" + +cleanup() { + # Remove the worktree and temp directory. + git -C "${REPO_ROOT}" worktree remove --force "${WORK_DIR}" \ + 2>/dev/null || true + rm -rf "${WORK_DIR}" 2>/dev/null || true +} +trap cleanup EXIT + +echo "Building tapd-integrated at ${VERSION_TAG}..." >&2 + +# Create a detached worktree at the requested tag. This avoids touching +# the developer's working copy. +git -C "${REPO_ROOT}" worktree add --detach "${WORK_DIR}" "${VERSION_TAG}" + +# Build the integrated binary inside the worktree. CGO is disabled for +# portability, matching the itest build. +( + cd "${WORK_DIR}" + CGO_ENABLED=0 go build -tags="dev monitoring" \ + -o "${BINARY_PATH}" \ + ./cmd/tapd-integrated +) + +chmod +x "${BINARY_PATH}" + +echo "Cached ${BINARY_NAME} at ${BINARY_PATH}" >&2 +echo "${BINARY_PATH}" From 41bdff885797496675e37f0de65424c8a0d5f0ba Mon Sep 17 00:00:00 2001 From: Dario Anongba Varela Date: Tue, 17 Mar 2026 19:15:31 +0100 Subject: [PATCH 3/6] rfqmsg: add versioned backward compatibility fixture tests Add wire format fixtures for v0.8 RFQ messages (request, accept, reject, HTLC) and a backward compatibility test that decodes each fixture and verifies round-trip encoding. Fixtures are generated deterministically via the gen_test_vectors build tag and tested on every PR. --- rfqmsg/backward_compat_test.go | 130 ++++++++++++++++++++++++ rfqmsg/compat_fixtures_test.go | 130 ++++++++++++++++++++++++ rfqmsg/testdata/compat/v0.8/accept.hex | 1 + rfqmsg/testdata/compat/v0.8/htlc.hex | 1 + rfqmsg/testdata/compat/v0.8/reject.hex | 1 + rfqmsg/testdata/compat/v0.8/request.hex | 1 + 6 files changed, 264 insertions(+) create mode 100644 rfqmsg/backward_compat_test.go create mode 100644 rfqmsg/compat_fixtures_test.go create mode 100644 rfqmsg/testdata/compat/v0.8/accept.hex create mode 100644 rfqmsg/testdata/compat/v0.8/htlc.hex create mode 100644 rfqmsg/testdata/compat/v0.8/reject.hex create mode 100644 rfqmsg/testdata/compat/v0.8/request.hex diff --git a/rfqmsg/backward_compat_test.go b/rfqmsg/backward_compat_test.go new file mode 100644 index 0000000000..54f1cfa9d6 --- /dev/null +++ b/rfqmsg/backward_compat_test.go @@ -0,0 +1,130 @@ +package rfqmsg + +import ( + "bytes" + "encoding/hex" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +// compatVersionsToTest lists all historical versions whose fixtures we +// validate against the current code. Add new entries as releases are cut. +var compatVersionsToTest = []string{ + "v0.8", +} + +// readHexFixture reads a hex-encoded fixture file from the testdata/compat +// directory and returns the decoded bytes. +func readHexFixture(t *testing.T, version, name string) []byte { + t.Helper() + + path := filepath.Join("testdata", compatFixtureDir, version, name) + hexBytes, err := os.ReadFile(path) + if os.IsNotExist(err) { + t.Skipf("fixture %s not found (run make gen-compat-fixtures "+ + "to generate)", path) + } + require.NoError(t, err) + + decoded, err := hex.DecodeString(string(hexBytes)) + require.NoError(t, err, "invalid hex in fixture %s", path) + + return decoded +} + +// TestBackwardCompatFixtures verifies that all historical wire format fixtures +// can be successfully decoded by the current code and survive a round-trip +// encode/decode cycle. This test runs on every PR as part of normal CI. +func TestBackwardCompatFixtures(t *testing.T) { + t.Parallel() + + for _, version := range compatVersionsToTest { + version := version + t.Run(version, func(t *testing.T) { + t.Parallel() + + t.Run("request", func(t *testing.T) { + testCompatRequest(t, version) + }) + t.Run("accept", func(t *testing.T) { + testCompatAccept(t, version) + }) + t.Run("reject", func(t *testing.T) { + testCompatReject(t, version) + }) + t.Run("htlc", func(t *testing.T) { + testCompatHtlc(t, version) + }) + }) + } +} + +// testCompatRequest tests that a historical request fixture can be decoded +// and re-encoded to produce identical bytes. +func testCompatRequest(t *testing.T, version string) { + t.Helper() + + raw := readHexFixture(t, version, "request.hex") + + var req requestWireMsgData + err := req.Decode(bytes.NewReader(raw)) + require.NoError(t, err, "failed to decode %s request fixture", version) + + // Re-encode and verify round-trip. + reEncoded, err := req.Bytes() + require.NoError(t, err, "failed to re-encode request") + require.Equal(t, raw, reEncoded, + "request round-trip mismatch for %s", version) +} + +// testCompatAccept tests that a historical accept fixture can be decoded +// and re-encoded to produce identical bytes. +func testCompatAccept(t *testing.T, version string) { + t.Helper() + + raw := readHexFixture(t, version, "accept.hex") + + var accept acceptWireMsgData + err := accept.Decode(bytes.NewReader(raw)) + require.NoError(t, err, "failed to decode %s accept fixture", version) + + reEncoded, err := accept.Bytes() + require.NoError(t, err, "failed to re-encode accept") + require.Equal(t, raw, reEncoded, + "accept round-trip mismatch for %s", version) +} + +// testCompatReject tests that a historical reject fixture can be decoded +// and re-encoded to produce identical bytes. +func testCompatReject(t *testing.T, version string) { + t.Helper() + + raw := readHexFixture(t, version, "reject.hex") + + var reject rejectWireMsgData + err := reject.Decode(bytes.NewReader(raw)) + require.NoError(t, err, "failed to decode %s reject fixture", version) + + reEncoded, err := reject.Bytes() + require.NoError(t, err, "failed to re-encode reject") + require.Equal(t, raw, reEncoded, + "reject round-trip mismatch for %s", version) +} + +// testCompatHtlc tests that a historical HTLC fixture can be decoded +// and re-encoded to produce identical bytes. +func testCompatHtlc(t *testing.T, version string) { + t.Helper() + + raw := readHexFixture(t, version, "htlc.hex") + + htlc, err := DecodeHtlc(raw) + require.NoError(t, err, "failed to decode %s HTLC fixture", version) + + reEncoded := htlc.Bytes() + require.Equal(t, raw, reEncoded, + "HTLC round-trip mismatch for %s", version) +} diff --git a/rfqmsg/compat_fixtures_test.go b/rfqmsg/compat_fixtures_test.go new file mode 100644 index 0000000000..fc1fc6b285 --- /dev/null +++ b/rfqmsg/compat_fixtures_test.go @@ -0,0 +1,130 @@ +package rfqmsg + +import ( + "bytes" + "path/filepath" + "testing" + + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/internal/test" + "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/require" +) + +const ( + // compatFixtureVersion is the current version string used for + // organizing backward compatibility fixtures. Update this when + // cutting a new release. + compatFixtureVersion = "v0.8" + + // compatFixtureDir is the directory under testdata where versioned + // compat fixtures are stored. + compatFixtureDir = "compat" +) + +// compatFixturePath returns the testdata-relative path for a compat fixture +// file. +func compatFixturePath(version, name string) string { + return filepath.Join(compatFixtureDir, version, name) +} + +// TestGenerateCompatFixtures generates deterministic wire format fixtures for +// the current version of the RFQ message types. These fixtures are committed +// to the repository and used by TestBackwardCompatFixtures to verify that +// future code changes don't break decoding of historical wire formats. +// +// Run with: make unit gen-test-vectors=true pkg=rfqmsg case=^TestGenerateCompatFixtures$ +func TestGenerateCompatFixtures(t *testing.T) { + t.Parallel() + + // Use deterministic test data so fixtures are reproducible. + var ( + idBytes [32]byte + assetIDBytes [32]byte + ) + copy(idBytes[:], bytes.Repeat([]byte{0xaa}, 32)) + copy(assetIDBytes[:], bytes.Repeat([]byte{0xbb}, 32)) + + id := ID(idBytes) + assetID := asset.ID(assetIDBytes) + var zeroAssetID asset.ID + + // --- Request fixture --- + req := requestWireMsgData{ + Version: tlv.NewPrimitiveRecord[tlv.TlvType0](V1), + ID: tlv.NewPrimitiveRecord[tlv.TlvType2](id), + // Use a far-future timestamp so the fixture stays valid + // across all test runs (year 2099). + Expiry: tlv.NewPrimitiveRecord[tlv.TlvType6]( + uint64(4102444800), + ), + InAssetID: tlv.SomeRecordT[tlv.TlvType9]( + tlv.NewPrimitiveRecord[tlv.TlvType9](assetID), + ), + OutAssetID: tlv.SomeRecordT[tlv.TlvType13]( + tlv.NewPrimitiveRecord[tlv.TlvType13](zeroAssetID), + ), + MaxInAsset: tlv.NewPrimitiveRecord[tlv.TlvType16]( + uint64(50000), + ), + } + reqBytes, err := req.Bytes() + require.NoError(t, err) + test.WriteTestFileHex( + t, compatFixturePath(compatFixtureVersion, "request.hex"), + reqBytes, + ) + + // --- Accept fixture --- + var sig [64]byte + copy(sig[:], bytes.Repeat([]byte{0xcc}, 64)) + + inRate := NewTlvFixedPointFromUint64(42000, 2) + outRate := NewTlvFixedPointFromUint64(1, 0) + + accept := acceptWireMsgData{ + Version: tlv.NewPrimitiveRecord[tlv.TlvType0](V1), + ID: tlv.NewPrimitiveRecord[tlv.TlvType2](id), + Expiry: tlv.NewPrimitiveRecord[tlv.TlvType4](uint64(4102444800)), + Sig: tlv.NewPrimitiveRecord[tlv.TlvType6](sig), + InAssetRate: tlv.NewRecordT[tlv.TlvType8](inRate), + OutAssetRate: tlv.NewRecordT[tlv.TlvType10](outRate), + } + acceptBytes, err := accept.Bytes() + require.NoError(t, err) + test.WriteTestFileHex( + t, compatFixturePath(compatFixtureVersion, "accept.hex"), + acceptBytes, + ) + + // --- Reject fixture --- + reject := rejectWireMsgData{ + Version: tlv.NewPrimitiveRecord[tlv.TlvType0](V1), + ID: tlv.NewPrimitiveRecord[tlv.TlvType2](id), + Err: tlv.NewRecordT[tlv.TlvType5](RejectErr{ + Code: PriceOracleUnavailableRejectCode, + Msg: "rates expired", + }), + } + rejectBytes, err := reject.Bytes() + require.NoError(t, err) + test.WriteTestFileHex( + t, compatFixturePath(compatFixtureVersion, "reject.hex"), + rejectBytes, + ) + + // --- HTLC fixture --- + htlc := NewHtlc( + []*AssetBalance{ + NewAssetBalance(assetID, 1000), + }, + fn.Some(id), + fn.None[[]ID](), + ) + htlcBytes := htlc.Bytes() + test.WriteTestFileHex( + t, compatFixturePath(compatFixtureVersion, "htlc.hex"), + htlcBytes, + ) +} diff --git a/rfqmsg/testdata/compat/v0.8/accept.hex b/rfqmsg/testdata/compat/v0.8/accept.hex new file mode 100644 index 0000000000..3c260b6c9b --- /dev/null +++ b/rfqmsg/testdata/compat/v0.8/accept.hex @@ -0,0 +1 @@ +0001010220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa040800000000f48657000640cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc080302a4100a020001 \ No newline at end of file diff --git a/rfqmsg/testdata/compat/v0.8/htlc.hex b/rfqmsg/testdata/compat/v0.8/htlc.hex new file mode 100644 index 0000000000..33aaa6c2ab --- /dev/null +++ b/rfqmsg/testdata/compat/v0.8/htlc.hex @@ -0,0 +1 @@ +fe000100002e012c0020bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb010800000000000003e8fe0001000220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file diff --git a/rfqmsg/testdata/compat/v0.8/reject.hex b/rfqmsg/testdata/compat/v0.8/reject.hex new file mode 100644 index 0000000000..bc0eb3b6ca --- /dev/null +++ b/rfqmsg/testdata/compat/v0.8/reject.hex @@ -0,0 +1 @@ +0001010220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa050e0172617465732065787069726564 \ No newline at end of file diff --git a/rfqmsg/testdata/compat/v0.8/request.hex b/rfqmsg/testdata/compat/v0.8/request.hex new file mode 100644 index 0000000000..9eca682b51 --- /dev/null +++ b/rfqmsg/testdata/compat/v0.8/request.hex @@ -0,0 +1 @@ +0001010220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa040100060800000000f48657000920bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0d2000000000000000000000000000000000000000000000000000000000000000001008000000000000c350 \ No newline at end of file From 3e20f127b31775bfd3640a2a886f83178052bdad Mon Sep 17 00:00:00 2001 From: Dario Anongba Varela Date: Tue, 17 Mar 2026 19:15:48 +0100 Subject: [PATCH 4/6] tapchannelmsg: add versioned backward compatibility fixture tests Add v0.8 wire format fixtures for channel messages (funding blob, commitment blob, HTLC blob) organized under testdata/compat/v0.8/. These are captured from real protocol interactions. A backward compatibility test verifies that each fixture can be decoded by the current code on every PR. --- tapchannelmsg/backward_compat_test.go | 117 +++++++++ .../compat/v0.8/commitment-blob.hexdump | 228 ++++++++++++++++++ .../testdata/compat/v0.8/funding-blob.hexdump | 106 ++++++++ .../testdata/compat/v0.8/htlc-blob.hexdump | 3 + 4 files changed, 454 insertions(+) create mode 100644 tapchannelmsg/backward_compat_test.go create mode 100644 tapchannelmsg/testdata/compat/v0.8/commitment-blob.hexdump create mode 100644 tapchannelmsg/testdata/compat/v0.8/funding-blob.hexdump create mode 100644 tapchannelmsg/testdata/compat/v0.8/htlc-blob.hexdump diff --git a/tapchannelmsg/backward_compat_test.go b/tapchannelmsg/backward_compat_test.go new file mode 100644 index 0000000000..38937a55c5 --- /dev/null +++ b/tapchannelmsg/backward_compat_test.go @@ -0,0 +1,117 @@ +package tapchannelmsg + +import ( + "os" + "path/filepath" + "testing" + + "github.com/lightninglabs/taproot-assets/rfqmsg" + "github.com/stretchr/testify/require" +) + +// compatVersions lists all historical versions whose wire format fixtures we +// validate against the current code. Add new entries as releases are cut. +var compatVersions = []string{ + "v0.8", +} + +// TestBackwardCompatFixtures verifies that all historical wire format fixtures +// for channel messages can be decoded by the current code. This test runs on +// every PR as part of normal CI. +// +// Unlike a full round-trip test, these fixtures are from real protocol +// interactions (captured from log hex dumps) so we only verify successful +// decode — the re-encoded form may differ due to optional field ordering +// changes. +func TestBackwardCompatFixtures(t *testing.T) { + t.Parallel() + + for _, version := range compatVersions { + version := version + t.Run(version, func(t *testing.T) { + t.Parallel() + + t.Run("funding blob", func(t *testing.T) { + testCompatFundingBlob(t, version) + }) + t.Run("commitment blob", func(t *testing.T) { + testCompatCommitmentBlob(t, version) + }) + t.Run("htlc blob", func(t *testing.T) { + testCompatHtlcBlob(t, version) + }) + }) + } +} + +// testCompatFundingBlob tests that a historical funding blob can be decoded. +func testCompatFundingBlob(t *testing.T, version string) { + t.Helper() + + path := filepath.Join( + "testdata", "compat", version, "funding-blob.hexdump", + ) + data, err := os.ReadFile(path) + if os.IsNotExist(err) { + t.Skipf("fixture %s not found", path) + } + require.NoError(t, err) + + hexBytes, err := ExtractHexDump(string(data)) + require.NoError(t, err, "failed to extract hex from %s", path) + + openChan, err := DecodeOpenChannel(hexBytes) + require.NoError(t, err, "failed to decode %s funding blob", version) + require.NotEmpty(t, openChan.Assets(), + "funding blob should contain assets") +} + +// testCompatCommitmentBlob tests that a historical commitment blob can be +// decoded. +func testCompatCommitmentBlob(t *testing.T, version string) { + t.Helper() + + path := filepath.Join( + "testdata", "compat", version, "commitment-blob.hexdump", + ) + data, err := os.ReadFile(path) + if os.IsNotExist(err) { + t.Skipf("fixture %s not found", path) + } + require.NoError(t, err) + + hexBytes, err := ExtractHexDump(string(data)) + require.NoError(t, err, "failed to extract hex from %s", path) + + commit, err := DecodeCommitment(hexBytes) + require.NoError(t, err, + "failed to decode %s commitment blob", version) + + // Verify we can access the balance fields without panicking. + _ = commit.LocalAssets.Val.Sum() + _ = commit.RemoteAssets.Val.Sum() +} + +// testCompatHtlcBlob tests that a historical HTLC blob can be decoded. +func testCompatHtlcBlob(t *testing.T, version string) { + t.Helper() + + path := filepath.Join( + "testdata", "compat", version, "htlc-blob.hexdump", + ) + data, err := os.ReadFile(path) + if os.IsNotExist(err) { + t.Skipf("fixture %s not found", path) + } + require.NoError(t, err) + + hexBytes, err := ExtractHexDump(string(data)) + require.NoError(t, err, "failed to extract hex from %s", path) + + htlc, err := rfqmsg.DecodeHtlc(hexBytes) + require.NoError(t, err, "failed to decode %s HTLC blob", version) + + // Verify we can access the balances without panicking. The fixture + // may have zero balances if it represents a no-op HTLC. + _ = htlc.Balances() +} diff --git a/tapchannelmsg/testdata/compat/v0.8/commitment-blob.hexdump b/tapchannelmsg/testdata/compat/v0.8/commitment-blob.hexdump new file mode 100644 index 0000000000..ed393d3f98 --- /dev/null +++ b/tapchannelmsg/testdata/compat/v0.8/commitment-blob.hexdump @@ -0,0 +1,228 @@ + 00000000 00 fd 06 25 01 fd 06 21 00 20 5b bc bd f0 0f 8e |...%...!. [.....| + 00000010 10 65 38 4e fe f9 28 66 46 ca 3b 97 65 45 8d f9 |.e8N..(fF.;.eE..| + 00000020 a2 2b aa 1b 1b d3 bb 75 bf 71 01 08 00 00 00 0d |.+.....u.q......| + 00000030 33 95 a9 4c 02 fd 05 f1 54 41 50 50 00 04 00 00 |3..L....TAPP....| + 00000040 00 00 02 24 9a f2 43 8f e5 6d a2 6a 02 7d 32 7b |...$..C..m.j.}2{| + 00000050 af 7c 46 bf 62 77 95 b4 d3 e3 3e c3 e9 8e 54 b8 |.|F.bw....>...T.| + 00000060 46 cf 62 b4 00 00 00 00 04 50 00 00 00 00 00 00 |F.b......P......| + 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 |................| + 000000b0 6e 88 00 00 00 00 00 00 00 00 06 df 02 00 00 00 |n...............| + 000000c0 01 9a f2 43 8f e5 6d a2 6a 02 7d 32 7b af 7c 46 |...C..m.j.}2{.|F| + 000000d0 bf 62 77 95 b4 d3 e3 3e c3 e9 8e 54 b8 46 cf 62 |.bw....>...T.F.b| + 000000e0 b4 00 00 00 00 00 00 00 00 00 04 4a 01 00 00 00 |...........J....| + 000000f0 00 00 00 22 51 20 3a 99 3f 75 7a df 8e 6f ba 90 |..."Q :.?uz..o..| + 00000100 2e 6a 7e d0 73 19 59 0b da cc f2 64 6f 6d 61 b7 |.j~.s.Y....doma.| + 00000110 fe bb dd 63 ce 83 4a 01 00 00 00 00 00 00 22 51 |...c..J......."Q| + 00000120 20 c8 e6 cd 25 59 05 1e 66 84 26 f0 d9 38 9f 54 | ...%Y..f.&..8.T| + 00000130 ef e1 55 70 56 0f 57 a3 60 b5 a9 7b 72 e9 36 c7 |..UpV.W.`..{r.6.| + 00000140 e0 ac 37 00 00 00 00 00 00 22 51 20 eb 42 61 b7 |..7......"Q .Ba.| + 00000150 24 ac 8a 69 97 98 de 51 10 5e 9e dc 49 2e 51 1c |$..i...Q.^..I.Q.| + 00000160 26 53 4b c7 28 cf 33 df 28 f6 a0 57 5f 4c 01 00 |&SK.(.3.(..W_L..| + 00000170 00 00 00 00 22 51 20 d2 d6 9d 0b 55 2f 37 94 d2 |...."Q ....U/7..| + 00000180 88 37 8d 0f 69 88 6d 6f b8 63 d6 38 97 91 2a 87 |.7..i.mo.c.8..*.| + 00000190 61 35 ef 48 26 c3 03 00 00 00 00 08 01 00 0a fd |a5.H&...........| + 000001a0 01 46 00 01 01 02 50 fe c0 b8 1d 9e 96 c2 27 56 |.F....P.......'V| + 000001b0 af 3d db 25 34 fc 44 2a b3 1d 74 1e 3e f6 98 cf |.=.%4.D*..t.>...| + 000001c0 02 df aa 74 d7 32 56 00 00 00 00 06 55 53 44 54 |...t.2V.....USDT| + 000001d0 4c 54 98 51 a1 7b 59 f7 6b 6a 64 c6 43 92 09 f6 |LT.Q.{Y.kjd.C...| + 000001e0 18 bf ce 05 9b e6 fb f4 ee e4 07 c3 a4 8b fc 91 |................| + 000001f0 f7 a1 00 00 00 00 00 04 01 00 06 09 ff 00 00 00 |................| + 00000200 0d 33 95 a9 4c 0b 90 01 8e 01 65 9a f2 43 8f e5 |.3..L.....e..C..| + 00000210 6d a2 6a 02 7d 32 7b af 7c 46 bf 62 77 95 b4 d3 |m.j.}2{.|F.bw...| + 00000220 e3 3e c3 e9 8e 54 b8 46 cf 62 b4 00 00 00 00 5b |.>...T.F.b.....[| + 00000230 bc bd f0 0f 8e 10 65 38 4e fe f9 28 66 46 ca 3b |......e8N..(fF.;| + 00000240 97 65 45 8d f9 a2 2b aa 1b 1b d3 bb 75 bf 71 02 |.eE...+.....u.q.| + 00000250 50 aa eb 16 6f 42 34 65 0d 84 a2 d8 a1 30 98 7a |P...oB4e.....0.z| + 00000260 ea f6 95 02 06 e0 90 54 01 ee 74 ff 3f 8d 18 e6 |.......T..t.?...| + 00000270 03 25 02 01 51 21 c1 dc a0 94 75 11 09 d0 bd 05 |.%..Q!....u.....| + 00000280 5d 03 56 58 74 e8 27 6d d5 3e 92 6b 44 e3 bd 1b |].VXt.'m.>.kD...| + 00000290 b6 bf 4b c1 30 a2 79 0d 28 f1 d3 fa 25 90 dd 39 |..K.0.y.(...%..9| + 000002a0 80 33 7c d4 32 bc 0b ab 25 ee 23 95 a1 ec bc 6e |.3|.2...%.#....n| + 000002b0 57 be e8 47 c9 e9 d3 41 b2 00 00 00 17 48 76 e8 |W..G...A.....Hv.| + 000002c0 00 0e 02 00 00 10 21 02 fa 23 85 30 b2 ae b0 c6 |......!..#.0....| + 000002d0 83 7e 2d 64 4d eb d1 0f 9c f0 31 f7 e2 87 4a 37 |.~-dM.....1...J7| + 000002e0 5c 13 fa 15 e8 27 60 64 0c fd 01 5a 00 04 00 00 |\....'`d...Z....| + 000002f0 00 02 02 21 02 dc a0 94 75 11 09 d0 bd 05 5d 03 |...!....u.....].| + 00000300 56 58 74 e8 27 6d d5 3e 92 6b 44 e3 bd 1b b6 bf |VXt.'m.>.kD.....| + 00000310 4b c1 30 a2 79 03 fd 01 2d 01 49 00 01 01 02 20 |K.0.y...-.I.... | + 00000320 5b bc bd f0 0f 8e 10 65 38 4e fe f9 28 66 46 ca |[......e8N..(fF.| + 00000330 3b 97 65 45 8d f9 a2 2b aa 1b 1b d3 bb 75 bf 71 |;.eE...+.....u.q| + 00000340 04 22 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |."..............| + 00000350 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000360 ff ff ff ff 02 27 00 01 02 02 22 00 00 ff ff ff |.....'....".....| + 00000370 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000380 ff ff ff ff ff ff ff ff ff ff ff ff ff 05 41 01 |..............A.| + 00000390 7c 82 be fe 07 d1 f4 c1 08 ac 93 04 bb c3 73 36 ||.............s6| + 000003a0 c5 e1 cf 1e 4d d9 46 db 3c 59 c6 93 ba 84 8c 1d |....M.F..kD...| + 00000470 b6 bf 4b c1 30 a2 79 03 fd 01 3c 01 71 00 01 01 |..K.0.y...<.q...| + 00000480 02 20 5b bc bd f0 0f 8e 10 65 38 4e fe f9 28 66 |. [......e8N..(f| + 00000490 46 ca 3b 97 65 45 8d f9 a2 2b aa 1b 1b d3 bb 75 |F.;.eE...+.....u| + 000004a0 bf 71 04 4a 00 01 04 04 89 78 6d 57 7b c5 2a 7d |.q.J.....xmW{.*}| + 000004b0 b6 32 d8 ca 0f 86 b2 5b a6 00 ce ce 0a 10 6b 60 |.2.....[......k`| + 000004c0 47 3c 35 b7 ea 08 00 00 00 0a 14 e1 3e b4 ff ff |G<5.........>...| + 000004d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 000004e0 ff ff ff ff ff ff ff ff ff ff ff ff ff 7f 02 27 |...............'| + 000004f0 00 01 02 02 22 00 00 ff ff ff ff ff ff ff ff ff |...."...........| + 00000500 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000510 ff ff ff ff ff ff ff 05 28 00 c0 25 20 93 af 6e |........(..% ..n| + 00000520 53 19 00 ed 21 76 57 b7 86 66 d3 70 d4 e1 6c 23 |S...!vW..f.p..l#| + 00000530 d4 25 71 9a f6 f8 82 2d e1 9e 38 2c 6a ac 51 b2 |.%q....-..8,j.Q.| + 00000540 75 07 74 01 02 20 93 aa 53 e2 42 66 eb cd 05 09 |u.t.. ..S.Bf....| + 00000550 ad 1b 54 0b f1 c8 ec 68 15 a4 89 99 e0 2f 03 81 |..T....h...../..| + 00000560 cf d6 f0 6b 75 51 02 4f 00 01 02 02 4a 00 01 d5 |...kuQ.O....J...| + 00000570 05 88 b0 24 04 b9 80 52 2f e1 09 56 2a 0d 3c 81 |...$...R/..V*.<.| + 00000580 ec ab 3a 8e 3b ee 64 72 13 9f 55 87 22 44 50 00 |..:.;.dr..U."DP.| + 00000590 00 00 0a 14 e1 3e b4 ff ff ff ff ff ff ff ff ff |.....>..........| + 000005a0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 000005b0 ff ff ff ff ff ff 7f 35 00 04 00 00 00 00 02 21 |.......5.......!| + 000005c0 03 f3 7c 4f 1d 80 4f e3 16 a4 7b 85 41 d2 a4 ae |..|O..O...{.A...| + 000005d0 3d dc ba ec f8 3a 2b c9 d4 a7 ec f2 02 e4 c4 11 |=....:+.........| + 000005e0 e7 05 0a 01 05 00 c0 02 60 b2 04 01 00 35 00 04 |........`....5..| + 000005f0 00 00 00 01 02 21 02 93 af 6e 53 19 00 ed 21 76 |.....!...nS...!v| + 00000600 57 b7 86 66 d3 70 d4 e1 6c 23 d4 25 71 9a f6 f8 |W..f.p..l#.%q...| + 00000610 82 2d e1 9e 38 2c 6a 05 0a 01 05 00 c0 02 60 b2 |.-..8,j.......`.| + 00000620 04 01 00 16 04 00 00 00 00 01 fd 07 62 01 fd 07 |............b...| + 00000630 5e 00 20 5b bc bd f0 0f 8e 10 65 38 4e fe f9 28 |^. [......e8N..(| + 00000640 66 46 ca 3b 97 65 45 8d f9 a2 2b aa 1b 1b d3 bb |fF.;.eE...+.....| + 00000650 75 bf 71 01 08 00 00 00 0a 14 e1 3e b4 02 fd 07 |u.q........>....| + 00000660 2e 54 41 50 50 00 04 00 00 00 00 02 24 9a f2 43 |.TAPP.......$..C| + 00000670 8f e5 6d a2 6a 02 7d 32 7b af 7c 46 bf 62 77 95 |..m.j.}2{.|F.bw.| + 00000680 b4 d3 e3 3e c3 e9 8e 54 b8 46 cf 62 b4 00 00 00 |...>...T.F.b....| + 00000690 00 04 50 00 00 00 00 00 00 00 00 00 00 00 00 00 |..P.............| + 000006a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 000006b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 000006c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 000006d0 00 00 00 00 00 00 00 00 09 6e 88 00 00 00 00 00 |.........n......| + 000006e0 00 00 00 06 df 02 00 00 00 01 9a f2 43 8f e5 6d |............C..m| + 000006f0 a2 6a 02 7d 32 7b af 7c 46 bf 62 77 95 b4 d3 e3 |.j.}2{.|F.bw....| + 00000700 3e c3 e9 8e 54 b8 46 cf 62 b4 00 00 00 00 00 00 |>...T.F.b.......| + 00000710 00 00 00 04 4a 01 00 00 00 00 00 00 22 51 20 3a |....J......."Q :| + 00000720 99 3f 75 7a df 8e 6f ba 90 2e 6a 7e d0 73 19 59 |.?uz..o...j~.s.Y| + 00000730 0b da cc f2 64 6f 6d 61 b7 fe bb dd 63 ce 83 4a |....doma....c..J| + 00000740 01 00 00 00 00 00 00 22 51 20 c8 e6 cd 25 59 05 |......."Q ...%Y.| + 00000750 1e 66 84 26 f0 d9 38 9f 54 ef e1 55 70 56 0f 57 |.f.&..8.T..UpV.W| + 00000760 a3 60 b5 a9 7b 72 e9 36 c7 e0 ac 37 00 00 00 00 |.`..{r.6...7....| + 00000770 00 00 22 51 20 eb 42 61 b7 24 ac 8a 69 97 98 de |.."Q .Ba.$..i...| + 00000780 51 10 5e 9e dc 49 2e 51 1c 26 53 4b c7 28 cf 33 |Q.^..I.Q.&SK.(.3| + 00000790 df 28 f6 a0 57 5f 4c 01 00 00 00 00 00 22 51 20 |.(..W_L......"Q | + 000007a0 d2 d6 9d 0b 55 2f 37 94 d2 88 37 8d 0f 69 88 6d |....U/7...7..i.m| + 000007b0 6f b8 63 d6 38 97 91 2a 87 61 35 ef 48 26 c3 03 |o.c.8..*.a5.H&..| + 000007c0 00 00 00 00 08 01 00 0a fd 02 91 00 01 01 02 50 |...............P| + 000007d0 fe c0 b8 1d 9e 96 c2 27 56 af 3d db 25 34 fc 44 |.......'V.=.%4.D| + 000007e0 2a b3 1d 74 1e 3e f6 98 cf 02 df aa 74 d7 32 56 |*..t.>......t.2V| + 000007f0 00 00 00 00 06 55 53 44 54 4c 54 98 51 a1 7b 59 |.....USDTLT.Q.{Y| + 00000800 f7 6b 6a 64 c6 43 92 09 f6 18 bf ce 05 9b e6 fb |.kjd.C..........| + 00000810 f4 ee e4 07 c3 a4 8b fc 91 f7 a1 00 00 00 00 00 |................| + 00000820 04 01 00 06 09 ff 00 00 00 0a 14 e1 3e b4 0b fd |............>...| + 00000830 02 03 01 fd 01 ff 01 65 00 00 00 00 00 00 00 00 |.......e........| + 00000840 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000850 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000870 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000880 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000890 00 00 00 00 00 00 00 00 00 00 00 00 00 05 fd 01 |................| + 000008a0 94 4a 00 01 84 41 dd 42 55 79 73 07 4f ee 88 d5 |.J...A.BUys.O...| + 000008b0 3e 6c 4d ba b9 6f 2a 01 8a b4 6b 83 39 d9 fa c3 |>lM..o*...k.9...| + 000008c0 9a 31 31 50 00 00 00 0d 33 95 a9 4c ff ff ff ff |.11P....3..L....| + 000008d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 000008e0 ff ff ff ff ff ff ff ff ff ff ff 7f fd 01 46 00 |..............F.| + 000008f0 01 01 02 50 fe c0 b8 1d 9e 96 c2 27 56 af 3d db |...P.......'V.=.| + 00000900 25 34 fc 44 2a b3 1d 74 1e 3e f6 98 cf 02 df aa |%4.D*..t.>......| + 00000910 74 d7 32 56 00 00 00 00 06 55 53 44 54 4c 54 98 |t.2V.....USDTLT.| + 00000920 51 a1 7b 59 f7 6b 6a 64 c6 43 92 09 f6 18 bf ce |Q.{Y.kjd.C......| + 00000930 05 9b e6 fb f4 ee e4 07 c3 a4 8b fc 91 f7 a1 00 |................| + 00000940 00 00 00 00 04 01 00 06 09 ff 00 00 00 0d 33 95 |..............3.| + 00000950 a9 4c 0b 90 01 8e 01 65 9a f2 43 8f e5 6d a2 6a |.L.....e..C..m.j| + 00000960 02 7d 32 7b af 7c 46 bf 62 77 95 b4 d3 e3 3e c3 |.}2{.|F.bw....>.| + 00000970 e9 8e 54 b8 46 cf 62 b4 00 00 00 00 5b bc bd f0 |..T.F.b.....[...| + 00000980 0f 8e 10 65 38 4e fe f9 28 66 46 ca 3b 97 65 45 |...e8N..(fF.;.eE| + 00000990 8d f9 a2 2b aa 1b 1b d3 bb 75 bf 71 02 50 aa eb |...+.....u.q.P..| + 000009a0 16 6f 42 34 65 0d 84 a2 d8 a1 30 98 7a ea f6 95 |.oB4e.....0.z...| + 000009b0 02 06 e0 90 54 01 ee 74 ff 3f 8d 18 e6 03 25 02 |....T..t.?....%.| + 000009c0 01 51 21 c1 dc a0 94 75 11 09 d0 bd 05 5d 03 56 |.Q!....u.....].V| + 000009d0 58 74 e8 27 6d d5 3e 92 6b 44 e3 bd 1b b6 bf 4b |Xt.'m.>.kD.....K| + 000009e0 c1 30 a2 79 0d 28 f1 d3 fa 25 90 dd 39 80 33 7c |.0.y.(...%..9.3|| + 000009f0 d4 32 bc 0b ab 25 ee 23 95 a1 ec bc 6e 57 be e8 |.2...%.#....nW..| + 00000a00 47 c9 e9 d3 41 b2 00 00 00 17 48 76 e8 00 0e 02 |G...A.....Hv....| + 00000a10 00 00 10 21 02 fa 23 85 30 b2 ae b0 c6 83 7e 2d |...!..#.0.....~-| + 00000a20 64 4d eb d1 0f 9c f0 31 f7 e2 87 4a 37 5c 13 fa |dM.....1...J7\..| + 00000a30 15 e8 27 60 64 0e 02 00 00 10 21 02 56 4f 68 4c |..'`d.....!.VOhL| + 00000a40 f8 73 85 f4 55 1d c9 32 73 1b 03 83 fe 12 9a 3b |.s..U..2s......;| + 00000a50 a2 06 3f aa 17 ed bf 85 71 3a e8 05 0c c9 00 04 |..?.....q:......| + 00000a60 00 00 00 03 02 21 02 dc a0 94 75 11 09 d0 bd 05 |.....!....u.....| + 00000a70 5d 03 56 58 74 e8 27 6d d5 3e 92 6b 44 e3 bd 1b |].VXt.'m.>.kD...| + 00000a80 b6 bf 4b c1 30 a2 79 03 9e 01 49 00 01 01 02 20 |..K.0.y...I.... | + 00000a90 5b bc bd f0 0f 8e 10 65 38 4e fe f9 28 66 46 ca |[......e8N..(fF.| + 00000aa0 3b 97 65 45 8d f9 a2 2b aa 1b 1b d3 bb 75 bf 71 |;.eE...+.....u.q| + 00000ab0 04 22 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |."..............| + 00000ac0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000ad0 ff ff ff ff 02 27 00 01 02 02 22 00 00 ff ff ff |.....'....".....| + 00000ae0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000af0 ff ff ff ff ff ff ff ff ff ff ff ff ff 05 28 00 |..............(.| + 00000b00 c0 25 20 93 af 6e 53 19 00 ed 21 76 57 b7 86 66 |.% ..nS...!vW..f| + 00000b10 d3 70 d4 e1 6c 23 d4 25 71 9a f6 f8 82 2d e1 9e |.p..l#.%q....-..| + 00000b20 38 2c 6a ac 51 b2 75 0d fd 01 7a 03 fd 01 0a 00 |8,j.Q.u...z.....| + 00000b30 04 00 00 00 02 02 21 02 dc a0 94 75 11 09 d0 bd |......!....u....| + 00000b40 05 5d 03 56 58 74 e8 27 6d d5 3e 92 6b 44 e3 bd |.].VXt.'m.>.kD..| + 00000b50 1b b6 bf 4b c1 30 a2 79 03 df 01 71 00 01 01 02 |...K.0.y...q....| + 00000b60 20 5b bc bd f0 0f 8e 10 65 38 4e fe f9 28 66 46 | [......e8N..(fF| + 00000b70 ca 3b 97 65 45 8d f9 a2 2b aa 1b 1b d3 bb 75 bf |.;.eE...+.....u.| + 00000b80 71 04 4a 00 01 26 cb e8 54 b3 3c de 72 fc 6f 34 |q.J..&..T.<.r.o4| + 00000b90 80 03 d4 80 23 62 7d a9 a4 84 79 2e 1a bf 3c e0 |....#b}...y...<.| + 00000ba0 9c 39 54 01 05 00 00 00 0d 33 95 a9 4c ff ff ff |.9T......3..L...| + 00000bb0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000bc0 ff ff ff ff ff ff ff ff ff ff ff ff 7f 02 27 00 |..............'.| + 00000bd0 01 02 02 22 00 00 ff ff ff ff ff ff ff ff ff ff |..."............| + 00000be0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000bf0 ff ff ff ff ff ff 05 41 01 7c 82 be fe 07 d1 f4 |.......A.|......| + 00000c00 c1 08 ac 93 04 bb c3 73 36 c5 e1 cf 1e 4d d9 46 |.......s6....M.F| + 00000c10 db 3c 59 c6 93 ba 84 8c 1d c3 a2 ed 89 67 e0 51 |..kD.....K.0.y| + 00000cd0 03 b7 01 49 00 01 01 02 20 5b bc bd f0 0f 8e 10 |...I.... [......| + 00000ce0 65 38 4e fe f9 28 66 46 ca 3b 97 65 45 8d f9 a2 |e8N..(fF.;.eE...| + 00000cf0 2b aa 1b 1b d3 bb 75 bf 71 04 22 00 00 ff ff ff |+.....u.q.".....| + 00000d00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000d10 ff ff ff ff ff ff ff ff ff ff ff ff ff 02 27 00 |..............'.| + 00000d20 01 02 02 22 00 00 ff ff ff ff ff ff ff ff ff ff |..."............| + 00000d30 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000d40 ff ff ff ff ff ff 05 41 01 7c 82 be fe 07 d1 f4 |.......A.|......| + 00000d50 c1 08 ac 93 04 bb c3 73 36 c5 e1 cf 1e 4d d9 46 |.......s6....M.F| + 00000d60 db 3c 59 c6 93 ba 84 8c 1d c3 a2 ed 89 67 e0 51 |.T...| + 00000db0 05 6c 52 16 4c 6b 8b bf 59 7b 6d db 28 02 1d 4d |.lR.Lk..Y{m.(..M| + 00000dc0 df 91 5e 60 36 3e 27 36 bd b1 ee 38 e2 6c 13 d3 |..^`6>'6...8.l..| + 00000dd0 54 60 66 36 09 aa b8 3b a1 7a cb 30 c4 c3 00 00 |T`f6...;.z.0....| + 00000de0 00 0d 33 95 a9 4c 01 4c c0 49 49 6a 47 e8 54 3f |..3..L.L.IIjG.T?| + 00000df0 2a b1 63 fa f6 93 97 1a 65 3e 54 ef e3 d8 05 6c |*.c.....e>T....l| + 00000e00 52 16 4c 6b 8b bf 59 7b 6d db 28 02 5c 7a ea 28 |R.Lk..Y{m.(.\z.(| + 00000e10 5e f7 aa 4a d4 31 55 d1 0c 19 dc 5f b6 fe 46 c7 |^..J.1U...._..F.| + 00000e20 9f 05 b0 fc a0 d1 99 4f 95 6d fc 70 00 00 00 0a |.......O.m.p....| + 00000e30 14 e1 3e b4 02 01 00 03 01 00 |..>.......| \ No newline at end of file diff --git a/tapchannelmsg/testdata/compat/v0.8/funding-blob.hexdump b/tapchannelmsg/testdata/compat/v0.8/funding-blob.hexdump new file mode 100644 index 0000000000..3572c2681c --- /dev/null +++ b/tapchannelmsg/testdata/compat/v0.8/funding-blob.hexdump @@ -0,0 +1,106 @@ + 00000000 00 fd 06 8c 01 fd 06 88 00 20 5b bc bd f0 0f 8e |......... [.....| + 00000010 10 65 38 4e fe f9 28 66 46 ca 3b 97 65 45 8d f9 |.e8N..(fF.;.eE..| + 00000020 a2 2b aa 1b 1b d3 bb 75 bf 71 01 08 00 00 00 17 |.+.....u.q......| + 00000030 48 76 e8 00 02 fd 06 58 54 41 50 50 00 04 00 00 |Hv.....XTAPP....| + 00000040 00 00 02 24 e3 b3 18 bb 44 96 eb 00 47 b9 da af |...$....D...G...| + 00000050 9e d0 cd 1f e3 4e fd 40 40 d1 ac a0 bd 48 4a b7 |.....N.@@....HJ.| + 00000060 9f 95 d8 da 00 00 00 00 04 50 00 00 00 00 00 00 |.........P......| + 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 |................| + 000000b0 6e 88 00 00 00 00 00 00 00 00 06 dd 02 00 00 00 |n...............| + 000000c0 02 e3 b3 18 bb 44 96 eb 00 47 b9 da af 9e d0 cd |.....D...G......| + 000000d0 1f e3 4e fd 40 40 d1 ac a0 bd 48 4a b7 9f 95 d8 |..N.@@....HJ....| + 000000e0 da 00 00 00 00 00 00 00 00 00 e3 b3 18 bb 44 96 |..............D.| + 000000f0 eb 00 47 b9 da af 9e d0 cd 1f e3 4e fd 40 40 d1 |..G........N.@@.| + 00000100 ac a0 bd 48 4a b7 9f 95 d8 da 02 00 00 00 00 00 |...HJ...........| + 00000110 00 00 00 03 a0 86 01 00 00 00 00 00 22 51 20 36 |............"Q 6| + 00000120 e0 66 44 e2 1f 95 43 4b 22 09 3b 99 d1 3d 7f b3 |.fD...CK".;..=..| + 00000130 74 16 a8 20 40 bb ac 6e 2b 3c da 92 df 2f 44 e8 |t.. @..n+<.../D.| + 00000140 03 00 00 00 00 00 00 22 51 20 78 c9 df a4 61 c9 |......."Q x...a.| + 00000150 63 5a 54 30 3c 45 20 bb c6 f7 78 c7 f7 54 ab ce |cZT0.....| + 000001c0 aa 74 d7 32 56 00 00 00 00 06 55 53 44 54 4c 54 |.t.2V.....USDTLT| + 000001d0 98 51 a1 7b 59 f7 6b 6a 64 c6 43 92 09 f6 18 bf |.Q.{Y.kjd.C.....| + 000001e0 ce 05 9b e6 fb f4 ee e4 07 c3 a4 8b fc 91 f7 a1 |................| + 000001f0 00 00 00 00 00 04 01 00 06 09 ff 00 00 00 17 48 |...............H| + 00000200 76 e8 00 0b fd 02 20 01 fd 02 1c 01 65 00 00 00 |v..... .....e...| + 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000270 00 00 05 fd 01 b1 4a 00 01 9d 62 8a 09 da 78 fd |......J...b...x.| + 00000280 55 2f 80 de 81 92 f9 3f b3 b5 d0 1d e8 b1 b8 7b |U/.....?.......{| + 00000290 5e 1b 94 e4 12 48 1a 50 25 00 00 00 8b b2 c9 70 |^....H.P%......p| + 000002a0 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 000002b0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 000002c0 bf fd 01 63 00 01 00 02 50 fe c0 b8 1d 9e 96 c2 |...c....P.......| + 000002d0 27 56 af 3d db 25 34 fc 44 2a b3 1d 74 1e 3e f6 |'V.=.%4.D*..t.>.| + 000002e0 98 cf 02 df aa 74 d7 32 56 00 00 00 00 06 55 53 |.....t.2V.....US| + 000002f0 44 54 4c 54 98 51 a1 7b 59 f7 6b 6a 64 c6 43 92 |DTLT.Q.{Y.kjd.C.| + 00000300 09 f6 18 bf ce 05 9b e6 fb f4 ee e4 07 c3 a4 8b |................| + 00000310 fc 91 f7 a1 00 00 00 00 00 04 01 00 06 09 ff 00 |................| + 00000320 00 00 8b b2 c9 70 00 0b ad 01 ab 01 65 e3 b3 18 |.....p......e...| + 00000330 bb 44 96 eb 00 47 b9 da af 9e d0 cd 1f e3 4e fd |.D...G........N.| + 00000340 40 40 d1 ac a0 bd 48 4a b7 9f 95 d8 da 00 00 00 |@@....HJ........| + 00000350 00 5b bc bd f0 0f 8e 10 65 38 4e fe f9 28 66 46 |.[......e8N..(fF| + 00000360 ca 3b 97 65 45 8d f9 a2 2b aa 1b 1b d3 bb 75 bf |.;.eE...+.....u.| + 00000370 71 02 c4 c1 0f 4b 66 6d 6c 65 04 94 86 e7 63 d1 |q....Kfmle....c.| + 00000380 fd 9c 58 50 c3 42 6e 98 95 a7 c2 76 5f d2 c9 a0 |..XP.Bn....v_...| + 00000390 e9 31 03 42 01 40 fb d6 b7 66 c5 44 90 21 f6 b4 |.1.B.@...f.D.!..| + 000003a0 01 4b 58 a9 92 af d3 85 47 52 18 4e 8d 8c d2 bf |.KX.....GR.N....| + 000003b0 ea fe 1d c1 1a ba bc c0 f1 2b 9a c9 aa d5 12 43 |.........+.....C| + 000003c0 49 ad e8 9e 04 2f ac 32 ae 0e c8 04 af 20 d3 b9 |I..../.2..... ..| + 000003d0 b2 cb bc 52 cf 20 0d 28 86 28 09 8b 45 4a 14 d6 |...R. .(.(..EJ..| + 000003e0 fb fd 14 d9 be fe 3b 69 99 eb be 1e 75 f5 a4 6b |......;i....u..k| + 000003f0 f8 61 64 dc 51 78 b9 80 00 00 00 a2 fb 40 58 00 |.ad.Qx.......@X.| + 00000400 0e 02 00 00 10 21 02 a8 21 5e 24 a9 c7 25 96 33 |.....!..!^$..%.3| + 00000410 1b f5 95 a0 35 f2 f5 18 86 18 b4 fb 62 72 49 37 |....5.......brI7| + 00000420 ed d4 77 f9 3a ea 47 0e 02 00 00 10 21 02 50 aa |..w.:.G.....!.P.| + 00000430 eb 16 6f 42 34 65 0d 84 a2 d8 a1 30 98 7a ea f6 |..oB4e.....0.z..| + 00000440 95 02 06 e0 90 54 01 ee 74 ff 3f 8d 18 e6 0c 9f |.....T..t.?.....| + 00000450 00 04 00 00 00 00 02 21 02 25 73 85 a5 39 6c 24 |.......!.%s..9l$| + 00000460 e0 6e 13 2c 58 cd 68 06 7b de bc 61 4d af d1 c7 |.n.,X.h.{..aM...| + 00000470 24 bc 7c 1d 80 8f 56 d3 1d 03 74 01 49 00 01 01 |$.|...V...t.I...| + 00000480 02 20 5b bc bd f0 0f 8e 10 65 38 4e fe f9 28 66 |. [......e8N..(f| + 00000490 46 ca 3b 97 65 45 8d f9 a2 2b aa 1b 1b d3 bb 75 |F.;.eE...+.....u| + 000004a0 bf 71 04 22 00 00 ff ff ff ff ff ff ff ff ff ff |.q."............| + 000004b0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 000004c0 ff ff ff ff ff ff 02 27 00 01 02 02 22 00 00 ff |.......'...."...| + 000004d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 000004e0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 0d |................| + 000004f0 f8 02 c7 00 04 00 00 00 01 02 21 02 24 12 a4 0a |..........!.$...| + 00000500 95 f1 15 6b cc 4a 6f 4c 8e cf 9b 93 f2 6f 83 57 |...k.JoL.....o.W| + 00000510 37 47 7b 1c da 80 61 ac 72 43 d6 ae 03 9c 01 71 |7G{...a.rC.....q| + 00000520 00 01 00 02 20 5b bc bd f0 0f 8e 10 65 38 4e fe |.... [......e8N.| + 00000530 f9 28 66 46 ca 3b 97 65 45 8d f9 a2 2b aa 1b 1b |.(fF.;.eE...+...| + 00000540 d3 bb 75 bf 71 04 4a 00 01 19 fe fa af 0e 61 b3 |..u.q.J.......a.| + 00000550 9b 16 91 93 29 06 39 79 e0 c7 8c 88 56 48 51 6d |....).9y....VHQm| + 00000560 d5 9e 02 97 94 87 89 b3 8e 00 00 00 8b b2 c9 70 |...............p| + 00000570 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000580 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000590 7f 02 27 00 01 02 02 22 00 00 ff ff ff ff ff ff |..'...."........| + 000005a0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 000005b0 ff ff ff ff ff ff ff ff ff ff 2e 00 04 00 00 00 |................| + 000005c0 02 02 21 02 01 d1 0b 6b 42 84 5c a8 63 b7 f9 fb |..!....kB.\.c...| + 000005d0 aa f0 b6 3d 7b ce f5 5d 6f d7 da 78 36 47 29 8e |...={..]o..x6G).| + 000005e0 ef a5 08 19 05 03 04 01 01 0f 9f 00 04 00 00 00 |................| + 000005f0 01 02 21 02 24 12 a4 0a 95 f1 15 6b cc 4a 6f 4c |..!.$......k.JoL| + 00000600 8e cf 9b 93 f2 6f 83 57 37 47 7b 1c da 80 61 ac |.....o.W7G{...a.| + 00000610 72 43 d6 ae 03 74 01 49 00 01 00 02 20 5b bc bd |rC...t.I.... [..| + 00000620 f0 0f 8e 10 65 38 4e fe f9 28 66 46 ca 3b 97 65 |....e8N..(fF.;.e| + 00000630 45 8d f9 a2 2b aa 1b 1b d3 bb 75 bf 71 04 22 00 |E...+.....u.q.".| + 00000640 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000650 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000660 ff 02 27 00 01 02 02 22 00 00 ff ff ff ff ff ff |..'...."........| + 00000670 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000680 ff ff ff ff ff ff ff ff ff ff 16 04 00 00 00 00 |................| + 00000690 01 01 06 |...| \ No newline at end of file diff --git a/tapchannelmsg/testdata/compat/v0.8/htlc-blob.hexdump b/tapchannelmsg/testdata/compat/v0.8/htlc-blob.hexdump new file mode 100644 index 0000000000..ee3c6e0168 --- /dev/null +++ b/tapchannelmsg/testdata/compat/v0.8/htlc-blob.hexdump @@ -0,0 +1,3 @@ + 00000000 fe 00 01 00 02 20 cb e4 1e 5c 1b be 71 1d 9e df |..... ...\..q...| + 00000010 32 45 c6 d8 48 4c c5 a3 39 fa 30 82 a4 00 f5 50 |2E..HL..9.0....P| + 00000020 eb e8 46 37 3a 3d fe 00 01 a1 47 01 00 |..F7:=....G..| \ No newline at end of file From e86aa1d9d29fda5ea32cba8d059cd0abad9fdb57 Mon Sep 17 00:00:00 2001 From: Dario Anongba Varela Date: Tue, 17 Mar 2026 19:16:34 +0100 Subject: [PATCH 5/6] itest: add backward compatibility integration test wrapper Add TestCustomChannelsCompat which runs a subset of custom channel tests (core, force close, v1 upgrade) with one node running an older binary for each configured historical version. The test builds old binaries from source via build-compat-binary.sh and caches them locally. The compatVersions list is initially empty with a TODO to populate once v0.8.0 is released. --- itest/custom_channels/compat_test.go | 184 +++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 itest/custom_channels/compat_test.go diff --git a/itest/custom_channels/compat_test.go b/itest/custom_channels/compat_test.go new file mode 100644 index 0000000000..8aefdcef6e --- /dev/null +++ b/itest/custom_channels/compat_test.go @@ -0,0 +1,184 @@ +//go:build itest + +package custom_channels + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/lightninglabs/taproot-assets/itest" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/miner" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/stretchr/testify/require" +) + +// compatVersions lists the historical release versions to test backward +// compatibility against. Update this list before each release to include +// the latest 2 minor versions. +var compatVersions = []string{ + // TODO: Uncomment once v0.8.0 is released and the integrated binary + // can be built from that tag. + // "v0.8.0", +} + +// compatTestCases is the subset of test cases that exercise critical +// backward compatibility surfaces: channel open/close, routing, force +// close, and upgrade. +var compatTestCases = []*ccTestCase{ + { + name: "core", + test: testCustomChannels, + }, + { + name: "force close", + test: testCustomChannelsForceClose, + }, + { + name: "v1 upgrade", + test: testCustomChannelsV1Upgrade, + }, +} + +// buildCompatBinary builds or retrieves a cached tapd-integrated binary for +// the given version tag. It returns the path to the binary. +func buildCompatBinary(t *testing.T, version string) string { + t.Helper() + + // Locate the build script relative to the repo root. + repoRoot, err := exec.Command( + "git", "rev-parse", "--show-toplevel", + ).Output() + require.NoError(t, err, "unable to find repo root") + + script := filepath.Join( + string(repoRoot[:len(repoRoot)-1]), + "scripts", "build-compat-binary.sh", + ) + + // Run the build script. It prints the binary path on stdout. + //nolint:gosec + cmd := exec.Command(script, version) + cmd.Stderr = os.Stderr + out, err := cmd.Output() + require.NoError(t, err, "unable to build compat binary for %s", + version) + + binaryPath := string(out[:len(out)-1]) // trim trailing newline + require.FileExists(t, binaryPath) + + return binaryPath +} + +// TestCustomChannelsCompat runs a subset of custom channel tests with one +// node running an older binary version for each historical version in +// compatVersions. This test is gated behind the `compat` build tag and +// intended to run only on release branches or via manual dispatch. +// +// The test creates a fresh network harness for each version, builds or +// retrieves the old binary, and runs each compat test case. In each test, +// one node (typically the "old" peer) is started with the historical binary +// via NewNodeWithBinary, while the other nodes use the current build. +func TestCustomChannelsCompat(t *testing.T) { + if len(compatVersions) == 0 { + t.Skip("no compat versions configured") + } + + for _, version := range compatVersions { + version := version + t.Run(version, func(t *testing.T) { + runCompatSuite(t, version) + }) + } +} + +// runCompatSuite runs all compat test cases for a single historical version. +func runCompatSuite(t *testing.T, version string) { + t.Helper() + + // Build or retrieve the old binary. + oldBinary := buildCompatBinary(t, version) + t.Logf("Using compat binary for %s: %s", version, oldBinary) + + lntest.MaxBlocksMinedPerTest = 250 + + logDir := node.GetLogDir() + netName := miner.HarnessNetParams.Name + for _, dir := range []string{".minerlogs", ".backendlogs"} { + path := fmt.Sprintf("%s/%s/%s", logDir, dir, netName) + require.NoError(t, os.MkdirAll(path, 0750)) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + m := miner.NewMiner(ctx, t) + require.NoError(t, m.SetUp(true, 50)) + require.NoError(t, m.Client.NotifyNewTransactions(false)) + t.Cleanup(func() { m.Stop() }) + + numBlocks := miner.HarnessNetParams.MinerConfirmationWindow * 2 + m.GenerateBlocks(numBlocks) + + chainBackend, cleanup, err := lntest.NewBackend( + m.P2PAddress(), miner.HarnessNetParams, + ) + require.NoError(t, err) + defer func() { + require.NoError(t, cleanup()) + }() + require.NoError(t, chainBackend.ConnectMiner()) + + feeService := lntest.NewFeeService(t) + feeService.SetFeeRate(chainfee.FeePerKwFloor, 1) + require.NoError(t, feeService.Start()) + t.Cleanup(func() { + require.NoError(t, feeService.Stop()) + }) + + net := itest.NewIntegratedNetworkHarness( + t, "../tapd-integrated-itest", chainBackend, + miner.HarnessNetParams, + ) + net.Miner = m + net.FeeServiceURL = feeService.URL() + net.FeeService = feeService + defer net.TearDown() + + // Store the old binary path in the harness so test cases can + // retrieve it. We use an environment variable as a simple + // side channel. + t.Setenv("COMPAT_OLD_BINARY", oldBinary) + + for _, tc := range compatTestCases { + tc := tc + success := t.Run(tc.name, func(t1 *testing.T) { + ht := &ccHarnessTest{ + t: t1, + testCase: tc, + lndHarness: net, + } + ctxt, cancel := context.WithTimeout( + ctx, 10*time.Minute, + ) + defer cancel() + + tc.test(ctxt, net, ht) + }) + + net.TearDown() + + if !success { + t.Logf("Failure time: %v", time.Now().Format( + "2006-01-02 15:04:05.000", + )) + return + } + } +} From d1421118490f5599268ae4b23cd45c819081852c Mon Sep 17 00:00:00 2001 From: Dario Anongba Varela Date: Tue, 17 Mar 2026 19:16:55 +0100 Subject: [PATCH 6/6] make+ci: add backward compatibility Makefile targets and CI workflow Add Makefile targets: - itest-cc-compat: runs backward compat integration tests - build-compat-binary: builds an old tapd-integrated from a git tag - gen-compat-fixtures: regenerates wire format fixture files Add a GitHub Actions workflow that triggers on release/* pushes and manual dispatch to run the compat test suite with cached old binaries. --- .github/workflows/compat.yaml | 51 +++++++++++++++++++++++++++++++++++ Makefile | 14 ++++++++++ 2 files changed, 65 insertions(+) create mode 100644 .github/workflows/compat.yaml diff --git a/.github/workflows/compat.yaml b/.github/workflows/compat.yaml new file mode 100644 index 0000000000..2d1b73ff16 --- /dev/null +++ b/.github/workflows/compat.yaml @@ -0,0 +1,51 @@ +name: Backward Compatibility Tests + +on: + workflow_dispatch: + inputs: + version: + description: 'Historical version to test against (e.g. v0.8.0). Leave empty to test all configured versions.' + required: false + type: string + push: + branches: + - 'release/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +env: + GOPATH: /home/runner/go + +jobs: + compat-tests: + name: Compat Tests + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history needed for git worktree builds. + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Cache compat binaries + uses: actions/cache@v4 + with: + path: ~/.tapd-compat-bins + key: compat-bins-${{ runner.os }}-${{ runner.arch }} + + - name: Build current integrated binary + run: make build-itest + + - name: Run backward compatibility tests + run: make itest-cc-compat diff --git a/Makefile b/Makefile index be1d0d5fe3..bbb7ad3888 100644 --- a/Makefile +++ b/Makefile @@ -273,6 +273,15 @@ itest-cc: build-itest clean-cc-itest-logs date $(GOTEST) ./itest/custom_channels -v -tags="$(ITEST_TAGS)" $(CC_TEST_FLAGS) -test.timeout=30m -logdir=regtest/.logs +itest-cc-compat: build-itest clean-cc-itest-logs + @$(call print, "Running backward compatibility integration tests.") + date + $(GOTEST) ./itest/custom_channels -v -tags="$(ITEST_TAGS)" -test.run=TestCustomChannelsCompat -test.timeout=60m -logdir=regtest/.logs + +build-compat-binary: + @$(call print, "Building compat binary for $(version).") + @scripts/build-compat-binary.sh $(version) + itest-cc-parallel: build-itest build-itest-cc-binary clean-cc-itest-logs @$(call print, "Running custom channel integration tests in parallel.") date @@ -445,6 +454,11 @@ gen-itest-test-vectors: gen-test-vectors: gen-deterministic-test-vectors gen-itest-test-vectors +gen-compat-fixtures: + @$(call print, "Generating backward compatibility fixtures.") + make unit gen-test-vectors=true pkg=tapchannelmsg case=^TestGenerateCompatFixtures$ + make unit gen-test-vectors=true pkg=rfqmsg case=^TestGenerateCompatFixtures$ + test-vector-check: gen-deterministic-test-vectors @$(call print, "Checking deterministic test vectors.") if test -n "$$(git status | grep -e ".json")"; then echo "Test vectors not updated"; git status; git diff; exit 1; fi