Skip to content

Commit f644a4b

Browse files
authored
test: add eip7702 e2e test (#685)
* test(eip7702): add eip7702 e2e test * test(eip7702): add e2e test cases * chore: add comments * test(eip7702): add e2e test case * chore(tests): fix eth client method * chore(tests): fix util function * chore(tests): fix util function * test(eip7702): fix tests * update CHANGELOG.md * test(eip7702): fix test
1 parent db8a08c commit f644a4b

File tree

13 files changed

+882
-35
lines changed

13 files changed

+882
-35
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
- [\#659](https://github.com/cosmos/evm/pull/659) Move configs out of EVMD and deduplicate configs
4646
- [\#664](https://github.com/cosmos/evm/pull/664) Add EIP-7702 integration test
4747
- [\#684](https://github.com/cosmos/evm/pull/684) Add unit test cases for EIP-7702
48+
- [\#685](https://github.com/cosmos/evm/pull/685) Add EIP-7702 e2e test
4849

4950
### FEATURES
5051

tests/jsonrpc/simulator/simulator

15.1 MB
Binary file not shown.

tests/systemtests/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ go test -p 1 -parallel 1 -mod=readonly -tags='system_test' -v ./... \
4444
--run TestExceptions --verbose --binary evmd --block-time 5s --chain-id local-4221
4545
```
4646

47-
## Run Entire test
47+
### Run EIP-7702 test
48+
49+
```shell
50+
go test -p 1 -mod=readonly -tags='system_test' -v ./... \
51+
--run TestEIP7702 --verbose --binary evmd --block-time 3s --chain-id local-4221
52+
```
53+
54+
## Run all tests
4855

4956
```shell
5057
make test
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package accountabstraction
2+
3+
import (
4+
"crypto/ecdsa"
5+
"math/big"
6+
"testing"
7+
8+
"github.com/ethereum/go-ethereum/common"
9+
ethtypes "github.com/ethereum/go-ethereum/core/types"
10+
)
11+
12+
type AccountAbstractionTestSuite interface {
13+
// Lifecycle
14+
SetupTest(t *testing.T)
15+
WaitForCommit(txHash common.Hash)
16+
17+
// Query helpers
18+
GetChainID() uint64
19+
GetNonce(accID string) uint64
20+
GetPrivKey(accID string) *ecdsa.PrivateKey
21+
GetAddr(accID string) common.Address
22+
GetCounterAddr() common.Address
23+
24+
// Transactions
25+
SendSetCodeTx(accID string, signedAuth ...ethtypes.SetCodeAuthorization) (common.Hash, error)
26+
InvokeCounter(accID string, method string, args ...interface{}) (common.Hash, error)
27+
28+
// Verification
29+
CheckSetCode(authorityAccID string, delegate common.Address, expectDelegation bool)
30+
QueryCounterNumber(accID string) (*big.Int, error)
31+
}
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
package accountabstraction
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
"github.com/ethereum/go-ethereum/common"
8+
9+
//nolint:revive // dot imports are fine for Ginkgo
10+
. "github.com/onsi/ginkgo/v2"
11+
//nolint:revive // dot imports are fine for Ginkgo
12+
. "github.com/onsi/gomega"
13+
)
14+
15+
func TestEIP7702(t *testing.T) {
16+
const (
17+
user0 = "acc0"
18+
user1 = "acc1"
19+
)
20+
21+
Describe("test EIP-7702 scenorios", Ordered, func() {
22+
var (
23+
s AccountAbstractionTestSuite
24+
)
25+
26+
// We intentionally use BeforeAll instead of BeforeAll because,
27+
// The test takes too much time if we restart network for each test case.
28+
BeforeAll(func() {
29+
s = NewTestSuite(t)
30+
s.SetupTest(t)
31+
})
32+
33+
AfterEach(func() {
34+
// Reset code of EoAs to 0x0 address
35+
//
36+
// We set user0's authorization nonce to currentNonce + 1
37+
// because user0 will also send the SetCode transaction.
38+
// Since the sender’s nonce is incremented before applying authorization,
39+
// the SetCodeAuthorization must use currentNonce + 1.
40+
user0Nonce := s.GetNonce(user0) + 1
41+
cleanupAuth0 := createSetCodeAuthorization(s.GetChainID(), user0Nonce, common.Address{})
42+
signedCleanup0, signErr := signSetCodeAuthorization(s.GetPrivKey(user0), cleanupAuth0)
43+
Expect(signErr).To(BeNil())
44+
45+
user1Nonce := s.GetNonce(user1)
46+
cleanupAuth1 := createSetCodeAuthorization(s.GetChainID(), user1Nonce, common.Address{})
47+
signedCleanup1, signErr := signSetCodeAuthorization(s.GetPrivKey(user1), cleanupAuth1)
48+
Expect(signErr).To(BeNil())
49+
50+
txHash, err := s.SendSetCodeTx(user0, signedCleanup0, signedCleanup1)
51+
Expect(err).To(BeNil(), "error while clearing SetCode delegation")
52+
53+
s.WaitForCommit(txHash)
54+
s.CheckSetCode(user0, common.Address{}, false)
55+
s.CheckSetCode(user1, common.Address{}, false)
56+
})
57+
58+
type testCase struct {
59+
authChainID func() uint64
60+
authNonce func() uint64
61+
authAddress func() common.Address
62+
authSigner string
63+
txSender string
64+
expDelegation bool
65+
}
66+
67+
DescribeTable("SetCode authorization scenarios", func(tc testCase) {
68+
authorization := createSetCodeAuthorization(tc.authChainID(), tc.authNonce(), tc.authAddress())
69+
signedAuthorization, err := signSetCodeAuthorization(s.GetPrivKey(tc.authSigner), authorization)
70+
Expect(err).To(BeNil())
71+
72+
txHash, err := s.SendSetCodeTx(tc.txSender, signedAuthorization)
73+
Expect(err).To(BeNil(), "error while sending SetCode tx")
74+
s.WaitForCommit(txHash)
75+
s.CheckSetCode(tc.authSigner, tc.authAddress(), tc.expDelegation)
76+
},
77+
Entry("setCode with invalid chainID should fail", testCase{
78+
authChainID: func() uint64 { return s.GetChainID() + 1 },
79+
authNonce: func() uint64 {
80+
return s.GetNonce(user0) + 1
81+
},
82+
authAddress: func() common.Address {
83+
return s.GetCounterAddr()
84+
},
85+
authSigner: user0,
86+
txSender: user0,
87+
expDelegation: false,
88+
}),
89+
Entry("setCode with empty address should reset delegation", testCase{
90+
authChainID: func() uint64 { return s.GetChainID() + 1 },
91+
authNonce: func() uint64 {
92+
return s.GetNonce(user0) + 1
93+
},
94+
authAddress: func() common.Address {
95+
return common.HexToAddress("0x0")
96+
},
97+
authSigner: user0,
98+
txSender: user0,
99+
expDelegation: false,
100+
}),
101+
Entry("setCode with invalid address should fail", testCase{
102+
authChainID: func() uint64 { return s.GetChainID() + 1 },
103+
authNonce: func() uint64 {
104+
return s.GetNonce(user0) + 1
105+
},
106+
authAddress: func() common.Address {
107+
return common.BytesToAddress([]byte("invalid"))
108+
},
109+
authSigner: user0,
110+
txSender: user0,
111+
expDelegation: false,
112+
}),
113+
Entry("setCode with EoA address should fail", testCase{
114+
authChainID: func() uint64 { return s.GetChainID() + 1 },
115+
authNonce: func() uint64 {
116+
return s.GetNonce(user0) + 1
117+
},
118+
authAddress: func() common.Address {
119+
return s.GetAddr(user1)
120+
},
121+
authSigner: user0,
122+
txSender: user0,
123+
expDelegation: false,
124+
}),
125+
Entry("same signer/sender with matching nonce should fail", testCase{
126+
authChainID: func() uint64 { return s.GetChainID() },
127+
authNonce: func() uint64 {
128+
return s.GetNonce(user0)
129+
},
130+
authAddress: func() common.Address {
131+
return s.GetCounterAddr()
132+
},
133+
authSigner: user0,
134+
txSender: user0,
135+
expDelegation: false,
136+
}),
137+
Entry("same signer/sender with future nonce sholud succeed", testCase{
138+
authChainID: func() uint64 { return s.GetChainID() },
139+
authNonce: func() uint64 {
140+
return s.GetNonce(user0) + 1
141+
},
142+
authAddress: func() common.Address {
143+
return s.GetCounterAddr()
144+
},
145+
authSigner: user0,
146+
txSender: user0,
147+
expDelegation: true,
148+
}),
149+
Entry("different signer/sender with current nonce should succeed", testCase{
150+
authChainID: func() uint64 { return s.GetChainID() },
151+
authNonce: func() uint64 {
152+
return s.GetNonce(user1)
153+
},
154+
authAddress: func() common.Address {
155+
return s.GetCounterAddr()
156+
},
157+
authSigner: user1,
158+
txSender: user0,
159+
expDelegation: true,
160+
}),
161+
Entry("different signer/sender with future nonce should fail", testCase{
162+
authChainID: func() uint64 { return s.GetChainID() },
163+
authNonce: func() uint64 {
164+
return s.GetNonce(user1) + 1
165+
},
166+
authAddress: func() common.Address {
167+
return s.GetCounterAddr()
168+
},
169+
authSigner: user1,
170+
txSender: user0,
171+
expDelegation: false,
172+
}),
173+
)
174+
175+
Describe("executes counter contract methods via delegated account", func() {
176+
Context("when delegation is active", func() {
177+
It("should succeed", func() {
178+
counterAddr := s.GetCounterAddr()
179+
180+
chainID := s.GetChainID()
181+
authorization := createSetCodeAuthorization(chainID, s.GetNonce(user0)+1, counterAddr)
182+
signedAuthorization, err := signSetCodeAuthorization(s.GetPrivKey(user0), authorization)
183+
Expect(err).To(BeNil())
184+
185+
txHash, err := s.SendSetCodeTx(user0, signedAuthorization)
186+
Expect(err).To(BeNil(), "error while sending SetCode tx")
187+
s.WaitForCommit(txHash)
188+
s.CheckSetCode(user0, counterAddr, true)
189+
190+
txHash, err = s.InvokeCounter(user0, "setNumber", big.NewInt(0))
191+
Expect(err).To(BeNil(), "failed to reset counter")
192+
s.WaitForCommit(txHash)
193+
194+
txHash, err = s.InvokeCounter(user0, "increment")
195+
Expect(err).To(BeNil(), "failed to increment counter")
196+
s.WaitForCommit(txHash)
197+
198+
value, err := s.QueryCounterNumber(user0)
199+
Expect(err).To(BeNil(), "failed to query counter value")
200+
Expect(value.Uint64()).To(Equal(uint64(1)))
201+
})
202+
})
203+
204+
Context("after delegation has been revoked", func() {
205+
It("should no longer execute counter methods", func() {
206+
counterAddr := s.GetCounterAddr()
207+
chainID := s.GetChainID()
208+
209+
authorization := createSetCodeAuthorization(chainID, s.GetNonce(user0)+1, counterAddr)
210+
signedAuthorization, err := signSetCodeAuthorization(s.GetPrivKey(user0), authorization)
211+
Expect(err).To(BeNil())
212+
213+
txHash, err := s.SendSetCodeTx(user0, signedAuthorization)
214+
Expect(err).To(BeNil(), "error while sending SetCode tx")
215+
s.WaitForCommit(txHash)
216+
s.CheckSetCode(user0, counterAddr, true)
217+
218+
cleanup := createSetCodeAuthorization(chainID, s.GetNonce(user0)+1, common.Address{})
219+
signedCleanup, err := signSetCodeAuthorization(s.GetPrivKey(user0), cleanup)
220+
Expect(err).To(BeNil())
221+
222+
txHash, err = s.SendSetCodeTx(user0, signedCleanup)
223+
Expect(err).To(BeNil(), "error while clearing SetCode delegation")
224+
s.WaitForCommit(txHash)
225+
s.CheckSetCode(user0, common.Address{}, false)
226+
227+
txHash, err = s.InvokeCounter(user0, "increment")
228+
Expect(err).To(BeNil(), "counter invocation tx should be accepted but do nothing")
229+
s.WaitForCommit(txHash)
230+
231+
value, err := s.QueryCounterNumber(user0)
232+
Expect(err).To(BeNil(), "failed to query counter value after revocation")
233+
Expect(value.Uint64()).To(Equal(uint64(0)), "counter value should remain unchanged without delegation")
234+
})
235+
})
236+
})
237+
})
238+
239+
// Run Ginkgo integration tests
240+
RegisterFailHandler(Fail)
241+
RunSpecs(t, "EIP7702 Integration Test Suite")
242+
}

0 commit comments

Comments
 (0)