@inversealtruism/cairn-sdk 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/{chunk-ETZYBDFC.js → chunk-FNBE2FPE.js} +6 -3
- package/dist/index.cjs +36 -9
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +33 -8
- package/dist/indexer.cjs +6 -3
- package/dist/indexer.d.cts +13 -6
- package/dist/indexer.d.ts +13 -6
- package/dist/indexer.js +1 -1
- package/package.json +14 -8
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ Subpath imports are available for tree-shaking: `@inversealtruism/cairn-sdk/{con
|
|
|
81
81
|
## Trust model & honest limits
|
|
82
82
|
|
|
83
83
|
- **Content is always verified client-side.** `cairn.content.get()` rejects bytes that don't hash to the requested `payloadHash` — an untrusted gateway can't serve tampered content.
|
|
84
|
-
- **Merkle inclusion is trust-minimized.** `verifyInclusion()` folds the proof and cross-checks the root against
|
|
84
|
+
- **Merkle inclusion is trust-minimized — to the strength of your header source.** `verifyInclusion()` folds the proof and, when a header-merkle source is wired, cross-checks the root against it → `trustLevel: "verified-inclusion"`; otherwise `"proof-consistent"` (indexer trusted). That cross-check only means something against an *independent* header, so the default `Cairn` wiring enables it ONLY when your node RPC is a different origin than the indexer (the default same-origin proxy stays at `"proof-consistent"` — a single compromised server can't be cross-checked against itself). For full trust-minimization against a lying node too, supply a PoW-verifying `headerMerkleAt` (e.g. backed by `cairn.chain.light()`).
|
|
85
85
|
- **Balances are RPC-trusted.** Headers don't commit to the UTXO set; balance reads trust the node/proxy (same as every light client).
|
|
86
86
|
- **Indexer/swarm run behind the cairn proxy.** By default `cairn.index` and content resolution use the cairn server's hardened, read-only, rate-limited `/explorer/api` reverse proxy (no separate public indexer needed). **SSE works through it; `subscribe()` (WebSocket) needs a direct indexer endpoint** — set `baseUrls.indexer` to a private/localhost indexer for WS.
|
|
87
87
|
- **Today the wallet injects only on `cairn-substrate.com` + localhost.** Connecting from an arbitrary third-party origin requires the wallet's "connect anywhere" release (broadened injection), which ships after the per-origin consent layer is proven.
|
|
@@ -103,6 +103,17 @@ new Cairn({
|
|
|
103
103
|
});
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
## Testing
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pnpm build && pnpm test # unit (offline): error mapping, client paths, content+merkle verify
|
|
110
|
+
pnpm test:e2e:read # live reads vs mainnet + adversarial (free)
|
|
111
|
+
pnpm test:e2e:wallet # real extension + real connector under Xvfb: connect/consent/sign (free)
|
|
112
|
+
pnpm test:e2e:write # full pipeline incl. a real on-chain Propose+Attest (spends ~0.3 CSD)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
See [`test/e2e/README.md`](test/e2e/README.md) for coverage, requirements, and cost of each layer. The unit + live-read + wallet-connector suites are CI-friendly and free; the write suite needs a funded key and is opt-in.
|
|
116
|
+
|
|
106
117
|
## License
|
|
107
118
|
|
|
108
119
|
MIT
|
|
@@ -75,9 +75,12 @@ var IndexerClient = class {
|
|
|
75
75
|
/**
|
|
76
76
|
* Verify a tx is included in a block by folding its merkle branch to the root
|
|
77
77
|
* with csd-codec.verifyMerkleProof. If `headerMerkleAt` was provided, the
|
|
78
|
-
* proof's root is cross-checked against the
|
|
79
|
-
*
|
|
80
|
-
*
|
|
78
|
+
* proof's root is cross-checked against the header merkle at that height →
|
|
79
|
+
* trustLevel "verified-inclusion" (only as trustworthy as that header source —
|
|
80
|
+
* see headerMerkleAt). Otherwise the proof is only internally consistent →
|
|
81
|
+
* "proof-consistent" (still useful, but the indexer is trusted). The Cairn
|
|
82
|
+
* facade wires headerMerkleAt ONLY when the node RPC is an independent origin
|
|
83
|
+
* from the indexer, so the default same-origin setup stays at "proof-consistent".
|
|
81
84
|
*/
|
|
82
85
|
async verifyInclusion(txid) {
|
|
83
86
|
let proof;
|
package/dist/index.cjs
CHANGED
|
@@ -437,9 +437,12 @@ var IndexerClient = class {
|
|
|
437
437
|
/**
|
|
438
438
|
* Verify a tx is included in a block by folding its merkle branch to the root
|
|
439
439
|
* with csd-codec.verifyMerkleProof. If `headerMerkleAt` was provided, the
|
|
440
|
-
* proof's root is cross-checked against the
|
|
441
|
-
*
|
|
442
|
-
*
|
|
440
|
+
* proof's root is cross-checked against the header merkle at that height →
|
|
441
|
+
* trustLevel "verified-inclusion" (only as trustworthy as that header source —
|
|
442
|
+
* see headerMerkleAt). Otherwise the proof is only internally consistent →
|
|
443
|
+
* "proof-consistent" (still useful, but the indexer is trusted). The Cairn
|
|
444
|
+
* facade wires headerMerkleAt ONLY when the node RPC is an independent origin
|
|
445
|
+
* from the indexer, so the default same-origin setup stays at "proof-consistent".
|
|
443
446
|
*/
|
|
444
447
|
async verifyInclusion(txid2) {
|
|
445
448
|
let proof;
|
|
@@ -794,6 +797,7 @@ async function connect(opts = {}) {
|
|
|
794
797
|
}
|
|
795
798
|
|
|
796
799
|
// src/index.ts
|
|
800
|
+
var DEFAULT_SPV_CHECKPOINT = { height: 31310, hash: "0x000000000000138bd3eee4b8d2fbacb8d5433ac3040ddbf1c45a5e3e0cc9e814" };
|
|
797
801
|
var DEFAULT_CAIRN = "https://cairn-substrate.com";
|
|
798
802
|
var Cairn = class {
|
|
799
803
|
config;
|
|
@@ -804,6 +808,10 @@ var Cairn = class {
|
|
|
804
808
|
board;
|
|
805
809
|
wallet;
|
|
806
810
|
cairnHttp;
|
|
811
|
+
// Lazily-seeded SPV light client + its checkpoint (audit M3 — PoW-verified header source).
|
|
812
|
+
_spvLight = null;
|
|
813
|
+
_spvSeeded = false;
|
|
814
|
+
_spvCp;
|
|
807
815
|
constructor(config = {}) {
|
|
808
816
|
this.config = config;
|
|
809
817
|
const fetch = config.fetch;
|
|
@@ -814,22 +822,41 @@ var Cairn = class {
|
|
|
814
822
|
this.cairnHttp = new Http({ baseUrl: cairn, fetch, timeoutMs });
|
|
815
823
|
const indexerHttp = new Http({ baseUrl: indexer, fetch, timeoutMs });
|
|
816
824
|
const swarmHttp = config.baseUrls?.swarm ? new Http({ baseUrl: config.baseUrls.swarm.replace(/\/+$/, ""), fetch, timeoutMs }) : void 0;
|
|
825
|
+
this._spvCp = config.spvCheckpoint ?? DEFAULT_SPV_CHECKPOINT;
|
|
817
826
|
this.chain = new Chain({ rpcUrl: rpc, fetch, timeoutMs });
|
|
818
827
|
this.wallet = config.wallet;
|
|
819
828
|
this.board = new BoardClient(this.cairnHttp, this.wallet);
|
|
820
829
|
this.index = new IndexerClient(indexerHttp, {
|
|
821
830
|
fetch,
|
|
822
831
|
WebSocketImpl: config.WebSocketImpl,
|
|
823
|
-
|
|
824
|
-
// proof root against the on-chain header merkle at that height.
|
|
825
|
-
headerMerkleAt: async (h) => {
|
|
826
|
-
const block = await this.chain.client.blockByHeight(h);
|
|
827
|
-
return String(block.header.merkle);
|
|
828
|
-
}
|
|
832
|
+
headerMerkleAt: (h) => this.verifiedHeaderMerkleAt(h)
|
|
829
833
|
});
|
|
830
834
|
this.content = new ContentClient({ swarm: swarmHttp, indexer: indexerHttp, cairn: this.cairnHttp });
|
|
831
835
|
this.registry = new RegistryClient({ baseUrl: indexer, fetch });
|
|
832
836
|
}
|
|
837
|
+
/**
|
|
838
|
+
* PoW-VERIFIED header merkle at a height (audit M3 — the no-PoW half). Lazily seeds a light
|
|
839
|
+
* client from the pinned SPV checkpoint and verifies the header chain FORWARD (PoW + LWMA +
|
|
840
|
+
* chainwork) to `height`, then returns that header's merkle root. THROWS for a height below the
|
|
841
|
+
* checkpoint (can't verify backward) or if verification fails — the IndexerClient then degrades
|
|
842
|
+
* to the honest "proof-consistent" instead of over-claiming "verified-inclusion". The verified
|
|
843
|
+
* header chain is cached across calls; the first call near tip syncs the post-checkpoint span.
|
|
844
|
+
*/
|
|
845
|
+
async verifiedHeaderMerkleAt(height) {
|
|
846
|
+
if (!Number.isInteger(height) || height < this._spvCp.height)
|
|
847
|
+
throw new Error(`height ${height} is below the SPV checkpoint ${this._spvCp.height} (cannot PoW-verify backward)`);
|
|
848
|
+
if (!this._spvLight) this._spvLight = new import_csd_light.LightClient({ client: this.chain.client, checkpoints: { [this._spvCp.height]: this._spvCp.hash } });
|
|
849
|
+
const lc = this._spvLight;
|
|
850
|
+
if (!this._spvSeeded) {
|
|
851
|
+
await lc.syncFromCheckpoint(this._spvCp.height, this._spvCp.hash);
|
|
852
|
+
this._spvSeeded = true;
|
|
853
|
+
}
|
|
854
|
+
const haveTo = lc.baseHeight + lc.chain.length - 1;
|
|
855
|
+
if (haveTo < height) await lc.sync(height);
|
|
856
|
+
const vh = lc.chain[height - lc.baseHeight];
|
|
857
|
+
if (!vh) throw new Error(`light client has no verified header at ${height}`);
|
|
858
|
+
return String(vh.header.merkle);
|
|
859
|
+
}
|
|
833
860
|
/**
|
|
834
861
|
* Detect + connect the Cairn Wallet (browser only). Wires the wallet into
|
|
835
862
|
* `cairn.board` for signed writes. Resolves to the connected address.
|
package/dist/index.d.cts
CHANGED
|
@@ -80,6 +80,18 @@ interface CairnConfig {
|
|
|
80
80
|
wallet?: WalletConnection;
|
|
81
81
|
/** WebSocket implementation for indexer subscriptions (defaults to global). */
|
|
82
82
|
WebSocketImpl?: typeof WebSocket;
|
|
83
|
+
/**
|
|
84
|
+
* SPV trust anchor for genuine "verified-inclusion" (audit M3). A merkle inclusion proof is only
|
|
85
|
+
* "verified-inclusion" when its block header is PoW-verified — otherwise a lying node can serve a
|
|
86
|
+
* fake header whose merkle matches a fake proof. The light client verifies the header chain
|
|
87
|
+
* (PoW + LWMA + chainwork) forward from this pinned checkpoint; inclusion at a height ≥ the
|
|
88
|
+
* checkpoint is then trust-minimized. Defaults to the ecosystem checkpoint (same hash swapguard
|
|
89
|
+
* ships). Pin your own for full control. Txs BELOW the checkpoint degrade to "proof-consistent".
|
|
90
|
+
*/
|
|
91
|
+
spvCheckpoint?: {
|
|
92
|
+
height: number;
|
|
93
|
+
hash: string;
|
|
94
|
+
};
|
|
83
95
|
}
|
|
84
96
|
declare class Cairn {
|
|
85
97
|
readonly config: CairnConfig;
|
|
@@ -90,7 +102,19 @@ declare class Cairn {
|
|
|
90
102
|
board: BoardClient;
|
|
91
103
|
wallet?: WalletConnection;
|
|
92
104
|
private readonly cairnHttp;
|
|
105
|
+
private _spvLight;
|
|
106
|
+
private _spvSeeded;
|
|
107
|
+
private readonly _spvCp;
|
|
93
108
|
constructor(config?: CairnConfig);
|
|
109
|
+
/**
|
|
110
|
+
* PoW-VERIFIED header merkle at a height (audit M3 — the no-PoW half). Lazily seeds a light
|
|
111
|
+
* client from the pinned SPV checkpoint and verifies the header chain FORWARD (PoW + LWMA +
|
|
112
|
+
* chainwork) to `height`, then returns that header's merkle root. THROWS for a height below the
|
|
113
|
+
* checkpoint (can't verify backward) or if verification fails — the IndexerClient then degrades
|
|
114
|
+
* to the honest "proof-consistent" instead of over-claiming "verified-inclusion". The verified
|
|
115
|
+
* header chain is cached across calls; the first call near tip syncs the post-checkpoint span.
|
|
116
|
+
*/
|
|
117
|
+
private verifiedHeaderMerkleAt;
|
|
94
118
|
/**
|
|
95
119
|
* Detect + connect the Cairn Wallet (browser only). Wires the wallet into
|
|
96
120
|
* `cairn.board` for signed writes. Resolves to the connected address.
|
package/dist/index.d.ts
CHANGED
|
@@ -80,6 +80,18 @@ interface CairnConfig {
|
|
|
80
80
|
wallet?: WalletConnection;
|
|
81
81
|
/** WebSocket implementation for indexer subscriptions (defaults to global). */
|
|
82
82
|
WebSocketImpl?: typeof WebSocket;
|
|
83
|
+
/**
|
|
84
|
+
* SPV trust anchor for genuine "verified-inclusion" (audit M3). A merkle inclusion proof is only
|
|
85
|
+
* "verified-inclusion" when its block header is PoW-verified — otherwise a lying node can serve a
|
|
86
|
+
* fake header whose merkle matches a fake proof. The light client verifies the header chain
|
|
87
|
+
* (PoW + LWMA + chainwork) forward from this pinned checkpoint; inclusion at a height ≥ the
|
|
88
|
+
* checkpoint is then trust-minimized. Defaults to the ecosystem checkpoint (same hash swapguard
|
|
89
|
+
* ships). Pin your own for full control. Txs BELOW the checkpoint degrade to "proof-consistent".
|
|
90
|
+
*/
|
|
91
|
+
spvCheckpoint?: {
|
|
92
|
+
height: number;
|
|
93
|
+
hash: string;
|
|
94
|
+
};
|
|
83
95
|
}
|
|
84
96
|
declare class Cairn {
|
|
85
97
|
readonly config: CairnConfig;
|
|
@@ -90,7 +102,19 @@ declare class Cairn {
|
|
|
90
102
|
board: BoardClient;
|
|
91
103
|
wallet?: WalletConnection;
|
|
92
104
|
private readonly cairnHttp;
|
|
105
|
+
private _spvLight;
|
|
106
|
+
private _spvSeeded;
|
|
107
|
+
private readonly _spvCp;
|
|
93
108
|
constructor(config?: CairnConfig);
|
|
109
|
+
/**
|
|
110
|
+
* PoW-VERIFIED header merkle at a height (audit M3 — the no-PoW half). Lazily seeds a light
|
|
111
|
+
* client from the pinned SPV checkpoint and verifies the header chain FORWARD (PoW + LWMA +
|
|
112
|
+
* chainwork) to `height`, then returns that header's merkle root. THROWS for a height below the
|
|
113
|
+
* checkpoint (can't verify backward) or if verification fails — the IndexerClient then degrades
|
|
114
|
+
* to the honest "proof-consistent" instead of over-claiming "verified-inclusion". The verified
|
|
115
|
+
* header chain is cached across calls; the first call near tip syncs the post-checkpoint span.
|
|
116
|
+
*/
|
|
117
|
+
private verifiedHeaderMerkleAt;
|
|
94
118
|
/**
|
|
95
119
|
* Detect + connect the Cairn Wallet (browser only). Wires the wallet into
|
|
96
120
|
* `cairn.board` for signed writes. Resolves to the connected address.
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,8 @@ import {
|
|
|
2
2
|
BoardClient
|
|
3
3
|
} from "./chunk-OGP2W7DL.js";
|
|
4
4
|
import {
|
|
5
|
-
Chain
|
|
5
|
+
Chain,
|
|
6
|
+
LightClient
|
|
6
7
|
} from "./chunk-6VWLWF7R.js";
|
|
7
8
|
import {
|
|
8
9
|
WalletConnection,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
} from "./chunk-PHMU3CG5.js";
|
|
26
27
|
import {
|
|
27
28
|
IndexerClient
|
|
28
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-FNBE2FPE.js";
|
|
29
30
|
import {
|
|
30
31
|
RegistryClient
|
|
31
32
|
} from "./chunk-G7FABIF4.js";
|
|
@@ -103,6 +104,7 @@ async function safeText(res) {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
// src/index.ts
|
|
107
|
+
var DEFAULT_SPV_CHECKPOINT = { height: 31310, hash: "0x000000000000138bd3eee4b8d2fbacb8d5433ac3040ddbf1c45a5e3e0cc9e814" };
|
|
106
108
|
var DEFAULT_CAIRN = "https://cairn-substrate.com";
|
|
107
109
|
var Cairn = class {
|
|
108
110
|
config;
|
|
@@ -113,6 +115,10 @@ var Cairn = class {
|
|
|
113
115
|
board;
|
|
114
116
|
wallet;
|
|
115
117
|
cairnHttp;
|
|
118
|
+
// Lazily-seeded SPV light client + its checkpoint (audit M3 — PoW-verified header source).
|
|
119
|
+
_spvLight = null;
|
|
120
|
+
_spvSeeded = false;
|
|
121
|
+
_spvCp;
|
|
116
122
|
constructor(config = {}) {
|
|
117
123
|
this.config = config;
|
|
118
124
|
const fetch = config.fetch;
|
|
@@ -123,22 +129,41 @@ var Cairn = class {
|
|
|
123
129
|
this.cairnHttp = new Http({ baseUrl: cairn, fetch, timeoutMs });
|
|
124
130
|
const indexerHttp = new Http({ baseUrl: indexer, fetch, timeoutMs });
|
|
125
131
|
const swarmHttp = config.baseUrls?.swarm ? new Http({ baseUrl: config.baseUrls.swarm.replace(/\/+$/, ""), fetch, timeoutMs }) : void 0;
|
|
132
|
+
this._spvCp = config.spvCheckpoint ?? DEFAULT_SPV_CHECKPOINT;
|
|
126
133
|
this.chain = new Chain({ rpcUrl: rpc, fetch, timeoutMs });
|
|
127
134
|
this.wallet = config.wallet;
|
|
128
135
|
this.board = new BoardClient(this.cairnHttp, this.wallet);
|
|
129
136
|
this.index = new IndexerClient(indexerHttp, {
|
|
130
137
|
fetch,
|
|
131
138
|
WebSocketImpl: config.WebSocketImpl,
|
|
132
|
-
|
|
133
|
-
// proof root against the on-chain header merkle at that height.
|
|
134
|
-
headerMerkleAt: async (h) => {
|
|
135
|
-
const block = await this.chain.client.blockByHeight(h);
|
|
136
|
-
return String(block.header.merkle);
|
|
137
|
-
}
|
|
139
|
+
headerMerkleAt: (h) => this.verifiedHeaderMerkleAt(h)
|
|
138
140
|
});
|
|
139
141
|
this.content = new ContentClient({ swarm: swarmHttp, indexer: indexerHttp, cairn: this.cairnHttp });
|
|
140
142
|
this.registry = new RegistryClient({ baseUrl: indexer, fetch });
|
|
141
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* PoW-VERIFIED header merkle at a height (audit M3 — the no-PoW half). Lazily seeds a light
|
|
146
|
+
* client from the pinned SPV checkpoint and verifies the header chain FORWARD (PoW + LWMA +
|
|
147
|
+
* chainwork) to `height`, then returns that header's merkle root. THROWS for a height below the
|
|
148
|
+
* checkpoint (can't verify backward) or if verification fails — the IndexerClient then degrades
|
|
149
|
+
* to the honest "proof-consistent" instead of over-claiming "verified-inclusion". The verified
|
|
150
|
+
* header chain is cached across calls; the first call near tip syncs the post-checkpoint span.
|
|
151
|
+
*/
|
|
152
|
+
async verifiedHeaderMerkleAt(height) {
|
|
153
|
+
if (!Number.isInteger(height) || height < this._spvCp.height)
|
|
154
|
+
throw new Error(`height ${height} is below the SPV checkpoint ${this._spvCp.height} (cannot PoW-verify backward)`);
|
|
155
|
+
if (!this._spvLight) this._spvLight = new LightClient({ client: this.chain.client, checkpoints: { [this._spvCp.height]: this._spvCp.hash } });
|
|
156
|
+
const lc = this._spvLight;
|
|
157
|
+
if (!this._spvSeeded) {
|
|
158
|
+
await lc.syncFromCheckpoint(this._spvCp.height, this._spvCp.hash);
|
|
159
|
+
this._spvSeeded = true;
|
|
160
|
+
}
|
|
161
|
+
const haveTo = lc.baseHeight + lc.chain.length - 1;
|
|
162
|
+
if (haveTo < height) await lc.sync(height);
|
|
163
|
+
const vh = lc.chain[height - lc.baseHeight];
|
|
164
|
+
if (!vh) throw new Error(`light client has no verified header at ${height}`);
|
|
165
|
+
return String(vh.header.merkle);
|
|
166
|
+
}
|
|
142
167
|
/**
|
|
143
168
|
* Detect + connect the Cairn Wallet (browser only). Wires the wallet into
|
|
144
169
|
* `cairn.board` for signed writes. Resolves to the connected address.
|
package/dist/indexer.cjs
CHANGED
|
@@ -99,9 +99,12 @@ var IndexerClient = class {
|
|
|
99
99
|
/**
|
|
100
100
|
* Verify a tx is included in a block by folding its merkle branch to the root
|
|
101
101
|
* with csd-codec.verifyMerkleProof. If `headerMerkleAt` was provided, the
|
|
102
|
-
* proof's root is cross-checked against the
|
|
103
|
-
*
|
|
104
|
-
*
|
|
102
|
+
* proof's root is cross-checked against the header merkle at that height →
|
|
103
|
+
* trustLevel "verified-inclusion" (only as trustworthy as that header source —
|
|
104
|
+
* see headerMerkleAt). Otherwise the proof is only internally consistent →
|
|
105
|
+
* "proof-consistent" (still useful, but the indexer is trusted). The Cairn
|
|
106
|
+
* facade wires headerMerkleAt ONLY when the node RPC is an independent origin
|
|
107
|
+
* from the indexer, so the default same-origin setup stays at "proof-consistent".
|
|
105
108
|
*/
|
|
106
109
|
async verifyInclusion(txid) {
|
|
107
110
|
let proof;
|
package/dist/indexer.d.cts
CHANGED
|
@@ -25,9 +25,13 @@ interface IndexerClientOptions {
|
|
|
25
25
|
/** A WebSocket implementation (defaults to globalThis.WebSocket). */
|
|
26
26
|
WebSocketImpl?: typeof WebSocket;
|
|
27
27
|
/**
|
|
28
|
-
* Optional: given a block height, return its on-chain header merkle root.
|
|
29
|
-
* this
|
|
30
|
-
* "
|
|
28
|
+
* Optional: given a block height, return its on-chain header merkle root. verifyInclusion()
|
|
29
|
+
* cross-checks the indexer's proof root against this and, on a match, labels the result
|
|
30
|
+
* "verified-inclusion". IMPORTANT: that label is only as strong as THIS source — it is fully
|
|
31
|
+
* trust-minimized ONLY if the header is independently verified (e.g. a PoW-checking light client,
|
|
32
|
+
* or at least a node on a DIFFERENT trust domain than the indexer). A header fetched by raw RPC
|
|
33
|
+
* from the SAME server that serves the proof proves nothing (one compromised server controls
|
|
34
|
+
* both); for that case prefer no headerMerkleAt → an honest "proof-consistent".
|
|
31
35
|
*/
|
|
32
36
|
headerMerkleAt?: (height: number) => Promise<string>;
|
|
33
37
|
}
|
|
@@ -64,9 +68,12 @@ declare class IndexerClient {
|
|
|
64
68
|
/**
|
|
65
69
|
* Verify a tx is included in a block by folding its merkle branch to the root
|
|
66
70
|
* with csd-codec.verifyMerkleProof. If `headerMerkleAt` was provided, the
|
|
67
|
-
* proof's root is cross-checked against the
|
|
68
|
-
*
|
|
69
|
-
*
|
|
71
|
+
* proof's root is cross-checked against the header merkle at that height →
|
|
72
|
+
* trustLevel "verified-inclusion" (only as trustworthy as that header source —
|
|
73
|
+
* see headerMerkleAt). Otherwise the proof is only internally consistent →
|
|
74
|
+
* "proof-consistent" (still useful, but the indexer is trusted). The Cairn
|
|
75
|
+
* facade wires headerMerkleAt ONLY when the node RPC is an independent origin
|
|
76
|
+
* from the indexer, so the default same-origin setup stays at "proof-consistent".
|
|
70
77
|
*/
|
|
71
78
|
verifyInclusion(txid: string): Promise<InclusionResult>;
|
|
72
79
|
/** Stream all events (blocks, proposals, attestations, reorgs) via SSE. */
|
package/dist/indexer.d.ts
CHANGED
|
@@ -25,9 +25,13 @@ interface IndexerClientOptions {
|
|
|
25
25
|
/** A WebSocket implementation (defaults to globalThis.WebSocket). */
|
|
26
26
|
WebSocketImpl?: typeof WebSocket;
|
|
27
27
|
/**
|
|
28
|
-
* Optional: given a block height, return its on-chain header merkle root.
|
|
29
|
-
* this
|
|
30
|
-
* "
|
|
28
|
+
* Optional: given a block height, return its on-chain header merkle root. verifyInclusion()
|
|
29
|
+
* cross-checks the indexer's proof root against this and, on a match, labels the result
|
|
30
|
+
* "verified-inclusion". IMPORTANT: that label is only as strong as THIS source — it is fully
|
|
31
|
+
* trust-minimized ONLY if the header is independently verified (e.g. a PoW-checking light client,
|
|
32
|
+
* or at least a node on a DIFFERENT trust domain than the indexer). A header fetched by raw RPC
|
|
33
|
+
* from the SAME server that serves the proof proves nothing (one compromised server controls
|
|
34
|
+
* both); for that case prefer no headerMerkleAt → an honest "proof-consistent".
|
|
31
35
|
*/
|
|
32
36
|
headerMerkleAt?: (height: number) => Promise<string>;
|
|
33
37
|
}
|
|
@@ -64,9 +68,12 @@ declare class IndexerClient {
|
|
|
64
68
|
/**
|
|
65
69
|
* Verify a tx is included in a block by folding its merkle branch to the root
|
|
66
70
|
* with csd-codec.verifyMerkleProof. If `headerMerkleAt` was provided, the
|
|
67
|
-
* proof's root is cross-checked against the
|
|
68
|
-
*
|
|
69
|
-
*
|
|
71
|
+
* proof's root is cross-checked against the header merkle at that height →
|
|
72
|
+
* trustLevel "verified-inclusion" (only as trustworthy as that header source —
|
|
73
|
+
* see headerMerkleAt). Otherwise the proof is only internally consistent →
|
|
74
|
+
* "proof-consistent" (still useful, but the indexer is trusted). The Cairn
|
|
75
|
+
* facade wires headerMerkleAt ONLY when the node RPC is an independent origin
|
|
76
|
+
* from the indexer, so the default same-origin setup stays at "proof-consistent".
|
|
70
77
|
*/
|
|
71
78
|
verifyInclusion(txid: string): Promise<InclusionResult>;
|
|
72
79
|
/** Stream all events (blocks, proposals, attestations, reorgs) via SSE. */
|
package/dist/indexer.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inversealtruism/cairn-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The Compute Substrate dApp kit — one cohesive toolkit to connect the Cairn Wallet, read the chain/board, publish self-certifying content, and resolve registries. Composes the published csd-* primitives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -53,18 +53,24 @@
|
|
|
53
53
|
"test": "tsx test/sdk.test.ts && tsx test/connect.test.ts",
|
|
54
54
|
"test:live": "node examples/read-only.mjs",
|
|
55
55
|
"build:example": "node scripts/build-example.mjs",
|
|
56
|
-
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
|
|
56
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
57
|
+
"test:e2e:read": "node test/e2e/e2e-read.mjs",
|
|
58
|
+
"test:e2e:wallet": "xvfb-run -a node test/e2e/e2e-wallet-connect.mjs",
|
|
59
|
+
"test:e2e:write": "node test/e2e/e2e-write.mjs",
|
|
60
|
+
"test:e2e": "node test/e2e/e2e-read.mjs && xvfb-run -a node test/e2e/e2e-wallet-connect.mjs",
|
|
61
|
+
"test:all": "pnpm test && pnpm test:e2e"
|
|
57
62
|
},
|
|
58
63
|
"dependencies": {
|
|
59
|
-
"@inversealtruism/csd-
|
|
60
|
-
"@inversealtruism/csd-
|
|
61
|
-
"@inversealtruism/csd-
|
|
62
|
-
"@inversealtruism/csd-
|
|
63
|
-
"@inversealtruism/csd-
|
|
64
|
-
"@inversealtruism/csd-
|
|
64
|
+
"@inversealtruism/csd-client": "^0.1.5",
|
|
65
|
+
"@inversealtruism/csd-codec": "^0.1.5",
|
|
66
|
+
"@inversealtruism/csd-crypto": "^0.1.5",
|
|
67
|
+
"@inversealtruism/csd-light": "^0.1.5",
|
|
68
|
+
"@inversealtruism/csd-registry": "^0.1.5",
|
|
69
|
+
"@inversealtruism/csd-tx": "^0.1.5"
|
|
65
70
|
},
|
|
66
71
|
"devDependencies": {
|
|
67
72
|
"esbuild": "^0.27.0",
|
|
73
|
+
"playwright-core": "^1.49.0",
|
|
68
74
|
"tsup": "^8.5.1",
|
|
69
75
|
"tsx": "^4.19.2",
|
|
70
76
|
"typescript": "^5.6.0"
|