pshenmic-dpp 2.0.0-dev.26 → 2.0.0-dev.28

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 (69) hide show
  1. package/dist/binaries/bindingsTypes.d.ts +266 -0
  2. package/dist/binaries/native/aarch64-apple-darwin/pshenmic_dpp.node +0 -0
  3. package/dist/binaries/native/aarch64-pc-windows-msvc/pshenmic_dpp.node +0 -0
  4. package/dist/binaries/native/aarch64-unknown-linux-gnu/pshenmic_dpp.node +0 -0
  5. package/dist/binaries/native/aarch64-unknown-linux-musl/pshenmic_dpp.node +0 -0
  6. package/dist/binaries/native/x86_64-apple-darwin/pshenmic_dpp.node +0 -0
  7. package/dist/binaries/native/x86_64-pc-windows-msvc/pshenmic_dpp.node +0 -0
  8. package/dist/binaries/native/x86_64-unknown-linux-gnu/pshenmic_dpp.node +0 -0
  9. package/dist/binaries/native/x86_64-unknown-linux-musl/pshenmic_dpp.node +0 -0
  10. package/dist/binaries/node.cjs +42 -21
  11. package/dist/binaries/node.d.cts +2 -2
  12. package/dist/binaries/pshenmic_dpp.android.node/arm64-v8a/libpshenmic_dpp.so +0 -0
  13. package/dist/binaries/pshenmic_dpp.apple.node/ios-arm64-simulator/libpshenmic_dpp.framework/libpshenmic_dpp +0 -0
  14. package/dist/binaries/pshenmic_dpp.d.ts +17 -0
  15. package/dist/binaries/pshenmic_dpp.js +1 -1
  16. package/dist/binaries/wasm/wasmBytes.cjs +1 -1
  17. package/dist/binaries/wasm.d.ts +17 -0
  18. package/dist/binaries/wasm.js +1 -1
  19. package/dist/src/dpp/dpp.d.ts +4 -0
  20. package/dist/src/dpp/dpp.js +4 -0
  21. package/dist/src/dpp/structs/AddressTransitions/AddressCreditWithdrawalTransition.d.ts +3 -1
  22. package/dist/src/dpp/structs/AddressTransitions/AddressCreditWithdrawalTransition.js +6 -0
  23. package/dist/src/dpp/structs/AddressTransitions/AddressFundingFromAssetLockTransition.d.ts +2 -1
  24. package/dist/src/dpp/structs/AddressTransitions/AddressFundingFromAssetLockTransition.js +3 -0
  25. package/dist/src/dpp/structs/AddressTransitions/AddressFundsTransferTransition.d.ts +3 -1
  26. package/dist/src/dpp/structs/AddressTransitions/AddressFundsTransferTransition.js +6 -0
  27. package/dist/src/dpp/structs/AddressTransitions/IdentityCreateFromAddressesTransition.d.ts +2 -1
  28. package/dist/src/dpp/structs/AddressTransitions/IdentityCreateFromAddressesTransition.js +3 -0
  29. package/dist/src/dpp/structs/AddressTransitions/IdentityCreditTransferToAddressesTransition.d.ts +2 -1
  30. package/dist/src/dpp/structs/AddressTransitions/IdentityCreditTransferToAddressesTransition.js +3 -0
  31. package/dist/src/dpp/structs/AddressTransitions/IdentityTopUpFromAddressesTransition.d.ts +2 -1
  32. package/dist/src/dpp/structs/AddressTransitions/IdentityTopUpFromAddressesTransition.js +3 -0
  33. package/dist/src/dpp/structs/Batch/BatchTransition.d.ts +2 -1
  34. package/dist/src/dpp/structs/Batch/BatchTransition.js +3 -0
  35. package/dist/src/dpp/structs/DataContractTransitions/DataContractCreateTransition.d.ts +2 -1
  36. package/dist/src/dpp/structs/DataContractTransitions/DataContractCreateTransition.js +3 -0
  37. package/dist/src/dpp/structs/DataContractTransitions/DataContractUpdateTransition.d.ts +2 -1
  38. package/dist/src/dpp/structs/DataContractTransitions/DataContractUpdateTransition.js +3 -0
  39. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreateTransition.d.ts +2 -1
  40. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreateTransition.js +3 -0
  41. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreditTransfer.d.ts +2 -1
  42. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreditTransfer.js +3 -0
  43. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreditWithdrawalTransition.d.ts +2 -1
  44. package/dist/src/dpp/structs/IdentityTransitions/IdentityCreditWithdrawalTransition.js +3 -0
  45. package/dist/src/dpp/structs/IdentityTransitions/IdentityTopUpTransition.d.ts +2 -1
  46. package/dist/src/dpp/structs/IdentityTransitions/IdentityTopUpTransition.js +3 -0
  47. package/dist/src/dpp/structs/IdentityTransitions/IdentityUpdateTransition.d.ts +2 -1
  48. package/dist/src/dpp/structs/IdentityTransitions/IdentityUpdateTransition.js +3 -0
  49. package/dist/src/dpp/structs/MasternodeVote/MasternodeVoteTransition.d.ts +2 -1
  50. package/dist/src/dpp/structs/MasternodeVote/MasternodeVoteTransition.js +3 -0
  51. package/dist/src/dpp/structs/PlatformAddress/PlatformAddress.d.ts +9 -1
  52. package/dist/src/dpp/structs/PlatformAddress/PlatformAddress.js +10 -0
  53. package/dist/src/dpp/structs/StateTransition.d.ts +2 -1
  54. package/dist/src/dpp/structs/StateTransition.js +3 -0
  55. package/dist/src/dpp/types.d.ts +82 -0
  56. package/dist/src/dpp/verify/AddressFunds/utils.d.ts +5 -0
  57. package/dist/src/dpp/verify/AddressFunds/utils.js +28 -0
  58. package/dist/src/dpp/verify/AddressFunds/verifyAddressesBranchState.d.ts +12 -0
  59. package/dist/src/dpp/verify/AddressFunds/verifyAddressesBranchState.js +29 -0
  60. package/dist/src/dpp/verify/AddressFunds/verifyAddressesTrunkState.d.ts +9 -0
  61. package/dist/src/dpp/verify/AddressFunds/verifyAddressesTrunkState.js +30 -0
  62. package/dist/src/dpp/verify/AddressFunds/verifyRecentAddressBalanceChanges.d.ts +9 -0
  63. package/dist/src/dpp/verify/AddressFunds/verifyRecentAddressBalanceChanges.js +24 -0
  64. package/dist/src/dpp/verify/AddressFunds/verifyRecentCompactedAddressBalanceChanges.d.ts +10 -0
  65. package/dist/src/dpp/verify/AddressFunds/verifyRecentCompactedAddressBalanceChanges.js +30 -0
  66. package/package.json +1 -1
  67. package/tests/unit/AddressTransitionsFees.spec.ts +274 -0
  68. package/tests/unit/Shielded.spec.ts +37 -4
  69. package/tests/unit/StateTransitionFees.spec.ts +177 -0
@@ -373,3 +373,85 @@ export interface VerifiedShieldedNullifiers {
373
373
  rootHash: Uint8Array;
374
374
  nullifiers: VerifiedShieldedNullifier[];
375
375
  }
376
+ export type AddressBalanceOperation = 'setCredits' | 'addToCredits';
377
+ export interface AddressBalanceChange {
378
+ address: PlatformAddressWASM;
379
+ /** `setCredits` means `credits` is the new balance, `addToCredits` means it is the delta. */
380
+ operation: AddressBalanceOperation;
381
+ credits: bigint;
382
+ }
383
+ export interface BlockAddressBalanceChanges {
384
+ blockHeight: bigint;
385
+ changes: AddressBalanceChange[];
386
+ }
387
+ export interface VerifiedRecentAddressBalanceChanges {
388
+ rootHash: Uint8Array;
389
+ blocks: BlockAddressBalanceChanges[];
390
+ }
391
+ export type CompactedAddressBalanceOperation = 'setCredits' | 'addToCreditsOperations';
392
+ export interface BlockHeightCredit {
393
+ blockHeight: bigint;
394
+ credits: bigint;
395
+ }
396
+ export interface CompactedAddressBalanceChange {
397
+ address: PlatformAddressWASM;
398
+ operation: CompactedAddressBalanceOperation;
399
+ /** Populated when `operation` is `setCredits`. */
400
+ setCredits?: bigint;
401
+ /** Populated when `operation` is `addToCreditsOperations`, otherwise empty. */
402
+ addToCreditsOperations: BlockHeightCredit[];
403
+ }
404
+ export interface CompactedBlockAddressBalanceChanges {
405
+ startBlockHeight: bigint;
406
+ endBlockHeight: bigint;
407
+ changes: CompactedAddressBalanceChange[];
408
+ }
409
+ export interface VerifiedRecentCompactedAddressBalanceChanges {
410
+ rootHash: Uint8Array;
411
+ ranges: CompactedBlockAddressBalanceChanges[];
412
+ }
413
+ export type GroveElementType = 'Item' | 'ItemWithSumItem' | 'SumItem' | 'Reference' | 'ReferenceWithSumItem' | 'Tree' | 'SumTree' | 'BigSumTree' | 'ProvableSumTree' | 'CountTree' | 'ProvableCountTree' | 'CountSumTree' | 'ProvableCountSumTree' | 'ProvableCountProvableSumTree' | 'CommitmentTree' | 'MmrTree' | 'BulkAppendTree' | 'DenseAppendOnlyFixedSizeTree';
414
+ export type GroveElementWrapper = 'NonCounted' | 'NotSummed' | 'NotCountedOrSummed';
415
+ export interface GroveElement {
416
+ type: GroveElementType;
417
+ /** Set when the element opts out of its parent's aggregates; `type` still describes the wrapped element. */
418
+ wrapper?: GroveElementWrapper;
419
+ /** An item's payload, or a subtree's root key. Absent for empty subtrees. */
420
+ value?: Uint8Array;
421
+ sum?: bigint;
422
+ count?: bigint;
423
+ }
424
+ export interface GroveElementEntry {
425
+ key: Uint8Array;
426
+ element: GroveElement;
427
+ }
428
+ export interface GroveLeafInfo {
429
+ key: Uint8Array;
430
+ /** Pass as `expectedRootHash` when verifying this key's branch proof. */
431
+ hash: Uint8Array;
432
+ count?: bigint;
433
+ }
434
+ export interface GroveAncestor {
435
+ levelsUp: number;
436
+ count: bigint;
437
+ key: Uint8Array;
438
+ hash: Uint8Array;
439
+ }
440
+ export interface VerifiedAddressesTrunkState {
441
+ rootHash: Uint8Array;
442
+ elements: GroveElementEntry[];
443
+ /** Subtrees the trunk stops at. Empty when the whole tree fit in the trunk. */
444
+ leafKeys: GroveLeafInfo[];
445
+ chunkDepths: number[];
446
+ maxTreeDepth: number;
447
+ /** Which truncated subtree would hold `key`, or undefined if it is already in `elements`. */
448
+ traceKeyToLeaf: (key: Uint8Array) => GroveLeafInfo | undefined;
449
+ getAncestor: (leafKey: Uint8Array, minPrivacyTreeCount: bigint) => GroveAncestor | undefined;
450
+ }
451
+ export interface VerifiedAddressesBranchState {
452
+ elements: GroveElementEntry[];
453
+ leafKeys: GroveLeafInfo[];
454
+ branchRootHash: Uint8Array;
455
+ traceKeyToLeaf: (key: Uint8Array) => GroveLeafInfo | undefined;
456
+ getAncestor: (leafKey: Uint8Array, minPrivacyTreeCount: bigint) => GroveAncestor | undefined;
457
+ }
@@ -0,0 +1,5 @@
1
+ import type * as protocol from '../../../../binaries/bindingsTypes.js';
2
+ import { GroveAncestor, GroveElementEntry, GroveLeafInfo } from '../../types.js';
3
+ export declare function mapGroveElementEntry(entry: protocol.GroveElementEntryNAPI): GroveElementEntry;
4
+ export declare function mapGroveLeafInfo(info: protocol.GroveLeafInfoNAPI): GroveLeafInfo;
5
+ export declare function mapGroveAncestor(ancestor: protocol.GroveAncestorNAPI): GroveAncestor;
@@ -0,0 +1,28 @@
1
+ export function mapGroveElementEntry(entry) {
2
+ const { element } = entry;
3
+ return {
4
+ key: entry.key,
5
+ element: {
6
+ type: element.type,
7
+ wrapper: element.wrapper != null ? element.wrapper : undefined,
8
+ value: element.value ?? undefined,
9
+ sum: element.sum != null ? BigInt(element.sum) : undefined,
10
+ count: element.count != null ? BigInt(element.count) : undefined
11
+ }
12
+ };
13
+ }
14
+ export function mapGroveLeafInfo(info) {
15
+ return {
16
+ key: info.key,
17
+ hash: info.hash,
18
+ count: info.count != null ? BigInt(info.count) : undefined
19
+ };
20
+ }
21
+ export function mapGroveAncestor(ancestor) {
22
+ return {
23
+ levelsUp: ancestor.levelsUp,
24
+ count: BigInt(ancestor.count),
25
+ key: ancestor.key,
26
+ hash: ancestor.hash
27
+ };
28
+ }
@@ -0,0 +1,12 @@
1
+ import { PlatformVersionLike, VerifiedAddressesBranchState } from '../../types.js';
2
+ /**
3
+ * Verifies a branch chunk proof for the address funds tree.
4
+ *
5
+ * `expectedRootHash` must be the `hash` the parent trunk or branch reported for
6
+ * `key`; it is what anchors this subtree to the proven root, so the result
7
+ * carries no root hash of its own.
8
+ *
9
+ * `depth` is validated against an allowed range, so take it from the parent
10
+ * trunk's `chunkDepths` rather than picking a value.
11
+ */
12
+ export declare function verifyAddressesBranchState(proof: Uint8Array, key: Uint8Array, depth: number, expectedRootHash: Uint8Array, platformVersion: PlatformVersionLike): VerifiedAddressesBranchState;
@@ -0,0 +1,29 @@
1
+ import { dppProvider } from '../../provider.js';
2
+ import { valueToDynamicValue } from '../../utils.js';
3
+ import { mapGroveAncestor, mapGroveElementEntry, mapGroveLeafInfo } from './utils.js';
4
+ /**
5
+ * Verifies a branch chunk proof for the address funds tree.
6
+ *
7
+ * `expectedRootHash` must be the `hash` the parent trunk or branch reported for
8
+ * `key`; it is what anchors this subtree to the proven root, so the result
9
+ * carries no root hash of its own.
10
+ *
11
+ * `depth` is validated against an allowed range, so take it from the parent
12
+ * trunk's `chunkDepths` rather than picking a value.
13
+ */
14
+ export function verifyAddressesBranchState(proof, key, depth, expectedRootHash, platformVersion) {
15
+ const result = dppProvider.dpp.verifyAddressesBranchState(proof, key, depth, expectedRootHash, valueToDynamicValue(platformVersion));
16
+ return {
17
+ elements: result.elements.map(mapGroveElementEntry),
18
+ leafKeys: result.leafKeys.map(mapGroveLeafInfo),
19
+ branchRootHash: result.branchRootHash,
20
+ traceKeyToLeaf: (traced) => {
21
+ const leaf = result.traceKeyToLeaf(traced);
22
+ return leaf != null ? mapGroveLeafInfo(leaf) : undefined;
23
+ },
24
+ getAncestor: (leafKey, minPrivacyTreeCount) => {
25
+ const ancestor = result.getAncestor(leafKey, minPrivacyTreeCount.toString());
26
+ return ancestor != null ? mapGroveAncestor(ancestor) : undefined;
27
+ }
28
+ };
29
+ }
@@ -0,0 +1,9 @@
1
+ import { PlatformVersionLike, VerifiedAddressesTrunkState } from '../../types.js';
2
+ /**
3
+ * Verifies a trunk chunk proof for the address funds tree.
4
+ *
5
+ * The trunk covers the top of the tree only. Subtrees below it are truncated
6
+ * and listed in `leafKeys`; use `traceKeyToLeaf` to find which one holds an
7
+ * address, then fetch it with `verifyAddressesBranchState`.
8
+ */
9
+ export declare function verifyAddressesTrunkState(proof: Uint8Array, platformVersion: PlatformVersionLike): VerifiedAddressesTrunkState;
@@ -0,0 +1,30 @@
1
+ import { dppProvider } from '../../provider.js';
2
+ import { valueToDynamicValue } from '../../utils.js';
3
+ import { mapGroveAncestor, mapGroveElementEntry, mapGroveLeafInfo } from './utils.js';
4
+ /**
5
+ * Verifies a trunk chunk proof for the address funds tree.
6
+ *
7
+ * The trunk covers the top of the tree only. Subtrees below it are truncated
8
+ * and listed in `leafKeys`; use `traceKeyToLeaf` to find which one holds an
9
+ * address, then fetch it with `verifyAddressesBranchState`.
10
+ */
11
+ export function verifyAddressesTrunkState(proof, platformVersion) {
12
+ const result = dppProvider.dpp.verifyAddressesTrunkState(proof, valueToDynamicValue(platformVersion));
13
+ return {
14
+ rootHash: result.rootHash,
15
+ elements: result.elements.map(mapGroveElementEntry),
16
+ leafKeys: result.leafKeys.map(mapGroveLeafInfo),
17
+ chunkDepths: result.chunkDepths,
18
+ maxTreeDepth: result.maxTreeDepth,
19
+ // These close over the verified result because tracing walks the proof's
20
+ // reconstructed tree, which is not projected into plain data.
21
+ traceKeyToLeaf: (key) => {
22
+ const leaf = result.traceKeyToLeaf(key);
23
+ return leaf != null ? mapGroveLeafInfo(leaf) : undefined;
24
+ },
25
+ getAncestor: (leafKey, minPrivacyTreeCount) => {
26
+ const ancestor = result.getAncestor(leafKey, minPrivacyTreeCount.toString());
27
+ return ancestor != null ? mapGroveAncestor(ancestor) : undefined;
28
+ }
29
+ };
30
+ }
@@ -0,0 +1,9 @@
1
+ import { PlatformVersionLike, VerifiedRecentAddressBalanceChanges } from '../../types.js';
2
+ /**
3
+ * Verifies a proof of the address balance changes from a block height onwards.
4
+ *
5
+ * `startHeightExclusive` must match the flag the proof was requested with: it
6
+ * selects a range starting just past `startBlockHeight` rather than at it, and
7
+ * the two produce different queries, so a mismatch fails verification.
8
+ */
9
+ export declare function verifyRecentAddressBalanceChanges(proof: Uint8Array, startBlockHeight: bigint, startHeightExclusive: boolean, limit: number | undefined, verifySubsetOfProof: boolean, platformVersion: PlatformVersionLike): VerifiedRecentAddressBalanceChanges;
@@ -0,0 +1,24 @@
1
+ import { dppProvider } from '../../provider.js';
2
+ import { valueToDynamicValue } from '../../utils.js';
3
+ import { PlatformAddressWASM } from '../../structs/PlatformAddress/PlatformAddress.js';
4
+ /**
5
+ * Verifies a proof of the address balance changes from a block height onwards.
6
+ *
7
+ * `startHeightExclusive` must match the flag the proof was requested with: it
8
+ * selects a range starting just past `startBlockHeight` rather than at it, and
9
+ * the two produce different queries, so a mismatch fails verification.
10
+ */
11
+ export function verifyRecentAddressBalanceChanges(proof, startBlockHeight, startHeightExclusive, limit, verifySubsetOfProof, platformVersion) {
12
+ const result = dppProvider.dpp.verifyRecentAddressBalanceChanges(proof, startBlockHeight.toString(), startHeightExclusive, limit, verifySubsetOfProof, valueToDynamicValue(platformVersion));
13
+ return {
14
+ rootHash: result.rootHash,
15
+ blocks: result.blocks.map((block) => ({
16
+ blockHeight: BigInt(block.blockHeight),
17
+ changes: block.changes.map((change) => ({
18
+ address: PlatformAddressWASM.createFromRawInstance(change.address),
19
+ operation: change.operation,
20
+ credits: BigInt(change.credits)
21
+ }))
22
+ }))
23
+ };
24
+ }
@@ -0,0 +1,10 @@
1
+ import { PlatformVersionLike, VerifiedRecentCompactedAddressBalanceChanges } from '../../types.js';
2
+ /**
3
+ * Verifies a proof of the compacted address balance changes from a block height
4
+ * onwards, where each entry merges a range of blocks.
5
+ *
6
+ * There is no subset flag here: the proof is a self-contained envelope whose
7
+ * predecessor half authenticates which range covers the requested height, so
8
+ * the forward half has nothing to be a subset of.
9
+ */
10
+ export declare function verifyRecentCompactedAddressBalanceChanges(proof: Uint8Array, startBlockHeight: bigint, limit: number | undefined, platformVersion: PlatformVersionLike): VerifiedRecentCompactedAddressBalanceChanges;
@@ -0,0 +1,30 @@
1
+ import { dppProvider } from '../../provider.js';
2
+ import { valueToDynamicValue } from '../../utils.js';
3
+ import { PlatformAddressWASM } from '../../structs/PlatformAddress/PlatformAddress.js';
4
+ /**
5
+ * Verifies a proof of the compacted address balance changes from a block height
6
+ * onwards, where each entry merges a range of blocks.
7
+ *
8
+ * There is no subset flag here: the proof is a self-contained envelope whose
9
+ * predecessor half authenticates which range covers the requested height, so
10
+ * the forward half has nothing to be a subset of.
11
+ */
12
+ export function verifyRecentCompactedAddressBalanceChanges(proof, startBlockHeight, limit, platformVersion) {
13
+ const result = dppProvider.dpp.verifyRecentCompactedAddressBalanceChanges(proof, startBlockHeight.toString(), limit, valueToDynamicValue(platformVersion));
14
+ return {
15
+ rootHash: result.rootHash,
16
+ ranges: result.ranges.map((range) => ({
17
+ startBlockHeight: BigInt(range.startBlockHeight),
18
+ endBlockHeight: BigInt(range.endBlockHeight),
19
+ changes: range.changes.map((change) => ({
20
+ address: PlatformAddressWASM.createFromRawInstance(change.address),
21
+ operation: change.operation,
22
+ setCredits: change.setCredits != null ? BigInt(change.setCredits) : undefined,
23
+ addToCreditsOperations: change.addToCreditsOperations.map((operation) => ({
24
+ blockHeight: BigInt(operation.blockHeight),
25
+ credits: BigInt(operation.credits)
26
+ }))
27
+ }))
28
+ }))
29
+ };
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pshenmic-dpp",
3
- "version": "2.0.0-dev.26",
3
+ "version": "2.0.0-dev.28",
4
4
  "main": "dist/src/wasm.js",
5
5
  "types": "dist/src/wasm.d.ts",
6
6
  "react-native": "./dist/src/react-native.js",
@@ -0,0 +1,274 @@
1
+ import {
2
+ AddressCreditWithdrawalTransitionWASM,
3
+ AddressFundingFromAssetLockTransitionWASM,
4
+ AddressFundsFeeStrategyStepWASM,
5
+ AddressFundsTransferTransitionWASM,
6
+ AddressWitnessWASM,
7
+ AssetLockProofWASM,
8
+ CoreScriptWASM,
9
+ IdentityCreateFromAddressesTransitionWASM,
10
+ IdentityCreditTransferToAddressesTransitionWASM,
11
+ IdentityPublicKeyInCreationWASM,
12
+ IdentityTopUpFromAddressesTransitionWASM,
13
+ InputAddressWASM,
14
+ KeyType,
15
+ OutPointWASM,
16
+ OutputAddressNullableCreditsWASM,
17
+ OutputAddressWASM,
18
+ PlatformAddressWASM,
19
+ PlatformVersionWASM,
20
+ Purpose,
21
+ SecurityLevel
22
+ } from 'pshenmic-dpp'
23
+
24
+ // Min fee components from the platform fee version (STATE_TRANSITION_MIN_FEES_VERSION1)
25
+ // Storage charged by the node for one address balance entry that does not exist yet,
26
+ // as pinned by platform's own fee regression suite.
27
+ const NEW_ADDRESS_STORAGE_FEE = BigInt(6075000)
28
+ const INPUT_COST = BigInt(500000)
29
+ const OUTPUT_COST = BigInt(6000000)
30
+ const ADDRESS_CREDIT_WITHDRAWAL_COST = BigInt(400000000)
31
+ const IDENTITY_CREATE_BASE_COST = BigInt(2000000)
32
+ const IDENTITY_KEY_IN_CREATION_COST = BigInt(6500000)
33
+ const IDENTITY_TOPUP_BASE_COST = BigInt(500000)
34
+ const CREDIT_TRANSFER_TO_ADDRESSES_COST = BigInt(500000)
35
+
36
+ function randomBytes (length: number): Uint8Array {
37
+ const bytes = new Uint8Array(length)
38
+ for (let i = 0; i < length; i++) {
39
+ bytes[i] = Math.floor(Math.random() * 256)
40
+ }
41
+ return bytes
42
+ }
43
+
44
+ function createTestAddress (): PlatformAddressWASM {
45
+ const addressBytes = new Uint8Array(21)
46
+ addressBytes[0] = 0x00
47
+ addressBytes.set(randomBytes(20), 1)
48
+ return PlatformAddressWASM.fromBytes(addressBytes)
49
+ }
50
+
51
+ function createTestInputs (count: number): InputAddressWASM[] {
52
+ return Array.from(
53
+ { length: count },
54
+ () => new InputAddressWASM(createTestAddress(), 0, BigInt(500000000))
55
+ )
56
+ }
57
+
58
+ function createTestOutputs (count: number): OutputAddressWASM[] {
59
+ return Array.from(
60
+ { length: count },
61
+ () => new OutputAddressWASM(createTestAddress(), BigInt(100000))
62
+ )
63
+ }
64
+
65
+ function createTestWitnesses (count: number): AddressWitnessWASM[] {
66
+ return Array.from({ length: count }, () => AddressWitnessWASM.P2PKH(randomBytes(65)))
67
+ }
68
+
69
+ const feeStrategy = (): AddressFundsFeeStrategyStepWASM[] =>
70
+ [AddressFundsFeeStrategyStepWASM.DeductFromInput(0)]
71
+
72
+ describe('AddressFundsTransferTransition fees', function () {
73
+ describe('estimateMinFee', function () {
74
+ test('should price inputs and outputs', function () {
75
+ expect(AddressFundsTransferTransitionWASM.estimateMinFee(1, 1))
76
+ .toEqual(INPUT_COST + OUTPUT_COST)
77
+
78
+ expect(AddressFundsTransferTransitionWASM.estimateMinFee(2, 3))
79
+ .toEqual(INPUT_COST * BigInt(2) + OUTPUT_COST * BigInt(3))
80
+ })
81
+
82
+ test('should charge at least one output', function () {
83
+ expect(AddressFundsTransferTransitionWASM.estimateMinFee(1, 0))
84
+ .toEqual(AddressFundsTransferTransitionWASM.estimateMinFee(1, 1))
85
+ })
86
+
87
+ test('should grow with the input and output counts', function () {
88
+ const one = AddressFundsTransferTransitionWASM.estimateMinFee(1, 1)
89
+ const two = AddressFundsTransferTransitionWASM.estimateMinFee(2, 2)
90
+
91
+ expect(two).toBeGreaterThan(one)
92
+ })
93
+
94
+ test('should default to the latest platform version', function () {
95
+ expect(AddressFundsTransferTransitionWASM.estimateMinFee(1, 1))
96
+ .toEqual(AddressFundsTransferTransitionWASM.estimateMinFee(1, 1, PlatformVersionWASM.PLATFORM_V13))
97
+ })
98
+ })
99
+
100
+ describe('calculateMinRequiredFee', function () {
101
+ test('should match the estimate for the transition inputs and outputs', function () {
102
+ const transition = new AddressFundsTransferTransitionWASM(
103
+ createTestInputs(2),
104
+ feeStrategy(),
105
+ 0,
106
+ createTestWitnesses(2),
107
+ createTestOutputs(1)
108
+ )
109
+
110
+ expect(transition.calculateMinRequiredFee())
111
+ .toEqual(AddressFundsTransferTransitionWASM.estimateMinFee(2, 1))
112
+ })
113
+ })
114
+ })
115
+
116
+ describe('AddressCreditWithdrawalTransition fees', function () {
117
+ const outputScript = (): CoreScriptWASM => CoreScriptWASM.newP2PKH(randomBytes(20))
118
+
119
+ describe('estimateMinFee', function () {
120
+ test('should price the withdrawal, its inputs and its change output', function () {
121
+ expect(AddressCreditWithdrawalTransitionWASM.estimateMinFee(1, false))
122
+ .toEqual(ADDRESS_CREDIT_WITHDRAWAL_COST + INPUT_COST)
123
+
124
+ expect(AddressCreditWithdrawalTransitionWASM.estimateMinFee(1, true))
125
+ .toEqual(ADDRESS_CREDIT_WITHDRAWAL_COST + INPUT_COST + OUTPUT_COST)
126
+ })
127
+
128
+ test('should grow with the input count', function () {
129
+ expect(AddressCreditWithdrawalTransitionWASM.estimateMinFee(2, false))
130
+ .toBeGreaterThan(AddressCreditWithdrawalTransitionWASM.estimateMinFee(1, false))
131
+ })
132
+ })
133
+
134
+ describe('calculateMinRequiredFee', function () {
135
+ test('should match the estimate without a change output', function () {
136
+ const transition = new AddressCreditWithdrawalTransitionWASM(
137
+ createTestInputs(1),
138
+ feeStrategy(),
139
+ 1,
140
+ 'Standard',
141
+ outputScript(),
142
+ 0,
143
+ createTestWitnesses(1)
144
+ )
145
+
146
+ expect(transition.calculateMinRequiredFee())
147
+ .toEqual(AddressCreditWithdrawalTransitionWASM.estimateMinFee(1, false))
148
+ })
149
+
150
+ test('should match the estimate with a change output', function () {
151
+ const transition = new AddressCreditWithdrawalTransitionWASM(
152
+ createTestInputs(1),
153
+ feeStrategy(),
154
+ 1,
155
+ 'Standard',
156
+ outputScript(),
157
+ 0,
158
+ createTestWitnesses(1),
159
+ createTestOutputs(1)[0]
160
+ )
161
+
162
+ expect(transition.calculateMinRequiredFee())
163
+ .toEqual(AddressCreditWithdrawalTransitionWASM.estimateMinFee(1, true))
164
+ })
165
+ })
166
+ })
167
+
168
+ describe('IdentityCreateFromAddressesTransition fees', function () {
169
+ test('calculateMinRequiredFee should price the base cost, inputs, output and keys', function () {
170
+ const publicKeys = [
171
+ new IdentityPublicKeyInCreationWASM(
172
+ 0,
173
+ Purpose.AUTHENTICATION,
174
+ SecurityLevel.MASTER,
175
+ KeyType.ECDSA_SECP256K1,
176
+ false,
177
+ randomBytes(33)
178
+ )
179
+ ]
180
+
181
+ const transition = new IdentityCreateFromAddressesTransitionWASM(
182
+ publicKeys,
183
+ createTestInputs(1),
184
+ feeStrategy(),
185
+ 0,
186
+ createTestWitnesses(1),
187
+ createTestOutputs(1)[0]
188
+ )
189
+
190
+ expect(transition.calculateMinRequiredFee()).toEqual(
191
+ IDENTITY_CREATE_BASE_COST + INPUT_COST + OUTPUT_COST + IDENTITY_KEY_IN_CREATION_COST
192
+ )
193
+ })
194
+ })
195
+
196
+ describe('IdentityTopUpFromAddressesTransition fees', function () {
197
+ test('calculateMinRequiredFee should price the base cost, inputs and output', function () {
198
+ const transition = new IdentityTopUpFromAddressesTransitionWASM(
199
+ randomBytes(32),
200
+ createTestInputs(1),
201
+ feeStrategy(),
202
+ 0,
203
+ createTestWitnesses(1),
204
+ createTestOutputs(1)[0]
205
+ )
206
+
207
+ expect(transition.calculateMinRequiredFee())
208
+ .toEqual(IDENTITY_TOPUP_BASE_COST + INPUT_COST + OUTPUT_COST)
209
+ })
210
+ })
211
+
212
+ describe('IdentityCreditTransferToAddressesTransition fees', function () {
213
+ test('calculateMinRequiredFee should price the base cost and each recipient', function () {
214
+ const transition = new IdentityCreditTransferToAddressesTransitionWASM(
215
+ randomBytes(32),
216
+ createTestOutputs(2),
217
+ BigInt(1),
218
+ 0
219
+ )
220
+
221
+ expect(transition.calculateMinRequiredFee())
222
+ .toEqual(CREDIT_TRANSFER_TO_ADDRESSES_COST + OUTPUT_COST * BigInt(2))
223
+ })
224
+ })
225
+
226
+ describe('AddressFundingFromAssetLockTransition fees', function () {
227
+ test('calculateMinRequiredFee should include the asset lock base cost', function () {
228
+ const outPoint = new OutPointWASM('a'.repeat(64), 0)
229
+ const assetLockProof = AssetLockProofWASM.createChainAssetLockProof(100, outPoint)
230
+
231
+ const transition = new AddressFundingFromAssetLockTransitionWASM(
232
+ assetLockProof,
233
+ createTestInputs(1),
234
+ feeStrategy(),
235
+ 0,
236
+ createTestWitnesses(1),
237
+ [new OutputAddressNullableCreditsWASM(createTestAddress(), BigInt(100000))]
238
+ )
239
+
240
+ const fee = transition.calculateMinRequiredFee()
241
+
242
+ expect(typeof fee).toEqual('bigint')
243
+ expect(fee).toBeGreaterThan(INPUT_COST + OUTPUT_COST)
244
+ })
245
+ })
246
+
247
+ describe('new address storage fee', function () {
248
+ test('should match the storage the node charges for one created address', function () {
249
+ expect(PlatformAddressWASM.estimateStorageFeeForNewAddresses(1))
250
+ .toEqual(NEW_ADDRESS_STORAGE_FEE)
251
+ })
252
+
253
+ test('should scale with the number of created addresses', function () {
254
+ expect(PlatformAddressWASM.estimateStorageFeeForNewAddresses(0)).toEqual(BigInt(0))
255
+ expect(PlatformAddressWASM.estimateStorageFeeForNewAddresses(3))
256
+ .toEqual(NEW_ADDRESS_STORAGE_FEE * BigInt(3))
257
+ })
258
+
259
+ test('should default to the latest platform version', function () {
260
+ expect(PlatformAddressWASM.estimateStorageFeeForNewAddresses(1))
261
+ .toEqual(PlatformAddressWASM.estimateStorageFeeForNewAddresses(1, PlatformVersionWASM.PLATFORM_V13))
262
+ })
263
+
264
+ test('should exceed the per output component of the min fee', function () {
265
+ const perOutput =
266
+ AddressFundsTransferTransitionWASM.estimateMinFee(1, 2) -
267
+ AddressFundsTransferTransitionWASM.estimateMinFee(1, 1)
268
+
269
+ // The min fee prices an output slightly below what a created address costs in storage,
270
+ // which is why a transfer paying a brand new address is charged a little more than its
271
+ // minimum required fee.
272
+ expect(NEW_ADDRESS_STORAGE_FEE).toBeGreaterThan(perOutput)
273
+ })
274
+ })
@@ -27,7 +27,9 @@ const ACCOUNT = 0
27
27
  // A funded testnet WIF reused across the builder/deposit tests.
28
28
  const WIF = 'cUy4wbim4y9NDwC24omx8oWY5WqfmjSU2gdcZtTXza2xDCAkQXRP'
29
29
 
30
- // Memo payloads are a fixed 32 bytes (MEMO_PAYLOAD_SIZE).
30
+ // The memo payload field is a fixed 32 bytes (MEMO_PAYLOAD_SIZE) on the wire.
31
+ // Text memos may be shorter and are zero-padded to it; `other` payloads are
32
+ // taken verbatim and so must match it exactly.
31
33
  const MEMO_PAYLOAD_SIZE = 32
32
34
 
33
35
  const DASH = 100_000_000_000n
@@ -62,6 +64,7 @@ describe('ShieldedMemo', function () {
62
64
 
63
65
  expect(memo).toBeInstanceOf(ShieldedMemoWASM)
64
66
  expect(memo.toBytes().length).toEqual(36) // on-wire memo size
67
+ expect(memo.toString()).toEqual('')
65
68
  })
66
69
  })
67
70
 
@@ -72,11 +75,41 @@ describe('ShieldedMemo', function () {
72
75
 
73
76
  expect(memo).toBeInstanceOf(ShieldedMemoWASM)
74
77
  expect(memo.toBytes().length).toEqual(36)
75
- expect(memo.toString().endsWith(text)).toBe(true)
78
+ expect(memo.toString()).toEqual(text)
76
79
  })
77
80
 
78
- test('should reject a payload that is not 32 bytes', function () {
79
- expect(() => ShieldedMemoWASM.fromString('too short')).toThrow()
81
+ test('should accept text shorter than the payload', function () {
82
+ const text = 'thanks for lunch' // 16 bytes, half the payload
83
+ const memo = ShieldedMemoWASM.fromString(text)
84
+ const bytes = memo.toBytes()
85
+
86
+ // The wire memo stays a fixed 36 bytes: a 4-byte little-endian kind tag
87
+ // (1 = text), the utf-8 text, then zero padding out to the 32-byte
88
+ // payload. The padding is not part of the decoded text.
89
+ expect(bytes.length).toEqual(36)
90
+ expect(Array.from(bytes.slice(0, 4))).toEqual([1, 0, 0, 0])
91
+ expect(new TextDecoder().decode(bytes.slice(4, 4 + text.length))).toEqual(text)
92
+ expect(bytes.slice(4 + text.length).every((byte) => byte === 0)).toBe(true)
93
+ expect(memo.toString()).toEqual(text)
94
+ })
95
+
96
+ test('should accept an empty string', function () {
97
+ const memo = ShieldedMemoWASM.fromString('')
98
+
99
+ expect(memo.toBytes().length).toEqual(36)
100
+ expect(memo.toString()).toEqual('')
101
+ })
102
+
103
+ test('should count multi-byte characters by their utf-8 byte length', function () {
104
+ // 8 x U+1F355 = 32 bytes, exactly the payload; 9 would be 36 bytes.
105
+ const memo = ShieldedMemoWASM.fromString('🍕'.repeat(8))
106
+
107
+ expect(memo.toString()).toEqual('🍕'.repeat(8))
108
+ expect(() => ShieldedMemoWASM.fromString('🍕'.repeat(9))).toThrow()
109
+ })
110
+
111
+ test('should reject text longer than 32 bytes', function () {
112
+ expect(() => ShieldedMemoWASM.fromString('A'.repeat(MEMO_PAYLOAD_SIZE + 1))).toThrow()
80
113
  })
81
114
  })
82
115