From 3ede80953bda3c4c8e448c284a5abf50820f7da5 Mon Sep 17 00:00:00 2001 From: Martin Chrzanowski Date: Fri, 16 Aug 2019 12:06:54 -0700 Subject: [PATCH 1/4] Expose validator list from istanbul engine --- consensus/clique/clique.go | 5 +++++ consensus/consensus.go | 4 ++++ consensus/ethash/consensus.go | 5 +++++ consensus/istanbul/backend/backend.go | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 69b16b4949..ef47b64cdd 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -268,6 +269,10 @@ func (c *Clique) Author(header *types.Header) (common.Address, error) { return ecrecover(header, c.signatures) } +func (c *Clique) GetValidators(proposal istanbul.Proposal) []istanbul.Validator { + return []istanbul.Validator{} +} + // VerifyHeader checks whether a header conforms to the consensus rules. func (c *Clique) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { return c.verifyHeader(chain, header, nil) diff --git a/consensus/consensus.go b/consensus/consensus.go index 8b87c98d36..669d8d1c53 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -21,6 +21,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" @@ -102,6 +103,9 @@ type Engine interface { // that a new block should have. CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int + // GetValidators returns the list of current validators. + GetValidators(proposal istanbul.Proposal) []istanbul.Validator + // APIs returns the RPC APIs this consensus engine provides. APIs(chain ChainReader) []rpc.API diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 320dd70465..d32fdd925e 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/istanbul" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -80,6 +81,10 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) { return header.Coinbase, nil } +func (ethash *Ethash) GetValidators(proposal istanbul.Proposal) []istanbul.Validator { + return []istanbul.Validator{} +} + // VerifyHeader checks whether a header conforms to the consensus rules of the // stock Ethereum ethash engine. func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index 0ef9005f83..fc9d737860 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -156,6 +156,11 @@ func (sb *Backend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet return sb.getValidators(proposal.Number().Uint64(), proposal.Hash()) } +func (sb *Backend) GetValidators(proposal istanbul.Proposal) []istanbul.Validator { + validatorSet := sb.getValidators(proposal.Number().Uint64(), proposal.Hash()) + return validatorSet.FilteredList() +} + // Broadcast implements istanbul.Backend.Broadcast func (sb *Backend) Broadcast(valSet istanbul.ValidatorSet, payload []byte) error { // send to others From 70ddd64485bf29f502c6ae81c740dc21f2557635 Mon Sep 17 00:00:00 2001 From: Martin Chrzanowski Date: Mon, 19 Aug 2019 16:07:28 -0700 Subject: [PATCH 2/4] Return validator address from precompile --- consensus/clique/clique.go | 2 +- consensus/consensus.go | 2 +- consensus/ethash/consensus.go | 2 +- consensus/istanbul/backend/backend.go | 4 ++-- core/evm.go | 1 + core/vm/contracts.go | 23 +++++++++++++++++++++++ core/vm/evm.go | 3 +++ params/protocol_params.go | 1 + 8 files changed, 33 insertions(+), 5 deletions(-) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index ef47b64cdd..dd2629e7a3 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -269,7 +269,7 @@ func (c *Clique) Author(header *types.Header) (common.Address, error) { return ecrecover(header, c.signatures) } -func (c *Clique) GetValidators(proposal istanbul.Proposal) []istanbul.Validator { +func (c *Clique) GetValidators(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator { return []istanbul.Validator{} } diff --git a/consensus/consensus.go b/consensus/consensus.go index 669d8d1c53..d76e2a8b11 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -104,7 +104,7 @@ type Engine interface { CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int // GetValidators returns the list of current validators. - GetValidators(proposal istanbul.Proposal) []istanbul.Validator + GetValidators(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator // APIs returns the RPC APIs this consensus engine provides. APIs(chain ChainReader) []rpc.API diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index d32fdd925e..a08525bd7f 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -81,7 +81,7 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) { return header.Coinbase, nil } -func (ethash *Ethash) GetValidators(proposal istanbul.Proposal) []istanbul.Validator { +func (ethash *Ethash) GetValidators(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator { return []istanbul.Validator{} } diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index fc9d737860..7f435bcb87 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -156,8 +156,8 @@ func (sb *Backend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet return sb.getValidators(proposal.Number().Uint64(), proposal.Hash()) } -func (sb *Backend) GetValidators(proposal istanbul.Proposal) []istanbul.Validator { - validatorSet := sb.getValidators(proposal.Number().Uint64(), proposal.Hash()) +func (sb *Backend) GetValidators(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator { + validatorSet := sb.getValidators(blockNumber.Uint64(), headerHash) return validatorSet.FilteredList() } diff --git a/core/evm.go b/core/evm.go index a967501607..672d06d5d3 100644 --- a/core/evm.go +++ b/core/evm.go @@ -68,6 +68,7 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author Difficulty: new(big.Int).Set(header.Difficulty), GasLimit: header.GasLimit, GasPrice: new(big.Int).Set(msg.GasPrice()), + Engine: chain.Engine(), } } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 27b4a5053b..7358551b58 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -57,6 +57,7 @@ var requestAttestationAddress = common.BytesToAddress(append([]byte{0}, CeloPrec var transferAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 2))) var fractionMulExpAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 3))) var proofOfPossessionAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 4))) +var getValidatorAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 5))) // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum // contracts used in the Byzantium release. @@ -75,6 +76,7 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ transferAddress: &transfer{}, fractionMulExpAddress: &fractionMulExp{}, proofOfPossessionAddress: &proofOfPossession{}, + getValidatorAddress: &getValidator{}, } // RunPrecompiledContract runs and evaluates the output of a precompiled contract. @@ -588,3 +590,24 @@ func (c *proofOfPossession) Run(input []byte, caller common.Address, evm *EVM, g return true32Byte, gas, nil } + +type getValidator struct{} + +func (c *getValidator) RequiredGas(input []byte) uint64 { + return params.GetValidatorGas +} + +func (c *getValidator) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) { + index := (&big.Int{}).SetBytes(input[0:32]) + blockNumber := evm.Context.BlockNumber + validators := evm.Context.Engine.GetValidators(blockNumber, evm.Context.GetHash(blockNumber.Uint64())) + gas, err := debitRequiredGas(c, input, gas) + if err != nil { + return nil, gas, err + } + + validatorAddress := validators[index.Uint64()].Address() + addressBytes := common.LeftPadBytes(validatorAddress[:], 32) + + return addressBytes, gas, nil +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 8f29bb3c18..763c5a5bd0 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/contract_comm/errors" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -99,6 +100,8 @@ type Context struct { Difficulty *big.Int // Provides information for DIFFICULTY Header *types.Header + + Engine consensus.Engine } // EVM is the Ethereum Virtual Machine base object and provides diff --git a/params/protocol_params.go b/params/protocol_params.go index 5e7454b114..ff9219aede 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -97,6 +97,7 @@ const ( FractionMulExpGas uint64 = 1050 // Cost of performing multiplication and exponentiation of fractions to an exponent of up to 10^3. // TODO(kobigurk): Figure out what the actual gas cost of this contract should be. ProofOfPossessionGas uint64 = 1050 // Cost of verifying a BLS proof of possession. + GetValidatorGas uint64 = 5000 // Cost of reading a validator's address // Celo registered contracts names. // These names are taken from celo-monorepo/packages/protocol/lib/registry-utils.ts From e5efa747d8d123e0081e7eb74c3c99ed48cf22e5 Mon Sep 17 00:00:00 2001 From: Martin Chrzanowski Date: Fri, 6 Sep 2019 13:41:37 -0700 Subject: [PATCH 3/4] Handle OOB, expose set size precompile --- core/vm/contracts.go | 26 ++++++++++++++++++++++++++ core/vm/errors.go | 1 + 2 files changed, 27 insertions(+) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 7358551b58..17a64844b6 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -58,6 +58,7 @@ var transferAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledCo var fractionMulExpAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 3))) var proofOfPossessionAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 4))) var getValidatorAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 5))) +var numberValidatorsAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 6))) // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum // contracts used in the Byzantium release. @@ -77,6 +78,7 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ fractionMulExpAddress: &fractionMulExp{}, proofOfPossessionAddress: &proofOfPossession{}, getValidatorAddress: &getValidator{}, + numberValidatorsAddress: &numberValidators{}, } // RunPrecompiledContract runs and evaluates the output of a precompiled contract. @@ -606,8 +608,32 @@ func (c *getValidator) Run(input []byte, caller common.Address, evm *EVM, gas ui return nil, gas, err } + if index.Cmp(big.NewInt(int64(len(validators)))) >= 0 { + return nil, gas, ErrValidatorsOutOfBounds + } + validatorAddress := validators[index.Uint64()].Address() addressBytes := common.LeftPadBytes(validatorAddress[:], 32) return addressBytes, gas, nil } + +type numberValidators struct{} + +func (c *numberValidators) RequiredGas(input []byte) uint64 { + return params.GetValidatorGas +} + +func (c *numberValidators) Run(input []byte, caller common.Address, evm *EVM, gas uint64) ([]byte, uint64, error) { + blockNumber := evm.Context.BlockNumber + validators := evm.Context.Engine.GetValidators(blockNumber, evm.Context.GetHash(blockNumber.Uint64())) + gas, err := debitRequiredGas(c, input, gas) + if err != nil { + return nil, gas, err + } + + numberValidators := big.NewInt(int64(len(validators))).Bytes() + numberValidatorsBytes := common.LeftPadBytes(numberValidators[:], 32) + + return numberValidatorsBytes, gas, nil +} diff --git a/core/vm/errors.go b/core/vm/errors.go index 7f88f324ea..d21a02b4b4 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -27,4 +27,5 @@ var ( ErrInsufficientBalance = errors.New("insufficient balance for transfer") ErrContractAddressCollision = errors.New("contract address collision") ErrNoCompatibleInterpreter = errors.New("no compatible interpreter") + ErrValidatorsOutOfBounds = errors.New("getValidators out of bounds") ) From 567dcc237cbe6af07cb4c474b91bd2c65381be96 Mon Sep 17 00:00:00 2001 From: Martin Chrzanowski Date: Mon, 9 Sep 2019 13:15:21 -0700 Subject: [PATCH 4/4] Avoid nil pointer dereference in unit test --- core/chain_makers.go | 2 +- core/evm.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/chain_makers.go b/core/chain_makers.go index 57707d4231..87e85677c7 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -91,7 +91,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { // further limitations on the content of transactions that can be // added. If contract code relies on the BLOCKHASH instruction, // the block in chain will be returned. -func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { +func (b *BlockGen) AddTxWithChain(bc ChainContext, tx *types.Transaction) { if b.gasPool == nil { b.SetCoinbase(common.Address{}) } diff --git a/core/evm.go b/core/evm.go index 672d06d5d3..ad5944398e 100644 --- a/core/evm.go +++ b/core/evm.go @@ -57,6 +57,11 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author beneficiary = *author } + var engine consensus.Engine + if chain != nil { + engine = chain.Engine() + } + return vm.Context{ CanTransfer: CanTransfer, Transfer: Transfer, @@ -68,7 +73,7 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author Difficulty: new(big.Int).Set(header.Difficulty), GasLimit: header.GasLimit, GasPrice: new(big.Int).Set(msg.GasPrice()), - Engine: chain.Engine(), + Engine: engine, } }