core, params, cmd: implement eip1702

This commit is contained in:
rjl493456442 2019-07-10 15:46:30 +08:00
parent beff5fa578
commit a8711c035d
21 changed files with 292 additions and 139 deletions

View file

@ -203,10 +203,10 @@ func runCmd(ctx *cli.Context) error {
var leftOverGas uint64
if ctx.GlobalBool(CreateFlag.Name) {
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig)
ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig) // The default code version is 0.
} else {
if len(code) > 0 {
statedb.SetCode(receiver, code)
statedb.SetCode(receiver, code, 0)
}
ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig)
}

View file

@ -48,6 +48,7 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author
CanTransfer: CanTransfer,
Transfer: Transfer,
GetHash: GetHashFn(header, chain),
ValidateCode: ValidateCode,
Origin: msg.From(),
Coinbase: beneficiary,
BlockNumber: new(big.Int).Set(header.Number),
@ -95,3 +96,10 @@ func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int)
db.SubBalance(sender, amount)
db.AddBalance(recipient, amount)
}
// ValidateCode returns an indicator whether the given code is valid.
func ValidateCode(version uint64, code []byte) bool {
// When code version is 0, the validation does nothing and always succeeds.
// Future VM versions can define additional validation that has to be passed.
return true
}

View file

@ -248,7 +248,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
for addr, account := range g.Alloc {
statedb.AddBalance(addr, account.Balance)
statedb.SetCode(addr, account.Code)
statedb.SetCode(addr, account.Code, 0) // Should all genesis accounts' code version being 0?
statedb.SetNonce(addr, account.Nonce)
for key, value := range account.Storage {
statedb.SetState(addr, key, value)

View file

@ -34,6 +34,7 @@ type DumpAccount struct {
Root string `json:"root"`
CodeHash string `json:"codeHash"`
Code string `json:"code,omitempty"`
CodeVersion uint64 `json:"codeVersion"`
Storage map[common.Hash]string `json:"storage,omitempty"`
Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
@ -70,6 +71,7 @@ func (self iterativeDump) onAccount(addr common.Address, account DumpAccount) {
Root: account.Root,
CodeHash: account.CodeHash,
Code: account.Code,
CodeVersion: account.CodeVersion,
Storage: account.Storage,
SecureKey: account.SecureKey,
Address: nil,
@ -86,7 +88,7 @@ func (self iterativeDump) onRoot(root common.Hash) {
}
func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool) {
emptyAddress := (common.Address{})
emptyAddress := common.Address{}
missingPreimages := 0
c.onRoot(self.trie.Hash())
it := trie.NewIterator(self.trie.NodeIterator(nil))
@ -102,6 +104,7 @@ func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissi
Nonce: data.Nonce,
Root: common.Bytes2Hex(data.Root[:]),
CodeHash: common.Bytes2Hex(data.CodeHash),
CodeVersion: data.CodeVersion,
}
if emptyAddress == addr {
// Preimage missing

View file

@ -114,6 +114,7 @@ type (
codeChange struct {
account *common.Address
prevcode, prevhash []byte
version uint64
}
// Changes to other state values.
@ -188,7 +189,7 @@ func (ch nonceChange) dirtied() *common.Address {
}
func (ch codeChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode, ch.version)
}
func (ch codeChange) dirtied() *common.Address {

View file

@ -43,7 +43,6 @@ func (s Storage) String() (str string) {
for key, value := range s {
str += fmt.Sprintf("%X : %X\n", key, value)
}
return
}
@ -52,7 +51,6 @@ func (s Storage) Copy() Storage {
for key, value := range s {
cpy[key] = value
}
return cpy
}
@ -95,13 +93,75 @@ func (s *stateObject) empty() bool {
return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
}
// Account is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
// legacyStoredAccount is the legacy storage encoding of a state object used in
// database.
type legacyStoredAccount struct {
Nonce uint64 // The nonce of account.
Balance *big.Int // The balance of account.
Root common.Hash // Merkle root of the storage trie
CodeHash []byte // The code hash of account.
}
// storedAccount is the storage encoding of a state object used in database which
// includes code version field introduced by EIP1702.
type storedAccount Account
// Account is ethereum consensus representation of accounts. These objects are
// stored in the main account trie.
type Account struct {
Nonce uint64
Balance *big.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
Nonce uint64 // The nonce of account.
Balance *big.Int // The balance of account.
Root common.Hash // Merkle root of the storage trie.
CodeHash []byte // The code hash of account.
CodeVersion uint64 // The version of account code.
}
// EncodeRLP implements rlp.Encoder.
func (a *Account) EncodeRLP(w io.Writer) error {
if a.CodeVersion == 0 {
return rlp.Encode(w, legacyStoredAccount{a.Nonce, a.Balance, a.Root, a.CodeHash})
}
return rlp.Encode(w, storedAccount(*a))
}
// EncodeRLP implements rlp.Decoder.
func (a *Account) DecodeRLP(r *rlp.Stream) error {
blob, err := r.Raw()
if err != nil {
return err
}
elems, _, err := rlp.SplitList(blob)
if err != nil {
return err
}
switch c, _ := rlp.CountValues(elems); c {
case 4:
return decodeLegacyAccount(a, blob)
case 5:
return decodeAccount(a, blob)
default:
return fmt.Errorf("invalid number of list elements: %v", c)
}
}
// decodeLegacyAccount decodes the account information from legacy format blob.
func decodeLegacyAccount(a *Account, blob []byte) error {
var dec legacyStoredAccount
if err := rlp.DecodeBytes(blob, &dec); err != nil {
return err
}
a.Nonce, a.Balance, a.Root, a.CodeHash = dec.Nonce, dec.Balance, dec.Root, dec.CodeHash
return nil
}
// decodeAccount decodes the account information from current format blob.
func decodeAccount(a *Account, blob []byte) error {
var dec storedAccount
if err := rlp.DecodeBytes(blob, &dec); err != nil {
return err
}
*a = Account(dec)
return nil
}
// newObject creates a state object.
@ -124,7 +184,7 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
// EncodeRLP implements rlp.Encoder.
func (s *stateObject) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, s.data)
return rlp.Encode(w, &s.data)
}
// setError remembers the first non-nil error it is called with.
@ -355,19 +415,21 @@ func (s *stateObject) Code(db Database) []byte {
return code
}
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
func (s *stateObject) SetCode(codeHash common.Hash, code []byte, version uint64) {
prevcode := s.Code(s.db.db)
s.db.journal.append(codeChange{
account: &s.address,
prevhash: s.CodeHash(),
prevcode: prevcode,
version: s.data.CodeVersion,
})
s.setCode(codeHash, code)
s.setCode(codeHash, code, version)
}
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
func (s *stateObject) setCode(codeHash common.Hash, code []byte, version uint64) {
s.code = code
s.data.CodeHash = codeHash[:]
s.data.CodeVersion = version
s.dirtyCode = true
}
@ -395,6 +457,10 @@ func (s *stateObject) Nonce() uint64 {
return s.data.Nonce
}
func (s *stateObject) CodeVersion() uint64 {
return s.data.CodeVersion
}
// Never called, but must be present to allow stateObject to be used
// as a vm.Account interface that also satisfies the vm.ContractRef
// interface. Interfaces are awesome.

View file

@ -42,7 +42,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01}))
obj1.AddBalance(big.NewInt(22))
obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}, 0)
obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02}))
obj3.SetBalance(big.NewInt(44))
@ -60,20 +60,23 @@ func (s *StateSuite) TestDump(c *checker.C) {
"balance": "22",
"nonce": 0,
"root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"codeVersion": 0
},
"0x0000000000000000000000000000000000000002": {
"balance": "44",
"nonce": 0,
"root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"codeVersion": 0
},
"0x0000000000000000000000000000000000000102": {
"balance": "0",
"nonce": 0,
"root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
"code": "03030303030303"
"code": "03030303030303",
"codeVersion": 0
}
}
}`
@ -153,7 +156,7 @@ func TestSnapshot2(t *testing.T) {
so0 := state.getStateObject(stateobjaddr0)
so0.SetBalance(big.NewInt(42))
so0.SetNonce(43)
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}, 0)
so0.suicided = false
so0.deleted = false
state.setStateObject(so0)
@ -165,7 +168,7 @@ func TestSnapshot2(t *testing.T) {
so1 := state.getStateObject(stateobjaddr1)
so1.SetBalance(big.NewInt(52))
so1.SetNonce(53)
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}, 0)
so1.suicided = true
so1.deleted = true
state.setStateObject(so1)

View file

@ -278,6 +278,14 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
return common.BytesToHash(stateObject.CodeHash())
}
func (self *StateDB) GetCodeVersion(addr common.Address) uint64 {
stateObject := self.getStateObject(addr)
if stateObject == nil {
return 0
}
return stateObject.CodeVersion()
}
// GetState retrieves a value from the given account's storage trie.
func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr)
@ -372,10 +380,10 @@ func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
}
}
func (self *StateDB) SetCode(addr common.Address, code []byte) {
func (self *StateDB) SetCode(addr common.Address, code []byte, version uint64) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetCode(crypto.Keccak256Hash(code), code)
stateObject.SetCode(crypto.Keccak256Hash(code), code, version)
}
}
@ -488,7 +496,6 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
prev = self.getStateObject(addr)
newobj = newObject(self, addr, Account{})
newobj.setNonce(0) // sets the object to dirty
if prev == nil {
self.journal.append(createObjectChange{account: &addr})
} else {

View file

@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
// Tests that updating a state trie does not leak any database writes prior to
@ -51,7 +52,7 @@ func TestUpdateLeaks(t *testing.T) {
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
}
if i%3 == 0 {
state.SetCode(addr, []byte{i, i, i, i, i})
state.SetCode(addr, []byte{i, i, i, i, i}, 0)
}
state.IntermediateRoot(false)
}
@ -80,7 +81,7 @@ func TestIntermediateLeaks(t *testing.T) {
state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak})
}
if i%3 == 0 {
state.SetCode(addr, []byte{i, i, i, i, i, tweak})
state.SetCode(addr, []byte{i, i, i, i, i, tweak}, 0)
}
}
@ -247,10 +248,10 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
fn: func(a testAction, s *StateDB) {
code := make([]byte, 16)
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
s.SetCode(addr, code)
binary.BigEndian.PutUint64(code[8:16], uint64(a.args[1]))
s.SetCode(addr, code, uint64(a.args[2]))
},
args: make([]int64, 2),
args: make([]int64, 3),
},
{
name: "CreateAccount",
@ -449,3 +450,49 @@ func TestCopyOfCopy(t *testing.T) {
t.Fatalf("2nd copy fail, expected 42, got %v", got)
}
}
func TestStateObjectDecoding(t *testing.T) {
var accounts = []Account{
{
Nonce: 100,
Balance: big.NewInt(100),
Root: common.HexToHash("deadbeef"),
CodeHash: []byte{0x01, 0x02, 0x03},
},
{
Nonce: 100,
Balance: big.NewInt(100),
Root: common.HexToHash("deadbeef"),
CodeHash: []byte{0x01, 0x02, 0x03},
CodeVersion: 0,
},
{
Nonce: 100,
Balance: big.NewInt(100),
Root: common.HexToHash("deadbeef"),
CodeHash: []byte{0x01, 0x02, 0x03},
CodeVersion: 1,
},
{
Nonce: 100,
Balance: big.NewInt(100),
Root: common.HexToHash("deadbeef"),
CodeHash: []byte{0x01, 0x02, 0x03},
CodeVersion: math.MaxUint64,
},
}
for _, acct := range accounts {
blob, err := rlp.EncodeToBytes(&acct)
if err != nil {
t.Fatalf("Failed to encode account %v", err)
}
var dec Account
err = rlp.DecodeBytes(blob, &dec)
if err != nil {
t.Fatalf("Failed to decode account %v", err)
}
if !reflect.DeepEqual(acct, dec) {
t.Fatalf("Mismatch after encoding/decoding, want %v, has %v", acct, dec)
}
}
}

View file

@ -56,7 +56,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
acc.nonce = uint64(42 * i)
if i%3 == 0 {
obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i})
obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i}, 0)
acc.code = []byte{i, i, i, i, i}
}
state.updateStateObject(obj)

View file

@ -27,9 +27,7 @@ import (
"github.com/ethereum/go-ethereum/params"
)
var (
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
)
var errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
/*
The State Transitioning Model
@ -206,7 +204,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
vmerr error
)
if contractCreation {
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value, st.evm.ChainConfig().LatestCodeVersion)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)

View file

@ -57,6 +57,7 @@ type Contract struct {
CodeAddr *common.Address
Input []byte
CodeVersion uint64
Gas uint64
value *big.Int
}
@ -169,16 +170,18 @@ func (c *Contract) Value() *big.Int {
// SetCallCode sets the code of the contract and address of the backing data
// object
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte, version uint64) {
c.Code = code
c.CodeHash = hash
c.CodeAddr = addr
c.CodeVersion = version
}
// SetCodeOptionalHash can be used to provide code, but it's optional to provide hash.
// In case hash is not provided, the jumpdest analysis will not be saved to the parent context
func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) {
func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash, version uint64) {
c.Code = codeAndHash.code
c.CodeHash = codeAndHash.hash
c.CodeAddr = addr
c.CodeVersion = version
}

View file

@ -33,11 +33,18 @@ var emptyCodeHash = crypto.Keccak256Hash(nil)
type (
// CanTransferFunc is the signature of a transfer guard function
CanTransferFunc func(StateDB, common.Address, *big.Int) bool
// TransferFunc is the signature of a transfer function
TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
// GetHashFunc returns the nth block hash in the blockchain
// and is used by the BLOCKHASH EVM op code.
GetHashFunc func(uint64) common.Hash
// ValidateCodeFunc returns an indicator whether the given contract
// code is valid or not. If false is returned, the transaction is failed
// and returns out-of-gas.
ValidateCodeFunc func(uint64, []byte) bool
)
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
@ -54,7 +61,8 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
return RunPrecompiledContract(p, input, contract)
}
}
for _, interpreter := range evm.interpreters {
interpreters := evm.interpreters[contract.CodeVersion]
for _, interpreter := range interpreters {
if interpreter.CanRun(contract.Code) {
if evm.interpreter != interpreter {
// Ensure that the interpreter pointer is set back
@ -76,11 +84,16 @@ type Context struct {
// CanTransfer returns whether the account contains
// sufficient ether to transfer the value
CanTransfer CanTransferFunc
// Transfer transfers ether from one account to the other
Transfer TransferFunc
// GetHash returns the hash corresponding to n
GetHash GetHashFunc
// ValidateCode returns whether the contract code is valid.
ValidateCode ValidateCodeFunc
// Message information
Origin common.Address // Provides information for ORIGIN
GasPrice *big.Int // Provides information for GASPRICE
@ -119,7 +132,7 @@ type EVM struct {
vmConfig Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
interpreters []Interpreter
interpreters map[uint64][]Interpreter
interpreter Interpreter
// abort is used to abort the EVM calling operations
// NOTE: must be set atomically
@ -139,7 +152,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
vmConfig: vmConfig,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(ctx.BlockNumber),
interpreters: make([]Interpreter, 0, 1),
interpreters: make(map[uint64][]Interpreter),
}
if chainConfig.IsEWASM(ctx.BlockNumber) {
@ -160,8 +173,8 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
// as we always want to have the built-in EVM as the failover option.
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
evm.interpreter = evm.interpreters[0]
evm.interpreters[0] = []Interpreter{NewEVMInterpreter(evm, vmConfig)}
evm.interpreter = evm.interpreters[0][0]
return evm
}
@ -226,7 +239,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr), evm.StateDB.GetCodeVersion(addr))
// Even if the account has no code, we need to continue because it might be a precompile
start := time.Now()
@ -281,7 +294,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr), evm.StateDB.GetCodeVersion(addr))
ret, err = run(evm, contract, input, false)
if err != nil {
@ -314,7 +327,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr), evm.StateDB.GetCodeVersion(addr))
ret, err = run(evm, contract, input, false)
if err != nil {
@ -346,7 +359,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, new(big.Int), gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr), evm.StateDB.GetCodeVersion(addr))
// We do an AddBalance of zero here, just in order to trigger a touch.
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
@ -380,7 +393,7 @@ func (c *codeAndHash) Hash() common.Hash {
}
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, version uint64) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
@ -389,6 +402,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, common.Address{}, gas, ErrInsufficientBalance
}
if !evm.ValidateCode(version, codeAndHash.code) {
// According to EIP1702, if the validation phrase fails,
// contract creation does not proceed, return out-of-gas
// and consume all remaining gas.
return nil, common.Address{}, 0, ErrOutOfGas
}
nonce := evm.StateDB.GetNonce(caller.Address())
evm.StateDB.SetNonce(caller.Address(), nonce+1)
@ -408,7 +427,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCodeOptionalHash(&address, codeAndHash)
contract.SetCodeOptionalHash(&address, codeAndHash, version)
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, address, gas, nil
@ -430,7 +449,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if err == nil && !maxCodeSizeExceeded {
createDataGas := uint64(len(ret)) * params.CreateDataGas
if contract.UseGas(createDataGas) {
evm.StateDB.SetCode(address, ret)
evm.StateDB.SetCode(address, ret, version)
} else {
err = ErrCodeStoreOutOfGas
}
@ -457,19 +476,19 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int, version uint64) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, version)
}
// Create2 creates a new contract using code as deployment code.
//
// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int, version uint64) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, version)
}
// ChainConfig returns the environment's chain configuration

View file

@ -699,7 +699,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
}
contract.UseGas(gas)
res, addr, returnGas, suberr := interpreter.evm.Create(contract, input, gas, value)
res, addr, returnGas, suberr := interpreter.evm.Create(contract, input, gas, value, contract.CodeVersion)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
@ -732,7 +732,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
// Apply EIP150
gas -= gas / 64
contract.UseGas(gas)
res, addr, returnGas, suberr := interpreter.evm.Create2(contract, input, gas, endowment, salt)
res, addr, returnGas, suberr := interpreter.evm.Create2(contract, input, gas, endowment, salt, contract.CodeVersion)
// Push item on the stack based on the returned error.
if suberr != nil {
stack.push(interpreter.intPool.getZero())

View file

@ -36,8 +36,9 @@ type StateDB interface {
GetCodeHash(common.Address) common.Hash
GetCode(common.Address) []byte
SetCode(common.Address, []byte)
SetCode(common.Address, []byte, uint64)
GetCodeSize(common.Address) int
GetCodeVersion(address common.Address) uint64
AddRefund(uint64)
SubRefund(uint64)
@ -65,16 +66,3 @@ type StateDB interface {
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
}
// CallContext provides a basic interface for the EVM calling conventions. The EVM
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
type CallContext interface {
// Call another contract
Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
// Take another's contract code and execute within our own context
CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
// Same as CallCode except sender and value is propagated from parent to child scope
DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
// Create a new contract
Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
}

View file

@ -27,7 +27,7 @@ func NewEnv(cfg *Config) *vm.EVM {
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: func(uint64) common.Hash { return common.Hash{} },
ValidateCode: core.ValidateCode,
Origin: cfg.Origin,
Coinbase: cfg.Coinbase,
BlockNumber: cfg.BlockNumber,

View file

@ -41,6 +41,7 @@ type Config struct {
GasLimit uint64
GasPrice *big.Int
Value *big.Int
Version uint64
Debug bool
EVMConfig vm.Config
@ -108,7 +109,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
)
cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(address, code)
cfg.State.SetCode(address, code, cfg.Version)
// Call the code with the given configuration.
ret, _, err := vmenv.Call(
sender,
@ -142,6 +143,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
input,
cfg.GasLimit,
cfg.Value,
cfg.Version,
)
return code, address, leftOverGas, err
}

View file

@ -104,7 +104,7 @@ func TestCall(t *testing.T) {
byte(vm.PUSH1), 32,
byte(vm.PUSH1), 0,
byte(vm.RETURN),
})
}, 0)
ret, _, err := Call(address, nil, &Config{State: state})
if err != nil {
@ -157,7 +157,7 @@ func benchmarkEVM_Create(bench *testing.B, code string) {
)
statedb.CreateAccount(sender)
statedb.SetCode(receiver, common.FromHex(code))
statedb.SetCode(receiver, common.FromHex(code), 0)
runtimeConfig := Config{
Origin: sender,
State: statedb,

View file

@ -146,6 +146,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
context := vm.Context{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
ValidateCode: core.ValidateCode,
Origin: origin,
Coinbase: common.Address{},
BlockNumber: new(big.Int).SetUint64(8000000),
@ -234,6 +235,7 @@ func TestCallTracer(t *testing.T) {
context := vm.Context{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
ValidateCode: core.ValidateCode,
Origin: origin,
Coinbase: test.Context.Miner,
BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),

View file

@ -58,6 +58,7 @@ var (
HomesteadBlock: big.NewInt(1150000),
DAOForkBlock: big.NewInt(1920000),
DAOForkSupport: true,
LatestCodeVersion: 0,
EIP150Block: big.NewInt(2463000),
EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
EIP155Block: big.NewInt(2675000),
@ -96,6 +97,7 @@ var (
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
LatestCodeVersion: 0,
EIP150Block: big.NewInt(0),
EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
EIP155Block: big.NewInt(10),
@ -134,6 +136,7 @@ var (
HomesteadBlock: big.NewInt(1),
DAOForkBlock: nil,
DAOForkSupport: true,
LatestCodeVersion: 0,
EIP150Block: big.NewInt(2),
EIP150Hash: common.HexToHash("0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"),
EIP155Block: big.NewInt(3),
@ -174,6 +177,7 @@ var (
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
LatestCodeVersion: 0,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
@ -213,16 +217,16 @@ var (
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil}
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, 0, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus.
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, 0, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil}
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, 0, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil}
TestRules = TestChainConfig.Rules(new(big.Int))
)
@ -281,6 +285,9 @@ type ChainConfig struct {
DAOForkBlock *big.Int `json:"daoForkBlock,omitempty"` // TheDAO hard-fork switch block (nil = no fork)
DAOForkSupport bool `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork
// EIP1702 introduces the code version change (https://eips.ethereum.org/EIPS/eip-1702)
LatestCodeVersion uint64 `json:"latestCodeVersion,omitempty"` // The latest account code version indicator, 0 as default.
// EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
EIP150Block *big.Int `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork)
EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed)
@ -293,7 +300,6 @@ type ChainConfig struct {
PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople)
IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul)
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
// Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"`
Clique *CliqueConfig `json:"clique,omitempty"`

View file

@ -173,7 +173,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB
sdb := state.NewDatabase(db)
statedb, _ := state.New(common.Hash{}, sdb)
for addr, a := range accounts {
statedb.SetCode(addr, a.Code)
statedb.SetCode(addr, a.Code, 0) // Default code version is 0.
statedb.SetNonce(addr, a.Nonce)
statedb.SetBalance(addr, a.Balance)
for k, v := range a.Storage {