mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-16 04:56:36 +00:00
core/vm: added EOFCREATE opcode
This commit is contained in:
parent
a58ba40cf6
commit
9fa14a1af4
3 changed files with 108 additions and 15 deletions
|
|
@ -21,6 +21,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// opRjump implements the RJUMP opcode.
|
// opRjump implements the RJUMP opcode.
|
||||||
|
|
@ -120,7 +122,55 @@ func opJumpf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
||||||
|
|
||||||
// opEOFCreate implements the EOFCREATE opcode
|
// opEOFCreate implements the EOFCREATE opcode
|
||||||
func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
panic("not implemented")
|
if interpreter.readOnly {
|
||||||
|
return nil, ErrWriteProtection
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||||
|
idx = code[*pc+1]
|
||||||
|
value = scope.Stack.pop()
|
||||||
|
salt = scope.Stack.pop()
|
||||||
|
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||||
|
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
|
)
|
||||||
|
if int(idx) >= len(scope.Contract.Container.subContainerCodes) {
|
||||||
|
return nil, fmt.Errorf("invalid subcontainer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct hashing charge
|
||||||
|
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
||||||
|
hashingCharge := (params.Keccak256WordGas) * ((uint64(len(scope.Contract.Container.subContainerCodes[idx])) + 31) / 32)
|
||||||
|
if ok := scope.Contract.UseGas(hashingCharge, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified); !ok {
|
||||||
|
return nil, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if interpreter.evm.Config.Tracer != nil {
|
||||||
|
if interpreter.evm.Config.Tracer != nil {
|
||||||
|
interpreter.evm.Config.Tracer.OnOpcode(*pc, byte(EOFCREATE), 0, hashingCharge, scope, interpreter.returnData, interpreter.evm.depth, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gas := scope.Contract.Gas
|
||||||
|
// Reuse last popped value from stack
|
||||||
|
stackvalue := size
|
||||||
|
// Apply EIP150
|
||||||
|
gas -= gas / 64
|
||||||
|
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||||
|
// Skip the immediate
|
||||||
|
*pc += 1
|
||||||
|
res, addr, returnGas, suberr := interpreter.evm.EOFCreate(scope.Contract, input, scope.Contract.Container.subContainerCodes[idx], gas, &value, &salt)
|
||||||
|
if suberr != nil {
|
||||||
|
stackvalue.Clear()
|
||||||
|
} else {
|
||||||
|
stackvalue.SetBytes(addr.Bytes())
|
||||||
|
}
|
||||||
|
scope.Stack.push(&stackvalue)
|
||||||
|
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
|
if suberr == ErrExecutionReverted {
|
||||||
|
interpreter.returnData = res // set REVERT data to return data buffer
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
interpreter.returnData = nil // clear dirty return data buffer
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// opReturnContract implements the RETURNCONTRACT opcode
|
// opReturnContract implements the RETURNCONTRACT opcode
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@ var (
|
||||||
ErrWriteProtection = errors.New("write protection")
|
ErrWriteProtection = errors.New("write protection")
|
||||||
ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
|
ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
|
||||||
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||||
|
ErrLegacyCode = errors.New("invalid code: EOF contract must not deploy legacy code")
|
||||||
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
|
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
|
||||||
|
ErrInvalidEOF = errors.New("invalid eof")
|
||||||
ErrInvalidEOFInitcode = errors.New("invalid eof initcode")
|
ErrInvalidEOFInitcode = errors.New("invalid eof initcode")
|
||||||
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -435,7 +435,7 @@ func (c *codeAndHash) Hash() common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create creates a new contract using code as deployment code.
|
// create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode, input []byte, allowEOF bool) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
|
||||||
if evm.Config.Tracer != nil {
|
if evm.Config.Tracer != nil {
|
||||||
evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig())
|
evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig())
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
|
|
@ -450,6 +450,33 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||||
return nil, common.Address{}, gas, ErrInsufficientBalance
|
return nil, common.Address{}, gas, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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. If
|
||||||
|
// the initcode is EOF, contract.Container will be set.
|
||||||
|
contract := NewContract(caller, AccountRef(address), value, gas)
|
||||||
|
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||||
|
contract.IsDeployment = true
|
||||||
|
|
||||||
|
// Validate initcode per EOF rules. If caller is EOF and initcode is legacy, fail.
|
||||||
|
isInitcodeEOF := hasEOFMagic(codeAndHash.code)
|
||||||
|
if isInitcodeEOF {
|
||||||
|
if allowEOF {
|
||||||
|
// If the initcode is EOF, verify it is well-formed.
|
||||||
|
var c Container
|
||||||
|
if err := c.UnmarshalBinary(codeAndHash.code, isInitcodeEOF); err != nil {
|
||||||
|
return nil, common.Address{}, gas, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, err)
|
||||||
|
}
|
||||||
|
if err := c.ValidateCode(evm.interpreter.tableEOF, isInitcodeEOF); err != nil {
|
||||||
|
return nil, common.Address{}, gas, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, err)
|
||||||
|
}
|
||||||
|
contract.Container = &c
|
||||||
|
} else {
|
||||||
|
// Don't allow EOF contract to execute legacy initcode.
|
||||||
|
return nil, common.Address{}, gas, ErrLegacyCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for nonce overflow and then update caller nonce by 1.
|
||||||
nonce := evm.StateDB.GetNonce(caller.Address())
|
nonce := evm.StateDB.GetNonce(caller.Address())
|
||||||
if nonce+1 < nonce {
|
if nonce+1 < nonce {
|
||||||
return nil, common.Address{}, gas, ErrNonceUintOverflow
|
return nil, common.Address{}, gas, ErrNonceUintOverflow
|
||||||
|
|
@ -517,13 +544,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
}
|
}
|
||||||
evm.Context.Transfer(evm.StateDB, caller.Address(), address, value)
|
evm.Context.Transfer(evm.StateDB, caller.Address(), address, value)
|
||||||
|
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
ret, err = evm.initNewContract(contract, address, value, isInitcodeEOF)
|
||||||
// The contract is a scoped environment for this execution context only.
|
|
||||||
contract := NewContract(caller, AccountRef(address), value, gas)
|
|
||||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
|
||||||
contract.IsDeployment = true
|
|
||||||
|
|
||||||
ret, err = evm.initNewContract(contract, address, value)
|
|
||||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
|
|
@ -535,7 +556,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
|
|
||||||
// initNewContract runs a new contract's creation code, performs checks on the
|
// initNewContract runs a new contract's creation code, performs checks on the
|
||||||
// resulting code that is to be deployed, and consumes necessary gas.
|
// resulting code that is to be deployed, and consumes necessary gas.
|
||||||
func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) {
|
func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int, isInitcodeEOF bool) ([]byte, error) {
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
|
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
|
||||||
|
|
@ -543,7 +564,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address, valu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := evm.interpreter.Run(contract, nil, false, true)
|
ret, err := evm.interpreter.Run(contract, nil, false, contract.IsDeployment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
@ -553,9 +574,22 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address, valu
|
||||||
return ret, ErrMaxCodeSizeExceeded
|
return ret, ErrMaxCodeSizeExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject legacy contract deployment from EOF.
|
||||||
|
if isInitcodeEOF && !hasEOFMagic(ret) {
|
||||||
|
return ret, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, ErrLegacyCode)
|
||||||
|
}
|
||||||
|
// Reject EOF deployment from legacy.
|
||||||
|
if isInitcodeEOF && hasEOFMagic(ret) {
|
||||||
|
return ret, ErrLegacyCode
|
||||||
|
}
|
||||||
|
|
||||||
// Reject code starting with 0xEF if EIP-3541 is enabled.
|
// Reject code starting with 0xEF if EIP-3541 is enabled.
|
||||||
if len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
|
if len(ret) >= 1 && HasEOFByte(ret) {
|
||||||
return ret, ErrInvalidCode
|
if evm.chainRules.IsPrague && isInitcodeEOF {
|
||||||
|
// Don't reject EOF contracts after Shanghai
|
||||||
|
} else if evm.chainRules.IsLondon {
|
||||||
|
err = ErrInvalidCode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !evm.chainRules.IsEIP4762 {
|
if !evm.chainRules.IsEIP4762 {
|
||||||
|
|
@ -576,7 +610,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address, valu
|
||||||
// Create creates a new contract using code as deployment code.
|
// Create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int, allowEOF bool) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int, allowEOF bool) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
|
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
|
||||||
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
|
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE, nil, allowEOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create2 creates a new contract using code as deployment code.
|
// Create2 creates a new contract using code as deployment code.
|
||||||
|
|
@ -586,7 +620,14 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint2
|
||||||
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
codeAndHash := &codeAndHash{code: code}
|
codeAndHash := &codeAndHash{code: code}
|
||||||
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
|
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
|
||||||
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
|
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2, nil, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOFCreate creates a new eof contract.
|
||||||
|
func (evm *EVM) EOFCreate(caller ContractRef, input []byte, subcontainer []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
|
codeAndHash := &codeAndHash{code: subcontainer}
|
||||||
|
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
|
||||||
|
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2, input, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainConfig returns the environment's chain configuration
|
// ChainConfig returns the environment's chain configuration
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue