EIP 155:重放攻擊保護——防止了在一個以太坊鏈上的交易被重複廣播到另一條鏈。git
在看橢圓曲線時有提到,與r、s、v中的v相關github
不一樣的共有鏈定義不一樣的chainId, 防止同一筆交易在不一樣的共有鏈上進行兩次交易,防止重放攻擊(其實就是防止測試網中的代幣發送到主網中去)web
v = 35 + chainId *2 | v = 36 + chainId * 2 (奇偶校驗)json
在創世區塊的json文件中有設置:網絡
"config": { "chainId": 555, "homesteadBlock": 0, "eip155Block": 0,//homestead和eip155兩種版本都不是,那麼就是Frontier版本了,因此v的值爲27或28,看下面代碼解釋 "eip158Block": 0 },
參數表明的含義以下所示:less
一、homesteadBlock:表明以太坊版本,這裏咱們設置爲0。ide
二、eip155Block:咱們的區塊鏈並不涉及EIP155硬分叉(hard-fork),所以這個值設置爲0。(這就是這個EIP將的內容)函數
三、eip158Block:同理,咱們的區塊鏈並不涉及EIP158硬分叉(hard-fork),所以這個值設置爲0。區塊鏈
4.正在公開使用的chainId:測試
0: Olympic, Ethereum public pre-release testnet 1: Frontier, Homestead, Metropolis, the Ethereum public main network 1: Classic, the (un)forked public Ethereum Classic main network, chain ID 61 1: Expanse, an alternative Ethereum implementation, chain ID 2 2: Morden, the public Ethereum testnet, now Ethereum Classic testnet 3: Ropsten, the public cross-client Ethereum testnet 4: Rinkeby, the public Geth PoA testnet 8: Ubiq, the public Gubiq main network with flux difficulty chain ID 8 42: Kovan, the public Parity PoA testnet 77: Sokol, the public POA Network testnet 99: Core, the public POA Network main network 7762959: Musicoin, the music blockchain 61717561: Aquachain, ASIC resistant chain [Other]: Could indicate that your connected to a local development test network.
注意:這裏Classic是當年硬分叉後的ETC(叫以太經典),其的chainId爲61(DAO(Decentralized Autonomous Organization)去中心化的自治組織);
2,3,4都爲測試網絡
EIP-155是後向兼容的,向後兼容是指舊軟件所產生的數據或者代碼能夠被新軟件使用,即win13可以使用win10的應用
反之,向前兼容即win10能使用win13的應用
Be aware that this backwards compatibility also means that transactions created from alternative Ethereum based blockchains that have not implemented EIP 155 (such as Ethereum Classic) can still be replayed on the main Ethereum chain.
eip | title | author | type | category | status | created |
---|---|---|---|---|---|---|
155
|
Simple replay attack protection
|
Vitalik Buterin
|
Standards Track
|
Core
|
Final
|
2016-10-14
|
FORK_BLKNUM
: 2,675,000 (the DAO 事件後進行分叉的區塊數)CHAIN_ID
: 1 (main net)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 signing or recovering, instead of hashing only the first six elements (i.e. nonce, gasprice, startgas, to, value, data), hash nine elements, with v
replaced by CHAIN_ID
, r = 0
and s = 0
. The currently existing signature scheme using v = 27
and v = 28
remains valid and continues to operate under the same rules as it does now.
若是區塊數大於分叉區塊數而且v = CHAIN_ID * 2 + 35
or v = CHAIN_ID * 2 + 36,那麼當爲了簽名或恢復進行交易hash的計算時,再也不僅僅只hash前六個元素,而是(nonce, gasprice, startgas, to, value, data),而是hash九個元素(還要再加上
CHAIN_ID
,r=0,s=0)。目前存在的使用
的簽名方案將在如今使用的相同規則下繼續操做(意思是這個也還在使用)。v = 27
and v = 28
來保證有效
說明的是版本(當其在簽名中值爲0x00或0x01時,要反向獲得加密帳戶,即調用ecrecover函數時,要加27)v = 27
and v = 28
Consider a transaction with nonce = 9
, gasprice = 20 * 10**9
, startgas = 21000
, to = 0x3535353535353535353535353535353535353535
, value = 10**18
, data=''
(empty).
The "signing data" becomes:
0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080
The "signing hash" becomes:
0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53
If the transaction is signed with the private key 0x4646464646464646464646464646464646464646464646464646464646464646
, then the v,r,s values become:
(37, 18515461264373351373200002665853028612451056578545711640558177340181847433846, 46948507304638947509940763649030358759909902576025900602547168820602576006531)
Notice the use of 37 instead of 27. The signed tx would become:
0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83
This would provide a way to send transactions that work on Ethereum without working on ETC or the Morden testnet. ETC is encouraged to adopt this EIP but replacing CHAIN_ID
with a different value, and all future testnets, consortium chains and alt-etherea are encouraged to adopt this EIP replacing CHAIN_ID
with a unique value.
這樣子就提供了一種方法來保證交易是使用在了以太坊上而不是另外一個分叉ETC或測試網絡Morden上,主網上的v = 1*2 + 35 = 37 或 v = 1*2 + 36 = 38(注意:這也是爲何以前在查看一些如今生成的簽名的v的時候,發現他們的v並不等於27或28,這個後面要注意一下,爲何仍是有些是27和28,這是看你使用的版本的緣由,下面有解釋)
CHAIN_ID |
Chain(s) |
---|---|
1 | Ethereum mainnet |
2 | Morden (disused), Expanse mainnet |
3 | Ropsten |
4 | Rinkeby |
30 | Rootstock mainnet |
31 | Rootstock testnet |
42 | Kovan |
61 | Ethereum Classic mainnet |
62 | Ethereum Classic testnet |
1337 | Geth private chains (default) |
簽名只會生成r,s兩個的值,v是加上去的
舉個例子說明:
web3.eth.signTransaction({ from: "0xEB014f8c8B418Db6b45774c326A0E64C78914dC0", gasPrice: "20000000000", gas: "21000", to: '0x3535353535353535353535353535353535353535', value: "1000000000000000000", data: "" }).then(console.log); > { raw: '0xf86c808504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a04f4c17305743700648bc4f6cd3038ec6f6af0df73e31757007b7f59df7bee88da07e1941b264348e80c78c4027afc65a87b0a5e43e86742b8ca0823584c6788fd0', tx: { nonce: '0x0', gasPrice: '0x4a817c800', gas: '0x5208', to: '0x3535353535353535353535353535353535353535', value: '0xde0b6b3a7640000', input: '0x', v: '0x25', r: '0x4f4c17305743700648bc4f6cd3038ec6f6af0df73e31757007b7f59df7bee88d', s: '0x7e1941b264348e80c78c4027afc65a87b0a5e43e86742b8ca0823584c6788fd0', hash: '0xda3be87732110de6c1354c83770aae630ede9ac308d9f7b399ecfba23d923384' } }
將raw處的簽名分解爲tx處的內容:
0xf86c8085 0 4a817c800 82 5208 94 3535353535353535353535353535353535353535 880 de0b6b3a7640000 80 25 a0 4f4c17305743700648bc4f6cd3038ec6f6af0df73e31757007b7f59df7bee88d a0 7e1941b264348e80c78c4027afc65a87b0a5e43e86742b8ca0823584c6788fd0
將這裏的數據分開後,能夠看見每個數據所在的位置,可是很奇怪的地方就是爲何每一個數據之間都有一些奇怪的數字,好比後面的v,r,s,中間都有着0xa0,這些值是有什麼意義的嗎,但願能找到解釋
從這裏的結果咱們就可以看見這裏的v = 0x25 = 37,說明它是在主網運行的
還有另外一個例子:
var rawTx = { from: '0x91b678137f09c8b4f294a14e88c09276522618cf', nonce: '0x'+nonce, gasPrice: '0x09184e72a000', gasLimit: 3000000,//2dc6c0 to: '0xceff99a34d9f6e7d3deae2bd0604086645368aee', value: '0x00', data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057' };
獲得的簽名分解是結果是:
f88a0b86 0 9184e72a000 83 2dc6c0 94 ceff99a34d9f6e7d3deae2bd0604086645368aee 80a4 7f7465737432000000000000000000000000000000000000000000000000000000600057 1b a0 b9c52b010ef238339f710bee60b8597b2953c19d32680490b8f0acc82f2fe3be a0 40f8e42202fdb3b72e2f2976fc5e4ae7485b45ca702b23792b38add2d4912ccb
可見這裏的v = 0x1b = 27,由於這個結果是我在本地區塊鏈運行的交易,並不涉及EIP155硬分叉(hard-fork),eip155Block這個值是設置爲0的
下面由代碼上查看:
https://github.com/ethereum/go-ethereum/blob/master/core/types/transaction_signing.go
從代碼上咱們能夠看見,當交易實現的規則不一樣時,v的值也會相應不一樣
1.EIP155Signer,則 v = chainId *2 +35,說明使用的是奇偶校驗中的偶校驗,即曲線點爲偶數(若是用的是36,則是奇校驗)
2.HomesteadSigner,v is 0 or 1 (0爲偶檢驗,1爲奇校驗)
3.FrontierSigner,爲27 or 28 (27爲偶檢驗,28爲奇校驗)
go-ethereum/core/types/transaction_signing.go
// EIP155Transaction implements Signer using the EIP155 rules. type EIP155Signer struct { chainId, chainIdMul *big.Int } func NewEIP155Signer(chainId *big.Int) EIP155Signer { if chainId == nil { chainId = new(big.Int) } return EIP155Signer{ chainId: chainId, chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)), } } func (s EIP155Signer) Equal(s2 Signer) bool { eip155, ok := s2.(EIP155Signer) return ok && eip155.chainId.Cmp(s.chainId) == 0 } var big8 = big.NewInt(8) func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) { if !tx.Protected() { return HomesteadSigner{}.Sender(tx) } if tx.ChainId().Cmp(s.chainId) != 0 { return common.Address{}, ErrInvalidChainId } V := new(big.Int).Sub(tx.data.V, s.chainIdMul) V.Sub(V, big8) return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true) } // WithSignature returns a new transaction with the given signature. This signature // needs to be in the [R || S || V] format where V is 0 or 1. func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig) if err != nil { return nil, nil, nil, err } if s.chainId.Sign() != 0 { V = big.NewInt(int64(sig[64] + 35)) V.Add(V, s.chainIdMul) } return R, S, V, nil } // Hash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. func (s EIP155Signer) Hash(tx *Transaction) common.Hash { return rlpHash([]interface{}{ tx.data.AccountNonce, tx.data.Price, tx.data.GasLimit, tx.data.Recipient, tx.data.Amount, tx.data.Payload, s.chainId, uint(0), uint(0), }) } // HomesteadTransaction implements TransactionInterface using the // homestead rules. type HomesteadSigner struct{ FrontierSigner } func (s HomesteadSigner) Equal(s2 Signer) bool { _, ok := s2.(HomesteadSigner) return ok } // SignatureValues returns signature values. This signature // needs to be in the [R || S || V] format where V is 0 or 1. func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) { return hs.FrontierSigner.SignatureValues(tx, sig) } func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) { return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true) } type FrontierSigner struct{} func (s FrontierSigner) Equal(s2 Signer) bool { _, ok := s2.(FrontierSigner) return ok } // SignatureValues returns signature values. This signature // needs to be in the [R || S || V] format where V is 0 or 1. func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) { if len(sig) != 65 { panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) } r = new(big.Int).SetBytes(sig[:32]) s = new(big.Int).SetBytes(sig[32:64]) v = new(big.Int).SetBytes([]byte{sig[64] + 27}) return r, s, v, nil } // Hash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { return rlpHash([]interface{}{ tx.data.AccountNonce, tx.data.Price, tx.data.GasLimit, tx.data.Recipient, tx.data.Amount, tx.data.Payload, }) } func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) { return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false) } func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) { if Vb.BitLen() > 8 { return common.Address{}, ErrInvalidSig } V := byte(Vb.Uint64() - 27) if !crypto.ValidateSignatureValues(V, R, S, homestead) { return common.Address{}, ErrInvalidSig } // encode the snature in uncompressed format r, s := R.Bytes(), S.Bytes() sig := make([]byte, 65) copy(sig[32-len(r):32], r) copy(sig[64-len(s):64], s) sig[64] = V // recover the public key from the snature pub, err := crypto.Ecrecover(sighash[:], sig) if err != nil { return common.Address{}, err } if len(pub) == 0 || pub[0] != 4 { return common.Address{}, errors.New("invalid public key") } var addr common.Address copy(addr[:], crypto.Keccak256(pub[1:])[12:]) return addr, nil }
經過上面的r,s,v獲得的簽名和信息hash來恢復公鑰:
https://github.com/ethereum/go-ethereum/blob/master/crypto/signature_cgo.go
go-ethereum/crypto/signature_cgo.go
// Ecrecover returns the uncompressed public key that created the given signature. func Ecrecover(hash, sig []byte) ([]byte, error) { return secp256k1.RecoverPubkey(hash, sig) }
https://github.com/ethereum/go-ethereum/blob/master/crypto/secp256k1/secp256.go
go-ethereum/crypto/secp256k1/secp256.go
// RecoverPubkey returns the public key of the signer. // msg must be the 32-byte hash of the message to be signed. // sig must be a 65-byte compact ECDSA signature containing the recovery id as the last element. func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) { if len(msg) != 32 { return nil, ErrInvalidMsgLen } if err := checkSignature(sig); err != nil { return nil, err } var ( pubkey = make([]byte, 65) sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) ) if C.secp256k1_ext_ecdsa_recover(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 { return nil, ErrRecoverFailed } return pubkey, nil }
https://github.com/ethereum/go-ethereum/blob/master/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h
go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h
int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) { secp256k1_ge q; secp256k1_scalar r, s; secp256k1_scalar m; int recid; VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(msg32 != NULL); ARG_CHECK(signature != NULL); ARG_CHECK(pubkey != NULL); secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature); VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */說明v能夠爲0,1,2,3 secp256k1_scalar_set_b32(&m, msg32, NULL); if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) { secp256k1_pubkey_save(pubkey, &q); return 1; } else { memset(pubkey, 0, sizeof(*pubkey)); return 0; } }
同一個代碼下
/** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1 * sage: for t in xrange(1023, -1, -1): * .. p = 2**256 - 2**32 - t * .. if p.is_prime(): * .. print '%x'%p * .. break * 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' * sage: a = 0 * sage: b = 7 * sage: F = FiniteField (p) * sage: '%x' % (EllipticCurve ([F (a), F (b)]).order()) * 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' */ static const secp256k1_fe_t secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST(//這是私鑰的最大值 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL ); /** Difference between field and order, values 'p' and 'n' values defined in * "Standards for Efficient Cryptography" (SEC2) 2.7.1. * sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F * sage: a = 0 * sage: b = 7 * sage: F = FiniteField (p) * sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order()) * '14551231950b75fc4402da1722fc9baee' */ static const secp256k1_fe_t secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL );
static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) { unsigned char brx[32]; secp256k1_fe fx; secp256k1_ge x; secp256k1_gej xj; secp256k1_scalar rn, u1, u2; secp256k1_gej qj; int r; if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { return 0; } secp256k1_scalar_get_b32(brx, sigr); r = secp256k1_fe_set_b32(&fx, brx); (void)r; VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */ if (recid & 2) {//用於得到v二進制倒數第二位的值,即當v = 2或3時,即二進制倒數第二位爲1,才進入該判斷語句 if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) {
/* fx + p >= n, so we can skip testing the second case. */ return 0; } secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe); }
//因此走到這的即v = 0或1的簽名 if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) { return 0; }
//前面這部分的內容真的很複雜,可是看起來像是進行一些檢驗 secp256k1_gej_set_ge(&xj, &x); secp256k1_scalar_inverse_var(&rn, sigr); secp256k1_scalar_mul(&u1, &rn, message); secp256k1_scalar_negate(&u1, &u1); secp256k1_scalar_mul(&u2, &rn, sigs); secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1); secp256k1_ge_set_gej_var(pubkey, &qj); return !secp256k1_gej_is_infinity(&qj); }
https://github.com/ethereum/go-ethereum/blob/461291882edce0ac4a28f64c4e8725b7f57cbeae/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h
go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h
static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { int i; #ifdef VERIFY VERIFY_CHECK(a->normalized); VERIFY_CHECK(b->normalized); secp256k1_fe_verify(a); secp256k1_fe_verify(b); #endif for (i = 4; i >= 0; i--) { if (a->n[i] > b->n[i]) { return 1; } if (a->n[i] < b->n[i]) { return -1; } } return 0; }
#ifdef VERIFY static void secp256k1_fe_verify(const secp256k1_fe *a) { const uint64_t *d = a->n; int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; /* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ r &= (d[0] <= 0xFFFFFFFFFFFFFULL * m); r &= (d[1] <= 0xFFFFFFFFFFFFFULL * m); r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m); r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m); r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m); r &= (a->magnitude >= 0); r &= (a->magnitude <= 2048); if (a->normalized) { r &= (a->magnitude <= 1); if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { r &= (d[0] < 0xFFFFEFFFFFC2FULL); } } VERIFY_CHECK(r == 1); } #endif
SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { #ifdef VERIFY secp256k1_fe_verify(a); #endif r->n[0] += a->n[0]; r->n[1] += a->n[1]; r->n[2] += a->n[2]; r->n[3] += a->n[3]; r->n[4] += a->n[4]; #ifdef VERIFY r->magnitude += a->magnitude; r->normalized = 0; secp256k1_fe_verify(r); #endif }
https://github.com/ethereum/go-ethereum/blob/461291882edce0ac4a28f64c4e8725b7f57cbeae/crypto/secp256k1/libsecp256k1/src/group_impl.h
go-ethereum/crypto/secp256k1/libsecp256k1/src/group_impl.h
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { if (!secp256k1_ge_set_xquad(r, x)) { return 0; } secp256k1_fe_normalize_var(&r->y); if (secp256k1_fe_is_odd(&r->y) != odd) { secp256k1_fe_negate(&r->y, &r->y, 1); } return 1; }
上面的代碼太複雜,沒看懂,可是大概以爲v的做用應該是用來對r的值進行的檢驗,之後在慢慢看吧!!!!!!!!!!!!!