Skip to content

Commit 54a5380

Browse files
committed
Squashed commit of the following:
commit c42375df3aa60eb0bae882e7e979c87d3bc1abfc Author: xdustinface <xdustinfacex@gmail.com> Date: Fri Feb 13 20:57:07 2026 +0100 fix: reset `FilterManager` when all peers disconnect commit 0a79b95d5df7f5f84db3e8e27926b1ff427b016e Author: xdustinface <xdustinfacex@gmail.com> Date: Fri Feb 13 20:34:03 2026 +0100 fix: resolve filter pipeline boundary issues between init and extend_target Store planned batch end heights at queue time so send_pending uses the original boundaries instead of dynamically recomputing them from the current target_height. This prevents both overlaps (when send_pending expands a truncated batch) and gaps (when the batch was already sent with a smaller range). Also fix start_download returning in Syncing state when filter headers haven't reached wallet birth height yet — now returns to WaitForEvents so subsequent filter header events re-trigger start_download correctly. Setup dashd in CI for both spv and ffi test groups. commit e9254d2cda8d7e6635f4657b06418d606ad8aace Author: xdustinface <xdustinfacex@gmail.com> Date: Fri Feb 13 05:31:04 2026 +0100 fix: separate filter scan committed height from per-block synced height Three related fixes for dashd integration test failures: 1. FiltersManager now emits FiltersSyncComplete for incremental updates when already in Synced state, not just during initial Syncing. This allows BlocksManager to properly transition to Synced after processing blocks discovered in post-sync filter updates. 2. wait_for_sync test helper checks filters.current_height (not just filter_headers) to prevent premature returns before filter scanning and block processing complete. 3. Introduces filter_committed_height on WalletInterface, separate from synced_height. process_block updates synced_height per-block (needed for balance/maturity calculations), but FiltersManager uses filter_committed_height for restart recovery. This prevents a bug where per-block synced_height advances past uncommitted filter batches, causing the rescan on restart to skip heights that need rescanning for newly discovered addresses. commit 2ecf9f882ad6580a51dffbf2cdecaa14bc9e8bf7 Author: xdustinface <xdustinfacex@gmail.com> Date: Tue Feb 10 15:26:44 2026 +0100 feat: implement `dashd` sync integration tests for SPV
1 parent d4a82f2 commit 54a5380

24 files changed

Lines changed: 3450 additions & 98 deletions

File tree

.github/workflows/build-and-test.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ on:
1313
permissions:
1414
contents: read
1515

16+
env:
17+
DASHVERSION: "23.0.2"
18+
TEST_DATA_REPO: "dashpay/regtest-blockchain"
19+
TEST_DATA_VERSION: "v0.0.2"
20+
1621
jobs:
1722
test:
1823
name: ${{ matrix.group }}
@@ -28,5 +33,32 @@ jobs:
2833
with:
2934
shared-key: "test-${{ inputs.os }}-${{ matrix.group }}"
3035
- run: pip install pyyaml
36+
37+
# Set up dashd and test data for groups that need it
38+
- name: Cache dashd and test data
39+
if: matrix.group == 'spv' || matrix.group == 'ffi'
40+
uses: actions/cache@v4
41+
with:
42+
path: .rust-dashcore-test
43+
key: rust-dashcore-test-${{ inputs.os }}-${{ env.DASHVERSION }}-${{ env.TEST_DATA_VERSION }}
44+
45+
- name: Setup dashd for integration tests
46+
if: matrix.group == 'spv' || matrix.group == 'ffi'
47+
env:
48+
CACHE_DIR: ${{ github.workspace }}/.rust-dashcore-test
49+
shell: bash
50+
run: python contrib/setup-dashd.py >> "$GITHUB_ENV"
51+
3152
- name: Run tests
53+
env:
54+
DASHD_TEST_RETAIN_DIR: ${{ matrix.group == 'spv' && '/tmp/dashd-test-logs' || '' }}
3255
run: python .github/scripts/ci_config.py run-group ${{ matrix.group }} --os ${{ inputs.os }}
56+
57+
- name: Upload failed SPV test logs
58+
if: failure() && matrix.group == 'spv'
59+
uses: actions/upload-artifact@v4
60+
with:
61+
name: spv-test-logs-${{ inputs.os }}
62+
path: /tmp/dashd-test-logs/
63+
retention-days: 7
64+
if-no-files-found: ignore

contrib/setup-dashd.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#!/usr/bin/env python3
2+
"""Cross-platform setup script for dashd and test blockchain data.
3+
4+
Downloads the Dash Core binary and regtest test data for integration tests.
5+
Outputs DASHD_PATH and DASHD_DATADIR lines suitable for appending to GITHUB_ENV
6+
or evaluating in a shell.
7+
8+
Environment variables:
9+
DASHVERSION - Dash Core version (default: 23.0.2)
10+
TEST_DATA_VERSION - Test data release version (default: v0.0.2)
11+
TEST_DATA_REPO - GitHub repo for test data (default: dashpay/regtest-blockchain)
12+
CACHE_DIR - Cache directory (default: ~/.rust-dashcore-test)
13+
"""
14+
15+
import os
16+
import platform
17+
import sys
18+
import tarfile
19+
import urllib.request
20+
import zipfile
21+
22+
DASHVERSION = os.environ.get("DASHVERSION", "23.0.2")
23+
TEST_DATA_VERSION = os.environ.get("TEST_DATA_VERSION", "v0.0.2")
24+
TEST_DATA_REPO = os.environ.get("TEST_DATA_REPO", "dashpay/regtest-blockchain")
25+
26+
27+
def get_cache_dir():
28+
if "CACHE_DIR" in os.environ:
29+
return os.environ["CACHE_DIR"]
30+
home = os.environ.get("HOME") or os.environ.get("USERPROFILE")
31+
if not home:
32+
sys.exit("Cannot determine home directory: neither HOME nor USERPROFILE is set")
33+
return os.path.join(home, ".rust-dashcore-test")
34+
35+
36+
def get_asset_info():
37+
"""Return (asset_filename, binary_relative_path) for the current platform."""
38+
system = platform.system()
39+
machine = platform.machine()
40+
41+
if system == "Linux":
42+
arch = "aarch64" if machine in ("aarch64", "arm64") else "x86_64"
43+
asset = f"dashcore-{DASHVERSION}-{arch}-linux-gnu.tar.gz"
44+
elif system == "Darwin":
45+
arch = "arm64" if machine == "arm64" else "x86_64"
46+
asset = f"dashcore-{DASHVERSION}-{arch}-apple-darwin.tar.gz"
47+
elif system == "Windows":
48+
asset = f"dashcore-{DASHVERSION}-win64.zip"
49+
else:
50+
sys.exit(f"Unsupported platform: {system}")
51+
52+
return asset
53+
54+
55+
def log(msg):
56+
print(msg, file=sys.stderr)
57+
58+
59+
def download(url, dest):
60+
log(f"Downloading {url} ...")
61+
urllib.request.urlretrieve(url, dest)
62+
63+
64+
def extract(archive_path, dest_dir):
65+
if archive_path.endswith(".zip"):
66+
with zipfile.ZipFile(archive_path, "r") as zf:
67+
zf.extractall(dest_dir)
68+
else:
69+
with tarfile.open(archive_path, "r:gz") as tf:
70+
tf.extractall(dest_dir)
71+
72+
73+
def setup_dashd(cache_dir):
74+
"""Download and extract dashd binary. Returns the path to the dashd binary."""
75+
asset = get_asset_info()
76+
dashd_dir = os.path.join(cache_dir, f"dashcore-{DASHVERSION}")
77+
78+
ext = ".exe" if platform.system() == "Windows" else ""
79+
dashd_bin = os.path.join(dashd_dir, "bin", f"dashd{ext}")
80+
81+
if os.path.isfile(dashd_bin):
82+
log(f"dashd {DASHVERSION} already available")
83+
return dashd_bin
84+
85+
log(f"Downloading dashd {DASHVERSION}...")
86+
archive_path = os.path.join(cache_dir, asset)
87+
url = f"https://github.com/dashpay/dash/releases/download/v{DASHVERSION}/{asset}"
88+
download(url, archive_path)
89+
extract(archive_path, cache_dir)
90+
os.remove(archive_path)
91+
log(f"Downloaded dashd to {dashd_dir}")
92+
93+
if not os.path.isfile(dashd_bin):
94+
sys.exit(f"Expected binary not found after extraction: {dashd_bin}")
95+
96+
return dashd_bin
97+
98+
99+
def setup_test_data(cache_dir):
100+
"""Download and extract test blockchain data. Returns the datadir path."""
101+
test_data_dir = os.path.join(
102+
cache_dir, f"regtest-blockchain-{TEST_DATA_VERSION}", "regtest-40000"
103+
)
104+
blocks_dir = os.path.join(test_data_dir, "regtest", "blocks")
105+
106+
if os.path.isdir(blocks_dir):
107+
log(f"Test blockchain data {TEST_DATA_VERSION} already available")
108+
return test_data_dir
109+
110+
log(f"Downloading test blockchain data {TEST_DATA_VERSION}...")
111+
parent_dir = os.path.join(cache_dir, f"regtest-blockchain-{TEST_DATA_VERSION}")
112+
os.makedirs(parent_dir, exist_ok=True)
113+
114+
archive_path = os.path.join(cache_dir, "regtest-40000.tar.gz")
115+
url = f"https://github.com/{TEST_DATA_REPO}/releases/download/{TEST_DATA_VERSION}/regtest-40000.tar.gz"
116+
download(url, archive_path)
117+
extract(archive_path, parent_dir)
118+
os.remove(archive_path)
119+
log(f"Downloaded test data to {test_data_dir}")
120+
121+
return test_data_dir
122+
123+
124+
def main():
125+
cache_dir = get_cache_dir()
126+
os.makedirs(cache_dir, exist_ok=True)
127+
128+
dashd_path = setup_dashd(cache_dir)
129+
datadir = setup_test_data(cache_dir)
130+
131+
# Output lines for GITHUB_ENV or shell eval
132+
print(f"DASHD_PATH={dashd_path}")
133+
print(f"DASHD_DATADIR={datadir}")
134+
135+
136+
if __name__ == "__main__":
137+
main()

dash-spv-ffi/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,17 @@ key-wallet = { path = "../key-wallet" }
3232
key-wallet-manager = { path = "../key-wallet-manager" }
3333
rand = "0.8"
3434
clap = { version = "4.5", features = ["derive"] }
35+
tempfile = { version = "3.8", optional = true }
36+
37+
[features]
38+
test-utils = ["dep:tempfile"]
3539

3640
[dev-dependencies]
37-
tempfile = "3.8"
41+
dash-spv = { path = "../dash-spv", features = ["test-utils"] }
42+
dash-spv-ffi = { path = ".", features = ["test-utils"] }
3843
serial_test = "3.0"
3944
env_logger = "0.10"
45+
hex = "0.4"
4046

4147
[build-dependencies]
4248
cbindgen = "0.29"

dash-spv-ffi/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ pub use platform_integration::*;
1616
pub use types::*;
1717
pub use utils::*;
1818

19+
#[cfg(any(test, feature = "test-utils"))]
20+
pub mod test_utils;
21+
1922
// FFINetwork is now defined in types.rs for cbindgen compatibility
2023
// It must match the definition in key_wallet_ffi
2124

0 commit comments

Comments
 (0)