test paymaster validations

This commit is contained in:
Dror Tirosh 2024-07-01 20:49:00 +03:00
parent 6b7385d6a7
commit b0b490f350
4 changed files with 83 additions and 18 deletions

View file

@ -447,10 +447,10 @@ func preparePostOpMessage(vpr *ValidationPhaseResult, chainConfig *params.ChainC
if err != nil {
return nil, err
}
var paymasterAddress common.Address = [20]byte(tx.PaymasterData[0:20])
var paymasterAddress = tx.Paymaster
return &Message{
From: chainConfig.EntryPointAddress,
To: &paymasterAddress,
To: paymasterAddress,
Value: big.NewInt(0),
GasLimit: tx.PaymasterGas - executionResult.UsedGas,
GasPrice: tx.GasFeeCap,
@ -490,6 +490,9 @@ func validatePaymasterReturnData(data []byte) (context []byte, validAfter, valid
if magicExpected != MAGIC_VALUE_PAYMASTER {
return nil, 0, 0, errors.New("paymaster did not return correct MAGIC_VALUE")
}
if len(context) > PAYMASTER_MAX_CONTEXT_SIZE {
return nil, 0, 0, errors.New("paymaster context too large")
}
return context, validAfter, validUntil, nil
}

View file

@ -6,7 +6,6 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"math/big"
"slices"
"testing"
)
@ -42,19 +41,6 @@ func TestPaymasterValidationFailure_revert(t *testing.T) {
}, "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).
@ -65,3 +51,45 @@ func TestPaymasterValidationFailure_unparseable_return_value(t *testing.T) {
Paymaster: &DEFAULT_PAYMASTER,
}, "invalid paymaster return data")
}
func TestPaymasterValidationFailure_wrong_magic(t *testing.T) {
handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 0).
withCode(DEFAULT_PAYMASTER.String(), returnWithData(paymasterReturnValue(1, 2, 3, []byte{})), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{
ValidationGas: 1000000000,
PaymasterGas: 1000000000,
GasFeeCap: big.NewInt(1000000000),
Paymaster: &DEFAULT_PAYMASTER,
}, "paymaster did not return correct MAGIC_VALUE")
}
func TestPaymasterValidationFailure_contextTooLarge(t *testing.T) {
//paymaster returning huge context.
// first word is magic return value
// 2nd word is offset (fixed 64)
// 3rd word is length of context (max+1)
// then we return the total length of above (context itself is uninitialized string of max+1 zeroes)
pmCode := createCode(
//vm.PUSH1, 1, vm.PUSH0, vm.RETURN,
copyToMemory(core.PackValidationData(core.MAGIC_VALUE_PAYMASTER, 0, 0), 0),
copyToMemory(asBytes32(64), 32),
copyToMemory(asBytes32(core.PAYMASTER_MAX_CONTEXT_SIZE+1), 64),
push(core.PAYMASTER_MAX_CONTEXT_SIZE+96+1), vm.PUSH0, vm.RETURN)
handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 0).
withCode(DEFAULT_PAYMASTER.String(), pmCode, DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{
ValidationGas: 1000000000,
PaymasterGas: 1000000000,
GasFeeCap: big.NewInt(1000000000),
Paymaster: &DEFAULT_PAYMASTER,
}, "paymaster context too large")
}
func TestPaymasterValidation_ok(t *testing.T) {
handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 0).
withCode(DEFAULT_PAYMASTER.String(), returnWithData(paymasterReturnValue(core.MAGIC_VALUE_PAYMASTER, 0, 0, []byte{})), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{
ValidationGas: 1000000000,
PaymasterGas: 1000000000,
GasFeeCap: big.NewInt(1000000000),
Paymaster: &DEFAULT_PAYMASTER,
}, "ok")
}

View file

@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/status-im/keycard-go/hexutils"
"math/big"
"slices"
"testing"
)
@ -83,6 +84,23 @@ func (tt *testContextBuilder) withCode(addr string, code []byte, balance int64)
return tt
}
// generate a push opcode and its following constant value
func push(n int) []byte {
if n < 0 {
panic("attempt to push negative")
}
if n < 256 {
return createCode(vm.PUSH1, byte(n))
}
if n < 65536 {
return createCode(vm.PUSH2, byte(n>>8), byte(n))
}
if n < 1<<32 {
return createCode(vm.PUSH4, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}
panic("larger number")
}
// create code to copy data into memory at the given offset
// NOTE: if data is not in 32-byte multiples, it will override the next bytes
// used by RETURN/REVERT
@ -135,6 +153,8 @@ func createCode(items ...interface{}) []byte {
buffer.WriteByte(byte(v))
case uint16:
buffer.Write([]byte{byte(v >> 8), byte(v)})
case uint32:
buffer.Write([]byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)})
case int:
if v >= 256 {
panic(fmt.Errorf("int defaults to int8 (byte). use int16, etc: %v", v))
@ -148,3 +168,17 @@ func createCode(items ...interface{}) []byte {
return buffer.Bytes()
}
func asBytes32(a int) []byte {
return common.LeftPadBytes(big.NewInt(int64(a)).Bytes(), 32)
}
func paymasterReturnValue(magic, validUntil, validAfter 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)
}

View file

@ -53,7 +53,7 @@ func TestValidationFailure_sigerror(t *testing.T) {
func TestValidationFailure_validAfter(t *testing.T) {
handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER,
returnData(core.PackValidationData(core.MAGIC_VALUE_SENDER, 300, 200)), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{
returnWithData(core.PackValidationData(core.MAGIC_VALUE_SENDER, 300, 200)), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{
ValidationGas: uint64(1000000000),
GasFeeCap: big.NewInt(1000000000),
}, "RIP-7560 transaction validity not reached yet")
@ -62,7 +62,7 @@ func TestValidationFailure_validAfter(t *testing.T) {
func TestValidationFailure_validUntil(t *testing.T) {
handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER,
returnData(core.PackValidationData(core.MAGIC_VALUE_SENDER, 1, 0)), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{
returnWithData(core.PackValidationData(core.MAGIC_VALUE_SENDER, 1, 0)), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{
ValidationGas: uint64(1000000000),
GasFeeCap: big.NewInt(1000000000),
}, "RIP-7560 transaction validity expired")