This repository has been archived by the owner on Jul 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 272
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* doc: ecRecover.md spec file * doc: signature circuit, copied from Sroll's design and revisted to fit our architecture * feat: impl. sig_circuit * test: add more cases * feat: add sig_table * doc: complete constraints desc. * feat: impl. ecRecover * test: add a normal case * test: complete testing * feat: remove rlc usage in sig circuit * feat: correct public key to little-endian * fix: is_success is always true and using iz_zero gadget for sig_r/v * test: fix testing data * fix return data length when the addr is not recoverable * doc: refinement
- Loading branch information
Showing
15 changed files
with
818 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# ecRecover precompile | ||
|
||
## Procedure | ||
|
||
To recover the signer from a signature. It returns signer's address if input signature is valid, otherwise returns 0. | ||
|
||
## EVM behavior | ||
|
||
### Inputs | ||
|
||
The length of inputs is 128 bytes. The first 32 bytes is keccak hash of the message, and the following 96 bytes are v, r, s values. The value v is either 27 or 28. | ||
|
||
### Output | ||
|
||
The recovered 20-byte address right aligned to 32 byte. If an address can't be recovered or not enough gas was given, then the output is 0. | ||
|
||
### Gas cost | ||
|
||
A constant gas cost: 3000 | ||
|
||
## Constraints | ||
|
||
1. If gas_left < gas_required, then is_success == false and return data is zero. | ||
1. v, r and s are valid | ||
- v is 27 or 28 | ||
- both of r and s are less than `secp256k1N (0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141)` | ||
- both of r and s are greater than `1` | ||
2. `sig_table` lookups | ||
3. recovered address is zero if the signature can't be recovered. | ||
|
||
## Code | ||
|
||
Please refer to `src/zkevm_specs/evm_circuit/execution/precompiles/ecrecover.py`. |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Signature Proof | ||
|
||
[Elliptic Curve Digital Signature Algorithm]: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm | ||
|
||
According to the [Elliptic Curve Digital Signature Algorithm] (ECDSA), the signatures `(r,s)` are calculated via ECDSA from `msg_hash` and a `public_key` using the formula | ||
|
||
`(r,s)=ecdsa(msg_hash, public_key)` | ||
|
||
The `public_key` is obtained from `private_key` by mapping the latter to an elliptic curve (EC) point. The `r` is the x-component of an EC point, and the same EC point's y-component will be used to determine the recovery id `v = y%2` (the parity of y). Given the signature `(v, r, s)`, the `public_key` can be recovered from `(v, r, s)` and `msg_hash` using `ecrecover`. | ||
|
||
|
||
## Circuit behavior | ||
|
||
SigTable built inside zkevm-circuits is used to verify signatures. It has the following columns: | ||
- `msg_hash`: Advice Column, the Keccak256 hash of the message that's signed; | ||
- `sig_v`: Advice Column, the recovery id, either 0 or 1, it should be the parity of y; | ||
- `sig_r`: Advice Column, the signature's `r` component; | ||
- `sig_s`: Advice Column, the signature's `s` component; | ||
- `recovered_addr`: Advice Column, the recovered address, i.e. the 20-bytes address that must have signed the message; | ||
- `is_valid`: Advice Column, indicates whether or not the signature is valid or not upon signature verification. | ||
|
||
Constraints on the shape of the table is like: | ||
|
||
| 0 msg_hash | 1 sig_v | 2 sig_r | 3 sig_s | 4 recovered_addr | 5 is_valid | | ||
| ------------- | ------ | ------------- | ------------- | ---------------- | ---------- | | ||
| $value{Lo,Hi} | 0/1 | $value{Lo,Hi} | $value{Lo,Hi} | $value{Lo,Hi} | bool | | ||
|
||
|
||
The Sig Circuit aims at proving the correctness of SigTable. This mainly includes the following type of constraints: | ||
- Checking that the signature is obtained correctly. This is done by the ECDSA chip, and the correctness of `v` is checked separately; | ||
- Checking that `msg_hash` is obtained correctly from Keccak hash function. This is done by lookup to Keccak table; | ||
|
||
|
||
## Constraints | ||
|
||
`assign_ecdsa` method takes the signature data and uses ECDSA chip to verify its correctness. The verification result `sig_is_valid` will be returned. The recovery id `v` value will be computed and verified. | ||
|
||
`sign_data_decomposition` method takes the signature data and the return values of `assign_ecdsa`, and returns the cells for byte decomposition of the keys and messages in the form of `SignDataDecomposed`. The latter consists of the following contents: | ||
- `SignDataDecomposed` | ||
- `pk_hash_cells`: byte cells for keccak256 hash of public key; | ||
- `msg_hash_cells`: byte cells for `msg_hash`; | ||
- `pk_cells`: byte cells for the EC coordinates of public key; | ||
- `address`: RLC of `pk_hash` last 20 bytes; | ||
- `is_address_zero`: check if address is zero; | ||
- `r_cells`, `s_cells`: byte cells for signatures `r` and `s`. | ||
|
||
The decomposed sign data are sent to `assign_sign_verify` method to compute and verify their RLC values and perform Keccak lookup checks. | ||
|
||
## Code | ||
|
||
Please refer to `src/zkevm-specs/sig_circuit.py` |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,4 @@ | |
from .table import * | ||
from .typing import * | ||
from .util import * | ||
from .precompile import * |
This file contains 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
65 changes: 65 additions & 0 deletions
65
src/zkevm_specs/evm_circuit/execution/precompiles/ecrecover.py
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from zkevm_specs.evm_circuit.instruction import Instruction | ||
from zkevm_specs.evm_circuit.table import ( | ||
CallContextFieldTag, | ||
FixedTableTag, | ||
RW, | ||
) | ||
from zkevm_specs.util import FQ, Word, EcrecoverGas | ||
|
||
SECP256K1N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 | ||
|
||
|
||
def ecRecover(instruction: Instruction): | ||
is_success = instruction.call_context_lookup(CallContextFieldTag.IsSuccess, RW.Read) | ||
address_word = instruction.call_context_lookup_word(CallContextFieldTag.CalleeAddress) | ||
address = instruction.word_to_address(address_word) | ||
instruction.fixed_lookup( | ||
FixedTableTag.PrecompileInfo, | ||
FQ(instruction.curr.execution_state), | ||
address, | ||
FQ(EcrecoverGas), | ||
) | ||
|
||
# Get msg_hash, signature and recovered address from aux_data | ||
msg_hash: Word = instruction.curr.aux_data[0] | ||
sig_v: Word = instruction.curr.aux_data[1] | ||
sig_r: Word = instruction.curr.aux_data[2] | ||
sig_s: Word = instruction.curr.aux_data[3] | ||
recovered_addr: FQ = instruction.curr.aux_data[4] | ||
|
||
is_recovered = FQ(instruction.is_zero(recovered_addr) != FQ(1)) | ||
|
||
# is_success is always true | ||
# ref: https://github.com/ethereum/execution-specs/blob/master/src/ethereum/shanghai/vm/precompiled_contracts/ecrecover.py | ||
instruction.constrain_equal(is_success, FQ(1)) | ||
|
||
# verify r and s | ||
sig_r_upper_bound, _ = instruction.compare_word(sig_r, Word(SECP256K1N)) | ||
sig_s_upper_bound, _ = instruction.compare_word(sig_s, Word(SECP256K1N)) | ||
sig_r_is_non_zero = FQ(instruction.is_zero_word(sig_r) != FQ(1)) | ||
sig_s_is_non_zero = FQ(instruction.is_zero_word(sig_s) != FQ(1)) | ||
valid_r_s = instruction.is_equal( | ||
sig_r_upper_bound + sig_s_upper_bound + sig_r_is_non_zero + sig_s_is_non_zero, FQ(4) | ||
) | ||
|
||
# verify v | ||
is_equal_27 = instruction.is_equal_word(sig_v, Word(27)) | ||
is_equal_28 = instruction.is_equal_word(sig_v, Word(28)) | ||
valid_v = instruction.is_equal(is_equal_27 + is_equal_28, FQ(1)) | ||
|
||
if valid_r_s + valid_v == FQ(2): | ||
# sig table lookups | ||
instruction.sig_lookup( | ||
msg_hash, sig_v.lo.expr() - FQ(27), sig_r, sig_s, recovered_addr, is_recovered | ||
) | ||
else: | ||
instruction.constrain_zero(is_recovered) | ||
instruction.constrain_zero(recovered_addr) | ||
|
||
# Restore caller state to next StepState | ||
instruction.step_state_transition_to_restored_context( | ||
rw_counter_delta=instruction.rw_counter_offset, | ||
return_data_offset=FQ.zero(), | ||
return_data_length=FQ(32) if is_recovered == FQ(1) else FQ.zero(), | ||
gas_left=instruction.curr.gas_left - EcrecoverGas, | ||
) |
This file contains 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
This file contains 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
Oops, something went wrong.