initial paymaster test flows

This commit is contained in:
Dror Tirosh 2024-07-01 16:46:06 +03:00
parent d5c3069d71
commit 6e2e44f32b
2 changed files with 91 additions and 8 deletions

View file

@ -37,9 +37,16 @@ func UnpackValidationData(validationData []byte) (authorizerMagic uint64, validU
} }
func UnpackPaymasterValidationReturn(paymasterValidationReturn []byte) (validationData, context []byte) { func UnpackPaymasterValidationReturn(paymasterValidationReturn []byte) (validationData, context []byte) {
if len(paymasterValidationReturn) < 96 {
return nil, nil
}
validationData = paymasterValidationReturn[0:32] validationData = paymasterValidationReturn[0:32]
//2nd bytes32 is ignored (its an offset value) //2nd bytes32 is ignored (its an offset value)
contextLen := new(big.Int).SetBytes(paymasterValidationReturn[64:96]) contextLen := new(big.Int).SetBytes(paymasterValidationReturn[64:96])
if uint64(len(paymasterValidationReturn)) < 96+contextLen.Uint64() {
return nil, nil
}
context = paymasterValidationReturn[96 : 96+contextLen.Uint64()] context = paymasterValidationReturn[96 : 96+contextLen.Uint64()]
return return
} }
@ -133,17 +140,17 @@ func BuyGasRip7560Transaction(st *types.Rip7560AccountAbstractionTx, state vm.St
mgval = mgval.Mul(mgval, gasFeeCap) mgval = mgval.Mul(mgval, gasFeeCap)
balanceCheck := new(uint256.Int).Set(mgval) balanceCheck := new(uint256.Int).Set(mgval)
chargeFrom := *st.Sender chargeFrom := st.Sender
if len(st.PaymasterData) >= 20 { if st.Paymaster != nil {
chargeFrom = [20]byte(st.PaymasterData[:20]) 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) 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 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) paymasterContext, pmValidationUsedGas, pmValidAfter, pmValidUntil, err := applyPaymasterValidationFrame(tx, chainConfig, signingHash, evm, gp, statedb, header)
if err != nil {
return nil, err
}
vpr := &ValidationPhaseResult{ vpr := &ValidationPhaseResult{
Tx: tx, Tx: tx,
TxHash: tx.Hash(), TxHash: tx.Hash(),
@ -241,6 +251,9 @@ func applyPaymasterValidationFrame(tx *types.Transaction, chainConfig *params.Ch
if err != nil { if err != nil {
return nil, 0, 0, 0, err return nil, 0, 0, 0, err
} }
if resultPm.Failed() {
return nil, 0, 0, 0, resultPm.Err
}
statedb.IntermediateRoot(true) 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")
@ -370,10 +383,10 @@ func prepareAccountValidationMessage(baseTx *types.Transaction, chainConfig *par
func preparePaymasterValidationMessage(baseTx *types.Transaction, config *params.ChainConfig, signingHash common.Hash) (*Message, error) { func preparePaymasterValidationMessage(baseTx *types.Transaction, config *params.ChainConfig, signingHash common.Hash) (*Message, error) {
tx := baseTx.Rip7560TransactionData() tx := baseTx.Rip7560TransactionData()
if len(tx.PaymasterData) < 20 { paymasterAddress := tx.Paymaster
if paymasterAddress == nil {
return nil, nil return nil, nil
} }
var paymasterAddress common.Address = [20]byte(tx.PaymasterData[0:20])
jsondata := `[ jsondata := `[
{"type":"function","name":"validatePaymasterTransaction","inputs": [{"name": "version","type": "uint256"},{"name": "txHash","type": "bytes32"},{"name": "transaction","type": "bytes"}]} {"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{ return &Message{
From: config.EntryPointAddress, From: config.EntryPointAddress,
To: &paymasterAddress, To: paymasterAddress,
Value: big.NewInt(0), Value: big.NewInt(0),
GasLimit: tx.PaymasterGas, GasLimit: tx.PaymasterGas,
GasPrice: tx.GasFeeCap, 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") return nil, 0, 0, errors.New("invalid paymaster return data length")
} }
validationData, context := UnpackPaymasterValidationReturn(data) validationData, context := UnpackPaymasterValidationReturn(data)
if validationData == nil {
return nil, 0, 0, errors.New("invalid paymaster return data")
}
magicExpected, validAfter, validUntil := UnpackValidationData(validationData) magicExpected, validAfter, validUntil := UnpackValidationData(validationData)
if magicExpected != MAGIC_VALUE_PAYMASTER { if magicExpected != MAGIC_VALUE_PAYMASTER {
return nil, 0, 0, errors.New("paymaster did not return correct MAGIC_VALUE") return nil, 0, 0, errors.New("paymaster did not return correct MAGIC_VALUE")

View file

@ -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")
}