Skip to content

fix: refresh IdentityBeacon bytecode (constructor was skipping BindingLib.bindComponent)#60

Open
koko1123 wants to merge 3 commits into
mainfrom
koko/refresh-identity-bytecode
Open

fix: refresh IdentityBeacon bytecode (constructor was skipping BindingLib.bindComponent)#60
koko1123 wants to merge 3 commits into
mainfrom
koko/refresh-identity-bytecode

Conversation

@koko1123

@koko1123 koko1123 commented May 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Every beacon created via POST /create_beacon_with_ecdsa on mainnet was deployed with verifier.authorizedCaller == address(0). The ECDSAVerifier's verify() starts with _checkAuthorizedCaller(), so every subsequent update() call from the beacon reverts with UnauthorizedCaller (selector 0x5c427cd9) — the beaconator surfaces that as HTTP 500 Internal Server Error to the updater.

Confirmed on Arbitrum One for the two Hormuz beacons created today:

beacon verifier authorizedCaller
0x1b37de2b…ef884 (count) 0x930E46b…6BF83 0x0 ← bug
0x853f7ce…b55152 (tonnage) 0x7e720E0…53ccc 0x0 ← bug

Root cause

abis/IdentityBeacon.bytecode was generated before BindingLib landed in beacons (commit "add caller binding (#59)", which shipped in tag v0.0.1). IdentityBeacon's constructor at v0.0.1 ends with:

BindingLib.bindComponent(address(_verifier));

…which binds the verifier to the new beacon atomically as part of the create tx. But the beaconator's create_identity_beacon raw-deploys bytecode from state.contracts.identity_beacon_bytecode — which was the pre-BindingLib build (file mtime Mar 2). The constructor in that older bytecode never calls bindComponent, so the verifier sits with authorizedCaller = 0x0 forever.

Fix

  • Regenerate abis/IdentityBeacon.bytecode from beacons@v0.0.1. Size went from 7994 → 8665 bytes; the delta is the BindingLib.bindComponent (staticcall to authorizedCaller, conditional bind() call) at the end of the constructor.
  • Extend scripts/refresh-abis.sh to write the deploy-time bytecode in the same pass as the ABIs. Adds an inspect_bytecode_to helper alongside inspect_abi_to and refactors the per-repo worktree setup so each worktree is created exactly once. This way a future beacons-side change can't silently desync the bytecode again.

Effect

After this lands and the beaconator is rebuilt + redeployed, every new beacon created via /create_beacon_with_ecdsa is correctly bound at deploy time. No application code changes (the bytecode is loaded from disk at startup into AppState.contracts.identity_beacon_bytecode).

Test plan

  • make fmt clean
  • make lint clean
  • make test-fast clean
  • make refresh-abis reproducible on a clean clone (script ran end-to-end, produced expected file deltas)
  • Deploy this branch to the mainnet beaconator, create one fresh beacon via /create_beacon_with_ecdsa, confirm verifier.authorizedCaller() returns the new beacon address — not 0x0.

Existing Hormuz beacons (not in this PR)

The two beacons already deployed on mainnet (count 0x1b37de2b…, tonnage 0x853f7ce…) need a one-time manual bind() to recover them. bind() has no access control beyond "not already bound", so any wallet with Arbitrum ETH can run two cast send calls. Handled out-of-band — not blocked by this PR.

Follow-up worth considering (not in this PR)

create_identity_beacon should probably also confirm the binding after deploy (one eth_call to verifier.authorizedCaller() matched against the new beacon address) so any future bytecode-vs-source desync is caught at create time, not on the next update(). Happy to ship that in a follow-up if you want it.

Summary by CodeRabbit

  • Chores

    • Updated IdentityBeacon contract bytecode to a fresh build.
    • Improved artifact generation tooling to produce ABIs and bytecode more reliably using reusable worktrees and atomic writes.
  • Tests

    • Added regression tests that validate the on-disk IdentityBeacon bytecode has expected structure and includes required constructor selectors.

…tructor calls BindingLib.bindComponent

Every beacon created via /create_beacon_with_ecdsa on mainnet has been
deployed with verifier.authorizedCaller == address(0), so the verifier
rejects every update with UnauthorizedCaller (0x5c427cd9). Confirmed
on-chain for the two Hormuz beacons created 2026-05-29:

  0x1b37de2b…ef884 (count)   verifier 0x930E46b…6BF83  authorizedCaller=0x0
  0x853f7ce…b55152 (tonnage) verifier 0x7e720E0…53ccc  authorizedCaller=0x0

Root cause: abis/IdentityBeacon.bytecode was generated before BindingLib
landed in beacons (commit 'add caller binding (#59)' in the v0.0.1 tag).
The IdentityBeacon constructor at v0.0.1 calls
`BindingLib.bindComponent(_verifier)` as its final step, which binds the
verifier to the new beacon atomically. But the beaconator's
create_identity_beacon raw-deploys bytecode from
state.contracts.identity_beacon_bytecode, which was the pre-BindingLib
build (Mar 2). Re-deploying with that bytecode skips the bind entirely.

Fix:
  - Regenerate abis/IdentityBeacon.bytecode from beacons@v0.0.1 (7994 ->
    8665 bytes, the delta is the BindingLib staticcall + bind call).
  - Extend scripts/refresh-abis.sh to write the deploy-time bytecode in
    the same pass as the ABIs, so a future beacons-side change can't
    silently desync the bytecode again. Adds a generic inspect_bytecode_to
    helper next to inspect_abi_to.

After this lands and the beaconator is rebuilt + redeployed every NEW
beacon created via /create_beacon_with_ecdsa will be correctly bound at
deploy time. The two existing Hormuz beacons are being fixed manually
out-of-band with two cast send bind(beacon) calls — that's not blocked
by this PR.

Tested: make fmt + make lint + make test-fast all green.
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: be3dfba9-abaa-420f-95d2-df1c02126880

📥 Commits

Reviewing files that changed from the base of the PR and between 820113e and 0208b71.

📒 Files selected for processing (1)
  • tests/unit_tests/identity_beacon_bytecode_tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit_tests/identity_beacon_bytecode_tests.rs

📝 Walkthrough

Walkthrough

refresh-abis.sh is refactored to create reusable detached worktrees and modular inspect helpers; the script regenerates ABIs and IdentityBeacon deploy-time bytecode. New Rust unit tests validate the on-disk IdentityBeacon.bytecode artifact.

Changes

ABI and Bytecode Refresh Tooling

Layer / File(s) Summary
Worktree setup helper
scripts/refresh-abis.sh
Adds setup_worktree to create detached git worktrees at pinned tags and initialize submodules for reuse.
Inspect helpers and atomic write
scripts/refresh-abis.sh
Replaces previous inspect flow with inspect_to(wt, contract, what, out, label) that runs forge inspect in a worktree and writes output atomically; adds inspect_abi_to and inspect_bytecode_to wrappers.
Orchestrated worktree reuse and artifact generation
scripts/refresh-abis.sh
Main execution now creates/reuses one worktree per repo (beacons + perpcity-contracts) and uses the new helpers to export ABIs and IdentityBeacon.bytecode.
IdentityBeacon bytecode replaced
abis/IdentityBeacon.bytecode
The on-disk IdentityBeacon.bytecode file is replaced with a new compiled deploy-time bytecode blob (different hex and embedded metadata).
Unit tests validating bytecode artifact
tests/unit_tests/identity_beacon_bytecode_tests.rs, tests/unit_tests/mod.rs
Adds tests that load and hex-decode abis/IdentityBeacon.bytecode, assert contract-creation shape and presence of selectors for authorizedCaller() and bind(address), and registers the test module in the test manifest.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I dug a worktree, neat and small,
Forked the forge to answer the call.
Bytecode fresh in a shiny file,
Tests hop in to check each mile.
Hooray — our artifacts compile! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and specifically describes the main fix: regenerating IdentityBeacon bytecode to include the missing BindingLib.bindComponent call in the constructor.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch koko/refresh-identity-bytecode

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/refresh-abis.sh`:
- Around line 105-106: The ABI/bytecode writes are currently redirected straight
into "$ABIS_DIR/$out" which can leave empty/partial files if `forge inspect`
fails (with set -e). Change each direct redirection (the `( cd "$wt" && forge
inspect "$contract" ... ) > "$ABIS_DIR/$out"` and the corresponding bytecode
line) to write to a temporary file (e.g. "$ABIS_DIR/$out.tmp.$$"), run the `( cd
"$wt" && forge inspect ... )` into that temp, check success, then mv the temp
into "$ABIS_DIR/$out"; ensure the temp is removed on failure to avoid orphan
files. This preserves atomicity for the commands that reference "$wt",
"$contract" and "$ABIS_DIR/$out".
- Around line 94-96: The git config token in scripts/refresh-abis.sh uses mixed
quoting which triggers ShellCheck SC2140; replace the mixed-quote -c
url."https://github.com/".insteadOf="git@github.com:" argument with a
single-quoted key=value token, e.g. change the git invocation (the line using
git -C "$wt" -c ...) to use -c
'url."https://github.com/".insteadOf=git@github.com:' so the entire config is
one argument and SC2140 is resolved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: feafdb9d-792b-49b9-887c-5582b0407560

📥 Commits

Reviewing files that changed from the base of the PR and between a9c118f and 6fd11be.

📒 Files selected for processing (2)
  • abis/IdentityBeacon.bytecode
  • scripts/refresh-abis.sh

Comment thread scripts/refresh-abis.sh
Comment thread scripts/refresh-abis.sh Outdated
Addresses CodeRabbit's two findings on #60:

- scripts/refresh-abis.sh: ShellCheck SC2140 — the inline `git -c`
  key=value argument with double-quote sandwiches now passes through
  as a single-quoted string ('url.https://github.com/.insteadOf=git@github.com:'),
  which both quiets the linter and removes the double-quote-inside-double-quote
  edge case in the original form.
- scripts/refresh-abis.sh: `forge inspect ... > $ABIS_DIR/$out`
  truncated the artefact before forge ran, so a failed inspect could
  silently leave a partial file in abis/. Both inspect helpers now share
  a single `inspect_to` core that streams into a mktemp under $ABIS_DIR
  and atomically mv's it into place on success only.

Adds tests/unit_tests/identity_beacon_bytecode_tests.rs to lock in
exactly the regression this PR fixes:

- identity_beacon_bytecode_is_non_trivial_creation_code asserts the
  file is on the order of 4-5 KiB and starts with PUSH1, so a future
  atomic-write hiccup leaving a truncated artefact in tree gets caught
  at `make test-fast` time instead of in production.
- identity_beacon_constructor_calls_bindcomponent reads the on-disk
  bytecode and searches it for the two selectors BindingLib needs:
  authorizedCaller() (0xfaa7d4b0) and bind(address) (0x0a3b0a4f).
  Both must be present as PUSH4 immediates in the constructor for the
  beacon to bind its verifier at deploy time. The selectors are derived
  via keccak256 at test time so the assertion stays canonical even if
  some renaming happens on the contracts side.

Tested locally: make fmt + make lint + the two new tests all green.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/refresh-abis.sh (1)

70-80: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cleanup logic never executes; worktrees leak.

Line 72 checks -d "$wt", but $wt is the composite string "repo::path" (e.g., "../beacons::/tmp/beacons-v0.0.1-xyz"), which is never a valid directory. The condition always fails, so git worktree remove never runs and temporary worktrees accumulate in /tmp.

🔧 Proposed fix

Extract repo and path before the directory check:

 cleanup() {
   for wt in "${WORKTREES[@]:-}"; do
-    if [[ -n "$wt" && -d "$wt" ]]; then
-      # First arg is the repo, second is the worktree path.
-      local repo="${wt%%::*}"
-      local path="${wt##*::}"
+    local repo="${wt%%::*}"
+    local path="${wt##*::}"
+    if [[ -n "$path" && -d "$path" ]]; then
       git -C "$repo" worktree remove "$path" --force >/dev/null 2>&1 || true
     fi
   done
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/refresh-abis.sh` around lines 70 - 80, The cleanup function is
checking -d on the composite WORKTREES entry (variable wt like "repo::path") so
the condition always fails; change cleanup() to first split each wt into
repo="${wt%%::*}" and path="${wt##*::}" and then test -n "$path" and -d "$path"
(or -e if you prefer) before calling git -C "$repo" worktree remove "$path"
--force, preserving the existing git command and the loop over WORKTREES.
🧹 Nitpick comments (1)
scripts/refresh-abis.sh (1)

120-120: ⚡ Quick win

Prefer word-splitting over eval for the forge command.

The eval on line 120 works correctly but is unnecessary. Since $what is always a hardcoded string ("abi --json" or "bytecode"), you can rely on unquoted word-splitting instead:

if ( cd "$wt" && forge inspect "$contract" $what ) > "$tmp"; then

This is simpler, avoids the nested quoting complexity, and eliminates a potential future ShellCheck SC2086 warning.

♻️ Proposed simplification
-  if ( cd "$wt" && eval "forge inspect \"$contract\" $what" ) > "$tmp"; then
+  # shellcheck disable=SC2086
+  if ( cd "$wt" && forge inspect "$contract" $what ) > "$tmp"; then

(The disable=SC2086 acknowledges intentional word-splitting of $what.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/refresh-abis.sh` at line 120, Replace the unnecessary eval in the
subshell that runs the Forge inspection: instead of using eval "forge inspect
\"$contract\" $what" use plain word-splitting so the line becomes ( cd "$wt" &&
forge inspect "$contract" $what ) > "$tmp"; this relies on $what being a
hardcoded token like "abi --json" or "bytecode"; if you want to document the
intentional word-splitting for linters, add a nearby shellcheck disable comment
for SC2086.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit_tests/identity_beacon_bytecode_tests.rs`:
- Around line 46-48: Update the error message string in the
identity_beacon_bytecode_tests.rs test that currently says "expected creation
code on the order of 4-5 KiB" so it reflects the actual bytecode size (~8665
bytes); change it to something like "expected creation code on the order of 8-9
KiB (≈8.7 KiB / 8665 bytes)" so the formatted message emitted by the test
matches the PR's bytecode size expectation.

---

Outside diff comments:
In `@scripts/refresh-abis.sh`:
- Around line 70-80: The cleanup function is checking -d on the composite
WORKTREES entry (variable wt like "repo::path") so the condition always fails;
change cleanup() to first split each wt into repo="${wt%%::*}" and
path="${wt##*::}" and then test -n "$path" and -d "$path" (or -e if you prefer)
before calling git -C "$repo" worktree remove "$path" --force, preserving the
existing git command and the loop over WORKTREES.

---

Nitpick comments:
In `@scripts/refresh-abis.sh`:
- Line 120: Replace the unnecessary eval in the subshell that runs the Forge
inspection: instead of using eval "forge inspect \"$contract\" $what" use plain
word-splitting so the line becomes ( cd "$wt" && forge inspect "$contract" $what
) > "$tmp"; this relies on $what being a hardcoded token like "abi --json" or
"bytecode"; if you want to document the intentional word-splitting for linters,
add a nearby shellcheck disable comment for SC2086.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68514573-0efc-4f9a-936d-6be5ae1da63b

📥 Commits

Reviewing files that changed from the base of the PR and between 6fd11be and 820113e.

📒 Files selected for processing (3)
  • scripts/refresh-abis.sh
  • tests/unit_tests/identity_beacon_bytecode_tests.rs
  • tests/unit_tests/mod.rs

Comment thread tests/unit_tests/identity_beacon_bytecode_tests.rs Outdated
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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