diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 67ad9df11c..78ec9b4f81 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(blockNumber *big.Int, headerHash common.Hash) []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..d76e2a8b11 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(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 320dd70465..a08525bd7f 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(blockNumber *big.Int, headerHash common.Hash) []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..7f435bcb87 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(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator { + validatorSet := sb.getValidators(blockNumber.Uint64(), headerHash) + return validatorSet.FilteredList() +} + // Broadcast implements istanbul.Backend.Broadcast func (sb *Backend) Broadcast(valSet istanbul.ValidatorSet, payload []byte) error { // send to others 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 a967501607..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,6 +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: engine, } } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 27b4a5053b..17a64844b6 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -57,6 +57,8 @@ 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))) +var numberValidatorsAddress = common.BytesToAddress(append([]byte{0}, (CeloPrecompiledContractsAddressOffset - 6))) // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum // contracts used in the Byzantium release. @@ -75,6 +77,8 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ transferAddress: &transfer{}, fractionMulExpAddress: &fractionMulExp{}, proofOfPossessionAddress: &proofOfPossession{}, + getValidatorAddress: &getValidator{}, + numberValidatorsAddress: &numberValidators{}, } // RunPrecompiledContract runs and evaluates the output of a precompiled contract. @@ -588,3 +592,48 @@ 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 + } + + 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") ) 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 fe8b506679..4013ab1eef 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -98,6 +98,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 ) var (