@parity/product-deploy 0.7.28-rc.0 → 0.7.28-rc.3
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/assets/environments.json +0 -2
- package/dist/bug-report.js +4 -4
- package/dist/{chunk-QMYW3D6E.js → chunk-A5PSVZFF.js} +54 -100
- package/dist/{chunk-OITUIM2E.js → chunk-HZKRLQLG.js} +0 -3
- package/dist/{chunk-KHVTYIIX.js → chunk-IW3X2MJF.js} +2 -0
- package/dist/{chunk-ADNBLFDP.js → chunk-JFL64IZ4.js} +2 -2
- package/dist/{chunk-NF2FL4ZO.js → chunk-JZRWI7D2.js} +3 -3
- package/dist/chunk-L2SKSKB6.js +308 -0
- package/dist/{chunk-P6CHOMN3.js → chunk-OOXMLONF.js} +25 -36
- package/dist/{chunk-QTZNULSH.js → chunk-PX33S2J3.js} +1 -1
- package/dist/{chunk-KJH2T5TQ.js → chunk-VWPVG4DQ.js} +1 -1
- package/dist/{chunk-43HLT335.js → chunk-XYITGX7M.js} +1 -1
- package/dist/{chunk-MFTODIIT.js → chunk-ZOC5WRIG.js} +13 -7
- package/dist/{chunk-DNXH4QTI.js → chunk-ZZCR63N2.js} +112 -16
- package/dist/chunk-probe.js +3 -3
- package/dist/deploy.d.ts +1 -2
- package/dist/deploy.js +11 -11
- package/dist/dotns.d.ts +10 -1
- package/dist/dotns.js +9 -5
- package/dist/environments.d.ts +0 -2
- package/dist/environments.js +1 -1
- package/dist/incremental-stats.d.ts +2 -0
- package/dist/incremental-stats.js +1 -1
- package/dist/index.js +12 -12
- package/dist/manifest/publish.js +12 -12
- package/dist/manifest-fetch.d.ts +2 -1
- package/dist/manifest-fetch.js +1 -1
- package/dist/manifest-roundtrip.js +1 -1
- package/dist/memory-report.js +2 -2
- package/dist/merkle.js +11 -11
- package/dist/personhood/bootstrap.js +5 -5
- package/dist/personhood/people-client.js +5 -5
- package/dist/pool.d.ts +6 -13
- package/dist/pool.js +3 -1
- package/dist/run-state.js +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.js +2 -2
- package/dist/version-check.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-FZWJV5AD.js +0 -231
package/assets/environments.json
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
"backend": "https://polkadot-app-stg.parity.io/",
|
|
9
9
|
"ipfs": "https://previewnet.substrate.dev",
|
|
10
10
|
"autoAccountMapping": true,
|
|
11
|
-
"bulletinAuthorizeV2": true,
|
|
12
11
|
"registerStorageDeposit": 2000000000000,
|
|
13
12
|
"contracts": {
|
|
14
13
|
"DOTNS_PROTOCOL_REGISTRY": "0xc07A2F24387DA27283CD87b9F24573b74C9e0c9b",
|
|
@@ -55,7 +54,6 @@
|
|
|
55
54
|
"ipfs": "https://paseo-bulletin-next-ipfs.polkadot.io",
|
|
56
55
|
"docsUrl": "https://sre.teleport.parity.io/environments/paseo-next/",
|
|
57
56
|
"autoAccountMapping": true,
|
|
58
|
-
"bulletinAuthorizeV2": true,
|
|
59
57
|
"nativeToEthRatio": 100000000,
|
|
60
58
|
"registerStorageDeposit": 2000000000000,
|
|
61
59
|
"popSelfServe": {
|
package/dist/bug-report.js
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
offerBugReport,
|
|
10
10
|
scrubSecrets,
|
|
11
11
|
setDeployContext
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-JFL64IZ4.js";
|
|
13
|
+
import "./chunk-XYITGX7M.js";
|
|
14
|
+
import "./chunk-ZOC5WRIG.js";
|
|
15
|
+
import "./chunk-VWPVG4DQ.js";
|
|
16
16
|
export {
|
|
17
17
|
buildCliFlagsSummary,
|
|
18
18
|
buildLabels,
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setDeployAttribute
|
|
3
|
+
} from "./chunk-ZOC5WRIG.js";
|
|
4
|
+
|
|
1
5
|
// src/pool.ts
|
|
2
6
|
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
3
7
|
import { DEV_PHRASE, entropyToMiniSecret, mnemonicToEntropy } from "@polkadot-labs/hdkd-helpers";
|
|
@@ -14,7 +18,6 @@ var DEPLOY_PATH_PREFIX = "//deploy";
|
|
|
14
18
|
var TOPUP_TRANSACTIONS = 1e3;
|
|
15
19
|
var TOPUP_BYTES = 100000000n;
|
|
16
20
|
var WS_HEARTBEAT_TIMEOUT_MS = 3e5;
|
|
17
|
-
var AUTHORIZATION_EXTENSION_BLOCKS = 2e6;
|
|
18
21
|
function derivePoolAccounts(poolSize = 10, mnemonic = DEV_PHRASE) {
|
|
19
22
|
const entropy = mnemonicToEntropy(mnemonic);
|
|
20
23
|
const miniSecret = entropyToMiniSecret(entropy);
|
|
@@ -30,23 +33,19 @@ function derivePoolAccounts(poolSize = 10, mnemonic = DEV_PHRASE) {
|
|
|
30
33
|
}
|
|
31
34
|
return accounts;
|
|
32
35
|
}
|
|
33
|
-
function isAuthorizationSufficient(auth, currentBlock,
|
|
36
|
+
function isAuthorizationSufficient(auth, currentBlock, needs) {
|
|
34
37
|
if (auth === void 0) return false;
|
|
35
38
|
if (Number(auth.expiration ?? 0) <= currentBlock) return false;
|
|
36
|
-
if (
|
|
39
|
+
if (needs) {
|
|
37
40
|
const txsRemaining = BigInt(auth.extent.transactions_allowance) - BigInt(auth.extent.transactions);
|
|
38
41
|
const bytesRemaining = BigInt(auth.extent.bytes_allowance) - BigInt(auth.extent.bytes);
|
|
39
|
-
if (txsRemaining <
|
|
40
|
-
if (bytesRemaining <
|
|
42
|
+
if (txsRemaining < needs.txs) return false;
|
|
43
|
+
if (bytesRemaining < needs.bytes) return false;
|
|
41
44
|
}
|
|
42
45
|
return true;
|
|
43
46
|
}
|
|
44
|
-
function selectAccount(authorizations, random = Math.random
|
|
45
|
-
|
|
46
|
-
(a) => currentBlock === void 0 || a.expiration > currentBlock
|
|
47
|
-
);
|
|
48
|
-
if (eligible.length === 0) return null;
|
|
49
|
-
return { account: eligible[Math.floor(random() * eligible.length)], eligibleCount: eligible.length };
|
|
47
|
+
function selectAccount(authorizations, random = Math.random) {
|
|
48
|
+
return { account: authorizations[Math.floor(random() * authorizations.length)], eligibleCount: authorizations.length };
|
|
50
49
|
}
|
|
51
50
|
async function fetchPoolAuthorizations(api, accounts) {
|
|
52
51
|
const results = await Promise.all(
|
|
@@ -145,83 +144,59 @@ function clampU32(n, name) {
|
|
|
145
144
|
if (n > U32_MAX) throw new Error(`${name} (${n}) exceeds u32 max \u2014 split the deploy into smaller batches`);
|
|
146
145
|
return Number(n);
|
|
147
146
|
}
|
|
148
|
-
|
|
147
|
+
function markBulletinAuthGranted() {
|
|
148
|
+
setDeployAttribute("deploy.unblock.bulletin_auth.fired", "true");
|
|
149
|
+
setDeployAttribute("deploy.unblock.bulletin_auth.granted_txs", String(TOPUP_TRANSACTIONS));
|
|
150
|
+
setDeployAttribute("deploy.unblock.bulletin_auth.granted_bytes", String(TOPUP_BYTES));
|
|
151
|
+
}
|
|
152
|
+
async function ensureAuthorized(api, address, label, needs) {
|
|
149
153
|
const [auth, currentBlock] = await Promise.all([
|
|
150
154
|
api.query.TransactionStorage.Authorizations.getValue(Enum("Account", address)),
|
|
151
155
|
api.query.System.Number.getValue()
|
|
152
156
|
]);
|
|
153
|
-
if (isAuthorizationSufficient(auth, currentBlock,
|
|
157
|
+
if (isAuthorizationSufficient(auth, currentBlock, needs)) return;
|
|
154
158
|
console.log(` Auto-authorizing ${label ?? "account"} (${address.slice(0, 8)}...)...`);
|
|
155
159
|
const { signer } = aliceKeyring();
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
} else {
|
|
168
|
-
const newExpiration = currentBlock + AUTHORIZATION_EXTENSION_BLOCKS;
|
|
169
|
-
await submitAliceTxWithRetry(
|
|
170
|
-
() => api.tx.TransactionStorage.authorize_account({
|
|
171
|
-
who: address,
|
|
172
|
-
expiration: newExpiration
|
|
173
|
-
}),
|
|
174
|
-
signer,
|
|
175
|
-
`authorize_account(${label ?? "account"})`
|
|
176
|
-
);
|
|
177
|
-
console.log(` Authorized: expires at block ${newExpiration} (current: ${currentBlock})`);
|
|
178
|
-
}
|
|
160
|
+
await submitAliceTxWithRetry(
|
|
161
|
+
() => api.tx.TransactionStorage.authorize_account({
|
|
162
|
+
who: address,
|
|
163
|
+
transactions: clampU32(BigInt(TOPUP_TRANSACTIONS), "transactions"),
|
|
164
|
+
bytes: TOPUP_BYTES
|
|
165
|
+
}),
|
|
166
|
+
signer,
|
|
167
|
+
`authorize_account(${label ?? "account"})`
|
|
168
|
+
);
|
|
169
|
+
console.log(` Authorized: ${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB`);
|
|
170
|
+
markBulletinAuthGranted();
|
|
179
171
|
}
|
|
180
|
-
async function topUpBy(api, address, needs, label
|
|
172
|
+
async function topUpBy(api, address, needs, label) {
|
|
181
173
|
const [currentAuth, currentBlock] = await Promise.all([
|
|
182
174
|
api.query.TransactionStorage.Authorizations.getValue(Enum("Account", address)),
|
|
183
175
|
api.query.System.Number.getValue()
|
|
184
176
|
]);
|
|
185
|
-
if (isAuthorizationSufficient(currentAuth, currentBlock,
|
|
177
|
+
if (isAuthorizationSufficient(currentAuth, currentBlock, needs)) {
|
|
186
178
|
const expiration = Number(currentAuth.expiration);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
console.log(` Pre-auth skipped for ${label ?? "account"} (${address.slice(0, 8)}...): authorized until block ${expiration}, ${txsRemaining} txs / ${fmtMB(bytesRemaining)}MB remaining.`);
|
|
192
|
-
} else {
|
|
193
|
-
console.log(` Pre-auth skipped for ${label ?? "account"} (${address.slice(0, 8)}...): authorized until block ${expiration}.`);
|
|
194
|
-
}
|
|
179
|
+
const fmtMB = (b) => (Number(b) / 1e6).toFixed(1);
|
|
180
|
+
const txsRemaining = BigInt(currentAuth.extent.transactions_allowance) - BigInt(currentAuth.extent.transactions);
|
|
181
|
+
const bytesRemaining = BigInt(currentAuth.extent.bytes_allowance) - BigInt(currentAuth.extent.bytes);
|
|
182
|
+
console.log(` Pre-auth skipped for ${label ?? "account"} (${address.slice(0, 8)}...): authorized until block ${expiration}, ${txsRemaining} txs / ${fmtMB(bytesRemaining)}MB remaining.`);
|
|
195
183
|
return;
|
|
196
184
|
}
|
|
197
185
|
const { signer } = aliceKeyring();
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
} else {
|
|
211
|
-
const newExpiration = currentBlock + AUTHORIZATION_EXTENSION_BLOCKS;
|
|
212
|
-
console.log(` Pre-authorizing ${label ?? "account"} (${address.slice(0, 8)}...): extending authorization to block ${newExpiration}...`);
|
|
213
|
-
await submitAliceTxWithRetry(
|
|
214
|
-
() => api.tx.TransactionStorage.authorize_account({
|
|
215
|
-
who: address,
|
|
216
|
-
expiration: newExpiration
|
|
217
|
-
}),
|
|
218
|
-
signer,
|
|
219
|
-
`topUpBy(${label ?? "account"})`
|
|
220
|
-
);
|
|
221
|
-
console.log(` Pre-authorized: expires at block ${newExpiration}`);
|
|
222
|
-
}
|
|
186
|
+
console.log(` Pre-authorizing ${label ?? "account"} (${address.slice(0, 8)}...): granting ${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB...`);
|
|
187
|
+
await submitAliceTxWithRetry(
|
|
188
|
+
() => api.tx.TransactionStorage.authorize_account({
|
|
189
|
+
who: address,
|
|
190
|
+
transactions: clampU32(BigInt(TOPUP_TRANSACTIONS), "transactions"),
|
|
191
|
+
bytes: TOPUP_BYTES
|
|
192
|
+
}),
|
|
193
|
+
signer,
|
|
194
|
+
`topUpBy(${label ?? "account"})`
|
|
195
|
+
);
|
|
196
|
+
console.log(` Pre-authorized: ${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB`);
|
|
197
|
+
markBulletinAuthGranted();
|
|
223
198
|
}
|
|
224
|
-
async function bootstrapPool(bulletinRpc, poolSize = 10, mnemonic
|
|
199
|
+
async function bootstrapPool(bulletinRpc, poolSize = 10, mnemonic) {
|
|
225
200
|
console.log(`Bootstrapping ${poolSize} pool accounts on ${bulletinRpc}...
|
|
226
201
|
`);
|
|
227
202
|
await cryptoWaitReady();
|
|
@@ -238,40 +213,19 @@ async function bootstrapPool(bulletinRpc, poolSize = 10, mnemonic, opts = {}) {
|
|
|
238
213
|
const aliceBalance = BigInt(aliceAccount.data.free);
|
|
239
214
|
console.log(`Alice balance: ${formatPasBalance(aliceBalance)} PAS
|
|
240
215
|
`);
|
|
241
|
-
|
|
242
|
-
if (opts.bulletinAuthorizeV2) {
|
|
243
|
-
console.log(`Authorizing accounts with V2 (${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB)
|
|
244
|
-
`);
|
|
245
|
-
} else {
|
|
246
|
-
const authExpiration = currentBlock + AUTHORIZATION_EXTENSION_BLOCKS;
|
|
247
|
-
console.log(`Authorizing accounts until block ${authExpiration} (current: ${currentBlock})
|
|
216
|
+
console.log(`Authorizing accounts (${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB)
|
|
248
217
|
`);
|
|
249
|
-
}
|
|
250
218
|
for (const account of accounts) {
|
|
251
219
|
console.log(`Account ${account.index}: ${account.address}`);
|
|
252
220
|
try {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
bytes: TOPUP_BYTES
|
|
259
|
-
});
|
|
260
|
-
} else {
|
|
261
|
-
const authExpiration = currentBlock + AUTHORIZATION_EXTENSION_BLOCKS;
|
|
262
|
-
tx = api.tx.TransactionStorage.authorize_account({
|
|
263
|
-
who: account.address,
|
|
264
|
-
expiration: authExpiration
|
|
265
|
-
});
|
|
266
|
-
}
|
|
221
|
+
const tx = api.tx.TransactionStorage.authorize_account({
|
|
222
|
+
who: account.address,
|
|
223
|
+
transactions: clampU32(BigInt(TOPUP_TRANSACTIONS), "transactions"),
|
|
224
|
+
bytes: TOPUP_BYTES
|
|
225
|
+
});
|
|
267
226
|
const result = await tx.signAndSubmit(aliceSigner);
|
|
268
227
|
if (!result.ok) throw new Error("dispatch failed");
|
|
269
|
-
|
|
270
|
-
console.log(` Authorized: ${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB`);
|
|
271
|
-
} else {
|
|
272
|
-
const authExpiration = currentBlock + AUTHORIZATION_EXTENSION_BLOCKS;
|
|
273
|
-
console.log(` Authorized: expires at block ${authExpiration}`);
|
|
274
|
-
}
|
|
228
|
+
console.log(` Authorized: ${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB`);
|
|
275
229
|
} catch (e) {
|
|
276
230
|
console.log(` Authorization failed: ${e.message?.slice(0, 80)}`);
|
|
277
231
|
}
|
|
@@ -17,7 +17,6 @@ var environments_default = {
|
|
|
17
17
|
backend: "https://polkadot-app-stg.parity.io/",
|
|
18
18
|
ipfs: "https://previewnet.substrate.dev",
|
|
19
19
|
autoAccountMapping: true,
|
|
20
|
-
bulletinAuthorizeV2: true,
|
|
21
20
|
registerStorageDeposit: 2e12,
|
|
22
21
|
contracts: {
|
|
23
22
|
DOTNS_PROTOCOL_REGISTRY: "0xc07A2F24387DA27283CD87b9F24573b74C9e0c9b",
|
|
@@ -64,7 +63,6 @@ var environments_default = {
|
|
|
64
63
|
ipfs: "https://paseo-bulletin-next-ipfs.polkadot.io",
|
|
65
64
|
docsUrl: "https://sre.teleport.parity.io/environments/paseo-next/",
|
|
66
65
|
autoAccountMapping: true,
|
|
67
|
-
bulletinAuthorizeV2: true,
|
|
68
66
|
nativeToEthRatio: 1e8,
|
|
69
67
|
registerStorageDeposit: 2e12,
|
|
70
68
|
popSelfServe: {
|
|
@@ -471,7 +469,6 @@ function resolveEndpoints(doc, envId) {
|
|
|
471
469
|
envName: env.name,
|
|
472
470
|
ipfs: env.ipfs,
|
|
473
471
|
autoAccountMapping: env.autoAccountMapping ?? false,
|
|
474
|
-
bulletinAuthorizeV2: env.bulletinAuthorizeV2 ?? false,
|
|
475
472
|
contracts: env.contracts ?? {},
|
|
476
473
|
nativeToEthRatio: BigInt(env.nativeToEthRatio ?? 1e6),
|
|
477
474
|
...env.registerStorageDeposit !== void 0 ? { registerStorageDeposit: BigInt(env.registerStorageDeposit) } : {}
|
|
@@ -10,6 +10,7 @@ function computeStats(input) {
|
|
|
10
10
|
return {
|
|
11
11
|
manifestSource: input.manifestSource,
|
|
12
12
|
manifestFetchAttempts: input.manifestFetchAttempts,
|
|
13
|
+
manifestFetchReason: input.manifestFetchReason,
|
|
13
14
|
manifestBytes: input.manifestBytes ?? 0,
|
|
14
15
|
framework: input.framework,
|
|
15
16
|
filesTotal: input.filesTotal,
|
|
@@ -46,6 +47,7 @@ function telemetryAttributes(s) {
|
|
|
46
47
|
return {
|
|
47
48
|
"deploy.cache.manifest_source": s.manifestSource,
|
|
48
49
|
"deploy.cache.manifest_fetch_attempts": String(s.manifestFetchAttempts),
|
|
50
|
+
"deploy.cache.manifest_fetch_reason": s.manifestFetchReason ?? "",
|
|
49
51
|
"deploy.cache.manifest_bytes": String(s.manifestBytes),
|
|
50
52
|
"deploy.cache.framework": s.framework ?? "",
|
|
51
53
|
"deploy.cache.hit_rate": String(Math.round(hitRate * 1e3) / 1e3),
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
classifyErrorArea,
|
|
3
3
|
isInteractive,
|
|
4
4
|
promptYesNo
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XYITGX7M.js";
|
|
6
6
|
import {
|
|
7
7
|
VERSION,
|
|
8
8
|
getCurrentSentryTraceId
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ZOC5WRIG.js";
|
|
10
10
|
|
|
11
11
|
// src/bug-report.ts
|
|
12
12
|
import { execSync, execFileSync } from "child_process";
|
|
@@ -6,15 +6,15 @@ import {
|
|
|
6
6
|
resolveDotnsConnectOptions,
|
|
7
7
|
storeDirectory,
|
|
8
8
|
storeFile
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-OOXMLONF.js";
|
|
10
10
|
import {
|
|
11
11
|
DotNS
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZZCR63N2.js";
|
|
13
13
|
import {
|
|
14
14
|
getPopSelfServeConfig,
|
|
15
15
|
loadEnvironments,
|
|
16
16
|
resolveEndpoints
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-HZKRLQLG.js";
|
|
18
18
|
import {
|
|
19
19
|
NonRetryableError
|
|
20
20
|
} from "./chunk-ZOC4GITL.js";
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MANIFEST_DIR,
|
|
3
|
+
MANIFEST_FILENAME,
|
|
4
|
+
parseManifest
|
|
5
|
+
} from "./chunk-S7EM5VMW.js";
|
|
6
|
+
|
|
7
|
+
// src/manifest-fetch.ts
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import * as os from "os";
|
|
11
|
+
import * as Sentry from "@sentry/node";
|
|
12
|
+
import { CarReader } from "@ipld/car/reader";
|
|
13
|
+
import * as dagPB from "@ipld/dag-pb";
|
|
14
|
+
import { CID } from "multiformats/cid";
|
|
15
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
16
|
+
var SIDECAR_FILENAME = ".last_deploy_cid";
|
|
17
|
+
var RANGE_TIERS = [
|
|
18
|
+
"bytes=0-4095",
|
|
19
|
+
"bytes=0-65535",
|
|
20
|
+
"bytes=0-1048575",
|
|
21
|
+
void 0
|
|
22
|
+
// full body
|
|
23
|
+
];
|
|
24
|
+
function getCacheDir() {
|
|
25
|
+
const override = process.env.BULLETIN_DEPLOY_CACHE_DIR;
|
|
26
|
+
if (override) return path.join(override, "manifests");
|
|
27
|
+
if (process.platform === "win32") return null;
|
|
28
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
29
|
+
if (xdg) return path.join(xdg, "bulletin-deploy", "manifests");
|
|
30
|
+
return path.join(os.homedir(), ".cache", "bulletin-deploy", "manifests");
|
|
31
|
+
}
|
|
32
|
+
function readPersistentLocalManifest(domain, prevContenthash) {
|
|
33
|
+
if (!domain) return null;
|
|
34
|
+
const cacheDir = getCacheDir();
|
|
35
|
+
if (!cacheDir) return null;
|
|
36
|
+
const cidPath = path.join(cacheDir, `${domain}.cid`);
|
|
37
|
+
const manifestPath = path.join(cacheDir, `${domain}.json`);
|
|
38
|
+
let storedCid;
|
|
39
|
+
try {
|
|
40
|
+
storedCid = fs.readFileSync(cidPath, "utf8").trim();
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (storedCid !== prevContenthash) return null;
|
|
45
|
+
let text;
|
|
46
|
+
try {
|
|
47
|
+
text = fs.readFileSync(manifestPath, "utf8");
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const parsed = parseManifest(text);
|
|
52
|
+
if (!parsed.ok) return null;
|
|
53
|
+
return {
|
|
54
|
+
source: "embedded",
|
|
55
|
+
manifest: parsed.manifest,
|
|
56
|
+
attempts: 0,
|
|
57
|
+
bytesDownloaded: text.length
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function writePersistentLocalManifest(domain, storageCid, manifestJson) {
|
|
61
|
+
const cacheDir = getCacheDir();
|
|
62
|
+
if (!cacheDir) return;
|
|
63
|
+
try {
|
|
64
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
65
|
+
const cidPath = path.join(cacheDir, `${domain}.cid`);
|
|
66
|
+
const manifestPath = path.join(cacheDir, `${domain}.json`);
|
|
67
|
+
const cidTmp = `${cidPath}.${process.pid}.tmp`;
|
|
68
|
+
const manifestTmp = `${manifestPath}.${process.pid}.tmp`;
|
|
69
|
+
fs.writeFileSync(cidTmp, storageCid);
|
|
70
|
+
fs.renameSync(cidTmp, cidPath);
|
|
71
|
+
fs.writeFileSync(manifestTmp, manifestJson);
|
|
72
|
+
fs.renameSync(manifestTmp, manifestPath);
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function fetchOneTier(url, tierIndex, budgetRemaining) {
|
|
77
|
+
const rangeHeader = RANGE_TIERS[tierIndex];
|
|
78
|
+
const rangeLabel = rangeHeader != null ? rangeHeader.replace("bytes=", "") : "full";
|
|
79
|
+
const isFullBody = rangeHeader === void 0;
|
|
80
|
+
return Sentry.startSpan(
|
|
81
|
+
{ op: "manifest.fetch.tier", name: `tier ${tierIndex} ${rangeLabel}` },
|
|
82
|
+
async (span) => {
|
|
83
|
+
span.setAttribute("manifest.tier.index", String(tierIndex));
|
|
84
|
+
span.setAttribute("manifest.tier.range", rangeLabel);
|
|
85
|
+
span.setAttribute("manifest.tier.http_status", "");
|
|
86
|
+
span.setAttribute("manifest.tier.bytes", "0");
|
|
87
|
+
span.setAttribute("manifest.tier.wait_ms", "0");
|
|
88
|
+
span.setAttribute("manifest.tier.read_ms", "0");
|
|
89
|
+
span.setAttribute("manifest.tier.error", "");
|
|
90
|
+
const headers = {};
|
|
91
|
+
if (rangeHeader !== void 0) headers.Range = rangeHeader;
|
|
92
|
+
let res;
|
|
93
|
+
const fetchStart = Date.now();
|
|
94
|
+
try {
|
|
95
|
+
const ctrl = new AbortController();
|
|
96
|
+
const timer = setTimeout(() => ctrl.abort(), Math.max(100, budgetRemaining));
|
|
97
|
+
try {
|
|
98
|
+
res = await fetch(url, { headers, signal: ctrl.signal });
|
|
99
|
+
} finally {
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
const isAbort = e?.name === "AbortError";
|
|
104
|
+
const outcome = isAbort ? "timeout" : "network_error";
|
|
105
|
+
const msg = e?.message ?? String(e);
|
|
106
|
+
span.setAttribute("manifest.tier.outcome", outcome);
|
|
107
|
+
span.setAttribute("manifest.tier.wait_ms", String(Date.now() - fetchStart));
|
|
108
|
+
span.setAttribute("manifest.tier.error", msg);
|
|
109
|
+
return { outcome, reason: `${outcome}: ${msg}`, bytes: 0 };
|
|
110
|
+
}
|
|
111
|
+
const waitMs = Date.now() - fetchStart;
|
|
112
|
+
span.setAttribute("manifest.tier.wait_ms", String(waitMs));
|
|
113
|
+
span.setAttribute("manifest.tier.http_status", String(res.status));
|
|
114
|
+
if (res.status === 404) {
|
|
115
|
+
span.setAttribute("manifest.tier.outcome", "http_404");
|
|
116
|
+
return { outcome: "http_404", reason: "gateway 404", bytes: 0 };
|
|
117
|
+
}
|
|
118
|
+
if (res.status !== 200 && res.status !== 206) {
|
|
119
|
+
const outcome = `http_${res.status}`;
|
|
120
|
+
span.setAttribute("manifest.tier.outcome", outcome);
|
|
121
|
+
span.setAttribute("manifest.tier.error", `HTTP ${res.status}`);
|
|
122
|
+
return { outcome, reason: `gateway HTTP ${res.status}`, bytes: 0 };
|
|
123
|
+
}
|
|
124
|
+
let carBytes;
|
|
125
|
+
const readStart = Date.now();
|
|
126
|
+
try {
|
|
127
|
+
const buf = await res.arrayBuffer();
|
|
128
|
+
carBytes = new Uint8Array(buf);
|
|
129
|
+
span.setAttribute("manifest.tier.bytes", String(carBytes.length));
|
|
130
|
+
span.setAttribute("manifest.tier.read_ms", String(Date.now() - readStart));
|
|
131
|
+
} catch (e) {
|
|
132
|
+
span.setAttribute("manifest.tier.read_ms", String(Date.now() - readStart));
|
|
133
|
+
const msg = `body read error: ${e?.message ?? e}`;
|
|
134
|
+
span.setAttribute("manifest.tier.outcome", "body_read_error");
|
|
135
|
+
span.setAttribute("manifest.tier.error", msg);
|
|
136
|
+
return { outcome: "body_read_error", reason: msg, bytes: 0 };
|
|
137
|
+
}
|
|
138
|
+
let manifestBytes;
|
|
139
|
+
try {
|
|
140
|
+
manifestBytes = await extractManifestFromCar(carBytes);
|
|
141
|
+
} catch (e) {
|
|
142
|
+
const msg = `CAR parse error: ${e?.message ?? e}`;
|
|
143
|
+
const outcome = isFullBody ? "car_parse_error" : "car_truncated";
|
|
144
|
+
span.setAttribute("manifest.tier.outcome", outcome);
|
|
145
|
+
span.setAttribute("manifest.tier.error", msg);
|
|
146
|
+
return { outcome, reason: msg, bytes: carBytes.length };
|
|
147
|
+
}
|
|
148
|
+
if (!manifestBytes) {
|
|
149
|
+
const outcome = isFullBody ? "manifest_missing" : "manifest_not_in_slice";
|
|
150
|
+
const msg = isFullBody ? "no .bulletin-deploy/manifest.json in deployed DAG" : `manifest not in slice tier ${tierIndex}`;
|
|
151
|
+
span.setAttribute("manifest.tier.outcome", outcome);
|
|
152
|
+
span.setAttribute("manifest.tier.error", msg);
|
|
153
|
+
return { outcome, reason: msg, bytes: carBytes.length };
|
|
154
|
+
}
|
|
155
|
+
const text = new TextDecoder().decode(manifestBytes);
|
|
156
|
+
const parsed = parseManifest(text);
|
|
157
|
+
if (parsed.ok) {
|
|
158
|
+
span.setAttribute("manifest.tier.outcome", "success");
|
|
159
|
+
return { outcome: "success", reason: "", bytes: carBytes.length, manifest: parsed.manifest };
|
|
160
|
+
}
|
|
161
|
+
span.setAttribute("manifest.tier.outcome", "manifest_parse_error");
|
|
162
|
+
span.setAttribute("manifest.tier.error", parsed.error);
|
|
163
|
+
return { outcome: "manifest_parse_error", reason: parsed.error, bytes: carBytes.length };
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
async function fetchAcrossTiers(url, budget, start) {
|
|
168
|
+
let lastReason = "unknown";
|
|
169
|
+
let attempts = 0;
|
|
170
|
+
let bytesDownloaded = 0;
|
|
171
|
+
for (let tier = 0; tier < RANGE_TIERS.length; tier++) {
|
|
172
|
+
const elapsed = Date.now() - start;
|
|
173
|
+
if (elapsed > budget) {
|
|
174
|
+
return { outcome: "retryable", reason: `budget exceeded: ${lastReason}`, attempts, bytesDownloaded };
|
|
175
|
+
}
|
|
176
|
+
attempts++;
|
|
177
|
+
const result = await fetchOneTier(url, tier, budget - elapsed);
|
|
178
|
+
bytesDownloaded += result.bytes;
|
|
179
|
+
if (result.outcome === "success") {
|
|
180
|
+
return { outcome: "success", manifest: result.manifest, attempts, bytesDownloaded };
|
|
181
|
+
}
|
|
182
|
+
if (result.outcome === "http_404") {
|
|
183
|
+
return { outcome: "404", attempts, bytesDownloaded };
|
|
184
|
+
}
|
|
185
|
+
if (result.outcome === "car_parse_error" || result.outcome === "manifest_missing" || result.outcome === "manifest_parse_error") {
|
|
186
|
+
return { outcome: "parse_error", reason: result.reason, attempts, bytesDownloaded };
|
|
187
|
+
}
|
|
188
|
+
if (result.outcome === "body_read_error" && tier === RANGE_TIERS.length - 1) {
|
|
189
|
+
return { outcome: "parse_error", reason: result.reason, attempts, bytesDownloaded };
|
|
190
|
+
}
|
|
191
|
+
lastReason = result.reason;
|
|
192
|
+
}
|
|
193
|
+
return { outcome: "retryable", reason: `tiers exhausted: ${lastReason}`, attempts, bytesDownloaded };
|
|
194
|
+
}
|
|
195
|
+
async function fetchPreviousManifest(prevContenthash, options = {}) {
|
|
196
|
+
if (prevContenthash === null) return { source: "none" };
|
|
197
|
+
const local = readPersistentLocalManifest(options.domain, prevContenthash);
|
|
198
|
+
if (local) return local;
|
|
199
|
+
const gatewayList = (options.gateways ?? (options.gateway ? [options.gateway] : [])).map((g) => g.replace(/\/$/, ""));
|
|
200
|
+
const budget = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
201
|
+
const start = Date.now();
|
|
202
|
+
let lastReason = "unknown";
|
|
203
|
+
let totalAttempts = 0;
|
|
204
|
+
let bytesDownloaded = 0;
|
|
205
|
+
for (const gatewayRaw of gatewayList) {
|
|
206
|
+
const gateway = gatewayRaw.replace(/\/ipfs\/?$/, "");
|
|
207
|
+
const url = `${gateway}/ipfs/${prevContenthash}`;
|
|
208
|
+
const gatewayStart = Date.now();
|
|
209
|
+
const tierResult = await Sentry.startSpan(
|
|
210
|
+
{
|
|
211
|
+
op: "manifest.fetch",
|
|
212
|
+
name: `manifest fetch ${prevContenthash.slice(0, 12)}`,
|
|
213
|
+
attributes: {
|
|
214
|
+
"manifest.fetch.gateway": gateway,
|
|
215
|
+
"manifest.fetch.cid": prevContenthash.slice(0, 12),
|
|
216
|
+
"manifest.fetch.budget_ms": String(budget)
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
async (span) => {
|
|
220
|
+
const result = await fetchAcrossTiers(url, budget, start);
|
|
221
|
+
span.setAttribute("manifest.fetch.outcome", result.outcome);
|
|
222
|
+
span.setAttribute("manifest.fetch.attempts", String(result.attempts));
|
|
223
|
+
span.setAttribute("manifest.fetch.bytes", String(result.bytesDownloaded));
|
|
224
|
+
span.setAttribute("manifest.fetch.elapsed_ms", String(Date.now() - gatewayStart));
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
if (tierResult.outcome === "success") {
|
|
229
|
+
return {
|
|
230
|
+
source: "embedded",
|
|
231
|
+
manifest: tierResult.manifest,
|
|
232
|
+
attempts: totalAttempts + tierResult.attempts,
|
|
233
|
+
bytesDownloaded: bytesDownloaded + tierResult.bytesDownloaded
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
if (tierResult.outcome === "404" || tierResult.outcome === "parse_error") {
|
|
237
|
+
return {
|
|
238
|
+
source: "heuristic_fallback",
|
|
239
|
+
reason: tierResult.outcome === "404" ? "gateway 404" : tierResult.reason,
|
|
240
|
+
attempts: totalAttempts + tierResult.attempts,
|
|
241
|
+
bytesDownloaded: bytesDownloaded + tierResult.bytesDownloaded
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
lastReason = tierResult.reason;
|
|
245
|
+
totalAttempts += tierResult.attempts;
|
|
246
|
+
bytesDownloaded += tierResult.bytesDownloaded;
|
|
247
|
+
}
|
|
248
|
+
return { source: "heuristic_fallback", reason: `all gateways exhausted: ${lastReason}`, attempts: totalAttempts, bytesDownloaded };
|
|
249
|
+
}
|
|
250
|
+
async function extractManifestFromCar(carBytes) {
|
|
251
|
+
const reader = await CarReader.fromBytes(carBytes);
|
|
252
|
+
const roots = await reader.getRoots();
|
|
253
|
+
if (roots.length === 0) return null;
|
|
254
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
255
|
+
for await (const { cid, bytes } of reader.blocks()) {
|
|
256
|
+
blocks.set(cid.toString(), bytes);
|
|
257
|
+
}
|
|
258
|
+
return walkDagToManifest(blocks, roots[0].toString());
|
|
259
|
+
}
|
|
260
|
+
async function walkDagToManifest(blocks, rootCid) {
|
|
261
|
+
const rootBytes = blocks.get(rootCid);
|
|
262
|
+
if (!rootBytes) return null;
|
|
263
|
+
const rootNode = dagPB.decode(rootBytes);
|
|
264
|
+
const bdLink = (rootNode.Links ?? []).find((l) => l.Name === MANIFEST_DIR);
|
|
265
|
+
if (!bdLink) return null;
|
|
266
|
+
const bdBytes = blocks.get(bdLink.Hash.toString());
|
|
267
|
+
if (!bdBytes) return null;
|
|
268
|
+
const bdNode = dagPB.decode(bdBytes);
|
|
269
|
+
const manLink = (bdNode.Links ?? []).find((l) => l.Name === MANIFEST_FILENAME);
|
|
270
|
+
if (!manLink) return null;
|
|
271
|
+
const manCidStr = manLink.Hash.toString();
|
|
272
|
+
const manCid = CID.parse(manCidStr);
|
|
273
|
+
const manBytes = blocks.get(manCidStr);
|
|
274
|
+
if (!manBytes) return null;
|
|
275
|
+
if (manCid.code === 85) {
|
|
276
|
+
return manBytes;
|
|
277
|
+
}
|
|
278
|
+
if (manCid.code === 112) {
|
|
279
|
+
const node = dagPB.decode(manBytes);
|
|
280
|
+
const parts = [];
|
|
281
|
+
let total = 0;
|
|
282
|
+
for (const link of node.Links ?? []) {
|
|
283
|
+
const leafBytes = blocks.get(link.Hash.toString());
|
|
284
|
+
if (!leafBytes) return null;
|
|
285
|
+
parts.push(leafBytes);
|
|
286
|
+
total += leafBytes.length;
|
|
287
|
+
}
|
|
288
|
+
const out = new Uint8Array(total);
|
|
289
|
+
let pos = 0;
|
|
290
|
+
for (const part of parts) {
|
|
291
|
+
out.set(part, pos);
|
|
292
|
+
pos += part.length;
|
|
293
|
+
}
|
|
294
|
+
return out;
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export {
|
|
300
|
+
DEFAULT_TIMEOUT_MS,
|
|
301
|
+
SIDECAR_FILENAME,
|
|
302
|
+
getCacheDir,
|
|
303
|
+
readPersistentLocalManifest,
|
|
304
|
+
writePersistentLocalManifest,
|
|
305
|
+
fetchPreviousManifest,
|
|
306
|
+
extractManifestFromCar,
|
|
307
|
+
walkDagToManifest
|
|
308
|
+
};
|