EIP-155 and Public Key Recovery
EIP-155
The EIP-155 “Simple Replay Attack Protection” standard specifies a replay-attack-protected transaction encoding, which includes a chain identifier inside the transaction data, prior to signing.
- This ensures that transactions created for one blockchain are invalid on another blockchain. Therefore, transactions broadcast on one network cannot be replayed on another.
Chain | Chain ID |
---|---|
Ethereum mainnet | 1 |
Ropsten | 3 |
Rinkeby | 4 |
EIP-155 took effect after the Spurious Dragon hard fork (FORK_BLKNUM
2,675,000, CHAIN_ID
1).
- If
block.number
>=FORK_BLKNUM
andCHAIN_ID
is available, then when computing the hash of a transaction for the purposes of signing, instead of hashing only six rlp encoded elements(nonce, gasprice, startgas, to, value, data)
, you SHOULD hash nine rlp encoded elements(nonce, gasprice, startgas, to, value, data, chainid, 0, 0)
. If you do, then thev
of the signature MUST be set to $$\{0,1\} + CHAIN\_ID * 2 + 35$$ where $$\{0,1\}$$ is the parity of the $$y$$ value of the curve point for whichr
is the $$x$$-value in thesecp256k1
signing process.- If you choose to only hash 6 values, then
v
continues to be set to $$\{0,1\} + 27$$ as previously.
- If you choose to only hash 6 values, then
- If
block.number >= FORK_BLKNUM
and $$v = CHAIN\_ID * 2 + 35$$ or $$v = CHAIN\_ID * 2 + 36$$, then when computing the hash of a transaction for purposes of recovering, instead of hashing six rlp encoded elements(nonce, gasprice, startgas, to, value, data)
, hash nine rlp encoded elements(nonce, gasprice, startgas, to, value, data, chainid, 0, 0)
.- The currently existing signature scheme using $$v = 27$$ and $$v = 28$$ remains valid and continues to operate under the same rules as it did previously.
Public Key Recovery
Given an ECDSA signature $$(r, s)$$ and EC domain parameters, it is generally possible to determine the public key $$Q$$, at least to within a small number of choices.
- This is useful for generating self-signed signatures; also useful in bandwidth constrained environments when transmission of public keys cannot be afforded.
Potentially, several candidate public keys can be recovered from a signature. At a small cost, the signer can generate the ECDSA signature in such a way that only one of the candidate public keys is viable, and such that the verifier has a very small additional cost of determining which is the correct public key.
- 公钥是椭圆曲线上的点,椭圆曲线坐标 $$x, y \in [0, p-1]$$,而签名 $$r, s \in [1, n-1]$$,$$p > n$$,所以会存在多个点对应同一个 $$r$$ 的情况。
v
of the signature makes the recovery process more efficient,.
ecrecover
implementation:
// https://github.com/ethereum/go-ethereum/blob/v1.10.25/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h#L28
/** Parse a compact ECDSA signature (64 bytes + recovery id).
*
* Returns: 1 when the signature could be parsed, 0 otherwise
* Args: ctx: a secp256k1 context object
* Out: sig: a pointer to a signature object
* In: input64: a pointer to a 64-byte compact signature
* recid: the recovery id (0, 1, 2 or 3)
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact(
const secp256k1_context* ctx,
secp256k1_ecdsa_recoverable_signature* sig,
const unsigned char *input64,
int recid
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
// https://github.com/ethereum/go-ethereum/blob/v1.10.25/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h#L273
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) {
// ...
if (recid) {
/* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log
* of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria.
*/
*recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0);
}
secp256k1_scalar_mul(&n, sigr, seckey);
secp256k1_scalar_add(&n, &n, message);
secp256k1_scalar_inverse(sigs, nonce);
secp256k1_scalar_mul(sigs, sigs, &n);
secp256k1_scalar_clear(&n);
secp256k1_gej_clear(&rp);
secp256k1_ge_clear(&r);
if (secp256k1_scalar_is_zero(sigs)) {
return 0;
}
if (secp256k1_scalar_is_high(sigs)) {
secp256k1_scalar_negate(sigs, sigs);
if (recid) {
*recid ^= 1;
}
}
return 1;
}
// https://github.com/ethereum/go-ethereum/blob/v1.10.25/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h#L170
Value of recid
:
- 0 -
y
is even,x
is finite - 1 -
y
is odd,x
is finite - 2 -
y
is even,x
is too large - 3 -
y
is odd,x
is too large
References