diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 48cb4d2027..dcfe4ccc0e 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -268,6 +268,8 @@ const ( GasChangeCallStorageColdAccess GasChangeReason = 13 // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. GasChangeCallFailedExecution GasChangeReason = 14 + // GasChangeContractCreation is the amount of gas that will be burned for a AUTH_CREATE. + GasChangeCallAuthCreation GasChangeReason = 15 // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as // it will be "manually" tracked by a direct emit of the gas change event. diff --git a/core/vm/eips.go b/core/vm/eips.go index 9f06b2818f..46ab9e84d7 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -319,3 +319,14 @@ func enable6780(jt *JumpTable) { maxStack: maxStack(1, 0), } } + +func enableXXXX(jt *JumpTable) { + jt[AUTH_CREATE] = &operation{ + execute: opAuthCreate, + constantGas: params.AuthCreateGas, + dynamicGas: gasAuthCreate, + minStack: minStack(3, 1), + maxStack: maxStack(3, 1), + memorySize: memoryAuthCreate, + } +} diff --git a/core/vm/errors.go b/core/vm/errors.go index e5efc952d4..63ff7e9780 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -38,6 +38,7 @@ var ( ErrGasUintOverflow = errors.New("gas uint64 overflow") ErrInvalidCode = errors.New("invalid code: must not begin with 0xef") ErrNonceUintOverflow = errors.New("nonce uint64 overflow") + ErrAuthCreateSigType = errors.New("auth_create signature type not found ") // errStopToken is an internal token indicating interpreter loop termination, // never returned to outside callers. diff --git a/core/vm/evm.go b/core/vm/evm.go index 045506a1fd..4896b093ba 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -523,6 +523,36 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) } +func (evm *EVM) AuthCreate(caller ContractRef, data []byte, gas uint64, endowment *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + if data[0] >= 4 { + return nil, common.Address{}, 0, ErrAuthCreateSigType + } + + code := make([]byte, len(data)-65) + copy(code[:], data[65:]) + codeAndHash := &codeAndHash{code: code} + + sig := make([]byte, 65) + copy(sig, data) + + msg := []byte{params.AuthCreateMagic} + msg = append(msg, common.LeftPadBytes(evm.chainConfig.ChainID.Bytes(), 32)...) + msg = append(msg, codeAndHash.Hash().Bytes()...) + msg = crypto.Keccak256(msg) + + sig = append(sig[1:], sig[0]) + pub, err := crypto.Ecrecover(msg, sig) + if err != nil { + return nil, common.Address{}, 0, err + } + + var eoaAddr common.Address + copy(eoaAddr[:], crypto.Keccak256(pub[1:])[12:]) + + evm.StateDB.SetNonce(eoaAddr, 0) + return evm.create(caller, codeAndHash, gas, endowment, eoaAddr, AUTH_CREATE) +} + // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index fd5fa14cf5..2751061dee 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -484,3 +484,20 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me } return gas, nil } + +func gasAuthCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + size, overflow := stack.Back(2).Uint64WithOverflow() + if overflow || size > params.MaxInitCodeSize { + return 0, ErrGasUintOverflow + } + // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow + moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32) + if gas, overflow = math.SafeAdd(gas, moreGas); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index a062bb15ff..9a8ca8ad02 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -652,6 +652,40 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, nil } +func opAuthCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } + var ( + endowment = scope.Stack.pop() + offset, size = scope.Stack.pop(), scope.Stack.pop() + data = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) + gas = scope.Contract.Gas + ) + // Apply EIP150 + gas -= gas / 64 + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallAuthCreation) + // reuse size int for stackvalue + stackvalue := size + res, addr, returnGas, suberr := interpreter.evm.AuthCreate(scope.Contract, data, gas, &endowment) + // Push item on the stack based on the returned error. + 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 +} + func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { stack := scope.Stack // Pop gas. The actual gas in interpreter.evm.callGasTemp. diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 406927e321..17af783bcd 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -99,6 +99,8 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // If jump table was not initialised we set the default one. var table *JumpTable switch { + case evm.chainRules.IsPrague: + table = &pragueInstructionSet case evm.chainRules.IsCancun: table = &cancunInstructionSet case evm.chainRules.IsShanghai: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 65716f9442..7a12961048 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -57,6 +57,7 @@ var ( mergeInstructionSet = newMergeInstructionSet() shanghaiInstructionSet = newShanghaiInstructionSet() cancunInstructionSet = newCancunInstructionSet() + pragueInstructionSet = newPraugeInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -80,6 +81,12 @@ func validate(jt JumpTable) JumpTable { return jt } +func newPraugeInstructionSet() JumpTable { + instructionSet := newCancunInstructionSet() + enableXXXX(&instructionSet) // EIP-XXXX AuthCreate + return validate(instructionSet) +} + func newCancunInstructionSet() JumpTable { instructionSet := newShanghaiInstructionSet() enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode) diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 61a910a03d..ee6dedfbdc 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -64,6 +64,10 @@ func memoryCreate2(stack *Stack) (uint64, bool) { return calcMemSize64(stack.Back(1), stack.Back(2)) } +func memoryAuthCreate(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(1), stack.Back(2)) +} + func memoryCall(stack *Stack) (uint64, bool) { x, overflow := calcMemSize64(stack.Back(5), stack.Back(6)) if overflow { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 2b9231fe1a..87660ddcce 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -217,6 +217,7 @@ const ( RETURN OpCode = 0xf3 DELEGATECALL OpCode = 0xf4 CREATE2 OpCode = 0xf5 + AUTH_CREATE OpCode = 0xf6 STATICCALL OpCode = 0xfa REVERT OpCode = 0xfd @@ -391,6 +392,7 @@ var opCodeToString = [256]string{ CALLCODE: "CALLCODE", DELEGATECALL: "DELEGATECALL", CREATE2: "CREATE2", + AUTH_CREATE: "AUTH_CREATE", STATICCALL: "STATICCALL", REVERT: "REVERT", INVALID: "INVALID", @@ -548,6 +550,7 @@ var stringToOp = map[string]OpCode{ "LOG4": LOG4, "CREATE": CREATE, "CREATE2": CREATE2, + "AUTH_CREATE": AUTH_CREATE, "CALL": CALL, "RETURN": RETURN, "CALLCODE": CALLCODE, diff --git a/params/config.go b/params/config.go index 439e882189..c037639ed6 100644 --- a/params/config.go +++ b/params/config.go @@ -184,6 +184,7 @@ var ( GrayGlacierBlock: big.NewInt(0), ShanghaiTime: newUint64(0), CancunTime: newUint64(0), + PragueTime: newUint64(0), TerminalTotalDifficulty: big.NewInt(0), TerminalTotalDifficultyPassed: true, } diff --git a/params/protocol_params.go b/params/protocol_params.go index 863cf58ece..49d35a8de9 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -86,6 +86,7 @@ const ( 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. Create2Gas uint64 = 32000 // Once per CREATE2 operation + AuthCreateGas uint64 = 32000 // Once per CREATE2 operation SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct 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. @@ -187,5 +188,6 @@ var ( // BeaconRootsAddress is the address where historical beacon roots are stored as per EIP-4788 BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") // SystemAddress is where the system-transaction is sent from as per EIP-4788 - SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") + SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") + AuthCreateMagic = byte(0x04) )