diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 4e5df633b7e3..7a2d975620c3 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3275,3 +3275,73 @@ func TestEIP1559Transition(t *testing.T) { t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) } } + +func TestEIP3074AuthCall(t *testing.T) { + var ( + aa = common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb") + + engine = ethash.NewFaker() + db = rawdb.NewMemoryDatabase() + diskdb = rawdb.NewMemoryDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key.PublicKey) + gspec = &Genesis{ + Config: params.AllEthashProtocolChanges, + Alloc: GenesisAlloc{ + addr: {Balance: big.NewInt(100000000000000)}, + // authcall into bb + aa: { + Code: common.FromHex("7f794dd7b68f540151c21953cc5322e6df1b809eec12e561353832a5d68e14809a7f7aa455a9f8b84965a8c2f32e29dbb8147a913fffa1b02375c6ab28161e6ebf2560007fbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf6506000808080808073000000000000000000000000000000000000bbbb6000f700"), + Nonce: 0, + Balance: big.NewInt(0), + }, + // store the caller in 0x00 + bb: { + Code: []byte{ + byte(vm.CALLER), + byte(vm.PUSH1), + byte(0x00), + byte(vm.SSTORE), + }, + Nonce: 0, + Balance: big.NewInt(0), + }, + }, + } + ) + + gspec.Config.BerlinBlock = common.Big0 + gspec.Config.LondonBlock = common.Big0 + gspec.Config.PuxiBlock = common.Big0 + genesis := gspec.MustCommit(db) + + blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) { + signer := types.LatestSigner(gspec.Config) + tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{ + Nonce: 0, + To: &aa, + Gas: 100000, + GasPrice: newGwei(1), + }) + b.AddTx(tx) + }) + + gspec.MustCommit(diskdb) + + chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + + state, _ := chain.State() + caller := common.BytesToAddress(state.GetState(bb, common.Hash{}).Bytes()) + expected := "0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B" + + if caller.Hex() != expected { + t.Fatalf("wrong caller, got: %s, expected: %s", caller.Hex(), expected) + } +} diff --git a/core/vm/contract.go b/core/vm/contract.go index 61dbd5007adb..58b140c92dbb 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -44,8 +44,12 @@ func (ar AccountRef) Address() common.Address { return (common.Address)(ar) } // the contract code, calling arguments. Contract implements ContractRef type Contract struct { // CallerAddress is the result of the caller which initialised this - // contract. However when the "call method" is delegated this value - // needs to be initialised to that of the caller's caller. + // contract. There are two cases where the caller may be overridden: + // + // 1. A DELEGATECALL will initialise the value to the caller's + // caller. + // 2. An AUTHCALL will initialise the value to last address + // authorized by AUTH. CallerAddress common.Address caller ContractRef self ContractRef diff --git a/core/vm/eips.go b/core/vm/eips.go index 4070a2db5342..8e52f863bf1f 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -20,6 +20,8 @@ import ( "fmt" "sort" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -27,6 +29,7 @@ import ( var activators = map[int]func(*JumpTable){ 3529: enable3529, 3198: enable3198, + 3074: enable3074, 2929: enable2929, 2200: enable2200, 1884: enable1884, @@ -174,3 +177,101 @@ func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Stack.push(baseFee) return nil, nil } + +func enable3074(jt *JumpTable) { + jt[AUTH] = &operation{ + execute: opAuth, + constantGas: params.AuthGasEIP3074, + minStack: minStack(4, 1), + maxStack: maxStack(4, 1), + } + + jt[AUTHCALL] = &operation{ + execute: opAuthCall, + constantGas: params.WarmStorageReadCostEIP2929, + dynamicGas: gasAuthCallEIP2929, + minStack: minStack(8, 1), + maxStack: maxStack(8, 1), + memorySize: memoryAuthCall, + returns: true, + } +} + +func opAuth(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + stack := scope.Stack + commit, v, r, s := stack.pop(), stack.pop(), stack.pop(), stack.pop() + + // Zero out the current authorized account. Only update it if an address + // is successfully recovered from the signature. + scope.Authorized = nil + + if v.BitLen() < 8 && crypto.ValidateSignatureValues(byte(v.Uint64()), r.ToBig(), s.ToBig(), true) { + msg := make([]byte, 65) + + // EIP-3074 messages are of the form + // keccak256(type ++ invoker ++ commit) + msg[0] = 0x03 + copy(msg[13:33], scope.Contract.Address().Bytes()) + commit.WriteToSlice(msg[33:65]) + hash := crypto.Keccak256(msg) + + sig := make([]byte, 65) + r.WriteToSlice(sig[0:32]) + s.WriteToSlice(sig[32:64]) + sig[64] = byte(v.Uint64()) + + pub, err := crypto.Ecrecover(hash[:], sig) + + if err == nil { + var addr common.Address + copy(addr[:], crypto.Keccak256(pub[1:])[12:]) + scope.Authorized = &addr + } + } + + // reuse commit to push the result + temp := commit + if scope.Authorized != nil { + temp.SetBytes20(scope.Authorized.Bytes()) + } else { + temp.Clear() + } + + stack.push(&temp) + return nil, nil +} + +func opAuthCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + // If no authorized account is set, revert. + if scope.Authorized == nil { + return nil, ErrNoAuthorizedAccount + } + + stack := scope.Stack + // Pop gas. The actual gas in interpreter.evm.callGasTemp. + // We can use this as a temporary value + temp := stack.pop() + gas := interpreter.evm.callGasTemp + // Pop other call parameters. + addr, value, extValue, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) + // Get the arguments from the memory. + args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + + ret, returnGas, err := interpreter.evm.AuthCall(scope.Contract, *scope.Authorized, toAddr, args, gas, value.ToBig(), extValue.ToBig()) + + if err == ErrInsufficientBalance { + return nil, ErrInsufficientBalance + } else if err != nil { + temp.Clear() + } else { + temp.SetOne() + } + stack.push(&temp) + if err == nil || err == ErrExecutionReverted { + scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + scope.Contract.Gas += returnGas + + return ret, nil +} diff --git a/core/vm/errors.go b/core/vm/errors.go index c7cfeae53ce3..9469ef6044c5 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -35,6 +35,11 @@ var ( ErrReturnDataOutOfBounds = errors.New("return data out of bounds") ErrGasUintOverflow = errors.New("gas uint64 overflow") ErrInvalidCode = errors.New("invalid code: must not begin with 0xef") + ErrInvalidRetsub = errors.New("invalid retsub") + ErrReturnStackExceeded = errors.New("return stack limit reached") + ErrNoAuthorizedAccount = errors.New("authorized account not set") + ErrInsufficientAuthCallGas = errors.New("insufficient remaining gas for authcall") + ErrNonZeroExtValue = errors.New("non-zero external value") ) // ErrStackUnderflow wraps an evm error when the items on the stack less diff --git a/core/vm/evm.go b/core/vm/evm.go index 8964766736fb..17a399f48276 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -364,6 +364,79 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte return ret, gas, err } +// AuthCall executes the contract associated with the addr with the given input +// as parameters. It reverses the state in case of an execution error. +func (evm *EVM) AuthCall(caller ContractRef, from, addr common.Address, input []byte, gas uint64, value, extValue *big.Int) (ret []byte, leftOverGas uint64, err error) { + if evm.Config.NoRecursion && evm.depth > 0 { + return nil, gas, nil + } + // Fail if we're trying to execute above the call depth limit + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, ErrDepth + } + // Fail if we're trying to transfer more than the available balance + if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + return nil, 0, ErrInsufficientBalance + } + // Fail if we're trying to transfer value external to the caller + if extValue.Sign() != 0 { + return nil, gas, ErrNonZeroExtValue + } + snapshot := evm.StateDB.Snapshot() + p, isPrecompile := evm.precompile(addr) + + if !evm.StateDB.Exist(addr) { + if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { + // Calling a non existing account, don't do anything, but ping the tracer + if evm.Config.Debug && evm.depth == 0 { + evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) + evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil) + } + return nil, gas, nil + } + evm.StateDB.CreateAccount(addr) + } + evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) + + // Capture the tracer start/end events in debug mode + if evm.Config.Debug && evm.depth == 0 { + evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) + defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters + evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err) + }(gas, time.Now()) + } + + if isPrecompile { + ret, gas, err = RunPrecompiledContract(p, input, gas) + } else { + // 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. + code := evm.StateDB.GetCode(addr) + if len(code) == 0 { + ret, err = nil, nil // gas is unchanged + } else { + addrCopy := addr + // If the account has no code, we can abort here + // The depth-check is already done, and precompiles handled above + contract := NewContract(caller, AccountRef(addrCopy), value, gas) + contract.CallerAddress = from + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas + } + } + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if err != nil { + evm.StateDB.RevertToSnapshot(snapshot) + if err != ErrExecutionReverted { + gas = 0 + } + } + return ret, gas, err +} + type codeAndHash struct { code []byte hash common.Hash diff --git a/core/vm/gas.go b/core/vm/gas.go index 5cf1d852d24a..8a5b40ca113a 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -34,7 +34,7 @@ const ( // // The cost of gas was changed during the homestead price change HF. // As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64. -func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) { +func callGas(isEip150, isAuthCall bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) { if isEip150 { availableGas = availableGas - base gas := availableGas - availableGas/64 @@ -42,6 +42,16 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u // is smaller than the requested amount. Therefore we return the new gas instead // of returning an error. if !callCost.IsUint64() || gas < callCost.Uint64() { + // AuthCall behaves differently than other call-like ops. If more gas is + // requested than is available, it throws. + if !isAuthCall { + return gas, nil + } else { + return 0, ErrInsufficientAuthCallGas + } + } else if isAuthCall && callCost.IsZero() { + // AuthCall has special behavior for 0 requested gas, in which case it passes + // in all available gas. return gas, nil } } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 944b6cf0a5df..ab25fe4daa7b 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -350,7 +350,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize return 0, ErrGasUintOverflow } - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, false, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -375,7 +375,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, false, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -390,7 +390,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me if err != nil { return 0, err } - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, false, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -406,7 +406,7 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo if err != nil { return 0, err } - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, false, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -417,6 +417,37 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo return gas, nil } +func gasAuthCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var ( + gas uint64 + transfersValue = !stack.Back(2).IsZero() + address = common.Address(stack.Back(1).Bytes20()) + ) + if transfersValue && evm.StateDB.Empty(address) { + gas += params.CallNewAccountGas + } + if transfersValue { + gas += params.CallValueTransferGas - params.CallStipend + } + memoryGas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { + return 0, ErrGasUintOverflow + } + + evm.callGasTemp, err = callGas(true, true, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil +} + func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var gas uint64 // EIP150 homestead gas reprice fork: diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 560d26a0b8d4..2ccd1aee3a97 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -105,7 +105,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) stack.push(x) stack.push(y) - opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, nil}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) } @@ -220,7 +220,7 @@ func TestAddMod(t *testing.T) { stack.push(z) stack.push(y) stack.push(x) - opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, nil}) actual := stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) @@ -242,7 +242,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) stack.push(x) stack.push(y) - opFn(&pc, interpreter, &ScopeContext{nil, stack, nil}) + opFn(&pc, interpreter, &ScopeContext{nil, stack, nil, nil}) actual := stack.pop() result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} } @@ -300,7 +300,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { a.SetBytes(arg) stack.push(a) } - op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, nil}) stack.pop() } } @@ -526,12 +526,12 @@ func TestOpMstore(t *testing.T) { pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } @@ -554,7 +554,7 @@ func BenchmarkOpMstore(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { stack.pushN(*value, *memStart) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, nil}) } } @@ -573,7 +573,7 @@ func BenchmarkOpSHA3(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { stack.pushN(*uint256.NewInt(32), *start) - opSha3(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opSha3(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, nil}) } } @@ -650,3 +650,75 @@ func TestCreate2Addreses(t *testing.T) { } } } + +func TestAuth(t *testing.T) { + var ( + env = NewEVM(BlockContext{}, TxContext{Origin: common.HexToAddress("970e8128ab834e8eac17ab8e3812f010678cf791")}, nil, params.TestChainConfig, Config{}) + stack = newstack() + evmInterpreter = NewEVMInterpreter(env, env.Config) + pc = uint64(0) + ) + + type testcase struct { + v string + r string + s string + invoker string + commit string + expected string + } + + for i, tt := range []testcase{ + { + v: "00", + r: "7aa455a9f8b84965a8c2f32e29dbb8147a913fffa1b02375c6ab28161e6ebf25", + s: "794dd7b68f540151c21953cc5322e6df1b809eec12e561353832a5d68e14809a", + invoker: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + commit: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + expected: "a94f5374Fce5edBC8E2a8697C15331677e6EbF0B", + }, + { + v: "01", + r: "9a50f2fa6aa26558eb2d469e494e32676e563c9ea0a149286caccdba26758abf", + s: "4de8a7bebbbf64ef197a11d23c3e2cc144481d8c1d9f80822b4844e98bccbc6d", + invoker: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + commit: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + expected: "a94f5374Fce5edBC8E2a8697C15331677e6EbF0B", + }, + // invalid signature + { + v: "02", + r: "7aa455a9f8b84965a8c2f32e29dbb8147a913fffa1b02375c6ab28161e6ebf25", + s: "794dd7b68f540151c21953cc5322e6df1b809eec12e561353832a5d68e14809a", + invoker: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + commit: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + expected: "0000000000000000000000000000000000000000", + }, + } { + stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(tt.s))) + stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(tt.r))) + stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(tt.v))) + stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(tt.commit))) + + self := AccountRef(common.HexToAddress(tt.invoker)) + contract := Contract{CallerAddress: common.Address{}, caller: nil, self: self} + ctx := ScopeContext{nil, stack, &contract, nil} + opAuth(&pc, evmInterpreter, &ctx) + + result := stack.pop() + addr := common.BigToAddress(result.ToBig()) + + if addr.Hex()[2:] != tt.expected { + t.Fatalf("Auth failed to authenticate signature: test #%d got %s, expected 0x%s", i, addr.Hex(), tt.expected) + } + + if ctx.Authorized != nil { + if addr.Hex() == (common.Address{}).Hex() { + t.Fatalf("Authorized ctx variable set after invalid auth invocation.") + } + if ctx.Authorized.Hex()[2:] != tt.expected { + t.Fatalf("Authorized ctx variable not equal to expected: test #%d got %s, expected 0x%s", i, ctx.Authorized.Hex(), tt.expected) + } + } + } +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 9cf0c4e2c1e0..e8954173879b 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -41,9 +41,10 @@ type Config struct { // ScopeContext contains the things that are per-call, such as stack and memory, // but not transients like pc and gas type ScopeContext struct { - Memory *Memory - Stack *Stack - Contract *Contract + Memory *Memory + Stack *Stack + Contract *Contract + Authorized *common.Address } // keccakState wraps sha3.state. In addition to the usual hash methods, it also supports @@ -74,6 +75,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { + case evm.chainRules.IsPuxi: + jt = puxiInsturctionSet case evm.chainRules.IsLondon: jt = londonInstructionSet case evm.chainRules.IsBerlin: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 329ad77cbf83..0f8fbe7c44b8 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -58,11 +58,20 @@ var ( istanbulInstructionSet = newIstanbulInstructionSet() berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() + puxiInsturctionSet = newPuxiInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +// newPuxiInstructionSet returns the frontier, homestead, byzantium, +// contantinople, istanbul, petersburg, berlin, london, puxi instructions. +func newPuxiInstructionSet() JumpTable { + instructionSet := newLondonInstructionSet() + enable3074(&instructionSet) + return instructionSet +} + // newLondonInstructionSet returns the frontier, homestead, byzantium, // contantinople, istanbul, petersburg, berlin and london instructions. func newLondonInstructionSet() JumpTable { diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 4fcb41442c4e..6d6b7aafcac5 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -70,6 +70,7 @@ func memoryCall(stack *Stack) (uint64, bool) { } return y, false } + func memoryDelegateCall(stack *Stack) (uint64, bool) { x, overflow := calcMemSize64(stack.Back(4), stack.Back(5)) if overflow { @@ -100,6 +101,21 @@ func memoryStaticCall(stack *Stack) (uint64, bool) { return y, false } +func memoryAuthCall(stack *Stack) (uint64, bool) { + x, overflow := calcMemSize64(stack.Back(6), stack.Back(7)) + if overflow { + return 0, true + } + y, overflow := calcMemSize64(stack.Back(4), stack.Back(5)) + if overflow { + return 0, true + } + if x > y { + return x, false + } + return y, false +} + func memoryReturn(stack *Stack) (uint64, bool) { return calcMemSize64(stack.Back(0), stack.Back(1)) } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 286307ae91ae..e447fb37a44f 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -214,6 +214,8 @@ const ( RETURN DELEGATECALL CREATE2 + AUTH + AUTHCALL STATICCALL OpCode = 0xfa REVERT OpCode = 0xfd SELFDESTRUCT OpCode = 0xff @@ -375,6 +377,8 @@ var opCodeToString = map[OpCode]string{ // 0xf0 range. CREATE: "CREATE", CALL: "CALL", + AUTH: "AUTH", + AUTHCALL: "AUTHCALL", RETURN: "RETURN", CALLCODE: "CALLCODE", DELEGATECALL: "DELEGATECALL", @@ -536,6 +540,8 @@ var stringToOp = map[string]OpCode{ "CREATE": CREATE, "CREATE2": CREATE2, "CALL": CALL, + "AUTH": AUTH, + "AUTHCALL": AUTHCALL, "RETURN": RETURN, "CALLCODE": CALLCODE, "REVERT": REVERT, diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 483226eefad8..5953843d7488 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -217,6 +217,8 @@ var ( // gasSStoreEIP2539 implements gas cost for SSTORE according to EPI-2539 // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800) gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529) + + gasAuthCallEIP2929 = makeCallVariantGasCallEIP2929(gasAuthCall) ) // makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539 diff --git a/params/config.go b/params/config.go index 591c36de2a37..4e06ff817a1e 100644 --- a/params/config.go +++ b/params/config.go @@ -247,16 +247,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), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil} + 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), 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), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + 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), 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), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil} + 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), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -336,6 +336,7 @@ type ChainConfig struct { 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) + PuxiBlock *big.Int `json:"puxiBlock,omitempty"` CatalystBlock *big.Int `json:"catalystBlock,omitempty"` // Catalyst switch block (nil = no fork, 0 = already on catalyst) // Various consensus engines @@ -373,7 +374,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Puxi: %v, Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -388,6 +389,7 @@ func (c *ChainConfig) String() string { c.MuirGlacierBlock, c.BerlinBlock, c.LondonBlock, + c.PuxiBlock, engine, ) } @@ -459,6 +461,10 @@ func (c *ChainConfig) IsCatalyst(num *big.Int) bool { return isForked(c.CatalystBlock, num) } +func (c *ChainConfig) IsPuxi(num *big.Int) bool { + return isForked(c.PuxiBlock, num) +} + // CheckCompatible checks whether scheduled fork transitions have been imported // with a mismatching chain configuration. func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError { @@ -635,7 +641,7 @@ type Rules struct { ChainID *big.Int IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool - IsBerlin, IsLondon, IsCatalyst bool + IsBerlin, IsLondon, IsPuxi, IsCatalyst bool } // Rules ensures c's ChainID is not nil. @@ -656,6 +662,7 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsIstanbul: c.IsIstanbul(num), IsBerlin: c.IsBerlin(num), IsLondon: c.IsLondon(num), + IsPuxi: c.IsPuxi(num), IsCatalyst: c.IsCatalyst(num), } } diff --git a/params/protocol_params.go b/params/protocol_params.go index 7abb2441bf30..83fc8af77b79 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -103,6 +103,7 @@ const ( ExtcodeHashGasConstantinople uint64 = 400 // Cost of EXTCODEHASH (introduced in Constantinople) ExtcodeHashGasEIP1884 uint64 = 700 // Cost of EXTCODEHASH after EIP 1884 (part in Istanbul) SelfdestructGasEIP150 uint64 = 5000 // Cost of SELFDESTRUCT post EIP 150 (Tangerine) + AuthGasEIP3074 uint64 = 3100 // Cost of AUTH (Alpine) // EXP has a dynamic portion depending on the size of the exponent ExpByteFrontier uint64 = 10 // was set to 10 in Frontier