bulletin-deploy 0.7.4 → 0.7.6
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 +88 -342
- package/bin/bulletin-bootstrap +48 -0
- package/bin/bulletin-deploy +55 -59
- package/dist/bug-report.js +4 -4
- package/dist/{chunk-SVKCVNXD.js → chunk-A2J6R5PD.js} +26 -60
- package/dist/{chunk-CQ753LDA.js → chunk-EECNTEAE.js} +1 -1
- package/dist/{chunk-YREZFNCC.js → chunk-G6CVI6U2.js} +50 -23
- package/dist/{chunk-UJP2PZGU.js → chunk-HHY32NGJ.js} +6 -4
- package/dist/{chunk-JHNW2EKY.js → chunk-WIBZPZSY.js} +44 -52
- package/dist/{chunk-DHQ3JGF4.js → chunk-Y6CTPQS2.js} +1 -1
- package/dist/{chunk-YYULF2JX.js → chunk-YX62STIA.js} +2 -3
- package/dist/deploy.js +7 -7
- package/dist/dotns.d.ts +2 -1
- package/dist/dotns.js +6 -4
- package/dist/index.js +7 -7
- package/dist/memory-report.js +2 -2
- package/dist/pool.d.ts +2 -2
- package/dist/pool.js +1 -1
- package/dist/run-state.js +1 -1
- package/dist/telemetry.js +2 -2
- package/dist/version-check.js +3 -3
- package/docs/bootstrap.md +49 -0
- package/docs/e2e-bootstrap.md +64 -0
- package/docs/telemetry.md +59 -0
- package/docs/testing.md +44 -0
- package/package.json +6 -4
|
@@ -12,6 +12,7 @@ var TOPUP_TRANSACTIONS = 1e3;
|
|
|
12
12
|
var TOPUP_BYTES = 100000000n;
|
|
13
13
|
var TOPUP_THRESHOLD_TXS = 50n;
|
|
14
14
|
var TOPUP_THRESHOLD_BYTES = 50000000n;
|
|
15
|
+
var WS_HEARTBEAT_TIMEOUT_MS = 3e5;
|
|
15
16
|
function derivePoolAccounts(poolSize = 10, mnemonic = DEV_PHRASE) {
|
|
16
17
|
const entropy = mnemonicToEntropy(mnemonic);
|
|
17
18
|
const miniSecret = entropyToMiniSecret(entropy);
|
|
@@ -126,23 +127,13 @@ function aliceKeyring() {
|
|
|
126
127
|
const signer = getPolkadotSigner(alice.publicKey, "Sr25519", (data) => alice.sign(data));
|
|
127
128
|
return { alice, signer };
|
|
128
129
|
}
|
|
129
|
-
async function withAliceApi(bulletinRpc, fn) {
|
|
130
|
-
const aliceClient = createClient(withPolkadotSdkCompat(getWsProvider(bulletinRpc)));
|
|
131
|
-
const aliceApi = aliceClient.getUnsafeApi();
|
|
132
|
-
try {
|
|
133
|
-
const { signer } = aliceKeyring();
|
|
134
|
-
return await fn(aliceApi, signer);
|
|
135
|
-
} finally {
|
|
136
|
-
aliceClient.destroy();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
130
|
var U32_MAX = 0xFFFFFFFFn;
|
|
140
131
|
function clampU32(n, name) {
|
|
141
132
|
if (n < 0n) throw new Error(`${name} must be non-negative`);
|
|
142
133
|
if (n > U32_MAX) throw new Error(`${name} (${n}) exceeds u32 max \u2014 split the deploy into smaller batches`);
|
|
143
134
|
return Number(n);
|
|
144
135
|
}
|
|
145
|
-
async function ensureAuthorized(api, address,
|
|
136
|
+
async function ensureAuthorized(api, address, label, minimum = { txs: TOPUP_THRESHOLD_TXS, bytes: TOPUP_THRESHOLD_BYTES }) {
|
|
146
137
|
const auth = await api.query.TransactionStorage.Authorizations.getValue(
|
|
147
138
|
Enum("Account", address)
|
|
148
139
|
);
|
|
@@ -152,54 +143,55 @@ async function ensureAuthorized(api, address, bulletinRpc, label, minimum = { tx
|
|
|
152
143
|
return;
|
|
153
144
|
}
|
|
154
145
|
console.log(` Auto-authorizing ${label ?? "account"} (${address.slice(0, 8)}...)...`);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
});
|
|
146
|
+
const { signer } = aliceKeyring();
|
|
147
|
+
await submitAliceTxWithRetry(
|
|
148
|
+
() => api.tx.TransactionStorage.authorize_account({
|
|
149
|
+
who: address,
|
|
150
|
+
transactions: TOPUP_TRANSACTIONS,
|
|
151
|
+
bytes: TOPUP_BYTES
|
|
152
|
+
}),
|
|
153
|
+
signer,
|
|
154
|
+
`authorize_account(${label ?? "account"})`
|
|
155
|
+
);
|
|
156
|
+
console.log(` Authorized: ${TOPUP_TRANSACTIONS} txs, ${TOPUP_BYTES / 1000000n}MB`);
|
|
167
157
|
}
|
|
168
|
-
async function topUpBy(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
});
|
|
158
|
+
async function topUpBy(api, address, needs, label) {
|
|
159
|
+
const currentAuth = await api.query.TransactionStorage.Authorizations.getValue(
|
|
160
|
+
Enum("Account", address)
|
|
161
|
+
);
|
|
162
|
+
const current = {
|
|
163
|
+
transactions: currentAuth ? BigInt(currentAuth.extent.transactions) : 0n,
|
|
164
|
+
bytes: currentAuth ? currentAuth.extent.bytes : 0n
|
|
165
|
+
};
|
|
166
|
+
const fmtMB = (b) => (Number(b) / 1e6).toFixed(1);
|
|
167
|
+
const target = computeTopUpTarget(current, needs);
|
|
168
|
+
if (!target) {
|
|
169
|
+
console.log(` Pre-auth skipped for ${label ?? "account"} (${address.slice(0, 8)}...): current ${current.transactions} txs / ${fmtMB(current.bytes)}MB covers needs + floor.`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const transactions = clampU32(target.transactions, "topUpBy.txs");
|
|
173
|
+
const { signer } = aliceKeyring();
|
|
174
|
+
console.log(` Pre-authorizing ${label ?? "account"} (${address.slice(0, 8)}...): current ${current.transactions} txs / ${fmtMB(current.bytes)}MB \u2192 target ${target.transactions} txs / ${fmtMB(target.bytes)}MB...`);
|
|
175
|
+
await submitAliceTxWithRetry(
|
|
176
|
+
() => api.tx.TransactionStorage.authorize_account({
|
|
177
|
+
who: address,
|
|
178
|
+
transactions,
|
|
179
|
+
bytes: target.bytes
|
|
180
|
+
}),
|
|
181
|
+
signer,
|
|
182
|
+
`topUpBy(${label ?? "account"})`
|
|
183
|
+
);
|
|
184
|
+
console.log(` Pre-authorized: target ${transactions} txs / ${fmtMB(target.bytes)}MB`);
|
|
196
185
|
}
|
|
197
186
|
async function bootstrapPool(bulletinRpc, poolSize = 10, mnemonic) {
|
|
198
187
|
console.log(`Bootstrapping ${poolSize} pool accounts on ${bulletinRpc}...
|
|
199
188
|
`);
|
|
200
189
|
await cryptoWaitReady();
|
|
201
190
|
const accounts = derivePoolAccounts(poolSize, mnemonic);
|
|
202
|
-
const client = createClient(withPolkadotSdkCompat(getWsProvider(
|
|
191
|
+
const client = createClient(withPolkadotSdkCompat(getWsProvider(
|
|
192
|
+
bulletinRpc,
|
|
193
|
+
{ heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS }
|
|
194
|
+
)));
|
|
203
195
|
const api = client.getUnsafeApi();
|
|
204
196
|
const keyring = new Keyring({ type: "sr25519" });
|
|
205
197
|
const alice = keyring.addFromUri("//Alice");
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
classifyErrorArea,
|
|
3
3
|
isInteractive,
|
|
4
4
|
promptYesNo
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-EECNTEAE.js";
|
|
6
6
|
import {
|
|
7
7
|
VERSION,
|
|
8
8
|
getCurrentSentryTraceId
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-Y6CTPQS2.js";
|
|
10
10
|
|
|
11
11
|
// src/bug-report.ts
|
|
12
12
|
import { execSync, execFileSync } from "child_process";
|
|
@@ -74,7 +74,6 @@ function scrubSecrets(text) {
|
|
|
74
74
|
}
|
|
75
75
|
function buildCliFlagsSummary(flags) {
|
|
76
76
|
const parts = [];
|
|
77
|
-
if (flags.bootstrap) parts.push("--bootstrap");
|
|
78
77
|
if (flags.jsMerkle) parts.push("--js-merkle");
|
|
79
78
|
if (flags.ghPagesMirror) parts.push("--gh-pages-mirror");
|
|
80
79
|
if (flags.poolSize != null) parts.push(`--pool-size ${String(flags.poolSize)}`);
|
package/dist/deploy.js
CHANGED
|
@@ -25,15 +25,15 @@ import {
|
|
|
25
25
|
storeChunkedContent,
|
|
26
26
|
storeDirectory,
|
|
27
27
|
storeFile
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import "./chunk-
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-
|
|
28
|
+
} from "./chunk-A2J6R5PD.js";
|
|
29
|
+
import "./chunk-YX62STIA.js";
|
|
30
|
+
import "./chunk-EECNTEAE.js";
|
|
31
|
+
import "./chunk-G6CVI6U2.js";
|
|
32
32
|
import "./chunk-2Q2WSKFD.js";
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-
|
|
33
|
+
import "./chunk-Y6CTPQS2.js";
|
|
34
|
+
import "./chunk-HHY32NGJ.js";
|
|
35
35
|
import "./chunk-B7GUYYAN.js";
|
|
36
|
-
import "./chunk-
|
|
36
|
+
import "./chunk-WIBZPZSY.js";
|
|
37
37
|
import "./chunk-QGM4M3NI.js";
|
|
38
38
|
export {
|
|
39
39
|
CHUNK_MORTALITY_PERIOD,
|
package/dist/dotns.d.ts
CHANGED
|
@@ -88,6 +88,7 @@ declare function stripTrailingDigits(label: string): string;
|
|
|
88
88
|
declare function sanitizeDomainLabel(label: string): string;
|
|
89
89
|
declare function validateDomainLabel(label: string): string;
|
|
90
90
|
declare function isCommitmentMature(chainNowSeconds: number, commitTimestampSeconds: number, minimumAgeSeconds: number): boolean;
|
|
91
|
+
declare function isExplicitCommitmentBuffer(envValue: string | undefined): boolean;
|
|
91
92
|
declare function classifyDotnsLabel(label: string): {
|
|
92
93
|
status: number;
|
|
93
94
|
message: string;
|
|
@@ -155,4 +156,4 @@ declare class DotNS {
|
|
|
155
156
|
}
|
|
156
157
|
declare const dotns: DotNS;
|
|
157
158
|
|
|
158
|
-
export { CONNECTION_TIMEOUT_MS, CONTRACTS, DECIMALS, DEFAULT_MNEMONIC, DOTNS_TX_MAX_ATTEMPTS, DOT_NODE, DotNS, type DotNSConnectOptions, type DotnsPreflightResult, NATIVE_TO_ETH_RATIO, OPERATION_TIMEOUT_MS, type OwnershipResult, type ParsedDomainName, type PriceValidationResult, ProofOfPersonhoodStatus, RPC_ENDPOINTS, TX_CHAIN_TIME_BUDGET_MS, TX_TIMEOUT_MS, TX_WALL_CLOCK_CEILING_MS, WS_HEARTBEAT_TIMEOUT_MS, canRegister, classifyDotnsLabel, classifyTxRetryDecision, computeDomainTokenId, convertWeiToNative, countTrailingDigits, dotns, fetchNonce, isCommitmentMature, parseDomainName, parseProofOfPersonhoodStatus, popStatusName, runDotnsCli, sanitizeDomainLabel, simulateUserStatus, stripTrailingDigits, validateDomainLabel, verifyNonceAdvanced };
|
|
159
|
+
export { CONNECTION_TIMEOUT_MS, CONTRACTS, DECIMALS, DEFAULT_MNEMONIC, DOTNS_TX_MAX_ATTEMPTS, DOT_NODE, DotNS, type DotNSConnectOptions, type DotnsPreflightResult, NATIVE_TO_ETH_RATIO, OPERATION_TIMEOUT_MS, type OwnershipResult, type ParsedDomainName, type PriceValidationResult, ProofOfPersonhoodStatus, RPC_ENDPOINTS, TX_CHAIN_TIME_BUDGET_MS, TX_TIMEOUT_MS, TX_WALL_CLOCK_CEILING_MS, WS_HEARTBEAT_TIMEOUT_MS, canRegister, classifyDotnsLabel, classifyTxRetryDecision, computeDomainTokenId, convertWeiToNative, countTrailingDigits, dotns, fetchNonce, isCommitmentMature, isExplicitCommitmentBuffer, parseDomainName, parseProofOfPersonhoodStatus, popStatusName, runDotnsCli, sanitizeDomainLabel, simulateUserStatus, stripTrailingDigits, validateDomainLabel, verifyNonceAdvanced };
|
package/dist/dotns.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
dotns,
|
|
24
24
|
fetchNonce,
|
|
25
25
|
isCommitmentMature,
|
|
26
|
+
isExplicitCommitmentBuffer,
|
|
26
27
|
parseDomainName,
|
|
27
28
|
parseProofOfPersonhoodStatus,
|
|
28
29
|
popStatusName,
|
|
@@ -32,10 +33,10 @@ import {
|
|
|
32
33
|
stripTrailingDigits,
|
|
33
34
|
validateDomainLabel,
|
|
34
35
|
verifyNonceAdvanced
|
|
35
|
-
} from "./chunk-
|
|
36
|
-
import "./chunk-
|
|
37
|
-
import "./chunk-
|
|
38
|
-
import "./chunk-
|
|
36
|
+
} from "./chunk-G6CVI6U2.js";
|
|
37
|
+
import "./chunk-Y6CTPQS2.js";
|
|
38
|
+
import "./chunk-HHY32NGJ.js";
|
|
39
|
+
import "./chunk-WIBZPZSY.js";
|
|
39
40
|
import "./chunk-QGM4M3NI.js";
|
|
40
41
|
export {
|
|
41
42
|
CONNECTION_TIMEOUT_MS,
|
|
@@ -62,6 +63,7 @@ export {
|
|
|
62
63
|
dotns,
|
|
63
64
|
fetchNonce,
|
|
64
65
|
isCommitmentMature,
|
|
66
|
+
isExplicitCommitmentBuffer,
|
|
65
67
|
parseDomainName,
|
|
66
68
|
parseProofOfPersonhoodStatus,
|
|
67
69
|
popStatusName,
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deploy
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-A2J6R5PD.js";
|
|
4
|
+
import "./chunk-YX62STIA.js";
|
|
5
|
+
import "./chunk-EECNTEAE.js";
|
|
6
6
|
import {
|
|
7
7
|
DotNS,
|
|
8
8
|
parseDomainName
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-G6CVI6U2.js";
|
|
10
10
|
import "./chunk-2Q2WSKFD.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-Y6CTPQS2.js";
|
|
12
12
|
import {
|
|
13
13
|
VERSION,
|
|
14
14
|
loadRunState,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
shouldSkipStaleWarning,
|
|
19
19
|
stateFilePath,
|
|
20
20
|
writeRunState
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-HHY32NGJ.js";
|
|
22
22
|
import {
|
|
23
23
|
merkleizeJS
|
|
24
24
|
} from "./chunk-B7GUYYAN.js";
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
ensureAuthorized,
|
|
29
29
|
fetchPoolAuthorizations,
|
|
30
30
|
selectAccount
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-WIBZPZSY.js";
|
|
32
32
|
import "./chunk-QGM4M3NI.js";
|
|
33
33
|
export {
|
|
34
34
|
DotNS,
|
package/dist/memory-report.js
CHANGED
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
maybeWriteMemoryReport,
|
|
6
6
|
safeHeap,
|
|
7
7
|
sampleFromBytes
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-Y6CTPQS2.js";
|
|
9
|
+
import "./chunk-HHY32NGJ.js";
|
|
10
10
|
import "./chunk-QGM4M3NI.js";
|
|
11
11
|
export {
|
|
12
12
|
DEFAULT_THRESHOLD_MB,
|
package/dist/pool.d.ts
CHANGED
|
@@ -30,8 +30,8 @@ declare function classifyAliceTxError(err: unknown): TxRetryDecision;
|
|
|
30
30
|
declare function isTestnetSpecName(specName: string | undefined | null): boolean;
|
|
31
31
|
declare function detectTestnet(api: any): Promise<boolean>;
|
|
32
32
|
declare function _resetTestnetCacheForTests(): void;
|
|
33
|
-
declare function ensureAuthorized(api: any, address: string,
|
|
34
|
-
declare function topUpBy(
|
|
33
|
+
declare function ensureAuthorized(api: any, address: string, label?: string, minimum?: AuthorizationNeeds): Promise<void>;
|
|
34
|
+
declare function topUpBy(api: any, address: string, needs: AuthorizationNeeds, label?: string): Promise<void>;
|
|
35
35
|
declare function bootstrapPool(bulletinRpc: string, poolSize?: number, mnemonic?: string): Promise<void>;
|
|
36
36
|
|
|
37
37
|
export { type AuthorizationNeeds, type PoolAccount, type PoolAuthorization, type TxRetryDecision, _resetTestnetCacheForTests, bootstrapPool, classifyAliceTxError, computeTopUpTarget, derivePoolAccounts, detectTestnet, ensureAuthorized, fetchPoolAuthorizations, isTestnetSpecName, selectAccount, topUpBy };
|
package/dist/pool.js
CHANGED
package/dist/run-state.js
CHANGED
package/dist/telemetry.js
CHANGED
package/dist/version-check.js
CHANGED
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
isPreReleaseVersion,
|
|
9
9
|
preReleaseWarning,
|
|
10
10
|
promptYesNo
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-EECNTEAE.js";
|
|
12
|
+
import "./chunk-Y6CTPQS2.js";
|
|
13
|
+
import "./chunk-HHY32NGJ.js";
|
|
14
14
|
import "./chunk-QGM4M3NI.js";
|
|
15
15
|
export {
|
|
16
16
|
assessVersion,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# bulletin-bootstrap
|
|
2
|
+
|
|
3
|
+
`bulletin-bootstrap` is the operator CLI for initializing Bulletin pool accounts. It is separate from `bulletin-deploy` on purpose: deploys are the normal user path, bootstrap is an admin/setup operation.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bulletin-bootstrap
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
|
|
13
|
+
| Flag | What it does |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `--rpc wss://...` | Override the Bulletin RPC endpoint. Also readable from `BULLETIN_RPC`. |
|
|
16
|
+
| `--pool-size N` | Number of derived pool accounts to initialize. Default: `10`. |
|
|
17
|
+
| `--mnemonic "..."` | Root mnemonic used to derive the pool accounts. Also readable from `BULLETIN_POOL_MNEMONIC`, then `MNEMONIC`. |
|
|
18
|
+
| `--version` | Print the CLI version. |
|
|
19
|
+
| `--help` | Show help. |
|
|
20
|
+
|
|
21
|
+
## What it does
|
|
22
|
+
|
|
23
|
+
The command derives the pool account set and initializes the on-chain authorization state needed for Bulletin uploads.
|
|
24
|
+
|
|
25
|
+
Use it when:
|
|
26
|
+
|
|
27
|
+
- you are bringing up a fresh pool on a testnet
|
|
28
|
+
- the shared uploader pool has not been authorized yet
|
|
29
|
+
- you want to pre-initialize a non-default pool mnemonic
|
|
30
|
+
|
|
31
|
+
Do not use it as part of routine deploys. Normal deploys go through `bulletin-deploy`.
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Default testnet pool against the default RPC
|
|
37
|
+
bulletin-bootstrap
|
|
38
|
+
|
|
39
|
+
# Different RPC and larger pool
|
|
40
|
+
bulletin-bootstrap --rpc wss://custom-bulletin.example.com --pool-size 20
|
|
41
|
+
|
|
42
|
+
# Explicit pool mnemonic
|
|
43
|
+
bulletin-bootstrap --mnemonic "..."
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Related Docs
|
|
47
|
+
|
|
48
|
+
- [Main README](../README.md)
|
|
49
|
+
- [E2E test setup](./e2e-bootstrap.md)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# E2E test setup
|
|
2
|
+
|
|
3
|
+
The E2E suite (`test/e2e.test.js`, driven by `.github/workflows/e2e.yml`) deploys real content to Paseo Bulletin testnet via `bulletin-deploy` and verifies the on-chain round-trip. It consumes the **shared default pool** (derived from `DEV_PHRASE` — the same pool real users hit in production) for Bulletin chunk upload, so no pool bootstrapping is required.
|
|
4
|
+
|
|
5
|
+
Three one-time setup items are needed before the workflow can pass. Do them once per testnet lifetime (redo if testnet is wiped).
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- `bulletin-deploy` built locally (`npm run build`).
|
|
10
|
+
- `@parity/dotns-cli` on `$PATH` (for the Bob registration in item 3). Install with `npm i -g @parity/dotns-cli`.
|
|
11
|
+
- Network access to Paseo Bulletin RPC (`wss://paseo-bulletin-rpc.polkadot.io`) and Asset Hub Paseo (DotNS).
|
|
12
|
+
- Alice's dev mnemonic: `bottom drive obey lake curtain smoke basket hold race lonely fit walk`.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
### 1. Grant Alice PoP Full
|
|
17
|
+
|
|
18
|
+
Both happy-path scenarios (S1, S2) deploy as Alice via DotNS. Registering a new un-reserved base name requires PoP Full:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node tools/check-pop-status.mjs --grant full
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Idempotent. Re-run if the check shows Alice below Full.
|
|
25
|
+
|
|
26
|
+
### 2. Fund and map Bob on Asset Hub Paseo
|
|
27
|
+
|
|
28
|
+
Bob (`//Bob` from the dev phrase) is the owner of `e2eowned.dot` (see item 3). He needs:
|
|
29
|
+
|
|
30
|
+
- **Balance** on Asset Hub Paseo for his on-chain fees. Request ~1 PAS from the Paseo faucet at [https://faucet.polkadot.io/](https://faucet.polkadot.io/) sending to Bob's SS58 address `5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty`.
|
|
31
|
+
- **Revive mapping** so he can sign EVM transactions. Running `check-pop-status` against Bob's key URI submits `map_account` automatically on connect if the mapping is absent:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
node tools/check-pop-status.mjs "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Bob"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Expected output: Bob's SS58, his H160 (`0x41dccbd49b26c50d34355ed86ff0fa9e489d1e01`), and PoP status (`NoStatus (0)` initially). Idempotent.
|
|
38
|
+
|
|
39
|
+
### 3. Register `e2eowned.dot` directly as Bob
|
|
40
|
+
|
|
41
|
+
The S3 negative scenario asserts that `bulletin-deploy` refuses to deploy to a domain owned by a different account (exit 78 with transfer guidance). Have Bob register the label via `dotns-cli` — no Alice intermediary, no Bulletin content needed (S3 never reads content):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
dotns register domain -n e2eowned -s full --cb 30 \
|
|
45
|
+
-k "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Bob"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Expected: exit 0, stdout ends with "Operation Complete" and `Domain: e2eowned.dot`. After this, `e2eowned.dot` is owned by Bob's H160 `0x41dccbd49b26c50d34355ed86ff0fa9e489d1e01`. PoP Full is auto-granted on testnet as part of the registration (an 8-char base name requires Full per `classifyDotnsLabel`). The `--cb 30` sets a 30s commitment buffer — the default 6s sometimes misses the 12s `minCommitmentAge` on Asset Hub Paseo.
|
|
49
|
+
|
|
50
|
+
If S3 ever fails because `e2eowned.dot` was transferred back to Alice by mistake, re-run this step to restore Bob's ownership.
|
|
51
|
+
|
|
52
|
+
## No pre-registration needed for `e2epool.dot` / `e2edirect.dot`
|
|
53
|
+
|
|
54
|
+
The stable happy-path labels auto-register to Alice on first deploy (pool mode and direct mode both resolve to Alice at the DotNS layer — `--mnemonic` only overrides the DotNS signer, not the Bulletin signer; Bulletin always uses the pool). Subsequent runs exercise the update path (new contenthash under existing ownership).
|
|
55
|
+
|
|
56
|
+
## Verifying locally
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
E2E=1 E2E_SIGNER=pool E2E_MERKLE=js E2E_SCENARIO=s1 \
|
|
60
|
+
BULLETIN_RPC=wss://paseo-bulletin-rpc.polkadot.io \
|
|
61
|
+
npm run test:e2e
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Vary `E2E_SCENARIO` (`s1`, `s2`, `s3`), `E2E_SIGNER` (`pool`, `direct`), and `E2E_MERKLE` (`js`, `kubo`) to cover the full CI matrix.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Telemetry
|
|
2
|
+
|
|
3
|
+
Telemetry is **off by default for external users**. It is enabled automatically for known internal Parity contexts and can also be controlled explicitly.
|
|
4
|
+
|
|
5
|
+
## Opt in / opt out
|
|
6
|
+
|
|
7
|
+
- `BULLETIN_DEPLOY_TELEMETRY=1`: explicit opt-in
|
|
8
|
+
- `BULLETIN_DEPLOY_TELEMETRY=0`: force off
|
|
9
|
+
|
|
10
|
+
Internal detection signals are OR'd together:
|
|
11
|
+
|
|
12
|
+
1. `GITHUB_REPOSITORY` matches a known internal org
|
|
13
|
+
2. `RUNNER_NAME` starts with `parity-`
|
|
14
|
+
3. `git remote get-url origin` points at a known internal org
|
|
15
|
+
|
|
16
|
+
## What is tracked
|
|
17
|
+
|
|
18
|
+
- deploy duration and success/failure
|
|
19
|
+
- storage phase timing
|
|
20
|
+
- DotNS phase timing
|
|
21
|
+
- pool account selection
|
|
22
|
+
- source metadata such as repo, branch, and CI vs local
|
|
23
|
+
- tool version
|
|
24
|
+
|
|
25
|
+
## Ambient Sentry mode
|
|
26
|
+
|
|
27
|
+
If another app embeds `bulletin-deploy` and already owns Sentry initialization, set these before importing or invoking the library:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
BULLETIN_DEPLOY_USE_AMBIENT_SENTRY=1
|
|
31
|
+
BULLETIN_DEPLOY_HOST_APP=<your-app-name>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
That makes `bulletin-deploy` reuse the existing Sentry client instead of calling its own `Sentry.init()`.
|
|
35
|
+
|
|
36
|
+
Requirements:
|
|
37
|
+
|
|
38
|
+
- the host app must initialize Sentry first
|
|
39
|
+
- Sentry SDK compatibility still matters
|
|
40
|
+
- quotas and issue grouping remain owned by the host project
|
|
41
|
+
|
|
42
|
+
## Tagging test traffic
|
|
43
|
+
|
|
44
|
+
Use `--tag` or `DEPLOY_TAG` to separate test and benchmark traffic from real deploys.
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
bulletin-deploy --tag e2e-ci-pr ./build my-app.dot
|
|
50
|
+
DEPLOY_TAG=load-test bulletin-deploy ./build my-app.dot
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Common tags in this repo:
|
|
54
|
+
|
|
55
|
+
- `e2e-ci-pr`
|
|
56
|
+
- `e2e-ci-nightly`
|
|
57
|
+
- `e2e-local-smoke`
|
|
58
|
+
- `e2e-local-pr`
|
|
59
|
+
- `e2e-local-nightly`
|
package/docs/testing.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
The repo has three practical test layers: offline unit tests, live-testnet E2E coverage, and GitHub Actions matrices that exercise the shipped reusable workflow.
|
|
4
|
+
|
|
5
|
+
## Offline tests
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm test
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This runs the local Node test suite without network access.
|
|
12
|
+
|
|
13
|
+
## Live-testnet E2E
|
|
14
|
+
|
|
15
|
+
The E2E suite deploys real content to Paseo Bulletin and verifies the on-chain round-trip.
|
|
16
|
+
|
|
17
|
+
Local launchers:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm run test:e2e:smoke
|
|
21
|
+
npm run test:e2e:pr
|
|
22
|
+
npm run test:e2e:nightly
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Quiet mode:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
E2E_QUIET=1 npm run test:e2e:smoke
|
|
29
|
+
E2E_QUIET=1 npm run test:e2e:pr
|
|
30
|
+
E2E_QUIET=1 npm run test:e2e:nightly
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Each scenario writes a JUnit XML report under `e2e-reports/`.
|
|
34
|
+
|
|
35
|
+
For one-time chain setup, see [E2E test setup](./e2e-bootstrap.md).
|
|
36
|
+
|
|
37
|
+
## CI matrices
|
|
38
|
+
|
|
39
|
+
`.github/workflows/e2e.yml` calls the shipped reusable `.github/workflows/deploy.yml` so the E2E jobs exercise the same path consumers use.
|
|
40
|
+
|
|
41
|
+
- per-PR: stable happy-path coverage plus negative ownership coverage
|
|
42
|
+
- nightly: broader signer, merkleization, and mirror-path coverage
|
|
43
|
+
|
|
44
|
+
E2E deploys are tagged so telemetry can distinguish them from real-user traffic. See [Telemetry](./telemetry.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bulletin-deploy",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"main": "./dist/index.js",
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"bin": {
|
|
17
|
-
"bulletin-deploy": "./bin/bulletin-deploy"
|
|
17
|
+
"bulletin-deploy": "./bin/bulletin-deploy",
|
|
18
|
+
"bulletin-bootstrap": "./bin/bulletin-bootstrap"
|
|
18
19
|
},
|
|
19
20
|
"exports": {
|
|
20
21
|
".": {
|
|
@@ -24,12 +25,13 @@
|
|
|
24
25
|
},
|
|
25
26
|
"files": [
|
|
26
27
|
"dist",
|
|
27
|
-
"bin"
|
|
28
|
+
"bin",
|
|
29
|
+
"docs"
|
|
28
30
|
],
|
|
29
31
|
"scripts": {
|
|
30
32
|
"build": "tsup src/index.ts src/deploy.ts src/dotns.ts src/pool.ts src/telemetry.ts src/memory-report.ts src/merkle.ts src/gh-pages-mirror.ts src/version-check.ts src/bug-report.ts src/run-state.ts --format esm --dts --clean --target node22",
|
|
31
33
|
"prepare": "npm run build",
|
|
32
|
-
"test": "npm run build && node --test test/test.js test/
|
|
34
|
+
"test": "npm run build && node --test test/test.js test/cli-help.test.js test/helpers/e2e-helpers.test.js",
|
|
33
35
|
"test:e2e": "npm run build && node --test test/e2e.test.js",
|
|
34
36
|
"test:e2e:smoke": "bash scripts/e2e-pass.sh smoke",
|
|
35
37
|
"test:e2e:pr": "bash scripts/e2e-pass.sh pr",
|