Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test abi encoding acceptAccount, acceptPaymaster #26

Open
wants to merge 4 commits into
base: RIP-7560-revision-2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 42 additions & 30 deletions core/rip7560_abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package core

import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"math/big"
"strings"
)

var Rip7560Abi, err = abi.JSON(strings.NewReader(Rip7560AbiJson))

type AcceptAccountData struct {
ValidAfter *big.Int
ValidUntil *big.Int
Expand All @@ -21,61 +24,70 @@ type AcceptPaymasterData struct {
}

func abiEncodeValidateTransaction(tx *types.Rip7560AccountAbstractionTx, signingHash common.Hash) ([]byte, error) {
jsonAbi, err := abi.JSON(strings.NewReader(ValidateTransactionAbi))

txAbiEncoding, err := tx.AbiEncode()
if err != nil {
return nil, err
}

txAbiEncoding, err := tx.AbiEncode()
validateTransactionData, err := jsonAbi.Pack("validateTransaction", big.NewInt(Rip7560AbiVersion), signingHash, txAbiEncoding)
validateTransactionData, err := Rip7560Abi.Pack("validateTransaction", big.NewInt(Rip7560AbiVersion), signingHash, txAbiEncoding)
return validateTransactionData, err
}

func abiEncodeValidatePaymasterTransaction(tx *types.Rip7560AccountAbstractionTx, signingHash common.Hash) ([]byte, error) {
jsonAbi, err := abi.JSON(strings.NewReader(ValidatePaymasterTransactionAbi))
txAbiEncoding, err := tx.AbiEncode()
data, err := jsonAbi.Pack("validatePaymasterTransaction", big.NewInt(Rip7560AbiVersion), signingHash, txAbiEncoding)
if err != nil {
return nil, err
}
data, err := Rip7560Abi.Pack("validatePaymasterTransaction", big.NewInt(Rip7560AbiVersion), signingHash, txAbiEncoding)
return data, err
}

func abiEncodePostPaymasterTransaction(context []byte) ([]byte, error) {
jsonAbi, err := abi.JSON(strings.NewReader(PostPaymasterTransactionAbi))
if err != nil {
return nil, err
}
// TODO: pass actual gas cost parameter here!
postOpData, err := jsonAbi.Pack("postPaymasterTransaction", true, big.NewInt(0), context)
postOpData, err := Rip7560Abi.Pack("postPaymasterTransaction", true, big.NewInt(0), context)
return postOpData, err
}

func abiDecodeAcceptAccount(input []byte) (*AcceptAccountData, error) {
jsonAbi, err := abi.JSON(strings.NewReader(AcceptAccountAbi))
func decodeMethodParamsToInterface(output interface{}, methodName string, input []byte) error {
m, err := Rip7560Abi.MethodById(input)
if err != nil {
return nil, err
return fmt.Errorf("unable to decode %s: %w", methodName, err)
}
methodSelector := new(big.Int).SetBytes(input[:4]).Uint64()
if methodSelector != AcceptAccountMethodSig {
if methodSelector == SigFailAccountMethodSig {
return nil, errors.New("account signature error")
}
return nil, errors.New("account validation did call the EntryPoint but not the 'acceptAccount' callback")
if methodName != m.Name {
return fmt.Errorf("unable to decode %s: got wrong method %s", methodName, m.Name)
}
acceptAccountData := &AcceptAccountData{}
err = jsonAbi.UnpackIntoInterface(acceptAccountData, "acceptAccount", input[4:])
return acceptAccountData, err
params, err := m.Inputs.Unpack(input[4:])
if err != nil {
return fmt.Errorf("unable to decode %s: %w", methodName, err)
}
err = m.Inputs.Copy(output, params)
if err != nil {
return fmt.Errorf("unable to decode %s: %v", methodName, err)
}
return nil
}

func abiDecodeAcceptPaymaster(input []byte) (*AcceptPaymasterData, error) {
jsonAbi, err := abi.JSON(strings.NewReader(AcceptPaymasterAbi))
func abiDecodeAcceptAccount(input []byte, allowSigFail bool) (*AcceptAccountData, error) {
acceptAccountData := &AcceptAccountData{}
err := decodeMethodParamsToInterface(acceptAccountData, "acceptAccount", input)
if err != nil && allowSigFail {
err = decodeMethodParamsToInterface(acceptAccountData, "sigFailAccount", input)
}
if err != nil {
return nil, err
}
methodSelector := new(big.Int).SetBytes(input[:4]).Uint64()
if methodSelector != AcceptPaymasterMethodSig {
return nil, errors.New("paymaster validation did call the EntryPoint but not the 'acceptPaymaster' callback")
}
return acceptAccountData, nil
}

func abiDecodeAcceptPaymaster(input []byte, allowSigFail bool) (*AcceptPaymasterData, error) {
acceptPaymasterData := &AcceptPaymasterData{}
err = jsonAbi.UnpackIntoInterface(acceptPaymasterData, "acceptPaymaster", input[4:])
err := decodeMethodParamsToInterface(acceptPaymasterData, "acceptPaymaster", input)
if err != nil {
err = decodeMethodParamsToInterface(acceptPaymasterData, "sigFailPaymaster", input)
}
if err != nil {
return nil, err
}
if len(acceptPaymasterData.Context) > PaymasterMaxContextSize {
return nil, errors.New("paymaster return data: context too large")
}
Expand Down
55 changes: 24 additions & 31 deletions core/rip7560_abi_constants.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package core

const AcceptAccountMethodSig = uint64(0x1256ebd1) // acceptAccount(uint256,uint256)
const AcceptPaymasterMethodSig = uint64(0x03be8439) // acceptPaymaster(uint256,uint256,bytes)
const SigFailAccountMethodSig = uint64(0x7715fac2) // sigFailAccount(uint256,uint256)
const PaymasterMaxContextSize = 65536
const Rip7560AbiVersion = 0

const ValidateTransactionAbi = `
const Rip7560AbiJson = `
[
{
"type":"function",
Expand All @@ -16,11 +12,7 @@ const ValidateTransactionAbi = `
{"name": "txHash","type": "bytes32"},
{"name": "transaction","type": "bytes"}
]
}
]`

const ValidatePaymasterTransactionAbi = `
[
},
{
"type":"function",
"name":"validatePaymasterTransaction",
Expand All @@ -29,11 +21,7 @@ const ValidatePaymasterTransactionAbi = `
{"name": "txHash","type": "bytes32"},
{"name": "transaction","type": "bytes"}
]
}
]`

const PostPaymasterTransactionAbi = `
[
},
{
"type":"function",
"name":"postPaymasterTransaction",
Expand All @@ -42,31 +30,36 @@ const PostPaymasterTransactionAbi = `
{"name": "actualGasCost","type": "uint256"},
{"name": "context","type": "bytes"}
]
}
]`

// AcceptAccountAbi Note that this is not a true ABI of the "acceptAccount" function.
// This ABI swaps inputs and outputs to simplify the ABI decoding.
const AcceptAccountAbi = `
[
},
{
"type":"function",
"name":"acceptAccount",
"outputs": [
"inputs": [
{"name": "validAfter","type": "uint256"},
{"name": "validUntil","type": "uint256"}
]
}
]`

// AcceptPaymasterAbi Note that this is not a true ABI of the "acceptPaymaster" function.
// This ABI swaps inputs and outputs to simplify the ABI decoding.
const AcceptPaymasterAbi = `
[
},
{
"type":"function",
"name":"acceptPaymaster",
"outputs": [
"inputs": [
{"name": "validAfter","type": "uint256"},
{"name": "validUntil","type": "uint256"},
{"name": "context","type": "bytes"}
]
},
{
"type":"function",
"name":"sigFailAccount",
"inputs": [
{"name": "validAfter","type": "uint256"},
{"name": "validUntil","type": "uint256"}
]
},
{
"type":"function",
"name":"sigFailPaymaster",
"inputs": [
{"name": "validAfter","type": "uint256"},
{"name": "validUntil","type": "uint256"},
{"name": "context","type": "bytes"}
Expand Down
59 changes: 59 additions & 0 deletions core/rip7560_abi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package core

import (
"github.com/stretchr/testify/assert"
"math/big"
"testing"
)

func Test_abiDecodeAcceptAccount(t *testing.T) {
packed, err := Rip7560Abi.Pack("acceptAccount", big.NewInt(1), big.NewInt(2))
if err != nil {
assert.Failf(t, "failed to encode acceptAccount", "error: %v", err)
return
}
acceptAccountData, err := abiDecodeAcceptAccount(packed)
if err != nil {
assert.Failf(t, "failed to decode acceptAccount", "error: %v", err)
return
}
assert.Equal(t, AcceptAccountData{
ValidAfter: big.NewInt(1),
ValidUntil: big.NewInt(2),
}, *acceptAccountData)
}

func Test_abiDecodeAcceptPaymaster(t *testing.T) {
context := []byte{0x03, 0x04, 0x05}
packed, err := Rip7560Abi.Pack("acceptPaymaster", big.NewInt(1), big.NewInt(2), context)
assert.NoError(t, err)
acceptPaymasterData, err := abiDecodeAcceptPaymaster(packed)
assert.NoError(t, err)
assert.Equal(t, AcceptPaymasterData{
ValidAfter: big.NewInt(1),
ValidUntil: big.NewInt(2),
Context: context,
}, *acceptPaymasterData)
}

func Test_abiDecodeAcceptAccount_wrongSig(t *testing.T) {
packed, err := Rip7560Abi.Pack("acceptAccount", big.NewInt(1), big.NewInt(2))
assert.NoError(t, err)

wrongSig := append([]byte{0x00}, packed[1:]...)
_, err = abiDecodeAcceptAccount(wrongSig)

assert.EqualError(t, err, "unable to decode acceptAccount: no method with id: 0x0056ebd1")

wrongData := packed[:len(packed)-1]
_, err = abiDecodeAcceptAccount(wrongData)
assert.EqualError(t, err, "unable to decode acceptAccount: abi: cannot marshal in to go type: length insufficient 63 require 64")

pmPacked, err := Rip7560Abi.Pack("acceptPaymaster", big.NewInt(1), big.NewInt(2), []byte{0x01})
assert.NoError(t, err)

wrongMethodSig := append(pmPacked[0:4], packed[4:]...)
_, err = abiDecodeAcceptAccount(wrongMethodSig)

assert.EqualError(t, err, "unable to decode acceptAccount: got wrong method acceptPaymaster")
}
Loading
Loading