pshenmic-dpp 2.0.0-dev.20 → 2.0.0-dev.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/binaries/bindingsTypes.d.ts +307 -0
- package/dist/binaries/native/aarch64-apple-darwin/pshenmic_dpp.node +0 -0
- package/dist/binaries/native/x86_64-apple-darwin/pshenmic_dpp.node +0 -0
- package/dist/binaries/native/x86_64-unknown-linux-gnu/pshenmic_dpp.node +0 -0
- package/dist/binaries/native/x86_64-unknown-linux-musl/pshenmic_dpp.node +0 -0
- package/dist/binaries/pshenmic_dpp.android.node/arm64-v8a/libpshenmic_dpp.so +0 -0
- package/dist/binaries/pshenmic_dpp.apple.node/ios-arm64-simulator/libpshenmic_dpp.framework/libpshenmic_dpp +0 -0
- package/dist/binaries/pshenmic_dpp.d.ts +27 -0
- package/dist/binaries/pshenmic_dpp.js +1 -1
- package/dist/binaries/wasm/wasmBytes.cjs +1 -1
- package/dist/binaries/wasm/wasmBytes.d.cts +1 -1
- package/dist/binaries/wasm.d.ts +27 -0
- package/dist/binaries/wasm.js +1 -1
- package/dist/src/dpp/dpp.d.ts +17 -0
- package/dist/src/dpp/dpp.js +17 -0
- package/dist/src/dpp/structs/Orchard/CommitmentTree.d.ts +14 -0
- package/dist/src/dpp/structs/Orchard/CommitmentTree.js +31 -0
- package/dist/src/dpp/structs/Orchard/IdentityCreateFromShieldedPoolResult.d.ts +21 -0
- package/dist/src/dpp/structs/Orchard/IdentityCreateFromShieldedPoolResult.js +38 -0
- package/dist/src/dpp/structs/Orchard/MerklePath.d.ts +11 -0
- package/dist/src/dpp/structs/Orchard/MerklePath.js +25 -0
- package/dist/src/dpp/structs/Orchard/Note.d.ts +18 -0
- package/dist/src/dpp/structs/Orchard/Note.js +42 -0
- package/dist/src/dpp/structs/Orchard/OrchardAddress.d.ts +15 -0
- package/dist/src/dpp/structs/Orchard/OrchardAddress.js +31 -0
- package/dist/src/dpp/structs/Orchard/OrchardProver.d.ts +12 -0
- package/dist/src/dpp/structs/Orchard/OrchardProver.js +18 -0
- package/dist/src/dpp/structs/Orchard/RecoveredNote.d.ts +18 -0
- package/dist/src/dpp/structs/Orchard/RecoveredNote.js +29 -0
- package/dist/src/dpp/structs/Orchard/ShieldedBuilder.d.ts +40 -0
- package/dist/src/dpp/structs/Orchard/ShieldedBuilder.js +45 -0
- package/dist/src/dpp/structs/Orchard/ShieldedMemo.d.ts +11 -0
- package/dist/src/dpp/structs/Orchard/ShieldedMemo.js +25 -0
- package/dist/src/dpp/structs/Orchard/ShieldedWithdrawalResult.d.ts +11 -0
- package/dist/src/dpp/structs/Orchard/ShieldedWithdrawalResult.js +18 -0
- package/dist/src/dpp/structs/Orchard/SpendableNote.d.ts +13 -0
- package/dist/src/dpp/structs/Orchard/SpendableNote.js +27 -0
- package/dist/src/dpp/types.d.ts +35 -0
- package/dist/src/dpp/verify/Shielded/verifyMostRecentShieldedAnchorProof.d.ts +2 -0
- package/dist/src/dpp/verify/Shielded/verifyMostRecentShieldedAnchorProof.js +9 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedAnchorsProof.d.ts +2 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedAnchorsProof.js +9 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedEncryptedNotesProof.d.ts +2 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedEncryptedNotesProof.js +15 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedNotesCountProof.d.ts +2 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedNotesCountProof.js +9 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedNullifiersProof.d.ts +2 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedNullifiersProof.js +12 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedPoolStateProof.d.ts +2 -0
- package/dist/src/dpp/verify/Shielded/verifyShieldedPoolStateProof.js +9 -0
- package/package.json +1 -1
- package/tests/unit/Shielded.spec.ts +344 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StateTransitionWASM } from '../StateTransition.js';
|
|
2
|
+
/** Result of a proving shielded spend (withdrawal / unshield / transfer). */
|
|
3
|
+
export class ShieldedWithdrawalResultWASM {
|
|
4
|
+
/** @private **/
|
|
5
|
+
_rawShieldedWithdrawalResult;
|
|
6
|
+
get stateTransition() {
|
|
7
|
+
return StateTransitionWASM.createFromRawInstance(this._rawShieldedWithdrawalResult.stateTransition);
|
|
8
|
+
}
|
|
9
|
+
/** The fixed shielded fee (credits) consensus charges for the spend. */
|
|
10
|
+
get fee() {
|
|
11
|
+
return BigInt(this._rawShieldedWithdrawalResult.fee);
|
|
12
|
+
}
|
|
13
|
+
static createFromRawInstance(rawInstance) {
|
|
14
|
+
const instance = Object.create(ShieldedWithdrawalResultWASM.prototype);
|
|
15
|
+
instance._rawShieldedWithdrawalResult = rawInstance;
|
|
16
|
+
return instance;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SpendableNoteNAPI } from '../../../../binaries/bindingsTypes.js';
|
|
2
|
+
import { NoteWASM } from './Note.js';
|
|
3
|
+
import { MerklePathWASM } from './MerklePath.js';
|
|
4
|
+
export declare class SpendableNoteWASM {
|
|
5
|
+
/** @private **/
|
|
6
|
+
_rawSpendableNote: SpendableNoteNAPI;
|
|
7
|
+
constructor(note: NoteWASM, merklePath: MerklePathWASM);
|
|
8
|
+
get note(): NoteWASM;
|
|
9
|
+
set note(value: NoteWASM);
|
|
10
|
+
get merklePath(): MerklePathWASM;
|
|
11
|
+
set merklePath(value: MerklePathWASM);
|
|
12
|
+
static createFromRawInstance(rawInstance: SpendableNoteNAPI): SpendableNoteWASM;
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { dppProvider } from '../../provider.js';
|
|
2
|
+
import { NoteWASM } from './Note.js';
|
|
3
|
+
import { MerklePathWASM } from './MerklePath.js';
|
|
4
|
+
export class SpendableNoteWASM {
|
|
5
|
+
/** @private **/
|
|
6
|
+
_rawSpendableNote;
|
|
7
|
+
constructor(note, merklePath) {
|
|
8
|
+
this._rawSpendableNote = new dppProvider.dpp.SpendableNoteNAPI(note._rawNote, merklePath._rawMerklePath);
|
|
9
|
+
}
|
|
10
|
+
get note() {
|
|
11
|
+
return NoteWASM.createFromRawInstance(this._rawSpendableNote.note);
|
|
12
|
+
}
|
|
13
|
+
set note(value) {
|
|
14
|
+
this._rawSpendableNote.note = value._rawNote;
|
|
15
|
+
}
|
|
16
|
+
get merklePath() {
|
|
17
|
+
return MerklePathWASM.createFromRawInstance(this._rawSpendableNote.merklePath);
|
|
18
|
+
}
|
|
19
|
+
set merklePath(value) {
|
|
20
|
+
this._rawSpendableNote.merklePath = value._rawMerklePath;
|
|
21
|
+
}
|
|
22
|
+
static createFromRawInstance(rawInstance) {
|
|
23
|
+
const instance = Object.create(SpendableNoteWASM.prototype);
|
|
24
|
+
instance._rawSpendableNote = rawInstance;
|
|
25
|
+
return instance;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/src/dpp/types.d.ts
CHANGED
|
@@ -310,3 +310,38 @@ export interface VerifiedPlatformAddressesInfosWithRootHash {
|
|
|
310
310
|
rootHash: Uint8Array;
|
|
311
311
|
infos: VerifiedPlatformAddressInfo[];
|
|
312
312
|
}
|
|
313
|
+
export interface VerifiedShieldedEncryptedNote {
|
|
314
|
+
cmx: Uint8Array;
|
|
315
|
+
nullifier: Uint8Array;
|
|
316
|
+
cvNet: Uint8Array;
|
|
317
|
+
encryptedNote: Uint8Array;
|
|
318
|
+
}
|
|
319
|
+
export interface VerifiedShieldedEncryptedNotes {
|
|
320
|
+
rootHash: Uint8Array;
|
|
321
|
+
notes: VerifiedShieldedEncryptedNote[];
|
|
322
|
+
totalCount: bigint;
|
|
323
|
+
}
|
|
324
|
+
export interface VerifiedShieldedAnchors {
|
|
325
|
+
rootHash: Uint8Array;
|
|
326
|
+
anchors: Uint8Array[];
|
|
327
|
+
}
|
|
328
|
+
export interface VerifiedMostRecentShieldedAnchor {
|
|
329
|
+
rootHash: Uint8Array;
|
|
330
|
+
anchor?: Uint8Array;
|
|
331
|
+
}
|
|
332
|
+
export interface VerifiedShieldedPoolState {
|
|
333
|
+
rootHash: Uint8Array;
|
|
334
|
+
totalBalance?: bigint;
|
|
335
|
+
}
|
|
336
|
+
export interface VerifiedShieldedNotesCount {
|
|
337
|
+
rootHash: Uint8Array;
|
|
338
|
+
count?: bigint;
|
|
339
|
+
}
|
|
340
|
+
export interface VerifiedShieldedNullifier {
|
|
341
|
+
nullifier: Uint8Array;
|
|
342
|
+
isSpent: boolean;
|
|
343
|
+
}
|
|
344
|
+
export interface VerifiedShieldedNullifiers {
|
|
345
|
+
rootHash: Uint8Array;
|
|
346
|
+
nullifiers: VerifiedShieldedNullifier[];
|
|
347
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { dppProvider } from '../../provider.js';
|
|
2
|
+
import { valueToDynamicValue } from '../../utils.js';
|
|
3
|
+
export function verifyMostRecentShieldedAnchorProof(proof, verifySubsetOfProof, platformVersion) {
|
|
4
|
+
const result = dppProvider.dpp.verifyMostRecentShieldedAnchorProof(proof, verifySubsetOfProof, valueToDynamicValue(platformVersion));
|
|
5
|
+
return {
|
|
6
|
+
rootHash: result.rootHash,
|
|
7
|
+
anchor: result.anchor ?? undefined
|
|
8
|
+
};
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { dppProvider } from '../../provider.js';
|
|
2
|
+
import { valueToDynamicValue } from '../../utils.js';
|
|
3
|
+
export function verifyShieldedAnchorsProof(proof, verifySubsetOfProof, platformVersion) {
|
|
4
|
+
const result = dppProvider.dpp.verifyShieldedAnchorsProof(proof, verifySubsetOfProof, valueToDynamicValue(platformVersion));
|
|
5
|
+
return {
|
|
6
|
+
rootHash: result.rootHash,
|
|
7
|
+
anchors: result.anchors
|
|
8
|
+
};
|
|
9
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { PlatformVersionLike, VerifiedShieldedEncryptedNotes } from '../../types.js';
|
|
2
|
+
export declare function verifyShieldedEncryptedNotesProof(proof: Uint8Array, startIndex: bigint, count: number, maxElements: number, verifySubsetOfProof: boolean, platformVersion: PlatformVersionLike): VerifiedShieldedEncryptedNotes;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { dppProvider } from '../../provider.js';
|
|
2
|
+
import { valueToDynamicValue } from '../../utils.js';
|
|
3
|
+
export function verifyShieldedEncryptedNotesProof(proof, startIndex, count, maxElements, verifySubsetOfProof, platformVersion) {
|
|
4
|
+
const result = dppProvider.dpp.verifyShieldedEncryptedNotesProof(proof, startIndex.toString(), count, maxElements, verifySubsetOfProof, valueToDynamicValue(platformVersion));
|
|
5
|
+
return {
|
|
6
|
+
rootHash: result.rootHash,
|
|
7
|
+
notes: result.notes.map((note) => ({
|
|
8
|
+
cmx: note.cmx,
|
|
9
|
+
nullifier: note.nullifier,
|
|
10
|
+
cvNet: note.cvNet,
|
|
11
|
+
encryptedNote: note.encryptedNote
|
|
12
|
+
})),
|
|
13
|
+
totalCount: BigInt(result.totalCount)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { dppProvider } from '../../provider.js';
|
|
2
|
+
import { valueToDynamicValue } from '../../utils.js';
|
|
3
|
+
export function verifyShieldedNotesCountProof(proof, verifySubsetOfProof, platformVersion) {
|
|
4
|
+
const result = dppProvider.dpp.verifyShieldedNotesCountProof(proof, verifySubsetOfProof, valueToDynamicValue(platformVersion));
|
|
5
|
+
return {
|
|
6
|
+
rootHash: result.rootHash,
|
|
7
|
+
count: result.count != null ? BigInt(result.count) : undefined
|
|
8
|
+
};
|
|
9
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { PlatformVersionLike, VerifiedShieldedNullifiers } from '../../types.js';
|
|
2
|
+
export declare function verifyShieldedNullifiersProof(proof: Uint8Array, nullifiers: Uint8Array[], verifySubsetOfProof: boolean, platformVersion: PlatformVersionLike): VerifiedShieldedNullifiers;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { dppProvider } from '../../provider.js';
|
|
2
|
+
import { valueToDynamicValue } from '../../utils.js';
|
|
3
|
+
export function verifyShieldedNullifiersProof(proof, nullifiers, verifySubsetOfProof, platformVersion) {
|
|
4
|
+
const result = dppProvider.dpp.verifyShieldedNullifiersProof(proof, nullifiers, verifySubsetOfProof, valueToDynamicValue(platformVersion));
|
|
5
|
+
return {
|
|
6
|
+
rootHash: result.rootHash,
|
|
7
|
+
nullifiers: result.nullifiers.map((nullifier) => ({
|
|
8
|
+
nullifier: nullifier.nullifier,
|
|
9
|
+
isSpent: nullifier.isSpent
|
|
10
|
+
}))
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { dppProvider } from '../../provider.js';
|
|
2
|
+
import { valueToDynamicValue } from '../../utils.js';
|
|
3
|
+
export function verifyShieldedPoolStateProof(proof, verifySubsetOfProof, platformVersion) {
|
|
4
|
+
const result = dppProvider.dpp.verifyShieldedPoolStateProof(proof, verifySubsetOfProof, valueToDynamicValue(platformVersion));
|
|
5
|
+
return {
|
|
6
|
+
rootHash: result.rootHash,
|
|
7
|
+
totalBalance: result.totalBalance != null ? BigInt(result.totalBalance) : undefined
|
|
8
|
+
};
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ShieldedMemoWASM,
|
|
3
|
+
OrchardAddressWASM,
|
|
4
|
+
orchardOvkFromSeed,
|
|
5
|
+
NoteWASM,
|
|
6
|
+
MerklePathWASM,
|
|
7
|
+
SpendableNoteWASM,
|
|
8
|
+
CommitmentTreeWASM,
|
|
9
|
+
ShieldedBuilderWASM,
|
|
10
|
+
OrchardProverWASM,
|
|
11
|
+
InputAddressWASM,
|
|
12
|
+
AddressFundsFeeStrategyStepWASM,
|
|
13
|
+
PrivateKeyWASM,
|
|
14
|
+
PlatformAddressWASM,
|
|
15
|
+
ShieldTransitionWASM,
|
|
16
|
+
StateTransitionWASM
|
|
17
|
+
} from 'pshenmic-dpp'
|
|
18
|
+
|
|
19
|
+
// Deterministic 64-byte BIP-39-style seed. Orchard derives via ZIP-32
|
|
20
|
+
// (m/32'/coinType'/account'), so any 64 bytes is a valid seed for derivation.
|
|
21
|
+
const SEED = new Uint8Array(64)
|
|
22
|
+
for (let i = 0; i < 64; i++) SEED[i] = (i * 7 + 1) & 0xff
|
|
23
|
+
|
|
24
|
+
const COIN_TYPE = 1 // testnets
|
|
25
|
+
const ACCOUNT = 0
|
|
26
|
+
|
|
27
|
+
// A funded testnet WIF reused across the builder/deposit tests.
|
|
28
|
+
const WIF = 'cUy4wbim4y9NDwC24omx8oWY5WqfmjSU2gdcZtTXza2xDCAkQXRP'
|
|
29
|
+
|
|
30
|
+
// Memo payloads are a fixed 32 bytes (MEMO_PAYLOAD_SIZE).
|
|
31
|
+
const MEMO_PAYLOAD_SIZE = 32
|
|
32
|
+
|
|
33
|
+
const DASH = 100_000_000_000n
|
|
34
|
+
|
|
35
|
+
function hexToBytes (hex: string): Uint8Array {
|
|
36
|
+
return Uint8Array.from(hex.match(/../g)!.map((x) => parseInt(x, 16)))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// A canonical Orchard note: rho is a valid Pallas base-field element (value 1),
|
|
40
|
+
// rseed is non-degenerate. Produces a valid commitment (cmx) the tree accepts.
|
|
41
|
+
function createTestNote (value: bigint = 1000n): NoteWASM {
|
|
42
|
+
const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
43
|
+
const rho = new Uint8Array(32)
|
|
44
|
+
rho[0] = 1
|
|
45
|
+
const rseed = new Uint8Array(32).fill(0x42)
|
|
46
|
+
return new NoteWASM(address, value, rho, rseed)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Platform P2PKH address (21 bytes: 0x00 type + 20-byte pubkey hash) for a key.
|
|
50
|
+
function platformAddressFromPrivateKey (pk: PrivateKeyWASM): PlatformAddressWASM {
|
|
51
|
+
const hash = hexToBytes(pk.getPublicKeyHash())
|
|
52
|
+
const bytes = new Uint8Array(21)
|
|
53
|
+
bytes[0] = 0x00
|
|
54
|
+
bytes.set(hash, 1)
|
|
55
|
+
return PlatformAddressWASM.fromBytes(bytes)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe('ShieldedMemo', function () {
|
|
59
|
+
describe('empty', function () {
|
|
60
|
+
test('should create an empty memo', function () {
|
|
61
|
+
const memo = ShieldedMemoWASM.empty()
|
|
62
|
+
|
|
63
|
+
expect(memo).toBeInstanceOf(ShieldedMemoWASM)
|
|
64
|
+
expect(memo.toBytes().length).toEqual(36) // on-wire memo size
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('fromString', function () {
|
|
69
|
+
test('should create a text memo from a 32-char string', function () {
|
|
70
|
+
const text = 'A'.repeat(MEMO_PAYLOAD_SIZE)
|
|
71
|
+
const memo = ShieldedMemoWASM.fromString(text)
|
|
72
|
+
|
|
73
|
+
expect(memo).toBeInstanceOf(ShieldedMemoWASM)
|
|
74
|
+
expect(memo.toBytes().length).toEqual(36)
|
|
75
|
+
expect(memo.toString().endsWith(text)).toBe(true)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('should reject a payload that is not 32 bytes', function () {
|
|
79
|
+
expect(() => ShieldedMemoWASM.fromString('too short')).toThrow()
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('other', function () {
|
|
84
|
+
test('should create a typed memo from a 32-byte payload', function () {
|
|
85
|
+
const payload = new Uint8Array(MEMO_PAYLOAD_SIZE).fill(9)
|
|
86
|
+
const memo = ShieldedMemoWASM.other(7, payload)
|
|
87
|
+
|
|
88
|
+
expect(memo).toBeInstanceOf(ShieldedMemoWASM)
|
|
89
|
+
expect(memo.toBytes().length).toEqual(36)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('should reject a payload that is not 32 bytes', function () {
|
|
93
|
+
expect(() => ShieldedMemoWASM.other(7, new Uint8Array([1, 2, 3]))).toThrow()
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe('OrchardAddress', function () {
|
|
99
|
+
describe('fromSeed', function () {
|
|
100
|
+
test('should derive an address from a seed', function () {
|
|
101
|
+
const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
102
|
+
|
|
103
|
+
expect(address).toBeInstanceOf(OrchardAddressWASM)
|
|
104
|
+
expect(address.bytes().length).toEqual(43)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('should be deterministic for the same seed', function () {
|
|
108
|
+
const a = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
109
|
+
const b = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
110
|
+
|
|
111
|
+
expect(a.bytes()).toEqual(b.bytes())
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
describe('serialization', function () {
|
|
116
|
+
test('should roundtrip through bytes', function () {
|
|
117
|
+
const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
118
|
+
const restored = OrchardAddressWASM.fromBytes(address.bytes())
|
|
119
|
+
|
|
120
|
+
expect(restored.bytes()).toEqual(address.bytes())
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('should roundtrip through bech32m', function () {
|
|
124
|
+
const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
125
|
+
const bech32m = address.toBech32m('testnet')
|
|
126
|
+
const restored = OrchardAddressWASM.fromBech32m(bech32m)
|
|
127
|
+
|
|
128
|
+
expect(restored.bytes()).toEqual(address.bytes())
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('orchardOvkFromSeed', function () {
|
|
133
|
+
test('should derive a 32-byte outgoing viewing key', function () {
|
|
134
|
+
const ovk = orchardOvkFromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
135
|
+
|
|
136
|
+
expect(ovk.length).toEqual(32)
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
describe('Note', function () {
|
|
142
|
+
describe('constructor', function () {
|
|
143
|
+
test('should create a note from valid parts', function () {
|
|
144
|
+
const note = createTestNote()
|
|
145
|
+
|
|
146
|
+
expect(note).toBeInstanceOf(NoteWASM)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test('should reject an invalid rho', function () {
|
|
150
|
+
const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
151
|
+
const invalidRho = new Uint8Array(32).fill(0xff) // not a canonical field element
|
|
152
|
+
const rseed = new Uint8Array(32).fill(0x42)
|
|
153
|
+
|
|
154
|
+
expect(() => new NoteWASM(address, 1000n, invalidRho, rseed)).toThrow()
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
describe('getters', function () {
|
|
159
|
+
test('should return all fields', function () {
|
|
160
|
+
const note = createTestNote(1234n)
|
|
161
|
+
|
|
162
|
+
expect(note.address).toBeInstanceOf(OrchardAddressWASM)
|
|
163
|
+
expect(note.value).toEqual(1234n)
|
|
164
|
+
expect(note.rho.length).toEqual(32)
|
|
165
|
+
expect(note.rseed.length).toEqual(32)
|
|
166
|
+
expect(note.cmx.length).toEqual(32)
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
describe('setters', function () {
|
|
171
|
+
test('should set the value', function () {
|
|
172
|
+
const note = createTestNote()
|
|
173
|
+
|
|
174
|
+
note.value = 5000n
|
|
175
|
+
expect(note.value).toEqual(5000n)
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
describe('CommitmentTree', function () {
|
|
181
|
+
describe('append / witness / anchor', function () {
|
|
182
|
+
test('should append a commitment and produce a witness and anchor', function () {
|
|
183
|
+
const note = createTestNote()
|
|
184
|
+
const tree = new CommitmentTreeWASM()
|
|
185
|
+
|
|
186
|
+
tree.append(note.cmx, true)
|
|
187
|
+
tree.checkpoint(1)
|
|
188
|
+
|
|
189
|
+
expect(tree.maxLeafPosition()).toEqual(0)
|
|
190
|
+
|
|
191
|
+
const path = tree.witness(0)
|
|
192
|
+
expect(path).toBeInstanceOf(MerklePathWASM)
|
|
193
|
+
expect(path!.authPath.length).toEqual(32)
|
|
194
|
+
|
|
195
|
+
expect(tree.anchor().length).toEqual(32)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('should reject a cmx that is not 32 bytes', function () {
|
|
199
|
+
const tree = new CommitmentTreeWASM()
|
|
200
|
+
|
|
201
|
+
expect(() => tree.append(new Uint8Array(16), true)).toThrow()
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
describe('MerklePath', function () {
|
|
207
|
+
let position: number
|
|
208
|
+
let authPath: Uint8Array[]
|
|
209
|
+
|
|
210
|
+
beforeAll(function () {
|
|
211
|
+
const note = createTestNote()
|
|
212
|
+
const tree = new CommitmentTreeWASM()
|
|
213
|
+
tree.append(note.cmx, true)
|
|
214
|
+
tree.checkpoint(1)
|
|
215
|
+
const path = tree.witness(0)!
|
|
216
|
+
position = path.position
|
|
217
|
+
authPath = path.authPath
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
describe('constructor', function () {
|
|
221
|
+
test('should create a MerklePath from parts', function () {
|
|
222
|
+
const path = new MerklePathWASM(position, authPath)
|
|
223
|
+
|
|
224
|
+
expect(path).toBeInstanceOf(MerklePathWASM)
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('getters', function () {
|
|
229
|
+
test('should return all fields', function () {
|
|
230
|
+
const path = new MerklePathWASM(position, authPath)
|
|
231
|
+
|
|
232
|
+
expect(path.position).toEqual(position)
|
|
233
|
+
expect(path.authPath.length).toEqual(32)
|
|
234
|
+
expect(path.authPath[0].length).toEqual(32)
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
describe('setters', function () {
|
|
239
|
+
test('should set the position', function () {
|
|
240
|
+
const path = new MerklePathWASM(position, authPath)
|
|
241
|
+
|
|
242
|
+
path.position = 0
|
|
243
|
+
expect(path.position).toEqual(0)
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
describe('SpendableNote', function () {
|
|
249
|
+
let note: NoteWASM
|
|
250
|
+
let merklePath: MerklePathWASM
|
|
251
|
+
|
|
252
|
+
beforeAll(function () {
|
|
253
|
+
note = createTestNote()
|
|
254
|
+
const tree = new CommitmentTreeWASM()
|
|
255
|
+
tree.append(note.cmx, true)
|
|
256
|
+
tree.checkpoint(1)
|
|
257
|
+
merklePath = tree.witness(0)!
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
describe('constructor', function () {
|
|
261
|
+
test('should create a SpendableNote from a note and witness', function () {
|
|
262
|
+
const spendable = new SpendableNoteWASM(note, merklePath)
|
|
263
|
+
|
|
264
|
+
expect(spendable).toBeInstanceOf(SpendableNoteWASM)
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
describe('getters', function () {
|
|
269
|
+
test('should return the note and merkle path', function () {
|
|
270
|
+
const spendable = new SpendableNoteWASM(note, merklePath)
|
|
271
|
+
|
|
272
|
+
expect(spendable.note).toBeInstanceOf(NoteWASM)
|
|
273
|
+
expect(spendable.note.value).toEqual(note.value)
|
|
274
|
+
expect(spendable.merklePath).toBeInstanceOf(MerklePathWASM)
|
|
275
|
+
expect(spendable.merklePath.position).toEqual(merklePath.position)
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
describe('setters', function () {
|
|
280
|
+
test('should set the note', function () {
|
|
281
|
+
const spendable = new SpendableNoteWASM(note, merklePath)
|
|
282
|
+
const newNote = createTestNote(2000n)
|
|
283
|
+
|
|
284
|
+
spendable.note = newNote
|
|
285
|
+
expect(spendable.note.value).toEqual(2000n)
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
describe('OrchardProver', function () {
|
|
291
|
+
// Constructing the prover builds the Halo 2 proving key (~seconds).
|
|
292
|
+
test('should construct a prover', function () {
|
|
293
|
+
const prover = new OrchardProverWASM()
|
|
294
|
+
|
|
295
|
+
expect(prover).toBeInstanceOf(OrchardProverWASM)
|
|
296
|
+
}, 30000)
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
describe('ShieldedBuilder', function () {
|
|
300
|
+
// The builder builds its own Halo 2 proving key on construction (~seconds),
|
|
301
|
+
// so build it once and reuse it across the deposit test.
|
|
302
|
+
let builder: ShieldedBuilderWASM
|
|
303
|
+
|
|
304
|
+
beforeAll(function () {
|
|
305
|
+
builder = new ShieldedBuilderWASM()
|
|
306
|
+
}, 30000)
|
|
307
|
+
|
|
308
|
+
describe('constructor', function () {
|
|
309
|
+
test('should construct a builder', function () {
|
|
310
|
+
expect(builder).toBeInstanceOf(ShieldedBuilderWASM)
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
describe('shield (transparent -> pool deposit)', function () {
|
|
315
|
+
test('should build and prove a shield transition', function () {
|
|
316
|
+
const recipient = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
317
|
+
const senderOvk = orchardOvkFromSeed(SEED, COIN_TYPE, ACCOUNT)
|
|
318
|
+
|
|
319
|
+
const privateKey = PrivateKeyWASM.fromWIF(WIF)
|
|
320
|
+
const inputAddress = platformAddressFromPrivateKey(privateKey)
|
|
321
|
+
// address, nonce, balance — balance must cover amount + fee.
|
|
322
|
+
const input = new InputAddressWASM(inputAddress, 0, DASH / 2n)
|
|
323
|
+
|
|
324
|
+
const st = builder.shield(
|
|
325
|
+
recipient,
|
|
326
|
+
(DASH * 4n) / 10n, // 0.4 DASH into the pool
|
|
327
|
+
[input],
|
|
328
|
+
[privateKey],
|
|
329
|
+
[AddressFundsFeeStrategyStepWASM.DeductFromInput(0)],
|
|
330
|
+
0, // user fee increase
|
|
331
|
+
ShieldedMemoWASM.empty(),
|
|
332
|
+
senderOvk
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
expect(st).toBeInstanceOf(StateTransitionWASM)
|
|
336
|
+
|
|
337
|
+
const transition = ShieldTransitionWASM.fromStateTransition(st)
|
|
338
|
+
expect(transition).toBeInstanceOf(ShieldTransitionWASM)
|
|
339
|
+
expect(transition.amount).toEqual((DASH * 4n) / 10n)
|
|
340
|
+
expect(transition.actions.length).toBeGreaterThanOrEqual(1)
|
|
341
|
+
expect(transition.proof.length).toBeGreaterThan(0)
|
|
342
|
+
}, 30000)
|
|
343
|
+
})
|
|
344
|
+
})
|