Testnet re-cut records (7 October 2026): infra/seed-nodes/testnet-object.json (igneum-testnet-1's object as override-file JSON, from print_testnet_object; the testnet itself takes no override file), docs/testnet/README.md with the re-cut genesis 01294fd3..., merkle bcd0e8fb..., digest 9390d235... and the switches on from genesis, the ledger's X3 evidence line, and the base-unit gate script from the decimals branch (tools/fleet/base-unit-gate.sh) for the gate on this tree
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
cbbaace4bd
commit
7087fbb850
4 changed files with 328 additions and 9 deletions
|
|
@ -2249,7 +2249,7 @@ Status: Fixed, stated (6 October 2026, night, the owner's decision): every menti
|
|||
|
||||
Answer: The date was the plan of 3 October 2026 and the chain overtook it: the testnet genesis was fixed on 5 October, the three seeds and rpc.testnet.igneum.network are up, and the remaining work is the go checklist. Rows that quoted the month (X3, O-X.2's blocker note, overclaim item 75's replacement text) read the new sentence by reference to this row.
|
||||
|
||||
Evidence: `docs/plans/testnet-go.md`; `docs/igneum-testnet` notes (genesis 87617621..., seeds seed1 to seed3.testnet.igneum.network, public RPC). Checked by `tools/ci/ledger-text-check.mjs` (the X3 and X31 rows).
|
||||
Evidence: `docs/plans/testnet-go.md`; `docs/igneum-testnet` notes (genesis 01294fd3... since the re-cut of 7 October 2026, 87617621... before it; seeds seed1 to seed3.testnet.igneum.network, public RPC). Checked by `tools/ci/ledger-text-check.mjs` (the X3 and X31 rows).
|
||||
|
||||
### X32. The roadmap carried calendar months beside a testnet that is weeks away
|
||||
"After X31 the roadmap read phase 4 'Apr to Jul 2027' and phase 6 'Nov 2027' with phase 5 'weeks away' between them, and phases 1 to 3 carried 'Oct to Nov 2026', 'Nov 2026 to Jan 2027' and '20 nodes by Mar 2027'. A reader spots the contradiction at once."
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Igneum public testnet: identity, parameters and reset policy (ADOPTED 5 October 2026)
|
||||
# Igneum public testnet: identity, parameters and reset policy (ADOPTED 5 October 2026; genesis RE-CUT 7 October 2026)
|
||||
|
||||
Every value in this file was proposed on the night of 4 October 2026 and ADOPTED by the owner on 5 October 2026,
|
||||
as proposed (sign-off recorded in `docs/plans/release-0.3.6.md`). The genesis below is the one the proposal
|
||||
|
|
@ -8,7 +8,13 @@ merged on 5 October 2026 into the fork's `release-0.3.6` (worktree `vendor/igneu
|
|||
sign-off added: the devnet and the simnet keep the prototype fee set until a height switch
|
||||
(`fees_v1_activation_daa`) or a `fees` object in the override file moves them; the testnet and the mainnet carry
|
||||
calibrated v1 from genesis (section 3). The devnet's switch is DAA score 210,000 (5 October 2026,
|
||||
`docs/plans/fee-switch-devnet.md`). The three seed nodes went up on 5 October 2026 from the final genesis below (`docs/plans/testnet-go.md`); nothing mines until the owner's go.
|
||||
`docs/plans/fee-switch-devnet.md`). The three seed nodes went up on 5 October 2026 from the 5 October genesis (`docs/plans/testnet-go.md`); nothing mines until the owner's go.
|
||||
|
||||
RE-CUT 7 October 2026 (the project lead's approvals of 09:3x UK, `docs/plans/ledger-decisions.md` "Decisions (7 October 2026, 09:3x UK)"):
|
||||
one cut with 18 decimals (O-2.6, the 16-byte coinbase subsidy), `EmissionSchedule::TESTNET_1`, and every switch on from
|
||||
genesis. The genesis hash and the consensus digest below are the re-cut's; the seeds still hold the 5 October chain at
|
||||
height 0 and move to the re-cut binary only on the project lead's go (`docs/plans/testnet-go.md`, the cut-over runbook). Fork branch
|
||||
`testnet-genesis-2-node` (release-0.3.18-node e69e8a39, the decimals branch df2fbd03, the vote-or-burn removal 420f9305).
|
||||
|
||||
## 1. Identity
|
||||
|
||||
|
|
@ -40,22 +46,31 @@ read methods plus `eth_sendRawTransaction`; `infra/seed-nodes/rpc/`).
|
|||
| Nonce, DAA score | 0, 0 | |
|
||||
| UTXO commitment | empty | |
|
||||
| Coinbase payload message | `igneum-testnet-1 \| 2026-10-05 \| coins here have no value \| resets are announced` | after the OP-FALSE script, as the devnet's `igneum-devnet`. FINAL 5 October 2026 (fork branch `testnet-infra`, 1c19441d): the proposal's "proposed, not final" was dropped before the first public node started, as `docs/plans/release-0.3.6.md` section 6 required. The message never changes again on `igneum-testnet-1` |
|
||||
| Hash | `87617621714af1bf33bd17f291f90a7e0bff760a669bba53083ea8c0f7cbd840` | computed 5 October 2026 by `print_genesis_hashes` (two runs: the merkle root first, then the header hash over it), pinned by `test_genesis_hashes` and `igneum_testnet_identity`; the three seeds started from it at height 0 the same day. The proposal's hash `52a3e6a9...` is void |
|
||||
| Merkle root | `44acfc40b1c6647011510f3c39ddb7606f979df92ad26a2914de56e44610efad` | same |
|
||||
| Coinbase payload subsidy field | 16 little-endian bytes, one IGN = 10^18 base units | the 18-decimal layout (O-2.6, `docs/design/base-unit.md` section 3); block 1 merges the genesis and reads this field at the network's unit |
|
||||
| Hash | `01294fd322704dc28fbef0e7a5ef86d6ee260ac5efaf88891cdba661b5fd58ac` | RE-CUT 7 October 2026, computed on igneum-build-1 by `print_genesis_hashes` (two runs: the merkle root first, then the header hash over it), pinned by `test_genesis_hashes` and `igneum_testnet_identity`. The 5 October hash `87617621...` (8-byte subsidy layout, the chain the seeds hold at height 0 until the cut-over) and the proposals `52a3e6a9...` and `494fc9a3...` are void |
|
||||
| Merkle root | `bcd0e8fb1099aeb3cc1370b50e6dec9f615e204374b2097f25b2438e7d22f49f` | same (5 October: `44acfc40...`) |
|
||||
| Consensus digest | `9390d235ad1543e2aa4b49013092bd45e39a917f312610e2bd1816a65c9071e7` | exchanged in the p2p handshake; a peer with another digest is refused. Pinned by `igneum_testnet_identity`; the whole object as override-file JSON is `infra/seed-nodes/testnet-object.json` (`print_testnet_object`). The 5 October digest `b7d8c915...` is void. The devnet's `c562d70e...` is unchanged |
|
||||
|
||||
## 3. Consensus parameters
|
||||
|
||||
| Parameter | Testnet (adopted) | Devnet today | Why |
|
||||
|---|---|---|---|
|
||||
| Block rate | 1 per second | 1 per second | spec 02 |
|
||||
| Difficulty rule | Igneum dual-lane, v2 from genesis | dual-lane, v2 from DAA 33,000 | a fresh chain has no pre-switch history |
|
||||
| Finality parameters | `FinalityParams::MAINNET`: 30-day weight window (2,592,000 DAA), dust 100, presence 240, 8 aggregators, 30-day equivocation ban, min DAA 2,592,000, certificate fold 6 | `DEVNET`: 2-hour window, dust 5, presence 20, fold 3 | the testnet runs the rule the mainnet will run; the first lock needs 30 days of weight, which is the point of a testnet |
|
||||
| Difficulty rule | Igneum dual-lane, v2 and v3 from genesis (`difficulty_v3_activation_daa` 0) | dual-lane, v2 from DAA 33,000, v3 by the override file | a fresh chain has no pre-switch history |
|
||||
| Finality parameters | `FinalityParams::MAINNET`: 30-day weight window (2,592,000 DAA), dust 100, presence 240, 8 aggregators, 30-day equivocation ban, min DAA 2,592,000, certificate fold 6, leave delay 3,600 | `DEVNET`: 2-hour window, dust 5, presence 20, fold 3 | the testnet runs the rule the mainnet will run; the first lock needs 30 days of weight, which is the point of a testnet |
|
||||
| Finality rule v3 | from genesis | from the override file | fresh chain |
|
||||
| The DAA-second finality rule (C1) | from genesis (`finality_daa_rule_activation_daa` 0): a checkpoint every 30 s of chain time at any block rate | by the override file | re-cut 7 October 2026 |
|
||||
| The signed leave item (W7) | from genesis (`finality_leave_activation_daa` 0, delay 3,600 DAA s): a key that announces its departure is in no denominator an hour later | never until the 95 percent signal | the project lead, 6 October 2026, question 4 |
|
||||
| The signing bonus, no burn | from genesis (`signing_bonus_activation_daa` 0, `signing_bonus_bps` 1,000): a tenth of a silent producer's subsidy share moves to the proving pool; nothing is ever destroyed; vote-or-burn is out of the tree (420f9305) | never | the project lead, 7 October 2026, row 2 |
|
||||
| Consensus proof verification | from genesis (`proving_consensus_verify_daa` 0) under the shard program id `0x2b1a81cb...ef7a` and the aggregator id `0x474678f3...3896` of `proving/igneum-prove/elf/manifest.json` (pinned 2026-10-05T16:20:38Z); a node whose embedded keys differ refuses to start; a Windows node verifies through the installed `igneum-prove-host` and refuses to start without it | off | the project lead, 6 October 2026, question 3 |
|
||||
| Program class | v4 from genesis, unconditional (`program_class_v4_activation_daa` 0, signal window 0) | v4 by miner signal | fresh chain |
|
||||
| The latency ladder | active from genesis at rung 0 (`latency_ladder_activation_daa` 0, window 86,400 DAA, seven windows at 90 percent to step): rungs 27, 35, 53 passes admissible; 88 inadmissible (quiet-core re-measure 7 October 2026, 08:46 UK: 10.85 ms cold with the SMT sibling loaded, over the 10 ms gate); 173 and 267 inadmissible | never | the project lead, 7 October 2026, row 3 |
|
||||
| Base unit | 18 decimals (`base_unit_decimals` 18): one IGN is 10^18 base units, the EVM's wei, the bridge is the identity | 8 (sompi) | the project lead, 6 October 2026, question 5; `docs/design/base-unit.md` |
|
||||
| Proving v0 payouts | from genesis | from the override file | fresh chain |
|
||||
| PoW schedule | epoch 3,600 DAA, lead 600, day 86,400,000 ms (the defaults) | same | |
|
||||
| Coinbase payload limit | 16,384 (the finality section) | same | |
|
||||
| Fees | `FeeParams::CALIBRATED_V1` from genesis (`fees_v1_activation_daa` 0; `docs/analysis/base-fee-floor.md`): `B_p` 120,000 pgas, `S_p` 30,000, intrinsic 300, modexp 10 + 1 per 10 bytes, floors 100 gwei per gas and 10,000 gwei per pgas | `FeeParams::PROTOTYPE` (`B_p` 30 M, 1 gwei), kept on the 0.3.6 node so the live chain does not change rules between builds; moves to v1 by the `fees_v1_activation_daa` height switch in the override file (`docs/plans/release-0.3.6.md`, section 5) | spec 05 section 5.10 |
|
||||
| Emission | the mainnet schedule: 31.69 IGN per block in year one, halving every two years, cap 4 billion | same | the testnet coins have no value whatever the schedule says |
|
||||
| Emission | `EmissionSchedule::TESTNET_1` at 18 decimals: 100 IGN a block at 1 bps, a monthly glide with a two-year half-life, a 90-day ramp from 10 percent, a tail of 1 percent of supply a year from the month the glide first pays under it (about year 11.4), no hard cap (`docs/analysis/tail-emission.md`) | `CURRENT`: 31.69 IGN per block in year one, halving every two years, cap 4 billion | the project lead, 7 October 2026, row 1; the testnet coins have no value whatever the schedule says |
|
||||
|
||||
Everything else (mass limits, GHOSTDAG k, merge depth, pruning) is the devnet's set, unchanged.
|
||||
|
||||
|
|
@ -81,5 +96,6 @@ Everything else (mass limits, GHOSTDAG k, merge depth, pruning) is the devnet's
|
|||
| The public RPC and explorer | the RPC is `https://rpc.testnet.igneum.network` (5 October 2026); the explorer is not built |
|
||||
| The prover's table mirror and fixtures | `docs/analysis/base-fee-floor.md` section 4 |
|
||||
| The app's testnet build | branch `testnet-app` (5 October 2026): the packaged file's `network`, `peers`, `public_rpc`; the testnet's ports, seeds and node directory in `app/igneum-app/src/config.rs`; the release engineer cuts 0.4.0 from it at go |
|
||||
| The params digest in the handshake (X18) | separate work, before the testnet |
|
||||
| The params digest in the handshake (X18) | done: `9390d235...` for the re-cut object |
|
||||
| The seeds on the re-cut genesis | NOT DONE: the three seeds hold the 5 October chain at height 0; the cut-over (new binary, wiped data directory, the digest line read back on each) runs on the project lead's go, `docs/plans/testnet-go.md` |
|
||||
| Terms on the download page | `site/index.html#testnet-terms`, on branch `testnet-prep` with this file |
|
||||
|
|
|
|||
146
infra/seed-nodes/testnet-object.json
Normal file
146
infra/seed-nodes/testnet-object.json
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
{
|
||||
"timestamp_deviation_tolerance": 132,
|
||||
"past_median_time_window_size": 27,
|
||||
"difficulty_window_size": 661,
|
||||
"min_difficulty_window_size": 150,
|
||||
"difficulty_rule": "igneum-dual",
|
||||
"coinbase_payload_script_public_key_max_len": 150,
|
||||
"max_coinbase_payload_len": 16384,
|
||||
"max_tx_inputs": 1000,
|
||||
"max_tx_outputs": 1000,
|
||||
"max_signature_script_len": 250000,
|
||||
"max_script_public_key_len": 10000,
|
||||
"mass_per_tx_byte": 1,
|
||||
"mass_per_script_pub_key_byte": 10,
|
||||
"mass_per_sig_op": 1000,
|
||||
"block_mass_limits": {
|
||||
"storage": 500000,
|
||||
"compute": 500000,
|
||||
"transient": 1000000
|
||||
},
|
||||
"block_lane_limits": {
|
||||
"lanes_per_block": 50,
|
||||
"gas_per_lane": 1000000000
|
||||
},
|
||||
"storage_mass_parameter": 1000000000000,
|
||||
"deflationary_phase_daa_score": 0,
|
||||
"pre_deflationary_phase_base_subsidy": 50000000000,
|
||||
"skip_proof_of_work": false,
|
||||
"max_block_level": 250,
|
||||
"pruning_proof_m": 1000,
|
||||
"blockrate": {
|
||||
"target_time_per_block": 1000,
|
||||
"ghostdag_k": 18,
|
||||
"past_median_time_sample_rate": 10,
|
||||
"difficulty_sample_rate": 4,
|
||||
"max_block_parents": 10,
|
||||
"mergeset_size_limit": 180,
|
||||
"merge_depth": 3600,
|
||||
"finality_depth": 43200,
|
||||
"pruning_depth": 108000,
|
||||
"coinbase_maturity": 100
|
||||
},
|
||||
"pre_crescendo_target_time_per_block": 1000,
|
||||
"crescendo_activation": 0,
|
||||
"genesis_bits": 487587840,
|
||||
"finality": {
|
||||
"checkpoint_interval": 30,
|
||||
"checkpoint_depth": 60,
|
||||
"weight_window": 2592000,
|
||||
"dust": 100,
|
||||
"presence_window": 240,
|
||||
"aggregators": 8,
|
||||
"equivocation_ban": 2592000,
|
||||
"min_daa": 2592000,
|
||||
"aggregator_fallback": 15,
|
||||
"certificate_fold": 6,
|
||||
"leave_delay": 3600
|
||||
},
|
||||
"pow_epoch_blocks": 3600,
|
||||
"pow_epoch_lead": 600,
|
||||
"pow_day_ms": 86400000,
|
||||
"difficulty_v2_activation_daa": 0,
|
||||
"difficulty_v3_activation_daa": 0,
|
||||
"proving_v0_activation_daa": 0,
|
||||
"finality_v3_activation_daa": 0,
|
||||
"finality_daa_rule_activation_daa": 0,
|
||||
"fork_gate_activation_daa": 18446744073709551615,
|
||||
"fork_gate_window_daa": 600,
|
||||
"signing_bonus_activation_daa": 0,
|
||||
"signing_bonus_bps": 1000,
|
||||
"finality_leave_activation_daa": 0,
|
||||
"program_class_v3_activation_daa": 0,
|
||||
"program_class_v4_activation_daa": 0,
|
||||
"program_class_v4_signal_window_daa": 0,
|
||||
"base_unit_decimals": 18,
|
||||
"pow_genesis_dataset_log2": 28,
|
||||
"latency_ladder": [
|
||||
{
|
||||
"reps": 27,
|
||||
"admissible": true
|
||||
},
|
||||
{
|
||||
"reps": 35,
|
||||
"admissible": true
|
||||
},
|
||||
{
|
||||
"reps": 53,
|
||||
"admissible": true
|
||||
},
|
||||
{
|
||||
"reps": 88,
|
||||
"admissible": false
|
||||
},
|
||||
{
|
||||
"reps": 173,
|
||||
"admissible": false
|
||||
},
|
||||
{
|
||||
"reps": 267,
|
||||
"admissible": false
|
||||
}
|
||||
],
|
||||
"latency_ladder_activation_daa": 0,
|
||||
"latency_ladder_window_daa": 86400,
|
||||
"fees": {
|
||||
"pgas": {
|
||||
"version": 1,
|
||||
"cycles_per_pgas": 1000,
|
||||
"intrinsic_pgas_per_tx": 300,
|
||||
"modexp_base": 10,
|
||||
"modexp_per_byte_numer": 1,
|
||||
"modexp_per_byte_denom": 10
|
||||
},
|
||||
"block_proving_gas_limit": 120000,
|
||||
"shard_proving_gas_budget": 30000,
|
||||
"min_execution_base_fee_wei": 100000000000,
|
||||
"min_proving_base_fee_wei": 10000000000000,
|
||||
"initial_execution_base_fee_wei": 100000000000,
|
||||
"initial_proving_base_fee_wei": 10000000000000,
|
||||
"base_fee_change_denominator": 8
|
||||
},
|
||||
"fees_v1_activation_daa": 0,
|
||||
"proving_v1_activation_daa": 18446744073709551615,
|
||||
"proving_v1_segment_blocks": 8,
|
||||
"proving_v1_unproven_daa": 600,
|
||||
"proving_v1_aggregator_share_bps": 1000,
|
||||
"proving_v1_fresh_rule_daa": 18446744073709551615,
|
||||
"exec_restart_number": 18446744073709551615,
|
||||
"exec_restart_hash": "",
|
||||
"exec_restart_trust_daa": 18446744073709551615,
|
||||
"exec_restart_state_root": "",
|
||||
"emission": {
|
||||
"launch_rate": "100000000000000000000",
|
||||
"ramp_seconds": 7776000,
|
||||
"ramp_start_percent": 10,
|
||||
"step_seconds": 2629800,
|
||||
"step_decay_q32": 4172697914,
|
||||
"tail": {
|
||||
"kind": "percent",
|
||||
"bps_per_year": 100
|
||||
}
|
||||
},
|
||||
"proving_consensus_verify_daa": 0,
|
||||
"proving_shard_program_id": "0x2b1a81cb413236cf063077b46ed3111628f6c41036bcf6e23ee4cbbf5679ef7a",
|
||||
"proving_aggregator_id": "0x474678f35f7545db28055d5e5bbc308231d84a5a072202087a2a8d5b09123896"
|
||||
}
|
||||
157
tools/fleet/base-unit-gate.sh
Executable file
157
tools/fleet/base-unit-gate.sh
Executable file
|
|
@ -0,0 +1,157 @@
|
|||
#!/usr/bin/env bash
|
||||
# B10 of the base-unit widening (O-2.6, docs/design/base-unit.md section 8): two nodes on the testnet params (18 decimals)
|
||||
# mine for N seconds on one box, then the coinbase, the gRPC and the execution layer must read one number.
|
||||
#
|
||||
# Runs ON igneum-build-1 (ssh build@188.40.146.49 'bash -s' < tools/fleet/base-unit-gate.sh [seconds]), against the
|
||||
# binaries of the decimals worktree's fork (target/release of /srv/builds/igneum-wt-decimals/vendor/igneum-node-decimals).
|
||||
# Ports 28110 to 28191 (nothing else on the box uses them); data under /srv/builds/_gate-decimals, wiped at the start; the
|
||||
# processes it starts are the only ones it stops (a pid file each, never a pattern). PASS is the last line.
|
||||
#
|
||||
# What it checks (each a FAIL line otherwise):
|
||||
# 1. both nodes answer and B reaches the same sink and block count as A (the p2p wire carries the wide amounts)
|
||||
# 2. at least MIN_BLOCKS blocks were mined
|
||||
# 3. igneum-miner inspect over the last 30 blocks: every coinbase's payload subsidy is above u64::MAX (18 decimals),
|
||||
# the outputs split 80/20 exactly (the UTXO side), identical on both nodes
|
||||
# 4. the execution layer: eth_getBalance of the miner's EVM address is equal on both nodes and equals the sum of the
|
||||
# producer shares the segments paid (igneum_getSegment over every chain block), which is the identity bridge
|
||||
# 5. every reward in a segment equals producer_share(block_subsidy(daa of the chain block)) under the testnet schedule
|
||||
# at 18 decimals (100 IGN a second, the 90-day ramp from 10%), computed here in exact integers
|
||||
#
|
||||
# CPU note (7 October 2026, 01:5x UK): the testnet genesis bits are 2^28 expected hashes a block and the box's CPU engine
|
||||
# does 0.147 MH/s on 32 threads, so a block takes 10 to 30 minutes on CPU; the ten-minute, hundreds-of-blocks form of
|
||||
# this gate needs a GPU wave box (the fleet lane's); on the box alone run it for an hour with MIN_BLOCKS=3.
|
||||
#
|
||||
# A fresh chain is never "synced" by the mining rule (its sink is the two-day-old genesis), so both nodes run with
|
||||
# --enable-unsynced-mining, as a devnet's first node does; without it every found block is Reject(IsInIBD) (seen 00:2x UK).
|
||||
#
|
||||
# Known-failed case: run with GATE_EXPECT_DECIMALS=8 against the same nodes and check 3 and 5 fail (the schedule at 8
|
||||
# does not match an 18-decimal chain). The self-test target below does that on the recorded output.
|
||||
set -euo pipefail
|
||||
SECS="${1:-600}"; MIN_BLOCKS="${MIN_BLOCKS:-60}"; THREADS="${THREADS:-48}"
|
||||
BIN="${BIN:-/srv/builds/igneum-wt-decimals/vendor/igneum-node-decimals/target/release}"
|
||||
ROOT="${ROOT:-/srv/builds/_gate-decimals}"; A_RPC=28110; A_P2P=28111; A_EVM=28190; B_RPC=28120; B_P2P=28121; B_EVM=28191
|
||||
# WORKER=<path to igneum-worker-cuda|igneum-worker-opencl>: the GPU form (a wave box; the fleet lane rents it): the first
|
||||
# pack is exported from node A, the worker serves it and the miner prepares the next seeds; THREADS is then ignored
|
||||
WORKER="${WORKER:-}"
|
||||
EVM_ADDR="00000000000000000000000000000000000000aa"
|
||||
fail=0
|
||||
say() { echo "$(date -u +%H:%M:%SZ) gate: $*"; }
|
||||
die() { say "FAIL: $*"; fail=1; }
|
||||
stop_all() {
|
||||
for p in "$ROOT"/*.pid; do [ -f "$p" ] && kill "$(cat "$p")" 2>/dev/null || true; done
|
||||
sleep 2
|
||||
}
|
||||
trap stop_all EXIT
|
||||
rm -rf "$ROOT"; mkdir -p "$ROOT/a" "$ROOT/b"
|
||||
[ -x "$BIN/igneumd" ] && [ -x "$BIN/igneum-miner" ] || { echo "FAIL: binaries missing in $BIN"; exit 2; }
|
||||
|
||||
say "starting node A (testnet params, 18 decimals)"
|
||||
"$BIN/igneumd" --testnet --netsuffix=1 --enable-unsynced-mining --appdir="$ROOT/a" --listen=127.0.0.1:$A_P2P --rpclisten=127.0.0.1:$A_RPC --evm-rpclisten=127.0.0.1:$A_EVM --outpeers=1 --loglevel=info > "$ROOT/a.log" 2>&1 &
|
||||
echo $! > "$ROOT/a.pid"
|
||||
sleep 3
|
||||
grep -m1 "Base unit" "$ROOT/a.log" || true
|
||||
if grep -q -E "refusing to start|panicked" "$ROOT/a.log"; then die "node A did not start: $(grep -m1 -E 'refusing|panicked' "$ROOT/a.log")"; exit 1; fi
|
||||
say "starting node B, connected to A"
|
||||
"$BIN/igneumd" --testnet --netsuffix=1 --enable-unsynced-mining --appdir="$ROOT/b" --listen=127.0.0.1:$B_P2P --rpclisten=127.0.0.1:$B_RPC --evm-rpclisten=127.0.0.1:$B_EVM --connect=127.0.0.1:$A_P2P --loglevel=info > "$ROOT/b.log" 2>&1 &
|
||||
echo $! > "$ROOT/b.pid"
|
||||
for i in $(seq 1 60); do
|
||||
if curl -s -m 2 -X POST -H 'content-type: application/json' --data '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' http://127.0.0.1:$A_EVM | grep -q result \
|
||||
&& curl -s -m 2 -X POST -H 'content-type: application/json' --data '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' http://127.0.0.1:$B_EVM | grep -q result; then break; fi
|
||||
sleep 2
|
||||
done
|
||||
if [ -n "$WORKER" ]; then
|
||||
say "nodes up; exporting the first pack from A, then mining $SECS s on the GPU worker $WORKER, payout to 0x$EVM_ADDR"
|
||||
mkdir -p "$ROOT/packs/first" "$ROOT/packs/prepare"
|
||||
"$BIN/igneum-miner" export-pack grpc://127.0.0.1:$A_RPC "$ROOT/packs/first" > "$ROOT/pack.log" 2>&1 || say "export-pack exit $? (the testnet address prefix; the miner prepares the pack itself)"
|
||||
"$BIN/igneum-miner" mine grpc://127.0.0.1:$A_RPC 1 "$SECS" gate --worker "$WORKER" --worker-args "--pack $ROOT/packs/first" --prepare-packs "$ROOT/packs/prepare" --network testnet --payout-label gate --evm-address "$EVM_ADDR" --dev-fee 0 --status-secs 60 > "$ROOT/miner.log" 2>&1 || say "miner exit $?"
|
||||
else
|
||||
say "nodes up; mining $SECS s with $THREADS CPU threads on A, payout to 0x$EVM_ADDR"
|
||||
nice -n 19 "$BIN/igneum-miner" mine grpc://127.0.0.1:$A_RPC "$THREADS" "$SECS" gate --engine igneum-pow --network testnet --payout-label gate --evm-address "$EVM_ADDR" --dev-fee 0 --status-secs 60 > "$ROOT/miner.log" 2>&1 || say "miner exit $?"
|
||||
fi
|
||||
sleep 5
|
||||
say "miner done; last status: $(grep -E "blocks|found|accepted" "$ROOT/miner.log" | tail -1 | cut -c1-200)"
|
||||
|
||||
say "check 1: both nodes agree"
|
||||
"$BIN/igneum-miner" watch 6 grpc://127.0.0.1:$A_RPC grpc://127.0.0.1:$B_RPC > "$ROOT/watch.log" 2>&1 || true
|
||||
tail -2 "$ROOT/watch.log" | cut -c1-240
|
||||
evm() { curl -s -m 5 -X POST -H 'content-type: application/json' --data "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"$2\",\"params\":$3}" "http://127.0.0.1:$1" ; }
|
||||
TIP_A=$(evm $A_EVM eth_blockNumber '[]' | jq -r .result); TIP_B=$(evm $B_EVM eth_blockNumber '[]' | jq -r .result)
|
||||
say "exec tips: A $TIP_A B $TIP_B"
|
||||
[ "$TIP_A" = "$TIP_B" ] || die "exec tips differ (A $TIP_A, B $TIP_B)"
|
||||
BLOCKS=$((TIP_A))
|
||||
[ "$BLOCKS" -ge "$MIN_BLOCKS" ] || die "only $BLOCKS chain blocks, wanted $MIN_BLOCKS"
|
||||
|
||||
say "check 3: the UTXO side over the last 30 blocks on both nodes"
|
||||
"$BIN/igneum-miner" inspect 30 grpc://127.0.0.1:$A_RPC grpc://127.0.0.1:$B_RPC > "$ROOT/inspect.log" 2>&1 || true
|
||||
grep -c "same_on_all_nodes=true" "$ROOT/inspect.log" | sed 's/^/ blocks identical on both nodes: /'
|
||||
if grep -q "MISMATCH" "$ROOT/inspect.log"; then die "80/20 mismatch: $(grep -m1 MISMATCH "$ROOT/inspect.log")"; fi
|
||||
if grep -q "same_on_all_nodes=false" "$ROOT/inspect.log"; then die "a block differs between the nodes"; fi
|
||||
python3 - "$ROOT/inspect.log" "${GATE_EXPECT_DECIMALS:-18}" <<'PY' || die "a coinbase payload subsidy is not the 18-decimal schedule (see above)"
|
||||
import re, sys
|
||||
decimals = int(sys.argv[2]); UNIT = 10 ** decimals
|
||||
launch_rate = 100 * UNIT; ramp_seconds = 90 * 86400; start = 10; bps = 1
|
||||
def subsidy(daa):
|
||||
s = daa // bps; full = launch_rate // bps
|
||||
return full if s >= ramp_seconds else full * (start * ramp_seconds + (100 - start) * s) // (100 * ramp_seconds)
|
||||
n = bad = 0
|
||||
for line in open(sys.argv[1]):
|
||||
m = re.search(r" daa=(\d+) .*subsidy_in_payload=(\d+)", line)
|
||||
if not m: continue
|
||||
n += 1; daa, got = int(m.group(1)), int(m.group(2))
|
||||
if got != subsidy(daa):
|
||||
bad += 1
|
||||
if bad <= 3: print(f" daa {daa}: payload subsidy {got} != schedule {subsidy(daa)}")
|
||||
print(f" payload subsidies checked {n}, wrong {bad}; one IGN = {UNIT}; day-0 block = {subsidy(0)} base units")
|
||||
assert n > 0 and bad == 0
|
||||
PY
|
||||
tail -1 "$ROOT/inspect.log" | cut -c1-200
|
||||
|
||||
say "check 4 and 5: the execution layer, both nodes, against the schedule"
|
||||
BAL_A=$(evm $A_EVM eth_getBalance "[\"0x$EVM_ADDR\",\"latest\"]" | jq -r .result); BAL_B=$(evm $B_EVM eth_getBalance "[\"0x$EVM_ADDR\",\"latest\"]" | jq -r .result)
|
||||
say "balances: A $BAL_A B $BAL_B"
|
||||
[ "$BAL_A" = "$BAL_B" ] || die "balances differ between the nodes"
|
||||
: > "$ROOT/segments.jsonl"
|
||||
for n in $(seq 1 "$BLOCKS"); do evm $A_EVM igneum_getSegment "[\"$(printf '0x%x' "$n")\"]" >> "$ROOT/segments.jsonl"; echo >> "$ROOT/segments.jsonl"; done
|
||||
python3 - "$ROOT/segments.jsonl" "$BAL_A" "$EVM_ADDR" "${GATE_EXPECT_DECIMALS:-18}" <<'PY' || die "the execution layer does not match the schedule (see above)"
|
||||
import json, sys
|
||||
path, bal_hex, addr, decimals = sys.argv[1], sys.argv[2], sys.argv[3].lower(), int(sys.argv[4])
|
||||
UNIT = 10 ** decimals
|
||||
# the testnet schedule (EmissionSchedule::TESTNET_1 rescaled to the unit): 100 IGN a second, a 90-day ramp from 10 percent,
|
||||
# the first monthly glide step is far beyond a ten-minute gate
|
||||
launch_rate = 100 * UNIT; ramp_seconds = 90 * 86400; start = 10; bps = 1
|
||||
def ramp(full, s):
|
||||
if s >= ramp_seconds: return full
|
||||
return full * (start * ramp_seconds + (100 - start) * s) // (100 * ramp_seconds)
|
||||
def subsidy(daa): return ramp(launch_rate // bps, daa // bps)
|
||||
def producer(a):
|
||||
q, r = divmod(a, 100); pool = q * 20 + r * 20 // 100
|
||||
return a - pool
|
||||
total = 0; checked = 0; bad = 0
|
||||
for line in open(path):
|
||||
line = line.strip()
|
||||
if not line: continue
|
||||
r = json.loads(line).get("result")
|
||||
if not r:
|
||||
print(" segment query failed:", line[:120]); bad += 1; continue
|
||||
chain_hash = r["hash"]
|
||||
daa = next((int(m["daaScore"], 16) if isinstance(m["daaScore"], str) else int(m["daaScore"]) for m in r["mergeset"] if m["hash"] == chain_hash), None)
|
||||
if daa is None:
|
||||
print(f" segment {r['number']}: the chain block is not in its own mergeset list"); bad += 1; continue
|
||||
expect = producer(subsidy(daa))
|
||||
blues = [m for m in r["mergeset"] if m["blue"]]
|
||||
for w in r["rewards"]:
|
||||
wei = int(w["wei"], 16) if isinstance(w["wei"], str) else int(w["wei"])
|
||||
checked += 1
|
||||
if wei != expect:
|
||||
bad += 1
|
||||
if bad <= 3: print(f" segment {r['number']}: reward {wei} != expected {expect} (daa {daa})")
|
||||
if w["miner"].lower().removeprefix("0x") == addr: total += wei
|
||||
bal = int(bal_hex, 16)
|
||||
print(f" rewards checked {checked}, wrong {bad}; sum of our rewards {total}; eth_getBalance {bal}; one IGN = {UNIT}")
|
||||
print(f" balance in IGN (8 visible digits): {bal // UNIT}.{(bal % UNIT) // (UNIT // 10**8):08d}")
|
||||
assert bad == 0, "a reward disagrees with the schedule"
|
||||
assert total == bal, "the balance is not the sum of the rewards (the bridge is not the identity)"
|
||||
assert bal > 18446744073709551615, "the balance fits a u64: not an 18-decimal chain"
|
||||
PY
|
||||
|
||||
if [ "$fail" = 0 ]; then say "PASS: two nodes at 18 decimals, $BLOCKS chain blocks, the coinbase, the gRPC and the execution layer read one number"; else say "FAIL (see above)"; exit 1; fi
|
||||
Loading…
Reference in a new issue