fix: F-2026-18829 | [Dual Defense] UEA_SVM Hardcodes Ed25519 Verifier 0x…00ca After Runtime Moved to 0xEC…01 - #320
Merged
Merged
Conversation
… 0x..00ca Nothing is registered at 0x..00ca, so declaring it active panics the EVM precompile lookup, and the real verifier was never activated.
Drops the legacy ed25519 verifier address from EVM ActiveStaticPrecompiles and adds 0xEC..01 if missing, for chains already past genesis.
audit-fixes targets mainnet, which starts from a fresh genesis, so the genesis scripts already carry the correct active_static_precompiles list and there are no live params to migrate. The upgrade handler belongs on the testnet branch instead, where donut has live params still declaring the legacy 0x...00ca address. Moves TestGenesisScriptsActivateCurrentVerifier out of the handler's test file into precompiles/usigverifier so the genesis half of the fix stays covered.
0x..00CB has no implementation anywhere and remove-utxverifier strips it from live chains, so leaving it in genesis re-introduced it on every fresh chain. Confirmed absent from live donut params, so this is script-only cleanup.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finding
The Ed25519 signature-verifier precompile moved from the legacy
0x00000000000000000000000000000000000000cato0xEC00000000000000000000000000000000000001. The node registers only the new address:app/app.goinstantiatesusigverifierprecompile.NewPrecompile()(precompiles/usigverifier,USigVerifierPrecompileAddress = 0xEC…01) and puts it into the static-precompile map handed toEVMKeeper.WithStaticPrecompiles.app/upgrades/ai-audit-fixes-2'sregisterPrecompileV2appends0xEC…01toActiveStaticPrecompileson chains that took that upgrade.Nothing is bound to
0x…00caanywhere in the node. But every genesis setup script still declared0x…00caactive and omitted0xEC…01entirely.ActiveStaticPrecompilesis what gates callability (Keeper.IsAvailableStaticPrecompile), so on a fresh genesis this produced two distinct failures:0x…00cawas declared but unimplemented.Keeper.GetStaticPrecompileInstancetreats an address that is active in params but absent from the in-memory precompile map as memory corruption andpanics withprecompiled contract not stored in memory: 0x…00ca. baseapp's recover contains it — the node survives and the tx fails — but every call to0x…00caaborts.0xEC…01was not active at all, so the real verifier was unreachable and Ed25519 verification could not work on a fresh chain.Per the decision on this finding,
0x…00cais removed, not re-registered.Changes
Genesis lists — dropped
0x…00ca, appended0xEC…01. The address is appended (rather than substituted in place) becausex/vm'sValidatePrecompilesrequires the list to be sorted and0xEC…sorts after every0x00…entry; the rest of each list is byte-identical.scripts/test_node.shlocal-native/scripts/setup-genesis-auto.shlocal-multi-validator/scripts/setup-genesis-auto.shtestnet/core/setup/setup_genesis_validator.shNo upgrade handler — deliberate.
audit-fixestargets mainnet, which starts from a fresh genesis, so the scripts above are the whole fix there: there are no live EVM params carrying0x…00cato migrate. Ausigverifier-precompile-fixhandler was written and then dropped from this PR (commit0faece52) for that reason. It belongs on the testnet branch, where donut is a running chain whose params still declare the legacy address; it will be raised there against a real upgrade height.Docs —
x/uexecutor/README.mdsaid the Ed25519 precompile lives at0x00…00ca; corrected to0xEC…01.Dead fixture —
test/utils/bytecode.go'sUEA_SVM_BYTECODEwas referenced by nothing in the repo and still embedded the old target (60ca5afa—PUSH1 0xca; STATICCALL— and selectorbbdd8207). Removed; nothing else in the tree referenced it.Tests
precompiles/usigverifier/genesis_scripts_test.go—TestGenesisScriptsActivateCurrentVerifierreads all four genesis scripts and asserts eachactive_static_precompilesline containsusigverifier.USigVerifierPrecompileAddress(taken from the source of truth, not re-typed) and does not contain the legacy0x…00ca. One sub-test per script.Regression-checked in both directions: re-introducing
0x…00cainto one script fails the sub-test, and removing0xEC…01from one script fails it on the other assertion.Follow-ups (deliberately out of scope here)
0x00000000000000000000000000000000000000CB(utxhashverifier) is the same defect.app/upgrades/remove-utxverifierderegisters it from live chains and theutxverifiermodule store is deleted, but all four genesis lists still declare it active — and there is noutxhashverifierimplementation anywhere inprecompiles/. A fresh genesis therefore re-introduces a declared-but-unimplemented address on exactly the panic path described above. Left in place so this PR stays scoped to the Ed25519 verifier.0x0000000000000000000000000000000000000803(vesting) — confirmed, same defect. It is declared active in all four genesis lists and is part of the evm fork'stypes.AvailableStaticPrecompiles, but there is no vesting precompile package in the evm fork (precompiles/has bank, bech32, callbacks, distribution, erc20, gov, ics20, p256, slashing, staking, werc20 — no vesting) andapp/precompiles.go'sNewAvailableStaticPrecompilesnever registers it. So0x803is active-but-unimplemented →precompiled contract not stored in memoryon any call. This matches Hacken #438 / RC-09.Bonus, opposite direction:
0x0000000000000000000000000000000000000806(slashing) is registered but never activated.app/precompiles.gobuilds and registersslashingPrecompile, but0x806appears in none of the four genesis lists, so the slashing precompile is unreachable on a fresh chain. Not a panic — just a silently dead feature.Contract half (tracked separately)
The durable fix is in
push-chain-core-contracts:src/uea/UEA_SVM.sol:42still haswhich must become
0xEC00000000000000000000000000000000000001, followed by aregisterUEAfor the new implementation. That change is not in this PR.Note that this is not self-healing for existing accounts:
UEAProxysnapshots its implementation address intoUEA_LOGIC_SLOTatinitializeUEAtime and_implementation()reads that slot on every delegatecall. Already-deployed SVM UEA proxies therefore keep delegating to the oldUEA_SVMimplementation even afterregisterUEApoints the factory at a new one, and need migrating through the module-sponsored migration path.