@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 +80 -0
- package/index.d.ts +318 -0
- package/index.js +316 -0
- package/package.json +49 -0
- package/witness-core.darwin-arm64.node +0 -0
- package/witness-core.linux-x64-gnu.node +0 -0
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
|