mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
core, crypto, params: implement CREATE2 evm instrction
This commit is contained in:
parent
323428865f
commit
3146ebbd17
8 changed files with 99 additions and 16 deletions
|
|
@ -319,9 +319,8 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
return ret, contract.Gas, err
|
return ret, contract.Gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) create(caller ContractRef, code []byte, gas uint64, value *big.Int, address common.Address) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
|
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
|
|
@ -330,35 +329,34 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
||||||
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||||
return nil, common.Address{}, gas, ErrInsufficientBalance
|
return nil, common.Address{}, gas, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
// Ensure there's no existing contract already at the designated address
|
|
||||||
nonce := evm.StateDB.GetNonce(caller.Address())
|
nonce := evm.StateDB.GetNonce(caller.Address())
|
||||||
evm.StateDB.SetNonce(caller.Address(), nonce+1)
|
evm.StateDB.SetNonce(caller.Address(), nonce+1)
|
||||||
|
|
||||||
contractAddr = crypto.CreateAddress(caller.Address(), nonce)
|
// Ensure there's no existing contract already at the designated address
|
||||||
contractHash := evm.StateDB.GetCodeHash(contractAddr)
|
contractHash := evm.StateDB.GetCodeHash(address)
|
||||||
if evm.StateDB.GetNonce(contractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
|
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
|
||||||
return nil, common.Address{}, 0, ErrContractAddressCollision
|
return nil, common.Address{}, 0, ErrContractAddressCollision
|
||||||
}
|
}
|
||||||
// Create a new account on the state
|
// Create a new account on the state
|
||||||
snapshot := evm.StateDB.Snapshot()
|
snapshot := evm.StateDB.Snapshot()
|
||||||
evm.StateDB.CreateAccount(contractAddr)
|
evm.StateDB.CreateAccount(address)
|
||||||
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
|
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
|
||||||
evm.StateDB.SetNonce(contractAddr, 1)
|
evm.StateDB.SetNonce(address, 1)
|
||||||
}
|
}
|
||||||
evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value)
|
evm.Transfer(evm.StateDB, caller.Address(), address, value)
|
||||||
|
|
||||||
// initialise a new contract and set the code that is to be used by the
|
// 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
|
// EVM. The contract is a scoped environment for this execution context
|
||||||
// only.
|
// only.
|
||||||
contract := NewContract(caller, AccountRef(contractAddr), value, gas)
|
contract := NewContract(caller, AccountRef(address), value, gas)
|
||||||
contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
|
contract.SetCallCode(&address, crypto.Keccak256Hash(code), code)
|
||||||
|
|
||||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||||
return nil, contractAddr, gas, nil
|
return nil, address, gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
|
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, code, gas, value)
|
||||||
}
|
}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
|
@ -373,7 +371,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
||||||
if err == nil && !maxCodeSizeExceeded {
|
if err == nil && !maxCodeSizeExceeded {
|
||||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||||
if contract.UseGas(createDataGas) {
|
if contract.UseGas(createDataGas) {
|
||||||
evm.StateDB.SetCode(contractAddr, ret)
|
evm.StateDB.SetCode(address, ret)
|
||||||
} else {
|
} else {
|
||||||
err = ErrCodeStoreOutOfGas
|
err = ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -395,7 +393,23 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
||||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||||
}
|
}
|
||||||
return ret, contractAddr, contract.Gas, err
|
return ret, address, contract.Gas, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
|
||||||
|
return evm.create(caller, code, gas, value, contractAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create2 creates a new contract using code as deployment code.
|
||||||
|
//
|
||||||
|
// The different between Create2 with Create is Create2 uses sha3(msg.sender ++ salt ++ 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) {
|
||||||
|
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes(), code)
|
||||||
|
return evm.create(caller, code, gas, endowment, contractAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainConfig returns the environment's chain configuration
|
// ChainConfig returns the environment's chain configuration
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,18 @@ func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gasCreate2(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
var overflow bool
|
||||||
|
gas, err := memoryGasCost(mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if gas, overflow = math.SafeAdd(gas, params.Create2Gas); overflow {
|
||||||
|
return 0, errGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
return gt.Balance, nil
|
return gt.Balance, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -665,6 +665,40 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func opCreate2(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
endowment = stack.pop()
|
||||||
|
offset, size = stack.pop(), stack.pop()
|
||||||
|
salt = stack.pop()
|
||||||
|
input = memory.Get(offset.Int64(), size.Int64())
|
||||||
|
gas = contract.Gas
|
||||||
|
)
|
||||||
|
if evm.ChainConfig().IsEIP150(evm.BlockNumber) {
|
||||||
|
gas -= gas / 64
|
||||||
|
}
|
||||||
|
|
||||||
|
contract.UseGas(gas)
|
||||||
|
res, addr, returnGas, suberr := evm.Create2(contract, input, gas, endowment, salt)
|
||||||
|
// 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
|
||||||
|
// ignore this error and pretend the operation was successful.
|
||||||
|
if evm.ChainConfig().IsHomestead(evm.BlockNumber) && suberr == ErrCodeStoreOutOfGas {
|
||||||
|
stack.push(evm.interpreter.intPool.getZero())
|
||||||
|
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
||||||
|
stack.push(evm.interpreter.intPool.getZero())
|
||||||
|
} else {
|
||||||
|
stack.push(addr.Big())
|
||||||
|
}
|
||||||
|
contract.Gas += returnGas
|
||||||
|
evm.interpreter.intPool.put(endowment, offset, size, salt)
|
||||||
|
|
||||||
|
if suberr == errExecutionReverted {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
// Pop gas. The actual gas in in evm.callGasTemp.
|
// Pop gas. The actual gas in in evm.callGasTemp.
|
||||||
evm.interpreter.intPool.put(stack.pop())
|
evm.interpreter.intPool.put(stack.pop())
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,15 @@ func newConstantinopleInstructionSet() [256]operation {
|
||||||
validateStack: makeStackFunc(2, 1),
|
validateStack: makeStackFunc(2, 1),
|
||||||
valid: true,
|
valid: true,
|
||||||
}
|
}
|
||||||
|
instructionSet[CREATE2] = operation{
|
||||||
|
execute: opCreate2,
|
||||||
|
gasCost: gasCreate2,
|
||||||
|
validateStack: makeStackFunc(4, 1),
|
||||||
|
memorySize: memoryCreate2,
|
||||||
|
valid: true,
|
||||||
|
writes: true,
|
||||||
|
returns: true,
|
||||||
|
}
|
||||||
return instructionSet
|
return instructionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@ func memoryCreate(stack *Stack) *big.Int {
|
||||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
return calcMemSize(stack.Back(1), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func memoryCreate2(stack *Stack) *big.Int {
|
||||||
|
return calcMemSize(stack.Back(1), stack.Back(2))
|
||||||
|
}
|
||||||
|
|
||||||
func memoryCall(stack *Stack) *big.Int {
|
func memoryCall(stack *Stack) *big.Int {
|
||||||
x := calcMemSize(stack.Back(5), stack.Back(6))
|
x := calcMemSize(stack.Back(5), stack.Back(6))
|
||||||
y := calcMemSize(stack.Back(3), stack.Back(4))
|
y := calcMemSize(stack.Back(3), stack.Back(4))
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@ const (
|
||||||
CALLCODE
|
CALLCODE
|
||||||
RETURN
|
RETURN
|
||||||
DELEGATECALL
|
DELEGATECALL
|
||||||
|
CREATE2
|
||||||
STATICCALL = 0xfa
|
STATICCALL = 0xfa
|
||||||
|
|
||||||
REVERT = 0xfd
|
REVERT = 0xfd
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,14 @@ func CreateAddress(b common.Address, nonce uint64) common.Address {
|
||||||
return common.BytesToAddress(Keccak256(data)[12:])
|
return common.BytesToAddress(Keccak256(data)[12:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateAddress2 creates an ethereum address given the address bytes, initial
|
||||||
|
// contract code and a salt.
|
||||||
|
// TODO(rjl493456442) considering address collision, should we add the 0xff as the input prefix?
|
||||||
|
func CreateAddress2(b common.Address, salt []byte, code []byte) common.Address {
|
||||||
|
data, _ := rlp.EncodeToBytes([]interface{}{b, salt, code})
|
||||||
|
return common.BytesToAddress(Keccak256(data)[12:])
|
||||||
|
}
|
||||||
|
|
||||||
// ToECDSA creates a private key with the given D value.
|
// ToECDSA creates a private key with the given D value.
|
||||||
func ToECDSA(d []byte) (*ecdsa.PrivateKey, error) {
|
func ToECDSA(d []byte) (*ecdsa.PrivateKey, error) {
|
||||||
return toECDSA(d, true)
|
return toECDSA(d, true)
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ const (
|
||||||
TierStepGas uint64 = 0 // Once per operation, for a selection of them.
|
TierStepGas uint64 = 0 // Once per operation, for a selection of them.
|
||||||
LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
|
LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
|
||||||
CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
|
CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
|
||||||
|
Create2Gas uint64 = 32000 // Once per CREATE2 operation
|
||||||
SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation.
|
SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation.
|
||||||
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
||||||
TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
|
TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue