From 6e2e44f32b8196f5e5eba66e4d45f5ff7c9c74ba Mon Sep 17 00:00:00 2001 From: Dror Tirosh Date: Mon, 1 Jul 2024 16:46:06 +0300 Subject: [PATCH] initial paymaster test flows --- core/state_processor_rip7560.go | 32 ++++++++++++---- tests/rip7560/paymaster_test.go | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 tests/rip7560/paymaster_test.go diff --git a/core/state_processor_rip7560.go b/core/state_processor_rip7560.go index 208e4609de..0ceb83448e 100644 --- a/core/state_processor_rip7560.go +++ b/core/state_processor_rip7560.go @@ -37,9 +37,16 @@ func UnpackValidationData(validationData []byte) (authorizerMagic uint64, validU } func UnpackPaymasterValidationReturn(paymasterValidationReturn []byte) (validationData, context []byte) { + if len(paymasterValidationReturn) < 96 { + return nil, nil + } validationData = paymasterValidationReturn[0:32] //2nd bytes32 is ignored (its an offset value) contextLen := new(big.Int).SetBytes(paymasterValidationReturn[64:96]) + if uint64(len(paymasterValidationReturn)) < 96+contextLen.Uint64() { + return nil, nil + } + context = paymasterValidationReturn[96 : 96+contextLen.Uint64()] return } @@ -133,17 +140,17 @@ func BuyGasRip7560Transaction(st *types.Rip7560AccountAbstractionTx, state vm.St mgval = mgval.Mul(mgval, gasFeeCap) balanceCheck := new(uint256.Int).Set(mgval) - chargeFrom := *st.Sender + chargeFrom := st.Sender - if len(st.PaymasterData) >= 20 { - chargeFrom = [20]byte(st.PaymasterData[:20]) + if st.Paymaster != nil { + chargeFrom = st.Paymaster } - 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) } - state.SubBalance(chargeFrom, mgval, 0) + state.SubBalance(*chargeFrom, mgval, 0) return nil } @@ -210,6 +217,9 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte } paymasterContext, pmValidationUsedGas, pmValidAfter, pmValidUntil, err := applyPaymasterValidationFrame(tx, chainConfig, signingHash, evm, gp, statedb, header) + if err != nil { + return nil, err + } vpr := &ValidationPhaseResult{ Tx: tx, TxHash: tx.Hash(), @@ -241,6 +251,9 @@ func applyPaymasterValidationFrame(tx *types.Transaction, chainConfig *params.Ch if err != nil { return nil, 0, 0, 0, err } + if resultPm.Failed() { + return nil, 0, 0, 0, resultPm.Err + } statedb.IntermediateRoot(true) if resultPm.Failed() { return nil, 0, 0, 0, errors.New("paymaster validation failed - invalid transaction") @@ -370,10 +383,10 @@ func prepareAccountValidationMessage(baseTx *types.Transaction, chainConfig *par func preparePaymasterValidationMessage(baseTx *types.Transaction, config *params.ChainConfig, signingHash common.Hash) (*Message, error) { tx := baseTx.Rip7560TransactionData() - if len(tx.PaymasterData) < 20 { + paymasterAddress := tx.Paymaster + if paymasterAddress == nil { return nil, nil } - var paymasterAddress common.Address = [20]byte(tx.PaymasterData[0:20]) jsondata := `[ {"type":"function","name":"validatePaymasterTransaction","inputs": [{"name": "version","type": "uint256"},{"name": "txHash","type": "bytes32"},{"name": "transaction","type": "bytes"}]} ]` @@ -387,7 +400,7 @@ func preparePaymasterValidationMessage(baseTx *types.Transaction, config *params } return &Message{ From: config.EntryPointAddress, - To: &paymasterAddress, + To: paymasterAddress, Value: big.NewInt(0), GasLimit: tx.PaymasterGas, GasPrice: tx.GasFeeCap, @@ -470,6 +483,9 @@ func validatePaymasterReturnData(data []byte) (context []byte, validAfter, valid return nil, 0, 0, errors.New("invalid paymaster return data length") } validationData, context := UnpackPaymasterValidationReturn(data) + if validationData == nil { + return nil, 0, 0, errors.New("invalid paymaster return data") + } magicExpected, validAfter, validUntil := UnpackValidationData(validationData) if magicExpected != MAGIC_VALUE_PAYMASTER { return nil, 0, 0, errors.New("paymaster did not return correct MAGIC_VALUE") diff --git a/tests/rip7560/paymaster_test.go b/tests/rip7560/paymaster_test.go new file mode 100644 index 0000000000..bb2cd6c670 --- /dev/null +++ b/tests/rip7560/paymaster_test.go @@ -0,0 +1,67 @@ +package rip7560 + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "math/big" + "slices" + "testing" +) + +var DEFAULT_PAYMASTER = common.HexToAddress("0xaaaaaaaaaabbbbbbbbbbccccccccccdddddddddd") + +func TestPaymasterValidationFailure_nobalance(t *testing.T) { + + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 0). + withCode(DEFAULT_PAYMASTER.String(), createCode(vm.PUSH0, vm.DUP1, vm.REVERT), 1), types.Rip7560AccountAbstractionTx{ + ValidationGas: 1000000000, + GasFeeCap: big.NewInt(1000000000), + Paymaster: &DEFAULT_PAYMASTER, + }, "insufficient funds for gas * price + value: address 0xaaAaaAAAAAbBbbbbBbBBCCCCcCCCcCdddDDDdddd have 1 want 1000000000000000000") +} + +func TestPaymasterValidationFailure_oog(t *testing.T) { + + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 0). + withCode(DEFAULT_PAYMASTER.String(), createCode(vm.PUSH0, vm.DUP1, vm.REVERT), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + ValidationGas: 1000000000, + GasFeeCap: big.NewInt(1000000000), + Paymaster: &DEFAULT_PAYMASTER, + }, "out of gas") +} +func TestPaymasterValidationFailure_revert(t *testing.T) { + + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 0). + withCode(DEFAULT_PAYMASTER.String(), createCode(vm.PUSH0, vm.DUP1, vm.REVERT), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + Paymaster: &DEFAULT_PAYMASTER, + PaymasterGas: 1000000000, + }, "execution reverted") +} + +func asBytes32(a int) []byte { + return common.LeftPadBytes(big.NewInt(int64(a)).Bytes(), 32) +} +func paymasterReturnValue(magic, validAfter, validUntil uint64, context []byte) []byte { + validationData := core.PackValidationData(magic, validUntil, validAfter) + //manual encode (bytes32 validationData, bytes context) + return slices.Concat( + common.LeftPadBytes(validationData, 32), + asBytes32(64), + asBytes32(len(context)), + context) +} + +func TestPaymasterValidationFailure_unparseable_return_value(t *testing.T) { + + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 0). + withCode(DEFAULT_PAYMASTER.String(), createAccountCode(), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + ValidationGas: 1000000000, + PaymasterGas: 1000000000, + GasFeeCap: big.NewInt(1000000000), + Paymaster: &DEFAULT_PAYMASTER, + }, "invalid paymaster return data") +}