@rolly-dev/witness-core 0.1.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/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # @rolly-dev/witness-core
2
+
3
+ Authoritative native witness-state engine for the Rolly ZK-Rollup, shipped to
4
+ Node.js as a [Node-API](https://nodejs.org/api/n-api.html) addon via
5
+ [napi-rs](https://napi.rs).
6
+
7
+ It is the single source of truth for the rollup witness state (Merkle tree,
8
+ server-seed chain, TL) and processes each transaction **exactly once**. Node
9
+ keeps only IO / RPC / orchestration. See the migration plan for the full
10
+ rationale (removing the TS/Rust double-recompute and the wasm Poseidon
11
+ bottleneck).
12
+
13
+ > Status: **scaffolding stage**. The Node-API surface and a placeholder
14
+ > `MerkleEngine` handle exist; the real state, `process_tx`, and `flush_block`
15
+ > land in later stages.
16
+
17
+ ## Design
18
+
19
+ One crate, `crate-type = ["cdylib", "rlib"]` with napi bindings inline (no
20
+ separate binding crate), mirroring how `wasm-signer` ships `cdylib` + `rlib`:
21
+
22
+ - `cdylib` -> the `.node` addon consumed by the backend.
23
+ - `rlib` -> native unit tests + (later) circuit differential tests live in
24
+ this same crate, so engine logic is testable without going through Node.
25
+
26
+ ## Build
27
+
28
+ Host platform (release):
29
+
30
+ ```bash
31
+ npm install # installs @napi-rs/cli
32
+ npm run build # -> witness-core.<platform>.node, index.js, index.d.ts
33
+ ```
34
+
35
+ or `bash build.sh`.
36
+
37
+ ### Prebuild for Linux x64 (production target)
38
+
39
+ Production likely runs Linux x64, so the package is configured to emit a
40
+ `x86_64-unknown-linux-gnu` artifact:
41
+
42
+ ```bash
43
+ npm run build:linux-x64
44
+ ```
45
+
46
+ Cross-compiling from macOS requires a Linux linker and the Rust target:
47
+
48
+ ```bash
49
+ rustup target add x86_64-unknown-linux-gnu
50
+ ```
51
+
52
+ Then pick one of:
53
+
54
+ - **GNU cross-linker (used here):** install the GNU cross toolchain so
55
+ `x86_64-linux-gnu-gcc` is on `PATH`. `.cargo/config.toml` already points the
56
+ target's linker at it, so `npm run build:linux-x64` produces a stripped
57
+ `witness-core.linux-x64-gnu.node` ELF directly.
58
+ - **Linux x64 CI runner:** run `npm run build:linux-x64` there natively
59
+ (`.cargo/config.toml`'s cross-linker entry is simply unused). Recommended for
60
+ release.
61
+ - **zig:** `cargo install cargo-zigbuild` + a `zig` on `PATH`, then add
62
+ `--cross-compile` to the napi build.
63
+
64
+ The release flow uses `napi prepublish` / `napi artifacts` to collect the
65
+ per-platform `.node` files before publishing.
66
+
67
+ ## Test
68
+
69
+ ```bash
70
+ npm test # builds nothing; runs ./test.js against the already-built addon
71
+ ```
72
+
73
+ ## Consuming from the backend
74
+
75
+ Same pattern as `@rolly-dev/wasm-signer`:
76
+
77
+ ```js
78
+ const { MerkleEngine, engineVersion } = require('@rolly-dev/witness-core');
79
+ const engine = new MerkleEngine();
80
+ ```
package/index.d.ts ADDED
@@ -0,0 +1,318 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ /** Caller-tunable flush inputs. */
7
+ export interface FlushOptions {
8
+ /**
9
+ * Block timestamp (unix seconds, decimal string). Defaults to `now - 10`,
10
+ * mirroring the engine's safety margin under the contract's upper bound.
11
+ */
12
+ blockTimestamp?: string
13
+ /**
14
+ * When true, the per-batch delta tree is reset AFTER this block (the batch
15
+ * boundary). Defaults to false (mid-batch).
16
+ */
17
+ closeBatch?: boolean
18
+ }
19
+ /**
20
+ * Block-level result of a flush. Monetary / root values are little-endian
21
+ * `Buffer`s or decimal `BigInt`s. `witness_json` is the prover input; the
22
+ * `*_json` strings are the block-pubdata records Node stores on the pipeline
23
+ * block doc; `ops_data_hex` is the raw withdrawal calldata. After awaiting
24
+ * this and persisting, Node MUST call `commit_flush` (or `rollback_flush` on
25
+ * persist failure) to finalize / undo the engine's running flush state.
26
+ */
27
+ export interface FlushResult {
28
+ blockId: bigint
29
+ txCount: number
30
+ depositCount: number
31
+ hasDeposit: boolean
32
+ hasWithdrawal: boolean
33
+ oldRoot: Buffer
34
+ newRoot: Buffer
35
+ tl: bigint
36
+ multisetOld: Buffer
37
+ multisetNew: Buffer
38
+ oldDepositChainHash: Buffer
39
+ newDepositChainHash: Buffer
40
+ oldOpsHash: Buffer
41
+ newOpsHash: Buffer
42
+ opsDataHex: string
43
+ blockTimestamp: bigint
44
+ witnessJson: string
45
+ deltasJson: string
46
+ addressRecordsJson: string
47
+ pkHashRecordsJson: string
48
+ }
49
+ /**
50
+ * Result of [`MerkleEngine::verify_root`], surfaced to Node for the post-load
51
+ * integrity check.
52
+ */
53
+ export interface VerifyRootResult {
54
+ valid: boolean
55
+ computed: Buffer
56
+ stored: Buffer
57
+ }
58
+ /** One dirty user leaf (32-byte little-endian hash). */
59
+ export interface DirtyLeaf {
60
+ userId: number
61
+ leaf: Buffer
62
+ }
63
+ /** One dirty internal node (`level:index` key + 32-byte hash). */
64
+ export interface DirtyNode {
65
+ level: number
66
+ index: number
67
+ hash: Buffer
68
+ }
69
+ /** One dirty raw balance leaf (56-byte little-endian, 7 limbs). */
70
+ export interface DirtyRaw {
71
+ userId: number
72
+ raw: Buffer
73
+ }
74
+ /** One dirty 32-byte hash field (pk / address). */
75
+ export interface DirtyHash {
76
+ userId: number
77
+ hash: Buffer
78
+ }
79
+ /** One dirty locked-balance entry (frozen buckets, NOT in the leaf hash). */
80
+ export interface DirtyLocked {
81
+ userId: number
82
+ wd: bigint
83
+ game: bigint
84
+ }
85
+ /** One dirty full server seed (64-byte little-endian, 8 limbs). */
86
+ export interface DirtySeed {
87
+ userId: number
88
+ seed: Buffer
89
+ }
90
+ /**
91
+ * Snapshot of everything changed since the last flush, for Node to upsert to
92
+ * Mongo (merkle leaves / nodes / raws / pk / address / locked + server seeds).
93
+ * Values are little-endian buffers; locked buckets are decimal `BigInt`s.
94
+ */
95
+ export interface DirtyData {
96
+ leaves: Array<DirtyLeaf>
97
+ nodes: Array<DirtyNode>
98
+ balanceRaws: Array<DirtyRaw>
99
+ pkHashes: Array<DirtyHash>
100
+ addressHashes: Array<DirtyHash>
101
+ lockedBalances: Array<DirtyLocked>
102
+ serverSeeds: Array<DirtySeed>
103
+ }
104
+ /**
105
+ * Per-tx request from Node. Opaque field groups arrive as little-endian
106
+ * `Buffer`s (`session_key` / `new_pk_hash` / `user_secret_random` = 32 bytes =
107
+ * 4 limbs; `user_address` = 20 bytes); monetary values arrive as decimal
108
+ * strings to dodge JS `Number` precision and `BigInt`-in-object quirks. Node
109
+ * keeps all hex/string parsing (incl. deriving `user_secret_random` from the
110
+ * raw user-seed source via wasm-signer); the engine takes only parsed bytes.
111
+ */
112
+ export interface ProcessTxRequest {
113
+ txType: number
114
+ userId: number
115
+ amount: string
116
+ winAmount?: string
117
+ userAddress?: Buffer
118
+ sessionKey?: Buffer
119
+ sessionExpiry?: string
120
+ newPkHash?: Buffer
121
+ gameId?: number
122
+ gameMode?: number
123
+ predictionLo?: number
124
+ predictionHi?: number
125
+ userSecretRandom?: Buffer
126
+ useGameLock?: boolean
127
+ maxBlockTimestamp?: string
128
+ }
129
+ /**
130
+ * Authoritative post-tx result returned to Node. Monetary values are decimal
131
+ * strings; field-element groups are little-endian hex (`random` / `user_seed`
132
+ * = 32 hex-encoded bytes, `next_seed_hash` = 24, `server_seed` = 64). The
133
+ * rejected case is surfaced as a thrown error carrying the typed reason.
134
+ */
135
+ export interface ProcessTxResult {
136
+ newBalance: string
137
+ available: string
138
+ newCredit: string
139
+ winAmount: string
140
+ random?: string
141
+ nextSeedHash?: string
142
+ /**
143
+ * Pre-tx leaf seed hash (24 little-endian hex). Persisted by Node as the
144
+ * block-explorer `server_seed_hash`. `None` only when all-zero.
145
+ */
146
+ oldSeedHash?: string
147
+ serverSeed?: string
148
+ userSeedHex?: string
149
+ gameResultJson?: string
150
+ }
151
+ /**
152
+ * Returns the native engine version. Lets the Node layer verify the loaded
153
+ * addon matches the expected npm package version at startup.
154
+ */
155
+ export declare function engineVersion(): string
156
+ /**
157
+ * Authoritative witness-state engine handle held on the Node side. Owns the
158
+ * Merkle tree, the TL accumulator and the per-user server-seed chain in native
159
+ * memory (no V8 GC, no wasm 4 GB ceiling), plus the in-progress block's slot
160
+ * buffer. Node drives it via this Node-API surface.
161
+ */
162
+ export declare class MerkleEngine {
163
+ /**
164
+ * Close the in-progress block: drain the buffered slots, pad with noops,
165
+ * fold the multiset / deposit / ops chains and per-batch delta tree, and
166
+ * (off the event loop) serialize + gzip the block witness / write-set.
167
+ * Resolves to a [`FlushResult`].
168
+ */
169
+ flushBlock(options?: FlushOptions | undefined | null): Promise<unknown>
170
+ /**
171
+ * Finalize a pending flush after Node has durably persisted the block
172
+ * (Mongo merkle + meta + CH + pipeline). Accepts the delta-tree mutations
173
+ * and, at a batch boundary (`close_batch`), resets the per-batch delta
174
+ * tree. No-op when no flush is pending.
175
+ */
176
+ commitFlush(): void
177
+ /**
178
+ * Undo a pending flush when the durable persist failed: restore the running
179
+ * accumulators / block-open tracking, re-buffer the block's real slots at
180
+ * the front of the queue (so they re-flush as the same block), and roll the
181
+ * per-batch delta tree back. No-op when no flush is pending.
182
+ */
183
+ rollbackFlush(): void
184
+ /** True iff a flush is awaiting commit / rollback. */
185
+ hasPendingFlush(): boolean
186
+ /**
187
+ * Per-batch pubdata delta entries (one per touched user) as the JSON the
188
+ * `pipeline_batches.delta_entries` field stores. Call at batch close
189
+ * BEFORE `reset_delta_tree`.
190
+ */
191
+ deltaExportEntriesJson(): string
192
+ /** Estimated raw delta-entry calldata size (bytes) for the open batch. */
193
+ deltaEstimatePubdataBytes(): bigint
194
+ /** Number of touched users in the open batch (= number of pubdata entries). */
195
+ deltaEntryCount(): number
196
+ getBlockId(): bigint
197
+ /** Adopt the persisted block id (startup / recovery). */
198
+ setBlockId(blockId: bigint): void
199
+ /** Running multiset accumulator as a 32-byte little-endian buffer. */
200
+ getMultiset(): Buffer
201
+ setMultiset(multiset: Buffer): void
202
+ getDepositChain(): Buffer
203
+ setDepositChain(chain: Buffer): void
204
+ getOpsHash(): Buffer
205
+ setOpsHash(opsHash: Buffer): void
206
+ /** Current per-batch delta-tree root (32-byte little-endian buffer). */
207
+ getDeltaRoot(): Buffer
208
+ /** Reset the per-batch delta tree to empty (the batch boundary). */
209
+ resetDeltaTree(): void
210
+ /**
211
+ * Replay one already-persisted block's slots into the per-batch delta tree
212
+ * (startup recovery of an open batch). Parses the block `witness_json` and
213
+ * advances the delta tree by each non-noop slot, so the next block's
214
+ * `old_delta_root` continues the batch exactly where the pre-restart run
215
+ * left off. Must be called in block order, after `reset_delta_tree`.
216
+ */
217
+ replayDeltaBlock(witnessJson: string): void
218
+ /**
219
+ * Creates an empty engine (all-empty root, zero TL). Startup then streams
220
+ * in the persisted tree via the `load*` methods + `set_root`.
221
+ */
222
+ constructor()
223
+ loadLeaf(userId: number, leaf: Buffer): void
224
+ loadNode(level: number, nodeIndex: number, hash: Buffer): void
225
+ loadBalanceLeafRaw(userId: number, raw: Buffer): void
226
+ loadPkHash(userId: number, pkHash: Buffer): void
227
+ loadAddressHash(userId: number, addressHash: Buffer): void
228
+ loadLockedBalance(userId: number, wd: bigint, game: bigint): void
229
+ /**
230
+ * Load a persisted full server seed for `user_id` (startup / recovery). Not
231
+ * marked dirty — the loaded value already matches the store.
232
+ */
233
+ loadServerSeed(userId: number, seed: Buffer): void
234
+ /**
235
+ * Adopt the persisted block-meta root after the full tree is loaded. The
236
+ * loaded leaves/nodes must reproduce it — verify with [`Self::verify_root`].
237
+ */
238
+ setRoot(root: Buffer): void
239
+ /** Adopt the persisted total liability from block meta. */
240
+ setTl(tl: bigint): void
241
+ /**
242
+ * Rebuild every internal node from the leaves and refresh the root. Used in
243
+ * recovery when persisted nodes are missing or suspect.
244
+ */
245
+ rebuildNodesFromLeaves(): void
246
+ /**
247
+ * Recompute the root bottom-up and compare against the cached one — the
248
+ * post-load integrity check.
249
+ */
250
+ verifyRoot(): VerifyRootResult
251
+ getRoot(): Buffer
252
+ getTl(): bigint
253
+ getBalance(userId: number): bigint
254
+ getCredit(userId: number): bigint
255
+ /** Spendable balance minus all frozen buckets (withdrawal + game locks). */
256
+ getAvailable(userId: number): bigint
257
+ getSeedHash(userId: number): Buffer
258
+ getPkHash(userId: number): Buffer
259
+ getAddressHash(userId: number): Buffer
260
+ getLockedTotal(userId: number): bigint
261
+ getLocked(userId: number, purpose: number): bigint
262
+ hasLeaf(userId: number): boolean
263
+ leafCount(): number
264
+ nodeCount(): number
265
+ /** Freeze `amount` more in a bucket. Errors on u64 overflow. */
266
+ lockBalance(userId: number, purpose: number, amount: bigint): void
267
+ /** Release `amount` from a bucket. Errors if it would go negative. */
268
+ unlockBalance(userId: number, purpose: number, amount: bigint): void
269
+ /** Set a bucket's absolute frozen amount. */
270
+ setLockedBalance(userId: number, purpose: number, locked: bigint): void
271
+ addTl(amount: bigint): void
272
+ subTl(amount: bigint): void
273
+ dirtyLeafCount(): number
274
+ dirtyNodeCount(): number
275
+ dirtyServerSeedCount(): number
276
+ /** Number of slot witnesses buffered for the in-progress block. */
277
+ bufferedSlotCount(): number
278
+ /**
279
+ * True iff a full server seed is loaded for `user_id` (needed before any
280
+ * `in_house_bet` on that user).
281
+ */
282
+ hasServerSeed(userId: number): boolean
283
+ clearDirty(): void
284
+ /**
285
+ * Snapshot the dirty merkle state + dirty server seeds for Node to persist.
286
+ * Does NOT clear the dirty markers — Node calls `clear_dirty` after a
287
+ * successful flush and `restore_dirty` on persist failure.
288
+ */
289
+ collectDirty(): DirtyData
290
+ /**
291
+ * Re-mark the keys in `dirty` as dirty after a failed flush (values stay
292
+ * the live post-block ones), so the next flush re-persists them. Buffer
293
+ * payloads are ignored — only the ids matter.
294
+ */
295
+ restoreDirty(dirty: DirtyData): void
296
+ dirtyLockedBalanceCount(): number
297
+ /**
298
+ * Snapshot the dirty locked buckets (fully-unlocked users emit zeros).
299
+ * Does NOT clear — call `clear_dirty_locked_balances` after persisting.
300
+ */
301
+ collectDirtyLockedBalances(): Array<DirtyLocked>
302
+ clearDirtyLockedBalances(): void
303
+ restoreDirtyLockedBalances(userIds: Array<number>): void
304
+ /**
305
+ * One-line status string (root, TL, leaf/node counts, dirty counts) for
306
+ * startup logging and health checks.
307
+ */
308
+ getStatus(): string
309
+ /**
310
+ * Process one transaction against live state: snapshot the leaf, generate
311
+ * any fairness fields, run the differentially-tested validator (the single
312
+ * compute + validation pass), apply the returned effects to the tree / TL /
313
+ * server-seed chain, and buffer the slot. Returns the authoritative post-tx
314
+ * result, or throws with the typed rejection reason (the slot never
315
+ * mutates state on rejection).
316
+ */
317
+ processTx(req: ProcessTxRequest): ProcessTxResult
318
+ }
package/index.js ADDED
@@ -0,0 +1,316 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
7
+ const { existsSync, readFileSync } = require('fs')
8
+ const { join } = require('path')
9
+
10
+ const { platform, arch } = process
11
+
12
+ let nativeBinding = null
13
+ let localFileExisted = false
14
+ let loadError = null
15
+
16
+ function isMusl() {
17
+ // For Node 10
18
+ if (!process.report || typeof process.report.getReport !== 'function') {
19
+ try {
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
+ return readFileSync(lddPath, 'utf8').includes('musl')
22
+ } catch (e) {
23
+ return true
24
+ }
25
+ } else {
26
+ const { glibcVersionRuntime } = process.report.getReport().header
27
+ return !glibcVersionRuntime
28
+ }
29
+ }
30
+
31
+ switch (platform) {
32
+ case 'android':
33
+ switch (arch) {
34
+ case 'arm64':
35
+ localFileExisted = existsSync(join(__dirname, 'witness-core.android-arm64.node'))
36
+ try {
37
+ if (localFileExisted) {
38
+ nativeBinding = require('./witness-core.android-arm64.node')
39
+ } else {
40
+ nativeBinding = require('@rolly-dev/witness-core-android-arm64')
41
+ }
42
+ } catch (e) {
43
+ loadError = e
44
+ }
45
+ break
46
+ case 'arm':
47
+ localFileExisted = existsSync(join(__dirname, 'witness-core.android-arm-eabi.node'))
48
+ try {
49
+ if (localFileExisted) {
50
+ nativeBinding = require('./witness-core.android-arm-eabi.node')
51
+ } else {
52
+ nativeBinding = require('@rolly-dev/witness-core-android-arm-eabi')
53
+ }
54
+ } catch (e) {
55
+ loadError = e
56
+ }
57
+ break
58
+ default:
59
+ throw new Error(`Unsupported architecture on Android ${arch}`)
60
+ }
61
+ break
62
+ case 'win32':
63
+ switch (arch) {
64
+ case 'x64':
65
+ localFileExisted = existsSync(
66
+ join(__dirname, 'witness-core.win32-x64-msvc.node')
67
+ )
68
+ try {
69
+ if (localFileExisted) {
70
+ nativeBinding = require('./witness-core.win32-x64-msvc.node')
71
+ } else {
72
+ nativeBinding = require('@rolly-dev/witness-core-win32-x64-msvc')
73
+ }
74
+ } catch (e) {
75
+ loadError = e
76
+ }
77
+ break
78
+ case 'ia32':
79
+ localFileExisted = existsSync(
80
+ join(__dirname, 'witness-core.win32-ia32-msvc.node')
81
+ )
82
+ try {
83
+ if (localFileExisted) {
84
+ nativeBinding = require('./witness-core.win32-ia32-msvc.node')
85
+ } else {
86
+ nativeBinding = require('@rolly-dev/witness-core-win32-ia32-msvc')
87
+ }
88
+ } catch (e) {
89
+ loadError = e
90
+ }
91
+ break
92
+ case 'arm64':
93
+ localFileExisted = existsSync(
94
+ join(__dirname, 'witness-core.win32-arm64-msvc.node')
95
+ )
96
+ try {
97
+ if (localFileExisted) {
98
+ nativeBinding = require('./witness-core.win32-arm64-msvc.node')
99
+ } else {
100
+ nativeBinding = require('@rolly-dev/witness-core-win32-arm64-msvc')
101
+ }
102
+ } catch (e) {
103
+ loadError = e
104
+ }
105
+ break
106
+ default:
107
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
+ }
109
+ break
110
+ case 'darwin':
111
+ localFileExisted = existsSync(join(__dirname, 'witness-core.darwin-universal.node'))
112
+ try {
113
+ if (localFileExisted) {
114
+ nativeBinding = require('./witness-core.darwin-universal.node')
115
+ } else {
116
+ nativeBinding = require('@rolly-dev/witness-core-darwin-universal')
117
+ }
118
+ break
119
+ } catch {}
120
+ switch (arch) {
121
+ case 'x64':
122
+ localFileExisted = existsSync(join(__dirname, 'witness-core.darwin-x64.node'))
123
+ try {
124
+ if (localFileExisted) {
125
+ nativeBinding = require('./witness-core.darwin-x64.node')
126
+ } else {
127
+ nativeBinding = require('@rolly-dev/witness-core-darwin-x64')
128
+ }
129
+ } catch (e) {
130
+ loadError = e
131
+ }
132
+ break
133
+ case 'arm64':
134
+ localFileExisted = existsSync(
135
+ join(__dirname, 'witness-core.darwin-arm64.node')
136
+ )
137
+ try {
138
+ if (localFileExisted) {
139
+ nativeBinding = require('./witness-core.darwin-arm64.node')
140
+ } else {
141
+ nativeBinding = require('@rolly-dev/witness-core-darwin-arm64')
142
+ }
143
+ } catch (e) {
144
+ loadError = e
145
+ }
146
+ break
147
+ default:
148
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
+ }
150
+ break
151
+ case 'freebsd':
152
+ if (arch !== 'x64') {
153
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
+ }
155
+ localFileExisted = existsSync(join(__dirname, 'witness-core.freebsd-x64.node'))
156
+ try {
157
+ if (localFileExisted) {
158
+ nativeBinding = require('./witness-core.freebsd-x64.node')
159
+ } else {
160
+ nativeBinding = require('@rolly-dev/witness-core-freebsd-x64')
161
+ }
162
+ } catch (e) {
163
+ loadError = e
164
+ }
165
+ break
166
+ case 'linux':
167
+ switch (arch) {
168
+ case 'x64':
169
+ if (isMusl()) {
170
+ localFileExisted = existsSync(
171
+ join(__dirname, 'witness-core.linux-x64-musl.node')
172
+ )
173
+ try {
174
+ if (localFileExisted) {
175
+ nativeBinding = require('./witness-core.linux-x64-musl.node')
176
+ } else {
177
+ nativeBinding = require('@rolly-dev/witness-core-linux-x64-musl')
178
+ }
179
+ } catch (e) {
180
+ loadError = e
181
+ }
182
+ } else {
183
+ localFileExisted = existsSync(
184
+ join(__dirname, 'witness-core.linux-x64-gnu.node')
185
+ )
186
+ try {
187
+ if (localFileExisted) {
188
+ nativeBinding = require('./witness-core.linux-x64-gnu.node')
189
+ } else {
190
+ nativeBinding = require('@rolly-dev/witness-core-linux-x64-gnu')
191
+ }
192
+ } catch (e) {
193
+ loadError = e
194
+ }
195
+ }
196
+ break
197
+ case 'arm64':
198
+ if (isMusl()) {
199
+ localFileExisted = existsSync(
200
+ join(__dirname, 'witness-core.linux-arm64-musl.node')
201
+ )
202
+ try {
203
+ if (localFileExisted) {
204
+ nativeBinding = require('./witness-core.linux-arm64-musl.node')
205
+ } else {
206
+ nativeBinding = require('@rolly-dev/witness-core-linux-arm64-musl')
207
+ }
208
+ } catch (e) {
209
+ loadError = e
210
+ }
211
+ } else {
212
+ localFileExisted = existsSync(
213
+ join(__dirname, 'witness-core.linux-arm64-gnu.node')
214
+ )
215
+ try {
216
+ if (localFileExisted) {
217
+ nativeBinding = require('./witness-core.linux-arm64-gnu.node')
218
+ } else {
219
+ nativeBinding = require('@rolly-dev/witness-core-linux-arm64-gnu')
220
+ }
221
+ } catch (e) {
222
+ loadError = e
223
+ }
224
+ }
225
+ break
226
+ case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'witness-core.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./witness-core.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('@rolly-dev/witness-core-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'witness-core.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./witness-core.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('@rolly-dev/witness-core-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'witness-core.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./witness-core.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('@rolly-dev/witness-core-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'witness-core.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./witness-core.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('@rolly-dev/witness-core-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
285
+ localFileExisted = existsSync(
286
+ join(__dirname, 'witness-core.linux-s390x-gnu.node')
287
+ )
288
+ try {
289
+ if (localFileExisted) {
290
+ nativeBinding = require('./witness-core.linux-s390x-gnu.node')
291
+ } else {
292
+ nativeBinding = require('@rolly-dev/witness-core-linux-s390x-gnu')
293
+ }
294
+ } catch (e) {
295
+ loadError = e
296
+ }
297
+ break
298
+ default:
299
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
300
+ }
301
+ break
302
+ default:
303
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304
+ }
305
+
306
+ if (!nativeBinding) {
307
+ if (loadError) {
308
+ throw loadError
309
+ }
310
+ throw new Error(`Failed to load native binding`)
311
+ }
312
+
313
+ const { MerkleEngine, engineVersion } = nativeBinding
314
+
315
+ module.exports.MerkleEngine = MerkleEngine
316
+ module.exports.engineVersion = engineVersion
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@rolly-dev/witness-core",
3
+ "version": "0.1.0",
4
+ "description": "Authoritative native witness-state engine for Rolly ZK-Rollup (Node-API addon)",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "license": "MIT",
8
+ "engines": {
9
+ "node": ">=16"
10
+ },
11
+ "napi": {
12
+ "name": "witness-core",
13
+ "triples": {
14
+ "defaults": false,
15
+ "additional": [
16
+ "x86_64-unknown-linux-gnu",
17
+ "aarch64-apple-darwin"
18
+ ]
19
+ }
20
+ },
21
+ "files": [
22
+ "index.js",
23
+ "index.d.ts",
24
+ "*.node"
25
+ ],
26
+ "scripts": {
27
+ "build": "napi build --platform --release",
28
+ "build:debug": "napi build --platform",
29
+ "build:linux-x64": "napi build --platform --release --target x86_64-unknown-linux-gnu",
30
+ "artifacts": "napi artifacts",
31
+ "prepublishOnly": "napi prepublish -t npm",
32
+ "test": "node test.js"
33
+ },
34
+ "devDependencies": {
35
+ "@napi-rs/cli": "^2.18.4"
36
+ },
37
+ "keywords": [
38
+ "napi",
39
+ "node-api",
40
+ "zk-rollup",
41
+ "witness",
42
+ "merkle",
43
+ "rolly"
44
+ ],
45
+ "optionalDependencies": {
46
+ "@rolly-dev/witness-core-linux-x64-gnu": "0.1.0",
47
+ "@rolly-dev/witness-core-darwin-arm64": "0.1.0"
48
+ }
49
+ }
Binary file
Binary file