@effectstream/sync 0.100.27 → 0.101.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effectstream/sync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.101.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://effectstream.github.io/docs/",
|
|
6
6
|
"repository": {
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
},
|
|
14
14
|
"exports": "./src/mod.ts",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@effectstream/config": "0.
|
|
17
|
-
"@effectstream/db": "0.
|
|
18
|
-
"@effectstream/log": "0.
|
|
19
|
-
"@effectstream/utils": "0.
|
|
16
|
+
"@effectstream/config": "0.101.0",
|
|
17
|
+
"@effectstream/db": "0.101.0",
|
|
18
|
+
"@effectstream/log": "0.101.0",
|
|
19
|
+
"@effectstream/utils": "0.101.0",
|
|
20
20
|
"@midnight-ntwrk/ledger-v8": "8.0.3",
|
|
21
21
|
"@midnight-ntwrk/midnight-js-indexer-public-data-provider": "4.0.4",
|
|
22
22
|
"@midnight-ntwrk/onchain-runtime": "npm:@midnight-ntwrk/onchain-runtime-v3@3.0.0",
|
|
@@ -54,6 +54,9 @@ const CELESTIA_NODE_URL = ENV.getString(
|
|
|
54
54
|
"http://localhost:26658",
|
|
55
55
|
);
|
|
56
56
|
const CELESTIA_AUTH_TOKEN = ENV.getString("CELESTIA_AUTH_TOKEN", "");
|
|
57
|
+
const CELESTIA_RPC_TIMEOUT_MS = parseInt(
|
|
58
|
+
ENV.getString("CELESTIA_RPC_TIMEOUT_MS", "15000"),
|
|
59
|
+
);
|
|
57
60
|
|
|
58
61
|
/**
|
|
59
62
|
* Converts a hex-encoded Celestia namespace to a base64-encoded 29-byte array.
|
|
@@ -80,29 +83,38 @@ export class CelestiaClient {
|
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
private async rpc<T>(method: string, params: unknown[]): Promise<T | null> {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
headers,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
86
|
+
// Retry forever — a transient stall must never cause a block to be skipped.
|
|
87
|
+
// The only exit paths are: (a) success, (b) "blob: not found" (empty namespace).
|
|
88
|
+
// Everything else (network error, timeout, RPC error) loops with backoff.
|
|
89
|
+
let attempt = 0;
|
|
90
|
+
while (true) {
|
|
91
|
+
try {
|
|
92
|
+
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
93
|
+
if (CELESTIA_AUTH_TOKEN) headers["Authorization"] = `Bearer ${CELESTIA_AUTH_TOKEN}`;
|
|
94
|
+
const res = await fetch(this.rpcUrl, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers,
|
|
97
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
98
|
+
signal: AbortSignal.timeout(CELESTIA_RPC_TIMEOUT_MS),
|
|
99
|
+
});
|
|
100
|
+
const json = await res.json();
|
|
101
|
+
if (json.error) {
|
|
102
|
+
const errMsg: string = json.error.message ?? String(json.error);
|
|
103
|
+
if (errMsg.includes("blob: not found")) return null;
|
|
104
|
+
throw new Error(`[Celestia] RPC error [${method}]: ${errMsg}`);
|
|
105
|
+
}
|
|
106
|
+
return json.result as T;
|
|
107
|
+
} catch (err: unknown) {
|
|
108
|
+
attempt++;
|
|
109
|
+
const backoffMs = Math.min(1000 * 2 ** Math.min(attempt - 1, 5), 30_000);
|
|
110
|
+
console.warn(
|
|
111
|
+
`[Celestia] ${method} attempt ${attempt} failed (retrying in ${backoffMs}ms): ${
|
|
112
|
+
err instanceof Error ? err.message : String(err)
|
|
113
|
+
}`,
|
|
114
|
+
);
|
|
115
|
+
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
116
|
+
}
|
|
104
117
|
}
|
|
105
|
-
return json.result as T;
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
async getLatestBlockHeight(): Promise<number> {
|
|
@@ -51,44 +51,49 @@ export class CelestiaFetcher extends BaseDataFetcher<
|
|
|
51
51
|
(p) => p.primitive.getAllBlockHeaders,
|
|
52
52
|
);
|
|
53
53
|
|
|
54
|
+
const heights: number[] = [];
|
|
55
|
+
for (let h = data.from; h <= data.to; h++) heights.push(h);
|
|
56
|
+
|
|
54
57
|
console.log(
|
|
55
|
-
`[Celestia] Fetching blocks from ${data.from} to ${data.to}.${
|
|
58
|
+
`[Celestia] Fetching blocks from ${data.from} to ${data.to} (${heights.length} blocks, concurrency=${(this.config.syncProtocol as any).concurrency ?? 1}).${
|
|
56
59
|
data.isPresync ? " [presync]" : ""
|
|
57
60
|
}`,
|
|
58
61
|
);
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
extHeader = yield* call(() =>
|
|
65
|
-
this.client.getHeaderAtHeight(height)
|
|
66
|
-
);
|
|
67
|
-
} catch (_e) {
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
63
|
+
const results: (Output | undefined)[] = new Array(heights.length);
|
|
64
|
+
let cursor = 0;
|
|
65
|
+
const concurrency: number = (this.config.syncProtocol as any).concurrency ?? 1;
|
|
66
|
+
const self = this;
|
|
70
67
|
|
|
71
|
-
|
|
68
|
+
// Each worker pulls the next unstarted height from `cursor` and stores the
|
|
69
|
+
// result at the corresponding index so output order is always height-ascending.
|
|
70
|
+
// The cursor increment is cooperative-race-free in effection: it runs
|
|
71
|
+
// synchronously before the first yield in each worker iteration.
|
|
72
|
+
function* worker(): Operation<void> {
|
|
73
|
+
while (true) {
|
|
74
|
+
const i = cursor++;
|
|
75
|
+
if (i >= heights.length) return;
|
|
76
|
+
const height = heights[i];
|
|
77
|
+
const extHeader = yield* call(() => self.client.getHeaderAtHeight(height));
|
|
78
|
+
const blockHash = extHeader.commit.block_id.hash;
|
|
79
|
+
const primitives = yield* self.readPrimitives(height, blockHash, self.config.primitives);
|
|
80
|
+
results[i] = { timestamp: extHeader.header.time, height, hash: blockHash, primitives };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.config.primitives,
|
|
84
|
+
if (heights.length > 0) {
|
|
85
|
+
yield* all(
|
|
86
|
+
Array.from({ length: Math.min(concurrency, heights.length) }, () => worker()),
|
|
77
87
|
);
|
|
88
|
+
}
|
|
78
89
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
primitives,
|
|
84
|
-
};
|
|
90
|
+
let lastFetchedOutput: Output | undefined;
|
|
91
|
+
for (let i = 0; i < results.length; i++) {
|
|
92
|
+
const output = results[i];
|
|
93
|
+
if (!output) continue;
|
|
85
94
|
lastFetchedOutput = output;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
outputs.push({
|
|
89
|
-
output,
|
|
90
|
-
cleanup: () => {},
|
|
91
|
-
});
|
|
95
|
+
if (fetchAllBlocks || output.primitives.length > 0 || output.height === data.to) {
|
|
96
|
+
outputs.push({ output, cleanup: () => {} });
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
|