implement EIP-4399, PREVRANDAO opcode.

and remove unused LondonBlock and BerlinBlock in `params/config.go` (already defined in constants.go)
This commit is contained in:
Gerui Wang 2024-03-02 18:45:28 +08:00
parent 928a0691ca
commit a31489541d
11 changed files with 89 additions and 16 deletions

View file

@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(38383838)
var TIPXDCXCancellationFeeTestnet = big.NewInt(38383838)
var BerlinBlock = big.NewInt(9999999999)
var LondonBlock = big.NewInt(9999999999)
var MergeBlock = big.NewInt(9999999999)
var TIPXDCXTestnet = big.NewInt(38383838)
var IsTestnet bool = false

View file

@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(225000)
var TIPXDCXCancellationFeeTestnet = big.NewInt(225000)
var BerlinBlock = big.NewInt(9999999999)
var LondonBlock = big.NewInt(9999999999)
var MergeBlock = big.NewInt(9999999999)
var TIPXDCXTestnet = big.NewInt(0)
var IsTestnet bool = false

View file

@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(23779191)
var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191)
var BerlinBlock = big.NewInt(9999999999)
var LondonBlock = big.NewInt(9999999999)
var MergeBlock = big.NewInt(9999999999)
var TIPXDCXTestnet = big.NewInt(23779191)
var IsTestnet bool = false

View file

@ -23,17 +23,23 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
)
// NewEVMContext creates a new context for use in the EVM.
func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainContext, author *common.Address) vm.Context {
// If we don't have an explicit author (i.e. not mining), extract from the header
var beneficiary common.Address
var (
beneficiary common.Address
random common.Hash
)
if author == nil {
beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation
} else {
beneficiary = *author
}
// since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random
random = crypto.Keccak256Hash(header.Number.Bytes())
return vm.Context{
CanTransfer: CanTransfer,
Transfer: Transfer,
@ -45,6 +51,7 @@ func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainConte
Difficulty: new(big.Int).Set(header.Difficulty),
GasLimit: header.GasLimit,
GasPrice: new(big.Int).Set(msg.GasPrice()),
Random: &random,
}
}

View file

@ -107,6 +107,7 @@ type Context struct {
BlockNumber *big.Int // Provides information for NUMBER
Time *big.Int // Provides information for TIME
Difficulty *big.Int // Provides information for DIFFICULTY
Random *common.Hash // Provides information for PREVRANDAO
}
// EVM is the Ethereum Virtual Machine base object and provides

View file

@ -477,6 +477,17 @@ func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
return nil, nil
}
func opRandom(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var v *uint256.Int
if interpreter.evm.Context.Random != nil {
v = new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes()))
} else { // if context random is not set, use emptyCodeHash as default
v = new(uint256.Int).SetBytes(emptyCodeHash.Bytes())
}
callContext.stack.push(v)
return nil, nil
}
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
return nil, nil

View file

@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"log"
"math/big"
"os"
"testing"
@ -661,3 +662,36 @@ func TestCreate2Addreses(t *testing.T) {
}
}
}
func TestRandom(t *testing.T) {
type testcase struct {
name string
random common.Hash
}
for _, tt := range []testcase{
{name: "empty hash", random: common.Hash{}},
{name: "1", random: common.Hash{0}},
{name: "emptyCodeHash", random: emptyCodeHash},
{name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})},
} {
var (
env = NewEVM(Context{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
opRandom(&pc, evmInterpreter, &callCtx{nil, stack, nil})
if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
}
actual := stack.pop()
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes()))
if overflow {
t.Errorf("Testcase %v: invalid overflow", tt.name)
}
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %v: expected %x, got %x", tt.name, expected, actual)
}
}
}

View file

@ -93,6 +93,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// If jump table was not initialised we set the default one.
if cfg.JumpTable == nil {
switch {
case evm.chainRules.IsMerge:
cfg.JumpTable = &mergeInstructionSet
case evm.chainRules.IsLondon:
cfg.JumpTable = &londonInstructionSet
case evm.chainRules.IsBerlin:
@ -262,7 +264,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
pc++
}
if err == errStopToken {
err = nil // clear stop token error
}

View file

@ -52,11 +52,23 @@ var (
istanbulInstructionSet = newIstanbulInstructionSet()
berlinInstructionSet = newBerlinInstructionSet()
londonInstructionSet = newLondonInstructionSet()
mergeInstructionSet = newMergeInstructionSet()
)
// JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation
func newMergeInstructionSet() JumpTable {
instructionSet := newLondonInstructionSet()
instructionSet[PREVRANDAO] = &operation{
execute: opRandom,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
return instructionSet
}
// newLondonInstructionSet returns the frontier, homestead, byzantium,
// constantinople, istanbul, petersburg, berlin and london instructions.
func newLondonInstructionSet() JumpTable {

View file

@ -98,6 +98,8 @@ const (
TIMESTAMP OpCode = 0x42
NUMBER OpCode = 0x43
DIFFICULTY OpCode = 0x44
RANDOM OpCode = 0x44 // Same as DIFFICULTY
PREVRANDAO OpCode = 0x44 // Same as DIFFICULTY
GASLIMIT OpCode = 0x45
CHAINID OpCode = 0x46
SELFBALANCE OpCode = 0x47
@ -277,7 +279,7 @@ var opCodeToString = [256]string{
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY",
DIFFICULTY: "DIFFICULTY", // TODO rename to PREVRANDAO post merge
GASLIMIT: "GASLIMIT",
CHAINID: "CHAINID",
SELFBALANCE: "SELFBALANCE",

View file

@ -362,8 +362,6 @@ type ChainConfig struct {
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin)
LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london)
// Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"`
@ -500,7 +498,7 @@ func (c *ChainConfig) String() string {
default:
engine = "unknown"
}
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v BerlinBlock: %v LondonBlock: %v Engine: %v}",
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %v Engine: %v}",
c.ChainId,
c.HomesteadBlock,
c.DAOForkBlock,
@ -510,8 +508,10 @@ func (c *ChainConfig) String() string {
c.EIP158Block,
c.ByzantiumBlock,
c.ConstantinopleBlock,
c.BerlinBlock,
c.LondonBlock,
common.TIPXDCXCancellationFee,
common.BerlinBlock,
common.LondonBlock,
common.MergeBlock,
engine,
)
}
@ -568,6 +568,12 @@ func (c *ChainConfig) IsLondon(num *big.Int) bool {
return isForked(common.LondonBlock, num)
}
// IsMerge returns whether num is either equal to the Merge fork block or greater.
// Different from Geth which uses `block.difficulty != nil`
func (c *ChainConfig) IsMerge(num *big.Int) bool {
return isForked(common.MergeBlock, num)
}
func (c *ChainConfig) IsTIP2019(num *big.Int) bool {
return isForked(common.TIP2019Block, num)
}
@ -669,13 +675,6 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
}
if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) {
return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock)
}
if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) {
return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock)
}
return nil
}
@ -744,6 +743,7 @@ type Rules struct {
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon bool
IsMerge bool
}
func (c *ChainConfig) Rules(num *big.Int) Rules {
@ -762,6 +762,7 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
IsPetersburg: c.IsPetersburg(num),
IsIstanbul: c.IsIstanbul(num),
IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num),
IsLondon: c.IsLondon(num),
IsMerge: c.IsMerge(num),
}
}