@secondlayer/cli 19.2.2 → 19.4.0
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/dist/cli.js +467 -156
- package/dist/cli.js.map +8 -7
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -2550,6 +2550,118 @@ var init_output = __esm(() => {
|
|
|
2550
2550
|
ANSI_ESCAPE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
2551
2551
|
});
|
|
2552
2552
|
|
|
2553
|
+
// src/lib/api-client.ts
|
|
2554
|
+
import { ApiError, SecondLayer } from "@secondlayer/sdk";
|
|
2555
|
+
function nextStepHint(status) {
|
|
2556
|
+
if (status === undefined)
|
|
2557
|
+
return;
|
|
2558
|
+
if (status === 403)
|
|
2559
|
+
return "You may not have access — check your active project with `secondlayer whoami`.";
|
|
2560
|
+
if (status === 404)
|
|
2561
|
+
return "Check the name/slug — run the matching `list` command to see what exists.";
|
|
2562
|
+
if (status === 400 || status === 422)
|
|
2563
|
+
return "Check the command arguments and flags — see `--help` for the expected format.";
|
|
2564
|
+
if (status === 429)
|
|
2565
|
+
return "Rate limited — wait a moment and retry.";
|
|
2566
|
+
if (status >= 500)
|
|
2567
|
+
return "Server error — retry shortly, or check `secondlayer status`.";
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
function handleApiError(err, action) {
|
|
2571
|
+
const status = err instanceof ApiError || err instanceof CliHttpError ? err.status : undefined;
|
|
2572
|
+
if (err instanceof CliHttpError && err.code === "SESSION_EXPIRED" || status === 401) {
|
|
2573
|
+
printError("Authentication required.", {
|
|
2574
|
+
hint: isOssMode() ? "Set INSTANCE_TOKEN from `secondlayer init`." : "Run `secondlayer login` to re-authenticate."
|
|
2575
|
+
});
|
|
2576
|
+
process.exit(1);
|
|
2577
|
+
}
|
|
2578
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2579
|
+
printError(`Failed to ${action}: ${detail}`, { hint: nextStepHint(status) });
|
|
2580
|
+
process.exit(1);
|
|
2581
|
+
}
|
|
2582
|
+
function withErrorHandling(fn, options) {
|
|
2583
|
+
return async (...args) => {
|
|
2584
|
+
try {
|
|
2585
|
+
await fn(...args);
|
|
2586
|
+
} catch (err) {
|
|
2587
|
+
if (options?.onError) {
|
|
2588
|
+
options.onError(err);
|
|
2589
|
+
} else {
|
|
2590
|
+
handleApiError(err, options?.action ?? "execute command");
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
async function getPlatformClient() {
|
|
2596
|
+
const { apiUrl, ephemeralKey } = await resolveHostedAuth();
|
|
2597
|
+
return new SecondLayer({ baseUrl: apiUrl, apiKey: ephemeralKey });
|
|
2598
|
+
}
|
|
2599
|
+
async function listSubgraphsApi() {
|
|
2600
|
+
return (await getPlatformClient()).subgraphs.list();
|
|
2601
|
+
}
|
|
2602
|
+
async function getSubgraphApi(name) {
|
|
2603
|
+
return (await getPlatformClient()).subgraphs.status(name);
|
|
2604
|
+
}
|
|
2605
|
+
async function getSubgraphOpenApi(name, options) {
|
|
2606
|
+
return (await getPlatformClient()).subgraphs.openapi(name, options);
|
|
2607
|
+
}
|
|
2608
|
+
async function getSubgraphAgentSchema(name, options) {
|
|
2609
|
+
return (await getPlatformClient()).subgraphs.schema(name, options);
|
|
2610
|
+
}
|
|
2611
|
+
async function getSubgraphMarkdown(name, options) {
|
|
2612
|
+
return (await getPlatformClient()).subgraphs.markdown(name, options);
|
|
2613
|
+
}
|
|
2614
|
+
async function reindexSubgraphApi(name) {
|
|
2615
|
+
return (await getPlatformClient()).subgraphs.reindex(name);
|
|
2616
|
+
}
|
|
2617
|
+
async function backfillSubgraphApi(name, options) {
|
|
2618
|
+
return (await getPlatformClient()).subgraphs.backfill(name, options);
|
|
2619
|
+
}
|
|
2620
|
+
async function stopSubgraphApi(name) {
|
|
2621
|
+
return (await getPlatformClient()).subgraphs.stop(name);
|
|
2622
|
+
}
|
|
2623
|
+
async function deleteSubgraphApi(name, options) {
|
|
2624
|
+
return (await getPlatformClient()).subgraphs.delete(name, options);
|
|
2625
|
+
}
|
|
2626
|
+
async function deploySubgraphApi(data) {
|
|
2627
|
+
return (await getPlatformClient()).subgraphs.deploy(data);
|
|
2628
|
+
}
|
|
2629
|
+
async function querySubgraphTableRows(name, table, params = {}) {
|
|
2630
|
+
return (await getPlatformClient()).subgraphs.rows(name, table, params);
|
|
2631
|
+
}
|
|
2632
|
+
async function querySubgraphTableCount(name, table, params = {}) {
|
|
2633
|
+
return (await getPlatformClient()).subgraphs.count(name, table, params);
|
|
2634
|
+
}
|
|
2635
|
+
async function getSubgraphGaps(name, opts) {
|
|
2636
|
+
return (await getPlatformClient()).subgraphs.gaps(name, opts);
|
|
2637
|
+
}
|
|
2638
|
+
async function listSubgraphOperationsApi(name) {
|
|
2639
|
+
return (await getPlatformClient()).subgraphs.operations(name);
|
|
2640
|
+
}
|
|
2641
|
+
async function getSubgraphOperationApi(name, operationId) {
|
|
2642
|
+
return (await getPlatformClient()).subgraphs.getOperation(name, operationId);
|
|
2643
|
+
}
|
|
2644
|
+
async function getSubgraphSourceApi(name) {
|
|
2645
|
+
return (await getPlatformClient()).subgraphs.getSource(name);
|
|
2646
|
+
}
|
|
2647
|
+
async function getSubgraphRowsApi(name, table, opts = {}) {
|
|
2648
|
+
return (await getPlatformClient()).subgraphs.rows(name, table, opts);
|
|
2649
|
+
}
|
|
2650
|
+
async function getContractPrintSchema(contractId) {
|
|
2651
|
+
let client;
|
|
2652
|
+
try {
|
|
2653
|
+
client = await getPlatformClient();
|
|
2654
|
+
} catch {
|
|
2655
|
+
client = new SecondLayer({ baseUrl: resolveApiUrl() });
|
|
2656
|
+
}
|
|
2657
|
+
return client.index.printSchema(contractId);
|
|
2658
|
+
}
|
|
2659
|
+
var init_api_client = __esm(() => {
|
|
2660
|
+
init_http();
|
|
2661
|
+
init_output();
|
|
2662
|
+
init_resolve_auth();
|
|
2663
|
+
});
|
|
2664
|
+
|
|
2553
2665
|
// ../../node_modules/fast-glob/out/utils/array.js
|
|
2554
2666
|
var require_array = __commonJS(function(exports) {
|
|
2555
2667
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -16630,7 +16742,7 @@ var init_dist3 = __esm(() => {
|
|
|
16630
16742
|
// ../../node_modules/tinyexec/dist/main.js
|
|
16631
16743
|
import { createRequire as e } from "module";
|
|
16632
16744
|
import { spawn as t } from "node:child_process";
|
|
16633
|
-
import { delimiter as
|
|
16745
|
+
import { delimiter as n3, dirname as r, normalize as i, resolve as a2 } from "node:path";
|
|
16634
16746
|
import { cwd as o } from "node:process";
|
|
16635
16747
|
import { PassThrough as s } from "node:stream";
|
|
16636
16748
|
import c from "node:readline";
|
|
@@ -16649,7 +16761,7 @@ function x(e) {
|
|
|
16649
16761
|
return b;
|
|
16650
16762
|
}
|
|
16651
16763
|
function S(e, t) {
|
|
16652
|
-
const i = t.value.split(
|
|
16764
|
+
const i = t.value.split(n3);
|
|
16653
16765
|
let o = e;
|
|
16654
16766
|
let s;
|
|
16655
16767
|
do {
|
|
@@ -16659,7 +16771,7 @@ function S(e, t) {
|
|
|
16659
16771
|
} while (o !== s);
|
|
16660
16772
|
return {
|
|
16661
16773
|
key: t.key,
|
|
16662
|
-
value: i.join(
|
|
16774
|
+
value: i.join(n3)
|
|
16663
16775
|
};
|
|
16664
16776
|
}
|
|
16665
16777
|
function C(e, t) {
|
|
@@ -23945,19 +24057,19 @@ class c2 {
|
|
|
23945
24057
|
}
|
|
23946
24058
|
}
|
|
23947
24059
|
function i2() {
|
|
23948
|
-
return this[
|
|
24060
|
+
return this[n4].next();
|
|
23949
24061
|
}
|
|
23950
24062
|
function o2(r) {
|
|
23951
|
-
return this[
|
|
24063
|
+
return this[n4].return(r);
|
|
23952
24064
|
}
|
|
23953
24065
|
function h2({ preventCancel: r = false } = {}) {
|
|
23954
24066
|
const e = this.getReader(), t = new c2(e, r), s = Object.create(u2);
|
|
23955
|
-
return s[
|
|
24067
|
+
return s[n4] = t, s;
|
|
23956
24068
|
}
|
|
23957
|
-
var a3,
|
|
24069
|
+
var a3, n4, u2;
|
|
23958
24070
|
var init_asyncIterator = __esm(() => {
|
|
23959
24071
|
a3 = Object.getPrototypeOf(Object.getPrototypeOf(async function* () {}).prototype);
|
|
23960
|
-
|
|
24072
|
+
n4 = Symbol();
|
|
23961
24073
|
Object.defineProperty(i2, "name", { value: "next" });
|
|
23962
24074
|
Object.defineProperty(o2, "name", { value: "return" });
|
|
23963
24075
|
u2 = Object.create(a3, {
|
|
@@ -28890,115 +29002,6 @@ var init_subgraphs = __esm(() => {
|
|
|
28890
29002
|
init_format();
|
|
28891
29003
|
});
|
|
28892
29004
|
|
|
28893
|
-
// src/lib/api-client.ts
|
|
28894
|
-
import { ApiError, SecondLayer as SecondLayer2 } from "@secondlayer/sdk";
|
|
28895
|
-
function nextStepHint(status) {
|
|
28896
|
-
if (status === undefined)
|
|
28897
|
-
return;
|
|
28898
|
-
if (status === 403)
|
|
28899
|
-
return "You may not have access — check your active project with `secondlayer whoami`.";
|
|
28900
|
-
if (status === 404)
|
|
28901
|
-
return "Check the name/slug — run the matching `list` command to see what exists.";
|
|
28902
|
-
if (status === 400 || status === 422)
|
|
28903
|
-
return "Check the command arguments and flags — see `--help` for the expected format.";
|
|
28904
|
-
if (status === 429)
|
|
28905
|
-
return "Rate limited — wait a moment and retry.";
|
|
28906
|
-
if (status >= 500)
|
|
28907
|
-
return "Server error — retry shortly, or check `secondlayer status`.";
|
|
28908
|
-
return;
|
|
28909
|
-
}
|
|
28910
|
-
function handleApiError(err, action) {
|
|
28911
|
-
const status = err instanceof ApiError || err instanceof CliHttpError ? err.status : undefined;
|
|
28912
|
-
if (err instanceof CliHttpError && err.code === "SESSION_EXPIRED" || status === 401) {
|
|
28913
|
-
printError("Authentication required.", {
|
|
28914
|
-
hint: isOssMode() ? "Set INSTANCE_TOKEN from `secondlayer init`." : "Run `secondlayer login` to re-authenticate."
|
|
28915
|
-
});
|
|
28916
|
-
process.exit(1);
|
|
28917
|
-
}
|
|
28918
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
28919
|
-
printError(`Failed to ${action}: ${detail}`, { hint: nextStepHint(status) });
|
|
28920
|
-
process.exit(1);
|
|
28921
|
-
}
|
|
28922
|
-
function withErrorHandling(fn, options) {
|
|
28923
|
-
return async (...args) => {
|
|
28924
|
-
try {
|
|
28925
|
-
await fn(...args);
|
|
28926
|
-
} catch (err) {
|
|
28927
|
-
if (options?.onError) {
|
|
28928
|
-
options.onError(err);
|
|
28929
|
-
} else {
|
|
28930
|
-
handleApiError(err, options?.action ?? "execute command");
|
|
28931
|
-
}
|
|
28932
|
-
}
|
|
28933
|
-
};
|
|
28934
|
-
}
|
|
28935
|
-
async function getPlatformClient() {
|
|
28936
|
-
const { apiUrl, ephemeralKey } = await resolveHostedAuth();
|
|
28937
|
-
return new SecondLayer2({ baseUrl: apiUrl, apiKey: ephemeralKey });
|
|
28938
|
-
}
|
|
28939
|
-
async function listSubgraphsApi() {
|
|
28940
|
-
return (await getPlatformClient()).subgraphs.list();
|
|
28941
|
-
}
|
|
28942
|
-
async function getSubgraphApi(name) {
|
|
28943
|
-
return (await getPlatformClient()).subgraphs.status(name);
|
|
28944
|
-
}
|
|
28945
|
-
async function getSubgraphOpenApi(name, options) {
|
|
28946
|
-
return (await getPlatformClient()).subgraphs.openapi(name, options);
|
|
28947
|
-
}
|
|
28948
|
-
async function getSubgraphAgentSchema(name, options) {
|
|
28949
|
-
return (await getPlatformClient()).subgraphs.schema(name, options);
|
|
28950
|
-
}
|
|
28951
|
-
async function getSubgraphMarkdown(name, options) {
|
|
28952
|
-
return (await getPlatformClient()).subgraphs.markdown(name, options);
|
|
28953
|
-
}
|
|
28954
|
-
async function reindexSubgraphApi(name) {
|
|
28955
|
-
return (await getPlatformClient()).subgraphs.reindex(name);
|
|
28956
|
-
}
|
|
28957
|
-
async function backfillSubgraphApi(name, options) {
|
|
28958
|
-
return (await getPlatformClient()).subgraphs.backfill(name, options);
|
|
28959
|
-
}
|
|
28960
|
-
async function stopSubgraphApi(name) {
|
|
28961
|
-
return (await getPlatformClient()).subgraphs.stop(name);
|
|
28962
|
-
}
|
|
28963
|
-
async function deleteSubgraphApi(name, options) {
|
|
28964
|
-
return (await getPlatformClient()).subgraphs.delete(name, options);
|
|
28965
|
-
}
|
|
28966
|
-
async function deploySubgraphApi(data) {
|
|
28967
|
-
return (await getPlatformClient()).subgraphs.deploy(data);
|
|
28968
|
-
}
|
|
28969
|
-
async function querySubgraphTableRows(name, table, params = {}) {
|
|
28970
|
-
return (await getPlatformClient()).subgraphs.rows(name, table, params);
|
|
28971
|
-
}
|
|
28972
|
-
async function querySubgraphTableCount(name, table, params = {}) {
|
|
28973
|
-
return (await getPlatformClient()).subgraphs.count(name, table, params);
|
|
28974
|
-
}
|
|
28975
|
-
async function getSubgraphGaps(name, opts) {
|
|
28976
|
-
return (await getPlatformClient()).subgraphs.gaps(name, opts);
|
|
28977
|
-
}
|
|
28978
|
-
async function listSubgraphOperationsApi(name) {
|
|
28979
|
-
return (await getPlatformClient()).subgraphs.operations(name);
|
|
28980
|
-
}
|
|
28981
|
-
async function getSubgraphOperationApi(name, operationId) {
|
|
28982
|
-
return (await getPlatformClient()).subgraphs.getOperation(name, operationId);
|
|
28983
|
-
}
|
|
28984
|
-
async function getSubgraphSourceApi(name) {
|
|
28985
|
-
return (await getPlatformClient()).subgraphs.getSource(name);
|
|
28986
|
-
}
|
|
28987
|
-
async function getContractPrintSchema(contractId) {
|
|
28988
|
-
let client;
|
|
28989
|
-
try {
|
|
28990
|
-
client = await getPlatformClient();
|
|
28991
|
-
} catch {
|
|
28992
|
-
client = new SecondLayer2({ baseUrl: resolveApiUrl() });
|
|
28993
|
-
}
|
|
28994
|
-
return client.index.printSchema(contractId);
|
|
28995
|
-
}
|
|
28996
|
-
var init_api_client = __esm(() => {
|
|
28997
|
-
init_http();
|
|
28998
|
-
init_output();
|
|
28999
|
-
init_resolve_auth();
|
|
29000
|
-
});
|
|
29001
|
-
|
|
29002
29005
|
// src/lib/derive-abi.ts
|
|
29003
29006
|
import {
|
|
29004
29007
|
extractSubgraphDefinition,
|
|
@@ -29440,7 +29443,7 @@ var init_git_status = () => {};
|
|
|
29440
29443
|
|
|
29441
29444
|
// ../../node_modules/typescript/lib/typescript.js
|
|
29442
29445
|
var require_typescript = __commonJS(function(exports, module) {
|
|
29443
|
-
var __dirname = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-
|
|
29446
|
+
var __dirname = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-a9a35af4d4343442c/node_modules/typescript/lib", __filename = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-a9a35af4d4343442c/node_modules/typescript/lib/typescript.js";
|
|
29444
29447
|
/*! *****************************************************************************
|
|
29445
29448
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
29446
29449
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
@@ -198790,7 +198793,7 @@ import {
|
|
|
198790
198793
|
watch,
|
|
198791
198794
|
writeFileSync as writeFileSync3
|
|
198792
198795
|
} from "node:fs";
|
|
198793
|
-
import { basename, dirname as dirname5, extname, join as join7, resolve as resolve5 } from "node:path";
|
|
198796
|
+
import { basename, dirname as dirname5, extname, join as join7, relative, resolve as resolve5 } from "node:path";
|
|
198794
198797
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
198795
198798
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
198796
198799
|
import {
|
|
@@ -198802,6 +198805,10 @@ import {
|
|
|
198802
198805
|
billingPausedCode
|
|
198803
198806
|
} from "@secondlayer/shared/schemas";
|
|
198804
198807
|
import { TRAIT_STANDARDS } from "@secondlayer/stacks/clarity";
|
|
198808
|
+
import {
|
|
198809
|
+
deriveVerification as deriveVerification2,
|
|
198810
|
+
isDeterminismViolation
|
|
198811
|
+
} from "@secondlayer/subgraphs/verification";
|
|
198805
198812
|
function webhookHint(apiUrl, name, firstTable) {
|
|
198806
198813
|
if (!firstTable || isMerchantUrl(apiUrl))
|
|
198807
198814
|
return null;
|
|
@@ -199121,6 +199128,36 @@ function createSubgraphDeployPreview(def, options = {}) {
|
|
|
199121
199128
|
...options.bundleBytes !== undefined ? { bundleSize: `${options.bundleBytes} bytes` } : {}
|
|
199122
199129
|
};
|
|
199123
199130
|
}
|
|
199131
|
+
function formatVerificationLines(verification, pin) {
|
|
199132
|
+
if (!verification)
|
|
199133
|
+
return [];
|
|
199134
|
+
const head = [
|
|
199135
|
+
verification.level,
|
|
199136
|
+
...pin ? [`pin ${pin.slice(0, 4)}…${pin.slice(-4)}`] : [],
|
|
199137
|
+
...verification.reasons
|
|
199138
|
+
].join(" · ");
|
|
199139
|
+
return [
|
|
199140
|
+
`verification ${head}`,
|
|
199141
|
+
...verification.unproven.map((u) => `unproven ${u}`)
|
|
199142
|
+
];
|
|
199143
|
+
}
|
|
199144
|
+
function formatVerificationStatus(verification) {
|
|
199145
|
+
if (!verification)
|
|
199146
|
+
return "unknown (deployed before levels were derived)";
|
|
199147
|
+
const head = [verification.level, ...verification.reasons].join(" · ");
|
|
199148
|
+
return verification.unproven.length > 0 ? `${head} (unproven: ${verification.unproven.join("; ")})` : head;
|
|
199149
|
+
}
|
|
199150
|
+
function checkDeterminism(def, findings) {
|
|
199151
|
+
const verification = deriveVerification2(def, findings);
|
|
199152
|
+
if (isDeterminismViolation(verification)) {
|
|
199153
|
+
error("Nondeterministic handler. This subgraph's sources are provable, so its handlers must give the same rows on every run:");
|
|
199154
|
+
for (const r of verification.reasons)
|
|
199155
|
+
error(` ✗ ${r}`);
|
|
199156
|
+
info("Derive values from event, ctx.block and ctx.tx only (no Date, Math.random, fetch, timers or locale formatting).");
|
|
199157
|
+
process.exit(1);
|
|
199158
|
+
}
|
|
199159
|
+
return verification;
|
|
199160
|
+
}
|
|
199124
199161
|
function printSubgraphDeployPreview(preview, context) {
|
|
199125
199162
|
success("Subgraph deploy dry run passed");
|
|
199126
199163
|
const pairs = [
|
|
@@ -199493,8 +199530,11 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
|
|
|
199493
199530
|
info(`${dryRun ? "Bundling for remote deploy dry run" : "Bundling for remote deploy"} (${config.network})...`);
|
|
199494
199531
|
const source = derived?.source ?? await readFile(absPath, "utf8");
|
|
199495
199532
|
const { bundleSubgraphCode } = await import("@secondlayer/bundler");
|
|
199496
|
-
const bundled = await bundleSubgraphCode(source
|
|
199533
|
+
const bundled = await bundleSubgraphCode(source, {
|
|
199534
|
+
fileName: relative(process.cwd(), absPath)
|
|
199535
|
+
});
|
|
199497
199536
|
const handlerCode = bundled.handlerCode;
|
|
199537
|
+
const localVerification = checkDeterminism(effectiveDef, bundled.findings);
|
|
199498
199538
|
if (dryRun) {
|
|
199499
199539
|
printSubgraphDeployPreview(createSubgraphDeployPreview(validated, {
|
|
199500
199540
|
bundleBytes: Buffer.byteLength(handlerCode, "utf8")
|
|
@@ -199503,6 +199543,9 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
|
|
|
199503
199543
|
file: absPath,
|
|
199504
199544
|
bundled: true
|
|
199505
199545
|
});
|
|
199546
|
+
for (const line of formatVerificationLines(localVerification)) {
|
|
199547
|
+
info(` ${line}`);
|
|
199548
|
+
}
|
|
199506
199549
|
return;
|
|
199507
199550
|
}
|
|
199508
199551
|
const deployStartBlock = startBlock ?? effectiveDef.startBlock;
|
|
@@ -199544,6 +199587,11 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
|
|
|
199544
199587
|
});
|
|
199545
199588
|
for (const w of result.warnings ?? [])
|
|
199546
199589
|
warn(w);
|
|
199590
|
+
const printVerification = () => {
|
|
199591
|
+
for (const line of formatVerificationLines(result.verification ?? localVerification, result.pin)) {
|
|
199592
|
+
info(` ${line}`);
|
|
199593
|
+
}
|
|
199594
|
+
};
|
|
199547
199595
|
const printDeployFooter = async () => {
|
|
199548
199596
|
try {
|
|
199549
199597
|
const { apiUrl } = await resolveAuth();
|
|
@@ -199563,10 +199611,13 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
|
|
|
199563
199611
|
};
|
|
199564
199612
|
if (result.action === "unchanged") {
|
|
199565
199613
|
info(`Subgraph "${effectiveDef.name}" is up to date (v${result.version} — no changes)`);
|
|
199614
|
+
printVerification();
|
|
199566
199615
|
} else if (result.action === "handler_updated") {
|
|
199567
199616
|
success(`Subgraph "${effectiveDef.name}" handler updated (v${result.version} — schema unchanged, no reindex needed)`);
|
|
199617
|
+
printVerification();
|
|
199568
199618
|
} else if (result.action === "created") {
|
|
199569
199619
|
success(`Subgraph "${effectiveDef.name}" created → v${result.version}`);
|
|
199620
|
+
printVerification();
|
|
199570
199621
|
await printDeployFooter();
|
|
199571
199622
|
} else if (result.action === "reindexed") {
|
|
199572
199623
|
if (result.diff) {
|
|
@@ -199583,6 +199634,7 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
|
|
|
199583
199634
|
}
|
|
199584
199635
|
}
|
|
199585
199636
|
success(`Subgraph "${effectiveDef.name}" updated → v${result.version} (reindexing)`);
|
|
199637
|
+
printVerification();
|
|
199586
199638
|
await printDeployFooter();
|
|
199587
199639
|
} else {
|
|
199588
199640
|
if (result.diff) {
|
|
@@ -199594,22 +199646,37 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
|
|
|
199594
199646
|
}
|
|
199595
199647
|
}
|
|
199596
199648
|
success(`Subgraph "${effectiveDef.name}" updated → v${result.version}`);
|
|
199649
|
+
printVerification();
|
|
199597
199650
|
await printDeployFooter();
|
|
199598
199651
|
}
|
|
199599
199652
|
} else {
|
|
199653
|
+
let findings;
|
|
199654
|
+
try {
|
|
199655
|
+
const { bundleSubgraphCode } = await import("@secondlayer/bundler");
|
|
199656
|
+
findings = (await bundleSubgraphCode(await readFile(absPath, "utf8"), {
|
|
199657
|
+
fileName: relative(process.cwd(), absPath)
|
|
199658
|
+
})).findings;
|
|
199659
|
+
} catch {
|
|
199660
|
+
findings = undefined;
|
|
199661
|
+
}
|
|
199662
|
+
const verification = findings ? checkDeterminism(effectiveDef, findings) : undefined;
|
|
199600
199663
|
if (dryRun) {
|
|
199601
199664
|
printSubgraphDeployPreview(createSubgraphDeployPreview(validated), {
|
|
199602
199665
|
network: config.network,
|
|
199603
199666
|
file: absPath,
|
|
199604
199667
|
bundled: false
|
|
199605
199668
|
});
|
|
199669
|
+
for (const line of formatVerificationLines(verification)) {
|
|
199670
|
+
info(` ${line}`);
|
|
199671
|
+
}
|
|
199606
199672
|
return;
|
|
199607
199673
|
}
|
|
199608
199674
|
const { deploySchema } = await import("@secondlayer/subgraphs");
|
|
199609
199675
|
const { getDb, closeDb } = await import("@secondlayer/shared/db");
|
|
199610
199676
|
const db = getDb();
|
|
199611
199677
|
const result = await deploySchema(db, effectiveDef, absPath, {
|
|
199612
|
-
forceReindex: startBlock !== undefined
|
|
199678
|
+
forceReindex: startBlock !== undefined,
|
|
199679
|
+
verification
|
|
199613
199680
|
});
|
|
199614
199681
|
if (result.action === "unchanged") {
|
|
199615
199682
|
info(`Subgraph "${effectiveDef.name}" is up to date (v${result.version} — no changes)`);
|
|
@@ -199622,6 +199689,9 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
|
|
|
199622
199689
|
} else {
|
|
199623
199690
|
success(`Subgraph "${effectiveDef.name}" updated → v${result.version}`);
|
|
199624
199691
|
}
|
|
199692
|
+
for (const line of formatVerificationLines(verification)) {
|
|
199693
|
+
info(` ${line}`);
|
|
199694
|
+
}
|
|
199625
199695
|
await closeDb();
|
|
199626
199696
|
}
|
|
199627
199697
|
} catch (err) {
|
|
@@ -199675,6 +199745,8 @@ ${data.length} subgraph(s) total`));
|
|
|
199675
199745
|
console.log(formatKeyValue([
|
|
199676
199746
|
["Name", subgraph.name],
|
|
199677
199747
|
["Version", subgraph.version],
|
|
199748
|
+
["Pin", subgraph.pin ?? "none (deployed before pins)"],
|
|
199749
|
+
["Verification", formatVerificationStatus(subgraph.verification)],
|
|
199678
199750
|
[
|
|
199679
199751
|
"Status",
|
|
199680
199752
|
formatSubgraphStatus(subgraph.status, subgraph.health.lastError)
|
|
@@ -202526,7 +202598,7 @@ var {
|
|
|
202526
202598
|
// package.json
|
|
202527
202599
|
var package_default = {
|
|
202528
202600
|
name: "@secondlayer/cli",
|
|
202529
|
-
version: "19.
|
|
202601
|
+
version: "19.4.0",
|
|
202530
202602
|
description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
|
|
202531
202603
|
repository: {
|
|
202532
202604
|
type: "git",
|
|
@@ -202563,13 +202635,13 @@ var package_default = {
|
|
|
202563
202635
|
"@biomejs/wasm-nodejs": "^1.9.0",
|
|
202564
202636
|
"@dsnp/parquetjs": "~1.8.8",
|
|
202565
202637
|
"@inquirer/prompts": "^8.2.0",
|
|
202566
|
-
"@secondlayer/bundler": "^0.
|
|
202638
|
+
"@secondlayer/bundler": "^0.7.0",
|
|
202567
202639
|
"@secondlayer/scaffold": "^1.6.1",
|
|
202568
|
-
"@secondlayer/sdk": "^14.
|
|
202569
|
-
"@secondlayer/shared": "^11.
|
|
202570
|
-
"@secondlayer/stacks": "^6.
|
|
202571
|
-
"@secondlayer/subgraphs": "^6.
|
|
202572
|
-
"@secondlayer/verify": "^0.
|
|
202640
|
+
"@secondlayer/sdk": "^14.5.0",
|
|
202641
|
+
"@secondlayer/shared": "^11.23.0",
|
|
202642
|
+
"@secondlayer/stacks": "^6.2.0",
|
|
202643
|
+
"@secondlayer/subgraphs": "^6.5.0",
|
|
202644
|
+
"@secondlayer/verify": "^0.3.0",
|
|
202573
202645
|
esbuild: "^0.28.2",
|
|
202574
202646
|
"smol-toml": "^1.8.0",
|
|
202575
202647
|
zod: "^4.3.6"
|
|
@@ -202596,7 +202668,7 @@ var package_default = {
|
|
|
202596
202668
|
|
|
202597
202669
|
// src/commands/archive.ts
|
|
202598
202670
|
import {
|
|
202599
|
-
SecondLayer,
|
|
202671
|
+
SecondLayer as SecondLayer2,
|
|
202600
202672
|
resolveAccountKey as resolveAccountKey3
|
|
202601
202673
|
} from "@secondlayer/sdk";
|
|
202602
202674
|
|
|
@@ -204021,6 +204093,7 @@ var LINKS = [
|
|
|
204021
204093
|
{ step: "burn", label: "burn" },
|
|
204022
204094
|
{ step: "signer-set", label: "cycle" },
|
|
204023
204095
|
{ step: "signatures", label: "signatures" },
|
|
204096
|
+
{ step: "txs", label: "txs" },
|
|
204024
204097
|
{ step: "witness", label: "state root" },
|
|
204025
204098
|
{ step: "names", label: "diff" },
|
|
204026
204099
|
{ step: "rows", label: "rows" }
|
|
@@ -204078,7 +204151,7 @@ function prepareSource(src) {
|
|
|
204078
204151
|
};
|
|
204079
204152
|
return out;
|
|
204080
204153
|
}
|
|
204081
|
-
function linkDetail(step, r, cp
|
|
204154
|
+
function linkDetail(step, r, cp) {
|
|
204082
204155
|
switch (step) {
|
|
204083
204156
|
case "ancestry": {
|
|
204084
204157
|
const a = r.ancestry;
|
|
@@ -204098,6 +204171,8 @@ function linkDetail(step, r, cp, rows) {
|
|
|
204098
204171
|
return r.cycle === cp.stacks.cycle ? `${r.cycle}, the checkpoint's own signer set` : `${r.cycle}, signer set proven forward from checkpoint cycle ${cp.stacks.cycle}`;
|
|
204099
204172
|
case "signatures":
|
|
204100
204173
|
return `${n(r.signerWeight ?? 0n)} / ${n(r.totalWeight ?? 0n)} weight signed, threshold ${n(r.threshold ?? 0n)}`;
|
|
204174
|
+
case "txs":
|
|
204175
|
+
return `${n(r.transactions?.length ?? 0)} transactions match the tx merkle root`;
|
|
204101
204176
|
case "witness":
|
|
204102
204177
|
return `${r.stateRoot} matches the witness`;
|
|
204103
204178
|
case "names": {
|
|
@@ -204105,7 +204180,7 @@ function linkDetail(step, r, cp, rows) {
|
|
|
204105
204180
|
if (!d)
|
|
204106
204181
|
return "";
|
|
204107
204182
|
const named = d.writes.filter((w) => w.named).length;
|
|
204108
|
-
const written = named ? `${d.writes.length} written (${named} named, ${d.writes.length - named} unnamed)` : `${d.writes.length} written (unnamed
|
|
204183
|
+
const written = named ? `${d.writes.length} written (${named} named, ${d.writes.length - named} unnamed)` : `${d.writes.length} written (unnamed)`;
|
|
204109
204184
|
return `${written}, ${d.carried.length} carried, ${d.internal.length} internal`;
|
|
204110
204185
|
}
|
|
204111
204186
|
case "rows":
|
|
@@ -204137,12 +204212,13 @@ function report(r, cp, rows) {
|
|
|
204137
204212
|
writeData(dim(`· ${label} not needed: the block is hash-chained to the checkpoint`));
|
|
204138
204213
|
else if (link.step === "rows" && r.rowsChecked === undefined)
|
|
204139
204214
|
writeData(dim(`· ${label} not checked: the source serves no indexed rows`));
|
|
204215
|
+
else if (link.step === "txs" && r.transactions === undefined)
|
|
204216
|
+
writeData(dim(`· ${label} not checked: no transaction bytes (epoch 2.x, or the source served the header only)`));
|
|
204140
204217
|
else
|
|
204141
|
-
writeData(`${green("✓")} ${label} ${linkDetail(link.step, r, cp
|
|
204218
|
+
writeData(`${green("✓")} ${label} ${linkDetail(link.step, r, cp)}`);
|
|
204142
204219
|
}
|
|
204143
204220
|
for (const line of r.notes)
|
|
204144
|
-
|
|
204145
|
-
note(` note: ${line}`);
|
|
204221
|
+
note(` note: ${line}`);
|
|
204146
204222
|
if (!first) {
|
|
204147
204223
|
const from = n(parseNakamotoHeader(unhex(cp.stacks.header)).chainLength);
|
|
204148
204224
|
writeData(green(`✓ Block ${n(r.height ?? 0)} is proven. Trusted: only the checkpoint (Stacks ${from}, Bitcoin ${n(cp.bitcoin.height)}).`));
|
|
@@ -204152,7 +204228,8 @@ function report(r, cp, rows) {
|
|
|
204152
204228
|
writeData(red(first.code === "unavailable" ? `✗ Could not check the ${label} link: the source could not serve it.` : `✗ Not proven: the ${label} link is broken.`));
|
|
204153
204229
|
}
|
|
204154
204230
|
function verificationJson(r) {
|
|
204155
|
-
|
|
204231
|
+
const { transactions, ...rest } = r;
|
|
204232
|
+
return JSON.stringify(transactions ? { ...rest, transactions: transactions.map((t) => t.txid) } : rest, (_k, v) => typeof v === "bigint" ? v.toString() : v, 2);
|
|
204156
204233
|
}
|
|
204157
204234
|
function exitCodeOf(r) {
|
|
204158
204235
|
if (r.ok)
|
|
@@ -204181,7 +204258,7 @@ async function runVerifyBlock(ref, opts, deps = {}) {
|
|
|
204181
204258
|
return exitCodeOf(result);
|
|
204182
204259
|
}
|
|
204183
204260
|
function attachVerifyBlockCommand(verify) {
|
|
204184
|
-
return verify.command("block").description("Prove one Stacks block from the checkpoint built into this release, trusting nothing else").argument("<ref>", "block height or index block hash").option("--node <url>", "read blocks and MARF proofs from this Stacks node RPC instead of the API").option("--checkpoint <file>", "trust this checkpoint JSON instead of the one built into this release").option("--rows", "also prove the
|
|
204261
|
+
return verify.command("block").description("Prove one Stacks block from the checkpoint built into this release, trusting nothing else").argument("<ref>", "block height or index block hash").option("--node <url>", "read blocks and MARF proofs from this Stacks node RPC instead of the API").option("--checkpoint <file>", "trust this checkpoint JSON instead of the one built into this release").option("--rows", "also prove the Index API's vm_events rows for the block against its state diff (billed as Index rows)").addHelpText("after", `
|
|
204185
204262
|
What is proven, link by link:
|
|
204186
204263
|
ancestry below the checkpoint only: the block's id, from the checkpoint by
|
|
204187
204264
|
parent links or one MARF proof (signer links are then not needed)
|
|
@@ -204189,9 +204266,13 @@ What is proven, link by link:
|
|
|
204189
204266
|
burn the block's consensus hash commits to that Bitcoin block
|
|
204190
204267
|
cycle the reward cycle's signer set, proven forward from the checkpoint's set
|
|
204191
204268
|
signatures signers holding at least 70% of the weight signed the block header
|
|
204269
|
+
txs the block's transactions hash to the tx merkle root in its header
|
|
204192
204270
|
state root the state witness hashes to the root in the signed header
|
|
204193
|
-
diff every leaf the block wrote, carried, or kept as bookkeeping
|
|
204194
|
-
|
|
204271
|
+
diff every leaf the block wrote, carried, or kept as bookkeeping; writes
|
|
204272
|
+
are named from the block's state_writes (free, /v1/proofs/writes)
|
|
204273
|
+
when the source has them, else listed as unnamed with a note
|
|
204274
|
+
rows with --rows: the Index API's vm_events rows match that diff
|
|
204275
|
+
(billed as Index rows; everything else is free)
|
|
204195
204276
|
|
|
204196
204277
|
Nothing is trusted except the checkpoint. Proofs come from the API
|
|
204197
204278
|
(SECONDLAYER_API_URL, or --api-url) with your account key, or from a node
|
|
@@ -204215,13 +204296,242 @@ Exit codes:
|
|
|
204215
204296
|
});
|
|
204216
204297
|
}
|
|
204217
204298
|
|
|
204299
|
+
// src/commands/verify-subgraph.ts
|
|
204300
|
+
init_api_client();
|
|
204301
|
+
init_output();
|
|
204302
|
+
import { deriveVerification } from "@secondlayer/subgraphs/verification";
|
|
204303
|
+
import {
|
|
204304
|
+
checkPin,
|
|
204305
|
+
compareRows,
|
|
204306
|
+
loadDeterministicDefinition,
|
|
204307
|
+
replayContracts,
|
|
204308
|
+
replaySubgraph
|
|
204309
|
+
} from "@secondlayer/subgraphs/verify";
|
|
204310
|
+
import {
|
|
204311
|
+
MAINNET_CHECKPOINT as MAINNET_CHECKPOINT2,
|
|
204312
|
+
parseNakamotoHeader as parseNakamotoHeader2,
|
|
204313
|
+
unhex as unhex2
|
|
204314
|
+
} from "@secondlayer/verify";
|
|
204315
|
+
var LINKS2 = [
|
|
204316
|
+
{ step: "pin", label: "pin" },
|
|
204317
|
+
{ step: "blocks", label: "blocks" },
|
|
204318
|
+
{ step: "txs", label: "txs" },
|
|
204319
|
+
{ step: "inputs", label: "inputs" },
|
|
204320
|
+
{ step: "handlers", label: "handlers" },
|
|
204321
|
+
{ step: "rows", label: "rows" }
|
|
204322
|
+
];
|
|
204323
|
+
var LABEL_WIDTH2 = Math.max(...LINKS2.map((l) => l.label.length));
|
|
204324
|
+
var PROGRESS_EVERY = 100;
|
|
204325
|
+
var PAGE = 1000;
|
|
204326
|
+
var n2 = (v) => v.toLocaleString("en-US");
|
|
204327
|
+
var short = (h) => `${h.slice(0, 4)}…${h.slice(-4)}`;
|
|
204328
|
+
|
|
204329
|
+
class InputError extends Error {
|
|
204330
|
+
}
|
|
204331
|
+
function parseHeight2(raw, flag) {
|
|
204332
|
+
if (raw === undefined)
|
|
204333
|
+
return;
|
|
204334
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(Number(raw)))
|
|
204335
|
+
throw new InputError(`${flag} must be a block height, got "${raw}"`);
|
|
204336
|
+
return Number(raw);
|
|
204337
|
+
}
|
|
204338
|
+
async function readTable(fetchRows, name, table) {
|
|
204339
|
+
const out = [];
|
|
204340
|
+
let cursor;
|
|
204341
|
+
for (;; ) {
|
|
204342
|
+
const page = await fetchRows(name, table, cursor);
|
|
204343
|
+
out.push(...page.rows);
|
|
204344
|
+
if (!page.next_cursor || page.rows.length === 0)
|
|
204345
|
+
return out;
|
|
204346
|
+
cursor = page.next_cursor;
|
|
204347
|
+
}
|
|
204348
|
+
}
|
|
204349
|
+
function linkLine(step, r) {
|
|
204350
|
+
const res = r.result;
|
|
204351
|
+
switch (step) {
|
|
204352
|
+
case "pin":
|
|
204353
|
+
return `${short(r.pinValue ?? "")} handler, schema and startBlock match the deploy`;
|
|
204354
|
+
case "blocks":
|
|
204355
|
+
return `${n2(res.blocks)} proven from the checkpoint${res.marfProofs ? ` (${n2(res.marfProofs)} MARF proof${res.marfProofs === 1 ? "" : "s"}, then parent links)` : ""}`;
|
|
204356
|
+
case "txs":
|
|
204357
|
+
return `${n2(res.txs)} transactions match their blocks' tx merkle roots`;
|
|
204358
|
+
case "inputs":
|
|
204359
|
+
return `${n2(res.blocks)} diffs fully named; ${n2(res.writes)} writes to ${r.contracts.map((c) => c.split(".")[1] ?? c).join(", ")}`;
|
|
204360
|
+
case "handlers":
|
|
204361
|
+
return `${n2(res.events)} events, ${n2(res.handlerErrors)} handler errors, deterministic realm`;
|
|
204362
|
+
case "rows":
|
|
204363
|
+
return (r.comparison?.tables ?? []).map((t) => `${t.table}: ${n2(t.equal)} equal${t.superseded ? `, ${n2(t.superseded)} superseded after ${n2(res.to)}` : ""} digest ${short(t.digest)}`).join(`
|
|
204364
|
+
${" ".repeat(LABEL_WIDTH2 + 4)}`);
|
|
204365
|
+
}
|
|
204366
|
+
}
|
|
204367
|
+
function firstFailure(r) {
|
|
204368
|
+
if (r.pin.status === "failed")
|
|
204369
|
+
return r.pin.failure;
|
|
204370
|
+
return r.result?.failures[0] ?? r.comparison?.failures[0];
|
|
204371
|
+
}
|
|
204372
|
+
function report2(r, from, to) {
|
|
204373
|
+
const first = firstFailure(r);
|
|
204374
|
+
const broken = first ? LINKS2.findIndex((l) => l.step === first.step) : LINKS2.length;
|
|
204375
|
+
const inconclusive = !first ? r.result?.inconclusive : undefined;
|
|
204376
|
+
for (const [i, link] of LINKS2.entries()) {
|
|
204377
|
+
const label = link.label.padEnd(LABEL_WIDTH2);
|
|
204378
|
+
if (first && i === broken)
|
|
204379
|
+
writeData(`${red("✗")} ${label} ${first.message}`);
|
|
204380
|
+
else if (i > broken)
|
|
204381
|
+
writeData(dim(`· ${label} not checked: an earlier link is broken`));
|
|
204382
|
+
else if (link.step === "pin" && r.pin.status === "unchecked")
|
|
204383
|
+
writeData(dim(`· ${label} not checked: deployed before pin inputs were stored (redeploy)`));
|
|
204384
|
+
else if (link.step === "rows" && inconclusive)
|
|
204385
|
+
writeData(dim(`· ${label} not checked: ${inconclusive}`));
|
|
204386
|
+
else
|
|
204387
|
+
writeData(`${green("✓")} ${label} ${linkLine(link.step, r)}`);
|
|
204388
|
+
}
|
|
204389
|
+
for (const line of [...r.pin.notes, ...r.result?.notes ?? []])
|
|
204390
|
+
note(` note: ${line}`);
|
|
204391
|
+
if (first) {
|
|
204392
|
+
const label = LINKS2[broken]?.label ?? first.step;
|
|
204393
|
+
writeData(red(first.unavailable ? `✗ Could not check the ${label} link: the source could not serve it.` : `✗ Not proven: the ${label} link is broken.`));
|
|
204394
|
+
return first.unavailable ? VERIFY_EXIT.UNANCHORED : VERIFY_EXIT.DIVERGED;
|
|
204395
|
+
}
|
|
204396
|
+
if (inconclusive) {
|
|
204397
|
+
writeData(dim(`· Inconclusive: ${inconclusive}. Rows were not compared.`));
|
|
204398
|
+
return VERIFY_EXIT.UNANCHORED;
|
|
204399
|
+
}
|
|
204400
|
+
const cp = MAINNET_CHECKPOINT2;
|
|
204401
|
+
const stacks = n2(Number(parseNakamotoHeader2(unhex2(cp.stacks.header)).chainLength));
|
|
204402
|
+
writeData(green(`✓ ${r.name} ${n2(from)}..${n2(to)} recomputed from proven inputs. Trusted: only the checkpoint (Stacks ${stacks}, Bitcoin ${n2(cp.bitcoin.height)}).`));
|
|
204403
|
+
writeData(dim(` unproven, so not compared: ${[...r.unproven, "rows compare declared columns and _block_height only"].join("; ")}`));
|
|
204404
|
+
return VERIFY_EXIT.CLEAN;
|
|
204405
|
+
}
|
|
204406
|
+
function reportJson(r) {
|
|
204407
|
+
const { tables, ...result } = r.result ?? {};
|
|
204408
|
+
return JSON.stringify({
|
|
204409
|
+
pin: r.pin,
|
|
204410
|
+
replay: r.result ? {
|
|
204411
|
+
...result,
|
|
204412
|
+
tables: Object.fromEntries([...tables ?? new Map].map(([t, rows]) => [t, rows.length]))
|
|
204413
|
+
} : null,
|
|
204414
|
+
comparison: r.comparison ?? null
|
|
204415
|
+
}, (_k, v) => typeof v === "bigint" ? v.toString() : v, 2);
|
|
204416
|
+
}
|
|
204417
|
+
async function runVerifySubgraph(name, opts, deps = {}) {
|
|
204418
|
+
const fetchSubgraph = deps.fetchSubgraph ?? getSubgraphApi;
|
|
204419
|
+
const fetchSource = deps.fetchSource ?? getSubgraphSourceApi;
|
|
204420
|
+
const fetchRows = deps.fetchRows ?? ((n, t, cursor) => getSubgraphRowsApi(n, t, { cursor, limit: PAGE }));
|
|
204421
|
+
try {
|
|
204422
|
+
if (!opts.replay)
|
|
204423
|
+
throw new InputError("pass --replay: the only check today re-runs the handlers over proven blocks");
|
|
204424
|
+
const fromFlag = parseHeight2(opts.from, "--from");
|
|
204425
|
+
const toFlag = parseHeight2(opts.to, "--to");
|
|
204426
|
+
const detail = await fetchSubgraph(name);
|
|
204427
|
+
const level = detail.verification;
|
|
204428
|
+
if (level?.level !== "state")
|
|
204429
|
+
throw new InputError(level ? `${name} is level ${level.level}, not state: ${level.reasons.join("; ")}` : `${name} has no verification level: deployed before levels were derived (redeploy)`);
|
|
204430
|
+
const served = await fetchSource(name);
|
|
204431
|
+
if (!served.handlerCode)
|
|
204432
|
+
throw new InputError(`${name} has no stored handler bundle to replay (a local deploy)`);
|
|
204433
|
+
const def = await loadDeterministicDefinition(served.handlerCode);
|
|
204434
|
+
const pin = checkPin({
|
|
204435
|
+
pin: served.pin ?? null,
|
|
204436
|
+
pinPreimage: served.pinPreimage ?? null,
|
|
204437
|
+
handlerCode: served.handlerCode
|
|
204438
|
+
}, def);
|
|
204439
|
+
const startBlock = typeof detail.definition?.startBlock === "number" ? detail.definition.startBlock : def.startBlock ?? 0;
|
|
204440
|
+
const contracts = replayContracts(def);
|
|
204441
|
+
const tables = Object.keys(def.schema);
|
|
204442
|
+
const first = tables[0] ? await fetchRows(name, tables[0]) : { tip: { subgraph_height: startBlock } };
|
|
204443
|
+
const tip = first.tip.subgraph_height;
|
|
204444
|
+
const from = fromFlag ?? startBlock;
|
|
204445
|
+
const to = toFlag ?? tip;
|
|
204446
|
+
if (to > tip)
|
|
204447
|
+
throw new InputError(`--to ${n2(to)} is past the served subgraph: it is at ${n2(tip)}`);
|
|
204448
|
+
if (from > to)
|
|
204449
|
+
throw new InputError(`--from ${n2(from)} is after --to ${n2(to)}`);
|
|
204450
|
+
const r = {
|
|
204451
|
+
name,
|
|
204452
|
+
pin,
|
|
204453
|
+
pinValue: served.pin ?? null,
|
|
204454
|
+
contracts: Array.isArray(contracts) ? contracts : [],
|
|
204455
|
+
unproven: deriveVerification(def).unproven
|
|
204456
|
+
};
|
|
204457
|
+
if (pin.status !== "failed") {
|
|
204458
|
+
const servedRows = new Map;
|
|
204459
|
+
for (const table of tables)
|
|
204460
|
+
servedRows.set(table, await readTable(fetchRows, name, table));
|
|
204461
|
+
note(`Replaying ${name} ${n2(from)}..${n2(to)} (${n2(to - from + 1)} blocks)`);
|
|
204462
|
+
r.result = await (deps.replay ?? replaySubgraph)({
|
|
204463
|
+
source: deps.source ?? defaultSource(opts.node),
|
|
204464
|
+
handlerCode: served.handlerCode,
|
|
204465
|
+
from,
|
|
204466
|
+
to,
|
|
204467
|
+
startBlock,
|
|
204468
|
+
onProgress: (h) => {
|
|
204469
|
+
const done = h - from + 1;
|
|
204470
|
+
if (done % PROGRESS_EVERY === 0)
|
|
204471
|
+
note(` replayed ${n2(done)} / ${n2(to - from + 1)} blocks`);
|
|
204472
|
+
}
|
|
204473
|
+
});
|
|
204474
|
+
if (r.result.failures.length === 0 && !r.result.inconclusive)
|
|
204475
|
+
r.comparison = compareRows(def.schema, r.result, servedRows);
|
|
204476
|
+
}
|
|
204477
|
+
if (opts.json) {
|
|
204478
|
+
writeData(reportJson(r));
|
|
204479
|
+
const f = firstFailure(r);
|
|
204480
|
+
if (f)
|
|
204481
|
+
return f.unavailable ? VERIFY_EXIT.UNANCHORED : VERIFY_EXIT.DIVERGED;
|
|
204482
|
+
return r.result?.inconclusive ? VERIFY_EXIT.UNANCHORED : VERIFY_EXIT.CLEAN;
|
|
204483
|
+
}
|
|
204484
|
+
return report2(r, from, to);
|
|
204485
|
+
} catch (err) {
|
|
204486
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
204487
|
+
return VERIFY_EXIT.UNANCHORED;
|
|
204488
|
+
}
|
|
204489
|
+
}
|
|
204490
|
+
function attachVerifySubgraphCommand(verify) {
|
|
204491
|
+
return verify.command("subgraph").description("Recompute a state-level subgraph from proven blocks and check its served rows").argument("<name>", "deployed subgraph name").option("--replay", "re-run the handlers over blocks proven from the checkpoint").option("--from <height>", "first block to replay (default: the subgraph's startBlock)").option("--to <height>", "last block to replay (default: the served subgraph's height)").option("--node <url>", "read blocks and MARF proofs from this Stacks node RPC instead of the API").addHelpText("after", `
|
|
204492
|
+
What is proven, link by link:
|
|
204493
|
+
pin the served pin is the sha256 of its preimage, which names the served
|
|
204494
|
+
handler bundle, its schema and startBlock
|
|
204495
|
+
blocks every block in the range, from the checkpoint (signatures above it,
|
|
204496
|
+
parent links and MARF proofs below it)
|
|
204497
|
+
txs each block's transactions hash to its header's tx merkle root
|
|
204498
|
+
inputs each block's writes are named, nothing hidden (state_writes vs witness)
|
|
204499
|
+
handlers the bundle runs in the deterministic realm over those writes
|
|
204500
|
+
rows the served rows equal the recomputed ones (keyed by the first
|
|
204501
|
+
unique key, else as a multiset), with a digest per table
|
|
204502
|
+
|
|
204503
|
+
Starting after startBlock (--from) is inconclusive once a handler reads or
|
|
204504
|
+
merges rows written before the range (findOne, increment, a partial upsert).
|
|
204505
|
+
Not proven, so not compared: which transaction made a write (event.tx,
|
|
204506
|
+
_tx_id) and writes overwritten within one block. Rows compare on their
|
|
204507
|
+
declared columns and _block_height.
|
|
204508
|
+
|
|
204509
|
+
Reads are free: proofs and each block's state writes from /v1/proofs (rate
|
|
204510
|
+
limited; witnesses 2 per second, so 1,000 blocks take 8 minutes or more) and
|
|
204511
|
+
the subgraph's rows from /v1/subgraphs. Nothing bills as Index rows.
|
|
204512
|
+
|
|
204513
|
+
Examples:
|
|
204514
|
+
$ secondlayer verify subgraph pool-reserves --replay
|
|
204515
|
+
$ secondlayer verify subgraph pool-reserves --replay --from 1230000 --to 1231000
|
|
204516
|
+
$ secondlayer verify subgraph pool-reserves --replay --json
|
|
204517
|
+
|
|
204518
|
+
Exit codes:
|
|
204519
|
+
0 every link holds
|
|
204520
|
+
1 a link is broken (the first is named)
|
|
204521
|
+
2 could not check: the source could not serve a link, the replay is
|
|
204522
|
+
inconclusive, the subgraph is not state-level, or bad input`).action(async (name, opts, cmd) => {
|
|
204523
|
+
const json = cmd.optsWithGlobals().json === true;
|
|
204524
|
+
process.exit(await runVerifySubgraph(name, { ...opts, json }));
|
|
204525
|
+
});
|
|
204526
|
+
}
|
|
204527
|
+
|
|
204218
204528
|
// src/commands/verify.ts
|
|
204219
204529
|
var VERIFY_EXIT = {
|
|
204220
204530
|
CLEAN: 0,
|
|
204221
204531
|
DIVERGED: 1,
|
|
204222
204532
|
UNANCHORED: 2
|
|
204223
204533
|
};
|
|
204224
|
-
function
|
|
204534
|
+
function parseHeight3(value, flag) {
|
|
204225
204535
|
const parsed = Number(value);
|
|
204226
204536
|
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
204227
204537
|
throw new Error(`${flag} must be a non-negative integer, got "${value}"`);
|
|
@@ -204257,8 +204567,8 @@ Exit codes:
|
|
|
204257
204567
|
try {
|
|
204258
204568
|
const target = parseVerifyTarget(targetArg);
|
|
204259
204569
|
const mode = opts.deep ? "deep" : opts.anchor ? "anchor" : "quick";
|
|
204260
|
-
const fromBlock = opts.fromBlock === undefined ? undefined :
|
|
204261
|
-
const toBlock = opts.toBlock === undefined ? undefined :
|
|
204570
|
+
const fromBlock = opts.fromBlock === undefined ? undefined : parseHeight3(opts.fromBlock, "--from-block");
|
|
204571
|
+
const toBlock = opts.toBlock === undefined ? undefined : parseHeight3(opts.toBlock, "--to-block");
|
|
204262
204572
|
if (fromBlock !== undefined && toBlock !== undefined && toBlock < fromBlock) {
|
|
204263
204573
|
throw new Error("--to-block must be >= --from-block");
|
|
204264
204574
|
}
|
|
@@ -204432,13 +204742,14 @@ Exit codes:
|
|
|
204432
204742
|
});
|
|
204433
204743
|
}
|
|
204434
204744
|
function registerVerifyCommand(program) {
|
|
204435
|
-
const verify = attachVerifyCommand(program.command("verify").description("Compare local chain data against a signed archive,
|
|
204745
|
+
const verify = attachVerifyCommand(program.command("verify").description("Compare local chain data against a signed archive, prove one block with `verify block`, or recompute a subgraph with `verify subgraph --replay` (read-only)"));
|
|
204436
204746
|
attachVerifyBlockCommand(verify);
|
|
204747
|
+
attachVerifySubgraphCommand(verify);
|
|
204437
204748
|
}
|
|
204438
204749
|
|
|
204439
204750
|
// src/commands/archive.ts
|
|
204440
204751
|
var ARCHIVE_KEY_HINT = "set SECONDLAYER_API_KEY (sk-sl_*) for archive latest/quote; INSTANCE_TOKEN is the instance";
|
|
204441
|
-
function
|
|
204752
|
+
function parseHeight4(value, flag) {
|
|
204442
204753
|
const parsed = Number(value);
|
|
204443
204754
|
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
204444
204755
|
throw new Error(`${flag} must be a non-negative integer, got "${value}"`);
|
|
@@ -204459,7 +204770,7 @@ function requireAccountKey(resolve = resolveAccountKey3) {
|
|
|
204459
204770
|
return accountKey;
|
|
204460
204771
|
}
|
|
204461
204772
|
function defaultArchive(accountKey) {
|
|
204462
|
-
return new
|
|
204773
|
+
return new SecondLayer2({
|
|
204463
204774
|
accountKey,
|
|
204464
204775
|
archiveOpsUrl: resolveArchiveOpsUrl()
|
|
204465
204776
|
}).archive;
|
|
@@ -204500,8 +204811,8 @@ async function runLatest(opts, deps = {}) {
|
|
|
204500
204811
|
async function runQuote(opts, deps = {}) {
|
|
204501
204812
|
const accountKey = requireAccountKey(deps.resolveAccountKey);
|
|
204502
204813
|
const flow = parseFlow(opts.flow);
|
|
204503
|
-
const fromBlock = opts.fromBlock === undefined ? undefined :
|
|
204504
|
-
const toBlock = opts.toBlock === undefined ? undefined :
|
|
204814
|
+
const fromBlock = opts.fromBlock === undefined ? undefined : parseHeight4(opts.fromBlock, "--from-block");
|
|
204815
|
+
const toBlock = opts.toBlock === undefined ? undefined : parseHeight4(opts.toBlock, "--to-block");
|
|
204505
204816
|
if (fromBlock !== undefined && toBlock !== undefined && toBlock < fromBlock) {
|
|
204506
204817
|
printError("--to-block must be >= --from-block");
|
|
204507
204818
|
process.exit(1);
|
|
@@ -205301,7 +205612,7 @@ import {
|
|
|
205301
205612
|
statSync as statSync2,
|
|
205302
205613
|
writeFileSync as writeFileSync4
|
|
205303
205614
|
} from "node:fs";
|
|
205304
|
-
import { dirname as dirname6, join as join9, relative, resolve as resolve7 } from "node:path";
|
|
205615
|
+
import { dirname as dirname6, join as join9, relative as relative2, resolve as resolve7 } from "node:path";
|
|
205305
205616
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
205306
205617
|
import { input, select as select3 } from "@inquirer/prompts";
|
|
205307
205618
|
import { SecondLayer as SecondLayer4 } from "@secondlayer/sdk";
|
|
@@ -205483,7 +205794,7 @@ async function createWebhook(name, opts) {
|
|
|
205483
205794
|
const targetDir = resolve7(process.cwd(), name);
|
|
205484
205795
|
if (opts.scaffold !== false) {
|
|
205485
205796
|
if (existsSync5(targetDir)) {
|
|
205486
|
-
error(`Directory already exists: ${
|
|
205797
|
+
error(`Directory already exists: ${relative2(process.cwd(), targetDir)}`);
|
|
205487
205798
|
process.exit(1);
|
|
205488
205799
|
}
|
|
205489
205800
|
const tplRoot = templatesRoot();
|
|
@@ -205530,7 +205841,7 @@ async function createWebhook(name, opts) {
|
|
|
205530
205841
|
}
|
|
205531
205842
|
if (signingSecret && opts.scaffold !== false) {
|
|
205532
205843
|
const envTarget = writeSigningSecretEnv(targetDir, signingSecret);
|
|
205533
|
-
success(`Signing secret written to ${
|
|
205844
|
+
success(`Signing secret written to ${relative2(process.cwd(), envTarget)}`);
|
|
205534
205845
|
} else if (signingSecret && opts.scaffold === false) {
|
|
205535
205846
|
info(`Signing secret (store securely): ${signingSecret}`);
|
|
205536
205847
|
}
|
|
@@ -206978,7 +207289,7 @@ function consumeHandlers(io) {
|
|
|
206978
207289
|
}
|
|
206979
207290
|
};
|
|
206980
207291
|
}
|
|
206981
|
-
function
|
|
207292
|
+
function parseHeight5(value, name) {
|
|
206982
207293
|
if (value === undefined)
|
|
206983
207294
|
return;
|
|
206984
207295
|
const n = Number.parseInt(value, 10);
|
|
@@ -207012,8 +207323,8 @@ Examples:
|
|
|
207012
207323
|
sender: parseList(options.sender),
|
|
207013
207324
|
recipient: parseList(options.recipient),
|
|
207014
207325
|
cursor: options.cursor,
|
|
207015
|
-
fromHeight:
|
|
207016
|
-
toHeight:
|
|
207326
|
+
fromHeight: parseHeight5(options.fromBlock, "--from-block"),
|
|
207327
|
+
toHeight: parseHeight5(options.toBlock, "--to-block"),
|
|
207017
207328
|
limit: parseLimit(options.limit)
|
|
207018
207329
|
});
|
|
207019
207330
|
writeData(JSON.stringify(envelope, null, 2));
|
|
@@ -207077,8 +207388,8 @@ Examples:
|
|
|
207077
207388
|
});
|
|
207078
207389
|
const runDumps = async (options) => {
|
|
207079
207390
|
try {
|
|
207080
|
-
const fromBlock =
|
|
207081
|
-
const toBlock =
|
|
207391
|
+
const fromBlock = parseHeight5(options.fromBlock, "--from-block");
|
|
207392
|
+
const toBlock = parseHeight5(options.toBlock, "--to-block");
|
|
207082
207393
|
const dumps = dumpsClient(options.dumpsUrl).dumps;
|
|
207083
207394
|
const manifest = await dumps.list();
|
|
207084
207395
|
const files = manifest.files.filter((f) => (fromBlock === undefined || f.to_block >= fromBlock) && (toBlock === undefined || f.from_block <= toBlock));
|
|
@@ -207109,7 +207420,7 @@ Examples:
|
|
|
207109
207420
|
$ secondlayer streams dumps --to ./dump --from-block 150000 --to-block 200000`).action(runDumps);
|
|
207110
207421
|
streams.command("canonical <height>").description("Canonical block metadata at a given height").option("--json", "Output as JSON (streams always emits JSON)").action(async (heightArg) => {
|
|
207111
207422
|
try {
|
|
207112
|
-
const height =
|
|
207423
|
+
const height = parseHeight5(heightArg, "<height>");
|
|
207113
207424
|
if (height === undefined)
|
|
207114
207425
|
throw new Error("<height> is required");
|
|
207115
207426
|
const block = await client2().canonical(height);
|
|
@@ -209529,5 +209840,5 @@ registerCreditsCommand(program);
|
|
|
209529
209840
|
registerArchiveCommand(program);
|
|
209530
209841
|
program.parse();
|
|
209531
209842
|
|
|
209532
|
-
//# debugId=
|
|
209843
|
+
//# debugId=F270856631D97EA764756E2164756E21
|
|
209533
209844
|
//# sourceMappingURL=cli.js.map
|