@parity/product-deploy 0.11.0 → 0.12.0-rc.2
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/bin/bulletin-bootstrap +23 -1
- package/dist/auth-config.js +3 -3
- package/dist/bug-report.js +4 -4
- package/dist/{chunk-SES7CGNY.js → chunk-DDC6NKE3.js} +1 -1
- package/dist/{chunk-TE2YKRYY.js → chunk-F4O42MUQ.js} +2 -2
- package/dist/{chunk-HLXRVFKL.js → chunk-FGCBIUMT.js} +38 -1
- package/dist/{chunk-HTZGBUZA.js → chunk-IR5BHZ2S.js} +3 -3
- package/dist/{chunk-7RYEYWYO.js → chunk-JGS6QR5X.js} +1 -1
- package/dist/{chunk-53BU4X2R.js → chunk-LRJTODL2.js} +12 -2
- package/dist/{chunk-UUJFLJKY.js → chunk-N7KXNIH4.js} +158 -61
- package/dist/{chunk-JWMHCW74.js → chunk-NRSIBGBP.js} +1 -1
- package/dist/{chunk-O4I37F5K.js → chunk-S62H6TWE.js} +55 -12
- package/dist/{chunk-7E33R3OQ.js → chunk-UPA5PEQA.js} +1 -1
- package/dist/{chunk-QDHLTSEO.js → chunk-WVVRMGKN.js} +2 -2
- package/dist/{chunk-MRQPJLPS.js → chunk-XVQICFTB.js} +22 -1
- package/dist/chunk-probe.d.ts +43 -1
- package/dist/chunk-probe.js +11 -5
- package/dist/commands/login.js +11 -11
- package/dist/commands/logout.js +4 -4
- package/dist/commands/transfer.js +4 -4
- package/dist/commands/whoami.js +3 -3
- package/dist/deploy-actors.js +6 -6
- package/dist/deploy.d.ts +45 -1
- package/dist/deploy.js +17 -11
- package/dist/dotns.d.ts +33 -1
- package/dist/dotns.js +14 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +18 -12
- package/dist/manifest/publish.js +12 -12
- package/dist/memory-report.js +2 -2
- package/dist/merkle.js +11 -11
- package/dist/personhood/bootstrap.js +4 -4
- package/dist/personhood/people-client.js +4 -4
- package/dist/personhood/reprove.js +19 -2
- package/dist/pool.d.ts +11 -1
- package/dist/pool.js +9 -1
- package/dist/run-state.js +1 -1
- package/dist/sss-allowance-cache.js +4 -4
- package/dist/storage-signer.d.ts +28 -3
- package/dist/storage-signer.js +13 -11
- package/dist/telemetry.js +2 -2
- package/dist/version-check.js +3 -3
- package/package.json +1 -1
- package/tools/release-retry-wrapper.mjs +6 -0
package/bin/bulletin-bootstrap
CHANGED
|
@@ -4,7 +4,7 @@ import * as fs from "node:fs/promises";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE } from "../dist/deploy.js";
|
|
7
|
-
import { bootstrapPool } from "../dist/pool.js";
|
|
7
|
+
import { bootstrapPool, BULLETIN_BLOCKS_PER_DAY, isAutoReauthorizeAllowed } from "../dist/pool.js";
|
|
8
8
|
import { VERSION } from "../dist/telemetry.js";
|
|
9
9
|
|
|
10
10
|
const args = process.argv.slice(2);
|
|
@@ -16,6 +16,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
16
16
|
else if (args[i] === "--authorizer") { flags.authorizer = args[++i]; }
|
|
17
17
|
else if (args[i] === "--rpc") { flags.rpc = args[++i]; }
|
|
18
18
|
else if (args[i] === "--env") { flags.env = args[++i]; }
|
|
19
|
+
else if (args[i] === "--pre-check") { flags.preCheck = true; }
|
|
19
20
|
else if (args[i] === "--version" || args[i] === "-V") { flags.version = true; }
|
|
20
21
|
else if (args[i] === "--help" || args[i] === "-h") { flags.help = true; }
|
|
21
22
|
else {
|
|
@@ -52,11 +53,22 @@ Options:
|
|
|
52
53
|
--rpc wss://... Bulletin RPC endpoint (or set BULLETIN_RPC env var)
|
|
53
54
|
--env <id> Load environment by id from environments.json
|
|
54
55
|
--pool-size N Number of pool accounts to check/initialize (default: 10)
|
|
56
|
+
--pre-check E2E pre-flight mode (#1059): renew any account expiring
|
|
57
|
+
within 24h, not just already-expired ones. Requires
|
|
58
|
+
--env, and is hard-gated to environments where
|
|
59
|
+
environments.json has network != "mainnet" and
|
|
60
|
+
bulletinAutoAuthorize: true — refuses to write
|
|
61
|
+
otherwise, even with --authorizer supplied.
|
|
55
62
|
--version Show version
|
|
56
63
|
--help Show this help`);
|
|
57
64
|
process.exit(0);
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
if (flags.preCheck && !flags.env) {
|
|
68
|
+
console.error("Error: --pre-check requires --env <id>");
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
60
72
|
let rpc = flags.rpc ?? process.env.BULLETIN_RPC ?? DEFAULT_BULLETIN_RPC;
|
|
61
73
|
const poolSize = flags.poolSize ?? parseInt(process.env.BULLETIN_POOL_SIZE ?? String(DEFAULT_POOL_SIZE), 10);
|
|
62
74
|
const mnemonic = flags.mnemonic ?? process.env.BULLETIN_POOL_MNEMONIC ?? process.env.MNEMONIC;
|
|
@@ -94,6 +106,16 @@ if (flags.env) {
|
|
|
94
106
|
}
|
|
95
107
|
|
|
96
108
|
bootstrapOpts.bulletinAuthorizeV2 = envEntry.bulletinAuthorizeV2 ?? false;
|
|
109
|
+
|
|
110
|
+
if (flags.preCheck) {
|
|
111
|
+
// #1059: widen the "needs authorization" threshold to 24h-before-expiry,
|
|
112
|
+
// and hard-gate the actual write to environments cleared for it. This is
|
|
113
|
+
// a config-driven (environments.json) gate, not a CLI flag override —
|
|
114
|
+
// --pre-check can never itself grant write access on an env that isn't
|
|
115
|
+
// eligible.
|
|
116
|
+
bootstrapOpts.reauthBufferBlocks = BULLETIN_BLOCKS_PER_DAY;
|
|
117
|
+
bootstrapOpts.allowAutoReauthorize = isAutoReauthorizeAllowed(envEntry);
|
|
118
|
+
}
|
|
97
119
|
}
|
|
98
120
|
|
|
99
121
|
await bootstrapPool(rpc, poolSize, mnemonic, bootstrapOpts);
|
package/dist/auth-config.js
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
getPeopleChainEndpoints,
|
|
10
10
|
hasPersistedSession,
|
|
11
11
|
resolveBulletinEndpoints
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-NRSIBGBP.js";
|
|
13
13
|
import "./chunk-TSPERKUS.js";
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-LRJTODL2.js";
|
|
15
|
+
import "./chunk-DDC6NKE3.js";
|
|
16
16
|
import "./chunk-JSYQ3JQS.js";
|
|
17
17
|
import "./chunk-ZOC4GITL.js";
|
|
18
18
|
export {
|
package/dist/bug-report.js
CHANGED
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
offerBugReport,
|
|
11
11
|
scrubSecrets,
|
|
12
12
|
setDeployContext
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-IR5BHZ2S.js";
|
|
14
|
+
import "./chunk-UPA5PEQA.js";
|
|
15
|
+
import "./chunk-LRJTODL2.js";
|
|
16
|
+
import "./chunk-DDC6NKE3.js";
|
|
17
17
|
export {
|
|
18
18
|
buildCliFlagsSummary,
|
|
19
19
|
buildLabels,
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
resolveDotnsConnectOptions,
|
|
8
8
|
storeDirectory,
|
|
9
9
|
storeFile
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-N7KXNIH4.js";
|
|
11
11
|
import {
|
|
12
12
|
DotNS
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-S62H6TWE.js";
|
|
14
14
|
import {
|
|
15
15
|
getPopSelfServeConfig,
|
|
16
16
|
loadEnvironments,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
captureWarning
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LRJTODL2.js";
|
|
4
4
|
|
|
5
5
|
// src/chunk-probe.ts
|
|
6
6
|
import { Twox128, Blake2128Concat, decAnyMetadata, unifyMetadata } from "@polkadot-api/substrate-bindings";
|
|
@@ -166,6 +166,40 @@ async function probeChunks(cids, options) {
|
|
|
166
166
|
}
|
|
167
167
|
return results;
|
|
168
168
|
}
|
|
169
|
+
function classifyFinalityGap(missingAtFinalized, bestBlockResults) {
|
|
170
|
+
const presentAtBest = new Set(
|
|
171
|
+
bestBlockResults.filter((r) => r.present === true).map((r) => r.cid)
|
|
172
|
+
);
|
|
173
|
+
const lagging = [];
|
|
174
|
+
const reallyMissing = [];
|
|
175
|
+
for (const cid of missingAtFinalized) {
|
|
176
|
+
if (presentAtBest.has(cid)) lagging.push(cid);
|
|
177
|
+
else reallyMissing.push(cid);
|
|
178
|
+
}
|
|
179
|
+
return { reallyMissing, lagging };
|
|
180
|
+
}
|
|
181
|
+
async function probeFinalityGap(missingAtFinalized, options) {
|
|
182
|
+
if (missingAtFinalized.length === 0) return { reallyMissing: [], lagging: [] };
|
|
183
|
+
let bestBlockResults = await probeChunks(missingAtFinalized, { ...options, atFinalized: false });
|
|
184
|
+
const indeterminate = bestBlockResults.filter((r) => r.present === null).map((r) => r.cid);
|
|
185
|
+
if (indeterminate.length > 0) {
|
|
186
|
+
const retry = await probeChunks(indeterminate, { ...options, atFinalized: false });
|
|
187
|
+
const retryByCid = new Map(retry.map((r) => [r.cid, r]));
|
|
188
|
+
bestBlockResults = bestBlockResults.map((r) => r.present === null ? retryByCid.get(r.cid) ?? r : r);
|
|
189
|
+
}
|
|
190
|
+
return classifyFinalityGap(missingAtFinalized, bestBlockResults);
|
|
191
|
+
}
|
|
192
|
+
async function getBestBlockNumber(client) {
|
|
193
|
+
try {
|
|
194
|
+
const header = await client._request("chain_getHeader", []);
|
|
195
|
+
const hex = header?.number;
|
|
196
|
+
if (typeof hex !== "string") return null;
|
|
197
|
+
const n = parseInt(hex, 16);
|
|
198
|
+
return Number.isFinite(n) ? n : null;
|
|
199
|
+
} catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
169
203
|
function _resetProbeSession() {
|
|
170
204
|
_metadataChecked = false;
|
|
171
205
|
_crossValidated = false;
|
|
@@ -180,6 +214,9 @@ export {
|
|
|
180
214
|
ChainProbeCrossValidationError,
|
|
181
215
|
_decodeStorageValue,
|
|
182
216
|
probeChunks,
|
|
217
|
+
classifyFinalityGap,
|
|
218
|
+
probeFinalityGap,
|
|
219
|
+
getBestBlockNumber,
|
|
183
220
|
_resetProbeSession,
|
|
184
221
|
_bypassMetadataCheckForTest
|
|
185
222
|
};
|
|
@@ -2,15 +2,15 @@ import {
|
|
|
2
2
|
classifyErrorArea,
|
|
3
3
|
isInteractive,
|
|
4
4
|
promptYesNo
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UPA5PEQA.js";
|
|
6
6
|
import {
|
|
7
7
|
VERSION,
|
|
8
8
|
getCurrentSentryTraceId,
|
|
9
9
|
resolveIssueRepoSlug
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LRJTODL2.js";
|
|
11
11
|
import {
|
|
12
12
|
package_default
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-DDC6NKE3.js";
|
|
14
14
|
|
|
15
15
|
// src/bug-report.ts
|
|
16
16
|
import { execSync, execFileSync } from "child_process";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
package_default,
|
|
3
3
|
writeRunState
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DDC6NKE3.js";
|
|
5
5
|
|
|
6
6
|
// src/memory-report.ts
|
|
7
7
|
import * as fs2 from "fs";
|
|
@@ -241,7 +241,11 @@ var DEPLOY_SEED_STORAGE = {
|
|
|
241
241
|
};
|
|
242
242
|
var DEPLOY_SEED_PROBE = {
|
|
243
243
|
"deploy.probe.finality_miss_count": 0,
|
|
244
|
-
"deploy.probe.finality_miss_reupload_count": 0
|
|
244
|
+
"deploy.probe.finality_miss_reupload_count": 0,
|
|
245
|
+
// #1049: chunks present at best-block but not yet at finalised head.
|
|
246
|
+
// These are NEVER re-uploaded — only tracked so we can see finality lag
|
|
247
|
+
// without conflating it with genuinely-dropped (re-uploaded) chunks.
|
|
248
|
+
"deploy.probe.finality_lagging_count": 0
|
|
245
249
|
};
|
|
246
250
|
var DEPLOY_SEED_POOL = {
|
|
247
251
|
"deploy.pool.eligible_count": 0,
|
|
@@ -337,6 +341,12 @@ var ERROR_KIND_RULES = [
|
|
|
337
341
|
[/requires ProofOfPersonhood(?:Full|Lite|Light),\s*but this signer is NoStatus/i, "naming.pop_required"],
|
|
338
342
|
[/requires NoStatus,\s*but this signer is ProofOfPersonhood/i, "naming.nostatus_required"],
|
|
339
343
|
[/Cannot decode zero data.*with ABI parameters/i, "naming.contract_unavailable"],
|
|
344
|
+
// Issue #1060: contractCall's own empty-`0x`-data guard (src/dotns.ts, #729) throws
|
|
345
|
+
// an actionable wrapper instead of letting the raw viem message above reach a
|
|
346
|
+
// caller. Same failure family (a DotNS contract read came back empty) — classify
|
|
347
|
+
// it the same way instead of letting it fall into 'unknown'.
|
|
348
|
+
[/No contract deployed at .+ returned empty success data/i, "naming.contract_unavailable"],
|
|
349
|
+
[/Contract call returned empty data — contract=/i, "naming.contract_unavailable"],
|
|
340
350
|
[/Domain\s+\S+\.dot\s+is already owned by\s+0x[a-fA-F0-9]+/i, "naming.already_owned"],
|
|
341
351
|
[/Cannot deploy\s+[\w.-]+\.dot:\s*parent\s+[\w.-]+\.dot\s+is owned by/i, "naming.subdomain_orphan"],
|
|
342
352
|
[/Post-deploy verification failed for .+: on-chain contenthash is /i, "verify.contenthash_mismatch"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
preflightSssAllowance
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JGS6QR5X.js";
|
|
4
4
|
import {
|
|
5
5
|
statementSigningAccount
|
|
6
6
|
} from "./chunk-GRPLHUYC.js";
|
|
@@ -31,16 +31,18 @@ import {
|
|
|
31
31
|
} from "./chunk-S7EM5VMW.js";
|
|
32
32
|
import {
|
|
33
33
|
setDeployContext
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-IR5BHZ2S.js";
|
|
35
35
|
import {
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
getBestBlockNumber,
|
|
37
|
+
probeChunks,
|
|
38
|
+
probeFinalityGap
|
|
39
|
+
} from "./chunk-FGCBIUMT.js";
|
|
38
40
|
import {
|
|
39
41
|
packSection
|
|
40
42
|
} from "./chunk-C2TS5MER.js";
|
|
41
43
|
import {
|
|
42
44
|
resolveStorageSigner
|
|
43
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-WVVRMGKN.js";
|
|
44
46
|
import {
|
|
45
47
|
createSlotAccountSigner,
|
|
46
48
|
requestResourceAllocation
|
|
@@ -49,7 +51,7 @@ import {
|
|
|
49
51
|
STALE_SESSION_MESSAGE,
|
|
50
52
|
getPeopleChainEndpoints,
|
|
51
53
|
hasPersistedSession
|
|
52
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-NRSIBGBP.js";
|
|
53
55
|
import {
|
|
54
56
|
CLI_NAME
|
|
55
57
|
} from "./chunk-TSPERKUS.js";
|
|
@@ -62,7 +64,7 @@ import {
|
|
|
62
64
|
parseDomainName,
|
|
63
65
|
popStatusName,
|
|
64
66
|
verifyNonceAdvanced
|
|
65
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-S62H6TWE.js";
|
|
66
68
|
import {
|
|
67
69
|
derivePoolAccounts,
|
|
68
70
|
detectTestnet,
|
|
@@ -70,7 +72,7 @@ import {
|
|
|
70
72
|
fetchPoolAuthorizations,
|
|
71
73
|
isAuthorizationSufficient,
|
|
72
74
|
selectAccount
|
|
73
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-XVQICFTB.js";
|
|
74
76
|
import {
|
|
75
77
|
VERSION,
|
|
76
78
|
captureWarning,
|
|
@@ -84,7 +86,7 @@ import {
|
|
|
84
86
|
truncateAddress,
|
|
85
87
|
withDeploySpan,
|
|
86
88
|
withSpan
|
|
87
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-LRJTODL2.js";
|
|
88
90
|
import {
|
|
89
91
|
DEFAULT_ENV_ID,
|
|
90
92
|
getPopSelfServeConfig,
|
|
@@ -339,27 +341,58 @@ async function waitForBulletinAuthorization(ss58, opts = {}) {
|
|
|
339
341
|
client.destroy();
|
|
340
342
|
}
|
|
341
343
|
}
|
|
342
|
-
async function
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
{
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
344
|
+
async function withTransientRetry(attempt, opts = {}) {
|
|
345
|
+
const { retries = 2, delayMs = 1e3 } = opts;
|
|
346
|
+
const debug = Boolean(process.env.DOT_DEBUG);
|
|
347
|
+
let lastErr;
|
|
348
|
+
for (let i = 0; i <= retries; i++) {
|
|
349
|
+
try {
|
|
350
|
+
return await attempt();
|
|
351
|
+
} catch (e) {
|
|
352
|
+
if (e instanceof BulletinSlotAuthError) throw e;
|
|
353
|
+
lastErr = e;
|
|
354
|
+
if (i < retries) {
|
|
355
|
+
if (debug) {
|
|
356
|
+
console.error(
|
|
357
|
+
`[slot-signer] transient error (attempt ${i + 1}/${retries + 1}): ${e instanceof Error ? e.message : String(e)}`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
358
363
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return
|
|
364
|
+
throw lastErr;
|
|
365
|
+
}
|
|
366
|
+
async function getSlotSignerProvider(signer, ss58) {
|
|
367
|
+
return withTransientRetry(async () => {
|
|
368
|
+
const primary = BULLETIN_ENDPOINTS[0];
|
|
369
|
+
console.log(` Connecting to Bulletin (slot signer): ${primary}`);
|
|
370
|
+
const client = createPolkadotClient(getWsProvider(
|
|
371
|
+
BULLETIN_ENDPOINTS,
|
|
372
|
+
{ heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
|
|
373
|
+
));
|
|
374
|
+
const unsafeApi = client.getUnsafeApi();
|
|
375
|
+
let auth;
|
|
376
|
+
let currentBlock;
|
|
377
|
+
try {
|
|
378
|
+
[auth, currentBlock] = await Promise.all([
|
|
379
|
+
unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum("Account", ss58)),
|
|
380
|
+
client.getFinalizedBlock()
|
|
381
|
+
]);
|
|
382
|
+
} catch (e) {
|
|
383
|
+
client.destroy();
|
|
384
|
+
throw e;
|
|
385
|
+
}
|
|
386
|
+
const result = isBulletinAuthActive(auth, currentBlock.number);
|
|
387
|
+
if (!result.active) {
|
|
388
|
+
client.destroy();
|
|
389
|
+
throw new BulletinSlotAuthError(result.reason, ss58, result.expiration);
|
|
390
|
+
}
|
|
391
|
+
console.log(` Using slot signer: ${ss58} (authorized until block ${result.expiration})`);
|
|
392
|
+
setDeployAttribute("deploy.signer.mode", "slot");
|
|
393
|
+
setDeployAttribute("deploy.signer.address", truncateAddress(ss58));
|
|
394
|
+
return { client, unsafeApi, signer, ss58 };
|
|
395
|
+
});
|
|
363
396
|
}
|
|
364
397
|
|
|
365
398
|
// src/deploy.ts
|
|
@@ -400,15 +433,28 @@ var MAX_RECONNECTIONS = parseInt(process.env.BULLETIN_MAX_RECONNECTIONS ?? "3",
|
|
|
400
433
|
var CHUNK_TIMEOUT_MS = parseInt(process.env.BULLETIN_CHUNK_TIMEOUT_MS ?? "180000", 10);
|
|
401
434
|
var CHUNK_MORTALITY_PERIOD = (() => {
|
|
402
435
|
const v = parseInt(process.env.BULLETIN_CHUNK_MORTALITY_PERIOD ?? "", 10);
|
|
403
|
-
return Number.isFinite(v) && v > 0 ? v :
|
|
436
|
+
return Number.isFinite(v) && v > 0 ? v : 64;
|
|
404
437
|
})();
|
|
438
|
+
var CHUNK_LIVENESS_MAX_WAIT_MS = parseInt(process.env.BULLETIN_CHUNK_LIVENESS_MAX_WAIT_MS ?? "60000", 10);
|
|
439
|
+
var CHUNK_LIVENESS_POLL_MS = 5e3;
|
|
405
440
|
var RETRY_BASE_DELAY_MS = 2e3;
|
|
406
441
|
var RETRY_MAX_DELAY_MS = 15e3;
|
|
407
442
|
var WS_HEARTBEAT_TIMEOUT_MS = 3e5;
|
|
408
|
-
var GRANDPA_NATURAL_WAIT_MS = parseInt(process.env.BULLETIN_GRANDPA_NATURAL_WAIT_MS ?? "
|
|
443
|
+
var GRANDPA_NATURAL_WAIT_MS = parseInt(process.env.BULLETIN_GRANDPA_NATURAL_WAIT_MS ?? "210000", 10);
|
|
409
444
|
var GRANDPA_REUPLOAD_POLL_MS = 5e3;
|
|
410
|
-
var GRANDPA_REUPLOAD_TIMEOUT_MS =
|
|
445
|
+
var GRANDPA_REUPLOAD_TIMEOUT_MS = parseInt(process.env.BULLETIN_GRANDPA_REUPLOAD_TIMEOUT_MS ?? "120000", 10);
|
|
411
446
|
var GRANDPA_REUPLOAD_MAX_ROUNDS = 3;
|
|
447
|
+
var GRANDPA_LAGGING_WAIT_MS = parseInt(process.env.BULLETIN_GRANDPA_LAGGING_WAIT_MS ?? "90000", 10);
|
|
448
|
+
async function pollUntilFinalized(cids, timeoutMs, client) {
|
|
449
|
+
const start = Date.now();
|
|
450
|
+
while (Date.now() - start < timeoutMs && cids.size > 0) {
|
|
451
|
+
await new Promise((r) => setTimeout(r, GRANDPA_REUPLOAD_POLL_MS));
|
|
452
|
+
const poll = await probeChunks([...cids], { client, atFinalized: true });
|
|
453
|
+
for (const r of poll) {
|
|
454
|
+
if (r.present === true) cids.delete(r.cid);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
412
458
|
var RETRY_BUDGET_MAX_EVENTS = parseInt(process.env.BULLETIN_RETRY_BUDGET_MAX ?? "5", 10);
|
|
413
459
|
var RETRY_BUDGET_WINDOW_MS = parseInt(process.env.BULLETIN_RETRY_BUDGET_WINDOW_MS ?? "30000", 10);
|
|
414
460
|
function retryBudgetExhausted(history, maxEvents, windowMs, now = Date.now()) {
|
|
@@ -621,6 +667,12 @@ function formatTransferModeStorageSignerLine(workerAddress) {
|
|
|
621
667
|
function formatTransferModeDotnsLine(alreadyOwned, dotName, recipient) {
|
|
622
668
|
return alreadyOwned ? ` DotNS: you already own ${dotName} \u2014 content update needs your phone signature (no transfer)` : ` DotNS: will register ${dotName} and transfer it to your account ${recipient}`;
|
|
623
669
|
}
|
|
670
|
+
function describeSlotFallbackReason(e) {
|
|
671
|
+
if (e instanceof BulletinSlotAuthError) {
|
|
672
|
+
return e.reason === "expired" && e.expiration != null ? `expired at block ${e.expiration}` : "no on-chain authorization found";
|
|
673
|
+
}
|
|
674
|
+
return e instanceof Error ? e.message : String(e);
|
|
675
|
+
}
|
|
624
676
|
function selectStorageReconnect(options) {
|
|
625
677
|
if (options.storageSigner && options.storageSignerAddress) {
|
|
626
678
|
let useSlot = true;
|
|
@@ -631,12 +683,8 @@ function selectStorageReconnect(options) {
|
|
|
631
683
|
} catch (e) {
|
|
632
684
|
useSlot = false;
|
|
633
685
|
setDeployAttribute("deploy.signer.mode", "pool-fallback");
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
reason = e.reason === "expired" && e.expiration != null ? `expired at block ${e.expiration}` : "no on-chain authorization found";
|
|
637
|
-
} else {
|
|
638
|
-
reason = e instanceof Error ? e.message : String(e);
|
|
639
|
-
}
|
|
686
|
+
const reason = describeSlotFallbackReason(e);
|
|
687
|
+
setDeployAttribute("deploy.signer.fallback_reason", reason);
|
|
640
688
|
console.warn(
|
|
641
689
|
`\u26A0 Bulletin allowance slot not usable: ${reason}
|
|
642
690
|
Falling back to the shared pool account for storage (fine on testnet).
|
|
@@ -713,6 +761,26 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
|
|
|
713
761
|
});
|
|
714
762
|
});
|
|
715
763
|
}
|
|
764
|
+
function reconcileTimedOutChunk(opts) {
|
|
765
|
+
const { originalNonce, currentNonce, nonceHeuristicValid, cidPresentAtBest } = opts;
|
|
766
|
+
if (cidPresentAtBest === true) return true;
|
|
767
|
+
if (nonceHeuristicValid && originalNonce !== void 0 && originalNonce < currentNonce) return true;
|
|
768
|
+
return false;
|
|
769
|
+
}
|
|
770
|
+
async function waitForChainLiveness(client, lastHeight, timeoutMs, pollMs = CHUNK_LIVENESS_POLL_MS) {
|
|
771
|
+
if (lastHeight == null) return getBestBlockNumber(client);
|
|
772
|
+
const deadline = Date.now() + timeoutMs;
|
|
773
|
+
let height = lastHeight;
|
|
774
|
+
while (Date.now() < deadline) {
|
|
775
|
+
const h = await getBestBlockNumber(client);
|
|
776
|
+
if (h == null) return height;
|
|
777
|
+
height = h;
|
|
778
|
+
if (height > lastHeight) return height;
|
|
779
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
780
|
+
}
|
|
781
|
+
return height;
|
|
782
|
+
}
|
|
783
|
+
var __waitForChainLivenessForTest = waitForChainLiveness;
|
|
716
784
|
async function storeChunk(unsafeApi, signer, chunkBytes, nonce, ss58, opts = {}) {
|
|
717
785
|
const cid = createCID(chunkBytes, CID_CONFIG.codec, CID_CONFIG.hashCode);
|
|
718
786
|
const tx = unsafeApi.tx.TransactionStorage.store_with_cid_config({ cid: { codec: BigInt(CID_CONFIG.codec), hashing: toHashingEnum(CID_CONFIG.hashCode) }, data: chunkBytes });
|
|
@@ -1062,8 +1130,14 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
1062
1130
|
try {
|
|
1063
1131
|
const currentNonce = await _fetchNonce(BULLETIN_ENDPOINTS, ss58);
|
|
1064
1132
|
const originalNonce = assignedNonces.get(fail.index);
|
|
1065
|
-
|
|
1066
|
-
|
|
1133
|
+
let cidPresentAtBest = null;
|
|
1134
|
+
try {
|
|
1135
|
+
const [probe] = await probeChunks([failCid.toString()], { client });
|
|
1136
|
+
cidPresentAtBest = probe.present === true ? true : null;
|
|
1137
|
+
} catch {
|
|
1138
|
+
}
|
|
1139
|
+
if (reconcileTimedOutChunk({ originalNonce, currentNonce, nonceHeuristicValid: !perRetryChanged, cidPresentAtBest })) {
|
|
1140
|
+
console.log(` Chunk ${fail.index}: reconcile found it already included (nonce ${originalNonce}\u2192${currentNonce}${cidPresentAtBest ? ", CID present at best-block" : ""}) \u2014 skipping resubmit`);
|
|
1067
1141
|
stored[fail.index] = { cid: createCID(fail.chunkData, CID_CONFIG.codec, 18), len: fail.chunkData.length, viaFallback: true };
|
|
1068
1142
|
nonceAdvanceIndices.add(fail.index);
|
|
1069
1143
|
assignedNonces.delete(fail.index);
|
|
@@ -1071,6 +1145,11 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
1071
1145
|
retried = true;
|
|
1072
1146
|
break;
|
|
1073
1147
|
}
|
|
1148
|
+
const heightBefore = await getBestBlockNumber(client);
|
|
1149
|
+
const heightAfter = await waitForChainLiveness(client, heightBefore, CHUNK_LIVENESS_MAX_WAIT_MS);
|
|
1150
|
+
if (heightBefore != null && heightAfter != null && heightAfter <= heightBefore) {
|
|
1151
|
+
console.log(` Chunk ${fail.index}: chain still frozen at block ${heightBefore} after ${(CHUNK_LIVENESS_MAX_WAIT_MS / 1e3).toFixed(0)}s wait \u2014 resubmitting anyway`);
|
|
1152
|
+
}
|
|
1074
1153
|
const retryNonce = originalNonce ?? currentNonce;
|
|
1075
1154
|
const result2 = await storeChunk(unsafeApi, signer, fail.chunkData, retryNonce, ss58, { fetchNonce: fetchNonceOverride });
|
|
1076
1155
|
stored[fail.index] = result2;
|
|
@@ -1662,23 +1741,27 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
1662
1741
|
let missingCids = new Set(finalityResults.filter((r) => r.present === false).map((r) => r.cid));
|
|
1663
1742
|
setDeployAttribute("deploy.probe.finality_miss_count", missingCids.size);
|
|
1664
1743
|
let reuploadCount = 0;
|
|
1744
|
+
let laggingFinalityCount = 0;
|
|
1665
1745
|
if (missingCids.size === 0) {
|
|
1666
1746
|
console.log(` \u2713 All ${grandpaCids.length} chunks finalised`);
|
|
1667
1747
|
} else {
|
|
1668
1748
|
console.log(` ${missingCids.size} chunks not yet finalised \u2014 waiting up to ${GRANDPA_NATURAL_WAIT_MS / 1e3}s for natural finalisation`);
|
|
1669
1749
|
for (const cid of missingCids) console.log(` ${cid.slice(0, 20)}\u2026`);
|
|
1670
1750
|
const waitStart = Date.now();
|
|
1671
|
-
|
|
1672
|
-
await new Promise((r) => setTimeout(r, GRANDPA_REUPLOAD_POLL_MS));
|
|
1673
|
-
const poll = await probeChunks([...missingCids], { client: phaseALiveProvider.client, atFinalized: true });
|
|
1674
|
-
for (const r of poll) {
|
|
1675
|
-
if (r.present === true) missingCids.delete(r.cid);
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1751
|
+
await pollUntilFinalized(missingCids, GRANDPA_NATURAL_WAIT_MS, phaseALiveProvider.client);
|
|
1678
1752
|
if (missingCids.size === 0) {
|
|
1679
1753
|
const elapsed = Math.round((Date.now() - waitStart) / 1e3);
|
|
1680
1754
|
console.log(` \u2713 All ${grandpaCids.length} chunks finalised (waited ${elapsed}s)`);
|
|
1681
1755
|
} else {
|
|
1756
|
+
const stillMissing = [...missingCids];
|
|
1757
|
+
const { reallyMissing, lagging } = await probeFinalityGap(stillMissing, { client: phaseALiveProvider.client });
|
|
1758
|
+
const laggingCids = new Set(lagging);
|
|
1759
|
+
laggingFinalityCount = laggingCids.size;
|
|
1760
|
+
missingCids = new Set(reallyMissing);
|
|
1761
|
+
setDeployAttribute("deploy.probe.finality_lagging_count", laggingFinalityCount);
|
|
1762
|
+
if (laggingCids.size > 0) {
|
|
1763
|
+
console.log(` ${laggingCids.size} chunk(s) present in best-block but finality-lagging \u2014 will NOT re-upload, waiting for GRANDPA (bounded)`);
|
|
1764
|
+
}
|
|
1682
1765
|
const rootHashCode = 18;
|
|
1683
1766
|
const rootChunkLinks = phaseB.chunks.map((c) => ({
|
|
1684
1767
|
cid: createCID(c, CID_CONFIG.codec, rootHashCode),
|
|
@@ -1737,14 +1820,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
1737
1820
|
}
|
|
1738
1821
|
throw e;
|
|
1739
1822
|
}
|
|
1740
|
-
|
|
1741
|
-
while (Date.now() - reuploadStart < GRANDPA_REUPLOAD_TIMEOUT_MS && missingCids.size > 0) {
|
|
1742
|
-
await new Promise((r) => setTimeout(r, GRANDPA_REUPLOAD_POLL_MS));
|
|
1743
|
-
const poll = await probeChunks([...missingCids], { client: phaseALiveProvider.client, atFinalized: true });
|
|
1744
|
-
for (const r of poll) {
|
|
1745
|
-
if (r.present === true) missingCids.delete(r.cid);
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1823
|
+
await pollUntilFinalized(missingCids, GRANDPA_REUPLOAD_TIMEOUT_MS, phaseALiveProvider.client);
|
|
1748
1824
|
}
|
|
1749
1825
|
if (missingCids.size > 0) {
|
|
1750
1826
|
const stuck = [...missingCids][0];
|
|
@@ -1752,7 +1828,21 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
1752
1828
|
`Deploy verification failed: ${missingCids.size} chunk(s) not finalised after ${GRANDPA_REUPLOAD_MAX_ROUNDS} re-upload round(s) (first: ${stuck.slice(0, 20)}\u2026). The chain may have dropped chunks due to a persistent fork. Re-run deploy.`
|
|
1753
1829
|
);
|
|
1754
1830
|
}
|
|
1755
|
-
|
|
1831
|
+
if (reuploadCount > 0) {
|
|
1832
|
+
console.log(` \u2713 All ${grandpaCids.length - laggingFinalityCount} chunks finalised after re-upload`);
|
|
1833
|
+
} else {
|
|
1834
|
+
console.log(` \u2713 No chunks genuinely missing from best-block \u2014 skipped re-upload entirely`);
|
|
1835
|
+
}
|
|
1836
|
+
if (laggingCids.size > 0) {
|
|
1837
|
+
await pollUntilFinalized(laggingCids, GRANDPA_LAGGING_WAIT_MS, phaseALiveProvider.client);
|
|
1838
|
+
if (laggingCids.size === 0) {
|
|
1839
|
+
console.log(` \u2713 finality-lagging chunk(s) caught up`);
|
|
1840
|
+
} else {
|
|
1841
|
+
console.warn(
|
|
1842
|
+
` \u26A0 ${laggingCids.size} chunk(s) still not finalised after extended wait, but confirmed present in best-block \u2014 deploy succeeds; finality pending asynchronously`
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1756
1846
|
}
|
|
1757
1847
|
}
|
|
1758
1848
|
setDeployAttribute("deploy.probe.finality_miss_reupload_count", reuploadCount);
|
|
@@ -1833,11 +1923,14 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
1833
1923
|
console.log(` Final root check: ${storageCid}`);
|
|
1834
1924
|
const rootProbe = await probeChunks([storageCid], { client: phaseALiveProvider.client, atFinalized: true });
|
|
1835
1925
|
if (rootProbe[0]?.present === false) {
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1926
|
+
const { reallyMissing } = await probeFinalityGap([storageCid], { client: phaseALiveProvider.client });
|
|
1927
|
+
if (reallyMissing.length > 0) {
|
|
1928
|
+
throw new Error(
|
|
1929
|
+
`Deploy verification failed: DAG-PB root ${storageCid.slice(0, 20)}\u2026 not finalised and not present in best-block. The chain may have evicted the root extrinsic. Re-run deploy.`
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1932
|
+
console.log(` Root confirmed present in best-block (finality-lagging, #1049) \u2014 treating as success.`);
|
|
1933
|
+
} else if (rootProbe[0]?.present === true) {
|
|
1841
1934
|
console.log(` \u2713 Root finalised on chain`);
|
|
1842
1935
|
} else {
|
|
1843
1936
|
console.log(` Root re-check inconclusive (RPC error) \u2014 GRANDPA probe above already verified; continuing.`);
|
|
@@ -3008,6 +3101,7 @@ export {
|
|
|
3008
3101
|
isBulletinAuthActive,
|
|
3009
3102
|
pollUntilBulletinAuthorized,
|
|
3010
3103
|
waitForBulletinAuthorization,
|
|
3104
|
+
withTransientRetry,
|
|
3011
3105
|
getSlotSignerProvider,
|
|
3012
3106
|
friendlyChainError,
|
|
3013
3107
|
DEFAULT_BULLETIN_RPC,
|
|
@@ -3039,6 +3133,9 @@ export {
|
|
|
3039
3133
|
formatStorageSignerLine,
|
|
3040
3134
|
formatTransferModeStorageSignerLine,
|
|
3041
3135
|
formatTransferModeDotnsLine,
|
|
3136
|
+
describeSlotFallbackReason,
|
|
3137
|
+
reconcileTimedOutChunk,
|
|
3138
|
+
__waitForChainLivenessForTest,
|
|
3042
3139
|
storeFile,
|
|
3043
3140
|
__assignDenseNoncesForTest,
|
|
3044
3141
|
storeChunkedContent,
|