Skip to content

Commit 1237eb9

Browse files
antoinedccursoragentantoine
authored
Index parent chains and process sequencer batches (#431)
* Add Orbit chain transaction state tracking and configuration Co-authored-by: antoine <[email protected]> * Add Orbit transaction processing with enhanced monitoring and security Co-authored-by: antoine <[email protected]> * Add parent chain support for Orbit chain infrastructure contracts Co-authored-by: antoine <[email protected]> * fix provider * update * Checkpoint before follow-up message * Improve RPC event querying with chunking and error handling Co-authored-by: antoine <[email protected]> * orbit * Add Orbit batch indexing and UI for tracking sequencer batches Co-authored-by: antoine <[email protected]> * Checkpoint before follow-up message Co-authored-by: antoine <[email protected]> * Add Orbit batch discovery and enhanced parsing mechanism Co-authored-by: antoine <[email protected]> * Enhance Orbit transaction processing with improved logging and error handling Co-authored-by: antoine <[email protected]> * fixes * fix column error * fixes * Implement orbit batch queue management with rate limiting and deduplication Co-authored-by: antoine <[email protected]> * orbit: improve batch event detection - use BigNumber for indexed arg and hexZeroPad - configurable SEARCH_BLOCK_WINDOW, default 50k - fallback to provider.getLogs with explicit topics - remove console.log and add better debug logs * iteration * Add parent workspace support for Orbit batch discovery and chain metadata Co-authored-by: antoine <[email protected]> * Simplify orbit batch discovery to use parent workspace indexed logs Co-authored-by: antoine <[email protected]> * introduce ndos * Add Orbit node indexing and batch-node mapping support Co-authored-by: antoine <[email protected]> * Update Orbit node indexing for Nitro rollup assertion events Co-authored-by: antoine <[email protected]> * Improve batch sequence number lookup for orbit nodes using afterAcc Co-authored-by: antoine <[email protected]> * rebase * iterations * cleanup * impr * fix conflict * finalization * deleted extra files * tests + cleanup * tests + cleanup * tests + cleanup * tests + cleanup * more tests * more tests * frontend tests * remove useless fns * cleanup * orbit config * add tests * frontend tests * fix reset fn + tests * remove tests * remove console log * fix migrations --------- Co-authored-by: Cursor Agent <[email protected]> Co-authored-by: antoine <[email protected]>
1 parent 7085fc7 commit 1237eb9

File tree

181 files changed

+35756
-833
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

181 files changed

+35756
-833
lines changed

.cursor/rules/frontend-tester.mdc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The linter for tests is tests/.eslintrc.js
1616
- When you create or update snapshots, always analyze them following instructions in the "Snapshots" sections of this file
1717
- Indent with 4 spaces
1818
- Make sure all imports are used. Remove unused ones.
19+
- It's fine to only have one test in a file if the tested file is simple. Each test should add some unique value.
1920

2021
# Imports
2122
- Never import mount, vi, createTestingPinia, flushPromises

pm2-server/app.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
const express = require('express');
22
const app = express();
33
const pm2 = require('./lib/pm2.js');
4+
const { secretMiddleware } = require('./lib/middleware.js');
45

56
const bodyParser = require('body-parser')
67
app.use(bodyParser.json());
78

89
const commands = ['stop', 'reload', 'restart', 'delete', 'resume'];
910

10-
const secretMiddleware = (req, res, next) => {
11-
if (req.query.secret == process.env.SECRET)
12-
next();
13-
else
14-
return res.status(401).send('Invalid secret');
15-
};
16-
1711
const handleError = (res, error) => {
1812
console.log(error);
1913
return res.status(400).send(error.message);
2014
};
2115

16+
app.post('/log-listener', secretMiddleware, async (req, res) => {
17+
const data = req.body;
18+
19+
try {
20+
if (!data.slug || !data.jsonArgs)
21+
throw new Error('Missing parameter');
22+
23+
const pm2Process = await pm2.startLogListener(data.slug, data.jsonArgs);
24+
25+
return res.status(200).send(pm2Process);
26+
} catch(error) {
27+
handleError(res, error);
28+
}
29+
});
30+
2231
app.get('/processes', secretMiddleware, async (req, res) => {
2332
try {
2433
const processes = await pm2.list()

pm2-server/ecosystem.config.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
module.exports = {
2-
apps : [{
3-
name: "PM2 Server",
4-
script: "./index.js",
5-
env: {
6-
NODE_ENV: "development",
2+
apps : [
3+
{
4+
name: 'PM2 Server',
5+
script: './index.js',
6+
env: {
7+
NODE_ENV: 'development',
8+
},
9+
env_production: {
10+
NODE_ENV: 'production',
11+
}
712
},
8-
env_production: {
9-
NODE_ENV: "production",
10-
}
11-
}]
13+
]
1214
}

pm2-server/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ const triggerSync = () => {
1616
app.listen(port, () => {
1717
console.log(`App is listening on port ${port}`);
1818
triggerSync();
19+
startL1Trackers();
1920
});

pm2-server/lib/client.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const { createPublicClient, http, defineChain, webSocket } = require('viem');
2+
3+
const getProvider = (_rpcServer) => {
4+
try {
5+
const rpcServer = new URL(_rpcServer);
6+
return rpcServer.protocol == 'ws:' || rpcServer.protocol == 'wss:' ? { http: [], webSocket: [_rpcServer] } : { http: [_rpcServer], webSocket: [] };
7+
} catch (error) {
8+
console.log(error);
9+
process.exit(1);
10+
}
11+
};
12+
13+
const fetchOptions = (_rpcServer) => {
14+
const rpcServer = new URL(_rpcServer);
15+
if (rpcServer.username.length || rpcServer.password.length) {
16+
const base64Credentials = btoa(`${rpcServer.username}:${rpcServer.password}`);
17+
return { headers: { 'Authorization': `Basic ${base64Credentials}` }};
18+
}
19+
else
20+
return {};
21+
};
22+
23+
const getClient = (workspace) => {
24+
const chain = defineChain({
25+
id: workspace.networkId,
26+
name: workspace.name,
27+
network: workspace.name,
28+
nativeCurrency: {
29+
decimals: 18,
30+
name: 'Ether',
31+
symbol: 'ETH'
32+
},
33+
rpcUrls: {
34+
default: getProvider(workspace.rpcServer),
35+
public: getProvider(workspace.rpcServer)
36+
}
37+
});
38+
39+
const url = new URL(workspace.rpcServer).origin + new URL(workspace.rpcServer).pathname + new URL(workspace.rpcServer).search;
40+
const transport = workspace.rpcServer.startsWith('ws') ?
41+
webSocket(url) :
42+
http(url, { fetchOptions: fetchOptions(workspace.rpcServer) });
43+
44+
return createPublicClient({ chain, transport });
45+
};
46+
47+
module.exports = {
48+
getClient
49+
};

pm2-server/lib/env.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
getEthernalSecret: () => process.env.ETHERNAL_SECRET,
3+
getApiHost: () => process.env.ETHERNAL_HOST || 'http://localhost:8888',
4+
getSecret: () => process.env.SECRET
5+
};

pm2-server/lib/middleware.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const { getSecret } = require('./env.js');
2+
3+
const secretMiddleware = (req, res, next) => {
4+
if (req.query.secret == getSecret())
5+
next();
6+
else
7+
return res.status(401).send('Invalid secret');
8+
};
9+
10+
module.exports = {
11+
secretMiddleware
12+
};

pm2-server/lib/pm2.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,34 @@ const _delete = (slug) => {
131131
});
132132
};
133133

134+
const startLogListener = (slug, jsonArgs) => {
135+
return new Promise((resolve, reject) => {
136+
if (!slug || !jsonArgs) reject(new Error('Missing parameter'));
137+
138+
pm2.connect(error => {
139+
if (error) reject(new Error(error));
140+
141+
const options = {
142+
name: slug,
143+
script: 'node',
144+
args: ['logListener.js', jsonArgs],
145+
interpreter: 'none',
146+
log_type: 'json'
147+
};
148+
149+
pm2.start(options, (error) => {
150+
if (error) reject(new Error(error));
151+
152+
pm2.describe(slug, (error, process) => {
153+
if (error) reject(new Error(error));
154+
155+
resolve(process[0]);
156+
});
157+
});
158+
});
159+
});
160+
};
161+
134162
const start = (slug, workspaceId) => {
135163
return new Promise((resolve, reject) => {
136164
if (!slug || !workspaceId) reject(new Error('Missing parameter'));
@@ -159,4 +187,4 @@ const start = (slug, workspaceId) => {
159187
});
160188
};
161189

162-
module.exports = { list, show, stop, reload, restart, delete: _delete, start, resume };
190+
module.exports = { list, show, stop, reload, restart, delete: _delete, start, resume, startLogListener };

pm2-server/lib/queue.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const Redis = require('ioredis');
2+
const { Queue } = require('bullmq');
3+
4+
const redisUrl = process.env.ETHERNAL_REDIS_URL
5+
6+
const defaultJobOptions = {
7+
attempts: 50,
8+
removeOnComplete: {
9+
count: 100,
10+
age: 4 * 60
11+
},
12+
timeout: 30000,
13+
backoff: {
14+
type: 'exponential',
15+
delay: 1000
16+
}
17+
};
18+
19+
const connection = new Redis(redisUrl);
20+
21+
const enqueue = (queueName, jobName, data, priority = 1) => {
22+
const queue = new Queue(queueName, { connection, defaultJobOptions });
23+
return queue.add(jobName, data, { priority });
24+
};
25+
26+
module.exports = {
27+
enqueue
28+
}

pm2-server/lib/workspace.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const axios = require('axios');
2+
const { getEthernalSecret, getApiHost } = require('./env');
3+
4+
const secret = getEthernalSecret();
5+
const apiHost = getApiHost();
6+
7+
if (!secret) {
8+
console.log(`Pass the secret with the ETHERNAL_SECRET env variable.`);
9+
process.exit(1);
10+
}
11+
12+
const getWorkspace = async (workspaceId) => {
13+
const { data: workspace } = await axios.get(`${apiHost}/api/workspaces/${workspaceId}`, { params: { secret } });
14+
return { workspace };
15+
};
16+
17+
module.exports = {
18+
getWorkspace
19+
}

0 commit comments

Comments
 (0)