bulletin-deploy 0.7.3 → 0.7.4
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/README.md +26 -0
- package/bin/bulletin-deploy +94 -1
- package/dist/bug-report.js +4 -3
- package/dist/{chunk-MDYSENTW.js → chunk-CQ753LDA.js} +1 -1
- package/dist/{chunk-BYIVK52G.js → chunk-DHQ3JGF4.js} +67 -75
- package/dist/{chunk-EPNPPAMS.js → chunk-SVKCVNXD.js} +68 -41
- package/dist/chunk-UJP2PZGU.js +155 -0
- package/dist/chunk-YREZFNCC.js +869 -0
- package/dist/{chunk-4EQERQRG.js → chunk-YYULF2JX.js} +2 -2
- package/dist/deploy.d.ts +4 -2
- package/dist/deploy.js +8 -5
- package/dist/dotns.d.ts +12 -40
- package/dist/dotns.js +9 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +24 -6
- package/dist/memory-report.js +2 -1
- package/dist/run-state.d.ts +22 -0
- package/dist/run-state.js +21 -0
- package/dist/telemetry.d.ts +5 -1
- package/dist/telemetry.js +8 -1
- package/dist/version-check.js +3 -2
- package/package.json +4 -3
- package/dist/chunk-M3H3F4FY.js +0 -1048
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
setDeployContext
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YYULF2JX.js";
|
|
4
4
|
import {
|
|
5
5
|
DotNS,
|
|
6
6
|
TX_TIMEOUT_MS,
|
|
7
7
|
fetchNonce,
|
|
8
8
|
parseDomainName,
|
|
9
|
-
popStatusName
|
|
10
|
-
|
|
9
|
+
popStatusName,
|
|
10
|
+
verifyNonceAdvanced
|
|
11
|
+
} from "./chunk-YREZFNCC.js";
|
|
11
12
|
import {
|
|
12
13
|
MirrorSkipped,
|
|
13
14
|
mirrorToGitHubPages,
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
truncateAddress,
|
|
27
28
|
withDeploySpan,
|
|
28
29
|
withSpan
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-DHQ3JGF4.js";
|
|
30
31
|
import {
|
|
31
32
|
merkleizeJS
|
|
32
33
|
} from "./chunk-B7GUYYAN.js";
|
|
@@ -48,7 +49,7 @@ import { sha256 } from "@noble/hashes/sha256";
|
|
|
48
49
|
import { blake2b } from "@noble/hashes/blake2b";
|
|
49
50
|
import { createClient as createPolkadotClient, Enum } from "polkadot-api";
|
|
50
51
|
import { Binary } from "@polkadot-api/substrate-bindings";
|
|
51
|
-
import { getWsProvider } from "polkadot-api/ws-provider";
|
|
52
|
+
import { getWsProvider, WsEvent } from "polkadot-api/ws-provider";
|
|
52
53
|
import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat";
|
|
53
54
|
import { CID } from "multiformats/cid";
|
|
54
55
|
import { create as createMultihash } from "multiformats/hashes/digest";
|
|
@@ -75,12 +76,23 @@ function friendlyChainError(msg) {
|
|
|
75
76
|
}
|
|
76
77
|
var DEFAULT_BULLETIN_RPC = "wss://paseo-bulletin-rpc.polkadot.io";
|
|
77
78
|
var DEFAULT_POOL_SIZE = 10;
|
|
78
|
-
var
|
|
79
|
+
var BULLETIN_ENDPOINTS = [DEFAULT_BULLETIN_RPC];
|
|
79
80
|
var POOL_SIZE = DEFAULT_POOL_SIZE;
|
|
81
|
+
var _deployRpcFailedOver = false;
|
|
82
|
+
function makeBulletinStatusHandler(primary) {
|
|
83
|
+
return (s) => {
|
|
84
|
+
if (s.type === WsEvent.CONNECTED && s.uri !== primary) {
|
|
85
|
+
_deployRpcFailedOver = true;
|
|
86
|
+
setDeployAttribute("deploy.rpc.failed_over", "true");
|
|
87
|
+
captureWarning("Bulletin RPC failover", { from: primary, to: s.uri });
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
80
91
|
var CHUNK_SIZE = 2 * 1024 * 1024;
|
|
81
92
|
var MAX_FILE_SIZE = 8 * 1024 * 1024;
|
|
82
93
|
var MAX_RECONNECTIONS = parseInt(process.env.BULLETIN_MAX_RECONNECTIONS ?? "3", 10);
|
|
83
94
|
var CHUNK_TIMEOUT_MS = parseInt(process.env.BULLETIN_CHUNK_TIMEOUT_MS ?? "180000", 10);
|
|
95
|
+
var CHUNK_MORTALITY_PERIOD = 16;
|
|
84
96
|
var RETRY_BASE_DELAY_MS = 2e3;
|
|
85
97
|
var RETRY_MAX_DELAY_MS = 15e3;
|
|
86
98
|
var WS_HEARTBEAT_TIMEOUT_MS = 3e5;
|
|
@@ -147,8 +159,12 @@ function toHashingEnum(mhCode) {
|
|
|
147
159
|
}
|
|
148
160
|
}
|
|
149
161
|
async function getProvider() {
|
|
150
|
-
|
|
151
|
-
|
|
162
|
+
const primary = BULLETIN_ENDPOINTS[0];
|
|
163
|
+
console.log(` Connecting to Bulletin: ${primary}`);
|
|
164
|
+
const client = createPolkadotClient(withPolkadotSdkCompat(getWsProvider(
|
|
165
|
+
BULLETIN_ENDPOINTS,
|
|
166
|
+
{ heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
|
|
167
|
+
)));
|
|
152
168
|
const unsafeApi = client.getUnsafeApi();
|
|
153
169
|
try {
|
|
154
170
|
await cryptoWaitReady();
|
|
@@ -159,10 +175,10 @@ async function getProvider() {
|
|
|
159
175
|
if (!selected) {
|
|
160
176
|
const best = authorizations.reduce((a, b) => a.transactions > b.transactions ? a : b);
|
|
161
177
|
console.log(` All pool accounts low on capacity, auto-authorizing account ${best.index}...`);
|
|
162
|
-
await ensureAuthorized(unsafeApi, best.address,
|
|
178
|
+
await ensureAuthorized(unsafeApi, best.address, BULLETIN_ENDPOINTS[0], `pool account ${best.index}`);
|
|
163
179
|
selected = best;
|
|
164
180
|
} else {
|
|
165
|
-
await ensureAuthorized(unsafeApi, selected.address,
|
|
181
|
+
await ensureAuthorized(unsafeApi, selected.address, BULLETIN_ENDPOINTS[0], `pool account ${selected.index}`);
|
|
166
182
|
}
|
|
167
183
|
console.log(` Using pool account ${selected.index}: ${selected.address}`);
|
|
168
184
|
setDeployAttribute("deploy.signer.mode", "pool");
|
|
@@ -175,8 +191,12 @@ async function getProvider() {
|
|
|
175
191
|
}
|
|
176
192
|
}
|
|
177
193
|
async function getDirectProvider(mnemonic, derivationPath = "") {
|
|
178
|
-
|
|
179
|
-
|
|
194
|
+
const primary = BULLETIN_ENDPOINTS[0];
|
|
195
|
+
console.log(` Connecting to Bulletin: ${primary}`);
|
|
196
|
+
const client = createPolkadotClient(withPolkadotSdkCompat(getWsProvider(
|
|
197
|
+
BULLETIN_ENDPOINTS,
|
|
198
|
+
{ heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
|
|
199
|
+
)));
|
|
180
200
|
const unsafeApi = client.getUnsafeApi();
|
|
181
201
|
const { signer, ss58 } = deriveRootSigner(mnemonic, derivationPath);
|
|
182
202
|
console.log(` Using direct signer: ${ss58}${derivationPath ? ` (path: ${derivationPath})` : ""}`);
|
|
@@ -195,8 +215,9 @@ async function getDirectProvider(mnemonic, derivationPath = "") {
|
|
|
195
215
|
return { client, unsafeApi, signer, ss58 };
|
|
196
216
|
}
|
|
197
217
|
var MAX_BEST_CHAIN_DROPS = 5;
|
|
198
|
-
function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction", rpc, senderSS58, expectedNonce, timeoutMs } = {}) {
|
|
218
|
+
function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction", rpc, senderSS58, expectedNonce, timeoutMs, fetchNonce: fetchNonceOverride } = {}) {
|
|
199
219
|
const timeout = timeoutMs ?? TX_TIMEOUT_MS;
|
|
220
|
+
const _fetchNonce = fetchNonceOverride ?? fetchNonce;
|
|
200
221
|
return new Promise((resolve2, reject) => {
|
|
201
222
|
let settled = false;
|
|
202
223
|
let sub;
|
|
@@ -211,14 +232,15 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
|
|
|
211
232
|
}
|
|
212
233
|
fn(...args);
|
|
213
234
|
};
|
|
214
|
-
const tryNonceFallback = async () => {
|
|
235
|
+
const tryNonceFallback = async (subscriptionErrored) => {
|
|
215
236
|
if (!rpc || !senderSS58 || expectedNonce == null) return false;
|
|
216
237
|
try {
|
|
217
|
-
const
|
|
238
|
+
const endpoints = Array.isArray(rpc) ? rpc : [rpc];
|
|
239
|
+
const verified = await verifyNonceAdvanced(endpoints, senderSS58, expectedNonce);
|
|
218
240
|
if (settled) return true;
|
|
219
|
-
if (
|
|
220
|
-
console.log(` ${label}: nonce advanced
|
|
221
|
-
settle(resolve2)({ value: onSuccess(), viaFallback: true });
|
|
241
|
+
if (verified.advanced) {
|
|
242
|
+
console.log(` ${label}: nonce advanced past ${expectedNonce} (witnessed by ${verified.witnessRpc}), tx was included`);
|
|
243
|
+
settle(resolve2)({ value: onSuccess(), viaFallback: true, subscriptionError: subscriptionErrored });
|
|
222
244
|
return true;
|
|
223
245
|
}
|
|
224
246
|
} catch (e) {
|
|
@@ -229,7 +251,7 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
|
|
|
229
251
|
};
|
|
230
252
|
const timer = setTimeout(async () => {
|
|
231
253
|
if (settled) return;
|
|
232
|
-
if (await tryNonceFallback()) return;
|
|
254
|
+
if (await tryNonceFallback(false)) return;
|
|
233
255
|
settle(reject)(new Error(`${label} timed out after ${timeout / 1e3}s waiting for block confirmation`));
|
|
234
256
|
}, timeout);
|
|
235
257
|
sub = tx.signSubmitAndWatch(signer, txOpts).subscribe({
|
|
@@ -237,7 +259,7 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
|
|
|
237
259
|
if (event.type === "txBestBlocksState") {
|
|
238
260
|
if (event.found) {
|
|
239
261
|
if (event.ok) {
|
|
240
|
-
settle(resolve2)({ value: onSuccess(event), viaFallback: false });
|
|
262
|
+
settle(resolve2)({ value: onSuccess(event), viaFallback: false, subscriptionError: false });
|
|
241
263
|
} else {
|
|
242
264
|
settle(reject)(new Error(`${label} dispatch error`));
|
|
243
265
|
}
|
|
@@ -245,7 +267,7 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
|
|
|
245
267
|
dropCount++;
|
|
246
268
|
if (dropCount >= MAX_BEST_CHAIN_DROPS) {
|
|
247
269
|
console.log(` ${label}: tx dropped ${dropCount} times, checking nonce...`);
|
|
248
|
-
if (await tryNonceFallback()) return;
|
|
270
|
+
if (await tryNonceFallback(true)) return;
|
|
249
271
|
settle(reject)(new Error(`${label} tx dropped from best chain ${dropCount} times`));
|
|
250
272
|
} else {
|
|
251
273
|
console.log(` ${label}: tx dropped from best chain (${dropCount}/${MAX_BEST_CHAIN_DROPS}), waiting...`);
|
|
@@ -260,16 +282,16 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
|
|
|
260
282
|
});
|
|
261
283
|
});
|
|
262
284
|
}
|
|
263
|
-
async function storeChunk(unsafeApi, signer, chunkBytes, nonce, ss58) {
|
|
285
|
+
async function storeChunk(unsafeApi, signer, chunkBytes, nonce, ss58, opts = {}) {
|
|
264
286
|
const hashCode = 18;
|
|
265
287
|
const cid = createCID(chunkBytes, CID_CONFIG.codec, hashCode);
|
|
266
288
|
const tx = unsafeApi.tx.TransactionStorage.store_with_cid_config({ cid: { codec: BigInt(CID_CONFIG.codec), hashing: toHashingEnum(hashCode) }, data: Binary.fromBytes(chunkBytes) });
|
|
267
|
-
const txOpts = { mortality: { mortal: true, period:
|
|
268
|
-
const { value, viaFallback } = await watchTransaction(tx, signer, txOpts, () => {
|
|
289
|
+
const txOpts = { mortality: { mortal: true, period: CHUNK_MORTALITY_PERIOD }, nonce };
|
|
290
|
+
const { value, viaFallback, subscriptionError } = await watchTransaction(tx, signer, txOpts, () => {
|
|
269
291
|
console.log(` CID: ${cid.toString()}`);
|
|
270
292
|
return { cid, len: chunkBytes.length };
|
|
271
|
-
}, { label: `chunk(nonce:${nonce})`, rpc:
|
|
272
|
-
return { ...value, viaFallback };
|
|
293
|
+
}, { label: `chunk(nonce:${nonce})`, rpc: BULLETIN_ENDPOINTS, senderSS58: ss58, expectedNonce: nonce, timeoutMs: CHUNK_TIMEOUT_MS, fetchNonce: opts.fetchNonce });
|
|
294
|
+
return { ...value, viaFallback, subscriptionError };
|
|
273
295
|
}
|
|
274
296
|
async function storeFile(contentBytes, { client: existingClient, unsafeApi: existingApi, signer: existingSigner } = {}) {
|
|
275
297
|
console.log(`
|
|
@@ -305,7 +327,8 @@ async function storeFile(contentBytes, { client: existingClient, unsafeApi: exis
|
|
|
305
327
|
throw e;
|
|
306
328
|
}
|
|
307
329
|
}
|
|
308
|
-
async function storeChunkedContent(chunks, { client: existingClient, unsafeApi: existingApi, signer: existingSigner, ss58: existingSS58, reconnect } = {}) {
|
|
330
|
+
async function storeChunkedContent(chunks, { client: existingClient, unsafeApi: existingApi, signer: existingSigner, ss58: existingSS58, reconnect, fetchNonce: fetchNonceOverride } = {}) {
|
|
331
|
+
const _fetchNonce = fetchNonceOverride ?? fetchNonce;
|
|
309
332
|
console.log(`
|
|
310
333
|
Chunks: ${chunks.length}`);
|
|
311
334
|
const totalBytes = chunks.reduce((s, c) => s + c.length, 0);
|
|
@@ -337,7 +360,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
337
360
|
Account has insufficient authorization for this upload (need ${requiredTxs} txs / ${(totalBytes / 1e6).toFixed(1)}MB, have ${txsRemaining} txs / ${Number(bytesRemaining) / 1e6}MB)`);
|
|
338
361
|
console.log(` Attempting to re-authorize with Alice...`);
|
|
339
362
|
try {
|
|
340
|
-
await ensureAuthorized(unsafeApi, ss58,
|
|
363
|
+
await ensureAuthorized(unsafeApi, ss58, BULLETIN_ENDPOINTS[0], void 0, { txs: requiredTxs, bytes: requiredBytes });
|
|
341
364
|
console.log(` Re-authorization successful`);
|
|
342
365
|
} catch (e) {
|
|
343
366
|
throw new NonRetryableError(`Account ${ss58} has insufficient Bulletin authorization quota and auto-authorization via Alice failed (${e.message}). Authorize the account on-chain.`);
|
|
@@ -368,7 +391,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
368
391
|
sampleMemory(`reconnect_${reconnectionsUsed}_after`);
|
|
369
392
|
}
|
|
370
393
|
try {
|
|
371
|
-
let startNonce = await
|
|
394
|
+
let startNonce = await _fetchNonce(BULLETIN_ENDPOINTS, ss58);
|
|
372
395
|
console.log(` Starting nonce: ${startNonce}`);
|
|
373
396
|
const BATCH_SIZE = 2;
|
|
374
397
|
const MAX_CHUNK_RETRIES = 3;
|
|
@@ -394,7 +417,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
394
417
|
}
|
|
395
418
|
const nonce = assignedNonces.get(i);
|
|
396
419
|
console.log(` [${i + 1}/${chunks.length}] ${(chunkData.length / 1024 / 1024).toFixed(2)} MB (nonce: ${nonce})`);
|
|
397
|
-
return storeChunk(unsafeApi, signer, chunkData, nonce, ss58);
|
|
420
|
+
return storeChunk(unsafeApi, signer, chunkData, nonce, ss58, { fetchNonce: fetchNonceOverride });
|
|
398
421
|
});
|
|
399
422
|
const results = await Promise.allSettled(batchPromises);
|
|
400
423
|
results.forEach((r, j) => {
|
|
@@ -404,11 +427,11 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
404
427
|
}
|
|
405
428
|
});
|
|
406
429
|
const failures = results.map((r, j) => r.status === "rejected" ? { index: batchIndices[j], chunkData: batchChunks[j], error: r.reason } : null).filter(Boolean);
|
|
407
|
-
const
|
|
408
|
-
const needsReconnect = failures.some((f) => isConnectionError(f.error)) ||
|
|
430
|
+
const subscriptionErrored = results.some((r) => r.status === "fulfilled" && r.value.subscriptionError);
|
|
431
|
+
const needsReconnect = failures.some((f) => isConnectionError(f.error)) || subscriptionErrored;
|
|
409
432
|
if (needsReconnect && reconnect && reconnectionsUsed < MAX_RECONNECTIONS) {
|
|
410
433
|
await doReconnect();
|
|
411
|
-
const currentNonce = await
|
|
434
|
+
const currentNonce = await _fetchNonce(BULLETIN_ENDPOINTS, ss58);
|
|
412
435
|
for (const idx of batchIndices) {
|
|
413
436
|
const chunkNonce = assignedNonces.get(idx);
|
|
414
437
|
if (chunkNonce !== void 0 && chunkNonce < currentNonce && stored[idx] === null) {
|
|
@@ -439,7 +462,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
439
462
|
}
|
|
440
463
|
}
|
|
441
464
|
try {
|
|
442
|
-
const currentNonce = await
|
|
465
|
+
const currentNonce = await _fetchNonce(BULLETIN_ENDPOINTS, ss58);
|
|
443
466
|
const originalNonce = assignedNonces.get(fail.index);
|
|
444
467
|
if (originalNonce !== void 0 && originalNonce < currentNonce) {
|
|
445
468
|
console.log(` Chunk ${fail.index + 1}: nonce ${originalNonce} consumed (current=${currentNonce}), treating as included`);
|
|
@@ -449,7 +472,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
449
472
|
break;
|
|
450
473
|
}
|
|
451
474
|
const retryNonce = originalNonce ?? currentNonce;
|
|
452
|
-
const result2 = await storeChunk(unsafeApi, signer, fail.chunkData, retryNonce, ss58);
|
|
475
|
+
const result2 = await storeChunk(unsafeApi, signer, fail.chunkData, retryNonce, ss58, { fetchNonce: fetchNonceOverride });
|
|
453
476
|
stored[fail.index] = result2;
|
|
454
477
|
assignedNonces.delete(fail.index);
|
|
455
478
|
retried = true;
|
|
@@ -495,7 +518,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
495
518
|
const MAX_ROOT_RETRIES = 3;
|
|
496
519
|
let result;
|
|
497
520
|
for (let rootAttempt = 1; rootAttempt <= MAX_ROOT_RETRIES; rootAttempt++) {
|
|
498
|
-
const rootNonce = await
|
|
521
|
+
const rootNonce = await _fetchNonce(BULLETIN_ENDPOINTS, ss58);
|
|
499
522
|
console.log(` Storing root node (nonce: ${rootNonce})...`);
|
|
500
523
|
const rootTx = unsafeApi.tx.TransactionStorage.store_with_cid_config({ cid: { codec: BigInt(112), hashing: toHashingEnum(hashCode) }, data: Binary.fromBytes(dagBytes) });
|
|
501
524
|
const rootTxOpts = { mortality: { mortal: true, period: 256 }, nonce: rootNonce };
|
|
@@ -504,9 +527,9 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
504
527
|
console.log(` Root CID: ${rootCid.toString()}
|
|
505
528
|
`);
|
|
506
529
|
return rootCid.toString();
|
|
507
|
-
}, { label: "root-node", rpc:
|
|
530
|
+
}, { label: "root-node", rpc: BULLETIN_ENDPOINTS, senderSS58: ss58, expectedNonce: rootNonce, timeoutMs: CHUNK_TIMEOUT_MS, fetchNonce: fetchNonceOverride });
|
|
508
531
|
result = watchResult.value;
|
|
509
|
-
if (watchResult.
|
|
532
|
+
if (watchResult.subscriptionError && reconnect && reconnectionsUsed < MAX_RECONNECTIONS) {
|
|
510
533
|
await doReconnect();
|
|
511
534
|
}
|
|
512
535
|
break;
|
|
@@ -665,7 +688,9 @@ async function estimateUploadBytes(content) {
|
|
|
665
688
|
}
|
|
666
689
|
}
|
|
667
690
|
async function deploy(content, domainName = null, options = {}) {
|
|
668
|
-
|
|
691
|
+
const userRpc = options.rpc ?? process.env.BULLETIN_RPC;
|
|
692
|
+
BULLETIN_ENDPOINTS = userRpc ? [userRpc, ...[DEFAULT_BULLETIN_RPC].filter((e) => e !== userRpc)] : [DEFAULT_BULLETIN_RPC];
|
|
693
|
+
_deployRpcFailedOver = false;
|
|
669
694
|
POOL_SIZE = options.poolSize ?? parseInt(process.env.BULLETIN_POOL_SIZE ?? String(DEFAULT_POOL_SIZE), 10);
|
|
670
695
|
initTelemetry();
|
|
671
696
|
const randomSuffix = Math.floor(Math.random() * 100).toString().padStart(2, "0");
|
|
@@ -762,7 +787,7 @@ async function deploy(content, domainName = null, options = {}) {
|
|
|
762
787
|
const uploadBytes = Math.ceil(estimated * 1.2);
|
|
763
788
|
const chunksNeeded = Math.max(1, Math.ceil(uploadBytes / CHUNK_SIZE));
|
|
764
789
|
const needs = { txs: BigInt(chunksNeeded + 2), bytes: BigInt(uploadBytes) };
|
|
765
|
-
await topUpBy(provider.ss58,
|
|
790
|
+
await topUpBy(provider.ss58, BULLETIN_ENDPOINTS[0], needs, "uploader");
|
|
766
791
|
}
|
|
767
792
|
console.log("\n" + "=".repeat(60));
|
|
768
793
|
console.log("Storage");
|
|
@@ -808,7 +833,7 @@ async function deploy(content, domainName = null, options = {}) {
|
|
|
808
833
|
carBytes,
|
|
809
834
|
cid: predictedCid,
|
|
810
835
|
toolVersion: VERSION,
|
|
811
|
-
bulletinRpc:
|
|
836
|
+
bulletinRpc: BULLETIN_ENDPOINTS[0],
|
|
812
837
|
encrypted: Boolean(options.password)
|
|
813
838
|
}).catch((err) => err instanceof Error ? err : new Error(String(err)));
|
|
814
839
|
}
|
|
@@ -939,6 +964,7 @@ async function deploy(content, domainName = null, options = {}) {
|
|
|
939
964
|
console.log("\n" + "=".repeat(60) + "\n");
|
|
940
965
|
return { domainName: name, fullDomain: `${name}.dot`, cid, ipfsCid };
|
|
941
966
|
} finally {
|
|
967
|
+
if (_deployRpcFailedOver) setDeployAttribute("deploy.rpc.failed_over", "true");
|
|
942
968
|
provider?.client.destroy();
|
|
943
969
|
}
|
|
944
970
|
});
|
|
@@ -950,6 +976,7 @@ export {
|
|
|
950
976
|
friendlyChainError,
|
|
951
977
|
DEFAULT_BULLETIN_RPC,
|
|
952
978
|
DEFAULT_POOL_SIZE,
|
|
979
|
+
CHUNK_MORTALITY_PERIOD,
|
|
953
980
|
isConnectionError,
|
|
954
981
|
deriveRootSigner,
|
|
955
982
|
createCID,
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// src/run-state.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
|
|
6
|
+
// package.json
|
|
7
|
+
var package_default = {
|
|
8
|
+
name: "bulletin-deploy",
|
|
9
|
+
version: "0.7.4",
|
|
10
|
+
private: false,
|
|
11
|
+
repository: {
|
|
12
|
+
type: "git",
|
|
13
|
+
url: "https://github.com/paritytech/bulletin-deploy.git"
|
|
14
|
+
},
|
|
15
|
+
publishConfig: {
|
|
16
|
+
registry: "https://registry.npmjs.org",
|
|
17
|
+
access: "public"
|
|
18
|
+
},
|
|
19
|
+
type: "module",
|
|
20
|
+
main: "./dist/index.js",
|
|
21
|
+
types: "./dist/index.d.ts",
|
|
22
|
+
bin: {
|
|
23
|
+
"bulletin-deploy": "./bin/bulletin-deploy"
|
|
24
|
+
},
|
|
25
|
+
exports: {
|
|
26
|
+
".": {
|
|
27
|
+
types: "./dist/index.d.ts",
|
|
28
|
+
import: "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
files: [
|
|
32
|
+
"dist",
|
|
33
|
+
"bin"
|
|
34
|
+
],
|
|
35
|
+
scripts: {
|
|
36
|
+
build: "tsup src/index.ts src/deploy.ts src/dotns.ts src/pool.ts src/telemetry.ts src/memory-report.ts src/merkle.ts src/gh-pages-mirror.ts src/version-check.ts src/bug-report.ts src/run-state.ts --format esm --dts --clean --target node22",
|
|
37
|
+
prepare: "npm run build",
|
|
38
|
+
test: "npm run build && node --test test/test.js test/pool.test.js test/helpers/e2e-helpers.test.js",
|
|
39
|
+
"test:e2e": "npm run build && node --test test/e2e.test.js",
|
|
40
|
+
"test:e2e:smoke": "bash scripts/e2e-pass.sh smoke",
|
|
41
|
+
"test:e2e:pr": "bash scripts/e2e-pass.sh pr",
|
|
42
|
+
"test:e2e:nightly": "bash scripts/e2e-pass.sh nightly",
|
|
43
|
+
benchmark: "npm run build && node benchmark.js"
|
|
44
|
+
},
|
|
45
|
+
dependencies: {
|
|
46
|
+
"@ipld/car": "^5.4.3",
|
|
47
|
+
"@ipld/dag-pb": "^4.1.3",
|
|
48
|
+
"@noble/hashes": "^1.7.2",
|
|
49
|
+
"@parity/dotns-cli": "0.5.6",
|
|
50
|
+
"@polkadot-api/substrate-bindings": "^0.16.5",
|
|
51
|
+
"@polkadot-labs/hdkd": "^0.0.25",
|
|
52
|
+
"@polkadot-labs/hdkd-helpers": "^0.0.26",
|
|
53
|
+
"@polkadot/keyring": "^13.0.0",
|
|
54
|
+
"@polkadot/util-crypto": "^13.0.0",
|
|
55
|
+
"@sentry/node": "^9.14.0",
|
|
56
|
+
"ipfs-unixfs": "^11.2.0",
|
|
57
|
+
"ipfs-unixfs-importer": "^16.1.4",
|
|
58
|
+
multiformats: "^13.4.1",
|
|
59
|
+
"polkadot-api": "^1.23.1",
|
|
60
|
+
viem: "^2.30.5"
|
|
61
|
+
},
|
|
62
|
+
devDependencies: {
|
|
63
|
+
"@types/node": "^22.0.0",
|
|
64
|
+
tsup: "^8.5.0",
|
|
65
|
+
typescript: "^5.9.3"
|
|
66
|
+
},
|
|
67
|
+
minimumVersion: "0.5.6",
|
|
68
|
+
engines: {
|
|
69
|
+
node: ">=22"
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/run-state.ts
|
|
74
|
+
var VERSION = package_default.version;
|
|
75
|
+
function resolveStateDir() {
|
|
76
|
+
if (process.platform === "darwin") {
|
|
77
|
+
return path.join(os.homedir(), "Library", "Application Support", "bulletin-deploy");
|
|
78
|
+
}
|
|
79
|
+
if (process.platform === "win32") {
|
|
80
|
+
const base2 = process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local");
|
|
81
|
+
return path.join(base2, "bulletin-deploy");
|
|
82
|
+
}
|
|
83
|
+
const base = process.env.XDG_STATE_HOME && process.env.XDG_STATE_HOME.length > 0 ? process.env.XDG_STATE_HOME : path.join(os.homedir(), ".local", "state");
|
|
84
|
+
return path.join(base, "bulletin-deploy");
|
|
85
|
+
}
|
|
86
|
+
function stateFilePath() {
|
|
87
|
+
return path.join(resolveStateDir(), "last-run.json");
|
|
88
|
+
}
|
|
89
|
+
function loadRunState() {
|
|
90
|
+
try {
|
|
91
|
+
const raw = fs.readFileSync(stateFilePath(), "utf-8");
|
|
92
|
+
const parsed = JSON.parse(raw);
|
|
93
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
94
|
+
return parsed;
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function writeRunState(patch) {
|
|
100
|
+
try {
|
|
101
|
+
const dir = resolveStateDir();
|
|
102
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
103
|
+
const file = stateFilePath();
|
|
104
|
+
let existing = {};
|
|
105
|
+
try {
|
|
106
|
+
const raw = fs.readFileSync(file, "utf-8");
|
|
107
|
+
const parsed = JSON.parse(raw);
|
|
108
|
+
if (parsed && typeof parsed === "object") existing = parsed;
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
const merged = { ...existing, ...patch };
|
|
112
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
113
|
+
fs.writeFileSync(tmp, JSON.stringify(merged), { encoding: "utf-8" });
|
|
114
|
+
fs.renameSync(tmp, file);
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function isPidAlive(pid) {
|
|
119
|
+
try {
|
|
120
|
+
process.kill(pid, 0);
|
|
121
|
+
return true;
|
|
122
|
+
} catch (err) {
|
|
123
|
+
const code = err.code;
|
|
124
|
+
if (code === "EPERM") return true;
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function shouldSkipStaleWarning(prev) {
|
|
129
|
+
if (prev.pid && isPidAlive(prev.pid)) return true;
|
|
130
|
+
if (prev.toolVersion !== VERSION) return true;
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
function probablyOomRssMb(override) {
|
|
134
|
+
if (typeof override === "number" && Number.isFinite(override)) return override;
|
|
135
|
+
const env = process.env.BULLETIN_DEPLOY_OOM_HINT_RSS_MB;
|
|
136
|
+
const parsed = env != null ? Number(env) : NaN;
|
|
137
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
138
|
+
return 1800;
|
|
139
|
+
}
|
|
140
|
+
function shouldShowOomHint(prev) {
|
|
141
|
+
if (prev.lastPeakRssMb == null) return false;
|
|
142
|
+
return prev.lastPeakRssMb >= probablyOomRssMb();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export {
|
|
146
|
+
package_default,
|
|
147
|
+
VERSION,
|
|
148
|
+
resolveStateDir,
|
|
149
|
+
stateFilePath,
|
|
150
|
+
loadRunState,
|
|
151
|
+
writeRunState,
|
|
152
|
+
shouldSkipStaleWarning,
|
|
153
|
+
probablyOomRssMb,
|
|
154
|
+
shouldShowOomHint
|
|
155
|
+
};
|