validateAccountAndPaymaster called by process, tracing

This commit is contained in:
Dror Tirosh 2024-08-13 11:23:33 +03:00
parent 2ce87f0003
commit 3ae9c9657f
3 changed files with 113 additions and 90 deletions

View file

@ -1,6 +1,7 @@
package core
import (
"bytes"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
@ -16,10 +17,19 @@ import (
var AA_ENTRY_POINT = common.HexToAddress("0x0000000000000000000000000000000000007560")
var AA_SENDER_CREATOR = common.HexToAddress("0x00000000000000000000000000000000ffff7560")
type EntryPointCallEntry struct {
From common.Address
Input []byte
}
type EntryPointCall struct {
OnEnterSuper tracing.EnterHook
Input []byte
err error
entries []EntryPointCallEntry
err error
}
func NewEntryPointCall() *EntryPointCall {
return &EntryPointCall{
entries: make([]EntryPointCallEntry, 0),
}
}
type ValidationPhaseResult struct {
@ -151,6 +161,49 @@ func CheckNonceRip7560(tx *types.Rip7560AccountAbstractionTx, st *state.StateDB)
return nil
}
// finalize validation return data from account and paymaster
func ValidateAccountAndPaymaster(time uint64, sender, paymaster *common.Address, epc *EntryPointCall) (*AcceptAccountData, *AcceptPaymasterData, error) {
if epc.err != nil {
return nil, nil, epc.err
}
if len(epc.entries) == 0 || epc.entries[0].From.Cmp(*sender) != 0 {
return nil, nil, errors.New("account validation did not call the EntryPoint 'acceptAccount' callback")
}
if paymaster == nil {
if len(epc.entries) > 1 {
return nil, nil, errors.New("EntryPoint callback called more than once")
}
} else {
if len(epc.entries) < 2 || epc.entries[1].From.Cmp(*paymaster) != 0 {
return nil, nil, errors.New("paymaster validation did not call the EntryPoint 'acceptPaymaster' callback")
}
if len(epc.entries) > 2 {
return nil, nil, errors.New("EntryPoint callback called too many times")
}
}
aad, err := ValidateAccountEntryPointCall(sender, epc.entries[0].Input)
if err != nil {
return nil, nil, err
}
err = validateValidityTimeRange(time, aad.ValidAfter.Uint64(), aad.ValidUntil.Uint64())
if err != nil {
return nil, nil, err
}
var apd *AcceptPaymasterData
if paymaster != nil {
apd, err = validatePaymasterEntryPointCall(paymaster, epc.entries[1].Input)
if err != nil {
return nil, nil, err
}
err = validateValidityTimeRange(time, apd.ValidAfter.Uint64(), apd.ValidUntil.Uint64())
if err != nil {
return nil, nil, err
}
}
return aad, apd, nil
}
func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config) (*ValidationPhaseResult, error) {
aatx := tx.Rip7560TransactionData()
err := CheckNonceRip7560(aatx, statedb)
@ -171,21 +224,19 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
blockContext := NewEVMBlockContext(header, bc, author)
sender := tx.Rip7560TransactionData().Sender
paymaster := tx.Rip7560TransactionData().Paymaster
txContext := vm.TxContext{
Origin: *sender,
GasPrice: gasPrice,
}
evm := vm.NewEVM(blockContext, txContext, statedb, chainConfig, cfg)
epc := &EntryPointCall{}
var epc *EntryPointCall
if evm.Config.Tracer == nil {
epc = NewEntryPointCall()
evm.Config.Tracer = &tracing.Hooks{
OnEnter: epc.OnEnter,
}
} else {
// keep the original tracer's OnEnter hook
epc.OnEnterSuper = evm.Config.Tracer.OnEnter
evm.Config.Tracer.OnEnter = epc.OnEnter
}
if evm.Config.Tracer.OnTxStart != nil {
@ -228,70 +279,54 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
if resultAccountValidation.Err != nil {
return nil, resultAccountValidation.Err
}
aad, err := validateAccountEntryPointCall(epc)
if err != nil {
return nil, err
}
// clear the EntryPoint calls array after parsing
epc.err = nil
epc.Input = nil
err = validateValidityTimeRange(header.Time, aad.ValidAfter.Uint64(), aad.ValidUntil.Uint64())
if err != nil {
return nil, err
}
vpr := &ValidationPhaseResult{}
paymasterContext, pmValidationUsedGas, pmValidAfter, pmValidUntil, err := applyPaymasterValidationFrame(epc, tx, chainConfig, signingHash, evm, gp, statedb, header)
pmValidationUsedGas, err := applyPaymasterValidationFrame(tx, chainConfig, signingHash, evm, gp, statedb, header)
if err != nil {
return nil, err
}
aad, apd, err := ValidateAccountAndPaymaster(header.Time, sender, paymaster, epc)
if err != nil {
return nil, err
}
vpr.Tx = tx
vpr.TxHash = tx.Hash()
vpr.PreCharge = preCharge
vpr.EffectiveGasPrice = gasPriceUint256
vpr.PaymasterContext = paymasterContext
vpr.PaymasterContext = apd.Context
vpr.DeploymentUsedGas = deploymentUsedGas
vpr.ValidationUsedGas = resultAccountValidation.UsedGas
vpr.PmValidationUsedGas = pmValidationUsedGas
vpr.SenderValidAfter = aad.ValidAfter.Uint64()
vpr.SenderValidUntil = aad.ValidUntil.Uint64()
vpr.PmValidAfter = pmValidAfter
vpr.PmValidUntil = pmValidUntil
vpr.PmValidAfter = apd.ValidAfter.Uint64()
vpr.PmValidUntil = apd.ValidUntil.Uint64()
statedb.Finalise(true)
return vpr, nil
}
func applyPaymasterValidationFrame(epc *EntryPointCall, tx *types.Transaction, chainConfig *params.ChainConfig, signingHash common.Hash, evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header) ([]byte, uint64, uint64, uint64, error) {
func applyPaymasterValidationFrame(tx *types.Transaction, chainConfig *params.ChainConfig, signingHash common.Hash, evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header) (uint64, error) {
/*** Paymaster Validation Frame ***/
var pmValidationUsedGas uint64
paymasterMsg, err := preparePaymasterValidationMessage(tx, chainConfig, signingHash)
if paymasterMsg == nil || err != nil {
return nil, 0, 0, 0, err
return 0, err
}
resultPm, err := ApplyMessage(evm, paymasterMsg, gp)
if err != nil {
return nil, 0, 0, 0, err
return 0, err
}
if resultPm.Failed() {
return nil, 0, 0, 0, resultPm.Err
return 0, resultPm.Err
}
if resultPm.Failed() {
return nil, 0, 0, 0, errors.New("paymaster validation failed - invalid transaction")
return 0, errors.New("paymaster validation failed - invalid transaction")
}
pmValidationUsedGas = resultPm.UsedGas
apd, err := validatePaymasterEntryPointCall(epc)
if err != nil {
return nil, 0, 0, 0, err
}
err = validateValidityTimeRange(header.Time, apd.ValidAfter.Uint64(), apd.ValidUntil.Uint64())
if err != nil {
return nil, 0, 0, 0, err
}
return apd.Context, pmValidationUsedGas, apd.ValidAfter.Uint64(), apd.ValidUntil.Uint64(), nil
return pmValidationUsedGas, nil
}
func applyPaymasterPostOpFrame(vpr *ValidationPhaseResult, executionResult *ExecutionResult, evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header) (*ExecutionResult, error) {
@ -466,31 +501,18 @@ func preparePostOpMessage(vpr *ValidationPhaseResult, chainConfig *params.ChainC
}, nil
}
func validateAccountEntryPointCall(epc *EntryPointCall) (*AcceptAccountData, error) {
if epc.err != nil {
return nil, epc.err
}
if epc.Input == nil {
return nil, errors.New("account validation did not call the EntryPoint 'acceptAccount' callback")
}
if len(epc.Input) != 68 {
func ValidateAccountEntryPointCall(sender *common.Address, input []byte) (*AcceptAccountData, error) {
if len(input) != 68 {
return nil, errors.New("invalid account return data length")
}
return abiDecodeAcceptAccount(epc.Input)
return abiDecodeAcceptAccount(input)
}
func validatePaymasterEntryPointCall(epc *EntryPointCall) (*AcceptPaymasterData, error) {
if epc.err != nil {
return nil, epc.err
}
if epc.Input == nil {
return nil, errors.New("paymaster validation did not call the EntryPoint 'acceptPaymaster' callback")
}
if len(epc.Input) < 100 {
func validatePaymasterEntryPointCall(paymaster *common.Address, input []byte) (*AcceptPaymasterData, error) {
if len(input) < 100 {
return nil, errors.New("invalid paymaster callback data length")
}
apd, err := abiDecodeAcceptPaymaster(epc.Input)
apd, err := abiDecodeAcceptPaymaster(input)
if err != nil {
return nil, err
}
@ -514,25 +536,18 @@ func validateValidityTimeRange(time uint64, validAfter uint64, validUntil uint64
}
func (epc *EntryPointCall) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
if epc.OnEnterSuper != nil {
epc.OnEnterSuper(depth, typ, from, to, input, gas, value)
}
isRip7560EntryPoint := to.Cmp(AA_ENTRY_POINT) == 0
if !isRip7560EntryPoint {
return
}
if depth != 1 {
println("ONENTER WITH WRONG DEPTH!")
epc.err = errors.New("same")
epc.err = errors.New("called EntryPoint not from top level account")
println(epc.err)
return
}
if epc.Input != nil {
println("repeated call to ep callback")
epc.err = errors.New("same")
return
}
epc.Input = make([]byte, len(input))
copy(epc.Input, input)
epc.entries = append(epc.entries, EntryPointCallEntry{
From: from,
Input: bytes.Clone(input),
})
}

View file

@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/native"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
"math/big"
@ -132,7 +133,9 @@ func (api *Rip7560API) traceTx(ctx context.Context, tx *types.Transaction, txctx
gp := new(core.GasPool).AddGas(10000000)
// TODO: this is added to allow our bundler checking the 'TraceValidation' API is supported on Geth
if tx.Rip7560TransactionData().Sender.Cmp(common.HexToAddress("0x0000000000000000000000000000000000000000")) == 0 {
sender := tx.Rip7560TransactionData().Sender
paymaster := tx.Rip7560TransactionData().Paymaster
if sender.Cmp(common.HexToAddress("0x0000000000000000000000000000000000000000")) == 0 {
return tracer.GetResult()
}
@ -141,5 +144,7 @@ func (api *Rip7560API) traceTx(ctx context.Context, tx *types.Transaction, txctx
if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}
x:= tracer.(*native.Rip7560ValidationTracer).EntryPointCall.entries
core.ValidateAccountAndPaymaster(0, sender, paymaster, tracer.)
return tracer.GetResult()
}

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -69,7 +70,7 @@ func newRip7560Tracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Trace
return nil, err
}
// TODO FIX mock fields
t := &rip7560ValidationTracer{
t := &Rip7560ValidationTracer{
TraceResults: make([]stateMap, ValidationFramesMaxCount),
UsedOpcodes: make([]map[string]bool, ValidationFramesMaxCount),
Created: make([]map[common.Address]bool, ValidationFramesMaxCount),
@ -119,13 +120,13 @@ type logsItem struct {
}
// Array fields contain of all access details of all validation frames
type rip7560ValidationTracer struct {
type Rip7560ValidationTracer struct {
//rip7560TxData *types.Rip7560AccountAbstractionTx
env *tracing.VMContext
TraceResults []stateMap `json:"traceResults"`
UsedOpcodes []map[string]bool `json:"usedOpcodes"`
Created []map[common.Address]bool `json:"created"`
EntryPointCall core.EntryPointCall
env *tracing.VMContext
TraceResults []stateMap `json:"traceResults"`
UsedOpcodes []map[string]bool `json:"usedOpcodes"`
Created []map[common.Address]bool `json:"created"`
//Deleted []map[common.Address]bool `json:"deleted"`
lastThreeOpCodes []*lastThreeOpCodesItem
@ -142,7 +143,9 @@ type rip7560ValidationTracer struct {
//reason error // Textual reason for the interruption
}
func (b *rip7560ValidationTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
func (b *Rip7560ValidationTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
b.entryPointCall.OnEnter(depth, typ, from, to, input, gas, value)
if depth == 0 {
b.createNewTopLevelFrame(to)
}
@ -157,7 +160,7 @@ func (b *rip7560ValidationTracer) OnEnter(depth int, typ byte, from common.Addre
})
}
func (b *rip7560ValidationTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
func (b *Rip7560ValidationTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
typ := "RETURN"
if err != nil {
typ = "REVERT"
@ -169,12 +172,12 @@ func (b *rip7560ValidationTracer) OnExit(depth int, output []byte, gasUsed uint6
})
}
func (b *rip7560ValidationTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
func (b *Rip7560ValidationTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
b.env = env
//b.rip7560TxData = tx.Rip7560TransactionData()
}
func (b *rip7560ValidationTracer) createNewTopLevelFrame(addr common.Address) {
func (b *Rip7560ValidationTracer) createNewTopLevelFrame(addr common.Address) {
b.CurrentLevel = &entryPointCall{
TopLevelTargetAddress: addr,
Access: map[common.Address]*access{},
@ -188,10 +191,10 @@ func (b *rip7560ValidationTracer) createNewTopLevelFrame(addr common.Address) {
return
}
func (b *rip7560ValidationTracer) OnTxEnd(receipt *types.Receipt, err error) {
func (b *Rip7560ValidationTracer) OnTxEnd(receipt *types.Receipt, err error) {
}
func (b *rip7560ValidationTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
func (b *Rip7560ValidationTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
opcode := vm.OpCode(op).String()
stackSize := len(scope.StackData())
@ -344,7 +347,7 @@ func StackBack(stackData []uint256.Int, n int) *uint256.Int {
return &stackData[len(stackData)-n-1]
}
func (b *rip7560ValidationTracer) isEXTorCALL(opcode string) bool {
func (b *Rip7560ValidationTracer) isEXTorCALL(opcode string) bool {
return strings.HasPrefix(opcode, "EXT") ||
opcode == "CALL" ||
opcode == "CALLCODE" ||
@ -354,22 +357,22 @@ func (b *rip7560ValidationTracer) isEXTorCALL(opcode string) bool {
// not using 'isPrecompiled' to only allow the ones defined by the ERC-7562 as stateless precompiles
// [OP-062]
func (b *rip7560ValidationTracer) isAllowedPrecompile(addr common.Address) bool {
func (b *Rip7560ValidationTracer) isAllowedPrecompile(addr common.Address) bool {
addrInt := addr.Big()
return addrInt.Cmp(big.NewInt(0)) == 1 && addrInt.Cmp(big.NewInt(10)) == -1
}
func (b *rip7560ValidationTracer) incrementCount(m map[string]uint64, k string) {
func (b *Rip7560ValidationTracer) incrementCount(m map[string]uint64, k string) {
if _, ok := m[k]; !ok {
m[k] = 0
}
m[k]++
}
func (b *rip7560ValidationTracer) GetResult() (json.RawMessage, error) {
func (b *Rip7560ValidationTracer) GetResult() (json.RawMessage, error) {
jsonResult, err := json.MarshalIndent(*b, "", " ")
return jsonResult, err
}
func (b *rip7560ValidationTracer) Stop(err error) {
func (b *Rip7560ValidationTracer) Stop(err error) {
}