diff options
Diffstat (limited to 'tests/scripts/test_bitflash_tools.py')
| -rw-r--r-- | tests/scripts/test_bitflash_tools.py | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/tests/scripts/test_bitflash_tools.py b/tests/scripts/test_bitflash_tools.py new file mode 100644 index 0000000..ff569e1 --- /dev/null +++ b/tests/scripts/test_bitflash_tools.py | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | """Regression tests for deterministic Bitflash Python tooling.""" | ||
| 3 | |||
| 4 | import json | ||
| 5 | import os | ||
| 6 | import struct | ||
| 7 | import subprocess | ||
| 8 | import sys | ||
| 9 | import tempfile | ||
| 10 | from pathlib import Path | ||
| 11 | |||
| 12 | |||
| 13 | ROOT = Path(__file__).resolve().parents[2] | ||
| 14 | SCRIPTS = ROOT / "scripts" | ||
| 15 | sys.path.insert(0, str(SCRIPTS)) | ||
| 16 | |||
| 17 | import bitflash_chain as chain # noqa: E402 | ||
| 18 | |||
| 19 | |||
| 20 | def run(args, **kwargs): | ||
| 21 | env = dict(os.environ) | ||
| 22 | env["PYTHONIOENCODING"] = "utf-8" | ||
| 23 | return subprocess.run( | ||
| 24 | [sys.executable] + [str(a) for a in args], | ||
| 25 | cwd=str(ROOT), | ||
| 26 | env=env, | ||
| 27 | check=True, | ||
| 28 | text=True, | ||
| 29 | stdout=subprocess.PIPE, | ||
| 30 | stderr=subprocess.PIPE, | ||
| 31 | **kwargs | ||
| 32 | ) | ||
| 33 | |||
| 34 | |||
| 35 | def write_block_file(path, blocks): | ||
| 36 | with Path(path).open("wb") as f: | ||
| 37 | f.write(b"noise") | ||
| 38 | f.write(chain.MAGIC) | ||
| 39 | f.write(struct.pack("<I", chain.MAX_BLOCK_PAYLOAD + 1)) | ||
| 40 | f.write(b"still-noise") | ||
| 41 | for block in blocks: | ||
| 42 | payload = chain.ser_block(block) | ||
| 43 | f.write(chain.MAGIC) | ||
| 44 | f.write(struct.pack("<I", len(payload))) | ||
| 45 | f.write(payload) | ||
| 46 | |||
| 47 | |||
| 48 | def assert_true(cond, msg): | ||
| 49 | if not cond: | ||
| 50 | raise AssertionError(msg) | ||
| 51 | |||
| 52 | |||
| 53 | def main(): | ||
| 54 | genesis = chain.reconstructed_genesis() | ||
| 55 | outpoint = "%s:0" % chain.GENESIS_MERKLE | ||
| 56 | |||
| 57 | with tempfile.TemporaryDirectory(prefix="bitflash-tool-tests-") as tmp: | ||
| 58 | tmp = Path(tmp) | ||
| 59 | datadir = tmp / "datadir" | ||
| 60 | datadir.mkdir() | ||
| 61 | blk1 = datadir / "blk0001.dat" | ||
| 62 | blk2 = datadir / "blk0002.dat" | ||
| 63 | write_block_file(blk1, [genesis]) | ||
| 64 | blk2.write_bytes(b"") | ||
| 65 | |||
| 66 | fair = run([SCRIPTS / "verify-fair-launch.py", "--datadir", datadir, | ||
| 67 | "--max-blocks", "1", "--json"]).stdout | ||
| 68 | fair_report = json.loads(fair) | ||
| 69 | assert_true(fair_report["verdict"] == "FAIR_LAUNCH_BASELINE_VERIFIED", | ||
| 70 | "fair launch verifier failed synthetic genesis") | ||
| 71 | |||
| 72 | utxo = run([SCRIPTS / "verify-utxo-set.py", "--datadir", datadir, | ||
| 73 | "--json"]).stdout | ||
| 74 | utxo_report = json.loads(utxo) | ||
| 75 | assert_true(utxo_report["verdict"] == "UTXO_COMMITMENT_VERIFIED", | ||
| 76 | "UTXO verifier failed synthetic genesis") | ||
| 77 | |||
| 78 | proof_path = tmp / "proof.json" | ||
| 79 | run([SCRIPTS / "prove-utxo.py", outpoint, "--datadir", datadir, | ||
| 80 | "--out", proof_path, "--json"]) | ||
| 81 | proof_check = run([SCRIPTS / "verify-utxo-proof.py", proof_path, | ||
| 82 | "--root", utxo_report["commitment"]["root"], "--json"]).stdout | ||
| 83 | proof_report = json.loads(proof_check) | ||
| 84 | assert_true(proof_report["verdict"] == "UTXO_PROOF_VERIFIED", | ||
| 85 | "UTXO proof verifier rejected synthetic proof") | ||
| 86 | |||
| 87 | explorer_dir = tmp / "explorer" | ||
| 88 | run([SCRIPTS / "build-explorer.py", blk1, blk2, explorer_dir]) | ||
| 89 | blocks = json.loads((explorer_dir / "blocks.json").read_text(encoding="ascii")) | ||
| 90 | assert_true(blocks["tipHeight"] == 0 and blocks["count"] == 1, | ||
| 91 | "explorer did not build the synthetic chain") | ||
| 92 | index_html = (explorer_dir / "index.html").read_text(encoding="utf-8") | ||
| 93 | assert_true("innerHTML" not in index_html, "explorer viewer still uses innerHTML") | ||
| 94 | |||
| 95 | try: | ||
| 96 | chain.apply_blocks([genesis, genesis], strict_duplicates=True) | ||
| 97 | except chain.ParseError: | ||
| 98 | pass | ||
| 99 | else: | ||
| 100 | raise AssertionError("strict duplicate output mode did not fail") | ||
| 101 | |||
| 102 | print("Bitflash script tool regressions passed") | ||
| 103 | return 0 | ||
| 104 | |||
| 105 | |||
| 106 | if __name__ == "__main__": | ||
| 107 | raise SystemExit(main()) | ||