-
Notifications
You must be signed in to change notification settings - Fork 124
Fix early revert in verify for accurate gas estimation
#70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
✅ Heimdall Review Status
|
| returns (bool) | ||
| { | ||
| bool hasFailedChecks = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Could use hasFailedChecks as return param and accumulate sig checks into it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the actual return is an expression that incorporates multiple booleans
test/WebAuthn.fork.t.sol
Outdated
| baseFork = vm.createFork("https://mainnet.base.org"); | ||
| ethFork = vm.createFork("https://ethereum-rpc.publicnode.com"); | ||
| arbFork = vm.createFork("https://arb1.arbitrum.io/rpc"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should opt to use env based rpc urls so that CI/CD is more reliable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the motivation for fork testing on arb, base and eth specifically? How were these networks chosen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
originally this was to get a sense of the gas difference between the early-return v.s. the heavy path on chains that do/don't implement RIP-7212 (precompile for secp256r1 ec i.e. passkey signatures). Ethereum mainnet does not implement this, but Base and Arbitrum should (among other rollups). we're not actually able to see a difference in fork testing, so I'm not sure if foundry's fork testing environment incorporates precompiles. Would be open to removing arbitrum to make it somewhat less arbitrary (i.e. one 7212-compatible chain and one non-7212 chain)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we're not actually able to see a difference in fork testing, so I'm not sure if foundry's fork testing environment incorporates precompiles. Would be open to removing arbitrum to make it somewhat less arbitrary (i.e. one 7212-compatible chain and one non-7212 chain)
Think this would be nice for clarity
ilikesymmetry
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks for the additional tests
| /// @dev Verifies a P256 signature using the precompiled contract or FCL. | ||
| /// @param messageHash The hash of the message to verify. | ||
| /// @param r The r value of the signature. | ||
| /// @param s The s value of the signature. | ||
| /// @param x The x coordinate of the public key. | ||
| /// @param y The y coordinate of the public key. | ||
| /// @return True if the signature is valid, false otherwise. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: separate tag types with empty line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we're not actually able to see a difference in fork testing, so I'm not sure if foundry's fork testing environment incorporates precompiles. Would be open to removing arbitrum to make it somewhat less arbitrary (i.e. one 7212-compatible chain and one non-7212 chain)
Think this would be nice for clarity
| WebAuthnForkTest:test_verify_fullPath_arb() (gas: 253734) | ||
| WebAuthnForkTest:test_verify_fullPath_base() (gas: 253756) | ||
| WebAuthnForkTest:test_verify_fullPath_eth() (gas: 253757) | ||
| WebAuthnForkTest:test_verify_invalidChallenge_arb() (gas: 43717) | ||
| WebAuthnForkTest:test_verify_invalidChallenge_base() (gas: 43740) | ||
| WebAuthnForkTest:test_verify_invalidChallenge_eth() (gas: 43739) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow crazy difference 😮
|
Review Error for ilikesymmetry @ 2025-10-21 16:18:19 UTC |
|
Review Error for stevieraykatz @ 2025-10-21 17:18:52 UTC |
|
Ci/Cd is failing because we include snapshot checking: We can safely merge with failed ci/cd in this instance. |
The
verifyfunction in WebAuthn-sol checks the validity of a passkey signature. It’s called during the validation phase of an ERC-4337 flow for passkey-signed userOps. When preparing a userOp, the wallet client must estimate the “verification gas limit” (VGL) for the userOp, which is the estimation of how much gas will be consumed during the onchain validation of the userOp before the execution of it’s actual payload. There is some tolerance for error in this estimation, but using a value that’s too high or too low will cause the bundler to reject the userOp.When the verification flow is simulated without a valid passkey signature, the verify function was returning much earlier in its execution flow than it would when executing with a valid passkey signature during the actual onchain verification. This was leading to an underestimation of simulated verification gas.
To resolve this, we change the verify function so that it does not return early and always executes the heavy path (computing the SHA‑256 hashes and attempting signature verification) regardless of whether the inputs are ultimately valid. With these changes, valid and invalid inputs now cost roughly the same gas, so simulations without a final signature approximate the true cost of a later, valid verification.
There is one trade‑off: genuinely invalid signatures onchain are now slightly more expensive than before because we no longer revert early. In practice, this is a rare scenario as it will usually be caught by simulation, and if not, the gas cost is still relatively minor.