mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
core: implement auth and authcall
This commit is contained in:
parent
5b3e3cd2be
commit
2c133df35b
14 changed files with 451 additions and 16 deletions
|
|
@ -17,6 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -4221,3 +4222,131 @@ func TestEIP3651(t *testing.T) {
|
|||
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP3074(t *testing.T) {
|
||||
var (
|
||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
||||
engine = beacon.NewFaker()
|
||||
|
||||
// A sender who makes transactions, has some funds
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||
config = *params.AllEthashProtocolChanges
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
Alloc: GenesisAlloc{
|
||||
addr: {Balance: funds},
|
||||
// The address 0xAAAA sloads 0x00 and 0x01
|
||||
aa: {
|
||||
Code: nil, // added below
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
// The address 0xBBBB calls 0xAAAA
|
||||
bb: {
|
||||
Code: []byte{
|
||||
byte(vm.CALLER),
|
||||
byte(vm.PUSH0),
|
||||
byte(vm.SSTORE),
|
||||
byte(vm.STOP),
|
||||
},
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
invoker := []byte{
|
||||
// copy sig to memory
|
||||
byte(vm.CALLDATASIZE),
|
||||
byte(vm.PUSH0),
|
||||
byte(vm.PUSH0),
|
||||
byte(vm.CALLDATACOPY),
|
||||
|
||||
// set up auth
|
||||
byte(vm.CALLDATASIZE),
|
||||
byte(vm.PUSH0),
|
||||
}
|
||||
// push authority to stack
|
||||
invoker = append(invoker, append([]byte{byte(vm.PUSH20)}, addr.Bytes()...)...)
|
||||
invoker = append(invoker, []byte{
|
||||
|
||||
byte(vm.AUTH),
|
||||
byte(vm.POP),
|
||||
|
||||
// execute authcall
|
||||
byte(vm.PUSH0), // out size
|
||||
byte(vm.DUP1), // out offset
|
||||
byte(vm.DUP1), // out insize
|
||||
byte(vm.DUP1), // in offset
|
||||
byte(vm.DUP1), // valueExt
|
||||
byte(vm.DUP1), // value
|
||||
byte(vm.PUSH2), // address
|
||||
byte(0xbb),
|
||||
byte(0xbb),
|
||||
byte(vm.GAS), // gas
|
||||
byte(vm.AUTHCALL),
|
||||
byte(vm.STOP),
|
||||
}...,
|
||||
)
|
||||
|
||||
// Set the invoker's code.
|
||||
if entry, _ := gspec.Alloc[aa]; true {
|
||||
entry.Code = invoker
|
||||
gspec.Alloc[aa] = entry
|
||||
}
|
||||
|
||||
gspec.Config.BerlinBlock = common.Big0
|
||||
gspec.Config.LondonBlock = common.Big0
|
||||
gspec.Config.TerminalTotalDifficulty = common.Big0
|
||||
gspec.Config.TerminalTotalDifficultyPassed = true
|
||||
gspec.Config.ShanghaiTime = u64(0)
|
||||
gspec.Config.CancunTime = u64(0)
|
||||
gspec.Config.PragueTime = u64(0)
|
||||
signer := types.LatestSigner(gspec.Config)
|
||||
|
||||
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
||||
commit := common.Hash{0x42}
|
||||
msg := []byte{params.AuthMagic}
|
||||
msg = append(msg, common.LeftPadBytes(gspec.Config.ChainID.Bytes(), 32)...)
|
||||
msg = append(msg, common.LeftPadBytes(common.Big1.Bytes(), 32)...)
|
||||
msg = append(msg, common.LeftPadBytes(aa.Bytes(), 32)...)
|
||||
msg = append(msg, commit.Bytes()...)
|
||||
msg = crypto.Keccak256(msg)
|
||||
|
||||
sig, _ := crypto.Sign(msg, key)
|
||||
sig = append([]byte{sig[len(sig)-1]}, sig[0:len(sig)-1]...)
|
||||
txdata := &types.DynamicFeeTx{
|
||||
ChainID: gspec.Config.ChainID,
|
||||
Nonce: 0,
|
||||
To: &aa,
|
||||
Gas: 500000,
|
||||
GasFeeCap: newGwei(5),
|
||||
GasTipCap: big.NewInt(2),
|
||||
AccessList: nil,
|
||||
Data: append(sig, commit.Bytes()...),
|
||||
}
|
||||
tx := types.NewTx(txdata)
|
||||
tx, _ = types.SignTx(tx, signer, key)
|
||||
|
||||
b.AddTx(tx)
|
||||
})
|
||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
defer chain.Stop()
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
// Verify authcall worked correctly.
|
||||
state, _ := chain.State()
|
||||
got := state.GetState(bb, common.Hash{})
|
||||
if want := common.LeftPadBytes(addr.Bytes(), 32); !bytes.Equal(got.Bytes(), want) {
|
||||
t.Fatalf("incorrect sender in authcall: got %s, want %s", got.Hex(), common.Bytes2Hex(want))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
109
core/vm/eips.go
109
core/vm/eips.go
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -39,6 +40,7 @@ var activators = map[int]func(*JumpTable){
|
|||
1884: enable1884,
|
||||
1344: enable1344,
|
||||
1153: enable1153,
|
||||
3074: enable3074,
|
||||
4762: enable4762,
|
||||
}
|
||||
|
||||
|
|
@ -533,3 +535,110 @@ func enable4762(jt *JumpTable) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enable3074(jt *JumpTable) {
|
||||
jt[AUTH] = &operation{
|
||||
execute: opAuth,
|
||||
constantGas: 3100 + params.WarmStorageReadCostEIP2929,
|
||||
dynamicGas: gasAuthEIP2929,
|
||||
minStack: minStack(3, 1),
|
||||
maxStack: maxStack(3, 1),
|
||||
memorySize: memoryAuth,
|
||||
}
|
||||
jt[AUTHCALL] = &operation{
|
||||
execute: opAuthCall,
|
||||
constantGas: params.WarmStorageReadCostEIP2929,
|
||||
dynamicGas: gasAuthCallEIP2929,
|
||||
minStack: minStack(8, 1),
|
||||
maxStack: maxStack(8, 1),
|
||||
memorySize: memoryCall,
|
||||
}
|
||||
}
|
||||
|
||||
// opAuth implements the EIP-3074 AUTH instruction.
|
||||
func opAuth(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
tmp = scope.Stack.pop()
|
||||
authority = common.Address(tmp.Bytes20())
|
||||
offset = scope.Stack.pop()
|
||||
length = scope.Stack.pop()
|
||||
data = scope.Memory.GetPtr(int64(offset.Uint64()), int64(length.Uint64()))
|
||||
sig = make([]byte, 65)
|
||||
commit common.Hash
|
||||
)
|
||||
copy(sig, data)
|
||||
if len(data) > 65 {
|
||||
copy(commit[:], data[65:])
|
||||
}
|
||||
|
||||
// If the desired authority has code, the operation must be considered
|
||||
// unsuccessful.
|
||||
statedb := interpreter.evm.StateDB
|
||||
if statedb.GetCodeSize(authority) != 0 {
|
||||
scope.Authorized = nil
|
||||
scope.Stack.push(uint256.NewInt(0))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Build original auth message.
|
||||
msg := []byte{params.AuthMagic}
|
||||
msg = append(msg, common.LeftPadBytes(interpreter.evm.chainConfig.ChainID.Bytes(), 32)...)
|
||||
msg = append(msg, common.LeftPadBytes(uint256.NewInt(statedb.GetNonce(authority)).Bytes(), 32)...)
|
||||
msg = append(msg, common.LeftPadBytes(scope.Contract.Address().Bytes(), 32)...)
|
||||
msg = append(msg, commit.Bytes()...)
|
||||
msg = crypto.Keccak256(msg)
|
||||
|
||||
// Verify signature against provided address.
|
||||
sig = append(sig[1:], sig[0]) // send y parity to back
|
||||
pub, err := crypto.Ecrecover(msg, sig)
|
||||
if err != nil {
|
||||
scope.Authorized = nil
|
||||
scope.Stack.push(uint256.NewInt(0))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check recovered matches expected authority.
|
||||
var recovered common.Address
|
||||
copy(recovered[:], crypto.Keccak256(pub[1:])[12:])
|
||||
if recovered != authority {
|
||||
scope.Authorized = nil
|
||||
scope.Stack.push(uint256.NewInt(0))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
scope.Stack.push(uint256.NewInt(1))
|
||||
scope.Authorized = &authority
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// opAuthCall implements the EIP-3074 AUTHCALL instruction.
|
||||
func opAuthCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if scope.Authorized == nil {
|
||||
return nil, ErrAuthorizedNotSet
|
||||
}
|
||||
var (
|
||||
stack = scope.Stack
|
||||
temp = stack.pop()
|
||||
gas = interpreter.evm.callGasTemp
|
||||
addr, value, _, inOffset, inSize, retOffset, retSize = stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr = common.Address(addr.Bytes20())
|
||||
args = scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||
)
|
||||
if interpreter.readOnly && !value.IsZero() {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
ret, returnGas, err := interpreter.evm.AuthCall(scope.Contract, *scope.Authorized, toAddr, args, gas, &value)
|
||||
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
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
ErrAuthorizedNotSet = errors.New("authcall without setting authorized")
|
||||
|
||||
// errStopToken is an internal token indicating interpreter loop termination,
|
||||
// never returned to outside callers.
|
||||
|
|
|
|||
|
|
@ -420,6 +420,73 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
return ret, gas, err
|
||||
}
|
||||
|
||||
// AuthCall mimic Call except it sets the caller to the Authorized addres$ in Scope.
|
||||
func (evm *EVM) AuthCall(invoker ContractRef, caller, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig())
|
||||
defer func(startGas uint64) {
|
||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
// 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.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
return nil, gas, ErrInsufficientBalance
|
||||
}
|
||||
snapshot := evm.StateDB.Snapshot()
|
||||
p, isPrecompile := evm.precompile(addr)
|
||||
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
||||
// Calling a non-existing account, don't do anything.
|
||||
return nil, gas, nil
|
||||
}
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
}
|
||||
evm.Context.Transfer(evm.StateDB, caller, addr, value)
|
||||
|
||||
if isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
||||
} 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
|
||||
callerCopy := caller
|
||||
// If the account has no code, we can abort here
|
||||
// The depth-check is already done, and precompiles handled above
|
||||
contract := NewContract(AccountRef(callerCopy), AccountRef(addrCopy), value, gas)
|
||||
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 {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
gas = 0
|
||||
}
|
||||
// TODO: consider clearing up unused snapshots:
|
||||
//} else {
|
||||
// evm.StateDB.DiscardSnapshot(snapshot)
|
||||
}
|
||||
return ret, gas, err
|
||||
}
|
||||
|
||||
type codeAndHash struct {
|
||||
code []byte
|
||||
hash common.Hash
|
||||
|
|
|
|||
|
|
@ -51,3 +51,18 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u
|
|||
|
||||
return callCost.Uint64(), nil
|
||||
}
|
||||
|
||||
func authCallGas(availableGas, base uint64, callCost *uint256.Int) (uint64, error) {
|
||||
availableGas = availableGas - base
|
||||
gas := availableGas - availableGas/64
|
||||
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
|
||||
// is smaller than the requested amount. Therefore we return the new gas instead
|
||||
// of returning an error.
|
||||
if !callCost.IsUint64() || gas < callCost.Uint64() {
|
||||
return gas, nil
|
||||
}
|
||||
if !callCost.IsUint64() {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return callCost.Uint64(), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -480,6 +480,41 @@ 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 {
|
||||
if evm.StateDB.Empty(address) {
|
||||
gas += params.CallNewAccountGas
|
||||
} else {
|
||||
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(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if gas == 0 {
|
||||
evm.callGasTemp = contract.Gas
|
||||
}
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -117,7 +117,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))
|
||||
}
|
||||
|
|
@ -232,7 +232,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)
|
||||
|
|
@ -259,7 +259,7 @@ func TestWriteExpectedValues(t *testing.T) {
|
|||
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)}
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
|||
var (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
scope = &ScopeContext{nil, stack, nil}
|
||||
scope = &ScopeContext{nil, stack, nil, nil}
|
||||
evmInterpreter = NewEVMInterpreter(env)
|
||||
)
|
||||
|
||||
|
|
@ -546,13 +546,13 @@ func TestOpMstore(t *testing.T) {
|
|||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
|
||||
stack.push(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.push(new(uint256.Int).SetUint64(0x1))
|
||||
stack.push(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")
|
||||
}
|
||||
|
|
@ -576,7 +576,7 @@ func BenchmarkOpMstore(bench *testing.B) {
|
|||
for i := 0; i < bench.N; i++ {
|
||||
stack.push(value)
|
||||
stack.push(memStart)
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, nil})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -591,7 +591,7 @@ func TestOpTstore(t *testing.T) {
|
|||
to = common.Address{1}
|
||||
contractRef = contractRef{caller}
|
||||
contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0)
|
||||
scopeContext = ScopeContext{mem, stack, contract}
|
||||
scopeContext = ScopeContext{mem, stack, contract, nil}
|
||||
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
|
||||
)
|
||||
|
||||
|
|
@ -639,7 +639,7 @@ func BenchmarkOpKeccak256(bench *testing.B) {
|
|||
for i := 0; i < bench.N; i++ {
|
||||
stack.push(uint256.NewInt(32))
|
||||
stack.push(start)
|
||||
opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, nil})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -734,7 +734,7 @@ func TestRandom(t *testing.T) {
|
|||
pc = uint64(0)
|
||||
evmInterpreter = env.interpreter
|
||||
)
|
||||
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
||||
}
|
||||
|
|
@ -776,7 +776,7 @@ func TestBlobHash(t *testing.T) {
|
|||
evmInterpreter = env.interpreter
|
||||
)
|
||||
stack.push(uint256.NewInt(tt.idx))
|
||||
opBlobHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
opBlobHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
||||
}
|
||||
|
|
@ -917,7 +917,7 @@ func TestOpMCopy(t *testing.T) {
|
|||
mem.Resize(memorySize)
|
||||
}
|
||||
// Do the copy
|
||||
opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, nil})
|
||||
want := common.FromHex(strings.ReplaceAll(tc.want, " ", ""))
|
||||
if have := mem.store; !bytes.Equal(want, have) {
|
||||
t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have)
|
||||
|
|
@ -928,3 +928,35 @@ func TestOpMCopy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP3074(t *testing.T) {
|
||||
var (
|
||||
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
env = NewEVM(BlockContext{}, TxContext{}, statedb, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
evmInterpreter = env.interpreter
|
||||
contractRef = contractRef{common.Address{42}}
|
||||
contract = NewContract(contractRef, AccountRef(common.Address{13, 37}), new(uint256.Int), 0)
|
||||
|
||||
data = make([]byte, 65)
|
||||
)
|
||||
|
||||
// Set pre
|
||||
mem := NewMemory()
|
||||
mem.Resize(uint64(len(data)))
|
||||
mem.Set(0, uint64(len(data)), data)
|
||||
|
||||
// Push stack args
|
||||
zero := new(uint256.Int)
|
||||
len := uint256.NewInt(uint64(len(data)))
|
||||
|
||||
stack.push(zero)
|
||||
stack.push(zero)
|
||||
stack.push(len)
|
||||
|
||||
_, err := opAuth(&pc, evmInterpreter, &ScopeContext{mem, stack, contract, nil})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error in auth: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,9 +38,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
|
||||
}
|
||||
|
||||
// MemoryData returns the underlying memory slice. Callers must not modify the contents
|
||||
|
|
@ -102,6 +103,8 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
|
|||
case evm.chainRules.IsVerkle:
|
||||
// TODO replace with proper instruction set when fork is specified
|
||||
table = &verkleInstructionSet
|
||||
case evm.chainRules.IsPrague:
|
||||
table = &pragueInstructionSet
|
||||
case evm.chainRules.IsCancun:
|
||||
table = &cancunInstructionSet
|
||||
case evm.chainRules.IsShanghai:
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ var (
|
|||
mergeInstructionSet = newMergeInstructionSet()
|
||||
shanghaiInstructionSet = newShanghaiInstructionSet()
|
||||
cancunInstructionSet = newCancunInstructionSet()
|
||||
pragueInstructionSet = newPraugeInstructionSet()
|
||||
verkleInstructionSet = newVerkleInstructionSet()
|
||||
)
|
||||
|
||||
|
|
@ -87,6 +88,12 @@ func newVerkleInstructionSet() JumpTable {
|
|||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
func newPraugeInstructionSet() JumpTable {
|
||||
instructionSet := newCancunInstructionSet()
|
||||
enable3074(&instructionSet) // EIP-3074 AUTH & AUTHCALL
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
func newCancunInstructionSet() JumpTable {
|
||||
instructionSet := newShanghaiInstructionSet()
|
||||
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@ func memoryMcopy(stack *Stack) (uint64, bool) {
|
|||
return calcMemSize64(mStart, stack.Back(2)) // stack[2]: length
|
||||
}
|
||||
|
||||
func memoryAuth(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCreate(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,6 +218,9 @@ const (
|
|||
DELEGATECALL OpCode = 0xf4
|
||||
CREATE2 OpCode = 0xf5
|
||||
|
||||
AUTH OpCode = 0xf6
|
||||
AUTHCALL OpCode = 0xf7
|
||||
|
||||
STATICCALL OpCode = 0xfa
|
||||
REVERT OpCode = 0xfd
|
||||
INVALID OpCode = 0xfe
|
||||
|
|
@ -391,6 +394,8 @@ var opCodeToString = [256]string{
|
|||
CALLCODE: "CALLCODE",
|
||||
DELEGATECALL: "DELEGATECALL",
|
||||
CREATE2: "CREATE2",
|
||||
AUTH: "AUTH",
|
||||
AUTHCALL: "AUTHCALL",
|
||||
STATICCALL: "STATICCALL",
|
||||
REVERT: "REVERT",
|
||||
INVALID: "INVALID",
|
||||
|
|
@ -548,6 +553,8 @@ var stringToOp = map[string]OpCode{
|
|||
"LOG4": LOG4,
|
||||
"CREATE": CREATE,
|
||||
"CREATE2": CREATE2,
|
||||
"AUTH": AUTH,
|
||||
"AUTHCALL": AUTHCALL,
|
||||
"CALL": CALL,
|
||||
"RETURN": RETURN,
|
||||
"CALLCODE": CALLCODE,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package vm
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
|
|
@ -47,7 +48,8 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
// Once we're done with YOLOv2 and schedule this for mainnet, might
|
||||
// be good to remove this panic here, which is just really a
|
||||
// canary to have during testing
|
||||
panic("impossible case: address was not present in access list during sstore op")
|
||||
|
||||
panic(fmt.Sprintf("impossible case: address was not present in access list during sstore op %s", contract.Address()))
|
||||
}
|
||||
}
|
||||
value := common.Hash(y.Bytes32())
|
||||
|
|
@ -205,6 +207,7 @@ var (
|
|||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
||||
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
||||
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
||||
gasAuthCallEIP2929 = makeCallVariantGasCallEIP2929(gasAuthCall)
|
||||
|
||||
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
|
||||
//
|
||||
|
|
@ -248,3 +251,22 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
}
|
||||
return gasFunc
|
||||
}
|
||||
|
||||
func gasAuthEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ var (
|
|||
GrayGlacierBlock: big.NewInt(0),
|
||||
ShanghaiTime: newUint64(0),
|
||||
CancunTime: newUint64(0),
|
||||
PragueTime: newUint64(0),
|
||||
TerminalTotalDifficulty: big.NewInt(0),
|
||||
TerminalTotalDifficultyPassed: true,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,4 +189,7 @@ var (
|
|||
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
|
||||
// SystemAddress is where the system-transaction is sent from as per EIP-4788
|
||||
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
|
||||
|
||||
// Magic prefix for EIP-3074 AUTH messages.
|
||||
AuthMagic = byte(0x04)
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue