commit 4377c85cd78611199bb4aace9234ea7873cfbb14
parent 700da7c158278af551822853674d4fc8956aa55d
Author: Joris Hartog <jorishartog@hotmail.com>
Date: Tue, 1 Sep 2026 12:56:43 +0200
Extend post-activation burn E2E
Diffstat:
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/e2e/README.md b/e2e/README.md
@@ -59,7 +59,11 @@ blocks, network-health, peers, mempool, and wallet APIs on every node.
`fallback-activation` crosses height 300 and checks that objective finality has
not activated early. `objective-finality` crosses heights 1000 and 1001 and
requires a certified checkpoint. `checkpoint-restart` proves that the certified
-checkpoint survives a full six-node restart and that the chain keeps advancing.
+checkpoint survives a full six-node restart, advances through height 1007, and
+that block 1007 is finalized by a ticket derived from a burn included at height
+1002 or later. At that height, the ticket maturity and expiry windows exclude
+every pre-pipeline burn, so this covers a complete post-activation burn-to-block
+lifecycle.
Use `--keep` on `smoke` to leave a failed or successful network running. Stop a
network without deleting its mutable data with:
diff --git a/e2e/iuna_e2e.py b/e2e/iuna_e2e.py
@@ -57,6 +57,7 @@ class Scenario:
minimum_finalized_height: int | None = None
canonical_height: int | None = None
require_fixture_hash: bool = False
+ leader_burn_minimum_height: int | None = None
SCENARIOS = {
@@ -72,10 +73,11 @@ SCENARIOS = {
),
"checkpoint-restart": Scenario(
snapshot="first-objective-checkpoint",
- through=1_002,
+ through=1_007,
minimum_finalized_height=1_000,
canonical_height=1_000,
require_fixture_hash=True,
+ leader_burn_minimum_height=1_002,
),
}
@@ -245,6 +247,36 @@ def block_at_height(service: str, height: int) -> dict:
return result[0]
+def assert_leader_uses_burn_from_height(
+ block_height: int, minimum_height: int
+) -> None:
+ block = block_at_height(SERVICES[0], block_height)
+ leader_proof = block.get("leader_proof")
+ if block.get("finalizer_mode") != "ticket" or not isinstance(leader_proof, dict):
+ raise E2EError(f"block {block_height} was not finalized by a burn ticket")
+
+ ticket_id = leader_proof.get("ticket_id")
+ if not isinstance(ticket_id, str) or not ticket_id:
+ raise E2EError(f"block {block_height} has no valid leader ticket ID")
+
+ for height in range(minimum_height, block_height):
+ source = block_at_height(SERVICES[0], height)
+ if any(
+ transaction.get("kind") == "burn"
+ and transaction.get("signature") == ticket_id
+ for transaction in source.get("transactions", [])
+ ):
+ print(
+ f"block {block_height} leader ticket comes from burn at height {height}"
+ )
+ return
+
+ raise E2EError(
+ f"block {block_height} leader ticket does not come from a burn at or after "
+ f"height {minimum_height}"
+ )
+
+
def assert_api_health(statuses: dict[str, dict], through: int) -> None:
for service in SERVICES:
blocks = node_json(service, "/api/blocks?limit=3")
@@ -607,6 +639,10 @@ def run_scenario(
assert_canonical_block(
scenario.canonical_height, scenario.require_fixture_hash
)
+ if scenario.leader_burn_minimum_height is not None:
+ assert_leader_uses_burn_from_height(
+ scenario.through, scenario.leader_burn_minimum_height
+ )
assert_api_health(statuses, scenario.through)
print(f"e2e scenario {name} passed")
except Exception: