From f4816ee8b7b915fb794a2d8595f5ac3915a04c37 Mon Sep 17 00:00:00 2001 From: zjubfd <296179868@qq.com> Date: Thu, 9 Jul 2020 15:46:37 +0800 Subject: [PATCH 1/4] add chain id into sign bytes to avoid replay attack (#18) --- consensus/parlia/parlia.go | 25 +++++++++++++------------ consensus/parlia/snapshot.go | 5 +++-- signer/core/signed_data.go | 8 ++++---- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index d5050cf3f9..7d5b324df9 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -156,7 +156,7 @@ func isToSystemContract(to common.Address) bool { } // ecrecover extracts the Ethereum account address from a signed header. -func ecrecover(header *types.Header, sigCache *lru.ARCCache) (common.Address, error) { +func ecrecover(header *types.Header, sigCache *lru.ARCCache, chainId *big.Int) (common.Address, error) { // If the signature's already cached, return that hash := header.Hash() if address, known := sigCache.Get(hash); known { @@ -169,7 +169,7 @@ func ecrecover(header *types.Header, sigCache *lru.ARCCache) (common.Address, er signature := header.Extra[len(header.Extra)-extraSeal:] // Recover the public key and the Ethereum address - pubkey, err := crypto.Ecrecover(SealHash(header).Bytes(), signature) + pubkey, err := crypto.Ecrecover(SealHash(header, chainId).Bytes(), signature) if err != nil { return common.Address{}, err } @@ -187,9 +187,9 @@ func ecrecover(header *types.Header, sigCache *lru.ARCCache) (common.Address, er // Note, the method requires the extra data to be at least 65 bytes, otherwise it // panics. This is done to avoid accidentally using both forms (signature present // or not), which could be abused to produce different hashes for the same header. -func ParliaRLP(header *types.Header) []byte { +func ParliaRLP(header *types.Header, chainId *big.Int) []byte { b := new(bytes.Buffer) - encodeSigHeader(b, header) + encodeSigHeader(b, header, chainId) return b.Bytes() } @@ -498,7 +498,7 @@ func (p *Parlia) snapshot(chain consensus.ChainReader, number uint64, hash commo headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] } - snap, err := snap.apply(headers, chain, parents) + snap, err := snap.apply(headers, chain, parents, p.chainConfig.ChainID) if err != nil { return nil, err } @@ -546,7 +546,7 @@ func (p *Parlia) verifySeal(chain consensus.ChainReader, header *types.Header, p } // Resolve the authorization key and check against validators - signer, err := ecrecover(header, p.signatures) + signer, err := ecrecover(header, p.signatures, p.chainConfig.ChainID) if err != nil { return err } @@ -821,7 +821,7 @@ func (p *Parlia) Seal(chain consensus.ChainReader, block *types.Block, results c log.Info("Sealing block with", "number", number, "delay", delay, "headerDifficulty", header.Difficulty, "val", val.Hex()) // Sign all the things! - sig, err := signFn(accounts.Account{Address: val}, accounts.MimetypeParlia, ParliaRLP(header)) + sig, err := signFn(accounts.Account{Address: val}, accounts.MimetypeParlia, ParliaRLP(header, p.chainConfig.ChainID)) if err != nil { return err } @@ -839,7 +839,7 @@ func (p *Parlia) Seal(chain consensus.ChainReader, block *types.Block, results c select { case results <- block.WithSeal(header): default: - log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header)) + log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header, p.chainConfig.ChainID)) } }() @@ -869,7 +869,7 @@ func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int { // SealHash returns the hash of a block prior to it being sealed. func (p *Parlia) SealHash(header *types.Header) common.Hash { - return SealHash(header) + return SealHash(header, p.chainConfig.ChainID) } // APIs implements consensus.Engine, returning the user facing RPC API to query snapshot. @@ -1109,15 +1109,16 @@ func (p *Parlia) applyTransaction( // =========================== utility function ========================== // SealHash returns the hash of a block prior to it being sealed. -func SealHash(header *types.Header) (hash common.Hash) { +func SealHash(header *types.Header, chainId *big.Int) (hash common.Hash) { hasher := sha3.NewLegacyKeccak256() - encodeSigHeader(hasher, header) + encodeSigHeader(hasher, header, chainId) hasher.Sum(hash[:0]) return hash } -func encodeSigHeader(w io.Writer, header *types.Header) { +func encodeSigHeader(w io.Writer, header *types.Header, chainId *big.Int) { err := rlp.Encode(w, []interface{}{ + chainId, header.ParentHash, header.UncleHash, header.Coinbase, diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 30d1c5d115..b1af95f5fc 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "errors" + "math/big" "sort" "github.com/ethereum/go-ethereum/common" @@ -123,7 +124,7 @@ func (s *Snapshot) copy() *Snapshot { return cpy } -func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainReader, parents []*types.Header) (*Snapshot, error) { +func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainReader, parents []*types.Header, chainId *big.Int) (*Snapshot, error) { // Allow passing in no headers for cleaner code if len(headers) == 0 { return s, nil @@ -153,7 +154,7 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainReader, p delete(snap.Recents, number-limit) } // Resolve the authorization key and check against signers - validator, err := ecrecover(header, s.sigCache) + validator, err := ecrecover(header, s.sigCache, chainId) if err != nil { return nil, err } diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go index 38c027d7a8..2031716b26 100644 --- a/signer/core/signed_data.go +++ b/signer/core/signed_data.go @@ -285,7 +285,7 @@ func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType header.Extra = newExtra } // Get back the rlp data, encoded by us - sighash, parliaRlp, err := parliaHeaderHashAndRlp(header) + sighash, parliaRlp, err := parliaHeaderHashAndRlp(header, api.chainID) if err != nil { return nil, useEthereumV, err } @@ -351,13 +351,13 @@ func cliqueHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) return hash, rlp, err } -func parliaHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) { +func parliaHeaderHashAndRlp(header *types.Header, chainId *big.Int) (hash, rlp []byte, err error) { if len(header.Extra) < 65 { err = fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra)) return } - rlp = parlia.ParliaRLP(header) - hash = parlia.SealHash(header).Bytes() + rlp = parlia.ParliaRLP(header, chainId) + hash = parlia.SealHash(header, chainId).Bytes() return hash, rlp, err } From 04287ebe50defb398b17bd2812e6ea9abe2f808b Mon Sep 17 00:00:00 2001 From: zjubfd <296179868@qq.com> Date: Tue, 28 Jul 2020 10:25:46 +0800 Subject: [PATCH 2/4] reestimate the gas consumption for system tx when there is 41 validators (#19) --- params/protocol_params.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/protocol_params.go b/params/protocol_params.go index d759305cac..e0c09f3ced 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -29,7 +29,7 @@ const ( CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. - SystemTxsGas uint64 = 100000 // The gas reserved for system txs; only for parlia consensus + SystemTxsGas uint64 = 500000 // The gas reserved for system txs; only for parlia consensus TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. From 5cc893a5e4727e34095b8a5c9c283306f99327ff Mon Sep 17 00:00:00 2001 From: zjubfd <296179868@qq.com> Date: Tue, 28 Jul 2020 18:12:34 +0800 Subject: [PATCH 3/4] prepare for 1.0.0-beta.1 (#20) --- params/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/version.go b/params/version.go index d7cb0dd7d6..242756b709 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 0 // Minor version component of the current release VersionPatch = 0 // Patch version component of the current release - VersionMeta = "beta.0" // Version metadata to append to the version string + VersionMeta = "beta.1" // Version metadata to append to the version string ) // Version holds the textual version string. From 8124e60e9d2ae90734472a79f62a1633d2a2585a Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 4 Aug 2020 13:56:13 +0800 Subject: [PATCH 4/4] resolve best practice advice --- consensus/parlia/parlia.go | 12 ++++++------ core/vm/contracts_lightclient.go | 11 ++++++----- core/vm/contracts_lightclient_test.go | 1 + core/vm/lightclient/types.go | 10 ++-------- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 7d5b324df9..0b30a1bdf6 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -396,9 +396,9 @@ func (p *Parlia) verifyCascadingFields(chain consensus.ChainReader, header *type } // Verify that the gas limit is <= 2^63-1 - cap := uint64(0x7fffffffffffffff) - if header.GasLimit > cap { - return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap) + capacity := uint64(0x7fffffffffffffff) + if header.GasLimit > capacity { + return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, capacity) } // Verify that the gasUsed is <= gasLimit if header.GasUsed > header.GasLimit { @@ -809,7 +809,7 @@ func (p *Parlia) Seal(chain consensus.ChainReader, block *types.Block, results c } // Sweet, the protocol permits us to sign the block, wait for our time - delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple + delay := time.Until(time.Unix(int64(header.Time), 0)) // nolint: gosimple if header.Difficulty.Cmp(diffNoTurn) == 0 { // It's not our turn explicitly to sign, delay it a bit wiggle := time.Duration(len(snap.Validators)/2+1) * wiggleTime @@ -1072,8 +1072,8 @@ func (p *Parlia) applyTransaction( return errors.New("supposed to get a actual transaction, but get none") } actualTx := (*receivedTxs)[0] - if bytes.Compare(p.signer.Hash(actualTx).Bytes(), expectedHash.Bytes()) != 0 { - return errors.New(fmt.Sprintf("expected tx hash %v, get %v", expectedHash.String(), actualTx.Hash().String())) + if !bytes.Equal(p.signer.Hash(actualTx).Bytes(), expectedHash.Bytes()) { + return fmt.Errorf("expected tx hash %v, get %v", expectedHash.String(), actualTx.Hash().String()) } expectedTx = actualTx // move to next diff --git a/core/vm/contracts_lightclient.go b/core/vm/contracts_lightclient.go index aaab153b38..a91eb06be6 100644 --- a/core/vm/contracts_lightclient.go +++ b/core/vm/contracts_lightclient.go @@ -9,6 +9,7 @@ import ( ) const ( + uint64TypeLength uint64 = 8 precompileContractInputMetaDataLength uint64 = 32 consensusStateLengthBytesLength uint64 = 32 @@ -20,7 +21,7 @@ const ( // consensus state length | consensus state | tendermint header | // 32 bytes | | | func decodeTendermintHeaderValidationInput(input []byte) (*lightclient.ConsensusState, *lightclient.Header, error) { - csLen := binary.BigEndian.Uint64(input[consensusStateLengthBytesLength-8 : consensusStateLengthBytesLength]) + csLen := binary.BigEndian.Uint64(input[consensusStateLengthBytesLength-uint64TypeLength : consensusStateLengthBytesLength]) if uint64(len(input)) <= consensusStateLengthBytesLength+csLen { return nil, nil, fmt.Errorf("expected payload size %d, actual size: %d", consensusStateLengthBytesLength+csLen, len(input)) } @@ -55,7 +56,7 @@ func (c *tmHeaderValidate) Run(input []byte) (result []byte, err error) { return nil, fmt.Errorf("invalid input") } - payloadLength := binary.BigEndian.Uint64(input[precompileContractInputMetaDataLength-8 : precompileContractInputMetaDataLength]) + payloadLength := binary.BigEndian.Uint64(input[precompileContractInputMetaDataLength-uint64TypeLength : precompileContractInputMetaDataLength]) if uint64(len(input)) != payloadLength+precompileContractInputMetaDataLength { return nil, fmt.Errorf("invalid input: input size should be %d, actual the size is %d", payloadLength+precompileContractInputMetaDataLength, len(input)) } @@ -83,7 +84,7 @@ func (c *tmHeaderValidate) Run(input []byte) (result []byte, err error) { copy(lengthBytes[:1], []byte{0x01}) } consensusStateBytesLength := uint64(len(consensusStateBytes)) - binary.BigEndian.PutUint64(lengthBytes[tmHeaderValidateResultMetaDataLength-8:], consensusStateBytesLength) + binary.BigEndian.PutUint64(lengthBytes[tmHeaderValidateResultMetaDataLength-uint64TypeLength:], consensusStateBytesLength) result = append(lengthBytes, consensusStateBytes...) @@ -113,7 +114,7 @@ func (c *iavlMerkleProofValidate) Run(input []byte) (result []byte, err error) { return nil, fmt.Errorf("invalid input: input should include %d bytes payload length and payload", precompileContractInputMetaDataLength) } - payloadLength := binary.BigEndian.Uint64(input[precompileContractInputMetaDataLength-8 : precompileContractInputMetaDataLength]) + payloadLength := binary.BigEndian.Uint64(input[precompileContractInputMetaDataLength-uint64TypeLength : precompileContractInputMetaDataLength]) if uint64(len(input)) != payloadLength+precompileContractInputMetaDataLength { return nil, fmt.Errorf("invalid input: input size should be %d, actual the size is %d", payloadLength+precompileContractInputMetaDataLength, len(input)) } @@ -129,6 +130,6 @@ func (c *iavlMerkleProofValidate) Run(input []byte) (result []byte, err error) { } result = make([]byte, merkleProofValidateResultLength) - binary.BigEndian.PutUint64(result[merkleProofValidateResultLength-8:], 0x01) + binary.BigEndian.PutUint64(result[merkleProofValidateResultLength-uint64TypeLength:], 0x01) return result, nil } diff --git a/core/vm/contracts_lightclient_test.go b/core/vm/contracts_lightclient_test.go index 41ea8e5c72..cf17c03559 100644 --- a/core/vm/contracts_lightclient_test.go +++ b/core/vm/contracts_lightclient_test.go @@ -24,6 +24,7 @@ func TestTmHeaderValidateAndMerkleProofValidate(t *testing.T) { "cc05d26c7b0be0c8b46418294171730e079f384fde2fa50bafc000000174876e80049b288e4ebbb3a281c2d546fc30253d5baf08993b6e5d295fb7" + "87a5b314a298e000000174876e80004224339688f012e649de48e241880092eaa8f6aa0f4f14bfcf9e0c76917c0b6000000174876e8004034b37ce" + "da8a0bf13b1abaeee7a8f9383542099a554d219b93d0ce69e3970e8000000174876e800") + require.NoError(t, err) cs, err := lightclient.DecodeConsensusState(consensusStateBytes) require.NoError(t, err) diff --git a/core/vm/lightclient/types.go b/core/vm/lightclient/types.go index a2ab51814b..406d428e74 100644 --- a/core/vm/lightclient/types.go +++ b/core/vm/lightclient/types.go @@ -226,17 +226,11 @@ func (kvmp *KeyValueMerkleProof) Validate() bool { if len(kvmp.Value) == 0 { err := prt.VerifyAbsence(kvmp.Proof, kvmp.AppHash, kp.String()) - if err != nil { - return false - } - return true + return err == nil } err := prt.VerifyValue(kvmp.Proof, kvmp.AppHash, kp.String(), kvmp.Value) - if err != nil { - return false - } - return true + return err == nil } // input: