From 87fb0116e531772840c4599904701d7a9eda8331 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Thu, 13 May 2021 15:40:02 -0700 Subject: [PATCH] Add vrf as a precompile in evm --- consensus/consensus_v2.go | 2 +- core/evm.go | 6 ++++++ core/vm/contracts.go | 32 ++++++++++++++++++++++++++++++++ core/vm/evm.go | 13 +++++++++++++ core/vm/interpreter.go | 1 + core/vm/runtime/env.go | 1 + core/vm/runtime/runtime.go | 1 + internal/params/config.go | 7 ++++--- 8 files changed, 59 insertions(+), 4 deletions(-) diff --git a/consensus/consensus_v2.go b/consensus/consensus_v2.go index 5e0188cf2..06e8874aa 100644 --- a/consensus/consensus_v2.go +++ b/consensus/consensus_v2.go @@ -724,7 +724,7 @@ func (consensus *Consensus) GenerateVrfAndProof(newHeader *block.Header) error { consensus.getLogger().Info(). Uint64("BlockNum", newHeader.Number().Uint64()). Uint64("Epoch", newHeader.Epoch().Uint64()). - Bytes("VRF+Proof", newHeader.Vrf()). + Hex("VRF+Proof", newHeader.Vrf()). Msg("[GenerateVrfAndProof] Leader generated a VRF") return nil diff --git a/core/evm.go b/core/evm.go index d2e1b73e2..c58a4f8c8 100644 --- a/core/evm.go +++ b/core/evm.go @@ -55,6 +55,11 @@ func NewEVMContext(msg Message, header *block.Header, chain ChainContext, author } else { beneficiary = *author } + vrf := common.Hash{} + if len(header.Vrf()) >= 32 { + vrfAndProof := header.Vrf() + copy(vrf[:], vrfAndProof[:32]) + } return vm.Context{ CanTransfer: CanTransfer, Transfer: Transfer, @@ -64,6 +69,7 @@ func NewEVMContext(msg Message, header *block.Header, chain ChainContext, author Coinbase: beneficiary, BlockNumber: header.Number(), EpochNumber: header.Epoch(), + VRF: vrf, Time: header.Time(), GasLimit: header.GasLimit(), GasPrice: new(big.Int).Set(msg.GasPrice()), diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 25a478275..2f5042804 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -76,6 +76,21 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{9}): &blake2F{}, } +// PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum +// contracts used in the Istanbul release. +var PrecompiledContractsVRF = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{255}): &vrf{}, +} + // RunPrecompiledContract runs and evaluates the output of a precompiled contract. func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { gas := p.RequiredGas(input) @@ -501,3 +516,20 @@ func (c *blake2F) Run(input []byte) ([]byte, error) { } return output, nil } + +// VRF implemented as a native contract. +type vrf struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *vrf) RequiredGas(input []byte) uint64 { + return GasQuickStep +} + +func (c *vrf) Run(input []byte) ([]byte, error) { + // Note the input was overwritten with the vrf of the block. + // So here we simply return it + return append([]byte{}, input...), nil +} diff --git a/core/vm/evm.go b/core/vm/evm.go index d2f6e8a94..080e5febd 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -54,7 +54,14 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err if evm.chainRules.IsIstanbul { precompiles = PrecompiledContractsIstanbul } + if evm.chainRules.IsVRF { + precompiles = PrecompiledContractsVRF + } if p := precompiles[*contract.CodeAddr]; p != nil { + if _, ok := p.(*vrf); ok { + // Override the input with vrf data of the current block so it can be returned to the contract program. + input = evm.Context.VRF.Bytes() + } return RunPrecompiledContract(p, input, contract) } } @@ -69,6 +76,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err evm.interpreter = interpreter } return interpreter.Run(contract, input, readOnly) + } } return nil, ErrNoCompatibleInterpreter @@ -99,6 +107,7 @@ type Context struct { BlockNumber *big.Int // Provides information for NUMBER EpochNumber *big.Int // Provides information for EPOCH Time *big.Int // Provides information for TIME + VRF common.Hash // Provides information for VRF TxType types.TransactionType } @@ -225,6 +234,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if evm.chainRules.IsIstanbul { precompiles = PrecompiledContractsIstanbul } + if evm.chainRules.IsVRF { + precompiles = PrecompiledContractsVRF + } + if precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug && evm.depth == 0 { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 0682a1c84..a50babdc0 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -271,6 +271,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // execute the operation res, err = operation.execute(&pc, in, contract, mem, stack) + // verifyPool is a build flag. Pool verification makes sure the integrity // of the integer pool by comparing values to a default value. if verifyPool { diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 8b59d8edb..3fa00ceb2 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -34,6 +34,7 @@ func NewEnv(cfg *Config) *vm.EVM { Coinbase: cfg.Coinbase, BlockNumber: cfg.BlockNumber, EpochNumber: cfg.EpochNumber, + VRF: cfg.VRF, Time: cfg.Time, GasLimit: cfg.GasLimit, GasPrice: cfg.GasPrice, diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 4f67349ee..8ebba4030 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -38,6 +38,7 @@ type Config struct { Coinbase common.Address BlockNumber *big.Int EpochNumber *big.Int + VRF common.Hash Time *big.Int GasLimit uint64 GasPrice *big.Int diff --git a/internal/params/config.go b/internal/params/config.go index 18f2dd319..69b0ee7a7 100644 --- a/internal/params/config.go +++ b/internal/params/config.go @@ -468,9 +468,9 @@ func isForked(s, epoch *big.Int) bool { // Rules is a one time interface meaning that it shouldn't be used in between transition // phases. type Rules struct { - ChainID *big.Int - EthChainID *big.Int - IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul bool + ChainID *big.Int + EthChainID *big.Int + IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul, IsVRF bool } // Rules ensures c's ChainID is not nil. @@ -491,5 +491,6 @@ func (c *ChainConfig) Rules(epoch *big.Int) Rules { IsS3: c.IsS3(epoch), IsReceiptLog: c.IsReceiptLog(epoch), IsIstanbul: c.IsIstanbul(epoch), + IsVRF: c.IsVRF(epoch), } }