AA-394 receipt fields (#21)

* handle transaction receipt status
  effectiveGasUsed
  refund excess to account/paymaster
This commit is contained in:
Dror Tirosh 2024-08-04 12:54:48 +03:00 committed by GitHub
parent d450ade0af
commit 2ee6caf9c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 55 additions and 27 deletions

View file

@ -6,6 +6,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -61,6 +62,8 @@ type ValidationPhaseResult struct {
Tx *types.Transaction Tx *types.Transaction
TxHash common.Hash TxHash common.Hash
PaymasterContext []byte PaymasterContext []byte
PreCharge *uint256.Int
EffectiveGasPrice *uint256.Int
DeploymentUsedGas uint64 DeploymentUsedGas uint64
ValidationUsedGas uint64 ValidationUsedGas uint64
PmValidationUsedGas uint64 PmValidationUsedGas uint64
@ -119,21 +122,21 @@ func handleRip7560Transactions(transactions []*types.Transaction, index int, sta
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
statedb.Finalise(true)
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...) allLogs = append(allLogs, receipt.Logs...)
} }
return validatedTransactions, receipts, allLogs, nil return validatedTransactions, receipts, allLogs, nil
} }
// BuyGasRip7560Transaction
// todo: move to a suitable interface, whatever that is // todo: move to a suitable interface, whatever that is
// todo 2: maybe handle the "shared gas pool" situation instead of just overriding it completely? // todo 2: maybe handle the "shared gas pool" situation instead of just overriding it completely?
func BuyGasRip7560Transaction(st *types.Rip7560AccountAbstractionTx, state vm.StateDB) error { func BuyGasRip7560Transaction(st *types.Rip7560AccountAbstractionTx, state vm.StateDB, gasPrice *uint256.Int) (*uint256.Int, error) {
gasLimit := st.Gas + st.ValidationGasLimit + st.PaymasterValidationGasLimit + st.PostOpGas gasLimit := st.Gas + st.ValidationGasLimit + st.PaymasterValidationGasLimit + st.PostOpGas
mgval := new(uint256.Int).SetUint64(gasLimit) preCharge := new(uint256.Int).SetUint64(gasLimit)
gasFeeCap, _ := uint256.FromBig(st.GasFeeCap) preCharge = preCharge.Mul(preCharge, gasPrice)
mgval = mgval.Mul(mgval, gasFeeCap) balanceCheck := new(uint256.Int).Set(preCharge)
balanceCheck := new(uint256.Int).Set(mgval)
chargeFrom := st.Sender chargeFrom := st.Sender
@ -142,11 +145,27 @@ func BuyGasRip7560Transaction(st *types.Rip7560AccountAbstractionTx, state vm.St
} }
if have, want := state.GetBalance(*chargeFrom), balanceCheck; have.Cmp(want) < 0 { if have, want := state.GetBalance(*chargeFrom), balanceCheck; have.Cmp(want) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, chargeFrom.Hex(), have, want) return nil, fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, chargeFrom.Hex(), have, want)
} }
state.SubBalance(*chargeFrom, mgval, 0) state.SubBalance(*chargeFrom, preCharge, 0)
return nil return preCharge, nil
}
// refund the transaction payer (either account or paymaster) with the excess gas cost
func refundPayer(vpr *ValidationPhaseResult, state vm.StateDB, gasUsed uint64) {
var chargeFrom *common.Address
if vpr.PmValidationUsedGas == 0 {
chargeFrom = vpr.Tx.Rip7560TransactionData().Sender
} else {
chargeFrom = vpr.Tx.Rip7560TransactionData().Paymaster
}
actualGasCost := new(uint256.Int).Mul(vpr.EffectiveGasPrice, new(uint256.Int).SetUint64(gasUsed))
refund := new(uint256.Int).Sub(vpr.PreCharge, actualGasCost)
state.AddBalance(*chargeFrom, refund, tracing.BalanceIncreaseGasReturn)
} }
// precheck nonce of transaction. // precheck nonce of transaction.
@ -173,7 +192,14 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = BuyGasRip7560Transaction(aatx, statedb)
gasPrice := new(big.Int).Add(header.BaseFee, tx.GasTipCap())
if gasPrice.Cmp(tx.GasFeeCap()) > 0 {
gasPrice = tx.GasFeeCap()
}
gasPriceUint256, _ := uint256.FromBig(gasPrice)
preCharge, err := BuyGasRip7560Transaction(aatx, statedb, gasPriceUint256)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -182,7 +208,7 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
sender := tx.Rip7560TransactionData().Sender sender := tx.Rip7560TransactionData().Sender
txContext := vm.TxContext{ txContext := vm.TxContext{
Origin: *sender, Origin: *sender,
GasPrice: tx.GasFeeCap(), GasPrice: gasPrice,
} }
evm := vm.NewEVM(blockContext, txContext, statedb, chainConfig, cfg) evm := vm.NewEVM(blockContext, txContext, statedb, chainConfig, cfg)
@ -211,7 +237,6 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
if err != nil { if err != nil {
return nil, fmt.Errorf("account deployment failed: %v", err) return nil, fmt.Errorf("account deployment failed: %v", err)
} }
statedb.IntermediateRoot(true)
} else { } else {
statedb.SetNonce(*sender, statedb.GetNonce(*sender)+1) statedb.SetNonce(*sender, statedb.GetNonce(*sender)+1)
} }
@ -224,7 +249,6 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
if err != nil { if err != nil {
return nil, err return nil, err
} }
statedb.IntermediateRoot(true)
if resultAccountValidation.Err != nil { if resultAccountValidation.Err != nil {
return nil, resultAccountValidation.Err return nil, resultAccountValidation.Err
} }
@ -241,9 +265,12 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
if err != nil { if err != nil {
return nil, err return nil, err
} }
vpr := &ValidationPhaseResult{ vpr := &ValidationPhaseResult{
Tx: tx, Tx: tx,
TxHash: tx.Hash(), TxHash: tx.Hash(),
PreCharge: preCharge,
EffectiveGasPrice: gasPriceUint256,
PaymasterContext: paymasterContext, PaymasterContext: paymasterContext,
DeploymentUsedGas: deploymentUsedGas, DeploymentUsedGas: deploymentUsedGas,
ValidationUsedGas: resultAccountValidation.UsedGas, ValidationUsedGas: resultAccountValidation.UsedGas,
@ -253,6 +280,7 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
PmValidAfter: pmValidAfter, PmValidAfter: pmValidAfter,
PmValidUntil: pmValidUntil, PmValidUntil: pmValidUntil,
} }
statedb.Finalise(true)
return vpr, nil return vpr, nil
} }
@ -275,7 +303,6 @@ func applyPaymasterValidationFrame(tx *types.Transaction, chainConfig *params.Ch
if resultPm.Failed() { if resultPm.Failed() {
return nil, 0, 0, 0, resultPm.Err return nil, 0, 0, 0, resultPm.Err
} }
statedb.IntermediateRoot(true)
if resultPm.Failed() { if resultPm.Failed() {
return nil, 0, 0, 0, errors.New("paymaster validation failed - invalid transaction") return nil, 0, 0, 0, errors.New("paymaster validation failed - invalid transaction")
} }
@ -321,36 +348,40 @@ func ApplyRip7560ExecutionPhase(config *params.ChainConfig, vpr *ValidationPhase
if err != nil { if err != nil {
return nil, err return nil, err
} }
root := statedb.IntermediateRoot(true).Bytes()
var paymasterPostOpResult *ExecutionResult var paymasterPostOpResult *ExecutionResult
if len(vpr.PaymasterContext) != 0 { if len(vpr.PaymasterContext) != 0 {
paymasterPostOpResult, err = applyPaymasterPostOpFrame(vpr, executionResult, evm, gp, statedb, header) paymasterPostOpResult, err = applyPaymasterPostOpFrame(vpr, executionResult, evm, gp, statedb, header)
root = statedb.IntermediateRoot(true).Bytes()
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
cumulativeGasUsed := gasUsed :=
vpr.ValidationUsedGas + vpr.ValidationUsedGas +
vpr.DeploymentUsedGas + vpr.DeploymentUsedGas +
vpr.PmValidationUsedGas + vpr.PmValidationUsedGas +
executionResult.UsedGas executionResult.UsedGas
if paymasterPostOpResult != nil { if paymasterPostOpResult != nil {
cumulativeGasUsed += gasUsed +=
paymasterPostOpResult.UsedGas paymasterPostOpResult.UsedGas
} }
receipt := &types.Receipt{Type: vpr.Tx.Type(), PostState: root, CumulativeGasUsed: cumulativeGasUsed} receipt := &types.Receipt{Type: vpr.Tx.Type(), TxHash: vpr.Tx.Hash(), GasUsed: gasUsed, CumulativeGasUsed: gasUsed}
// Set the receipt logs and create the bloom filter.
receipt.Logs = statedb.GetLogs(vpr.Tx.Hash(), header.Number.Uint64(), header.Hash())
if executionResult.Failed() || (paymasterPostOpResult != nil && paymasterPostOpResult.Failed()) { if executionResult.Failed() || (paymasterPostOpResult != nil && paymasterPostOpResult.Failed()) {
receipt.Status = types.ReceiptStatusFailed receipt.Status = types.ReceiptStatusFailed
} else { } else {
receipt.Status = types.ReceiptStatusSuccessful receipt.Status = types.ReceiptStatusSuccessful
} }
refundPayer(vpr, statedb, gasUsed)
// Set the receipt logs and create the bloom filter.
blockNumber := header.Number
receipt.Logs = statedb.GetLogs(vpr.TxHash, blockNumber.Uint64(), common.Hash{})
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
receipt.TransactionIndex = uint(vpr.TxIndex)
// other fields are filled in DeriveFields (all tx, block fields, and updating CumulativeGasUsed
return receipt, err return receipt, err
} }

View file

@ -348,7 +348,8 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
rs[i].TransactionIndex = uint(i) rs[i].TransactionIndex = uint(i)
// The contract address can be derived from the transaction itself // The contract address can be derived from the transaction itself
if txs[i].To() == nil { // AA transactions always have "sender" as the account address, regardless if it is created by this TX
if txs[i].To() == nil && txs[i].Type() != Rip7560Type {
// Deriving the signer is expensive, only do if it's actually needed // Deriving the signer is expensive, only do if it's actually needed
from, _ := Sender(signer, txs[i]) from, _ := Sender(signer, txs[i])
rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())

View file

@ -2,7 +2,6 @@ package tracers
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -12,7 +11,6 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"log"
"math/big" "math/big"
"time" "time"
) )
@ -61,8 +59,6 @@ func (api *Rip7560API) TraceRip7560Validation(
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Println("TraceRip7560Validation result")
log.Println(string(traceResult.(json.RawMessage)))
return traceResult, err return traceResult, err
} }