Skip to content

Stop baking absolute mise path into validation file cmd field#705

Merged
leopoldjoy merged 2 commits into
mainfrom
leopold/fix-multisig-mk-absolute-mise-path
May 28, 2026
Merged

Stop baking absolute mise path into validation file cmd field#705
leopoldjoy merged 2 commits into
mainfrom
leopold/fix-multisig-mk-absolute-mise-path

Conversation

@leopoldjoy
Copy link
Copy Markdown
Contributor

@leopoldjoy leopoldjoy commented May 28, 2026

Problem

PR #704 wrapped toolchain invocations with $(MISE_EXEC), which resolves at Makefile-parse time to either:

  • mise exec -- — when mise is on PATH, or
  • $(HOME)/.local/bin/mise exec -- — fallback when it is not.

The fallback case bakes a machine-specific absolute path into the cmd field of every committed validation JSON via:

  1. Multisig.mk's GEN_VALIDATION macro (used by most current tasks), and
  2. setup-templates/template-set-bridge-partner-threshold/Makefile's gen-validation target (which writes validations/signer.json via state-diff).

Both write a forge command into a JSON that is committed to git and hashed by the task-signing-tool at verification time. A validation file generated on machine A (with $HOME=/Users/alice) will fail signature verification on machine B (with $HOME=/Users/bob). This was discovered while finalising mainnet/2026-05-27-update-aggregate-verifier — three task-origin signatures had to be regenerated because the committed validation files contained the original signer's home directory.

The execution macros MULTISIG_APPROVE and MULTISIG_EXECUTE, and the inline $(MISE_EXEC) usages in other templates (template-gas-increase, template-pause-bridge-base, template-pause-superchain-config, template-upgrade-fault-proofs, template-safe-management), are unaffected because their $(MISE_EXEC) is only used inside the make recipe shell — nothing is written to disk.

Fix

  1. Multisig.mk: in GEN_VALIDATION's embedded --forge-cmd, replace $(MISE_EXEC) with the bare literal mise exec --. The generated JSON is now portable across machines. Added a NOTE comment explaining the trade-off.
  2. setup-templates/template-set-bridge-partner-threshold/Makefile: same fix in the template's inline state-diff invocation.
  3. Makefile: extend bootstrap-mise with a command -v mise check that prints a clear warning + remediation when mise is installed at $HOME/.local/bin/mise but is not on the user's PATH. The signer-tool re-executes the validation file's cmd in a fresh shell that does not inherit the Makefile's $(MISE) resolution, so mise must be discoverable via PATH for the re-execution to work.
  4. README.md: document the PATH requirement in the existing "Toolchain (mise)" section.

Why keep mise exec at all (i.e. why not strip it entirely)?

mise exec is what isolates this repo's pinned Foundry/Node/Go versions from any system-level foundryup or global toolchain installs. Stripping it would risk version drift between signers. The pattern we want is: the validation file carries the machine-agnostic token mise exec --, and each signer's environment ensures mise is resolvable.

Verification

  • make -n bootstrap-mise parses cleanly.
  • The signer-tool subprocess sees mise exec -- forge script ... in the generated cmd, with no absolute paths.
  • Other templates that use $(MISE_EXEC) only inside recipe shells (not embedded in JSON output) are intentionally left unchanged.

Follow-ups

This will be the basis for the two upcoming testnet tasks (Sepolia + Zeronet aggregate-verifier hash updates). Both will inherit the fixed GEN_VALIDATION behaviour from main once this lands.

PR #704 wraps toolchain calls with $(MISE_EXEC), which resolves at
Makefile-parse time to either 'mise exec --' (when mise is on PATH) or
the absolute fallback '$(HOME)/.local/bin/mise exec --' (when it is not).
The execution macros above GEN_VALIDATION (MULTISIG_APPROVE,
MULTISIG_EXECUTE) only use that expansion inside the make recipe shell,
so the absolute fallback is harmless. GEN_VALIDATION is different: the
embedded --forge-cmd string is written verbatim into the validation
JSON's 'cmd' field, which is then committed to git and re-hashed at
verification time on other machines. Baking an absolute, user-specific
path into that file made committed validation files non-portable and
caused signature-verification failures whenever the verifier's $HOME
differed from the signer's.

Fix:

- Multisig.mk: replace $(MISE_EXEC) with the bare literal 'mise exec --'
  inside GEN_VALIDATION's --forge-cmd. The generated JSON is now portable.
- Makefile: extend bootstrap-mise with a PATH check that prints a clear
  warning + remediation when 'mise' is installed but not on PATH, since
  the signer-tool re-execs the cmd in a fresh shell that does not inherit
  the Makefile's $(MISE) resolution.
- README.md: document the PATH requirement in the existing Toolchain
  section.
@cb-heimdall
Copy link
Copy Markdown
Collaborator

cb-heimdall commented May 28, 2026

✅ Heimdall Review Status

Requirement Status More Info
Reviews 2/2
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 2
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 2
2
1 if commit is unverified 0
Sum 2

@leopoldjoy leopoldjoy requested a review from jackchuma May 28, 2026 16:07
The template's gen-validation target uses state-diff to write
validations/signer.json, embedding a forge command that contained
$(MISE_EXEC). Same bug as Multisig.mk's GEN_VALIDATION: the absolute
path resolved at Makefile-parse time gets baked into the committed
JSON, breaking signature verification on other machines. Replace with
the bare literal 'mise exec --'.
@leopoldjoy leopoldjoy requested a review from 0x00101010 May 28, 2026 16:16
Copy link
Copy Markdown
Contributor

@jackchuma jackchuma left a comment

Choose a reason for hiding this comment

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

How about we just exclude mise exec from gen validation commands altogether? We can trust that the internal engineers running these commands have mise working

@cb-heimdall
Copy link
Copy Markdown
Collaborator

Review Error for jackchuma @ 2026-05-28 16:45:27 UTC
User failed mfa authentication, see go/mfa-help

@leopoldjoy
Copy link
Copy Markdown
Contributor Author

How about we just exclude mise exec from gen validation commands altogether? We can trust that the internal engineers running these commands have mise working

Agreed! But doesn't this new solution also work on that assumption?

@leopoldjoy leopoldjoy merged commit 0b13931 into main May 28, 2026
14 checks passed
@leopoldjoy leopoldjoy deleted the leopold/fix-multisig-mk-absolute-mise-path branch May 28, 2026 17:03
leopoldjoy added a commit that referenced this pull request May 28, 2026
Two validation files generated by the signer-tool for the nested
PROXY_ADMIN_OWNER -> {CB_MULTISIG, BASE_SECURITY_COUNCIL} multisig:

- base-signer.json: CB Coordinator Safe approve path
- base-signer-2.json: Mock OP / Mock Security Council approve path

Post-processing applied to each file:

- Stripped absolute mise path from 'cmd' field ($HOME-specific
  /Users/.../.local/bin/mise -> bare 'mise exec --'), keeping the
  committed JSON portable across signer machines. Same fix as PR #705
  applies upstream to Multisig.mk.
- Filled in placeholder descriptions:
  * PROXY_ADMIN_OWNER approvedHashes slot: 'Approves the transaction
    hash for execution by the nested multisig.'
  * DGF gameImpls[621] slot: 'Updates the game type 621 implementation
    in the DisputeGameFactory from the old AggregateVerifier to the
    new AggregateVerifier with updated TEE_IMAGE_HASH, ZK_RANGE_HASH,
    and ZK_AGGREGATE_HASH.'
- Disabled task-origin validation: removed 'taskOriginConfig' block,
  added 'skipTaskOriginValidation: true' (this task does not ship
  task-origin signatures).
leopoldjoy added a commit that referenced this pull request May 28, 2026
Two validation files generated by the signer-tool for the nested
PROXY_ADMIN_OWNER -> {CB_MULTISIG, BASE_SECURITY_COUNCIL} multisig:

- coinbase-signer.json: CB Signer Safe approve path
- security-council-signer.json: Security Council Safe approve path

Filename naming follows the mainnet pattern (signer-tool renders it as
the role label) rather than the May-18 testnet precedent's
'base-signer{,-2}.json'. Makefile gen-validation-* targets and
FACILITATOR.md updated to match.

Post-processing applied to each file:

- Stripped absolute mise path from 'cmd' field ($HOME-specific
  /Users/.../.local/bin/mise -> bare 'mise exec --'), keeping the
  committed JSON portable across signer machines. Same fix as PR #705
  applies upstream to Multisig.mk.
- Filled in placeholder descriptions:
  * DGF gameImpls[621] slot: 'Updates the game type 621 implementation
    in the DisputeGameFactory from the old AggregateVerifier to the
    new AggregateVerifier with updated TEE_IMAGE_HASH, ZK_RANGE_HASH,
    and ZK_AGGREGATE_HASH.'
  * PROXY_ADMIN_OWNER approvedHashes slot: 'Approves the transaction
    hash for execution by the nested multisig.'
- Disabled task-origin validation: removed 'taskOriginConfig' block,
  added 'skipTaskOriginValidation: true' (this task does not ship
  task-origin signatures).
leopoldjoy added a commit that referenced this pull request May 29, 2026
* Sepolia: upgrade ZK and TEE hash (2026-05-28)

Mirrors mainnet/2026-05-27-update-aggregate-verifier on sepolia so the
testnet exercises the same TEE image + ZK range program ahead of mainnet
rollout. Follows the existing sepolia/2026-05-18-upgrade-zk-and-tee-hash
template verbatim; only the three hash values in .env change:

- TEE_IMAGE_HASH:   updated to mainnet target
- ZK_RANGE_HASH:    updated to mainnet target
- ZK_AGGREGATE_HASH: unchanged from previous testnet deployment (set
                     explicitly for clarity; mainnet target equals it)

All other AggregateVerifier immutables (DELAYED_WETH, TEE_VERIFIER,
ZK_VERIFIER, ANCHOR_STATE_REGISTRY, CONFIG_HASH, BLOCK_INTERVAL,
INTERMEDIATE_BLOCK_INTERVAL, L2_CHAIN_ID, GAME_TYPE) are read from the
existing on-chain AggregateVerifier at deploy/upgrade time, so no env
plumbing is required for them.

Task does not ship task-origin signatures; FACILITATOR.md instructs the
facilitator to set 'skipTaskOriginValidation: true' on each generated
validation file before committing.

* Sepolia: commit AggregateVerifier deploy artefacts

Deployed new AggregateVerifier at 0xeCe9c5b9DCa09f1a0Ed85DA97f8F1396dC5634Ce
on sepolia in tx 0xaef9dc24...07ac4 (block 10941556, Etherscan-verified).

- addresses.json: aggregateVerifier populated
- README.md: Transactions section added with explorer + artefact links
- records/DeployAggregateVerifier.s.sol/11155111/run-1779990011323.json:
  forge-broadcast artefact

* Sepolia: add validation files (task-origin validation disabled)

Two validation files generated by the signer-tool for the nested
PROXY_ADMIN_OWNER -> {CB_MULTISIG, BASE_SECURITY_COUNCIL} multisig:

- base-signer.json: CB Coordinator Safe approve path
- base-signer-2.json: Mock OP / Mock Security Council approve path

Post-processing applied to each file:

- Stripped absolute mise path from 'cmd' field ($HOME-specific
  /Users/.../.local/bin/mise -> bare 'mise exec --'), keeping the
  committed JSON portable across signer machines. Same fix as PR #705
  applies upstream to Multisig.mk.
- Filled in placeholder descriptions:
  * PROXY_ADMIN_OWNER approvedHashes slot: 'Approves the transaction
    hash for execution by the nested multisig.'
  * DGF gameImpls[621] slot: 'Updates the game type 621 implementation
    in the DisputeGameFactory from the old AggregateVerifier to the
    new AggregateVerifier with updated TEE_IMAGE_HASH, ZK_RANGE_HASH,
    and ZK_AGGREGATE_HASH.'
- Disabled task-origin validation: removed 'taskOriginConfig' block,
  added 'skipTaskOriginValidation: true' (this task does not ship
  task-origin signatures).

* Sepolia: rename validation files for human-readable signer-tool labels

base-signer.json -> coinbase-signer.json (CB Multisig path)
base-signer-2.json -> security-council-signer.json (Base Security Council path)

Filename is the role label shown in the signer-tool UI; 'base-signer-2'
gives signers zero hint about which nested-safe path they are on.
Mirrors mainnet/2026-05-27-update-aggregate-verifier naming convention
and AGENTS.md guidance ('Always name the validation file(s) something
simple ... [for] human readable names in the signer tool').

JSON content unchanged - signatures are computed over
expectedDomainAndMessageHashes, not the filename - so no regeneration
needed. Makefile gen-validation-* targets and FACILITATOR.md updated
to match.

* Sepolia: task executed — record approve + execute artefacts

CB Multisig approval, Security Council approval, and final execution all
succeeded on sepolia (chain 11155111):

- CB approve: 0x33465e04d448f1627adfd6130fa2765bd672d0280a4aa190e79fd780d9d29d48
  (block 10947347)
- SC approve: 0x91f51a84ac2100ea2da72e9555ac423ef169f43239197a31a4d4e313b1f77838
  (block 10947374)
- Execute:    0xe6b2fde0ac66fc17e5ecb6b56aaa0eddbc567130ad66a9dff2c25376320f8e32
  (block 10947378)

README status flipped from READY TO SIGN to
[EXECUTED](https://sepolia.etherscan.io/tx/0xe6b2fde0...32) and the
Transactions section extended with explorer + artefact links for the
two approvals and the execution.
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.

4 participants