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
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -4221,3 +4222,131 @@ func TestEIP3651(t *testing.T) {
|
||||||
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
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/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -39,6 +40,7 @@ var activators = map[int]func(*JumpTable){
|
||||||
1884: enable1884,
|
1884: enable1884,
|
||||||
1344: enable1344,
|
1344: enable1344,
|
||||||
1153: enable1153,
|
1153: enable1153,
|
||||||
|
3074: enable3074,
|
||||||
4762: enable4762,
|
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")
|
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||||
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
|
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
|
||||||
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
||||||
|
ErrAuthorizedNotSet = errors.New("authcall without setting authorized")
|
||||||
|
|
||||||
// errStopToken is an internal token indicating interpreter loop termination,
|
// errStopToken is an internal token indicating interpreter loop termination,
|
||||||
// never returned to outside callers.
|
// never returned to outside callers.
|
||||||
|
|
|
||||||
|
|
@ -420,6 +420,73 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
return ret, gas, err
|
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 {
|
type codeAndHash struct {
|
||||||
code []byte
|
code []byte
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
|
|
|
||||||
|
|
@ -51,3 +51,18 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u
|
||||||
|
|
||||||
return callCost.Uint64(), nil
|
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
|
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) {
|
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
// EIP150 homestead gas reprice fork:
|
// 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))
|
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
|
||||||
stack.push(x)
|
stack.push(x)
|
||||||
stack.push(y)
|
stack.push(y)
|
||||||
opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, nil})
|
||||||
if len(stack.data) != 1 {
|
if len(stack.data) != 1 {
|
||||||
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
|
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(z)
|
||||||
stack.push(y)
|
stack.push(y)
|
||||||
stack.push(x)
|
stack.push(x)
|
||||||
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, nil})
|
||||||
actual := stack.pop()
|
actual := stack.pop()
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
|
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))
|
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
|
||||||
stack.push(x)
|
stack.push(x)
|
||||||
stack.push(y)
|
stack.push(y)
|
||||||
opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
|
opFn(&pc, interpreter, &ScopeContext{nil, stack, nil, nil})
|
||||||
actual := stack.pop()
|
actual := stack.pop()
|
||||||
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
|
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 (
|
var (
|
||||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
scope = &ScopeContext{nil, stack, nil}
|
scope = &ScopeContext{nil, stack, nil, nil}
|
||||||
evmInterpreter = NewEVMInterpreter(env)
|
evmInterpreter = NewEVMInterpreter(env)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -546,13 +546,13 @@ func TestOpMstore(t *testing.T) {
|
||||||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||||
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
|
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
|
||||||
stack.push(new(uint256.Int))
|
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 {
|
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
|
||||||
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
||||||
}
|
}
|
||||||
stack.push(new(uint256.Int).SetUint64(0x1))
|
stack.push(new(uint256.Int).SetUint64(0x1))
|
||||||
stack.push(new(uint256.Int))
|
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" {
|
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
||||||
t.Fatalf("Mstore failed to overwrite previous value")
|
t.Fatalf("Mstore failed to overwrite previous value")
|
||||||
}
|
}
|
||||||
|
|
@ -576,7 +576,7 @@ func BenchmarkOpMstore(bench *testing.B) {
|
||||||
for i := 0; i < bench.N; i++ {
|
for i := 0; i < bench.N; i++ {
|
||||||
stack.push(value)
|
stack.push(value)
|
||||||
stack.push(memStart)
|
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}
|
to = common.Address{1}
|
||||||
contractRef = contractRef{caller}
|
contractRef = contractRef{caller}
|
||||||
contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0)
|
contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0)
|
||||||
scopeContext = ScopeContext{mem, stack, contract}
|
scopeContext = ScopeContext{mem, stack, contract, nil}
|
||||||
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
|
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -639,7 +639,7 @@ func BenchmarkOpKeccak256(bench *testing.B) {
|
||||||
for i := 0; i < bench.N; i++ {
|
for i := 0; i < bench.N; i++ {
|
||||||
stack.push(uint256.NewInt(32))
|
stack.push(uint256.NewInt(32))
|
||||||
stack.push(start)
|
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)
|
pc = uint64(0)
|
||||||
evmInterpreter = env.interpreter
|
evmInterpreter = env.interpreter
|
||||||
)
|
)
|
||||||
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, nil})
|
||||||
if len(stack.data) != 1 {
|
if len(stack.data) != 1 {
|
||||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
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
|
evmInterpreter = env.interpreter
|
||||||
)
|
)
|
||||||
stack.push(uint256.NewInt(tt.idx))
|
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 {
|
if len(stack.data) != 1 {
|
||||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
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)
|
mem.Resize(memorySize)
|
||||||
}
|
}
|
||||||
// Do the copy
|
// 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, " ", ""))
|
want := common.FromHex(strings.ReplaceAll(tc.want, " ", ""))
|
||||||
if have := mem.store; !bytes.Equal(want, have) {
|
if have := mem.store; !bytes.Equal(want, have) {
|
||||||
t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, 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,
|
// ScopeContext contains the things that are per-call, such as stack and memory,
|
||||||
// but not transients like pc and gas
|
// but not transients like pc and gas
|
||||||
type ScopeContext struct {
|
type ScopeContext struct {
|
||||||
Memory *Memory
|
Memory *Memory
|
||||||
Stack *Stack
|
Stack *Stack
|
||||||
Contract *Contract
|
Contract *Contract
|
||||||
|
Authorized *common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemoryData returns the underlying memory slice. Callers must not modify the contents
|
// 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:
|
case evm.chainRules.IsVerkle:
|
||||||
// TODO replace with proper instruction set when fork is specified
|
// TODO replace with proper instruction set when fork is specified
|
||||||
table = &verkleInstructionSet
|
table = &verkleInstructionSet
|
||||||
|
case evm.chainRules.IsPrague:
|
||||||
|
table = &pragueInstructionSet
|
||||||
case evm.chainRules.IsCancun:
|
case evm.chainRules.IsCancun:
|
||||||
table = &cancunInstructionSet
|
table = &cancunInstructionSet
|
||||||
case evm.chainRules.IsShanghai:
|
case evm.chainRules.IsShanghai:
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ var (
|
||||||
mergeInstructionSet = newMergeInstructionSet()
|
mergeInstructionSet = newMergeInstructionSet()
|
||||||
shanghaiInstructionSet = newShanghaiInstructionSet()
|
shanghaiInstructionSet = newShanghaiInstructionSet()
|
||||||
cancunInstructionSet = newCancunInstructionSet()
|
cancunInstructionSet = newCancunInstructionSet()
|
||||||
|
pragueInstructionSet = newPraugeInstructionSet()
|
||||||
verkleInstructionSet = newVerkleInstructionSet()
|
verkleInstructionSet = newVerkleInstructionSet()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -87,6 +88,12 @@ func newVerkleInstructionSet() JumpTable {
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newPraugeInstructionSet() JumpTable {
|
||||||
|
instructionSet := newCancunInstructionSet()
|
||||||
|
enable3074(&instructionSet) // EIP-3074 AUTH & AUTHCALL
|
||||||
|
return validate(instructionSet)
|
||||||
|
}
|
||||||
|
|
||||||
func newCancunInstructionSet() JumpTable {
|
func newCancunInstructionSet() JumpTable {
|
||||||
instructionSet := newShanghaiInstructionSet()
|
instructionSet := newShanghaiInstructionSet()
|
||||||
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
|
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
|
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) {
|
func memoryCreate(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,9 @@ const (
|
||||||
DELEGATECALL OpCode = 0xf4
|
DELEGATECALL OpCode = 0xf4
|
||||||
CREATE2 OpCode = 0xf5
|
CREATE2 OpCode = 0xf5
|
||||||
|
|
||||||
|
AUTH OpCode = 0xf6
|
||||||
|
AUTHCALL OpCode = 0xf7
|
||||||
|
|
||||||
STATICCALL OpCode = 0xfa
|
STATICCALL OpCode = 0xfa
|
||||||
REVERT OpCode = 0xfd
|
REVERT OpCode = 0xfd
|
||||||
INVALID OpCode = 0xfe
|
INVALID OpCode = 0xfe
|
||||||
|
|
@ -391,6 +394,8 @@ var opCodeToString = [256]string{
|
||||||
CALLCODE: "CALLCODE",
|
CALLCODE: "CALLCODE",
|
||||||
DELEGATECALL: "DELEGATECALL",
|
DELEGATECALL: "DELEGATECALL",
|
||||||
CREATE2: "CREATE2",
|
CREATE2: "CREATE2",
|
||||||
|
AUTH: "AUTH",
|
||||||
|
AUTHCALL: "AUTHCALL",
|
||||||
STATICCALL: "STATICCALL",
|
STATICCALL: "STATICCALL",
|
||||||
REVERT: "REVERT",
|
REVERT: "REVERT",
|
||||||
INVALID: "INVALID",
|
INVALID: "INVALID",
|
||||||
|
|
@ -548,6 +553,8 @@ var stringToOp = map[string]OpCode{
|
||||||
"LOG4": LOG4,
|
"LOG4": LOG4,
|
||||||
"CREATE": CREATE,
|
"CREATE": CREATE,
|
||||||
"CREATE2": CREATE2,
|
"CREATE2": CREATE2,
|
||||||
|
"AUTH": AUTH,
|
||||||
|
"AUTHCALL": AUTHCALL,
|
||||||
"CALL": CALL,
|
"CALL": CALL,
|
||||||
"RETURN": RETURN,
|
"RETURN": RETURN,
|
||||||
"CALLCODE": CALLCODE,
|
"CALLCODE": CALLCODE,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"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
|
// Once we're done with YOLOv2 and schedule this for mainnet, might
|
||||||
// be good to remove this panic here, which is just really a
|
// be good to remove this panic here, which is just really a
|
||||||
// canary to have during testing
|
// 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())
|
value := common.Hash(y.Bytes32())
|
||||||
|
|
@ -205,6 +207,7 @@ var (
|
||||||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
||||||
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
||||||
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
||||||
|
gasAuthCallEIP2929 = makeCallVariantGasCallEIP2929(gasAuthCall)
|
||||||
|
|
||||||
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
|
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
|
||||||
//
|
//
|
||||||
|
|
@ -248,3 +251,22 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
}
|
}
|
||||||
return 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),
|
GrayGlacierBlock: big.NewInt(0),
|
||||||
ShanghaiTime: newUint64(0),
|
ShanghaiTime: newUint64(0),
|
||||||
CancunTime: newUint64(0),
|
CancunTime: newUint64(0),
|
||||||
|
PragueTime: newUint64(0),
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
TerminalTotalDifficultyPassed: true,
|
TerminalTotalDifficultyPassed: true,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,4 +189,7 @@ var (
|
||||||
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
|
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
|
||||||
// SystemAddress is where the system-transaction is sent from as per EIP-4788
|
// SystemAddress is where the system-transaction is sent from as per EIP-4788
|
||||||
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
|
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
|
||||||
|
|
||||||
|
// Magic prefix for EIP-3074 AUTH messages.
|
||||||
|
AuthMagic = byte(0x04)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue