Add vrf as a precompile in evm

pull/3703/head
Rongjian Lan 4 years ago
parent d4f48df8e9
commit 87fb0116e5
  1. 2
      consensus/consensus_v2.go
  2. 6
      core/evm.go
  3. 32
      core/vm/contracts.go
  4. 13
      core/vm/evm.go
  5. 1
      core/vm/interpreter.go
  6. 1
      core/vm/runtime/env.go
  7. 1
      core/vm/runtime/runtime.go
  8. 7
      internal/params/config.go

@ -724,7 +724,7 @@ func (consensus *Consensus) GenerateVrfAndProof(newHeader *block.Header) error {
consensus.getLogger().Info(). consensus.getLogger().Info().
Uint64("BlockNum", newHeader.Number().Uint64()). Uint64("BlockNum", newHeader.Number().Uint64()).
Uint64("Epoch", newHeader.Epoch().Uint64()). Uint64("Epoch", newHeader.Epoch().Uint64()).
Bytes("VRF+Proof", newHeader.Vrf()). Hex("VRF+Proof", newHeader.Vrf()).
Msg("[GenerateVrfAndProof] Leader generated a VRF") Msg("[GenerateVrfAndProof] Leader generated a VRF")
return nil return nil

@ -55,6 +55,11 @@ func NewEVMContext(msg Message, header *block.Header, chain ChainContext, author
} else { } else {
beneficiary = *author beneficiary = *author
} }
vrf := common.Hash{}
if len(header.Vrf()) >= 32 {
vrfAndProof := header.Vrf()
copy(vrf[:], vrfAndProof[:32])
}
return vm.Context{ return vm.Context{
CanTransfer: CanTransfer, CanTransfer: CanTransfer,
Transfer: Transfer, Transfer: Transfer,
@ -64,6 +69,7 @@ func NewEVMContext(msg Message, header *block.Header, chain ChainContext, author
Coinbase: beneficiary, Coinbase: beneficiary,
BlockNumber: header.Number(), BlockNumber: header.Number(),
EpochNumber: header.Epoch(), EpochNumber: header.Epoch(),
VRF: vrf,
Time: header.Time(), Time: header.Time(),
GasLimit: header.GasLimit(), GasLimit: header.GasLimit(),
GasPrice: new(big.Int).Set(msg.GasPrice()), GasPrice: new(big.Int).Set(msg.GasPrice()),

@ -76,6 +76,21 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{9}): &blake2F{}, 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. // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(input) gas := p.RequiredGas(input)
@ -501,3 +516,20 @@ func (c *blake2F) Run(input []byte) ([]byte, error) {
} }
return output, nil 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
}

@ -54,7 +54,14 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
if evm.chainRules.IsIstanbul { if evm.chainRules.IsIstanbul {
precompiles = PrecompiledContractsIstanbul precompiles = PrecompiledContractsIstanbul
} }
if evm.chainRules.IsVRF {
precompiles = PrecompiledContractsVRF
}
if p := precompiles[*contract.CodeAddr]; p != nil { 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) return RunPrecompiledContract(p, input, contract)
} }
} }
@ -69,6 +76,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
evm.interpreter = interpreter evm.interpreter = interpreter
} }
return interpreter.Run(contract, input, readOnly) return interpreter.Run(contract, input, readOnly)
} }
} }
return nil, ErrNoCompatibleInterpreter return nil, ErrNoCompatibleInterpreter
@ -99,6 +107,7 @@ type Context struct {
BlockNumber *big.Int // Provides information for NUMBER BlockNumber *big.Int // Provides information for NUMBER
EpochNumber *big.Int // Provides information for EPOCH EpochNumber *big.Int // Provides information for EPOCH
Time *big.Int // Provides information for TIME Time *big.Int // Provides information for TIME
VRF common.Hash // Provides information for VRF
TxType types.TransactionType TxType types.TransactionType
} }
@ -225,6 +234,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if evm.chainRules.IsIstanbul { if evm.chainRules.IsIstanbul {
precompiles = PrecompiledContractsIstanbul precompiles = PrecompiledContractsIstanbul
} }
if evm.chainRules.IsVRF {
precompiles = PrecompiledContractsVRF
}
if precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && value.Sign() == 0 { 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 // Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 { if evm.vmConfig.Debug && evm.depth == 0 {

@ -271,6 +271,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// execute the operation // execute the operation
res, err = operation.execute(&pc, in, contract, mem, stack) res, err = operation.execute(&pc, in, contract, mem, stack)
// verifyPool is a build flag. Pool verification makes sure the integrity // verifyPool is a build flag. Pool verification makes sure the integrity
// of the integer pool by comparing values to a default value. // of the integer pool by comparing values to a default value.
if verifyPool { if verifyPool {

@ -34,6 +34,7 @@ func NewEnv(cfg *Config) *vm.EVM {
Coinbase: cfg.Coinbase, Coinbase: cfg.Coinbase,
BlockNumber: cfg.BlockNumber, BlockNumber: cfg.BlockNumber,
EpochNumber: cfg.EpochNumber, EpochNumber: cfg.EpochNumber,
VRF: cfg.VRF,
Time: cfg.Time, Time: cfg.Time,
GasLimit: cfg.GasLimit, GasLimit: cfg.GasLimit,
GasPrice: cfg.GasPrice, GasPrice: cfg.GasPrice,

@ -38,6 +38,7 @@ type Config struct {
Coinbase common.Address Coinbase common.Address
BlockNumber *big.Int BlockNumber *big.Int
EpochNumber *big.Int EpochNumber *big.Int
VRF common.Hash
Time *big.Int Time *big.Int
GasLimit uint64 GasLimit uint64
GasPrice *big.Int GasPrice *big.Int

@ -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 // Rules is a one time interface meaning that it shouldn't be used in between transition
// phases. // phases.
type Rules struct { type Rules struct {
ChainID *big.Int ChainID *big.Int
EthChainID *big.Int EthChainID *big.Int
IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul bool IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul, IsVRF bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -491,5 +491,6 @@ func (c *ChainConfig) Rules(epoch *big.Int) Rules {
IsS3: c.IsS3(epoch), IsS3: c.IsS3(epoch),
IsReceiptLog: c.IsReceiptLog(epoch), IsReceiptLog: c.IsReceiptLog(epoch),
IsIstanbul: c.IsIstanbul(epoch), IsIstanbul: c.IsIstanbul(epoch),
IsVRF: c.IsVRF(epoch),
} }
} }

Loading…
Cancel
Save