feat(node): accept a wallet's personal_sign signature on a transaction - #23
Merged
Merged
Conversation
MetaMask and Trust Wallet will not sign a bare hash. They sign a text with
personal_sign (EIP-191), which hashes "\x19Ethereum Signed Message:\n" + length
+ text. Until now the node only accepted a signature over the hash string
itself, so no wallet could send a transaction.
Transaction::verify_signature now tries two schemes, each against its own
digest:
1. the key signed the hash string (the SDK's local key, the mint authority,
the faucet). Unchanged.
2. a wallet signed "clutch-tx:{chain_id}:{hash}" with personal_sign, with the
hash as 64 lowercase hex and no 0x. The text names the chain, and
verify_hash already ties the hash to chain_id.
The wire format, the hash, the block signature and the mint cosignatures do not
change. A malformed signature keeps its old error text.
This is a consensus change: a node without it rejects a block that carries a
scheme 2 transaction. Every validator must run this build before the first
wallet transaction is sent.
SignatureKeys::personal_sign_bytes builds the prefixed bytes. sign,
recover_address and verify already hash what they are given with Keccak-256, so
they serve both schemes with no new code path.
Tests: the layout of the prefixed bytes; the published ethers digest for
"Hello World"; a signature made by @noble/secp256k1 (what the SDK uses) that
the node verifies; sign and verify unit cases for the wrong chain, the wrong key,
another transaction, a flipped recovery id and a malformed signature; and
tests/wallet_signature.rs, which sends a wallet-signed transfer through the pool
and an authored block, and refuses two bad ones.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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.
What this changes
The node now accepts a wallet's
personal_signsignature on a transaction, next to the signature it accepted before.MetaMask and Trust Wallet will not sign a bare hash. They sign a text, and they hash it with a fixed prefix (EIP-191). Until now the node checked only a signature over the hash string itself, so no wallet could send a transaction.
Transaction::verify_signaturetries two schemes. Each one is checked against its own digest, so one cannot pass for the other:clutch-tx:{chain_id}:{hash}withpersonal_signIn scheme 2 the hash is 64 lowercase hex characters with no
0x. The text names the chain, andverify_hashalready ties the hash tochain_id, so a signature cannot move to another chain or to another transaction.The wire format, the hash, the block signature and the mint cosignatures do not change.
This is a consensus change
A node without this change rejects a block that carries a scheme 2 transaction. Every validator must run this build before the first wallet transaction is sent. Plan: stage gets it first (the 3 stage nodes move together); mainnet gets it through a chain reset, because no CLT has ever been minted there.
Tests
personal_sign_bytes_has_the_eip191_layoutpersonal_sign_digest_matches_the_published_hello_world_vector(the digest comes from the ethers documentation, not from this code)a_signature_made_by_a_javascript_library_verifies(made by@noble/secp256k1, which the SDK uses)a_wallet_signature_binds_the_signer_and_the_exact_texttransaction.rs:wallet_signing_text_names_the_chain_and_the_bare_hash,verify_signature_accepts_a_wallet_signature,verify_signature_accepts_the_sign_personal_helper,verify_signature_still_accepts_a_signature_over_the_hash_string,verify_signature_accepts_the_signature_a_javascript_library_made, and five refusals (another chain, another key, another transaction, a flipped recovery id, a malformed signature)tests/wallet_signature.rs: a wallet-signed transfer goes through the pool and an authored block and is applied; two bad signatures are refusedNothing was run on the author's machine. The
Testworkflow is the check.Also
CLAUDE.mdsays the new scheme in "Gotchas", and its old line "There is no CI job runningcargo test" is corrected:test.ymlhas run on every PR for some time.🤖 Generated with Claude Code