From ca86ce62a7e9db4d24ffa912b9201c34bee16874 Mon Sep 17 00:00:00 2001 From: Dror Tirosh Date: Sun, 30 Jun 2024 14:38:27 +0300 Subject: [PATCH 1/5] update tests, match return values to RIP --- core/state_processor_rip7560.go | 66 +++++++++++-------- tests/rip7560/process_test.go | 4 +- tests/rip7560/rip7560TestUtils.go | 13 +--- tests/rip7560/validation_test.go | 105 ++++++++++++++++++++++++------ 4 files changed, 130 insertions(+), 58 deletions(-) diff --git a/core/state_processor_rip7560.go b/core/state_processor_rip7560.go index d05efdc9a5..23bced3307 100644 --- a/core/state_processor_rip7560.go +++ b/core/state_processor_rip7560.go @@ -1,7 +1,6 @@ package core import ( - "encoding/binary" "errors" "fmt" "github.com/ethereum/go-ethereum/accounts/abi" @@ -15,6 +14,36 @@ import ( "strings" ) +const MAGIC_VALUE_SENDER = uint64(0xbf45c166) +const MAGIC_VALUE_PAYMASTER = uint64(0xe0e6183a) +const MAGIC_VALUE_SIGFAIL = uint64(0x31665494) +const PAYMASTER_MAX_CONTEXT_SIZE = 65536 + +func PackValidationData(authorizerMagic uint64, validUntil, validAfter uint64) []byte { + + t := new(big.Int).SetUint64(uint64(validAfter)) + t = t.Lsh(t, 48).Add(t, new(big.Int).SetUint64(validUntil&0xffffff)) + t = t.Lsh(t, 160).Add(t, new(big.Int).SetUint64(uint64(authorizerMagic))) + return common.LeftPadBytes(t.Bytes(), 32) +} + +func UnpackValidationData(validationData []byte) (authorizerMagic uint64, validUntil, validAfter uint64) { + + t := new(big.Int).SetBytes(validationData) + authorizerMagic = t.Uint64() + validUntil = t.Rsh(t, 160).Uint64() & 0xffffff + validAfter = t.Rsh(t, 48).Uint64() + return +} + +func UnpackPaymasterValidationReturn(paymasterValidationReturn []byte) (validationData, context []byte) { + validationData = paymasterValidationReturn[0:32] + //2nd bytes32 is ignored (its an offset value) + contextLen := new(big.Int).SetBytes(paymasterValidationReturn[64:96]) + context = paymasterValidationReturn[96 : 96+contextLen.Uint64()] + return +} + type ValidationPhaseResult struct { TxIndex int Tx *types.Transaction @@ -399,45 +428,30 @@ func preparePostOpMessage(vpr *ValidationPhaseResult, chainConfig *params.ChainC } func validateAccountReturnData(data []byte) (uint64, uint64, error) { - MAGIC_VALUE_SENDER := uint32(0xbf45c166) if len(data) != 32 { return 0, 0, errors.New("invalid account return data length") } - magicExpected := binary.BigEndian.Uint32(data[:4]) + magicExpected, validUntil, validAfter := UnpackValidationData(data) + //todo: we check first 8 bytes of the 20-byte address (the rest is expected to be zeros) if magicExpected != MAGIC_VALUE_SENDER { + if magicExpected == MAGIC_VALUE_SIGFAIL { + return 0, 0, errors.New("account signature error") + } return 0, 0, errors.New("account did not return correct MAGIC_VALUE") } - validAfter := binary.BigEndian.Uint64(data[4:12]) - validUntil := binary.BigEndian.Uint64(data[12:20]) return validAfter, validUntil, nil } -func validatePaymasterReturnData(data []byte) ([]byte, uint64, uint64, error) { - MAGIC_VALUE_PAYMASTER := uint32(0xe0e6183a) - if len(data) < 4 { +func validatePaymasterReturnData(data []byte) (context []byte, validAfter, validUntil uint64, error error) { + if len(data) < 32 { return nil, 0, 0, errors.New("invalid paymaster return data length") } - magicExpected := binary.BigEndian.Uint32(data[:4]) + validationData, context := UnpackPaymasterValidationReturn(data) + magicExpected, validAfter, validUntil := UnpackValidationData(validationData) if magicExpected != MAGIC_VALUE_PAYMASTER { return nil, 0, 0, errors.New("paymaster did not return correct MAGIC_VALUE") } - - jsondata := `[ - {"type":"function","name":"validatePaymasterTransaction","outputs": [{"name": "context","type": "bytes"},{"name": "validUntil","type": "uint256"},{"name": "validAfter","type": "uint256"}]} - ]` - validatePaymasterTransactionAbi, err := abi.JSON(strings.NewReader(jsondata)) - if err != nil { - // todo: wrap error message - return nil, 0, 0, err - } - decodedPmReturnData, err := validatePaymasterTransactionAbi.Unpack("validatePaymasterTransaction", data[4:]) - if err != nil { - return nil, 0, 0, err - } - context := decodedPmReturnData[0].([]byte) - validAfter := decodedPmReturnData[1].(*big.Int) - validUntil := decodedPmReturnData[2].(*big.Int) - return context, validAfter.Uint64(), validUntil.Uint64(), nil + return context, validAfter, validUntil, nil } func validateValidityTimeRange(time uint64, validAfter uint64, validUntil uint64) error { diff --git a/tests/rip7560/process_test.go b/tests/rip7560/process_test.go index 2e83d05861..c3973aaa7b 100644 --- a/tests/rip7560/process_test.go +++ b/tests/rip7560/process_test.go @@ -48,9 +48,7 @@ func TestProcess1(t *testing.T) { Data: []byte{1, 2, 3}, }, }) - if err != nil { - panic(err) - } + assert.NoError(t, err) } // run a set of AA transactions, with a legacy TXs before and after. diff --git a/tests/rip7560/rip7560TestUtils.go b/tests/rip7560/rip7560TestUtils.go index 258d8ed609..710fc51897 100644 --- a/tests/rip7560/rip7560TestUtils.go +++ b/tests/rip7560/rip7560TestUtils.go @@ -15,6 +15,7 @@ import ( ) const DEFAULT_SENDER = "0x1111111111222222222233333333334444444444" +const DEFAULT_BALANCE = 1 << 62 type testContext struct { genesisAlloc types.GenesisAlloc @@ -83,26 +84,18 @@ func (tt *testContextBuilder) withCode(addr string, code []byte, balance int64) // generate the code to return the given byte array (up to 32 bytes) func returnData(data []byte) []byte { - //couldn't get geth to support PUSH0 ... datalen := len(data) - if datalen == 0 { - data = []byte{0} - } if datalen > 32 { panic(fmt.Errorf("data length is too big %v", data)) } PUSHn := byte(int(vm.PUSH0) + datalen) - ret := createCode(PUSHn, data, vm.PUSH1, 0, vm.MSTORE, vm.PUSH1, 32, vm.PUSH1, 0, vm.RETURN) + ret := createCode(PUSHn, data, vm.PUSH0, vm.MSTORE, vm.PUSH1, datalen, vm.PUSH1, 0, vm.RETURN) return ret } -// create bytecode for account func createAccountCode() []byte { - magic := big.NewInt(0xbf45c166) - magic.Lsh(magic, 256-32) - - return returnData(magic.Bytes()) + return returnData(core.PackValidationData(core.MAGIC_VALUE_SENDER, 0, 0)) } // create EVM code from OpCode, byte and []bytes diff --git a/tests/rip7560/validation_test.go b/tests/rip7560/validation_test.go index 4a0b47f6b8..4ccc399676 100644 --- a/tests/rip7560/validation_test.go +++ b/tests/rip7560/validation_test.go @@ -13,51 +13,111 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) -func TestValidation_OOG(t *testing.T) { +func TestPackValidationData(t *testing.T) { + //assert.Equal(t, make([]byte, 32), packValidationData(0, 0, 0)) + //assert.Equal(t, new(big.Int).SetInt64(0x1234).Text(16), new(big.Int).SetBytes(packValidationData(0x1234, 0, 0)).Text(16)) + // ------------------------------------ bbbbbbbbbbbb-aaaaaaaaaaa-mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + packed, _ := new(big.Int).SetString("0000000000020000000000010000000000000000000000000000000000001234", 16) + assert.Equal(t, packed.Text(16), new(big.Int).SetBytes(core.PackValidationData(0x1234, 1, 2)).Text(16)) +} + +func TestUnpackValidationData(t *testing.T) { + packed := core.PackValidationData(0xdead, 0xcafe, 0xface) + magic, until, after := core.UnpackValidationData(packed) + assert.Equal(t, []uint64{0xdead, 0xcafe, 0xface}, []uint64{magic, until, after}) +} + +func TestValidationFailure_OOG(t *testing.T) { magic := big.NewInt(0xbf45c166) magic.Lsh(magic, 256-32) - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(magic.Bytes()), 0), types.Rip7560AccountAbstractionTx{ + validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(magic.Bytes()), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1), GasFeeCap: big.NewInt(1000000000), }, "out of gas") } -func TestValidation_ok(t *testing.T) { +func TestValidationFailure_no_balance(t *testing.T) { + magic := big.NewInt(0xbf45c166) + magic.Lsh(magic, 256-32) - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 0), types.Rip7560AccountAbstractionTx{ - ValidationGas: uint64(1000000000), + validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(magic.Bytes()), 1), types.Rip7560AccountAbstractionTx{ + ValidationGas: uint64(1), GasFeeCap: big.NewInt(1000000000), - }, "") + }, "insufficient funds for gas * price + value: address 0x1111111111222222222233333333334444444444 have 1 want 1000000000") } -func TestValidation_account_revert(t *testing.T) { +func TestValidationFailure_sigerror(t *testing.T) { + validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(core.PackValidationData(core.MAGIC_VALUE_SIGFAIL, 0, 0)), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + }, "account signature error") +} + +func TestValidation_ok(t *testing.T) { + + validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + }, "ok") +} + +func TestValidation_ok_paid(t *testing.T) { + + aatx := types.Rip7560AccountAbstractionTx{ + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + } + tb := newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), DEFAULT_BALANCE) + validatePhase(tb, aatx, "ok") + + maxCost := new(big.Int).SetUint64(aatx.ValidationGas + aatx.PaymasterGas + aatx.Gas) + maxCost.Mul(maxCost, aatx.GasFeeCap) +} + +func TestValidationFailure_account_revert(t *testing.T) { validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, - createCode(vm.PUSH1, 0, vm.DUP1, vm.REVERT), 0), types.Rip7560AccountAbstractionTx{ + createCode(vm.PUSH0, vm.DUP1, vm.REVERT), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), }, "execution reverted") } -func TestValidation_account_no_return_value(t *testing.T) { - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, []byte{ - byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.RETURN), - }, 0), types.Rip7560AccountAbstractionTx{ +func TestValidationFailure_account_out_of_range(t *testing.T) { + validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, + createCode(vm.PUSH0, vm.DUP1, vm.REVERT), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + }, "execution reverted") +} + +func TestValidationFailure_account_wrong_return_length(t *testing.T) { + validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, + returnData([]byte{1, 2, 3}), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), }, "invalid account return data length") } -func TestValidation_account_wrong_return_value(t *testing.T) { +func TestValidationFailure_account_no_return_value(t *testing.T) { validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, - returnData(createCode(1)), - 0), types.Rip7560AccountAbstractionTx{ + returnData([]byte{}), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + }, "invalid account return data length") +} + +func TestValidationFailure_account_wrong_return_value(t *testing.T) { + // create buffer of 32 byte array + validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, + returnData(make([]byte, 32)), + DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), }, "account did not return correct MAGIC_VALUE") } -func validatePhase(tb *testContextBuilder, aatx types.Rip7560AccountAbstractionTx, expectedErr string) { +func validatePhase(tb *testContextBuilder, aatx types.Rip7560AccountAbstractionTx, expectedErr string) *core.ValidationPhaseResult { t := tb.build() if aatx.Sender == nil { //pre-deployed sender account @@ -69,13 +129,20 @@ func validatePhase(tb *testContextBuilder, aatx types.Rip7560AccountAbstractionT var state = tests.MakePreState(rawdb.NewMemoryDatabase(), t.genesisAlloc, false, rawdb.HashScheme) defer state.Close() - _, err := core.ApplyRip7560ValidationPhases(t.genesis.Config, t.chainContext, &common.Address{}, t.gaspool, state.StateDB, t.genesisBlock.Header(), tx, vm.Config{}) - // err string or empty if nil - errStr := "" + state.StateDB.SetTxContext(tx.Hash(), 0) + err := core.BuyGasRip7560Transaction(&aatx, state.StateDB) + + var res *core.ValidationPhaseResult + if err == nil { + res, err = core.ApplyRip7560ValidationPhases(t.genesis.Config, t.chainContext, &common.Address{}, t.gaspool, state.StateDB, t.genesisBlock.Header(), tx, vm.Config{}) + // err string or empty if nil + } + errStr := "ok" if err != nil { errStr = err.Error() } assert.Equal(t.t, expectedErr, errStr) + return res } //test failure on non-rip7560 From 71e6d5ad1d25142b60a16e91eb1631d310d8952b Mon Sep 17 00:00:00 2001 From: Dror Tirosh Date: Sun, 30 Jun 2024 15:41:10 +0300 Subject: [PATCH 2/5] refactor: test HandleRip7560Transactions (instead of individual validation methods) --- tests/rip7560/validation_test.go | 34 +++++++++++--------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/tests/rip7560/validation_test.go b/tests/rip7560/validation_test.go index 4ccc399676..cddb45a7fc 100644 --- a/tests/rip7560/validation_test.go +++ b/tests/rip7560/validation_test.go @@ -28,27 +28,23 @@ func TestUnpackValidationData(t *testing.T) { } func TestValidationFailure_OOG(t *testing.T) { - magic := big.NewInt(0xbf45c166) - magic.Lsh(magic, 256-32) - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(magic.Bytes()), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1), GasFeeCap: big.NewInt(1000000000), }, "out of gas") } func TestValidationFailure_no_balance(t *testing.T) { - magic := big.NewInt(0xbf45c166) - magic.Lsh(magic, 256-32) - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(magic.Bytes()), 1), types.Rip7560AccountAbstractionTx{ + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), 1), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1), GasFeeCap: big.NewInt(1000000000), }, "insufficient funds for gas * price + value: address 0x1111111111222222222233333333334444444444 have 1 want 1000000000") } func TestValidationFailure_sigerror(t *testing.T) { - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(core.PackValidationData(core.MAGIC_VALUE_SIGFAIL, 0, 0)), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(core.PackValidationData(core.MAGIC_VALUE_SIGFAIL, 0, 0)), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), }, "account signature error") @@ -56,7 +52,7 @@ func TestValidationFailure_sigerror(t *testing.T) { func TestValidation_ok(t *testing.T) { - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), }, "ok") @@ -69,14 +65,14 @@ func TestValidation_ok_paid(t *testing.T) { GasFeeCap: big.NewInt(1000000000), } tb := newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), DEFAULT_BALANCE) - validatePhase(tb, aatx, "ok") + handleTransaction(tb, aatx, "ok") maxCost := new(big.Int).SetUint64(aatx.ValidationGas + aatx.PaymasterGas + aatx.Gas) maxCost.Mul(maxCost, aatx.GasFeeCap) } func TestValidationFailure_account_revert(t *testing.T) { - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createCode(vm.PUSH0, vm.DUP1, vm.REVERT), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), @@ -84,7 +80,7 @@ func TestValidationFailure_account_revert(t *testing.T) { } func TestValidationFailure_account_out_of_range(t *testing.T) { - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createCode(vm.PUSH0, vm.DUP1, vm.REVERT), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), @@ -92,7 +88,7 @@ func TestValidationFailure_account_out_of_range(t *testing.T) { } func TestValidationFailure_account_wrong_return_length(t *testing.T) { - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData([]byte{1, 2, 3}), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), @@ -100,7 +96,7 @@ func TestValidationFailure_account_wrong_return_length(t *testing.T) { } func TestValidationFailure_account_no_return_value(t *testing.T) { - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData([]byte{}), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), GasFeeCap: big.NewInt(1000000000), @@ -109,7 +105,7 @@ func TestValidationFailure_account_no_return_value(t *testing.T) { func TestValidationFailure_account_wrong_return_value(t *testing.T) { // create buffer of 32 byte array - validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(make([]byte, 32)), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ ValidationGas: uint64(1000000000), @@ -117,7 +113,7 @@ func TestValidationFailure_account_wrong_return_value(t *testing.T) { }, "account did not return correct MAGIC_VALUE") } -func validatePhase(tb *testContextBuilder, aatx types.Rip7560AccountAbstractionTx, expectedErr string) *core.ValidationPhaseResult { +func handleTransaction(tb *testContextBuilder, aatx types.Rip7560AccountAbstractionTx, expectedErr string) { t := tb.build() if aatx.Sender == nil { //pre-deployed sender account @@ -130,19 +126,13 @@ func validatePhase(tb *testContextBuilder, aatx types.Rip7560AccountAbstractionT defer state.Close() state.StateDB.SetTxContext(tx.Hash(), 0) - err := core.BuyGasRip7560Transaction(&aatx, state.StateDB) + _, _, _, err := core.HandleRip7560Transactions([]*types.Transaction{tx}, 0, state.StateDB, &common.Address{}, t.genesisBlock.Header(), t.gaspool, t.genesis.Config, t.chainContext, vm.Config{}) - var res *core.ValidationPhaseResult - if err == nil { - res, err = core.ApplyRip7560ValidationPhases(t.genesis.Config, t.chainContext, &common.Address{}, t.gaspool, state.StateDB, t.genesisBlock.Header(), tx, vm.Config{}) - // err string or empty if nil - } errStr := "ok" if err != nil { errStr = err.Error() } assert.Equal(t.t, expectedErr, errStr) - return res } //test failure on non-rip7560 From 702d157aef8a766def35b1f29fee3165849ed5d8 Mon Sep 17 00:00:00 2001 From: Dror Tirosh Date: Mon, 1 Jul 2024 12:56:48 +0300 Subject: [PATCH 3/5] test nonce --- core/state_processor_rip7560.go | 28 ++++++++++++++++++++++++++-- core/types/tx_rip7560.go | 7 ++++--- tests/rip7560/validation_test.go | 13 +++++++++++-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/core/state_processor_rip7560.go b/core/state_processor_rip7560.go index 23bced3307..27eb35a2ef 100644 --- a/core/state_processor_rip7560.go +++ b/core/state_processor_rip7560.go @@ -88,11 +88,16 @@ func handleRip7560Transactions(transactions []*types.Transaction, index int, sta aatx := tx.Rip7560TransactionData() statedb.SetTxContext(tx.Hash(), index+i) - err := BuyGasRip7560Transaction(aatx, statedb) - var vpr *ValidationPhaseResult + err := CheckNonceRip7560(aatx, statedb) if err != nil { return nil, nil, nil, err } + err = BuyGasRip7560Transaction(aatx, statedb) + if err != nil { + return nil, nil, nil, err + } + + var vpr *ValidationPhaseResult vpr, err = ApplyRip7560ValidationPhases(chainConfig, bc, coinbase, gp, statedb, header, tx, cfg) if err != nil { return nil, nil, nil, err @@ -142,6 +147,24 @@ func BuyGasRip7560Transaction(st *types.Rip7560AccountAbstractionTx, state vm.St return nil } +// precheck nonce of transaction. +// (standard preCheck function check both nonce and no-code of account) +func CheckNonceRip7560(tx *types.Rip7560AccountAbstractionTx, st *state.StateDB) error { + // Make sure this transaction's nonce is correct. + stNonce := st.GetNonce(*tx.Sender) + if msgNonce := tx.Nonce; stNonce < msgNonce { + return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh, + tx.Sender.Hex(), msgNonce, stNonce) + } else if stNonce > msgNonce { + return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow, + tx.Sender.Hex(), msgNonce, stNonce) + } else if stNonce+1 < stNonce { + return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax, + tx.Sender.Hex(), stNonce) + } + return nil +} + func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config) (*ValidationPhaseResult, error) { blockContext := NewEVMBlockContext(header, bc, author) txContext := vm.TxContext{ @@ -333,6 +356,7 @@ func prepareAccountValidationMessage(baseTx *types.Transaction, chainConfig *par return &Message{ From: chainConfig.EntryPointAddress, To: tx.Sender, + Nonce: tx.Nonce, Value: big.NewInt(0), GasLimit: tx.ValidationGas - deploymentUsedGas, GasPrice: tx.GasFeeCap, diff --git a/core/types/tx_rip7560.go b/core/types/tx_rip7560.go index 96855be3a4..76443ac078 100644 --- a/core/types/tx_rip7560.go +++ b/core/types/tx_rip7560.go @@ -55,9 +55,10 @@ type Rip7560AccountAbstractionTx struct { // copy creates a deep copy of the transaction data and initializes all fields. func (tx *Rip7560AccountAbstractionTx) copy() TxData { cpy := &Rip7560AccountAbstractionTx{ - To: copyAddressPtr(tx.To), - Data: common.CopyBytes(tx.Data), - Gas: tx.Gas, + To: copyAddressPtr(tx.To), + Data: common.CopyBytes(tx.Data), + Nonce: tx.Nonce, + Gas: tx.Gas, // These are copied below. AccessList: make(AccessList, len(tx.AccessList)), Value: new(big.Int), diff --git a/tests/rip7560/validation_test.go b/tests/rip7560/validation_test.go index cddb45a7fc..cd44610b1c 100644 --- a/tests/rip7560/validation_test.go +++ b/tests/rip7560/validation_test.go @@ -16,8 +16,9 @@ import ( func TestPackValidationData(t *testing.T) { //assert.Equal(t, make([]byte, 32), packValidationData(0, 0, 0)) //assert.Equal(t, new(big.Int).SetInt64(0x1234).Text(16), new(big.Int).SetBytes(packValidationData(0x1234, 0, 0)).Text(16)) - // ------------------------------------ bbbbbbbbbbbb-aaaaaaaaaaa-mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm - packed, _ := new(big.Int).SetString("0000000000020000000000010000000000000000000000000000000000001234", 16) + // --------------- after 6bytes before 6 bytes magic 20 bytes + validationData := "000000000002" + "000000000001" + "0000000000000000000000000000000000001234" + packed, _ := new(big.Int).SetString(validationData, 16) assert.Equal(t, packed.Text(16), new(big.Int).SetBytes(core.PackValidationData(0x1234, 1, 2)).Text(16)) } @@ -71,6 +72,14 @@ func TestValidation_ok_paid(t *testing.T) { maxCost.Mul(maxCost, aatx.GasFeeCap) } +func TestValidationFailure_account_nonce(t *testing.T) { + handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ + Nonce: 1234, + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + }, "nonce too high: address 0x1111111111222222222233333333334444444444, tx: 1234 state: 0") +} + func TestValidationFailure_account_revert(t *testing.T) { handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createCode(vm.PUSH0, vm.DUP1, vm.REVERT), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ From 49bc2a85701ec4c4beab2abae3129e00c12c0f16 Mon Sep 17 00:00:00 2001 From: Dror Tirosh Date: Mon, 1 Jul 2024 14:23:48 +0300 Subject: [PATCH 4/5] added time-range tests --- tests/rip7560/rip7560TestUtils.go | 1 + tests/rip7560/validation_test.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/tests/rip7560/rip7560TestUtils.go b/tests/rip7560/rip7560TestUtils.go index 710fc51897..1e50d9b7fb 100644 --- a/tests/rip7560/rip7560TestUtils.go +++ b/tests/rip7560/rip7560TestUtils.go @@ -46,6 +46,7 @@ func newTestContextBuilder(t *testing.T) *testContextBuilder { func (tb *testContextBuilder) build() *testContext { genesis := core.DeveloperGenesisBlock(10_000_000, &common.Address{}) + genesis.Timestamp = 100 genesisBlock := genesis.ToBlock() gaspool := new(core.GasPool).AddGas(genesisBlock.GasLimit()) diff --git a/tests/rip7560/validation_test.go b/tests/rip7560/validation_test.go index cd44610b1c..7ab87ddee5 100644 --- a/tests/rip7560/validation_test.go +++ b/tests/rip7560/validation_test.go @@ -51,6 +51,24 @@ func TestValidationFailure_sigerror(t *testing.T) { }, "account signature error") } +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{ + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + }, "RIP-7560 transaction validity not reached yet") +} + +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{ + ValidationGas: uint64(1000000000), + GasFeeCap: big.NewInt(1000000000), + }, "RIP-7560 transaction validity expired") +} + func TestValidation_ok(t *testing.T) { handleTransaction(newTestContextBuilder(t).withCode(DEFAULT_SENDER, createAccountCode(), DEFAULT_BALANCE), types.Rip7560AccountAbstractionTx{ From d5c3069d710129ede8f743eddc65333193fe1581 Mon Sep 17 00:00:00 2001 From: Dror Tirosh Date: Sat, 6 Jul 2024 15:12:44 +0300 Subject: [PATCH 5/5] pr issues --- core/state_processor_rip7560.go | 1 - tests/rip7560/validation_test.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/core/state_processor_rip7560.go b/core/state_processor_rip7560.go index 27eb35a2ef..208e4609de 100644 --- a/core/state_processor_rip7560.go +++ b/core/state_processor_rip7560.go @@ -356,7 +356,6 @@ func prepareAccountValidationMessage(baseTx *types.Transaction, chainConfig *par return &Message{ From: chainConfig.EntryPointAddress, To: tx.Sender, - Nonce: tx.Nonce, Value: big.NewInt(0), GasLimit: tx.ValidationGas - deploymentUsedGas, GasPrice: tx.GasFeeCap, diff --git a/tests/rip7560/validation_test.go b/tests/rip7560/validation_test.go index 7ab87ddee5..cdbd5d3c24 100644 --- a/tests/rip7560/validation_test.go +++ b/tests/rip7560/validation_test.go @@ -14,8 +14,6 @@ import ( ) func TestPackValidationData(t *testing.T) { - //assert.Equal(t, make([]byte, 32), packValidationData(0, 0, 0)) - //assert.Equal(t, new(big.Int).SetInt64(0x1234).Text(16), new(big.Int).SetBytes(packValidationData(0x1234, 0, 0)).Text(16)) // --------------- after 6bytes before 6 bytes magic 20 bytes validationData := "000000000002" + "000000000001" + "0000000000000000000000000000000000001234" packed, _ := new(big.Int).SetString(validationData, 16)