Skip to content

fix(genesis-tool): close 4 medium audit findings (#533–#536)#102

Merged
ByteYue merged 1 commit into
mainfrom
fix/genesis-tool-audit-533-536
May 18, 2026
Merged

fix(genesis-tool): close 4 medium audit findings (#533–#536)#102
ByteYue merged 1 commit into
mainfrom
fix/genesis-tool-audit-533-536

Conversation

@ByteYue
Copy link
Copy Markdown
Contributor

@ByteYue ByteYue commented May 18, 2026

Summary

Closes four medium-severity findings from the Galxe/gravity-audit batch against the genesis-tool crate. Each fix converts a silent / heuristic failure mode into a loud, fail-closed invariant so a misconfigured input cannot ship a broken genesis through CI.

Audit issue Fix
Galxe/gravity-audit#533 — votingPower decoupled from stake_amount at genesis Assert voting_power == stake_amount per validator in convert_config_to_sol (genesis.rs) — ValidatorManagement.initialize bypasses _validateRegistration, so this tool is the only line of defense.
Galxe/gravity-audit#534extract_runtime_bytecode no-op heuristic Rename to decode_runtime_bytecode, drop the dead 0x60/0x61 branch + noisy WARN (both arms returned the same bytes; all Solidity runtime starts with 60 80 60 40, so the heuristic could not distinguish constructor from runtime). Document the caller contract: must supply deployedBytecode.object (already done by scripts/helpers/extract_bytecode.py).
Galxe/gravity-audit#535run_generate exits Ok(()) on verify failure Plumb Result<(), String> through print_active_validators_resulthandle_execution_result (now takes a fallible success_handler) → verify_resultrun_generate, so a validator-count mismatch becomes a non-zero exit instead of a buried error log.
Galxe/gravity-audit#536parse_u256_hex silently returns zero on parse failure Return Result<U256> and propagate at all three callsites with context naming the offending address/key. A malformed balance/storage value now fails verification instead of silently zeroing the account.

Notes

  • The extract_runtime_bytecode heuristic in #534 fired on every legitimate run (all .hex artifacts start with 60 80 60 40), but both branches of the if/else returned the same bytes, so this was log noise rather than a behavior bug. Current pipeline (via scripts/helpers/extract_bytecode.py reading deployedBytecode.object) was never affected; the rename + comment makes the caller contract explicit so a future pipeline change cannot resurrect the trap.
  • The signature change for handle_execution_result (success_handler now returns Result<(), String>) is internal — print_active_validators_result is its only external caller.
  • The #536 fix preserves the existing hex-only format expectation; if reth-standard decimal balances are also intended to be accepted, that is a separate scope (currently the generator also writes hex, so they're consistent).

Test plan

  • cargo check -p genesis-tool — clean (warnings are pre-existing).
  • cargo build -p genesis-tool — clean.
  • cargo test -p genesis-tool — 0 tests, all pass (no existing tests in the crate).
  • Manual smoke: run genesis-tool generate against the current new_genesis_config.json and confirm exit 0 (happy path unchanged).
  • Manual smoke: deliberately set voting_power != stake_amount for one validator → expect panic from #533 assertion.
  • Manual smoke: feed verify a genesis.json with a decimal balance string → expect non-zero exit citing the offending address (was: silent success).

🤖 Generated with Claude Code

Address four medium-severity findings against `genesis-tool` from the
Galxe/gravity-audit batch. Each fix converts a silent / heuristic
failure mode into a loud, fail-closed invariant so a misconfigured
input cannot ship a broken genesis through CI.

#533 — convert_config_to_sol (genesis.rs):
  votingPower and stakeAmount were two independent attacker- (or
  operator-) controlled fields in GenesisConfig, written verbatim into
  SolInitialValidator. ValidatorManagement.initialize deliberately
  bypasses _validateRegistration (no PoP / minimumBond check), so this
  tool was the only line of defense against a validator booted with
  consensus weight decoupled from staked bond. Assert
  votingPower == stake_amount per validator and abort with a clear
  diagnostic naming the offending operator/moniker.

#534 — extract_runtime_bytecode (execute.rs):
  Misleading name and useless heuristic: the function "extracted"
  nothing — it hex-decoded the input and, if the first byte was
  0x60/0x61 and len > 100, emitted a WARN and returned bytes unchanged
  (both arms returned `bytes`). All Solidity runtime bytecode begins
  with `60 80 60 40` (free-memory-pointer prologue), so the WARN fired
  on every legitimate run and could not distinguish constructor from
  runtime bytecode. Rename to decode_runtime_bytecode, drop the dead
  branch, and document that callers must supply
  `deployedBytecode.object` (per scripts/helpers/extract_bytecode.py).

#535 — print_active_validators_result + verify_result (genesis.rs,
post_genesis.rs, main.rs):
  Validator-count mismatch only logged `error!` and returned `()`;
  verify_result `.expect`-panicked but print_active_validators_result
  itself swallowed the failure inside `let _ = handle_execution_result`.
  Make the leaf return Result<(), String>, lift handle_execution_result
  to take a fallible success_handler, and propagate through verify_result
  → run_generate via `?` so a mismatch becomes a non-zero exit instead
  of a buried log line in CI.

#536 — parse_u256_hex (verify.rs):
  Returned U256::ZERO on parse failure, masking malformed balances and
  storage values in the alloc the `verify` subcommand is supposed to
  independently police. Return Result, propagate at the three callsites
  with context naming the offending address/key, so a decimal-or-typo'd
  balance fails verification rather than silently zeroing the account.

cargo check / cargo test: clean (pre-existing warnings unchanged).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ByteYue ByteYue merged commit 47e7b91 into main May 18, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant