Skip to content

Commit e7b0284

Browse files
committed
one shot ai test
1 parent 914db69 commit e7b0284

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
//go:build evm
2+
3+
package e2e
4+
5+
import (
6+
"bytes"
7+
"context"
8+
"encoding/json"
9+
"fmt"
10+
"net/http"
11+
"os"
12+
"path/filepath"
13+
"testing"
14+
"time"
15+
16+
"github.com/ethereum/go-ethereum/common"
17+
"github.com/ethereum/go-ethereum/ethclient"
18+
"github.com/stretchr/testify/require"
19+
20+
"github.com/evstack/ev-node/execution/evm"
21+
)
22+
23+
// enableForceInclusionInGenesis modifies the genesis file to set the force inclusion epoch
24+
// to a small value suitable for testing.
25+
func enableForceInclusionInGenesis(t *testing.T, homeDir string, epoch uint64) {
26+
t.Helper()
27+
genesisPath := filepath.Join(homeDir, "config", "genesis.json")
28+
data, err := os.ReadFile(genesisPath)
29+
require.NoError(t, err)
30+
31+
var genesis map[string]interface{}
32+
err = json.Unmarshal(data, &genesis)
33+
require.NoError(t, err)
34+
35+
genesis["da_epoch_forced_inclusion"] = epoch
36+
37+
newData, err := json.MarshalIndent(genesis, "", " ")
38+
require.NoError(t, err)
39+
40+
err = os.WriteFile(genesisPath, newData, 0644)
41+
require.NoError(t, err)
42+
}
43+
44+
// submitForceInclusionTx sends a raw transaction to the force inclusion server
45+
func submitForceInclusionTx(t *testing.T, fiUrl string, txBytes []byte) {
46+
t.Helper()
47+
reqBody := map[string]interface{}{
48+
"jsonrpc": "2.0",
49+
"id": 1,
50+
"method": "eth_sendRawTransaction",
51+
"params": []string{"0x" + common.Bytes2Hex(txBytes)},
52+
}
53+
54+
jsonData, err := json.Marshal(reqBody)
55+
require.NoError(t, err)
56+
57+
resp, err := http.Post(fiUrl, "application/json", bytes.NewBuffer(jsonData))
58+
require.NoError(t, err)
59+
defer resp.Body.Close()
60+
61+
require.Equal(t, http.StatusOK, resp.StatusCode)
62+
63+
var res map[string]interface{}
64+
err = json.NewDecoder(resp.Body).Decode(&res)
65+
require.NoError(t, err)
66+
require.Nil(t, res["error"], "RPC returned error: %v", res["error"])
67+
require.NotNil(t, res["result"], "RPC result is nil")
68+
}
69+
70+
// setupSequencerWithForceInclusion sets up a sequencer node with force inclusion enabled
71+
func setupSequencerWithForceInclusion(t *testing.T, sut *SystemUnderTest, nodeHome string, fiPort int) (string, string) {
72+
t.Helper()
73+
74+
// Use common setup (no full node needed initially)
75+
jwtSecret, _, genesisHash, endpoints := setupCommonEVMTest(t, sut, false)
76+
77+
// Create passphrase file
78+
passphraseFile := createPassphraseFile(t, nodeHome)
79+
80+
// Create JWT secret file
81+
jwtSecretFile := createJWTSecretFile(t, nodeHome, jwtSecret)
82+
83+
// Initialize sequencer node
84+
output, err := sut.RunCmd(evmSingleBinaryPath,
85+
"init",
86+
"--evnode.node.aggregator=true",
87+
"--evnode.signer.passphrase_file", passphraseFile,
88+
"--home", nodeHome,
89+
)
90+
require.NoError(t, err, "failed to init sequencer", output)
91+
92+
// Modify genesis to lower the epoch for faster testing (2 DA blocks)
93+
enableForceInclusionInGenesis(t, nodeHome, 2)
94+
95+
// Start sequencer with force inclusion server enabled
96+
fiAddr := fmt.Sprintf("127.0.0.1:%d", fiPort)
97+
args := []string{
98+
"start",
99+
"--evm.jwt-secret-file", jwtSecretFile,
100+
"--evm.genesis-hash", genesisHash,
101+
"--evnode.node.block_time", DefaultBlockTime,
102+
"--evnode.node.aggregator=true",
103+
"--evnode.signer.passphrase_file", passphraseFile,
104+
"--home", nodeHome,
105+
"--evnode.da.block_time", DefaultDABlockTime,
106+
"--evnode.da.address", endpoints.GetDAAddress(),
107+
"--evnode.da.namespace", DefaultDANamespace,
108+
"--evnode.da.forced_inclusion_namespace", "forced-inc",
109+
"--evnode.rpc.address", endpoints.GetRollkitRPCListen(),
110+
"--evnode.p2p.listen_address", endpoints.GetRollkitP2PAddress(),
111+
"--evm.engine-url", endpoints.GetSequencerEngineURL(),
112+
"--evm.eth-url", endpoints.GetSequencerEthURL(),
113+
"--force-inclusion-server", fiAddr,
114+
}
115+
sut.ExecCmd(evmSingleBinaryPath, args...)
116+
sut.AwaitNodeUp(t, endpoints.GetRollkitRPCAddress(), NodeStartupTimeout)
117+
118+
return genesisHash, endpoints.GetSequencerEthURL()
119+
}
120+
121+
func TestEvmSequencerForceInclusionE2E(t *testing.T) {
122+
sut := NewSystemUnderTest(t)
123+
workDir := t.TempDir()
124+
sequencerHome := filepath.Join(workDir, "sequencer")
125+
126+
// Get a port for force inclusion server
127+
fiPort, err := getAvailablePort()
128+
require.NoError(t, err)
129+
fiUrl := fmt.Sprintf("http://127.0.0.1:%d", fiPort)
130+
131+
// Setup sequencer with force inclusion enabled
132+
genesisHash, seqEthURL := setupSequencerWithForceInclusion(t, sut, sequencerHome, fiPort)
133+
t.Logf("Sequencer started with force inclusion server at %s", fiUrl)
134+
t.Logf("Genesis hash: %s", genesisHash)
135+
136+
// Connect to sequencer EVM
137+
client, err := ethclient.Dial(seqEthURL)
138+
require.NoError(t, err)
139+
defer client.Close()
140+
141+
// 1. Send a normal transaction first to ensure chain is moving
142+
t.Log("Sending normal transaction...")
143+
var nonce uint64 = 0
144+
txNormal := evm.GetRandomTransaction(t, TestPrivateKey, TestToAddress, DefaultChainID, DefaultGasLimit, &nonce)
145+
err = client.SendTransaction(context.Background(), txNormal)
146+
require.NoError(t, err)
147+
148+
require.Eventually(t, func() bool {
149+
return evm.CheckTxIncluded(client, txNormal.Hash())
150+
}, 15*time.Second, 500*time.Millisecond, "Normal transaction not included")
151+
t.Log("Normal transaction included")
152+
153+
// 2. Send a Forced Inclusion transaction
154+
t.Log("Sending forced inclusion transaction...")
155+
txForce := evm.GetRandomTransaction(t, TestPrivateKey, TestToAddress, DefaultChainID, DefaultGasLimit, &nonce)
156+
txBytes, err := txForce.MarshalBinary()
157+
require.NoError(t, err)
158+
159+
submitForceInclusionTx(t, fiUrl, txBytes)
160+
t.Logf("Forced inclusion transaction submitted: %s", txForce.Hash().Hex())
161+
162+
// Wait for inclusion
163+
// Force inclusion depends on DA epoch. With epoch=2 and fast DA block time (200ms),
164+
// this should be reasonably fast, but we allow enough time for robustness.
165+
require.Eventually(t, func() bool {
166+
return evm.CheckTxIncluded(client, txForce.Hash())
167+
}, 30*time.Second, 1*time.Second, "Forced inclusion transaction not included")
168+
169+
t.Log("Forced inclusion transaction included successfully in Sequencer")
170+
}
171+
172+
func TestEvmFullNodeForceInclusionE2E(t *testing.T) {
173+
sut := NewSystemUnderTest(t)
174+
workDir := t.TempDir()
175+
sequencerHome := filepath.Join(workDir, "sequencer")
176+
fullNodeHome := filepath.Join(workDir, "fullnode")
177+
178+
// Get a port for force inclusion server
179+
fiPort, err := getAvailablePort()
180+
require.NoError(t, err)
181+
fiUrl := fmt.Sprintf("http://127.0.0.1:%d", fiPort)
182+
183+
// --- Start Sequencer Setup ---
184+
// We manually setup sequencer here because we need the force inclusion flag,
185+
// and we need to capture variables for full node setup.
186+
jwtSecret, fullNodeJwtSecret, genesisHash, endpoints := setupCommonEVMTest(t, sut, true)
187+
188+
passphraseFile := createPassphraseFile(t, sequencerHome)
189+
jwtSecretFile := createJWTSecretFile(t, sequencerHome, jwtSecret)
190+
191+
output, err := sut.RunCmd(evmSingleBinaryPath,
192+
"init",
193+
"--evnode.node.aggregator=true",
194+
"--evnode.signer.passphrase_file", passphraseFile,
195+
"--home", sequencerHome,
196+
)
197+
require.NoError(t, err, "failed to init sequencer", output)
198+
199+
// Set epoch to 2 for fast testing
200+
enableForceInclusionInGenesis(t, sequencerHome, 2)
201+
202+
fiAddr := fmt.Sprintf("127.0.0.1:%d", fiPort)
203+
seqArgs := []string{
204+
"start",
205+
"--evm.jwt-secret-file", jwtSecretFile,
206+
"--evm.genesis-hash", genesisHash,
207+
"--evnode.node.block_time", DefaultBlockTime,
208+
"--evnode.node.aggregator=true",
209+
"--evnode.signer.passphrase_file", passphraseFile,
210+
"--home", sequencerHome,
211+
"--evnode.da.block_time", DefaultDABlockTime,
212+
"--evnode.da.address", endpoints.GetDAAddress(),
213+
"--evnode.da.namespace", DefaultDANamespace,
214+
"--evnode.da.forced_inclusion_namespace", "forced-inc",
215+
"--evnode.rpc.address", endpoints.GetRollkitRPCListen(),
216+
"--evnode.p2p.listen_address", endpoints.GetRollkitP2PAddress(),
217+
"--evm.engine-url", endpoints.GetSequencerEngineURL(),
218+
"--evm.eth-url", endpoints.GetSequencerEthURL(),
219+
"--force-inclusion-server", fiAddr,
220+
}
221+
sut.ExecCmd(evmSingleBinaryPath, seqArgs...)
222+
sut.AwaitNodeUp(t, endpoints.GetRollkitRPCAddress(), NodeStartupTimeout)
223+
t.Log("Sequencer is up with force inclusion enabled")
224+
// --- End Sequencer Setup ---
225+
226+
// --- Start Full Node Setup ---
227+
// Reuse setupFullNode helper which handles genesis copying and node startup
228+
setupFullNode(t, sut, fullNodeHome, sequencerHome, fullNodeJwtSecret, genesisHash, endpoints.GetRollkitP2PAddress(), endpoints)
229+
t.Log("Full node is up")
230+
// --- End Full Node Setup ---
231+
232+
// Connect to clients
233+
seqClient, err := ethclient.Dial(endpoints.GetSequencerEthURL())
234+
require.NoError(t, err)
235+
defer seqClient.Close()
236+
237+
fnClient, err := ethclient.Dial(endpoints.GetFullNodeEthURL())
238+
require.NoError(t, err)
239+
defer fnClient.Close()
240+
241+
var nonce uint64 = 0
242+
243+
// 1. Send normal tx to sequencer
244+
t.Log("Sending normal transaction...")
245+
txNormal := evm.GetRandomTransaction(t, TestPrivateKey, TestToAddress, DefaultChainID, DefaultGasLimit, &nonce)
246+
err = seqClient.SendTransaction(context.Background(), txNormal)
247+
require.NoError(t, err)
248+
249+
// Wait for full node to sync it
250+
require.Eventually(t, func() bool {
251+
return evm.CheckTxIncluded(fnClient, txNormal.Hash())
252+
}, 20*time.Second, 500*time.Millisecond, "Normal tx not synced to full node")
253+
t.Log("Normal tx synced to full node")
254+
255+
// 2. Send forced inclusion tx
256+
t.Log("Sending forced inclusion transaction...")
257+
txForce := evm.GetRandomTransaction(t, TestPrivateKey, TestToAddress, DefaultChainID, DefaultGasLimit, &nonce)
258+
txBytes, err := txForce.MarshalBinary()
259+
require.NoError(t, err)
260+
261+
submitForceInclusionTx(t, fiUrl, txBytes)
262+
t.Logf("Forced inclusion transaction submitted: %s", txForce.Hash().Hex())
263+
264+
// Wait for full node to sync it
265+
require.Eventually(t, func() bool {
266+
return evm.CheckTxIncluded(fnClient, txForce.Hash())
267+
}, 40*time.Second, 1*time.Second, "Forced inclusion tx not synced to full node")
268+
269+
t.Log("Forced inclusion tx synced to full node successfully")
270+
}

0 commit comments

Comments
 (0)