pshenmic-dpp 2.0.0-dev.8 → 2.0.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.
Files changed (161) hide show
  1. package/README.md +164 -77
  2. package/dist/binaries/bindingsTypes.d.ts +903 -18
  3. package/dist/binaries/bindingsTypes.js +13 -0
  4. package/dist/binaries/memory.cjs +7 -0
  5. package/dist/binaries/memory.d.cts +2 -0
  6. package/dist/binaries/native/aarch64-apple-darwin/pshenmic_dpp.node +0 -0
  7. package/dist/binaries/native/aarch64-pc-windows-msvc/pshenmic_dpp.node +0 -0
  8. package/dist/binaries/native/aarch64-unknown-linux-gnu/pshenmic_dpp.node +0 -0
  9. package/dist/binaries/native/aarch64-unknown-linux-musl/pshenmic_dpp.node +0 -0
  10. package/dist/binaries/native/x86_64-apple-darwin/pshenmic_dpp.node +0 -0
  11. package/dist/binaries/native/x86_64-pc-windows-msvc/pshenmic_dpp.node +0 -0
  12. package/dist/binaries/native/x86_64-unknown-linux-gnu/pshenmic_dpp.node +0 -0
  13. package/dist/binaries/native/x86_64-unknown-linux-musl/pshenmic_dpp.node +0 -0
  14. package/dist/binaries/node.cjs +71 -0
  15. package/dist/binaries/node.d.cts +2 -0
  16. package/dist/binaries/pshenmic_dpp.android.node/arm64-v8a/libpshenmic_dpp.so +0 -0
  17. package/dist/binaries/pshenmic_dpp.apple.node/Info.plist +29 -0
  18. package/dist/binaries/pshenmic_dpp.apple.node/ios-arm64-simulator/libpshenmic_dpp.framework/Info.plist +24 -0
  19. package/dist/binaries/pshenmic_dpp.apple.node/ios-arm64-simulator/libpshenmic_dpp.framework/libpshenmic_dpp +0 -0
  20. package/dist/binaries/pshenmic_dpp.apple.node/react-native-node-api-module +0 -0
  21. package/dist/binaries/pshenmic_dpp.d.ts +60 -0
  22. package/dist/binaries/pshenmic_dpp.js +1 -1
  23. package/dist/binaries/wasm/wasmBytes.cjs +4 -0
  24. package/dist/binaries/wasm/wasmBytes.d.cts +2 -0
  25. package/dist/binaries/wasm/workerBundle.cjs +6 -0
  26. package/dist/binaries/wasm/workerBundle.d.cts +2 -0
  27. package/dist/binaries/wasm.d.ts +60 -0
  28. package/dist/binaries/wasm.js +2 -44
  29. package/dist/binaries/wasmCreation.cjs +127 -0
  30. package/dist/binaries/wasmCreation.d.cts +4 -0
  31. package/dist/src/dpp/dpp.d.ts +33 -0
  32. package/dist/src/dpp/dpp.js +33 -0
  33. package/dist/src/dpp/enums.d.ts +1 -1
  34. package/dist/src/dpp/enums.js +1 -1
  35. package/dist/src/dpp/structs/AddressTransitions/AddressCreditWithdrawalTransition.d.ts +3 -1
  36. package/dist/src/dpp/structs/AddressTransitions/AddressCreditWithdrawalTransition.js +6 -0
  37. package/dist/src/dpp/structs/AddressTransitions/AddressFundingFromAssetLockTransition.d.ts +2 -1
  38. package/dist/src/dpp/structs/AddressTransitions/AddressFundingFromAssetLockTransition.js +3 -0
  39. package/dist/src/dpp/structs/AddressTransitions/AddressFundsTransferTransition.d.ts +3 -1
  40. package/dist/src/dpp/structs/AddressTransitions/AddressFundsTransferTransition.js +6 -0
  41. package/dist/src/dpp/structs/AddressTransitions/IdentityCreateFromAddressesTransition.d.ts +2 -1
  42. package/dist/src/dpp/structs/AddressTransitions/IdentityCreateFromAddressesTransition.js +3 -0
  43. package/dist/src/dpp/structs/AddressTransitions/IdentityCreditTransferToAddressesTransition.d.ts +2 -1
  44. package/dist/src/dpp/structs/AddressTransitions/IdentityCreditTransferToAddressesTransition.js +3 -0
  45. package/dist/src/dpp/structs/AddressTransitions/IdentityTopUpFromAddressesTransition.d.ts +2 -1
  46. package/dist/src/dpp/structs/AddressTransitions/IdentityTopUpFromAddressesTransition.js +3 -0
  47. package/dist/src/dpp/structs/Batch/BatchTransition.d.ts +2 -1
  48. package/dist/src/dpp/structs/Batch/BatchTransition.js +3 -0
  49. package/dist/src/dpp/structs/DataContractTransitions/DataContractCreateTransition.d.ts +2 -1
  50. package/dist/src/dpp/structs/DataContractTransitions/DataContractCreateTransition.js +3 -0
  51. package/dist/src/dpp/structs/DataContractTransitions/DataContractUpdateTransition.d.ts +2 -1
  52. package/dist/src/dpp/structs/DataContractTransitions/DataContractUpdateTransition.js +3 -0
  53. package/dist/src/dpp/structs/Identifier.js +3 -0
  54. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreateTransition.d.ts +2 -1
  55. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreateTransition.js +3 -0
  56. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreditTransfer.d.ts +2 -1
  57. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreditTransfer.js +3 -0
  58. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreditWithdrawalTransition.d.ts +2 -1
  59. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreditWithdrawalTransition.js +3 -0
  60. package/dist/src/dpp/structs/IdentityTransitions/IdentityTopUpTransition.d.ts +2 -1
  61. package/dist/src/dpp/structs/IdentityTransitions/IdentityTopUpTransition.js +3 -0
  62. package/dist/src/dpp/structs/IdentityTransitions/IdentityUpdateTransition.d.ts +2 -1
  63. package/dist/src/dpp/structs/IdentityTransitions/IdentityUpdateTransition.js +3 -0
  64. package/dist/src/dpp/structs/MasternodeVote/MasternodeVoteTransition.d.ts +2 -3
  65. package/dist/src/dpp/structs/MasternodeVote/MasternodeVoteTransition.js +3 -6
  66. package/dist/src/dpp/structs/Orchard/CommitmentTree.d.ts +14 -0
  67. package/dist/src/dpp/structs/Orchard/CommitmentTree.js +31 -0
  68. package/dist/src/dpp/structs/Orchard/FullViewingKey.d.ts +27 -0
  69. package/dist/src/dpp/structs/Orchard/FullViewingKey.js +45 -0
  70. package/dist/src/dpp/structs/Orchard/IdentityCreateFromShieldedPoolResult.d.ts +21 -0
  71. package/dist/src/dpp/structs/Orchard/IdentityCreateFromShieldedPoolResult.js +38 -0
  72. package/dist/src/dpp/structs/Orchard/IncomingViewingKey.d.ts +29 -0
  73. package/dist/src/dpp/structs/Orchard/IncomingViewingKey.js +44 -0
  74. package/dist/src/dpp/structs/Orchard/MerklePath.d.ts +11 -0
  75. package/dist/src/dpp/structs/Orchard/MerklePath.js +25 -0
  76. package/dist/src/dpp/structs/Orchard/Note.d.ts +18 -0
  77. package/dist/src/dpp/structs/Orchard/Note.js +42 -0
  78. package/dist/src/dpp/structs/Orchard/OrchardAddress.d.ts +27 -0
  79. package/dist/src/dpp/structs/Orchard/OrchardAddress.js +45 -0
  80. package/dist/src/dpp/structs/Orchard/OrchardProver.d.ts +19 -0
  81. package/dist/src/dpp/structs/Orchard/OrchardProver.js +34 -0
  82. package/dist/src/dpp/structs/Orchard/RecoveredNote.d.ts +19 -0
  83. package/dist/src/dpp/structs/Orchard/RecoveredNote.js +32 -0
  84. package/dist/src/dpp/structs/Orchard/ShieldedBuilder.d.ts +64 -0
  85. package/dist/src/dpp/structs/Orchard/ShieldedBuilder.js +79 -0
  86. package/dist/src/dpp/structs/Orchard/ShieldedMemo.d.ts +11 -0
  87. package/dist/src/dpp/structs/Orchard/ShieldedMemo.js +25 -0
  88. package/dist/src/dpp/structs/Orchard/ShieldedOutput.d.ts +23 -0
  89. package/dist/src/dpp/structs/Orchard/ShieldedOutput.js +41 -0
  90. package/dist/src/dpp/structs/Orchard/ShieldedWithdrawalResult.d.ts +11 -0
  91. package/dist/src/dpp/structs/Orchard/ShieldedWithdrawalResult.js +18 -0
  92. package/dist/src/dpp/structs/Orchard/SpendableNote.d.ts +13 -0
  93. package/dist/src/dpp/structs/Orchard/SpendableNote.js +27 -0
  94. package/dist/src/dpp/structs/PlatformAddress/PlatformAddress.d.ts +9 -1
  95. package/dist/src/dpp/structs/PlatformAddress/PlatformAddress.js +10 -0
  96. package/dist/src/dpp/structs/ShieldedTransitions/IdentityCreateFromShieldedPoolTransition.d.ts +32 -0
  97. package/dist/src/dpp/structs/ShieldedTransitions/IdentityCreateFromShieldedPoolTransition.js +76 -0
  98. package/dist/src/dpp/structs/ShieldedTransitions/SerializedAction.d.ts +19 -0
  99. package/dist/src/dpp/structs/ShieldedTransitions/SerializedAction.js +49 -0
  100. package/dist/src/dpp/structs/ShieldedTransitions/ShieldFromAssetLockTransition.d.ts +31 -0
  101. package/dist/src/dpp/structs/ShieldedTransitions/ShieldFromAssetLockTransition.js +80 -0
  102. package/dist/src/dpp/structs/ShieldedTransitions/ShieldTransition.d.ts +33 -0
  103. package/dist/src/dpp/structs/ShieldedTransitions/ShieldTransition.js +81 -0
  104. package/dist/src/dpp/structs/ShieldedTransitions/ShieldedTransferTransition.d.ts +22 -0
  105. package/dist/src/dpp/structs/ShieldedTransitions/ShieldedTransferTransition.js +54 -0
  106. package/dist/src/dpp/structs/ShieldedTransitions/ShieldedWithdrawalTransition.d.ts +30 -0
  107. package/dist/src/dpp/structs/ShieldedTransitions/ShieldedWithdrawalTransition.js +74 -0
  108. package/dist/src/dpp/structs/ShieldedTransitions/UnshieldTransition.d.ts +26 -0
  109. package/dist/src/dpp/structs/ShieldedTransitions/UnshieldTransition.js +62 -0
  110. package/dist/src/dpp/structs/StateTransition.d.ts +2 -1
  111. package/dist/src/dpp/structs/StateTransition.js +3 -0
  112. package/dist/src/dpp/types.d.ts +177 -3
  113. package/dist/src/dpp/verify/AddressFunds/utils.d.ts +5 -0
  114. package/dist/src/dpp/verify/AddressFunds/utils.js +28 -0
  115. package/dist/src/dpp/verify/AddressFunds/verifyAddressesBranchState.d.ts +12 -0
  116. package/dist/src/dpp/verify/AddressFunds/verifyAddressesBranchState.js +29 -0
  117. package/dist/src/dpp/verify/AddressFunds/verifyAddressesTrunkState.d.ts +9 -0
  118. package/dist/src/dpp/verify/AddressFunds/verifyAddressesTrunkState.js +30 -0
  119. package/dist/src/dpp/verify/AddressFunds/verifyRecentAddressBalanceChanges.d.ts +9 -0
  120. package/dist/src/dpp/verify/AddressFunds/verifyRecentAddressBalanceChanges.js +24 -0
  121. package/dist/src/dpp/verify/AddressFunds/verifyRecentCompactedAddressBalanceChanges.d.ts +10 -0
  122. package/dist/src/dpp/verify/AddressFunds/verifyRecentCompactedAddressBalanceChanges.js +30 -0
  123. package/dist/src/dpp/verify/Contested/verifyVotePollVoteStateProof.d.ts +1 -1
  124. package/dist/src/dpp/verify/Contested/verifyVotePollVoteStateProof.js +1 -1
  125. package/dist/src/dpp/verify/Shielded/verifyMostRecentShieldedAnchorProof.d.ts +2 -0
  126. package/dist/src/dpp/verify/Shielded/verifyMostRecentShieldedAnchorProof.js +9 -0
  127. package/dist/src/dpp/verify/Shielded/verifyShieldedAnchorsProof.d.ts +2 -0
  128. package/dist/src/dpp/verify/Shielded/verifyShieldedAnchorsProof.js +9 -0
  129. package/dist/src/dpp/verify/Shielded/verifyShieldedEncryptedNotesProof.d.ts +2 -0
  130. package/dist/src/dpp/verify/Shielded/verifyShieldedEncryptedNotesProof.js +15 -0
  131. package/dist/src/dpp/verify/Shielded/verifyShieldedNotesCountProof.d.ts +2 -0
  132. package/dist/src/dpp/verify/Shielded/verifyShieldedNotesCountProof.js +9 -0
  133. package/dist/src/dpp/verify/Shielded/verifyShieldedNullifiersProof.d.ts +2 -0
  134. package/dist/src/dpp/verify/Shielded/verifyShieldedNullifiersProof.js +12 -0
  135. package/dist/src/dpp/verify/Shielded/verifyShieldedPoolStateProof.d.ts +2 -0
  136. package/dist/src/dpp/verify/Shielded/verifyShieldedPoolStateProof.js +9 -0
  137. package/dist/src/dpp/verify/StateTransition/utils.js +76 -11
  138. package/dist/src/dpp/verify/StateTransition/verifyStateTransitionResult.js +2 -1
  139. package/dist/src/dpp/verify/System/verifyFinalizedEpochInfosProof.d.ts +2 -0
  140. package/dist/src/dpp/verify/System/verifyFinalizedEpochInfosProof.js +27 -0
  141. package/dist/src/native.d.ts +3 -0
  142. package/dist/src/native.js +8 -0
  143. package/dist/utils/{base122.js → base122.cjs} +3 -1
  144. package/package.json +19 -29
  145. package/tests/unit/AddressTransitionsFees.spec.ts +274 -0
  146. package/tests/unit/DataContract.spec.ts +281 -0
  147. package/tests/unit/DynamicValue.spec.ts +2 -2
  148. package/tests/unit/Identifier.spec.ts +1 -1
  149. package/tests/unit/Identity.spec.ts +1 -1
  150. package/tests/unit/IdentityPublicKey.spec.ts +1 -1
  151. package/tests/unit/NodeApi.spec.ts +49 -0
  152. package/tests/unit/Shielded.spec.ts +555 -0
  153. package/tests/unit/ShieldedTransitions.spec.ts +877 -0
  154. package/tests/unit/StateTransitionFees.spec.ts +177 -0
  155. package/tests/unit/TokenConfiguration.spec.ts +114 -0
  156. package/tests/unit/WebAssembly.spec.ts +88 -0
  157. package/tests/unit/utils/buildTokenConfiguration.ts +66 -0
  158. package/dist/binaries/pshenmic_dpp.android.node/x86_64/libpshenmic_dpp.so +0 -0
  159. package/dist/binaries/wasm/wasmBytes.d.ts +0 -2
  160. package/dist/binaries/wasm/wasmBytes.js +0 -2
  161. /package/dist/utils/{base122.d.ts → base122.d.cts} +0 -0
@@ -0,0 +1,555 @@
1
+ import {
2
+ ShieldedMemoWASM,
3
+ OrchardAddressWASM,
4
+ FullViewingKeyWASM,
5
+ IncomingViewingKeyWASM,
6
+ orchardOvkFromSeed,
7
+ NoteWASM,
8
+ MerklePathWASM,
9
+ SpendableNoteWASM,
10
+ CommitmentTreeWASM,
11
+ ShieldedBuilderWASM,
12
+ OrchardProverWASM,
13
+ InputAddressWASM,
14
+ AddressFundsFeeStrategyStepWASM,
15
+ PrivateKeyWASM,
16
+ PlatformAddressWASM,
17
+ ShieldTransitionWASM,
18
+ StateTransitionWASM
19
+ } from 'pshenmic-dpp'
20
+
21
+ // Deterministic 64-byte BIP-39-style seed. Orchard derives via ZIP-32
22
+ // (m/32'/coinType'/account'), so any 64 bytes is a valid seed for derivation.
23
+ const SEED = new Uint8Array(64)
24
+ for (let i = 0; i < 64; i++) SEED[i] = (i * 7 + 1) & 0xff
25
+
26
+ const COIN_TYPE = 1 // testnets
27
+ const ACCOUNT = 0
28
+
29
+ // A funded testnet WIF reused across the builder/deposit tests.
30
+ const WIF = 'cUy4wbim4y9NDwC24omx8oWY5WqfmjSU2gdcZtTXza2xDCAkQXRP'
31
+
32
+ // The memo payload field is a fixed 32 bytes (MEMO_PAYLOAD_SIZE) on the wire.
33
+ // Text memos may be shorter and are zero-padded to it; `other` payloads are
34
+ // taken verbatim and so must match it exactly.
35
+ const MEMO_PAYLOAD_SIZE = 32
36
+
37
+ const DASH = 100_000_000_000n
38
+
39
+ function hexToBytes (hex: string): Uint8Array {
40
+ return Uint8Array.from(hex.match(/../g)!.map((x) => parseInt(x, 16)))
41
+ }
42
+
43
+ // A canonical Orchard note: rho is a valid Pallas base-field element (value 1),
44
+ // rseed is non-degenerate. Produces a valid commitment (cmx) the tree accepts.
45
+ function createTestNote (value: bigint = 1000n): NoteWASM {
46
+ const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
47
+ const rho = new Uint8Array(32)
48
+ rho[0] = 1
49
+ const rseed = new Uint8Array(32).fill(0x42)
50
+ return new NoteWASM(address, value, rho, rseed)
51
+ }
52
+
53
+ // Platform P2PKH address (21 bytes: 0x00 type + 20-byte pubkey hash) for a key.
54
+ function platformAddressFromPrivateKey (pk: PrivateKeyWASM): PlatformAddressWASM {
55
+ const hash = hexToBytes(pk.getPublicKeyHash())
56
+ const bytes = new Uint8Array(21)
57
+ bytes[0] = 0x00
58
+ bytes.set(hash, 1)
59
+ return PlatformAddressWASM.fromBytes(bytes)
60
+ }
61
+
62
+ describe('ShieldedMemo', function () {
63
+ describe('empty', function () {
64
+ test('should create an empty memo', function () {
65
+ const memo = ShieldedMemoWASM.empty()
66
+
67
+ expect(memo).toBeInstanceOf(ShieldedMemoWASM)
68
+ expect(memo.toBytes().length).toEqual(36) // on-wire memo size
69
+ expect(memo.toString()).toEqual('')
70
+ })
71
+ })
72
+
73
+ describe('fromString', function () {
74
+ test('should create a text memo from a 32-char string', function () {
75
+ const text = 'A'.repeat(MEMO_PAYLOAD_SIZE)
76
+ const memo = ShieldedMemoWASM.fromString(text)
77
+
78
+ expect(memo).toBeInstanceOf(ShieldedMemoWASM)
79
+ expect(memo.toBytes().length).toEqual(36)
80
+ expect(memo.toString()).toEqual(text)
81
+ })
82
+
83
+ test('should accept text shorter than the payload', function () {
84
+ const text = 'thanks for lunch' // 16 bytes, half the payload
85
+ const memo = ShieldedMemoWASM.fromString(text)
86
+ const bytes = memo.toBytes()
87
+
88
+ // The wire memo stays a fixed 36 bytes: a 4-byte little-endian kind tag
89
+ // (1 = text), the utf-8 text, then zero padding out to the 32-byte
90
+ // payload. The padding is not part of the decoded text.
91
+ expect(bytes.length).toEqual(36)
92
+ expect(Array.from(bytes.slice(0, 4))).toEqual([1, 0, 0, 0])
93
+ expect(new TextDecoder().decode(bytes.slice(4, 4 + text.length))).toEqual(text)
94
+ expect(bytes.slice(4 + text.length).every((byte) => byte === 0)).toBe(true)
95
+ expect(memo.toString()).toEqual(text)
96
+ })
97
+
98
+ test('should accept an empty string', function () {
99
+ const memo = ShieldedMemoWASM.fromString('')
100
+
101
+ expect(memo.toBytes().length).toEqual(36)
102
+ expect(memo.toString()).toEqual('')
103
+ })
104
+
105
+ test('should count multi-byte characters by their utf-8 byte length', function () {
106
+ // 8 x U+1F355 = 32 bytes, exactly the payload; 9 would be 36 bytes.
107
+ const memo = ShieldedMemoWASM.fromString('🍕'.repeat(8))
108
+
109
+ expect(memo.toString()).toEqual('🍕'.repeat(8))
110
+ expect(() => ShieldedMemoWASM.fromString('🍕'.repeat(9))).toThrow()
111
+ })
112
+
113
+ test('should reject text longer than 32 bytes', function () {
114
+ expect(() => ShieldedMemoWASM.fromString('A'.repeat(MEMO_PAYLOAD_SIZE + 1))).toThrow()
115
+ })
116
+ })
117
+
118
+ describe('other', function () {
119
+ test('should create a typed memo from a 32-byte payload', function () {
120
+ const payload = new Uint8Array(MEMO_PAYLOAD_SIZE).fill(9)
121
+ const memo = ShieldedMemoWASM.other(7, payload)
122
+
123
+ expect(memo).toBeInstanceOf(ShieldedMemoWASM)
124
+ expect(memo.toBytes().length).toEqual(36)
125
+ })
126
+
127
+ test('should reject a payload that is not 32 bytes', function () {
128
+ expect(() => ShieldedMemoWASM.other(7, new Uint8Array([1, 2, 3]))).toThrow()
129
+ })
130
+ })
131
+ })
132
+
133
+ describe('OrchardAddress', function () {
134
+ describe('fromSeed', function () {
135
+ test('should derive an address from a seed', function () {
136
+ const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
137
+
138
+ expect(address).toBeInstanceOf(OrchardAddressWASM)
139
+ expect(address.bytes().length).toEqual(43)
140
+ })
141
+
142
+ test('should be deterministic for the same seed', function () {
143
+ const a = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
144
+ const b = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
145
+
146
+ expect(a.bytes()).toEqual(b.bytes())
147
+ })
148
+ })
149
+
150
+ describe('serialization', function () {
151
+ test('should roundtrip through bytes', function () {
152
+ const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
153
+ const restored = OrchardAddressWASM.fromBytes(address.bytes())
154
+
155
+ expect(restored.bytes()).toEqual(address.bytes())
156
+ })
157
+
158
+ test('should roundtrip through bech32m', function () {
159
+ const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
160
+ const bech32m = address.toBech32m('testnet')
161
+ const restored = OrchardAddressWASM.fromBech32m(bech32m)
162
+
163
+ expect(restored.bytes()).toEqual(address.bytes())
164
+ })
165
+ })
166
+
167
+ describe('orchardOvkFromSeed', function () {
168
+ test('should derive a 32-byte outgoing viewing key', function () {
169
+ const ovk = orchardOvkFromSeed(SEED, COIN_TYPE, ACCOUNT)
170
+
171
+ expect(ovk.length).toEqual(32)
172
+ })
173
+ })
174
+
175
+ describe('fromFullViewingKey', function () {
176
+ test('should match the address derived from the same seed', function () {
177
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
178
+
179
+ expect(OrchardAddressWASM.fromFullViewingKey(fvk).bytes())
180
+ .toEqual(OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT).bytes())
181
+ })
182
+
183
+ test('should derive a different address per diversifier index', function () {
184
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
185
+
186
+ expect(OrchardAddressWASM.fromFullViewingKey(fvk, 1).bytes())
187
+ .not.toEqual(OrchardAddressWASM.fromFullViewingKey(fvk, 0).bytes())
188
+ })
189
+
190
+ test('should derive a different address per scope', function () {
191
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
192
+
193
+ expect(OrchardAddressWASM.fromFullViewingKey(fvk, 0, 'internal').bytes())
194
+ .not.toEqual(OrchardAddressWASM.fromFullViewingKey(fvk, 0, 'external').bytes())
195
+ })
196
+
197
+ test('should reject an unknown scope', function () {
198
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
199
+
200
+ expect(() => OrchardAddressWASM.fromFullViewingKey(fvk, 0, 'nope' as any)).toThrow()
201
+ })
202
+ })
203
+
204
+ describe('fromIncomingViewingKey', function () {
205
+ test('should match the address derived from the same seed', function () {
206
+ const ivk = IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
207
+
208
+ expect(OrchardAddressWASM.fromIncomingViewingKey(ivk).bytes())
209
+ .toEqual(OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT).bytes())
210
+ })
211
+
212
+ test('should derive a different address per diversifier index', function () {
213
+ const ivk = IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
214
+
215
+ expect(OrchardAddressWASM.fromIncomingViewingKey(ivk, 7).bytes())
216
+ .not.toEqual(OrchardAddressWASM.fromIncomingViewingKey(ivk, 0).bytes())
217
+ })
218
+ })
219
+
220
+ describe('fromSeed scope', function () {
221
+ test('should derive the internal-scope address', function () {
222
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
223
+
224
+ expect(OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT, 0, 'internal').bytes())
225
+ .toEqual(OrchardAddressWASM.fromFullViewingKey(fvk, 0, 'internal').bytes())
226
+ })
227
+ })
228
+ })
229
+
230
+ describe('FullViewingKey', function () {
231
+ describe('fromSeed', function () {
232
+ test('should derive a 96-byte key deterministically', function () {
233
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
234
+
235
+ expect(fvk).toBeInstanceOf(FullViewingKeyWASM)
236
+ expect(fvk.bytes().length).toEqual(96)
237
+ expect(fvk.bytes()).toEqual(FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT).bytes())
238
+ })
239
+
240
+ test('should differ per account', function () {
241
+ expect(FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, 1).bytes())
242
+ .not.toEqual(FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, 0).bytes())
243
+ })
244
+
245
+ test('should reject a hardened account index', function () {
246
+ expect(() => FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, 0x80000000)).toThrow()
247
+ })
248
+ })
249
+
250
+ describe('serialization', function () {
251
+ test('should roundtrip through bytes', function () {
252
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
253
+
254
+ expect(FullViewingKeyWASM.fromBytes(fvk.bytes()).bytes()).toEqual(fvk.bytes())
255
+ })
256
+
257
+ test('should reject bytes of the wrong length', function () {
258
+ expect(() => FullViewingKeyWASM.fromBytes(new Uint8Array(64))).toThrow()
259
+ })
260
+
261
+ test('should reject bytes that are not a valid key', function () {
262
+ expect(() => FullViewingKeyWASM.fromBytes(new Uint8Array(96).fill(0xff))).toThrow()
263
+ })
264
+ })
265
+
266
+ describe('toOvk', function () {
267
+ test('should match orchardOvkFromSeed for the external scope', function () {
268
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
269
+
270
+ expect(fvk.toOvk()).toEqual(orchardOvkFromSeed(SEED, COIN_TYPE, ACCOUNT))
271
+ expect(fvk.toOvk('internal')).not.toEqual(fvk.toOvk('external'))
272
+ })
273
+ })
274
+
275
+ describe('toIvk', function () {
276
+ test('should derive the same key as IncomingViewingKey.fromSeed', function () {
277
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
278
+
279
+ expect(fvk.toIvk().bytes())
280
+ .toEqual(IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT).bytes())
281
+ expect(fvk.toIvk('internal').bytes()).not.toEqual(fvk.toIvk('external').bytes())
282
+ })
283
+ })
284
+
285
+ describe('scopeForAddress', function () {
286
+ test('should report the scope of its own addresses', function () {
287
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
288
+
289
+ expect(fvk.scopeForAddress(fvk.address(0, 'external'))).toEqual('External')
290
+ expect(fvk.scopeForAddress(fvk.address(0, 'internal'))).toEqual('Internal')
291
+ })
292
+
293
+ test('should return null for a foreign address', function () {
294
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
295
+ const other = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, 1)
296
+
297
+ expect(fvk.scopeForAddress(other.address())).toBeNull()
298
+ })
299
+ })
300
+ })
301
+
302
+ describe('IncomingViewingKey', function () {
303
+ describe('fromSeed', function () {
304
+ test('should derive a 64-byte key deterministically', function () {
305
+ const ivk = IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
306
+
307
+ expect(ivk).toBeInstanceOf(IncomingViewingKeyWASM)
308
+ expect(ivk.bytes().length).toEqual(64)
309
+ expect(ivk.bytes()).toEqual(IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT).bytes())
310
+ })
311
+ })
312
+
313
+ describe('fromFullViewingKey', function () {
314
+ test('should match the key derived from the same seed', function () {
315
+ const fvk = FullViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
316
+
317
+ expect(IncomingViewingKeyWASM.fromFullViewingKey(fvk).bytes())
318
+ .toEqual(IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT).bytes())
319
+ })
320
+ })
321
+
322
+ describe('serialization', function () {
323
+ test('should roundtrip through bytes', function () {
324
+ const ivk = IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
325
+
326
+ expect(IncomingViewingKeyWASM.fromBytes(ivk.bytes()).bytes()).toEqual(ivk.bytes())
327
+ })
328
+
329
+ test('should reject bytes of the wrong length', function () {
330
+ expect(() => IncomingViewingKeyWASM.fromBytes(new Uint8Array(96))).toThrow()
331
+ })
332
+ })
333
+
334
+ describe('diversifierIndex', function () {
335
+ test('should recover the index of its own address', function () {
336
+ const ivk = IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
337
+
338
+ expect(ivk.diversifierIndex(ivk.address(5))).toEqual(5)
339
+ })
340
+
341
+ test('should return null for a foreign address', function () {
342
+ const ivk = IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
343
+ const other = IncomingViewingKeyWASM.fromSeed(SEED, COIN_TYPE, 1)
344
+
345
+ expect(ivk.diversifierIndex(other.address())).toBeNull()
346
+ })
347
+ })
348
+ })
349
+
350
+ describe('Note', function () {
351
+ describe('constructor', function () {
352
+ test('should create a note from valid parts', function () {
353
+ const note = createTestNote()
354
+
355
+ expect(note).toBeInstanceOf(NoteWASM)
356
+ })
357
+
358
+ test('should reject an invalid rho', function () {
359
+ const address = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
360
+ const invalidRho = new Uint8Array(32).fill(0xff) // not a canonical field element
361
+ const rseed = new Uint8Array(32).fill(0x42)
362
+
363
+ expect(() => new NoteWASM(address, 1000n, invalidRho, rseed)).toThrow()
364
+ })
365
+ })
366
+
367
+ describe('getters', function () {
368
+ test('should return all fields', function () {
369
+ const note = createTestNote(1234n)
370
+
371
+ expect(note.address).toBeInstanceOf(OrchardAddressWASM)
372
+ expect(note.value).toEqual(1234n)
373
+ expect(note.rho.length).toEqual(32)
374
+ expect(note.rseed.length).toEqual(32)
375
+ expect(note.cmx.length).toEqual(32)
376
+ })
377
+ })
378
+
379
+ describe('setters', function () {
380
+ test('should set the value', function () {
381
+ const note = createTestNote()
382
+
383
+ note.value = 5000n
384
+ expect(note.value).toEqual(5000n)
385
+ })
386
+ })
387
+ })
388
+
389
+ describe('CommitmentTree', function () {
390
+ describe('append / witness / anchor', function () {
391
+ test('should append a commitment and produce a witness and anchor', function () {
392
+ const note = createTestNote()
393
+ const tree = new CommitmentTreeWASM()
394
+
395
+ tree.append(note.cmx, true)
396
+ tree.checkpoint(1)
397
+
398
+ expect(tree.maxLeafPosition()).toEqual(0)
399
+
400
+ const path = tree.witness(0)
401
+ expect(path).toBeInstanceOf(MerklePathWASM)
402
+ expect(path!.authPath.length).toEqual(32)
403
+
404
+ expect(tree.anchor().length).toEqual(32)
405
+ })
406
+
407
+ test('should reject a cmx that is not 32 bytes', function () {
408
+ const tree = new CommitmentTreeWASM()
409
+
410
+ expect(() => tree.append(new Uint8Array(16), true)).toThrow()
411
+ })
412
+ })
413
+ })
414
+
415
+ describe('MerklePath', function () {
416
+ let position: number
417
+ let authPath: Uint8Array[]
418
+
419
+ beforeAll(function () {
420
+ const note = createTestNote()
421
+ const tree = new CommitmentTreeWASM()
422
+ tree.append(note.cmx, true)
423
+ tree.checkpoint(1)
424
+ const path = tree.witness(0)!
425
+ position = path.position
426
+ authPath = path.authPath
427
+ })
428
+
429
+ describe('constructor', function () {
430
+ test('should create a MerklePath from parts', function () {
431
+ const path = new MerklePathWASM(position, authPath)
432
+
433
+ expect(path).toBeInstanceOf(MerklePathWASM)
434
+ })
435
+ })
436
+
437
+ describe('getters', function () {
438
+ test('should return all fields', function () {
439
+ const path = new MerklePathWASM(position, authPath)
440
+
441
+ expect(path.position).toEqual(position)
442
+ expect(path.authPath.length).toEqual(32)
443
+ expect(path.authPath[0].length).toEqual(32)
444
+ })
445
+ })
446
+
447
+ describe('setters', function () {
448
+ test('should set the position', function () {
449
+ const path = new MerklePathWASM(position, authPath)
450
+
451
+ path.position = 0
452
+ expect(path.position).toEqual(0)
453
+ })
454
+ })
455
+ })
456
+
457
+ describe('SpendableNote', function () {
458
+ let note: NoteWASM
459
+ let merklePath: MerklePathWASM
460
+
461
+ beforeAll(function () {
462
+ note = createTestNote()
463
+ const tree = new CommitmentTreeWASM()
464
+ tree.append(note.cmx, true)
465
+ tree.checkpoint(1)
466
+ merklePath = tree.witness(0)!
467
+ })
468
+
469
+ describe('constructor', function () {
470
+ test('should create a SpendableNote from a note and witness', function () {
471
+ const spendable = new SpendableNoteWASM(note, merklePath)
472
+
473
+ expect(spendable).toBeInstanceOf(SpendableNoteWASM)
474
+ })
475
+ })
476
+
477
+ describe('getters', function () {
478
+ test('should return the note and merkle path', function () {
479
+ const spendable = new SpendableNoteWASM(note, merklePath)
480
+
481
+ expect(spendable.note).toBeInstanceOf(NoteWASM)
482
+ expect(spendable.note.value).toEqual(note.value)
483
+ expect(spendable.merklePath).toBeInstanceOf(MerklePathWASM)
484
+ expect(spendable.merklePath.position).toEqual(merklePath.position)
485
+ })
486
+ })
487
+
488
+ describe('setters', function () {
489
+ test('should set the note', function () {
490
+ const spendable = new SpendableNoteWASM(note, merklePath)
491
+ const newNote = createTestNote(2000n)
492
+
493
+ spendable.note = newNote
494
+ expect(spendable.note.value).toEqual(2000n)
495
+ })
496
+ })
497
+ })
498
+
499
+ describe('OrchardProver', function () {
500
+ // Initializing the prover builds the Halo 2 proving key (~seconds).
501
+ test('should construct a prover', async function () {
502
+ const prover = new OrchardProverWASM()
503
+ await prover.init()
504
+
505
+ expect(prover).toBeInstanceOf(OrchardProverWASM)
506
+ }, 30000)
507
+ })
508
+
509
+ describe('ShieldedBuilder', function () {
510
+ // The builder builds its own Halo 2 proving key on init (~seconds),
511
+ // so build it once and reuse it across the deposit test.
512
+ let builder: ShieldedBuilderWASM
513
+
514
+ beforeAll(async function () {
515
+ builder = new ShieldedBuilderWASM()
516
+ await builder.init()
517
+ }, 30000)
518
+
519
+ describe('constructor', function () {
520
+ test('should construct a builder', function () {
521
+ expect(builder).toBeInstanceOf(ShieldedBuilderWASM)
522
+ })
523
+ })
524
+
525
+ describe('shield (transparent -> pool deposit)', function () {
526
+ test('should build and prove a shield transition', async function () {
527
+ const recipient = OrchardAddressWASM.fromSeed(SEED, COIN_TYPE, ACCOUNT)
528
+ const senderOvk = orchardOvkFromSeed(SEED, COIN_TYPE, ACCOUNT)
529
+
530
+ const privateKey = PrivateKeyWASM.fromWIF(WIF)
531
+ const inputAddress = platformAddressFromPrivateKey(privateKey)
532
+ // address, nonce, balance — balance must cover amount + fee.
533
+ const input = new InputAddressWASM(inputAddress, 0, DASH / 2n)
534
+
535
+ const st = await builder.shield(
536
+ recipient,
537
+ (DASH * 4n) / 10n, // 0.4 DASH into the pool
538
+ [input],
539
+ [privateKey],
540
+ [AddressFundsFeeStrategyStepWASM.DeductFromInput(0)],
541
+ 0, // user fee increase
542
+ ShieldedMemoWASM.empty(),
543
+ senderOvk
544
+ )
545
+
546
+ expect(st).toBeInstanceOf(StateTransitionWASM)
547
+
548
+ const transition = ShieldTransitionWASM.fromStateTransition(st)
549
+ expect(transition).toBeInstanceOf(ShieldTransitionWASM)
550
+ expect(transition.amount).toEqual((DASH * 4n) / 10n)
551
+ expect(transition.actions.length).toBeGreaterThanOrEqual(1)
552
+ expect(transition.proof.length).toBeGreaterThan(0)
553
+ }, 30000)
554
+ })
555
+ })