@parity/product-deploy 0.8.3-rc.0 → 0.8.3-rc.11
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 +53 -2
- package/bin/bulletin-deploy +54 -8
- package/dist/allocations-B65Is4Md.d.ts +97 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/index.js +26 -0
- package/dist/auth/vendor/index.d.ts +32 -0
- package/dist/auth/vendor/index.js +27 -0
- package/dist/auth/vendor/ui/index.d.ts +15 -0
- package/dist/auth/vendor/ui/index.js +10 -0
- package/dist/auth-DkRZBK-T.d.ts +122 -0
- package/dist/auth-config.d.ts +39 -0
- package/dist/auth-config.js +20 -0
- package/dist/bug-report.js +4 -4
- package/dist/chunk-2OZVKA3D.js +410 -0
- package/dist/{chunk-PV3N2XTC.js → chunk-4CZ4GIWK.js} +677 -356
- package/dist/{chunk-LJMYOXQV.js → chunk-4GX3KJPU.js} +12 -1
- package/dist/{chunk-OAWXITVX.js → chunk-4W6VNILJ.js} +2 -2
- package/dist/{chunk-CBCUKOOJ.js → chunk-AKCO2LGH.js} +1 -1
- package/dist/{chunk-VRZXAB7J.js → chunk-AMGKEAOH.js} +2 -2
- package/dist/chunk-JQKKMUCT.js +0 -0
- package/dist/{chunk-UXVBF7TD.js → chunk-KFIIAUQU.js} +17 -3
- package/dist/{chunk-L5Z3TJD7.js → chunk-OCKCB72S.js} +6 -6
- package/dist/{chunk-YT2XCGZK.js → chunk-P2ZOBSCJ.js} +12 -5
- package/dist/chunk-RIRDBSBG.js +36 -0
- package/dist/{chunk-Z6PRIHI7.js → chunk-X2Q5FPIQ.js} +1 -1
- package/dist/chunk-YUSHBZBX.js +52 -0
- package/dist/chunk-probe.js +3 -3
- package/dist/commands/login.d.ts +28 -0
- package/dist/commands/login.js +116 -0
- package/dist/commands/logout.d.ts +21 -0
- package/dist/commands/logout.js +37 -0
- package/dist/commands/whoami.d.ts +22 -0
- package/dist/commands/whoami.js +47 -0
- package/dist/deploy.d.ts +49 -3
- package/dist/deploy.js +19 -8
- package/dist/dotns.d.ts +7 -0
- package/dist/dotns.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +13 -12
- package/dist/manifest/publish.js +10 -9
- package/dist/memory-report.js +2 -2
- package/dist/merkle.d.ts +3 -1
- package/dist/merkle.js +9 -8
- package/dist/personhood/bind-paid-alias.js +3 -3
- package/dist/personhood/bind-personal-id.js +2 -2
- package/dist/personhood/bootstrap.js +16 -16
- package/dist/personhood/claim-pgas.js +2 -2
- package/dist/personhood/people-client.js +3 -3
- package/dist/personhood/proof-validity.js +2 -2
- package/dist/personhood/reprove.js +5 -5
- package/dist/run-state.js +1 -1
- package/dist/signer-CriGqahj.d.ts +35 -0
- package/dist/storage-signer.d.ts +38 -0
- package/dist/storage-signer.js +28 -0
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.js +2 -2
- package/dist/version-check.js +3 -3
- package/package.json +17 -3
- package/tools/release-retry-wrapper.mjs +1 -0
- package/dist/{chunk-LHLCPDGL.js → chunk-7URNKK6J.js} +3 -3
- package/dist/{chunk-7Y7RDOGT.js → chunk-EATOPQFR.js} +5 -5
- package/dist/{chunk-SLE4P6MO.js → chunk-EJI3MX4G.js} +3 -3
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MirrorSkipped,
|
|
3
|
+
mirrorToGitHubPages,
|
|
4
|
+
pollMirrorFreshness
|
|
5
|
+
} from "./chunk-HOTQDYHD.js";
|
|
1
6
|
import {
|
|
2
7
|
computeStats,
|
|
3
8
|
renderSummary,
|
|
@@ -18,24 +23,30 @@ import {
|
|
|
18
23
|
classifyFile,
|
|
19
24
|
parseManifest
|
|
20
25
|
} from "./chunk-S7EM5VMW.js";
|
|
26
|
+
import {
|
|
27
|
+
DOT_DAPP_ID,
|
|
28
|
+
DOT_PRODUCT_ID,
|
|
29
|
+
hasPersistedSession
|
|
30
|
+
} from "./chunk-YUSHBZBX.js";
|
|
21
31
|
import {
|
|
22
32
|
setDeployContext
|
|
23
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-AMGKEAOH.js";
|
|
24
34
|
import {
|
|
25
35
|
probeChunks
|
|
26
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-X2Q5FPIQ.js";
|
|
27
37
|
import {
|
|
28
38
|
packSection
|
|
29
39
|
} from "./chunk-C2TS5MER.js";
|
|
30
40
|
import {
|
|
31
41
|
DotNS,
|
|
42
|
+
PUBLISHER_ABI,
|
|
32
43
|
PublisherNotSupportedError,
|
|
33
44
|
TX_TIMEOUT_MS,
|
|
34
45
|
fetchNonce,
|
|
35
46
|
parseDomainName,
|
|
36
47
|
popStatusName,
|
|
37
48
|
verifyNonceAdvanced
|
|
38
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-4GX3KJPU.js";
|
|
39
50
|
import {
|
|
40
51
|
derivePoolAccounts,
|
|
41
52
|
detectTestnet,
|
|
@@ -57,7 +68,7 @@ import {
|
|
|
57
68
|
truncateAddress,
|
|
58
69
|
withDeploySpan,
|
|
59
70
|
withSpan
|
|
60
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-P2ZOBSCJ.js";
|
|
61
72
|
import {
|
|
62
73
|
DEFAULT_ENV_ID,
|
|
63
74
|
getPopSelfServeConfig,
|
|
@@ -67,11 +78,6 @@ import {
|
|
|
67
78
|
import {
|
|
68
79
|
NonRetryableError
|
|
69
80
|
} from "./chunk-ZOC4GITL.js";
|
|
70
|
-
import {
|
|
71
|
-
MirrorSkipped,
|
|
72
|
-
mirrorToGitHubPages,
|
|
73
|
-
pollMirrorFreshness
|
|
74
|
-
} from "./chunk-HOTQDYHD.js";
|
|
75
81
|
|
|
76
82
|
// src/merkle.ts
|
|
77
83
|
import * as fs2 from "fs";
|
|
@@ -91,19 +97,186 @@ import * as path from "path";
|
|
|
91
97
|
import { execSync } from "child_process";
|
|
92
98
|
import { sha256 } from "@noble/hashes/sha256";
|
|
93
99
|
import { blake2b } from "@noble/hashes/blake2b";
|
|
94
|
-
import { createClient as
|
|
95
|
-
import { getWsProvider, WsEvent } from "polkadot-api/ws";
|
|
100
|
+
import { createClient as createPolkadotClient2, Enum as Enum2 } from "polkadot-api";
|
|
101
|
+
import { getWsProvider as getWsProvider2, WsEvent } from "polkadot-api/ws";
|
|
96
102
|
import { CID } from "multiformats/cid";
|
|
97
103
|
import { create as createMultihash } from "multiformats/hashes/digest";
|
|
98
104
|
import { base32 } from "multiformats/bases/base32";
|
|
99
105
|
import { base58btc } from "multiformats/bases/base58";
|
|
100
106
|
import * as dagPB from "@ipld/dag-pb";
|
|
101
107
|
import { UnixFS } from "ipfs-unixfs";
|
|
108
|
+
import { keccak256, toBytes } from "viem";
|
|
102
109
|
import { cryptoWaitReady } from "@polkadot/util-crypto";
|
|
103
|
-
import { getPolkadotSigner } from "polkadot-api/signer";
|
|
104
|
-
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
105
|
-
import { mnemonicToEntropy, entropyToMiniSecret, ss58Address } from "@polkadot-labs/hdkd-helpers";
|
|
110
|
+
import { getPolkadotSigner as getPolkadotSigner2 } from "polkadot-api/signer";
|
|
111
|
+
import { sr25519CreateDerive as sr25519CreateDerive2 } from "@polkadot-labs/hdkd";
|
|
112
|
+
import { mnemonicToEntropy, entropyToMiniSecret, ss58Address as ss58Address2 } from "@polkadot-labs/hdkd-helpers";
|
|
106
113
|
import { CarReader } from "@ipld/car/reader";
|
|
114
|
+
|
|
115
|
+
// src/storage-signer.ts
|
|
116
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
117
|
+
import { homedir } from "os";
|
|
118
|
+
import { join, dirname } from "path";
|
|
119
|
+
|
|
120
|
+
// node_modules/@polkadot-api/utils/dist/hex.js
|
|
121
|
+
var HEX_STR = "0123456789abcdef";
|
|
122
|
+
function toHex(bytes) {
|
|
123
|
+
const result = new Array(bytes.length + 1);
|
|
124
|
+
result[0] = "0x";
|
|
125
|
+
for (let i = 0; i < bytes.length; ) {
|
|
126
|
+
const b = bytes[i++];
|
|
127
|
+
result[i] = HEX_STR[b >> 4] + HEX_STR[b & 15];
|
|
128
|
+
}
|
|
129
|
+
return result.join("");
|
|
130
|
+
}
|
|
131
|
+
var HEX_MAP = {
|
|
132
|
+
0: 0,
|
|
133
|
+
1: 1,
|
|
134
|
+
2: 2,
|
|
135
|
+
3: 3,
|
|
136
|
+
4: 4,
|
|
137
|
+
5: 5,
|
|
138
|
+
6: 6,
|
|
139
|
+
7: 7,
|
|
140
|
+
8: 8,
|
|
141
|
+
9: 9,
|
|
142
|
+
a: 10,
|
|
143
|
+
b: 11,
|
|
144
|
+
c: 12,
|
|
145
|
+
d: 13,
|
|
146
|
+
e: 14,
|
|
147
|
+
f: 15,
|
|
148
|
+
A: 10,
|
|
149
|
+
B: 11,
|
|
150
|
+
C: 12,
|
|
151
|
+
D: 13,
|
|
152
|
+
E: 14,
|
|
153
|
+
F: 15
|
|
154
|
+
};
|
|
155
|
+
function fromHex(hexString) {
|
|
156
|
+
const isOdd = hexString.length % 2;
|
|
157
|
+
const base = (hexString[1] === "x" ? 2 : 0) + isOdd;
|
|
158
|
+
const nBytes = (hexString.length - base) / 2 + isOdd;
|
|
159
|
+
const bytes = new Uint8Array(nBytes);
|
|
160
|
+
if (isOdd) bytes[0] = 0 | HEX_MAP[hexString[2]];
|
|
161
|
+
for (let i = 0; i < nBytes; ) {
|
|
162
|
+
const idx = base + i * 2;
|
|
163
|
+
const a = HEX_MAP[hexString[idx]];
|
|
164
|
+
const b = HEX_MAP[hexString[idx + 1]];
|
|
165
|
+
bytes[isOdd + i++] = a << 4 | b;
|
|
166
|
+
}
|
|
167
|
+
return bytes;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/storage-signer.ts
|
|
171
|
+
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
172
|
+
import { sr25519, ss58Address } from "@polkadot-labs/hdkd-helpers";
|
|
173
|
+
import { createClient as createPolkadotClient, Enum } from "polkadot-api";
|
|
174
|
+
import { getPolkadotSigner } from "polkadot-api/signer";
|
|
175
|
+
import { getWsProvider } from "polkadot-api/ws";
|
|
176
|
+
function sanitize(appId) {
|
|
177
|
+
return appId.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
178
|
+
}
|
|
179
|
+
function cacheFilePath(appId, storageDir) {
|
|
180
|
+
return join(storageDir ?? homedir(), ".polkadot-apps", `${sanitize(appId)}_AllowanceKeys.json`);
|
|
181
|
+
}
|
|
182
|
+
function normalizeSchnorrkelKey(key) {
|
|
183
|
+
if (key.length !== 64) return key;
|
|
184
|
+
const out = new Uint8Array(key);
|
|
185
|
+
let carry = 0;
|
|
186
|
+
for (let i = 0; i < 32; i++) {
|
|
187
|
+
const v = key[i] * 8 + carry;
|
|
188
|
+
out[i] = v & 255;
|
|
189
|
+
carry = v >> 8;
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
function signerFromSecret(secret) {
|
|
194
|
+
if (secret.length === 32) {
|
|
195
|
+
const kp = sr25519CreateDerive(secret)("");
|
|
196
|
+
return getPolkadotSigner(kp.publicKey, "Sr25519", async (d) => kp.sign(d));
|
|
197
|
+
}
|
|
198
|
+
if (secret.length === 64) {
|
|
199
|
+
const normalized = normalizeSchnorrkelKey(secret);
|
|
200
|
+
const pub = sr25519.getPublicKey(normalized);
|
|
201
|
+
return getPolkadotSigner(pub, "Sr25519", async (d) => sr25519.sign(d, normalized));
|
|
202
|
+
}
|
|
203
|
+
throw new Error(
|
|
204
|
+
`BulletInAllowance slot key: unexpected length ${secret.length} (expected 32 or 64)`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
async function readBulletinSlotSigner(appId, storageDir) {
|
|
208
|
+
let raw;
|
|
209
|
+
try {
|
|
210
|
+
raw = await readFile(cacheFilePath(appId, storageDir), "utf-8");
|
|
211
|
+
} catch (e) {
|
|
212
|
+
if (e?.code === "ENOENT") return null;
|
|
213
|
+
throw e;
|
|
214
|
+
}
|
|
215
|
+
let cache;
|
|
216
|
+
try {
|
|
217
|
+
cache = JSON.parse(raw);
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
const entry = cache?.entries?.BulletInAllowance;
|
|
222
|
+
if (!entry?.slotAccountKey) return null;
|
|
223
|
+
let secret;
|
|
224
|
+
try {
|
|
225
|
+
secret = fromHex(entry.slotAccountKey);
|
|
226
|
+
} catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
const signer = signerFromSecret(secret);
|
|
230
|
+
return { signer, ss58: ss58Address(signer.publicKey) };
|
|
231
|
+
}
|
|
232
|
+
async function writeBulletinSlotKey(appId, hexKey, storageDir) {
|
|
233
|
+
const path3 = cacheFilePath(appId, storageDir);
|
|
234
|
+
await mkdir(dirname(path3), { recursive: true, mode: 448 });
|
|
235
|
+
let existing = { version: 1, entries: {} };
|
|
236
|
+
try {
|
|
237
|
+
existing = JSON.parse(await readFile(path3, "utf-8"));
|
|
238
|
+
} catch {
|
|
239
|
+
}
|
|
240
|
+
existing.entries ??= {};
|
|
241
|
+
existing.entries.BulletInAllowance = { tag: "BulletInAllowance", slotAccountKey: hexKey };
|
|
242
|
+
await writeFile(path3, `${JSON.stringify(existing, null, 2)}
|
|
243
|
+
`, { mode: 384 });
|
|
244
|
+
}
|
|
245
|
+
function extractBulletinSlotKey(outcomes) {
|
|
246
|
+
for (const outcome of outcomes) {
|
|
247
|
+
if (outcome.tag !== "Allocated") continue;
|
|
248
|
+
const allocated = outcome.value;
|
|
249
|
+
if (allocated?.tag !== "BulletInAllowance") continue;
|
|
250
|
+
const key = allocated.value?.slotAccountKey;
|
|
251
|
+
if (!(key instanceof Uint8Array)) continue;
|
|
252
|
+
return toHex(key);
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
async function getSlotSignerProvider(signer, ss58) {
|
|
257
|
+
const primary = BULLETIN_ENDPOINTS[0];
|
|
258
|
+
console.log(` Connecting to Bulletin (slot signer): ${primary}`);
|
|
259
|
+
const client = createPolkadotClient(getWsProvider(
|
|
260
|
+
BULLETIN_ENDPOINTS,
|
|
261
|
+
{ heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
|
|
262
|
+
));
|
|
263
|
+
const unsafeApi = client.getUnsafeApi();
|
|
264
|
+
const [auth, currentBlock] = await Promise.all([
|
|
265
|
+
unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum("Account", ss58)),
|
|
266
|
+
client.getFinalizedBlock()
|
|
267
|
+
]);
|
|
268
|
+
const now = currentBlock.number;
|
|
269
|
+
if (!auth || Number(auth.expiration ?? 0) <= now) {
|
|
270
|
+
client.destroy();
|
|
271
|
+
throw new Error(`Slot account ${ss58} not authorized on Bulletin`);
|
|
272
|
+
}
|
|
273
|
+
console.log(` Using slot signer: ${ss58} (authorized until block ${Number(auth?.expiration ?? 0)})`);
|
|
274
|
+
setDeployAttribute("deploy.signer.mode", "slot");
|
|
275
|
+
setDeployAttribute("deploy.signer.address", truncateAddress(ss58));
|
|
276
|
+
return { client, unsafeApi, signer, ss58 };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/deploy.ts
|
|
107
280
|
function friendlyChainError(msg) {
|
|
108
281
|
if (/"type":\s*"Invalid"[\s\S]*?"type":\s*"Payment"/i.test(msg)) {
|
|
109
282
|
return "Bulletin quota exhausted (signed extension rejected the tx \u2014 signer is out of allowed txs or bytes; grant quota on-chain)";
|
|
@@ -166,10 +339,10 @@ var CID_CONFIG = { version: 1, codec: 85, hashCode: 18, hashLength: 32 };
|
|
|
166
339
|
function deriveRootSigner(mnemonic, path3 = "") {
|
|
167
340
|
const entropy = mnemonicToEntropy(mnemonic);
|
|
168
341
|
const miniSecret = entropyToMiniSecret(entropy);
|
|
169
|
-
const derive =
|
|
342
|
+
const derive = sr25519CreateDerive2(miniSecret);
|
|
170
343
|
const keyPair = derive(path3);
|
|
171
|
-
const signer =
|
|
172
|
-
return { signer, ss58:
|
|
344
|
+
const signer = getPolkadotSigner2(keyPair.publicKey, "Sr25519", keyPair.sign);
|
|
345
|
+
return { signer, ss58: ss58Address2(keyPair.publicKey) };
|
|
173
346
|
}
|
|
174
347
|
function createCID(data, codec = CID_CONFIG.codec, hashCode = CID_CONFIG.hashCode) {
|
|
175
348
|
let hash;
|
|
@@ -223,7 +396,7 @@ function toHashingEnum(mhCode) {
|
|
|
223
396
|
async function getProvider() {
|
|
224
397
|
const primary = BULLETIN_ENDPOINTS[0];
|
|
225
398
|
console.log(` Connecting to Bulletin: ${primary}`);
|
|
226
|
-
const client =
|
|
399
|
+
const client = createPolkadotClient2(getWsProvider2(
|
|
227
400
|
BULLETIN_ENDPOINTS,
|
|
228
401
|
{ heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
|
|
229
402
|
));
|
|
@@ -251,7 +424,7 @@ async function getProvider() {
|
|
|
251
424
|
async function getDirectProvider(mnemonic, derivationPath = "") {
|
|
252
425
|
const primary = BULLETIN_ENDPOINTS[0];
|
|
253
426
|
console.log(` Connecting to Bulletin: ${primary}`);
|
|
254
|
-
const client =
|
|
427
|
+
const client = createPolkadotClient2(getWsProvider2(
|
|
255
428
|
BULLETIN_ENDPOINTS,
|
|
256
429
|
{ heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
|
|
257
430
|
));
|
|
@@ -259,7 +432,7 @@ async function getDirectProvider(mnemonic, derivationPath = "") {
|
|
|
259
432
|
const { signer, ss58 } = deriveRootSigner(mnemonic, derivationPath);
|
|
260
433
|
console.log(` Using direct signer: ${ss58}${derivationPath ? ` (path: ${derivationPath})` : ""}`);
|
|
261
434
|
let [auth, currentBlock] = await Promise.all([
|
|
262
|
-
unsafeApi.query.TransactionStorage.Authorizations.getValue(
|
|
435
|
+
unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
|
|
263
436
|
client.getFinalizedBlock()
|
|
264
437
|
]);
|
|
265
438
|
let now = currentBlock.number;
|
|
@@ -267,7 +440,7 @@ async function getDirectProvider(mnemonic, derivationPath = "") {
|
|
|
267
440
|
try {
|
|
268
441
|
await ensureAuthorized(unsafeApi, ss58, "direct signer");
|
|
269
442
|
[auth, currentBlock] = await Promise.all([
|
|
270
|
-
unsafeApi.query.TransactionStorage.Authorizations.getValue(
|
|
443
|
+
unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
|
|
271
444
|
client.getFinalizedBlock()
|
|
272
445
|
]);
|
|
273
446
|
now = currentBlock.number;
|
|
@@ -284,14 +457,14 @@ async function getDirectProvider(mnemonic, derivationPath = "") {
|
|
|
284
457
|
async function getSignerProvider(signer, ss58) {
|
|
285
458
|
const primary = BULLETIN_ENDPOINTS[0];
|
|
286
459
|
console.log(` Connecting to Bulletin: ${primary}`);
|
|
287
|
-
const client =
|
|
460
|
+
const client = createPolkadotClient2(getWsProvider2(
|
|
288
461
|
BULLETIN_ENDPOINTS,
|
|
289
462
|
{ heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
|
|
290
463
|
));
|
|
291
464
|
const unsafeApi = client.getUnsafeApi();
|
|
292
465
|
console.log(` Using external signer: ${ss58}`);
|
|
293
466
|
let [auth, currentBlock] = await Promise.all([
|
|
294
|
-
unsafeApi.query.TransactionStorage.Authorizations.getValue(
|
|
467
|
+
unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
|
|
295
468
|
client.getFinalizedBlock()
|
|
296
469
|
]);
|
|
297
470
|
let now = currentBlock.number;
|
|
@@ -299,7 +472,7 @@ async function getSignerProvider(signer, ss58) {
|
|
|
299
472
|
try {
|
|
300
473
|
await ensureAuthorized(unsafeApi, ss58, "external signer");
|
|
301
474
|
[auth, currentBlock] = await Promise.all([
|
|
302
|
-
unsafeApi.query.TransactionStorage.Authorizations.getValue(
|
|
475
|
+
unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
|
|
303
476
|
client.getFinalizedBlock()
|
|
304
477
|
]);
|
|
305
478
|
now = currentBlock.number;
|
|
@@ -314,19 +487,37 @@ async function getSignerProvider(signer, ss58) {
|
|
|
314
487
|
return { client, unsafeApi, signer, ss58 };
|
|
315
488
|
}
|
|
316
489
|
function __selectStorageProviderModeForTest(options) {
|
|
490
|
+
if (options.storageSigner && options.storageSignerAddress) return "storageSigner";
|
|
317
491
|
if (options.signer && options.signerAddress) return "signer";
|
|
318
492
|
if (options.mnemonic) return "direct";
|
|
319
493
|
return "pool";
|
|
320
494
|
}
|
|
495
|
+
function chooseSignerInput(opts) {
|
|
496
|
+
if (opts.mnemonic) return "mnemonic";
|
|
497
|
+
if (opts.hasInjectedSigner) return "injected";
|
|
498
|
+
if (opts.suri) return "resolve";
|
|
499
|
+
if (opts.hasSession) return "resolve";
|
|
500
|
+
return "pool";
|
|
501
|
+
}
|
|
321
502
|
function selectStorageReconnect(options) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
503
|
+
if (options.storageSigner && options.storageSignerAddress) {
|
|
504
|
+
let useSlot = true;
|
|
505
|
+
return async () => {
|
|
506
|
+
if (!useSlot) return getProvider();
|
|
507
|
+
try {
|
|
508
|
+
return await getSlotSignerProvider(options.storageSigner, options.storageSignerAddress);
|
|
509
|
+
} catch {
|
|
510
|
+
useSlot = false;
|
|
511
|
+
setDeployAttribute("deploy.signer.mode", "pool-fallback");
|
|
512
|
+
return getProvider();
|
|
513
|
+
}
|
|
514
|
+
};
|
|
329
515
|
}
|
|
516
|
+
if (options.signer && options.signerAddress)
|
|
517
|
+
return () => getSignerProvider(options.signer, options.signerAddress);
|
|
518
|
+
if (options.mnemonic)
|
|
519
|
+
return () => getDirectProvider(options.mnemonic, options.derivationPath);
|
|
520
|
+
return () => getProvider();
|
|
330
521
|
}
|
|
331
522
|
function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction", rpc, senderSS58, expectedNonce, timeoutMs, fetchNonce: fetchNonceOverride } = {}) {
|
|
332
523
|
const timeout = timeoutMs ?? TX_TIMEOUT_MS;
|
|
@@ -491,7 +682,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
491
682
|
}
|
|
492
683
|
}
|
|
493
684
|
const readUploadAuthorization = () => Promise.all([
|
|
494
|
-
unsafeApi.query.TransactionStorage.Authorizations.getValue(
|
|
685
|
+
unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
|
|
495
686
|
unsafeApi.query.System.Number.getValue()
|
|
496
687
|
]);
|
|
497
688
|
let uploadAuth;
|
|
@@ -1120,7 +1311,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
1120
1311
|
useKubo = hasIPFS();
|
|
1121
1312
|
}
|
|
1122
1313
|
const phaseA = await withSpan("deploy.merkleize", `1a. merkleize (${useKubo ? "kubo" : "js"}, stable)`, { "deploy.directory": dirBasename, "deploy.merkle": useKubo ? "kubo" : "js" }, async () => {
|
|
1123
|
-
const r = await merkleizeWithStableOrder(directoryPath, prevManifest?.stableBlockOrder, { useKubo });
|
|
1314
|
+
const r = await merkleizeWithStableOrder(directoryPath, prevManifest?.stableBlockOrder, { useKubo, phase: "Phase A" });
|
|
1124
1315
|
sampleMemory("merkleize_end");
|
|
1125
1316
|
return r;
|
|
1126
1317
|
});
|
|
@@ -1239,7 +1430,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
1239
1430
|
carChunksA.length = 0;
|
|
1240
1431
|
phaseA.carBytes = new Uint8Array(0);
|
|
1241
1432
|
const phaseB = await withSpan("deploy.merkleize", "1c. merkleize (js, finalise)", { "deploy.directory": dirBasename }, async () => {
|
|
1242
|
-
const r = await merkleizeWithStableOrder(directoryPath, phaseA.stableOrder, { useKubo });
|
|
1433
|
+
const r = await merkleizeWithStableOrder(directoryPath, phaseA.stableOrder, { useKubo, phase: "Phase B" });
|
|
1243
1434
|
sampleMemory("merkleize_finalise_end");
|
|
1244
1435
|
return r;
|
|
1245
1436
|
});
|
|
@@ -1533,8 +1724,7 @@ async function publish(dotns, parsed, failOnError) {
|
|
|
1533
1724
|
setDeployAttribute("deploy.publish.status", "failed");
|
|
1534
1725
|
if (failOnError) throw e;
|
|
1535
1726
|
const msg = e?.message ?? String(e);
|
|
1536
|
-
console.
|
|
1537
|
-
captureWarning("publish failed", { error: msg.slice(0, 200) });
|
|
1727
|
+
console.log(` Publish failed: ${msg}`);
|
|
1538
1728
|
}
|
|
1539
1729
|
}
|
|
1540
1730
|
async function unpublish(domainName, options = {}) {
|
|
@@ -1654,359 +1844,478 @@ async function deploy(content, domainName = null, options = {}) {
|
|
|
1654
1844
|
BULLETIN_ENDPOINTS = userRpc ? [userRpc, ...envBulletin.filter((e) => e !== userRpc)] : envBulletin;
|
|
1655
1845
|
_deployRpcFailedOver = false;
|
|
1656
1846
|
POOL_SIZE = options.poolSize ?? parseInt(process.env.BULLETIN_POOL_SIZE ?? String(DEFAULT_POOL_SIZE), 10);
|
|
1847
|
+
let sessionCleanup;
|
|
1848
|
+
const hasSession = hasPersistedSession();
|
|
1849
|
+
const signerChoice = chooseSignerInput({
|
|
1850
|
+
mnemonic: options.mnemonic,
|
|
1851
|
+
suri: options.suri,
|
|
1852
|
+
hasInjectedSigner: !!(options.signer && options.signerAddress),
|
|
1853
|
+
hasSession
|
|
1854
|
+
});
|
|
1855
|
+
let resolvedUserSession = void 0;
|
|
1856
|
+
if (signerChoice === "resolve") {
|
|
1857
|
+
const { resolveSigner: resolveSignerFn } = await import("./auth/index.js");
|
|
1858
|
+
const { getAuthClient } = await import("./auth-config.js");
|
|
1859
|
+
const authClient = await getAuthClient(envId);
|
|
1860
|
+
try {
|
|
1861
|
+
const resolved = await resolveSignerFn(authClient, { suri: options.suri });
|
|
1862
|
+
options = { ...options, signer: resolved.signer, signerAddress: resolved.address };
|
|
1863
|
+
sessionCleanup = resolved.destroy.bind(resolved);
|
|
1864
|
+
console.log(` Using ${resolved.source} signer: ${resolved.address}`);
|
|
1865
|
+
if (resolved.source === "session") resolvedUserSession = resolved;
|
|
1866
|
+
} catch (e) {
|
|
1867
|
+
if (options.suri) throw e;
|
|
1868
|
+
if (e?.name === "SignerNotAvailableError") {
|
|
1869
|
+
console.log(
|
|
1870
|
+
" Login session unavailable or expired \u2014 falling back to pool. Run `bulletin-deploy login` to use your identity."
|
|
1871
|
+
);
|
|
1872
|
+
} else {
|
|
1873
|
+
throw e;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
if (!options.storageSigner) {
|
|
1878
|
+
try {
|
|
1879
|
+
let slotResult = await readBulletinSlotSigner(DOT_DAPP_ID);
|
|
1880
|
+
if (!slotResult && resolvedUserSession?.userSession) {
|
|
1881
|
+
const { requestResourceAllocation } = await import("./auth/index.js");
|
|
1882
|
+
console.log("Requesting Bulletin storage allowance \u2014 check your phone to approve");
|
|
1883
|
+
const outcomes = await requestResourceAllocation(
|
|
1884
|
+
resolvedUserSession.userSession,
|
|
1885
|
+
DOT_PRODUCT_ID,
|
|
1886
|
+
// StatementStoreAllowance is required for createTransaction (the session
|
|
1887
|
+
// signer uses it to send signing requests to the phone). Include it here
|
|
1888
|
+
// so a deploy without a prior `login` still works. onExisting:"Ignore"
|
|
1889
|
+
// makes this a no-op if the allowance was already granted by login.
|
|
1890
|
+
[
|
|
1891
|
+
{ tag: "BulletInAllowance", value: void 0 },
|
|
1892
|
+
{ tag: "StatementStoreAllowance", value: void 0 }
|
|
1893
|
+
]
|
|
1894
|
+
);
|
|
1895
|
+
const hexKey = extractBulletinSlotKey(outcomes);
|
|
1896
|
+
if (hexKey) {
|
|
1897
|
+
await writeBulletinSlotKey(DOT_DAPP_ID, hexKey);
|
|
1898
|
+
slotResult = await readBulletinSlotSigner(DOT_DAPP_ID);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
if (slotResult) {
|
|
1902
|
+
options = { ...options, storageSigner: slotResult.signer, storageSignerAddress: slotResult.ss58 };
|
|
1903
|
+
}
|
|
1904
|
+
} catch {
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1657
1907
|
initTelemetry();
|
|
1658
1908
|
const randomSuffix = Math.floor(Math.random() * 100).toString().padStart(2, "0");
|
|
1659
1909
|
const parsed = domainName ? parseDomainName(domainName) : null;
|
|
1660
1910
|
const name = parsed ? parsed.label : `test-domain-${Date.now().toString(36)}${randomSuffix}`;
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
setDeployAttribute("deploy.env", envId);
|
|
1668
|
-
setDeployAttribute("deploy.label", parsed?.label ?? name);
|
|
1669
|
-
setDeployAttribute("deploy.subdomain", String(parsed?.isSubdomain ?? false));
|
|
1670
|
-
if (envNetwork) setDeployAttribute("deploy.network", envNetwork);
|
|
1671
|
-
if (envSource) setDeployAttribute("deploy.environments_source", envSource);
|
|
1672
|
-
let cid;
|
|
1673
|
-
let ipfsCid;
|
|
1674
|
-
let mirrorPromise = Promise.resolve(null);
|
|
1675
|
-
console.log("\n" + "=".repeat(60));
|
|
1676
|
-
console.log(`DEPLOYING TO TESTNET v${VERSION}`);
|
|
1677
|
-
console.log("=".repeat(60));
|
|
1678
|
-
if (envName) console.log(` Environment: ${envName}`);
|
|
1679
|
-
console.log(` Domain: ${name}.dot`);
|
|
1680
|
-
if (deployTag) console.log(` Tag: ${deployTag}`);
|
|
1681
|
-
if (options.inputCar) console.log(` Input CAR: ${path.resolve(options.inputCar)}`);
|
|
1682
|
-
else if (typeof content === "string") console.log(` Build dir: ${path.resolve(content)}`);
|
|
1683
|
-
if (process.env.CI) console.log(` Runner: ${resolveRunner()} (${resolveRunnerType()})`);
|
|
1684
|
-
if (options.password) console.log(` Encrypted: yes`);
|
|
1685
|
-
let provider;
|
|
1686
|
-
const reconnect = selectStorageReconnect(options);
|
|
1687
|
-
let dotnsPreflight = null;
|
|
1688
|
-
let previousContenthashCid = null;
|
|
1689
|
-
try {
|
|
1690
|
-
console.log("\n" + "=".repeat(60));
|
|
1691
|
-
console.log("Preflight");
|
|
1692
|
-
console.log("=".repeat(60));
|
|
1693
|
-
const preflight = new DotNS();
|
|
1694
|
-
await preflight.connect(resolveDotnsConnectOptions(options, envAssetHub, envAutoAccountMapping, envContracts, envNativeToEthRatio, envId, envPopSelfServe, envRegisterStorageDeposit));
|
|
1695
|
-
if (parsed?.isSubdomain) {
|
|
1696
|
-
try {
|
|
1697
|
-
const subResult = await preflight.checkSubdomainOwnership(parsed.sublabel, parsed.parentLabel);
|
|
1698
|
-
assertSubdomainOwnerMatchesSigner(subResult, preflight.evmAddress, parsed.sublabel, parsed.parentLabel);
|
|
1699
|
-
if (!subResult.owned) {
|
|
1700
|
-
const { owned: parentOwned, owner: parentOwner } = await preflight.checkOwnership(parsed.parentLabel);
|
|
1701
|
-
if (!parentOwned) {
|
|
1702
|
-
throw new NonRetryableError(
|
|
1703
|
-
`Cannot deploy ${parsed.fullName}: parent ${parsed.parentLabel}.dot is owned by ${parentOwner ?? "no one"}, not by this signer.`
|
|
1704
|
-
);
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
previousContenthashCid = await readPreviousContenthashSafe(preflight, parsed.fullName);
|
|
1708
|
-
setDeployAttribute("deploy.incremental", previousContenthashCid ? "true" : "false");
|
|
1709
|
-
} finally {
|
|
1710
|
-
preflight.disconnect();
|
|
1711
|
-
}
|
|
1712
|
-
console.log(` Mode: subdomain (parent ${parsed.parentLabel}.dot owned by signer)`);
|
|
1713
|
-
} else {
|
|
1714
|
-
try {
|
|
1715
|
-
dotnsPreflight = await preflight.preflight(name);
|
|
1716
|
-
previousContenthashCid = await readPreviousContenthashSafe(preflight, name);
|
|
1717
|
-
setDeployAttribute("deploy.incremental", previousContenthashCid ? "true" : "false");
|
|
1718
|
-
} finally {
|
|
1719
|
-
preflight.disconnect();
|
|
1720
|
-
}
|
|
1721
|
-
if (dotnsPreflight) {
|
|
1722
|
-
setDeployAttribute("deploy.dotns.preflight.action", dotnsPreflight.plannedAction);
|
|
1723
|
-
setDeployAttribute("deploy.dotns.preflight.classification", popStatusName(dotnsPreflight.classification.status));
|
|
1724
|
-
}
|
|
1725
|
-
const alreadyOwned = dotnsPreflight.plannedAction === "already-owned-by-us";
|
|
1726
|
-
const reqSuffix = alreadyOwned ? " (already owned, requirement not enforced)" : "";
|
|
1727
|
-
console.log(` DotNS: ${name}.dot requires ${popStatusName(dotnsPreflight.classification.status)}${reqSuffix}`);
|
|
1728
|
-
if (dotnsPreflight.canProceed) {
|
|
1729
|
-
const fromName = popStatusName(dotnsPreflight.userStatus);
|
|
1730
|
-
console.log(` Your PoP: ${fromName}`);
|
|
1731
|
-
console.log(` Domain: ${dotnsPreflight.plannedAction === "already-owned-by-us" ? "owned by you" : "available"}`);
|
|
1732
|
-
}
|
|
1733
|
-
if (!dotnsPreflight.canProceed) {
|
|
1734
|
-
throw new NonRetryableError(
|
|
1735
|
-
dotnsPreflight.reason ?? "DotNS preflight rejected the deploy; please check the label and signer."
|
|
1736
|
-
);
|
|
1737
|
-
}
|
|
1911
|
+
try {
|
|
1912
|
+
return await withDeploySpan(name, async () => {
|
|
1913
|
+
const deployTag = options.tag ?? process.env.DEPLOY_TAG;
|
|
1914
|
+
if (deployTag) {
|
|
1915
|
+
setDeployAttribute("deploy.tag", deployTag);
|
|
1916
|
+
setDeploySentryTag("deploy.tag", deployTag);
|
|
1738
1917
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
setDeployAttribute("deploy.
|
|
1918
|
+
setDeployAttribute("deploy.env", envId);
|
|
1919
|
+
setDeployAttribute("deploy.label", parsed?.label ?? name);
|
|
1920
|
+
setDeployAttribute("deploy.subdomain", String(parsed?.isSubdomain ?? false));
|
|
1921
|
+
if (envNetwork) setDeployAttribute("deploy.network", envNetwork);
|
|
1922
|
+
if (envSource) setDeployAttribute("deploy.environments_source", envSource);
|
|
1923
|
+
let cid;
|
|
1924
|
+
let ipfsCid;
|
|
1925
|
+
let mirrorPromise = Promise.resolve(null);
|
|
1743
1926
|
console.log("\n" + "=".repeat(60));
|
|
1744
|
-
console.log(
|
|
1927
|
+
console.log(`DEPLOYING TO TESTNET v${VERSION}`);
|
|
1745
1928
|
console.log("=".repeat(60));
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
let prevStableOrder = [];
|
|
1775
|
-
const manifestBytes = await extractManifestFromCar(carContent);
|
|
1776
|
-
if (manifestBytes) {
|
|
1777
|
-
const parsed2 = parseManifest(Buffer.from(manifestBytes).toString("utf8"));
|
|
1778
|
-
if (parsed2.ok) prevStableOrder = parsed2.manifest.stableBlockOrder;
|
|
1929
|
+
if (envName) console.log(` Environment: ${envName}`);
|
|
1930
|
+
console.log(` Domain: ${name}.dot`);
|
|
1931
|
+
if (deployTag) console.log(` Tag: ${deployTag}`);
|
|
1932
|
+
if (options.inputCar) console.log(` Input CAR: ${path.resolve(options.inputCar)}`);
|
|
1933
|
+
else if (typeof content === "string") console.log(` Build dir: ${path.resolve(content)}`);
|
|
1934
|
+
if (process.env.CI) console.log(` Runner: ${resolveRunner()} (${resolveRunnerType()})`);
|
|
1935
|
+
if (options.password) console.log(` Encrypted: yes`);
|
|
1936
|
+
let provider;
|
|
1937
|
+
const reconnect = selectStorageReconnect(options);
|
|
1938
|
+
let dotnsPreflight = null;
|
|
1939
|
+
let previousContenthashCid = null;
|
|
1940
|
+
let preflightPublishNeeded = false;
|
|
1941
|
+
try {
|
|
1942
|
+
console.log("\n" + "=".repeat(60));
|
|
1943
|
+
console.log("Preflight");
|
|
1944
|
+
console.log("=".repeat(60));
|
|
1945
|
+
const preflight = new DotNS();
|
|
1946
|
+
await preflight.connect(resolveDotnsConnectOptions(options, envAssetHub, envAutoAccountMapping, envContracts, envNativeToEthRatio, envId, envPopSelfServe, envRegisterStorageDeposit));
|
|
1947
|
+
if (parsed?.isSubdomain) {
|
|
1948
|
+
try {
|
|
1949
|
+
const subResult = await preflight.checkSubdomainOwnership(parsed.sublabel, parsed.parentLabel);
|
|
1950
|
+
assertSubdomainOwnerMatchesSigner(subResult, preflight.evmAddress, parsed.sublabel, parsed.parentLabel);
|
|
1951
|
+
if (!subResult.owned) {
|
|
1952
|
+
const { owned: parentOwned, owner: parentOwner } = await preflight.checkOwnership(parsed.parentLabel);
|
|
1953
|
+
if (!parentOwned) {
|
|
1954
|
+
throw new NonRetryableError(
|
|
1955
|
+
`Cannot deploy ${parsed.fullName}: parent ${parsed.parentLabel}.dot is owned by ${parentOwner ?? "no one"}, not by this signer.`
|
|
1956
|
+
);
|
|
1779
1957
|
}
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1958
|
+
}
|
|
1959
|
+
previousContenthashCid = await readPreviousContenthashSafe(preflight, parsed.fullName);
|
|
1960
|
+
setDeployAttribute("deploy.incremental", previousContenthashCid ? "true" : "false");
|
|
1961
|
+
} finally {
|
|
1962
|
+
preflight.disconnect();
|
|
1963
|
+
}
|
|
1964
|
+
console.log(` Mode: subdomain (parent ${parsed.parentLabel}.dot owned by signer)`);
|
|
1965
|
+
} else {
|
|
1966
|
+
preflightPublishNeeded = false;
|
|
1967
|
+
try {
|
|
1968
|
+
dotnsPreflight = await preflight.preflight(name);
|
|
1969
|
+
previousContenthashCid = await readPreviousContenthashSafe(preflight, name);
|
|
1970
|
+
setDeployAttribute("deploy.incremental", previousContenthashCid ? "true" : "false");
|
|
1971
|
+
if (options.publish && parsed && !parsed.isSubdomain) {
|
|
1972
|
+
const publisher = preflight._contracts?.PUBLISHER;
|
|
1973
|
+
const zeroAddr = "0x0000000000000000000000000000000000000000";
|
|
1974
|
+
if (!publisher || publisher === zeroAddr) {
|
|
1975
|
+
console.log(` Publish: not supported on this environment \u2014 will be skipped`);
|
|
1783
1976
|
} else {
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1977
|
+
const labelhash = keccak256(toBytes(name));
|
|
1978
|
+
try {
|
|
1979
|
+
const alreadyPublished = await preflight.contractCall(
|
|
1980
|
+
publisher,
|
|
1981
|
+
PUBLISHER_ABI,
|
|
1982
|
+
"isPublished",
|
|
1983
|
+
[labelhash]
|
|
1984
|
+
);
|
|
1985
|
+
preflightPublishNeeded = !alreadyPublished;
|
|
1986
|
+
if (!preflightPublishNeeded) {
|
|
1987
|
+
console.log(` Publish: already published \u2014 will be skipped`);
|
|
1988
|
+
}
|
|
1989
|
+
} catch {
|
|
1990
|
+
preflightPublishNeeded = true;
|
|
1991
|
+
}
|
|
1788
1992
|
}
|
|
1789
|
-
} catch (err) {
|
|
1790
|
-
captureWarning("input CAR ordered rechunk failed; falling back to size chunking", {
|
|
1791
|
-
rootCid: ipfsCid,
|
|
1792
|
-
reason: err?.message ?? String(err)
|
|
1793
|
-
});
|
|
1794
|
-
carChunks = chunk(carContent, CHUNK_SIZE);
|
|
1795
1993
|
}
|
|
1994
|
+
} finally {
|
|
1995
|
+
preflight.disconnect();
|
|
1996
|
+
}
|
|
1997
|
+
if (dotnsPreflight) {
|
|
1998
|
+
setDeployAttribute("deploy.dotns.preflight.action", dotnsPreflight.plannedAction);
|
|
1999
|
+
setDeployAttribute("deploy.dotns.preflight.classification", popStatusName(dotnsPreflight.classification.status));
|
|
1796
2000
|
}
|
|
1797
|
-
const
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
bulletinRpc: BULLETIN_ENDPOINTS[0],
|
|
1805
|
-
encrypted: Boolean(options.password)
|
|
1806
|
-
}).catch((err) => err instanceof Error ? err : new Error(String(err)));
|
|
2001
|
+
const alreadyOwned = dotnsPreflight.plannedAction === "already-owned-by-us";
|
|
2002
|
+
const reqSuffix = alreadyOwned ? " (already owned, requirement not enforced)" : "";
|
|
2003
|
+
console.log(` DotNS: ${name}.dot requires ${popStatusName(dotnsPreflight.classification.status)}${reqSuffix}`);
|
|
2004
|
+
if (dotnsPreflight.canProceed) {
|
|
2005
|
+
const fromName = popStatusName(dotnsPreflight.userStatus);
|
|
2006
|
+
console.log(` Your PoP: ${fromName}`);
|
|
2007
|
+
console.log(` Domain: ${dotnsPreflight.plannedAction === "already-owned-by-us" ? "owned by you" : "available"}`);
|
|
1807
2008
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
if (options.password) {
|
|
1812
|
-
throw new Error(
|
|
1813
|
-
"IPFS_CID and --password are mutually exclusive: IPFS_CID skips the upload step, so there is nothing to encrypt. Either unset IPFS_CID to upload and encrypt fresh content, or remove --password to reuse the existing CID as-is."
|
|
2009
|
+
if (!dotnsPreflight.canProceed) {
|
|
2010
|
+
throw new NonRetryableError(
|
|
2011
|
+
dotnsPreflight.reason ?? "DotNS preflight rejected the deploy; please check the label and signer."
|
|
1814
2012
|
);
|
|
1815
2013
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
console.log(` Encrypted: ${(encrypted.length / 1024).toFixed(1)} KB`);
|
|
1830
|
-
contentChunks = chunk(encrypted);
|
|
2014
|
+
}
|
|
2015
|
+
if (options.signer && options.signerAddress) {
|
|
2016
|
+
const steps = computePhoneSigningSteps(dotnsPreflight, preflightPublishNeeded);
|
|
2017
|
+
if (steps.length === 1) {
|
|
2018
|
+
console.log(`
|
|
2019
|
+
\u{1F4F1} Have your phone ready \u2014 1 signature needed (${steps[0].toLowerCase()})`);
|
|
2020
|
+
} else if (steps.length > 1) {
|
|
2021
|
+
const display = steps.flatMap(
|
|
2022
|
+
(s, i) => s === "Register" && steps[i - 1] === "Commitment" ? ["(wait)", s] : [s]
|
|
2023
|
+
);
|
|
2024
|
+
console.log(`
|
|
2025
|
+
\u{1F4F1} Have your phone ready \u2014 ${steps.length} signatures needed`);
|
|
2026
|
+
console.log(` ${display.map((s) => s.toLowerCase()).join(" \xB7 ")}`);
|
|
1831
2027
|
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
2028
|
+
}
|
|
2029
|
+
provider = await reconnect();
|
|
2030
|
+
const providerWithReconnect = { ...provider, reconnect };
|
|
2031
|
+
const isTestnet = await detectTestnet(provider.unsafeApi);
|
|
2032
|
+
setDeployAttribute("deploy.is_testnet", isTestnet ? "true" : "false");
|
|
2033
|
+
console.log("\n" + "=".repeat(60));
|
|
2034
|
+
console.log("Storage");
|
|
2035
|
+
console.log("=".repeat(60));
|
|
2036
|
+
setDeployAttribute("deploy.content_type", "unknown");
|
|
2037
|
+
setDeployAttribute("deploy.encrypted", "false");
|
|
2038
|
+
await withSpan("deploy.storage", "1. storage", {}, async () => {
|
|
2039
|
+
if (options.inputCar) {
|
|
2040
|
+
setDeployAttribute("deploy.content_type", "inputCar");
|
|
2041
|
+
const carPath = path.resolve(options.inputCar);
|
|
2042
|
+
if (!fs.existsSync(carPath)) throw new Error(`CAR file not found: ${carPath}`);
|
|
1840
2043
|
console.log(`
|
|
1841
|
-
Mode:
|
|
1842
|
-
console.log(` Path: ${
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
const
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
2044
|
+
Mode: Pre-built CAR`);
|
|
2045
|
+
console.log(` Path: ${carPath}`);
|
|
2046
|
+
let carContent = fs.readFileSync(carPath);
|
|
2047
|
+
console.log(` Size: ${(carContent.length / 1024 / 1024).toFixed(2)} MB`);
|
|
2048
|
+
const reader = await CarReader.fromBytes(carContent);
|
|
2049
|
+
const roots = await reader.getRoots();
|
|
2050
|
+
if (roots.length === 0) throw new Error("CAR file has no roots");
|
|
2051
|
+
ipfsCid = roots[0].toString();
|
|
2052
|
+
console.log(` Root CID: ${ipfsCid}`);
|
|
2053
|
+
if (options.password) {
|
|
2054
|
+
setDeployAttribute("deploy.encrypted", "true");
|
|
2055
|
+
console.log(` Encrypting CAR file...`);
|
|
2056
|
+
carContent = await encryptContent(carContent, options.password);
|
|
2057
|
+
console.log(` Encrypted: ${(carContent.length / 1024 / 1024).toFixed(2)} MB`);
|
|
2058
|
+
}
|
|
2059
|
+
let carChunks;
|
|
2060
|
+
if (options.password) {
|
|
2061
|
+
carChunks = chunk(carContent, CHUNK_SIZE);
|
|
2062
|
+
} else {
|
|
2063
|
+
try {
|
|
2064
|
+
let prevStableOrder = [];
|
|
2065
|
+
const manifestBytes = await extractManifestFromCar(carContent);
|
|
2066
|
+
if (manifestBytes) {
|
|
2067
|
+
const parsed2 = parseManifest(Buffer.from(manifestBytes).toString("utf8"));
|
|
2068
|
+
if (parsed2.ok) prevStableOrder = parsed2.manifest.stableBlockOrder;
|
|
2069
|
+
}
|
|
2070
|
+
const rebuilt = await rebuildOrderedCarFromBytes(carContent, prevStableOrder);
|
|
2071
|
+
if (Buffer.compare(Buffer.from(rebuilt.carBytes), Buffer.from(carContent)) === 0) {
|
|
2072
|
+
carChunks = rebuilt.chunks;
|
|
2073
|
+
} else {
|
|
2074
|
+
captureWarning("input CAR ordered rechunk drift; falling back to size chunking", {
|
|
2075
|
+
rootCid: ipfsCid
|
|
2076
|
+
});
|
|
2077
|
+
carChunks = chunk(carContent, CHUNK_SIZE);
|
|
1867
2078
|
}
|
|
2079
|
+
} catch (err) {
|
|
2080
|
+
captureWarning("input CAR ordered rechunk failed; falling back to size chunking", {
|
|
2081
|
+
rootCid: ipfsCid,
|
|
2082
|
+
reason: err?.message ?? String(err)
|
|
2083
|
+
});
|
|
2084
|
+
carChunks = chunk(carContent, CHUNK_SIZE);
|
|
1868
2085
|
}
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
2086
|
+
}
|
|
2087
|
+
const predictedStorageCid = computeStorageCid(carChunks);
|
|
2088
|
+
if (options.ghPagesMirror) {
|
|
2089
|
+
mirrorPromise = mirrorToGitHubPages({
|
|
2090
|
+
domain: name,
|
|
2091
|
+
carBytes: carContent,
|
|
2092
|
+
cid: predictedStorageCid,
|
|
2093
|
+
toolVersion: VERSION,
|
|
2094
|
+
bulletinRpc: BULLETIN_ENDPOINTS[0],
|
|
2095
|
+
encrypted: Boolean(options.password)
|
|
2096
|
+
}).catch((err) => err instanceof Error ? err : new Error(String(err)));
|
|
2097
|
+
}
|
|
2098
|
+
cid = (await storeChunkedContent(carChunks, providerWithReconnect)).storageCid;
|
|
2099
|
+
} else if (process.env.IPFS_CID) {
|
|
2100
|
+
setDeployAttribute("deploy.content_type", "ipfsCid");
|
|
2101
|
+
if (options.password) {
|
|
2102
|
+
throw new Error(
|
|
2103
|
+
"IPFS_CID and --password are mutually exclusive: IPFS_CID skips the upload step, so there is nothing to encrypt. Either unset IPFS_CID to upload and encrypt fresh content, or remove --password to reuse the existing CID as-is."
|
|
2104
|
+
);
|
|
2105
|
+
}
|
|
2106
|
+
cid = process.env.IPFS_CID;
|
|
2107
|
+
ipfsCid = cid;
|
|
2108
|
+
console.log(`
|
|
2109
|
+
Using CID: ${cid}`);
|
|
2110
|
+
} else if (Array.isArray(content)) {
|
|
2111
|
+
setDeployAttribute("deploy.content_type", "multiChunk");
|
|
1874
2112
|
console.log(`
|
|
2113
|
+
Mode: Multi-chunk (${content.length} chunks)`);
|
|
2114
|
+
let contentChunks = content;
|
|
2115
|
+
if (options.password) {
|
|
2116
|
+
setDeployAttribute("deploy.encrypted", "true");
|
|
2117
|
+
console.log(` Encrypting...`);
|
|
2118
|
+
const encrypted = await encryptContent(Buffer.concat(content), options.password);
|
|
2119
|
+
console.log(` Encrypted: ${(encrypted.length / 1024).toFixed(1)} KB`);
|
|
2120
|
+
contentChunks = chunk(encrypted);
|
|
2121
|
+
}
|
|
2122
|
+
cid = (await storeChunkedContent(contentChunks, providerWithReconnect)).storageCid;
|
|
2123
|
+
} else if (typeof content === "string") {
|
|
2124
|
+
setDeployAttribute("deploy.content_type", "path");
|
|
2125
|
+
const contentPath = path.resolve(content);
|
|
2126
|
+
if (!fs.existsSync(contentPath)) throw new Error(`Path not found: ${contentPath}`);
|
|
2127
|
+
const stats = fs.statSync(contentPath);
|
|
2128
|
+
if (stats.isDirectory()) {
|
|
2129
|
+
setDeployAttribute("deploy.content_type", "directory");
|
|
2130
|
+
console.log(`
|
|
2131
|
+
Mode: Directory`);
|
|
2132
|
+
console.log(` Path: ${contentPath}`);
|
|
2133
|
+
if (previousContenthashCid) console.log(` Incremental: previous contenthash ${previousContenthashCid}`);
|
|
2134
|
+
else console.log(` Incremental: first deploy (no previous contenthash)`);
|
|
2135
|
+
if (options.password) setDeployAttribute("deploy.encrypted", "true");
|
|
2136
|
+
const storeFn = options.password ? storeDirectory : storeDirectoryV2;
|
|
2137
|
+
const { storageCid: sCid, ipfsCid: iCid } = await storeFn(contentPath, {
|
|
2138
|
+
provider: providerWithReconnect,
|
|
2139
|
+
password: options.password,
|
|
2140
|
+
jsMerkle: options.jsMerkle,
|
|
2141
|
+
previousContenthash: previousContenthashCid,
|
|
2142
|
+
allowLargeDeploy: options.allowLargeDeploy,
|
|
2143
|
+
reproducibleSource: options.reproducibleSource,
|
|
2144
|
+
domain: name,
|
|
2145
|
+
gateway: envIpfs,
|
|
2146
|
+
dumpCar: options.dumpCar,
|
|
2147
|
+
onCarReady: (carBytes, predictedCid) => {
|
|
2148
|
+
if (options.ghPagesMirror) {
|
|
2149
|
+
mirrorPromise = mirrorToGitHubPages({
|
|
2150
|
+
domain: name,
|
|
2151
|
+
carBytes,
|
|
2152
|
+
cid: predictedCid,
|
|
2153
|
+
toolVersion: VERSION,
|
|
2154
|
+
bulletinRpc: BULLETIN_ENDPOINTS[0],
|
|
2155
|
+
encrypted: Boolean(options.password)
|
|
2156
|
+
}).catch((err) => err instanceof Error ? err : new Error(String(err)));
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
});
|
|
2160
|
+
cid = sCid;
|
|
2161
|
+
ipfsCid = iCid;
|
|
2162
|
+
} else {
|
|
2163
|
+
setDeployAttribute("deploy.content_type", "file");
|
|
2164
|
+
console.log(`
|
|
1875
2165
|
Mode: File`);
|
|
1876
|
-
|
|
1877
|
-
|
|
2166
|
+
console.log(` Path: ${contentPath}`);
|
|
2167
|
+
let fileContent = fs.readFileSync(contentPath);
|
|
2168
|
+
if (options.password) {
|
|
2169
|
+
setDeployAttribute("deploy.encrypted", "true");
|
|
2170
|
+
console.log(` Encrypting...`);
|
|
2171
|
+
fileContent = await encryptContent(fileContent, options.password);
|
|
2172
|
+
console.log(` Encrypted: ${(fileContent.length / 1024).toFixed(1)} KB`);
|
|
2173
|
+
}
|
|
2174
|
+
if (fileContent.length > MAX_FILE_SIZE) {
|
|
2175
|
+
console.log(` Exceeds 8MB, chunking...`);
|
|
2176
|
+
cid = (await storeChunkedContent(chunk(fileContent), providerWithReconnect)).storageCid;
|
|
2177
|
+
} else {
|
|
2178
|
+
cid = await storeFile(fileContent, providerWithReconnect);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
} else if (content instanceof Uint8Array) {
|
|
2182
|
+
setDeployAttribute("deploy.content_type", "multiChunk");
|
|
2183
|
+
console.log(`
|
|
2184
|
+
Mode: Bytes`);
|
|
2185
|
+
let bytesContent = content;
|
|
1878
2186
|
if (options.password) {
|
|
1879
2187
|
setDeployAttribute("deploy.encrypted", "true");
|
|
1880
2188
|
console.log(` Encrypting...`);
|
|
1881
|
-
|
|
1882
|
-
console.log(` Encrypted: ${(
|
|
2189
|
+
bytesContent = await encryptContent(bytesContent, options.password);
|
|
2190
|
+
console.log(` Encrypted: ${(bytesContent.length / 1024).toFixed(1)} KB`);
|
|
1883
2191
|
}
|
|
1884
|
-
if (
|
|
2192
|
+
if (bytesContent.length > MAX_FILE_SIZE) {
|
|
1885
2193
|
console.log(` Exceeds 8MB, chunking...`);
|
|
1886
|
-
cid = (await storeChunkedContent(chunk(
|
|
2194
|
+
cid = (await storeChunkedContent(chunk(bytesContent), providerWithReconnect)).storageCid;
|
|
1887
2195
|
} else {
|
|
1888
|
-
cid = await storeFile(
|
|
2196
|
+
cid = await storeFile(bytesContent, providerWithReconnect);
|
|
1889
2197
|
}
|
|
1890
|
-
}
|
|
1891
|
-
} else if (content instanceof Uint8Array) {
|
|
1892
|
-
setDeployAttribute("deploy.content_type", "multiChunk");
|
|
1893
|
-
console.log(`
|
|
1894
|
-
Mode: Bytes`);
|
|
1895
|
-
let bytesContent = content;
|
|
1896
|
-
if (options.password) {
|
|
1897
|
-
setDeployAttribute("deploy.encrypted", "true");
|
|
1898
|
-
console.log(` Encrypting...`);
|
|
1899
|
-
bytesContent = await encryptContent(bytesContent, options.password);
|
|
1900
|
-
console.log(` Encrypted: ${(bytesContent.length / 1024).toFixed(1)} KB`);
|
|
1901
|
-
}
|
|
1902
|
-
if (bytesContent.length > MAX_FILE_SIZE) {
|
|
1903
|
-
console.log(` Exceeds 8MB, chunking...`);
|
|
1904
|
-
cid = (await storeChunkedContent(chunk(bytesContent), providerWithReconnect)).storageCid;
|
|
1905
|
-
} else {
|
|
1906
|
-
cid = await storeFile(bytesContent, providerWithReconnect);
|
|
1907
|
-
}
|
|
1908
|
-
} else {
|
|
1909
|
-
throw new Error("Invalid content: must be path, Uint8Array, or Array<Uint8Array>");
|
|
1910
|
-
}
|
|
1911
|
-
});
|
|
1912
|
-
setDeployAttribute("deploy.cid", cid);
|
|
1913
|
-
if (options.attributes) {
|
|
1914
|
-
for (const [key, value] of Object.entries(options.attributes)) {
|
|
1915
|
-
setDeployAttribute(key, value);
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
console.log("\n" + "=".repeat(60));
|
|
1919
|
-
console.log("DotNS");
|
|
1920
|
-
console.log("=".repeat(60));
|
|
1921
|
-
await withSpan("deploy.dotns", "2. dotns", { "deploy.domain": name, "deploy.subdomain": String(parsed?.isSubdomain ?? false) }, async () => {
|
|
1922
|
-
const dotns = new DotNS();
|
|
1923
|
-
await dotns.connect(resolveDotnsConnectOptions(options, envAssetHub, envAutoAccountMapping, envContracts, envNativeToEthRatio, envId, envPopSelfServe, envRegisterStorageDeposit));
|
|
1924
|
-
if (parsed?.isSubdomain) {
|
|
1925
|
-
const { owned, owner } = await dotns.checkSubdomainOwnership(parsed.sublabel, parsed.parentLabel);
|
|
1926
|
-
if (owned) {
|
|
1927
|
-
console.log(` Status: Already owned`);
|
|
1928
|
-
} else if (owner) {
|
|
1929
|
-
throw new Error(`Subdomain ${parsed.fullName} is owned by ${owner}, not ${dotns.evmAddress}`);
|
|
1930
2198
|
} else {
|
|
1931
|
-
|
|
1932
|
-
if (!parentOwnership.owned) throw new Error(`You must own ${parsed.parentLabel}.dot to register subdomains under it`);
|
|
1933
|
-
console.log(` Status: Registering subdomain...`);
|
|
1934
|
-
await dotns.registerSubdomain(parsed.sublabel, parsed.parentLabel);
|
|
2199
|
+
throw new Error("Invalid content: must be path, Uint8Array, or Array<Uint8Array>");
|
|
1935
2200
|
}
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
console.log(` Status: Registering...`);
|
|
1942
|
-
await dotns.register(name);
|
|
2201
|
+
});
|
|
2202
|
+
setDeployAttribute("deploy.cid", cid);
|
|
2203
|
+
if (options.attributes) {
|
|
2204
|
+
for (const [key, value] of Object.entries(options.attributes)) {
|
|
2205
|
+
setDeployAttribute(key, value);
|
|
1943
2206
|
}
|
|
1944
2207
|
}
|
|
1945
|
-
const contenthashHex = `0x${encodeContenthash(cid)}`;
|
|
1946
|
-
await dotns.setContenthash(name, contenthashHex);
|
|
1947
|
-
if (options.publish && parsed) {
|
|
1948
|
-
await publish(dotns, parsed, options.failOnPublishError);
|
|
1949
|
-
}
|
|
1950
|
-
dotns.disconnect();
|
|
1951
|
-
});
|
|
1952
|
-
await withSpan("deploy.p2p-check", "3. p2p-check", { "deploy.domain": name }, async () => {
|
|
1953
|
-
const probe = await probeP2pRetrieval(provider.client, cid);
|
|
1954
|
-
setDeployAttribute("deploy.p2p.retrievable", probe.retrievable ? "true" : "false");
|
|
1955
|
-
setDeployAttribute("deploy.p2p.check_ms", String(probe.durationMs));
|
|
1956
|
-
setDeployAttribute("deploy.p2p.error_variant", probe.errorVariant);
|
|
1957
|
-
if (probe.retrievable) {
|
|
1958
|
-
console.log(` P2P retrieval: \u2713 (${probe.durationMs}ms)`);
|
|
1959
|
-
} else {
|
|
1960
|
-
console.log(` P2P retrieval: \u26A0 not yet retrievable (${probe.errorVariant}, ${probe.durationMs}ms)`);
|
|
1961
|
-
}
|
|
1962
|
-
});
|
|
1963
|
-
if (options.ghPagesMirror) {
|
|
1964
2208
|
console.log("\n" + "=".repeat(60));
|
|
1965
|
-
console.log("
|
|
2209
|
+
console.log("DotNS");
|
|
1966
2210
|
console.log("=".repeat(60));
|
|
1967
|
-
await withSpan("deploy.
|
|
1968
|
-
const
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
2211
|
+
await withSpan("deploy.dotns", "2. dotns", { "deploy.domain": name, "deploy.subdomain": String(parsed?.isSubdomain ?? false) }, async () => {
|
|
2212
|
+
const dotns = new DotNS();
|
|
2213
|
+
await dotns.connect({
|
|
2214
|
+
...resolveDotnsConnectOptions(options, envAssetHub, envAutoAccountMapping, envContracts, envNativeToEthRatio, envId, envPopSelfServe, envRegisterStorageDeposit),
|
|
2215
|
+
...options.signer && options.signerAddress ? { onPhoneSigningRequired: (label) => console.log(`
|
|
2216
|
+
Check your phone \u2192 ${label}`) } : {}
|
|
2217
|
+
});
|
|
2218
|
+
if (parsed?.isSubdomain) {
|
|
2219
|
+
const { owned, owner } = await dotns.checkSubdomainOwnership(parsed.sublabel, parsed.parentLabel);
|
|
2220
|
+
if (owned) {
|
|
2221
|
+
console.log(` Status: Already owned`);
|
|
2222
|
+
} else if (owner) {
|
|
2223
|
+
throw new Error(`Subdomain ${parsed.fullName} is owned by ${owner}, not ${dotns.evmAddress}`);
|
|
2224
|
+
} else {
|
|
2225
|
+
const parentOwnership = await dotns.checkOwnership(parsed.parentLabel);
|
|
2226
|
+
if (!parentOwnership.owned) throw new Error(`You must own ${parsed.parentLabel}.dot to register subdomains under it`);
|
|
2227
|
+
console.log(` Status: Registering subdomain...`);
|
|
2228
|
+
await dotns.registerSubdomain(parsed.sublabel, parsed.parentLabel);
|
|
2229
|
+
}
|
|
2230
|
+
} else {
|
|
2231
|
+
const { owned } = await dotns.checkOwnership(name);
|
|
2232
|
+
if (owned) {
|
|
2233
|
+
console.log(` Status: Already owned`);
|
|
2234
|
+
} else {
|
|
2235
|
+
console.log(` Status: Registering...`);
|
|
2236
|
+
await dotns.register(name);
|
|
2237
|
+
}
|
|
1976
2238
|
}
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2239
|
+
const contenthashHex = `0x${encodeContenthash(cid)}`;
|
|
2240
|
+
await dotns.setContenthash(name, contenthashHex);
|
|
2241
|
+
if (options.publish && parsed) {
|
|
2242
|
+
if (preflightPublishNeeded !== false) {
|
|
2243
|
+
await publish(dotns, parsed, options.failOnPublishError);
|
|
2244
|
+
}
|
|
1981
2245
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2246
|
+
dotns.disconnect();
|
|
2247
|
+
});
|
|
2248
|
+
await withSpan("deploy.p2p-check", "3. p2p-check", { "deploy.domain": name }, async () => {
|
|
2249
|
+
const probe = await probeP2pRetrieval(provider.client, cid);
|
|
2250
|
+
setDeployAttribute("deploy.p2p.retrievable", probe.retrievable ? "true" : "false");
|
|
2251
|
+
setDeployAttribute("deploy.p2p.check_ms", String(probe.durationMs));
|
|
2252
|
+
setDeployAttribute("deploy.p2p.error_variant", probe.errorVariant);
|
|
2253
|
+
if (probe.retrievable) {
|
|
2254
|
+
console.log(` P2P retrieval: \u2713 (${probe.durationMs}ms)`);
|
|
1990
2255
|
} else {
|
|
1991
|
-
console.log(`
|
|
1992
|
-
console.log(` GitHub Pages last served cid=${freshness.lastCid ?? "n/a"} (expected ${cid}); it should catch up shortly. Non-fatal.`);
|
|
1993
|
-
setDeployAttribute("deploy.gh_pages_freshness_verified", "false");
|
|
2256
|
+
console.log(` P2P retrieval: \u26A0 not yet retrievable (${probe.errorVariant}, ${probe.durationMs}ms)`);
|
|
1994
2257
|
}
|
|
1995
2258
|
});
|
|
2259
|
+
if (options.ghPagesMirror) {
|
|
2260
|
+
console.log("\n" + "=".repeat(60));
|
|
2261
|
+
console.log("Final checks");
|
|
2262
|
+
console.log("=".repeat(60));
|
|
2263
|
+
await withSpan("deploy.gh-pages-mirror", "4. gh-pages-mirror", { "deploy.domain": name }, async () => {
|
|
2264
|
+
const mirror = await mirrorPromise;
|
|
2265
|
+
if (mirror === null) {
|
|
2266
|
+
console.log(" GitHub Pages mirror: skipped (only directory deploys produce a CAR suitable for mirroring).");
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
2269
|
+
if (mirror instanceof MirrorSkipped) {
|
|
2270
|
+
console.log(` GitHub Pages mirror: skipped \u2014 ${mirror.message}`);
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
2273
|
+
if (mirror instanceof Error) {
|
|
2274
|
+
console.log(` GitHub Pages mirror: failed (non-fatal) \u2014 ${mirror.message}`);
|
|
2275
|
+
captureWarning("gh-pages mirror failed", { error: mirror.message.slice(0, 200) });
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
console.log(` Mirror: ${mirror.url}`);
|
|
2279
|
+
console.log(` Manifest: https://${mirror.owner}.github.io/${mirror.repo}/${mirror.manifestPath}`);
|
|
2280
|
+
setDeployAttribute("deploy.gh_pages_url", mirror.url);
|
|
2281
|
+
process.stdout.write(" Verifying Pages serves this deploy's CAR... ");
|
|
2282
|
+
const freshness = await pollMirrorFreshness(mirror.url, cid, { timeoutMs: 3 * 60 * 1e3, intervalMs: 1e4 });
|
|
2283
|
+
if (freshness.verified) {
|
|
2284
|
+
console.log(`ok (${freshness.attempts} attempt${freshness.attempts === 1 ? "" : "s"}, ${(freshness.durationMs / 1e3).toFixed(0)}s).`);
|
|
2285
|
+
setDeployAttribute("deploy.gh_pages_freshness_verified", "true");
|
|
2286
|
+
} else {
|
|
2287
|
+
console.log(`timed out.`);
|
|
2288
|
+
console.log(` GitHub Pages last served cid=${freshness.lastCid ?? "n/a"} (expected ${cid}); it should catch up shortly. Non-fatal.`);
|
|
2289
|
+
setDeployAttribute("deploy.gh_pages_freshness_verified", "false");
|
|
2290
|
+
}
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2293
|
+
console.log("\n" + "=".repeat(60));
|
|
2294
|
+
console.log("DEPLOYMENT COMPLETE!");
|
|
2295
|
+
console.log("=".repeat(60));
|
|
2296
|
+
console.log("\n\u{1F53A} Polkadot Triangle");
|
|
2297
|
+
console.log(` - Polkadot Desktop: ${name}.dot`);
|
|
2298
|
+
console.log(` - Polkadot Browser: ${browserUrlFor(name, envId)}`);
|
|
2299
|
+
console.log("\n" + "=".repeat(60) + "\n");
|
|
2300
|
+
return { domainName: name, fullDomain: `${name}.dot`, cid, ipfsCid };
|
|
2301
|
+
} finally {
|
|
2302
|
+
if (_deployRpcFailedOver) setDeployAttribute("deploy.rpc.failed_over", "true");
|
|
2303
|
+
provider?.client.destroy();
|
|
1996
2304
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2305
|
+
});
|
|
2306
|
+
} finally {
|
|
2307
|
+
sessionCleanup?.();
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
function computePhoneSigningSteps(dotnsPreflight, publishNeeded) {
|
|
2311
|
+
if (!dotnsPreflight || dotnsPreflight.plannedAction === "abort") return [];
|
|
2312
|
+
const steps = [];
|
|
2313
|
+
if (dotnsPreflight.plannedAction === "register") {
|
|
2314
|
+
steps.push("Commitment", "Register");
|
|
2315
|
+
}
|
|
2316
|
+
steps.push("Link content");
|
|
2317
|
+
if (publishNeeded) steps.push("Publish to registry");
|
|
2318
|
+
return steps;
|
|
2010
2319
|
}
|
|
2011
2320
|
|
|
2012
2321
|
// src/merkle.ts
|
|
@@ -2183,12 +2492,13 @@ async function merkleizeKuboBackend(directoryPath) {
|
|
|
2183
2492
|
const { fileBlocks, fileCids, rootBlockCids, subdirCids } = walkFileBlocks(cidStr, blocks);
|
|
2184
2493
|
return { rootCid: cidStr, blocks, fileBlocks, fileCids, rootBlockCids, subdirCids };
|
|
2185
2494
|
}
|
|
2186
|
-
async function merkleizeBackend(directoryPath, useKubo) {
|
|
2495
|
+
async function merkleizeBackend(directoryPath, useKubo, phase) {
|
|
2496
|
+
const tag = phase ? ` \u2014 ${phase}` : "";
|
|
2187
2497
|
if (useKubo) {
|
|
2188
|
-
console.log(` Merkleizing (Kubo): ${directoryPath}`);
|
|
2498
|
+
console.log(` Merkleizing (Kubo${tag}): ${directoryPath}`);
|
|
2189
2499
|
return merkleizeKuboBackend(directoryPath);
|
|
2190
2500
|
}
|
|
2191
|
-
console.log(` Merkleizing (JS): ${directoryPath}`);
|
|
2501
|
+
console.log(` Merkleizing (JS${tag}): ${directoryPath}`);
|
|
2192
2502
|
return merkleizeJSBackend(directoryPath);
|
|
2193
2503
|
}
|
|
2194
2504
|
function encodeCarFrame(cid, payload) {
|
|
@@ -2237,6 +2547,7 @@ function frameBlockCid(frame) {
|
|
|
2237
2547
|
async function buildOrderedCar(options) {
|
|
2238
2548
|
const { output, prevStableOrder = [] } = options;
|
|
2239
2549
|
const clsFn = options.classifyFn ?? ((p) => classifyFile(p));
|
|
2550
|
+
const tag = options.phase ? ` \u2014 ${options.phase}` : "";
|
|
2240
2551
|
const MANIFEST_PATH_LITERAL = ".bulletin-deploy/manifest.json";
|
|
2241
2552
|
const stableFiles = [];
|
|
2242
2553
|
const volatileFiles = [];
|
|
@@ -2321,7 +2632,7 @@ async function buildOrderedCar(options) {
|
|
|
2321
2632
|
const s1Bytes = section1Chunks.reduce((s, b) => s + b.length, 0);
|
|
2322
2633
|
const s2Bytes = section2Chunks.reduce((s, b) => s + b.length, 0);
|
|
2323
2634
|
console.log(
|
|
2324
|
-
` CAR (3-section): ${(carBytes.length / 1024 / 1024).toFixed(2)} MB (s0=${section0Bytes.length}B s1=${s1Bytes}B s2=${s2Bytes}B), ${allChunks.length} frames (${section0Chunks.length} header + ${section1Chunks.length} data + ${section2Chunks.length} manifest)`
|
|
2635
|
+
` CAR (3-section${tag}): ${(carBytes.length / 1024 / 1024).toFixed(2)} MB (s0=${section0Bytes.length}B s1=${s1Bytes}B s2=${s2Bytes}B), ${allChunks.length} frames (${section0Chunks.length} header + ${section1Chunks.length} data + ${section2Chunks.length} manifest)`
|
|
2325
2636
|
);
|
|
2326
2637
|
return {
|
|
2327
2638
|
carBytes,
|
|
@@ -2358,8 +2669,9 @@ async function rebuildOrderedCarFromBytes(carBytes, prevStableOrder = []) {
|
|
|
2358
2669
|
}
|
|
2359
2670
|
async function merkleizeWithStableOrder(directoryPath, prevStableOrder, options) {
|
|
2360
2671
|
const useKubo = options?.useKubo ?? false;
|
|
2361
|
-
const
|
|
2362
|
-
|
|
2672
|
+
const phase = options?.phase;
|
|
2673
|
+
const output = await merkleizeBackend(directoryPath, useKubo, phase);
|
|
2674
|
+
return buildOrderedCar({ output, classifyFn: options?.classifyFn, prevStableOrder, phase });
|
|
2363
2675
|
}
|
|
2364
2676
|
async function merkleizeJS(directoryPath) {
|
|
2365
2677
|
console.log(` Merkleizing (JS): ${directoryPath}`);
|
|
@@ -2403,11 +2715,18 @@ export {
|
|
|
2403
2715
|
rebuildOrderedCarFromBytes,
|
|
2404
2716
|
merkleizeWithStableOrder,
|
|
2405
2717
|
merkleizeJS,
|
|
2718
|
+
readBulletinSlotSigner,
|
|
2719
|
+
writeBulletinSlotKey,
|
|
2720
|
+
extractBulletinSlotKey,
|
|
2721
|
+
getSlotSignerProvider,
|
|
2406
2722
|
friendlyChainError,
|
|
2407
2723
|
DEFAULT_BULLETIN_RPC,
|
|
2408
2724
|
DEFAULT_POOL_SIZE,
|
|
2725
|
+
BULLETIN_ENDPOINTS,
|
|
2409
2726
|
setWsHaltCallback,
|
|
2727
|
+
makeBulletinStatusHandler,
|
|
2410
2728
|
CHUNK_MORTALITY_PERIOD,
|
|
2729
|
+
WS_HEARTBEAT_TIMEOUT_MS,
|
|
2411
2730
|
retryBudgetExhausted,
|
|
2412
2731
|
isConnectionError,
|
|
2413
2732
|
deriveRootSigner,
|
|
@@ -2421,6 +2740,7 @@ export {
|
|
|
2421
2740
|
ENCRYPT_PBKDF2_ITERATIONS,
|
|
2422
2741
|
encryptContent,
|
|
2423
2742
|
__selectStorageProviderModeForTest,
|
|
2743
|
+
chooseSignerInput,
|
|
2424
2744
|
storeFile,
|
|
2425
2745
|
__assignDenseNoncesForTest,
|
|
2426
2746
|
storeChunkedContent,
|
|
@@ -2442,5 +2762,6 @@ export {
|
|
|
2442
2762
|
browserUrlFor,
|
|
2443
2763
|
interpretBitswapResult,
|
|
2444
2764
|
probeP2pRetrieval,
|
|
2445
|
-
deploy
|
|
2765
|
+
deploy,
|
|
2766
|
+
computePhoneSigningSteps
|
|
2446
2767
|
};
|