ethereum/EIPs-155 Simple replay attack protection 35,36


EIP 155:重放攻擊保護——防止了在一個以太坊鏈上的交易被重復廣播到另外一條鏈。

在看橢圓曲線時有提到,與r、s、v中的v相關

 

不同的共有鏈定義不同的chainId, 防止同一筆交易在不同的共有鏈上進行兩次交易,防止重放攻擊(其實就是防止測試網中的代幣發送到主網中去)

v = 35 + chainId *2 | v = 36 + chainId * 2 (奇偶校驗)

 

在創世區塊的json文件中有設置:

    "config": {
        "chainId": 555,
        "homesteadBlock": 0,
        "eip155Block": 0,//homesteadeip155兩種版本都不是,那么就是Frontier版本了,所以v的值為27或28,看下面代碼解釋
        "eip158Block": 0
    },

參數代表的含義如下所示:

1、homesteadBlock:代表以太坊版本,這里我們設置為0。

2、eip155Block:我們的區塊鏈並不涉及EIP155硬分叉(hard-fork),因此這個值設置為0。(這就是這個EIP將的內容)

3、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

Hard fork

Spurious Dragon

Parameters

  • FORK_BLKNUM: 2,675,000 (the DAO 事件后進行分叉的區塊數)
  • CHAIN_ID: 1 (main net)

Specification

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 來保證有效的簽名方案將在現在使用的相同規則下繼續操作(意思是這個也還在使用)v = 27 and v = 28說明的是版本(當其在簽名中值為0x00或0x01時,要反向得到加密賬戶,即調用ecrecover函數時,要加27)

Example舉例說明

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

Rationale

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,這是看你使用的版本的原因,下面有解釋)

 

List of Chain ID's:

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的值進行的檢驗,以后在慢慢看吧!!!!!!!!!!!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM