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 TIPXDCXCancellationFeeTestnet = big.NewInt(38383838)
var BerlinBlock = big.NewInt(9999999999) var BerlinBlock = big.NewInt(9999999999)
var LondonBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999)
var MergeBlock = big.NewInt(9999999999)
var TIPXDCXTestnet = big.NewInt(38383838) var TIPXDCXTestnet = big.NewInt(38383838)
var IsTestnet bool = false var IsTestnet bool = false

View file

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

View file

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

View file

@ -23,17 +23,23 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
) )
// NewEVMContext creates a new context for use in the EVM. // 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 { 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 // 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 { if author == nil {
beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation
} else { } else {
beneficiary = *author 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{ return vm.Context{
CanTransfer: CanTransfer, CanTransfer: CanTransfer,
Transfer: Transfer, Transfer: Transfer,
@ -45,6 +51,7 @@ func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainConte
Difficulty: new(big.Int).Set(header.Difficulty), Difficulty: new(big.Int).Set(header.Difficulty),
GasLimit: header.GasLimit, GasLimit: header.GasLimit,
GasPrice: new(big.Int).Set(msg.GasPrice()), 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 BlockNumber *big.Int // Provides information for NUMBER
Time *big.Int // Provides information for TIME Time *big.Int // Provides information for TIME
Difficulty *big.Int // Provides information for DIFFICULTY Difficulty *big.Int // Provides information for DIFFICULTY
Random *common.Hash // Provides information for PREVRANDAO
} }
// EVM is the Ethereum Virtual Machine base object and provides // 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 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) { func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
return nil, nil return nil, nil

View file

@ -21,6 +21,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
"math/big"
"os" "os"
"testing" "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 jump table was not initialised we set the default one.
if cfg.JumpTable == nil { if cfg.JumpTable == nil {
switch { switch {
case evm.chainRules.IsMerge:
cfg.JumpTable = &mergeInstructionSet
case evm.chainRules.IsLondon: case evm.chainRules.IsLondon:
cfg.JumpTable = &londonInstructionSet cfg.JumpTable = &londonInstructionSet
case evm.chainRules.IsBerlin: case evm.chainRules.IsBerlin:
@ -262,7 +264,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} }
pc++ pc++
} }
if err == errStopToken { if err == errStopToken {
err = nil // clear stop token error err = nil // clear stop token error
} }

View file

@ -52,11 +52,23 @@ var (
istanbulInstructionSet = newIstanbulInstructionSet() istanbulInstructionSet = newIstanbulInstructionSet()
berlinInstructionSet = newBerlinInstructionSet() berlinInstructionSet = newBerlinInstructionSet()
londonInstructionSet = newLondonInstructionSet() londonInstructionSet = newLondonInstructionSet()
mergeInstructionSet = newMergeInstructionSet()
) )
// JumpTable contains the EVM opcodes supported at a given fork. // JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation 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, // newLondonInstructionSet returns the frontier, homestead, byzantium,
// constantinople, istanbul, petersburg, berlin and london instructions. // constantinople, istanbul, petersburg, berlin and london instructions.
func newLondonInstructionSet() JumpTable { func newLondonInstructionSet() JumpTable {

View file

@ -98,6 +98,8 @@ const (
TIMESTAMP OpCode = 0x42 TIMESTAMP OpCode = 0x42
NUMBER OpCode = 0x43 NUMBER OpCode = 0x43
DIFFICULTY OpCode = 0x44 DIFFICULTY OpCode = 0x44
RANDOM OpCode = 0x44 // Same as DIFFICULTY
PREVRANDAO OpCode = 0x44 // Same as DIFFICULTY
GASLIMIT OpCode = 0x45 GASLIMIT OpCode = 0x45
CHAINID OpCode = 0x46 CHAINID OpCode = 0x46
SELFBALANCE OpCode = 0x47 SELFBALANCE OpCode = 0x47
@ -277,7 +279,7 @@ var opCodeToString = [256]string{
COINBASE: "COINBASE", COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP", TIMESTAMP: "TIMESTAMP",
NUMBER: "NUMBER", NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY", DIFFICULTY: "DIFFICULTY", // TODO rename to PREVRANDAO post merge
GASLIMIT: "GASLIMIT", GASLIMIT: "GASLIMIT",
CHAINID: "CHAINID", CHAINID: "CHAINID",
SELFBALANCE: "SELFBALANCE", 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) 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) 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 // Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"` Ethash *EthashConfig `json:"ethash,omitempty"`
@ -500,7 +498,7 @@ func (c *ChainConfig) String() string {
default: default:
engine = "unknown" 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.ChainId,
c.HomesteadBlock, c.HomesteadBlock,
c.DAOForkBlock, c.DAOForkBlock,
@ -510,8 +508,10 @@ func (c *ChainConfig) String() string {
c.EIP158Block, c.EIP158Block,
c.ByzantiumBlock, c.ByzantiumBlock,
c.ConstantinopleBlock, c.ConstantinopleBlock,
c.BerlinBlock, common.TIPXDCXCancellationFee,
c.LondonBlock, common.BerlinBlock,
common.LondonBlock,
common.MergeBlock,
engine, engine,
) )
} }
@ -568,6 +568,12 @@ func (c *ChainConfig) IsLondon(num *big.Int) bool {
return isForked(common.LondonBlock, num) 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 { func (c *ChainConfig) IsTIP2019(num *big.Int) bool {
return isForked(common.TIP2019Block, num) 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) { if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) 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 return nil
} }
@ -744,6 +743,7 @@ type Rules struct {
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon bool IsBerlin, IsLondon bool
IsMerge bool
} }
func (c *ChainConfig) Rules(num *big.Int) Rules { func (c *ChainConfig) Rules(num *big.Int) Rules {
@ -762,6 +762,7 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
IsPetersburg: c.IsPetersburg(num), IsPetersburg: c.IsPetersburg(num),
IsIstanbul: c.IsIstanbul(num), IsIstanbul: c.IsIstanbul(num),
IsBerlin: c.IsBerlin(num), IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num), IsLondon: c.IsLondon(num),
IsMerge: c.IsMerge(num),
} }
} }