pshenmic-dpp 2.0.0-dev.23 → 2.0.0-dev.25

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.
@@ -1,46 +1,127 @@
1
1
  "use strict";
2
2
  const { WASI } = require('@tybys/wasm-util');
3
3
  const { getDefaultContext } = require('@emnapi/runtime');
4
- const { instantiateNapiModuleSync } = require('@emnapi/core');
5
- const { bytes } = require('./wasm/wasmBytes.cjs');
6
- const { decode } = require('../utils/base122.cjs');
7
- const { decompressSync } = require('fflate');
8
- const wasmBytes = new Uint8Array(decode(bytes));
9
- const wasi = new WASI({
10
- version: 'preview1',
11
- print: function () {
12
- console.log.apply(console, arguments);
13
- },
14
- printErr: function () {
15
- console.error.apply(console, arguments);
16
- }
17
- });
4
+ const { instantiateNapiModuleSync, MessageHandler } = require('@emnapi/core');
18
5
  const emnapiContext = getDefaultContext();
19
6
  emnapiContext.feature.supportNewFunction = false;
20
7
  emnapiContext.feature.supportBigInt = false;
21
- const __sharedMemory = new WebAssembly.Memory({
22
- initial: 1000,
23
- maximum: 2000,
24
- shared: true
25
- });
26
- const wasm = instantiateNapiModuleSync(decompressSync(wasmBytes), {
27
- context: emnapiContext,
28
- wasi,
29
- overwriteImports(importObject) {
30
- importObject.env = {
31
- ...importObject.env,
32
- ...importObject.napi,
33
- ...importObject.emnapi,
34
- memory: __sharedMemory
35
- };
36
- },
37
- beforeInit({ instance }) {
38
- for (const name of Object.keys(instance.exports)) {
39
- if (name.startsWith('__napi_register__')) {
40
- instance.exports[name]();
41
- }
8
+ const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
9
+ const nodeWorkerThreads = isNode ? require('worker_threads') : null;
10
+ const isWorker = isNode
11
+ ? !nodeWorkerThreads.isMainThread
12
+ : (typeof self !== 'undefined' && !self.document);
13
+ if (isWorker) {
14
+ // wasi pthread worker: the main thread posts the compiled module + shared
15
+ // memory here and this instantiates a child-thread copy over them. Async
16
+ // `#[napi]` fns (the Halo 2 proving stack) run on these threads.
17
+ const postMsg = isNode
18
+ ? (d) => nodeWorkerThreads.parentPort.postMessage(d)
19
+ : (d) => self.postMessage(d);
20
+ if (isNode) {
21
+ // emnapi's threadsafe-function dispatch calls the bare global
22
+ // `postMessage` from pthread contexts (a Web Worker global); Node
23
+ // worker_threads doesn't have it, so polyfill with parentPort.
24
+ globalThis.self = globalThis;
25
+ globalThis.postMessage = postMsg;
26
+ }
27
+ const handler = new MessageHandler({
28
+ postMessage: postMsg,
29
+ onLoad({ wasmModule, wasmMemory }) {
30
+ const wasi = new WASI({ version: 'preview1' });
31
+ return instantiateNapiModuleSync(wasmModule, {
32
+ childThread: true,
33
+ wasi,
34
+ context: emnapiContext,
35
+ postMessage: postMsg,
36
+ overwriteImports(importObject) {
37
+ importObject.env = {
38
+ ...importObject.env,
39
+ ...importObject.napi,
40
+ ...importObject.emnapi,
41
+ memory: wasmMemory
42
+ };
43
+ }
44
+ });
42
45
  }
46
+ });
47
+ if (isNode) {
48
+ nodeWorkerThreads.parentPort.on('message', (data) => handler.handle({ data }));
43
49
  }
44
- });
45
- module.exports = wasm.napiModule.exports;
46
- module.exports.default = wasm.napiModule.exports;
50
+ else {
51
+ self.onmessage = (e) => handler.handle({ data: e.data });
52
+ }
53
+ module.exports = null;
54
+ }
55
+ else {
56
+ // Main thread only. This block must NEVER run in a worker: it decodes,
57
+ // decompresses and sync-compiles the whole 7.5MB module and allocates a
58
+ // fresh 64MB shared memory — workers receive the already-compiled module
59
+ // and the real memory via the 'load' message instead. Running it per
60
+ // worker multiplies init time by the core count (rayon spawns one thread
61
+ // per core while building the proving key).
62
+ const { bytes } = require('./wasm/wasmBytes.cjs');
63
+ const { decode } = require('../utils/base122.cjs');
64
+ const { decompressSync } = require('fflate');
65
+ const { default: sharedMemory } = require('./memory.cjs');
66
+ const wasmBytes = new Uint8Array(decode(bytes));
67
+ const wasi = new WASI({
68
+ version: 'preview1',
69
+ print: function () {
70
+ console.log.apply(console, arguments);
71
+ },
72
+ printErr: function () {
73
+ console.error.apply(console, arguments);
74
+ }
75
+ });
76
+ let workerBlobUrl;
77
+ const wasm = instantiateNapiModuleSync(decompressSync(wasmBytes), {
78
+ context: emnapiContext,
79
+ wasi,
80
+ // Threads are spawned lazily (first async `#[napi]` call), so
81
+ // instantiation stays synchronous: emnapi only forbids sync loading
82
+ // with a PREloaded worker pool (`reuseWorker.size > 0`). `true` keeps
83
+ // size 0 but returns finished workers to a pool instead of
84
+ // terminating them, so repeated proving calls reuse them.
85
+ reuseWorker: true,
86
+ onCreateWorker() {
87
+ if (isNode) {
88
+ return new nodeWorkerThreads.Worker(__filename);
89
+ }
90
+ // Escape hatch for hosts that want to control worker creation
91
+ // (e.g. strict CSP environments where blob: workers are forbidden).
92
+ if (typeof globalThis.__PSHENMIC_DPP_CREATE_WORKER__ === 'function') {
93
+ return globalThis.__PSHENMIC_DPP_CREATE_WORKER__();
94
+ }
95
+ if (typeof Worker === 'undefined') {
96
+ throw new Error('Cannot create a wasm thread worker: no Worker constructor in this environment. ' +
97
+ 'Provide globalThis.__PSHENMIC_DPP_CREATE_WORKER__ returning a Worker running workerBundle.cjs `source`.');
98
+ }
99
+ // Self-contained worker (all emnapi deps inlined) spawned from a Blob
100
+ // URL, so it survives being bundled into a single file: the worker
101
+ // receives the compiled module + shared memory via postMessage and
102
+ // needs no access to this script's URL or the wasm bytes.
103
+ if (workerBlobUrl === undefined) {
104
+ const { source } = require('./wasm/workerBundle.cjs');
105
+ workerBlobUrl = URL.createObjectURL(new Blob([source], { type: 'text/javascript' }));
106
+ }
107
+ return new Worker(workerBlobUrl, { name: 'pshenmic-dpp-wasm-thread' });
108
+ },
109
+ overwriteImports(importObject) {
110
+ importObject.env = {
111
+ ...importObject.env,
112
+ ...importObject.napi,
113
+ ...importObject.emnapi,
114
+ memory: sharedMemory
115
+ };
116
+ },
117
+ beforeInit({ instance }) {
118
+ for (const name of Object.keys(instance.exports)) {
119
+ if (name.startsWith('__napi_register__')) {
120
+ instance.exports[name]();
121
+ }
122
+ }
123
+ }
124
+ });
125
+ module.exports = wasm.napiModule.exports;
126
+ module.exports.default = wasm.napiModule.exports;
127
+ }
@@ -1,2 +1,4 @@
1
1
  declare const _exports: any;
2
2
  export = _exports;
3
+ declare const _exports: any;
4
+ export = _exports;
@@ -7,6 +7,13 @@ import type { OrchardProverNAPI } from '../../../../binaries/bindingsTypes.js';
7
7
  export declare class OrchardProverWASM {
8
8
  /** @private **/
9
9
  _rawOrchardProver: OrchardProverNAPI;
10
+ /**
11
+ * The proving key is deterministic and expensive (~10s), so it's built
12
+ * once per process and shared by every prover instance. Cleared on
13
+ * failure so a failed init can be retried.
14
+ */
15
+ private static _initPromise;
10
16
  constructor();
17
+ init(): Promise<void>;
11
18
  static createFromRawInstance(rawInstance: OrchardProverNAPI): OrchardProverWASM;
12
19
  }
@@ -7,8 +7,24 @@ import { dppProvider } from '../../provider.js';
7
7
  export class OrchardProverWASM {
8
8
  /** @private **/
9
9
  _rawOrchardProver;
10
+ /**
11
+ * The proving key is deterministic and expensive (~10s), so it's built
12
+ * once per process and shared by every prover instance. Cleared on
13
+ * failure so a failed init can be retried.
14
+ */
15
+ static _initPromise = null;
10
16
  constructor() {
11
- this._rawOrchardProver = new dppProvider.dpp.OrchardProverNAPI();
17
+ // empty
18
+ }
19
+ async init() {
20
+ if (OrchardProverWASM._initPromise == null) {
21
+ OrchardProverWASM._initPromise = dppProvider.dpp.OrchardProverNAPI.init()
22
+ .catch((e) => {
23
+ OrchardProverWASM._initPromise = null;
24
+ throw e;
25
+ });
26
+ }
27
+ this._rawOrchardProver = await OrchardProverWASM._initPromise;
12
28
  }
13
29
  static createFromRawInstance(rawInstance) {
14
30
  const instance = Object.create(OrchardProverWASM.prototype);
@@ -24,17 +24,24 @@ import { IdentityCreateFromShieldedPoolResultWASM } from './IdentityCreateFromSh
24
24
  export declare class ShieldedBuilderWASM {
25
25
  /** @private **/
26
26
  _rawShieldedBuilder: ShieldedBuilderNAPI;
27
+ /**
28
+ * Building the Halo 2 proving key takes ~10s, and the key is deterministic,
29
+ * so it's built once per process: every builder instance shares the same
30
+ * raw builder. Cleared on failure so a failed init can be retried.
31
+ */
32
+ private static _initPromise;
27
33
  constructor();
34
+ init(): Promise<void>;
28
35
  /** Asset lock -> pool (deposit). */
29
- shieldFromAssetLock(recipient: OrchardAddressWASM, shieldAmount: bigint, assetLockProof: AssetLockProofWASM, privateKey: PrivateKeyWASM, memo: ShieldedMemoWASM, dummyOutputs: number, senderOvk?: Uint8Array, surplusOutput?: PlatformAddressLike, platformVersion?: PlatformVersionNAPI): StateTransitionWASM;
36
+ shieldFromAssetLock(recipient: OrchardAddressWASM, shieldAmount: bigint, assetLockProof: AssetLockProofWASM, privateKey: PrivateKeyWASM, memo: ShieldedMemoWASM, dummyOutputs: number, senderOvk?: Uint8Array, surplusOutput?: PlatformAddressLike, platformVersion?: PlatformVersionNAPI): Promise<StateTransitionWASM>;
30
37
  /** Transparent platform addresses -> pool (deposit). One private key per input. */
31
- shield(recipient: OrchardAddressWASM, shieldAmount: bigint, inputs: InputAddressWASM[], privateKeys: PrivateKeyWASM[], feeStrategy: AddressFundsFeeStrategyStepWASM[], userFeeIncrease: number, memo: ShieldedMemoWASM, senderOvk?: Uint8Array, platformVersion?: PlatformVersionNAPI): StateTransitionWASM;
38
+ shield(recipient: OrchardAddressWASM, shieldAmount: bigint, inputs: InputAddressWASM[], privateKeys: PrivateKeyWASM[], feeStrategy: AddressFundsFeeStrategyStepWASM[], userFeeIncrease: number, memo: ShieldedMemoWASM, senderOvk?: Uint8Array, platformVersion?: PlatformVersionNAPI): Promise<StateTransitionWASM>;
32
39
  /** Pool -> core L1 (spend). */
33
- shieldedWithdrawal(spends: SpendableNoteWASM[], withdrawalAmount: bigint, outputScript: CoreScriptWASM, coreFeePerByte: number, pooling: PoolingLike, changeAddress: OrchardAddressWASM, seed: Uint8Array, coinType: number, account: number, anchor: Uint8Array, memo: ShieldedMemoWASM, platformVersion?: PlatformVersionNAPI): ShieldedWithdrawalResultWASM;
40
+ shieldedWithdrawal(spends: SpendableNoteWASM[], withdrawalAmount: bigint, outputScript: CoreScriptWASM, coreFeePerByte: number, pooling: PoolingLike, changeAddress: OrchardAddressWASM, seed: Uint8Array, coinType: number, account: number, anchor: Uint8Array, memo: ShieldedMemoWASM, platformVersion?: PlatformVersionNAPI): Promise<ShieldedWithdrawalResultWASM>;
34
41
  /** Pool -> platform identity balance (spend). */
35
- unshield(spends: SpendableNoteWASM[], outputAddress: PlatformAddressLike, unshieldAmount: bigint, changeAddress: OrchardAddressWASM, seed: Uint8Array, coinType: number, account: number, anchor: Uint8Array, memo: ShieldedMemoWASM, platformVersion?: PlatformVersionNAPI): ShieldedWithdrawalResultWASM;
42
+ unshield(spends: SpendableNoteWASM[], outputAddress: PlatformAddressLike, unshieldAmount: bigint, changeAddress: OrchardAddressWASM, seed: Uint8Array, coinType: number, account: number, anchor: Uint8Array, memo: ShieldedMemoWASM, platformVersion?: PlatformVersionNAPI): Promise<ShieldedWithdrawalResultWASM>;
36
43
  /** Pool -> pool (spend). */
37
- shieldedTransfer(spends: SpendableNoteWASM[], recipient: OrchardAddressWASM, transferAmount: bigint, changeAddress: OrchardAddressWASM, seed: Uint8Array, coinType: number, account: number, anchor: Uint8Array, memo: ShieldedMemoWASM, platformVersion?: PlatformVersionNAPI): ShieldedWithdrawalResultWASM;
44
+ shieldedTransfer(spends: SpendableNoteWASM[], recipient: OrchardAddressWASM, transferAmount: bigint, changeAddress: OrchardAddressWASM, seed: Uint8Array, coinType: number, account: number, anchor: Uint8Array, memo: ShieldedMemoWASM, platformVersion?: PlatformVersionNAPI): Promise<ShieldedWithdrawalResultWASM>;
38
45
  /** Pool -> new identity (spend). One private key per public key, same order. */
39
- identityCreateFromShieldedPool(publicKeys: IdentityPublicKeyInCreationWASM[], privateKeys: PrivateKeyWASM[], denomination: bigint, sendToAddressOnCreationFailure: PlatformAddressLike, spends: SpendableNoteWASM[], changeAddress: OrchardAddressWASM, seed: Uint8Array, coinType: number, account: number, anchor: Uint8Array, memo: ShieldedMemoWASM, platformVersion?: PlatformVersionNAPI): IdentityCreateFromShieldedPoolResultWASM;
46
+ identityCreateFromShieldedPool(publicKeys: IdentityPublicKeyInCreationWASM[], privateKeys: PrivateKeyWASM[], denomination: bigint, sendToAddressOnCreationFailure: PlatformAddressLike, spends: SpendableNoteWASM[], changeAddress: OrchardAddressWASM, seed: Uint8Array, coinType: number, account: number, anchor: Uint8Array, memo: ShieldedMemoWASM, platformVersion?: PlatformVersionNAPI): Promise<IdentityCreateFromShieldedPoolResultWASM>;
40
47
  }
@@ -15,31 +15,47 @@ import { IdentityCreateFromShieldedPoolResultWASM } from './IdentityCreateFromSh
15
15
  export class ShieldedBuilderWASM {
16
16
  /** @private **/
17
17
  _rawShieldedBuilder;
18
+ /**
19
+ * Building the Halo 2 proving key takes ~10s, and the key is deterministic,
20
+ * so it's built once per process: every builder instance shares the same
21
+ * raw builder. Cleared on failure so a failed init can be retried.
22
+ */
23
+ static _initPromise = null;
18
24
  constructor() {
19
- this._rawShieldedBuilder = new dppProvider.dpp.ShieldedBuilderNAPI();
25
+ // empty
26
+ }
27
+ async init() {
28
+ if (ShieldedBuilderWASM._initPromise == null) {
29
+ ShieldedBuilderWASM._initPromise = dppProvider.dpp.ShieldedBuilderNAPI.init()
30
+ .catch((e) => {
31
+ ShieldedBuilderWASM._initPromise = null;
32
+ throw e;
33
+ });
34
+ }
35
+ this._rawShieldedBuilder = await ShieldedBuilderWASM._initPromise;
20
36
  }
21
37
  /** Asset lock -> pool (deposit). */
22
- shieldFromAssetLock(recipient, shieldAmount, assetLockProof, privateKey, memo, dummyOutputs, senderOvk, surplusOutput, platformVersion) {
23
- return StateTransitionWASM.createFromRawInstance(this._rawShieldedBuilder.shieldFromAssetLock(recipient._rawOrchardAddress, shieldAmount.toString(), assetLockProof._rawAssetLockProof, privateKey._rawPrivateKey, memo._rawShieldedMemo, dummyOutputs, senderOvk, surplusOutput != null ? preparePlatformAddressValue(surplusOutput) : undefined, platformVersion));
38
+ async shieldFromAssetLock(recipient, shieldAmount, assetLockProof, privateKey, memo, dummyOutputs, senderOvk, surplusOutput, platformVersion) {
39
+ return StateTransitionWASM.createFromRawInstance(await this._rawShieldedBuilder.shieldFromAssetLock(recipient._rawOrchardAddress, shieldAmount.toString(), assetLockProof._rawAssetLockProof, privateKey._rawPrivateKey, memo._rawShieldedMemo, dummyOutputs, senderOvk, surplusOutput != null ? preparePlatformAddressValue(surplusOutput) : undefined, platformVersion));
24
40
  }
25
41
  /** Transparent platform addresses -> pool (deposit). One private key per input. */
26
- shield(recipient, shieldAmount, inputs, privateKeys, feeStrategy, userFeeIncrease, memo, senderOvk, platformVersion) {
27
- return StateTransitionWASM.createFromRawInstance(this._rawShieldedBuilder.shield(recipient._rawOrchardAddress, shieldAmount.toString(), inputs.map(i => i._rawInputAddress), privateKeys.map(k => k._rawPrivateKey), feeStrategy.map(s => s._rawAddressFundsFeeStrategyStep), userFeeIncrease, memo._rawShieldedMemo, senderOvk, platformVersion));
42
+ async shield(recipient, shieldAmount, inputs, privateKeys, feeStrategy, userFeeIncrease, memo, senderOvk, platformVersion) {
43
+ return StateTransitionWASM.createFromRawInstance(await this._rawShieldedBuilder.shield(recipient._rawOrchardAddress, shieldAmount.toString(), inputs.map(i => i._rawInputAddress), privateKeys.map(k => k._rawPrivateKey), feeStrategy.map(s => s._rawAddressFundsFeeStrategyStep), userFeeIncrease, memo._rawShieldedMemo, senderOvk, platformVersion));
28
44
  }
29
45
  /** Pool -> core L1 (spend). */
30
- shieldedWithdrawal(spends, withdrawalAmount, outputScript, coreFeePerByte, pooling, changeAddress, seed, coinType, account, anchor, memo, platformVersion) {
31
- return ShieldedWithdrawalResultWASM.createFromRawInstance(this._rawShieldedBuilder.shieldedWithdrawal(spends.map(s => s._rawSpendableNote), withdrawalAmount.toString(), outputScript._rawCoreScript, coreFeePerByte, valueToDynamicValue(pooling), changeAddress._rawOrchardAddress, seed, coinType, account, anchor, memo._rawShieldedMemo, platformVersion));
46
+ async shieldedWithdrawal(spends, withdrawalAmount, outputScript, coreFeePerByte, pooling, changeAddress, seed, coinType, account, anchor, memo, platformVersion) {
47
+ return ShieldedWithdrawalResultWASM.createFromRawInstance(await this._rawShieldedBuilder.shieldedWithdrawal(spends.map(s => s._rawSpendableNote), withdrawalAmount.toString(), outputScript._rawCoreScript, coreFeePerByte, valueToDynamicValue(pooling), changeAddress._rawOrchardAddress, seed, coinType, account, anchor, memo._rawShieldedMemo, platformVersion));
32
48
  }
33
49
  /** Pool -> platform identity balance (spend). */
34
- unshield(spends, outputAddress, unshieldAmount, changeAddress, seed, coinType, account, anchor, memo, platformVersion) {
35
- return ShieldedWithdrawalResultWASM.createFromRawInstance(this._rawShieldedBuilder.unshield(spends.map(s => s._rawSpendableNote), preparePlatformAddressValue(outputAddress), unshieldAmount.toString(), changeAddress._rawOrchardAddress, seed, coinType, account, anchor, memo._rawShieldedMemo, platformVersion));
50
+ async unshield(spends, outputAddress, unshieldAmount, changeAddress, seed, coinType, account, anchor, memo, platformVersion) {
51
+ return ShieldedWithdrawalResultWASM.createFromRawInstance(await this._rawShieldedBuilder.unshield(spends.map(s => s._rawSpendableNote), preparePlatformAddressValue(outputAddress), unshieldAmount.toString(), changeAddress._rawOrchardAddress, seed, coinType, account, anchor, memo._rawShieldedMemo, platformVersion));
36
52
  }
37
53
  /** Pool -> pool (spend). */
38
- shieldedTransfer(spends, recipient, transferAmount, changeAddress, seed, coinType, account, anchor, memo, platformVersion) {
39
- return ShieldedWithdrawalResultWASM.createFromRawInstance(this._rawShieldedBuilder.shieldedTransfer(spends.map(s => s._rawSpendableNote), recipient._rawOrchardAddress, transferAmount.toString(), changeAddress._rawOrchardAddress, seed, coinType, account, anchor, memo._rawShieldedMemo, platformVersion));
54
+ async shieldedTransfer(spends, recipient, transferAmount, changeAddress, seed, coinType, account, anchor, memo, platformVersion) {
55
+ return ShieldedWithdrawalResultWASM.createFromRawInstance(await this._rawShieldedBuilder.shieldedTransfer(spends.map(s => s._rawSpendableNote), recipient._rawOrchardAddress, transferAmount.toString(), changeAddress._rawOrchardAddress, seed, coinType, account, anchor, memo._rawShieldedMemo, platformVersion));
40
56
  }
41
57
  /** Pool -> new identity (spend). One private key per public key, same order. */
42
- identityCreateFromShieldedPool(publicKeys, privateKeys, denomination, sendToAddressOnCreationFailure, spends, changeAddress, seed, coinType, account, anchor, memo, platformVersion) {
43
- return IdentityCreateFromShieldedPoolResultWASM.createFromRawInstance(this._rawShieldedBuilder.identityCreateFromShieldedPool(publicKeys.map(k => k._rawKeyInCreation), privateKeys.map(k => k._rawPrivateKey), denomination.toString(), preparePlatformAddressValue(sendToAddressOnCreationFailure), spends.map(s => s._rawSpendableNote), changeAddress._rawOrchardAddress, seed, coinType, account, anchor, memo._rawShieldedMemo, platformVersion));
58
+ async identityCreateFromShieldedPool(publicKeys, privateKeys, denomination, sendToAddressOnCreationFailure, spends, changeAddress, seed, coinType, account, anchor, memo, platformVersion) {
59
+ return IdentityCreateFromShieldedPoolResultWASM.createFromRawInstance(await this._rawShieldedBuilder.identityCreateFromShieldedPool(publicKeys.map(k => k._rawKeyInCreation), privateKeys.map(k => k._rawPrivateKey), denomination.toString(), preparePlatformAddressValue(sendToAddressOnCreationFailure), spends.map(s => s._rawSpendableNote), changeAddress._rawOrchardAddress, seed, coinType, account, anchor, memo._rawShieldedMemo, platformVersion));
44
60
  }
45
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pshenmic-dpp",
3
- "version": "2.0.0-dev.23",
3
+ "version": "2.0.0-dev.25",
4
4
  "main": "dist/src/wasm.js",
5
5
  "types": "dist/src/wasm.d.ts",
6
6
  "react-native": "./dist/src/react-native.js",
@@ -288,21 +288,23 @@ describe('SpendableNote', function () {
288
288
  })
289
289
 
290
290
  describe('OrchardProver', function () {
291
- // Constructing the prover builds the Halo 2 proving key (~seconds).
292
- test('should construct a prover', function () {
291
+ // Initializing the prover builds the Halo 2 proving key (~seconds).
292
+ test('should construct a prover', async function () {
293
293
  const prover = new OrchardProverWASM()
294
+ await prover.init()
294
295
 
295
296
  expect(prover).toBeInstanceOf(OrchardProverWASM)
296
297
  }, 30000)
297
298
  })
298
299
 
299
300
  describe('ShieldedBuilder', function () {
300
- // The builder builds its own Halo 2 proving key on construction (~seconds),
301
+ // The builder builds its own Halo 2 proving key on init (~seconds),
301
302
  // so build it once and reuse it across the deposit test.
302
303
  let builder: ShieldedBuilderWASM
303
304
 
304
- beforeAll(function () {
305
+ beforeAll(async function () {
305
306
  builder = new ShieldedBuilderWASM()
307
+ await builder.init()
306
308
  }, 30000)
307
309
 
308
310
  describe('constructor', function () {
@@ -312,7 +314,7 @@ describe('ShieldedBuilder', function () {
312
314
  })
313
315
 
314
316
  describe('shield (transparent -> pool deposit)', function () {
315
- test('should build and prove a shield transition', function () {
317
+ test('should build and prove a shield transition', async function () {
316
318
  const recipient = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
317
319
  const senderOvk = orchardOvkFromSeed(SEED, COIN_TYPE, ACCOUNT)
318
320
 
@@ -321,7 +323,7 @@ describe('ShieldedBuilder', function () {
321
323
  // address, nonce, balance — balance must cover amount + fee.
322
324
  const input = new InputAddressWASM(inputAddress, 0, DASH / 2n)
323
325
 
324
- const st = builder.shield(
326
+ const st = await builder.shield(
325
327
  recipient,
326
328
  (DASH * 4n) / 10n, // 0.4 DASH into the pool
327
329
  [input],
@@ -1,5 +1,5 @@
1
1
  import * as dpp from "pshenmic-dpp/wasm";
2
- import {IdentifierWASM} from "pshenmic-dpp/wasm";
2
+ import {IdentifierWASM, ShieldedBuilderWASM} from "pshenmic-dpp/wasm";
3
3
 
4
4
  let identifierBytes: Uint8Array;
5
5
 
@@ -46,4 +46,43 @@ describe("dpp", function () {
46
46
  expect(identifier.bytes()).toEqual(identifierBytes);
47
47
  });
48
48
  })
49
+
50
+ describe('shielded init', function () {
51
+ test('should allow to use old instances after shielded init', async function () {
52
+ const identifier = IdentifierWASM.fromBytes(identifierBytes);
53
+
54
+ const builder = new ShieldedBuilderWASM()
55
+
56
+ await builder.init();
57
+
58
+ expect(identifier.bytes()).toEqual(identifierBytes);
59
+ })
60
+
61
+ test('should build a real Halo 2 proof with instances created before init', async function () {
62
+ const seed = new Uint8Array(64).fill(7);
63
+
64
+ // created BEFORE init — must stay usable by the proving call
65
+ const recipient = dpp.OrchardAddressWASM.fromSeed(seed, 1, 0);
66
+ const senderOvk = dpp.orchardOvkFromSeed(seed, 1, 0);
67
+ const privateKey = dpp.PrivateKeyWASM.fromWIF('cUy4wbim4y9NDwC24omx8oWY5WqfmjSU2gdcZtTXza2xDCAkQXRP');
68
+ const outPoint = new dpp.OutPointWASM('79af4b339ddfee8c9b6d08d74155373c6ef547cda5e65473a254b9b0f8533f0c', 0);
69
+ const assetLockProof = dpp.AssetLockProofWASM.createChainAssetLockProof(1499737, outPoint);
70
+ const memo = dpp.ShieldedMemoWASM.empty();
71
+
72
+ const builder = new ShieldedBuilderWASM();
73
+ await builder.init();
74
+
75
+ const st = await builder.shieldFromAssetLock(
76
+ recipient,
77
+ 40_000_000_000n,
78
+ assetLockProof,
79
+ privateKey,
80
+ memo,
81
+ 0,
82
+ senderOvk
83
+ );
84
+
85
+ expect(st.hex().length).toBeGreaterThan(0);
86
+ }, 120000)
87
+ })
49
88
  });