Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
- [\#659](https://github.com/cosmos/evm/pull/659) Move configs out of EVMD and deduplicate configs
- [\#664](https://github.com/cosmos/evm/pull/664) Add EIP-7702 integration test
- [\#684](https://github.com/cosmos/evm/pull/684) Add unit test cases for EIP-7702
- [\#685](https://github.com/cosmos/evm/pull/685) Add EIP-7702 e2e test

### FEATURES

Expand Down
Binary file added tests/jsonrpc/simulator/simulator
Binary file not shown.
9 changes: 8 additions & 1 deletion tests/systemtests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ go test -p 1 -parallel 1 -mod=readonly -tags='system_test' -v ./... \
--run TestExceptions --verbose --binary evmd --block-time 5s --chain-id local-4221
```

## Run Entire test
### Run EIP-7702 test

```shell
go test -p 1 -mod=readonly -tags='system_test' -v ./... \
--run TestEIP7702 --verbose --binary evmd --block-time 3s --chain-id local-4221
```

## Run all tests

```shell
make test
Expand Down
31 changes: 31 additions & 0 deletions tests/systemtests/accountabstraction/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package accountabstraction

import (
"crypto/ecdsa"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)

type AccountAbstractionTestSuite interface {
// Lifecycle
SetupTest(t *testing.T)
WaitForCommit(txHash common.Hash)

// Query helpers
GetChainID() uint64
GetNonce(accID string) uint64
GetPrivKey(accID string) *ecdsa.PrivateKey
GetAddr(accID string) common.Address
GetCounterAddr() common.Address

// Transactions
SendSetCodeTx(accID string, signedAuth ...ethtypes.SetCodeAuthorization) (common.Hash, error)
InvokeCounter(accID string, method string, args ...interface{}) (common.Hash, error)

// Verification
CheckSetCode(authorityAccID string, delegate common.Address, expectDelegation bool)
QueryCounterNumber(accID string) (*big.Int, error)
}
242 changes: 242 additions & 0 deletions tests/systemtests/accountabstraction/test_eip7702.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package accountabstraction

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"

//nolint:revive // dot imports are fine for Ginkgo
. "github.com/onsi/ginkgo/v2"
//nolint:revive // dot imports are fine for Ginkgo
. "github.com/onsi/gomega"
)

func TestEIP7702(t *testing.T) {
const (
user0 = "acc0"
user1 = "acc1"
)

Describe("test EIP-7702 scenorios", Ordered, func() {
var (
s AccountAbstractionTestSuite
)

// We intentionally use BeforeAll instead of BeforeAll because,
// The test takes too much time if we restart network for each test case.
BeforeAll(func() {
s = NewTestSuite(t)
s.SetupTest(t)
})

AfterEach(func() {
// Reset code of EoAs to 0x0 address
//
// We set user0's authorization nonce to currentNonce + 1
// because user0 will also send the SetCode transaction.
// Since the sender’s nonce is incremented before applying authorization,
// the SetCodeAuthorization must use currentNonce + 1.
user0Nonce := s.GetNonce(user0) + 1
cleanupAuth0 := createSetCodeAuthorization(s.GetChainID(), user0Nonce, common.Address{})
signedCleanup0, signErr := signSetCodeAuthorization(s.GetPrivKey(user0), cleanupAuth0)
Expect(signErr).To(BeNil())

user1Nonce := s.GetNonce(user1)
cleanupAuth1 := createSetCodeAuthorization(s.GetChainID(), user1Nonce, common.Address{})
signedCleanup1, signErr := signSetCodeAuthorization(s.GetPrivKey(user1), cleanupAuth1)
Expect(signErr).To(BeNil())

txHash, err := s.SendSetCodeTx(user0, signedCleanup0, signedCleanup1)
Expect(err).To(BeNil(), "error while clearing SetCode delegation")

s.WaitForCommit(txHash)
s.CheckSetCode(user0, common.Address{}, false)
s.CheckSetCode(user1, common.Address{}, false)
})

type testCase struct {
authChainID func() uint64
authNonce func() uint64
authAddress func() common.Address
authSigner string
txSender string
expDelegation bool
}

DescribeTable("SetCode authorization scenarios", func(tc testCase) {
authorization := createSetCodeAuthorization(tc.authChainID(), tc.authNonce(), tc.authAddress())
signedAuthorization, err := signSetCodeAuthorization(s.GetPrivKey(tc.authSigner), authorization)
Expect(err).To(BeNil())

txHash, err := s.SendSetCodeTx(tc.txSender, signedAuthorization)
Expect(err).To(BeNil(), "error while sending SetCode tx")
s.WaitForCommit(txHash)
s.CheckSetCode(tc.authSigner, tc.authAddress(), tc.expDelegation)
},
Entry("setCode with invalid chainID should fail", testCase{
authChainID: func() uint64 { return s.GetChainID() + 1 },
authNonce: func() uint64 {
return s.GetNonce(user0) + 1
},
authAddress: func() common.Address {
return s.GetCounterAddr()
},
authSigner: user0,
txSender: user0,
expDelegation: false,
}),
Entry("setCode with empty address should reset delegation", testCase{
authChainID: func() uint64 { return s.GetChainID() + 1 },
authNonce: func() uint64 {
return s.GetNonce(user0) + 1
},
authAddress: func() common.Address {
return common.HexToAddress("0x0")
},
authSigner: user0,
txSender: user0,
expDelegation: false,
}),
Entry("setCode with invalid address should fail", testCase{
authChainID: func() uint64 { return s.GetChainID() + 1 },
authNonce: func() uint64 {
return s.GetNonce(user0) + 1
},
authAddress: func() common.Address {
return common.BytesToAddress([]byte("invalid"))
},
authSigner: user0,
txSender: user0,
expDelegation: false,
}),
Entry("setCode with EoA address should fail", testCase{
authChainID: func() uint64 { return s.GetChainID() + 1 },
authNonce: func() uint64 {
return s.GetNonce(user0) + 1
},
authAddress: func() common.Address {
return s.GetAddr(user1)
},
authSigner: user0,
txSender: user0,
expDelegation: false,
}),
Entry("same signer/sender with matching nonce should fail", testCase{
authChainID: func() uint64 { return s.GetChainID() },
authNonce: func() uint64 {
return s.GetNonce(user0)
},
authAddress: func() common.Address {
return s.GetCounterAddr()
},
authSigner: user0,
txSender: user0,
expDelegation: false,
}),
Entry("same signer/sender with future nonce sholud succeed", testCase{
authChainID: func() uint64 { return s.GetChainID() },
authNonce: func() uint64 {
return s.GetNonce(user0) + 1
},
authAddress: func() common.Address {
return s.GetCounterAddr()
},
authSigner: user0,
txSender: user0,
expDelegation: true,
}),
Entry("different signer/sender with current nonce should succeed", testCase{
authChainID: func() uint64 { return s.GetChainID() },
authNonce: func() uint64 {
return s.GetNonce(user1)
},
authAddress: func() common.Address {
return s.GetCounterAddr()
},
authSigner: user1,
txSender: user0,
expDelegation: true,
}),
Entry("different signer/sender with future nonce should fail", testCase{
authChainID: func() uint64 { return s.GetChainID() },
authNonce: func() uint64 {
return s.GetNonce(user1) + 1
},
authAddress: func() common.Address {
return s.GetCounterAddr()
},
authSigner: user1,
txSender: user0,
expDelegation: false,
}),
)

Describe("executes counter contract methods via delegated account", func() {
Context("when delegation is active", func() {
It("should succeed", func() {
counterAddr := s.GetCounterAddr()

chainID := s.GetChainID()
authorization := createSetCodeAuthorization(chainID, s.GetNonce(user0)+1, counterAddr)
signedAuthorization, err := signSetCodeAuthorization(s.GetPrivKey(user0), authorization)
Expect(err).To(BeNil())

txHash, err := s.SendSetCodeTx(user0, signedAuthorization)
Expect(err).To(BeNil(), "error while sending SetCode tx")
s.WaitForCommit(txHash)
s.CheckSetCode(user0, counterAddr, true)

txHash, err = s.InvokeCounter(user0, "setNumber", big.NewInt(0))
Expect(err).To(BeNil(), "failed to reset counter")
s.WaitForCommit(txHash)

txHash, err = s.InvokeCounter(user0, "increment")
Expect(err).To(BeNil(), "failed to increment counter")
s.WaitForCommit(txHash)

value, err := s.QueryCounterNumber(user0)
Expect(err).To(BeNil(), "failed to query counter value")
Expect(value.Uint64()).To(Equal(uint64(1)))
})
})

Context("after delegation has been revoked", func() {
It("should no longer execute counter methods", func() {
counterAddr := s.GetCounterAddr()
chainID := s.GetChainID()

authorization := createSetCodeAuthorization(chainID, s.GetNonce(user0)+1, counterAddr)
signedAuthorization, err := signSetCodeAuthorization(s.GetPrivKey(user0), authorization)
Expect(err).To(BeNil())

txHash, err := s.SendSetCodeTx(user0, signedAuthorization)
Expect(err).To(BeNil(), "error while sending SetCode tx")
s.WaitForCommit(txHash)
s.CheckSetCode(user0, counterAddr, true)

cleanup := createSetCodeAuthorization(chainID, s.GetNonce(user0)+1, common.Address{})
signedCleanup, err := signSetCodeAuthorization(s.GetPrivKey(user0), cleanup)
Expect(err).To(BeNil())

txHash, err = s.SendSetCodeTx(user0, signedCleanup)
Expect(err).To(BeNil(), "error while clearing SetCode delegation")
s.WaitForCommit(txHash)
s.CheckSetCode(user0, common.Address{}, false)

txHash, err = s.InvokeCounter(user0, "increment")
Expect(err).To(BeNil(), "counter invocation tx should be accepted but do nothing")
s.WaitForCommit(txHash)

value, err := s.QueryCounterNumber(user0)
Expect(err).To(BeNil(), "failed to query counter value after revocation")
Expect(value.Uint64()).To(Equal(uint64(0)), "counter value should remain unchanged without delegation")
})
})
})
})

// Run Ginkgo integration tests
RegisterFailHandler(Fail)
RunSpecs(t, "EIP7702 Integration Test Suite")
}
Loading
Loading