dash-platform-sdk 1.4.0-dev.9 → 1.5.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -0
- package/bundle.min.js +17 -17
- package/package.json +6 -2
- package/proto/generated/platform.client.d.ts +60 -0
- package/proto/generated/platform.client.js +42 -0
- package/proto/generated/platform.d.ts +783 -0
- package/proto/generated/platform.js +1582 -1
- package/src/DashPlatformSDK.d.ts +2 -0
- package/src/DashPlatformSDK.js +3 -0
- package/src/constants.d.ts +2 -1
- package/src/constants.js +3 -1
- package/src/documents/index.js +1 -1
- package/src/documents/query.js +4 -6
- package/src/keyPair/index.d.ts +25 -0
- package/src/keyPair/index.js +33 -0
- package/src/shielded/createStateTransition.d.ts +3 -0
- package/src/shielded/createStateTransition.js +55 -0
- package/src/shielded/getMostRecentShieldedAnchor.d.ts +2 -0
- package/src/shielded/getMostRecentShieldedAnchor.js +36 -0
- package/src/shielded/getShieldedAnchors.d.ts +2 -0
- package/src/shielded/getShieldedAnchors.js +36 -0
- package/src/shielded/getShieldedEncryptedNotes.d.ts +3 -0
- package/src/shielded/getShieldedEncryptedNotes.js +43 -0
- package/src/shielded/getShieldedNotesCount.d.ts +2 -0
- package/src/shielded/getShieldedNotesCount.js +36 -0
- package/src/shielded/getShieldedNullifiers.d.ts +3 -0
- package/src/shielded/getShieldedNullifiers.js +40 -0
- package/src/shielded/getShieldedPoolState.d.ts +2 -0
- package/src/shielded/getShieldedPoolState.js +36 -0
- package/src/shielded/index.d.ts +130 -0
- package/src/shielded/index.js +186 -0
- package/src/stateTransitions/broadcast.js +3 -2
- package/test/unit/DataContract.spec.js +1 -1
- package/test/unit/Document.spec.js +31 -2
- package/test/unit/Identity.spec.js +26 -26
- package/test/unit/KeyPair.spec.js +32 -0
- package/test/unit/PlatformAddress.spec.js +4 -4
- package/test/unit/Shielded.spec.d.ts +1 -0
- package/test/unit/Shielded.spec.js +236 -0
- package/test/unit/Tokens.spec.js +2 -2
- package/types.d.ts +86 -2
- package/types.js +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import GRPCConnectionPool from '../grpcConnectionPool.js';
|
|
2
|
+
import { ShieldedEncryptedNote, ShieldedNullifierStatus, ShieldedTransitionParamsMap, ShieldedTransitionType } from '../../types.js';
|
|
3
|
+
import { RecoveredNoteWASM, ShieldedBuilderWASM, SpendableNoteWASM, StateTransitionWASM } from 'pshenmic-dpp';
|
|
4
|
+
/**
|
|
5
|
+
* Shielded controller for requesting information about shielded pool
|
|
6
|
+
*
|
|
7
|
+
* @hideconstructor
|
|
8
|
+
*/
|
|
9
|
+
export declare class ShieldedController {
|
|
10
|
+
/** @ignore **/
|
|
11
|
+
grpcPool: GRPCConnectionPool;
|
|
12
|
+
/** @ignore **/
|
|
13
|
+
shieldedBuilder?: ShieldedBuilderWASM;
|
|
14
|
+
constructor(grpcPool: GRPCConnectionPool);
|
|
15
|
+
/**
|
|
16
|
+
* Set bindings instance of builder
|
|
17
|
+
* Needed for custom builder load, because ShieldedBuilderWASM inits very slow on old hardware
|
|
18
|
+
*/
|
|
19
|
+
init(builder?: ShieldedBuilderWASM): ShieldedBuilderWASM;
|
|
20
|
+
/**
|
|
21
|
+
* Lazily constructs and caches a {ShieldedBuilderWASM}. Construction builds
|
|
22
|
+
* the Halo 2 proving key (~seconds), so the instance is created once and reused.
|
|
23
|
+
*
|
|
24
|
+
* @ignore
|
|
25
|
+
*/
|
|
26
|
+
getShieldedBuilder(): ShieldedBuilderWASM;
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves a batch of shielded encrypted notes from the shielded pool,
|
|
29
|
+
* starting from the given note index.
|
|
30
|
+
*
|
|
31
|
+
* Returns an array of encrypted notes, each containing the nullifier,
|
|
32
|
+
* note commitment (cmx), encrypted note payload and value commitment (cvNet).
|
|
33
|
+
*
|
|
34
|
+
* @param startIndex {bigint} - index of the first note to fetch
|
|
35
|
+
* @param count {number} - amount of notes to fetch
|
|
36
|
+
*
|
|
37
|
+
* @return {Promise<ShieldedEncryptedNote[]>}
|
|
38
|
+
*/
|
|
39
|
+
getShieldedEncryptedNotes(startIndex: bigint, count: number): Promise<ShieldedEncryptedNote[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Retrieves the set of valid anchors (note commitment tree roots) currently
|
|
42
|
+
* accepted by the shielded pool.
|
|
43
|
+
*
|
|
44
|
+
* @return {Promise<Uint8Array[]>}
|
|
45
|
+
*/
|
|
46
|
+
getShieldedAnchors(): Promise<Uint8Array[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Retrieves the most recent anchor (note commitment tree root) of the shielded pool.
|
|
49
|
+
*
|
|
50
|
+
* @return {Promise<Uint8Array>}
|
|
51
|
+
*/
|
|
52
|
+
getMostRecentShieldedAnchor(): Promise<Uint8Array | undefined>;
|
|
53
|
+
/**
|
|
54
|
+
* Retrieves the total balance currently held in the shielded pool.
|
|
55
|
+
*
|
|
56
|
+
* @return {Promise<bigint>}
|
|
57
|
+
*/
|
|
58
|
+
getShieldedPoolState(): Promise<bigint | undefined>;
|
|
59
|
+
/**
|
|
60
|
+
* Retrieves the total count of notes (leaves) in the shielded notes
|
|
61
|
+
* commitment tree. Useful as a denominator for shielded sync progress.
|
|
62
|
+
*
|
|
63
|
+
* @return {Promise<bigint>}
|
|
64
|
+
*/
|
|
65
|
+
getShieldedNotesCount(): Promise<bigint | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Checks the spent status of the given nullifiers in the shielded pool.
|
|
68
|
+
*
|
|
69
|
+
* Returns an array of statuses, each containing the nullifier and whether
|
|
70
|
+
* it has already been spent.
|
|
71
|
+
*
|
|
72
|
+
* @param nullifiers {Uint8Array[]} - nullifiers to check
|
|
73
|
+
*
|
|
74
|
+
* @return {Promise<ShieldedNullifierStatus[]>}
|
|
75
|
+
*/
|
|
76
|
+
getShieldedNullifiers(nullifiers: Uint8Array[]): Promise<ShieldedNullifierStatus[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Recovers the notes owned by a wallet from a set of shielded encrypted notes
|
|
79
|
+
* (as returned by {@link getShieldedEncryptedNotes}) by trial-decrypting each
|
|
80
|
+
* with the viewing key derived from the seed (ZIP-32 m/32'/coinType'/account').
|
|
81
|
+
* Only notes addressed to the wallet are returned, each with its global leaf
|
|
82
|
+
* position (`index`) in the commitment tree.
|
|
83
|
+
*
|
|
84
|
+
* @param notes {ShieldedEncryptedNote[]} - shielded encrypted notes to scan
|
|
85
|
+
* @param seed {Uint8Array} - BIP-39 seed bytes
|
|
86
|
+
* @param account {number} - ZIP-32 account index
|
|
87
|
+
*
|
|
88
|
+
* @return {RecoveredNoteWASM[]}
|
|
89
|
+
*/
|
|
90
|
+
recoverNotes(notes: ShieldedEncryptedNote[], seed: Uint8Array, account: number): RecoveredNoteWASM[];
|
|
91
|
+
/**
|
|
92
|
+
* Rebuilds the note commitment tree from the full on-chain note set and
|
|
93
|
+
* witnesses the given recovered notes against it, producing the spendable
|
|
94
|
+
* notes and the shared anchor required by spend transitions (`shieldedTransfer`,
|
|
95
|
+
* `unshield`, `shieldedWithdrawal`, `identityCreateFromShieldedPool`).
|
|
96
|
+
*
|
|
97
|
+
* Pass the complete note set from {@link getShieldedEncryptedNotes} as `notes`
|
|
98
|
+
* (the commitment tree leaves) and the notes you want to spend from
|
|
99
|
+
* {@link recoverNotes} as `recovered`. This keeps the whole spend flow inside
|
|
100
|
+
* the SDK — no need to construct a commitment tree yourself.
|
|
101
|
+
*
|
|
102
|
+
* @param notes {ShieldedEncryptedNote[]} - full on-chain note set (the tree leaves)
|
|
103
|
+
* @param recovered {RecoveredNoteWASM[]} - the recovered notes to spend
|
|
104
|
+
*
|
|
105
|
+
* @return {{ spends: SpendableNoteWASM[], anchor: Uint8Array }}
|
|
106
|
+
*/
|
|
107
|
+
buildSpendableNotes(notes: ShieldedEncryptedNote[], recovered: RecoveredNoteWASM[]): {
|
|
108
|
+
spends: SpendableNoteWASM[];
|
|
109
|
+
anchor: Uint8Array;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Helper function for building and proving shielded (Orchard) transitions.
|
|
113
|
+
* It may be used to create any of 6 shielded transition actions:
|
|
114
|
+
*
|
|
115
|
+
* 1) shield - transparent platform addresses -> pool (deposit)
|
|
116
|
+
* 2) shieldFromAssetLock - asset lock -> pool (deposit)
|
|
117
|
+
* 3) shieldedWithdrawal - pool -> core L1 (spend)
|
|
118
|
+
* 4) unshield - pool -> platform identity balance (spend)
|
|
119
|
+
* 5) shieldedTransfer - pool -> pool (spend)
|
|
120
|
+
* 6) identityCreateFromShieldedPool - pool -> new identity (spend)
|
|
121
|
+
*
|
|
122
|
+
* Spends require a {SpendableNoteWASM} witnessed against an on-chain anchor.
|
|
123
|
+
*
|
|
124
|
+
* @param type {string} Type of the shielded transition, must be one of ('shield' | 'shieldFromAssetLock' | 'shieldedWithdrawal' | 'unshield' | 'shieldedTransfer' | 'identityCreateFromShieldedPool')
|
|
125
|
+
* @param params {ShieldedTransitionParams} params
|
|
126
|
+
*
|
|
127
|
+
* @return {StateTransitionWASM}
|
|
128
|
+
*/
|
|
129
|
+
createStateTransition<K extends ShieldedTransitionType>(type: K, params: ShieldedTransitionParamsMap[K]): StateTransitionWASM;
|
|
130
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import getShieldedEncryptedNotes from './getShieldedEncryptedNotes.js';
|
|
2
|
+
import getShieldedAnchors from './getShieldedAnchors.js';
|
|
3
|
+
import getMostRecentShieldedAnchor from './getMostRecentShieldedAnchor.js';
|
|
4
|
+
import getShieldedPoolState from './getShieldedPoolState.js';
|
|
5
|
+
import getShieldedNotesCount from './getShieldedNotesCount.js';
|
|
6
|
+
import getShieldedNullifiers from './getShieldedNullifiers.js';
|
|
7
|
+
import createStateTransition from './createStateTransition.js';
|
|
8
|
+
import { CommitmentTreeWASM, ContractBoundsWASM, IdentityPublicKeyInCreationWASM, recoverNotes, SerializedActionWASM, ShieldedBuilderWASM, ShieldedMemoWASM, SpendableNoteWASM } from 'pshenmic-dpp';
|
|
9
|
+
/**
|
|
10
|
+
* Shielded controller for requesting information about shielded pool
|
|
11
|
+
*
|
|
12
|
+
* @hideconstructor
|
|
13
|
+
*/
|
|
14
|
+
export class ShieldedController {
|
|
15
|
+
/** @ignore **/
|
|
16
|
+
grpcPool;
|
|
17
|
+
/** @ignore **/
|
|
18
|
+
shieldedBuilder;
|
|
19
|
+
constructor(grpcPool) {
|
|
20
|
+
this.grpcPool = grpcPool;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Set bindings instance of builder
|
|
24
|
+
* Needed for custom builder load, because ShieldedBuilderWASM inits very slow on old hardware
|
|
25
|
+
*/
|
|
26
|
+
init(builder) {
|
|
27
|
+
this.shieldedBuilder = builder ?? new ShieldedBuilderWASM();
|
|
28
|
+
return this.shieldedBuilder;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Lazily constructs and caches a {ShieldedBuilderWASM}. Construction builds
|
|
32
|
+
* the Halo 2 proving key (~seconds), so the instance is created once and reused.
|
|
33
|
+
*
|
|
34
|
+
* @ignore
|
|
35
|
+
*/
|
|
36
|
+
getShieldedBuilder() {
|
|
37
|
+
if (this.shieldedBuilder == null) {
|
|
38
|
+
return this.init();
|
|
39
|
+
}
|
|
40
|
+
return this.shieldedBuilder;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Retrieves a batch of shielded encrypted notes from the shielded pool,
|
|
44
|
+
* starting from the given note index.
|
|
45
|
+
*
|
|
46
|
+
* Returns an array of encrypted notes, each containing the nullifier,
|
|
47
|
+
* note commitment (cmx), encrypted note payload and value commitment (cvNet).
|
|
48
|
+
*
|
|
49
|
+
* @param startIndex {bigint} - index of the first note to fetch
|
|
50
|
+
* @param count {number} - amount of notes to fetch
|
|
51
|
+
*
|
|
52
|
+
* @return {Promise<ShieldedEncryptedNote[]>}
|
|
53
|
+
*/
|
|
54
|
+
async getShieldedEncryptedNotes(startIndex, count) {
|
|
55
|
+
return await getShieldedEncryptedNotes(this.grpcPool, startIndex, count);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves the set of valid anchors (note commitment tree roots) currently
|
|
59
|
+
* accepted by the shielded pool.
|
|
60
|
+
*
|
|
61
|
+
* @return {Promise<Uint8Array[]>}
|
|
62
|
+
*/
|
|
63
|
+
async getShieldedAnchors() {
|
|
64
|
+
return await getShieldedAnchors(this.grpcPool);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Retrieves the most recent anchor (note commitment tree root) of the shielded pool.
|
|
68
|
+
*
|
|
69
|
+
* @return {Promise<Uint8Array>}
|
|
70
|
+
*/
|
|
71
|
+
async getMostRecentShieldedAnchor() {
|
|
72
|
+
return await getMostRecentShieldedAnchor(this.grpcPool);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Retrieves the total balance currently held in the shielded pool.
|
|
76
|
+
*
|
|
77
|
+
* @return {Promise<bigint>}
|
|
78
|
+
*/
|
|
79
|
+
async getShieldedPoolState() {
|
|
80
|
+
return await getShieldedPoolState(this.grpcPool);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Retrieves the total count of notes (leaves) in the shielded notes
|
|
84
|
+
* commitment tree. Useful as a denominator for shielded sync progress.
|
|
85
|
+
*
|
|
86
|
+
* @return {Promise<bigint>}
|
|
87
|
+
*/
|
|
88
|
+
async getShieldedNotesCount() {
|
|
89
|
+
return await getShieldedNotesCount(this.grpcPool);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Checks the spent status of the given nullifiers in the shielded pool.
|
|
93
|
+
*
|
|
94
|
+
* Returns an array of statuses, each containing the nullifier and whether
|
|
95
|
+
* it has already been spent.
|
|
96
|
+
*
|
|
97
|
+
* @param nullifiers {Uint8Array[]} - nullifiers to check
|
|
98
|
+
*
|
|
99
|
+
* @return {Promise<ShieldedNullifierStatus[]>}
|
|
100
|
+
*/
|
|
101
|
+
async getShieldedNullifiers(nullifiers) {
|
|
102
|
+
return await getShieldedNullifiers(this.grpcPool, nullifiers);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Recovers the notes owned by a wallet from a set of shielded encrypted notes
|
|
106
|
+
* (as returned by {@link getShieldedEncryptedNotes}) by trial-decrypting each
|
|
107
|
+
* with the viewing key derived from the seed (ZIP-32 m/32'/coinType'/account').
|
|
108
|
+
* Only notes addressed to the wallet are returned, each with its global leaf
|
|
109
|
+
* position (`index`) in the commitment tree.
|
|
110
|
+
*
|
|
111
|
+
* @param notes {ShieldedEncryptedNote[]} - shielded encrypted notes to scan
|
|
112
|
+
* @param seed {Uint8Array} - BIP-39 seed bytes
|
|
113
|
+
* @param account {number} - ZIP-32 account index
|
|
114
|
+
*
|
|
115
|
+
* @return {RecoveredNoteWASM[]}
|
|
116
|
+
*/
|
|
117
|
+
recoverNotes(notes, seed, account) {
|
|
118
|
+
// SLIP-44 coin type: 5 = Dash mainnet, 1 = testnets. Orchard derives via ZIP-32
|
|
119
|
+
const coinType = this.grpcPool.network === 'mainnet' ? 5 : 1;
|
|
120
|
+
// ShieldedEncryptedNote carries everything needed for trial-decryption; the
|
|
121
|
+
// spend-authorization fields (rk, spendAuthSig) are unused here, so zero them.
|
|
122
|
+
const actions = notes.map(note => new SerializedActionWASM(note.nullifier, new Uint8Array(32), note.cmx, note.encryptedNote, note.cvNet, new Uint8Array(64)));
|
|
123
|
+
return recoverNotes(actions, seed, coinType, account);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Rebuilds the note commitment tree from the full on-chain note set and
|
|
127
|
+
* witnesses the given recovered notes against it, producing the spendable
|
|
128
|
+
* notes and the shared anchor required by spend transitions (`shieldedTransfer`,
|
|
129
|
+
* `unshield`, `shieldedWithdrawal`, `identityCreateFromShieldedPool`).
|
|
130
|
+
*
|
|
131
|
+
* Pass the complete note set from {@link getShieldedEncryptedNotes} as `notes`
|
|
132
|
+
* (the commitment tree leaves) and the notes you want to spend from
|
|
133
|
+
* {@link recoverNotes} as `recovered`. This keeps the whole spend flow inside
|
|
134
|
+
* the SDK — no need to construct a commitment tree yourself.
|
|
135
|
+
*
|
|
136
|
+
* @param notes {ShieldedEncryptedNote[]} - full on-chain note set (the tree leaves)
|
|
137
|
+
* @param recovered {RecoveredNoteWASM[]} - the recovered notes to spend
|
|
138
|
+
*
|
|
139
|
+
* @return {{ spends: SpendableNoteWASM[], anchor: Uint8Array }}
|
|
140
|
+
*/
|
|
141
|
+
buildSpendableNotes(notes, recovered) {
|
|
142
|
+
// only the leaves we intend to witness need to be marked spendable
|
|
143
|
+
const spendableIndices = new Set(recovered.map(note => note.index));
|
|
144
|
+
const tree = new CommitmentTreeWASM();
|
|
145
|
+
notes.forEach((note, index) => tree.append(note.cmx, spendableIndices.has(index)));
|
|
146
|
+
tree.checkpoint(0);
|
|
147
|
+
const anchor = tree.anchor();
|
|
148
|
+
const spends = recovered.map(recoveredNote => {
|
|
149
|
+
const merklePath = tree.witness(recoveredNote.index, 0) ?? tree.witness(recoveredNote.index, 1);
|
|
150
|
+
if (merklePath == null) {
|
|
151
|
+
throw new Error(`Failed to witness recovered note at index ${recoveredNote.index}`);
|
|
152
|
+
}
|
|
153
|
+
return new SpendableNoteWASM(recoveredNote.note, merklePath);
|
|
154
|
+
});
|
|
155
|
+
return { spends, anchor };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Helper function for building and proving shielded (Orchard) transitions.
|
|
159
|
+
* It may be used to create any of 6 shielded transition actions:
|
|
160
|
+
*
|
|
161
|
+
* 1) shield - transparent platform addresses -> pool (deposit)
|
|
162
|
+
* 2) shieldFromAssetLock - asset lock -> pool (deposit)
|
|
163
|
+
* 3) shieldedWithdrawal - pool -> core L1 (spend)
|
|
164
|
+
* 4) unshield - pool -> platform identity balance (spend)
|
|
165
|
+
* 5) shieldedTransfer - pool -> pool (spend)
|
|
166
|
+
* 6) identityCreateFromShieldedPool - pool -> new identity (spend)
|
|
167
|
+
*
|
|
168
|
+
* Spends require a {SpendableNoteWASM} witnessed against an on-chain anchor.
|
|
169
|
+
*
|
|
170
|
+
* @param type {string} Type of the shielded transition, must be one of ('shield' | 'shieldFromAssetLock' | 'shieldedWithdrawal' | 'unshield' | 'shieldedTransfer' | 'identityCreateFromShieldedPool')
|
|
171
|
+
* @param params {ShieldedTransitionParams} params
|
|
172
|
+
*
|
|
173
|
+
* @return {StateTransitionWASM}
|
|
174
|
+
*/
|
|
175
|
+
createStateTransition(type, params) {
|
|
176
|
+
// @ts-expect-error a plain string memo is normalized to ShieldedMemoWASM (empty when omitted)
|
|
177
|
+
params.memo = typeof params.memo === 'string' ? ShieldedMemoWASM.fromString(params.memo) : ShieldedMemoWASM.empty();
|
|
178
|
+
if (type === 'identityCreateFromShieldedPool') {
|
|
179
|
+
const identityParams = params;
|
|
180
|
+
// @ts-expect-error builder expects IdentityPublicKeyInCreationWASM instances
|
|
181
|
+
identityParams.publicKeys = identityParams.publicKeys
|
|
182
|
+
.map(({ id, purpose, securityLevel, keyType, readOnly, data, signature, contractBounds }) => new IdentityPublicKeyInCreationWASM(id, purpose, securityLevel, keyType, readOnly, data, signature, (contractBounds != null) ? new ContractBoundsWASM(contractBounds.dataContractId, contractBounds.documentType) : undefined));
|
|
183
|
+
}
|
|
184
|
+
return createStateTransition(this.getShieldedBuilder(), type, params);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -11,8 +11,9 @@ export default async function broadcast(grpcPool, stateTransition) {
|
|
|
11
11
|
await grpcPool.getClient().broadcastStateTransition(broadcastStateTransitionRequest);
|
|
12
12
|
}
|
|
13
13
|
catch (err) {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const errorBin = err.meta?.['dash-serialized-consensus-error-bin'];
|
|
15
|
+
if (errorBin?.length !== 0 && errorBin != null) {
|
|
16
|
+
throw new Error(deserializeConsensusError(errorBin));
|
|
16
17
|
}
|
|
17
18
|
else {
|
|
18
19
|
throw err;
|
|
@@ -11,7 +11,7 @@ describe('DataContract', () => {
|
|
|
11
11
|
ownerIdentifier = 'GARSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec';
|
|
12
12
|
identityNonce = BigInt(11);
|
|
13
13
|
config = {
|
|
14
|
-
$
|
|
14
|
+
$formatVersion: '1',
|
|
15
15
|
canBeDeleted: true,
|
|
16
16
|
readonly: true,
|
|
17
17
|
keepsHistory: false,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DocumentWASM, StateTransitionWASM } from 'pshenmic-dpp';
|
|
1
|
+
import { DocumentWASM, GasFeesPaidByWASM, StateTransitionWASM } from 'pshenmic-dpp';
|
|
2
2
|
import { DashPlatformSDK } from '../../src/DashPlatformSDK.js';
|
|
3
3
|
let sdk;
|
|
4
4
|
let dataContract;
|
|
@@ -42,6 +42,35 @@ describe('Document', () => {
|
|
|
42
42
|
expect(document.dataContractId.base58()).toEqual(dataContract);
|
|
43
43
|
expect(document).toEqual(expect.any(DocumentWASM));
|
|
44
44
|
});
|
|
45
|
+
test('should be able to get document with startAt', async () => {
|
|
46
|
+
const dataContract = '6hVQW16jyvZyGSQk2YVty4ND6bgFXozizYWnPt753uW5';
|
|
47
|
+
const documentType = 'torrent';
|
|
48
|
+
const limit = 5;
|
|
49
|
+
// @ts-expect-error
|
|
50
|
+
const [firstQueriedDoc] = (await sdk.documents.query(dataContract, documentType, null, null, limit)).toReversed();
|
|
51
|
+
// @ts-expect-error
|
|
52
|
+
const [secondQueriedDoc] = await sdk.documents.query(dataContract, documentType, null, null, limit, firstQueriedDoc.id);
|
|
53
|
+
expect(secondQueriedDoc.id.base58()).toEqual(firstQueriedDoc.id.base58());
|
|
54
|
+
expect(secondQueriedDoc.createdAtBlockHeight).toEqual(undefined);
|
|
55
|
+
expect(secondQueriedDoc.dataContractId.base58()).toEqual(dataContract);
|
|
56
|
+
expect(secondQueriedDoc).toEqual(expect.any(DocumentWASM));
|
|
57
|
+
});
|
|
58
|
+
test('should be able to get document with startAfter', async () => {
|
|
59
|
+
const dataContract = '6hVQW16jyvZyGSQk2YVty4ND6bgFXozizYWnPt753uW5';
|
|
60
|
+
const documentType = 'torrent';
|
|
61
|
+
const limit = 5;
|
|
62
|
+
const masterQueryLimit = limit * 2;
|
|
63
|
+
// @ts-expect-error
|
|
64
|
+
const masterQuery = await sdk.documents.query(dataContract, documentType, null, null, masterQueryLimit);
|
|
65
|
+
// @ts-expect-error
|
|
66
|
+
const [firstQueriedDoc] = (await sdk.documents.query(dataContract, documentType, null, null, limit)).toReversed();
|
|
67
|
+
// @ts-expect-error
|
|
68
|
+
const [secondQueriedDoc] = await sdk.documents.query(dataContract, documentType, null, null, limit, undefined, firstQueriedDoc.id);
|
|
69
|
+
expect(masterQuery[5].id.base58()).toEqual(secondQueriedDoc.id.base58());
|
|
70
|
+
expect(secondQueriedDoc.createdAtBlockHeight).toEqual(undefined);
|
|
71
|
+
expect(secondQueriedDoc.dataContractId.base58()).toEqual(dataContract);
|
|
72
|
+
expect(secondQueriedDoc).toEqual(expect.any(DocumentWASM));
|
|
73
|
+
});
|
|
45
74
|
describe('should be able to create state transition', () => {
|
|
46
75
|
test('should be able to create a create transition', async () => {
|
|
47
76
|
const document = sdk.documents.create(dataContract, documentType, data, identity);
|
|
@@ -67,7 +96,7 @@ describe('Document', () => {
|
|
|
67
96
|
tokenContractPosition: 0,
|
|
68
97
|
minimumTokenCost: BigInt(1000),
|
|
69
98
|
maximumTokenCost: BigInt(100000),
|
|
70
|
-
gasFeesPaidBy:
|
|
99
|
+
gasFeesPaidBy: GasFeesPaidByWASM.ContractOwner
|
|
71
100
|
};
|
|
72
101
|
const stateTransition = sdk.documents.createStateTransition(document, 'create', { identityContractNonce, tokenPaymentInfo });
|
|
73
102
|
expect(stateTransition).toEqual(expect.any(StateTransitionWASM));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CoreScriptWASM, IdentityPublicKeyWASM, IdentityWASM, PrivateKeyWASM, StateTransitionWASM } from 'pshenmic-dpp';
|
|
1
|
+
import { CoreScriptWASM, IdentityPublicKeyWASM, IdentityWASM, KeyType, PrivateKeyWASM, Purpose, SecurityLevel, StateTransitionWASM } from 'pshenmic-dpp';
|
|
2
2
|
import { DashPlatformSDK } from '../../types.js';
|
|
3
3
|
import { base58 } from '@scure/base';
|
|
4
4
|
let sdk;
|
|
@@ -50,18 +50,18 @@ describe('Identity', () => {
|
|
|
50
50
|
const privateKey1 = PrivateKeyWASM.fromHex('a1286dd195e2b8e1f6bdc946c56a53e0c544750d6452ddc0f4c593ef311f21af', 'testnet');
|
|
51
51
|
const identityPublicKeyInCreation1 = {
|
|
52
52
|
id: 0,
|
|
53
|
-
purpose:
|
|
54
|
-
securityLevel:
|
|
55
|
-
keyType:
|
|
53
|
+
purpose: Purpose.AUTHENTICATION,
|
|
54
|
+
securityLevel: SecurityLevel.MASTER,
|
|
55
|
+
keyType: KeyType.ECDSA_SECP256K1,
|
|
56
56
|
readOnly: false,
|
|
57
57
|
data: privateKey1.getPublicKey().bytes()
|
|
58
58
|
};
|
|
59
59
|
const privateKey2 = PrivateKeyWASM.fromHex('44a8195e242364b935e9d7ff2106ed109e9baf3800907f5e58a259fdfd1ca5e5', 'testnet');
|
|
60
60
|
const identityPublicKeyInCreation2 = {
|
|
61
61
|
id: 1,
|
|
62
|
-
purpose:
|
|
63
|
-
securityLevel:
|
|
64
|
-
keyType:
|
|
62
|
+
purpose: Purpose.AUTHENTICATION,
|
|
63
|
+
securityLevel: SecurityLevel.HIGH,
|
|
64
|
+
keyType: KeyType.ECDSA_SECP256K1,
|
|
65
65
|
readOnly: false,
|
|
66
66
|
data: privateKey2.getPublicKey().bytes()
|
|
67
67
|
};
|
|
@@ -76,7 +76,7 @@ describe('Identity', () => {
|
|
|
76
76
|
type: 'chainLock'
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
|
-
identityCreateStateTransition.signByPrivateKey(privateKey1,
|
|
79
|
+
identityCreateStateTransition.signByPrivateKey(privateKey1, KeyType.ECDSA_SECP256K1);
|
|
80
80
|
identityPublicKeyInCreation1.signature = identityCreateStateTransition.signature;
|
|
81
81
|
// Set identity public key signature for public key 1
|
|
82
82
|
identityCreateStateTransition = sdk.identities.createStateTransition('create', {
|
|
@@ -88,7 +88,7 @@ describe('Identity', () => {
|
|
|
88
88
|
type: 'chainLock'
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
|
-
identityCreateStateTransition.signByPrivateKey(privateKey2,
|
|
91
|
+
identityCreateStateTransition.signByPrivateKey(privateKey2, KeyType.ECDSA_SECP256K1);
|
|
92
92
|
identityPublicKeyInCreation2.signature = identityCreateStateTransition.signature;
|
|
93
93
|
// Finalize
|
|
94
94
|
identityCreateStateTransition = sdk.identities.createStateTransition('create', {
|
|
@@ -100,7 +100,7 @@ describe('Identity', () => {
|
|
|
100
100
|
type: 'chainLock'
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
|
-
identityCreateStateTransition.signByPrivateKey(assetLockPrivateKey,
|
|
103
|
+
identityCreateStateTransition.signByPrivateKey(assetLockPrivateKey, KeyType.ECDSA_SECP256K1);
|
|
104
104
|
});
|
|
105
105
|
test('should be able to create IdentityCreateTransition via InstantSend', async () => {
|
|
106
106
|
const assetLockPrivateKey = PrivateKeyWASM.fromHex('edd04a71bddb31e530f6c2314fd42ada333f6656bb853ece13f0577a8fd30612', 'testnet');
|
|
@@ -110,18 +110,18 @@ describe('Identity', () => {
|
|
|
110
110
|
const privateKey1 = PrivateKeyWASM.fromHex('a1286dd195e2b8e1f6bdc946c56a53e0c544750d6452ddc0f4c593ef311f21af', 'testnet');
|
|
111
111
|
const identityPublicKeyInCreation1 = {
|
|
112
112
|
id: 0,
|
|
113
|
-
purpose:
|
|
114
|
-
securityLevel:
|
|
115
|
-
keyType:
|
|
113
|
+
purpose: Purpose.AUTHENTICATION,
|
|
114
|
+
securityLevel: SecurityLevel.MASTER,
|
|
115
|
+
keyType: KeyType.ECDSA_SECP256K1,
|
|
116
116
|
readOnly: false,
|
|
117
117
|
data: privateKey1.getPublicKey().bytes()
|
|
118
118
|
};
|
|
119
119
|
const privateKey2 = PrivateKeyWASM.fromHex('44a8195e242364b935e9d7ff2106ed109e9baf3800907f5e58a259fdfd1ca5e5', 'testnet');
|
|
120
120
|
const identityPublicKeyInCreation2 = {
|
|
121
121
|
id: 1,
|
|
122
|
-
purpose:
|
|
123
|
-
securityLevel:
|
|
124
|
-
keyType:
|
|
122
|
+
purpose: Purpose.AUTHENTICATION,
|
|
123
|
+
securityLevel: SecurityLevel.HIGH,
|
|
124
|
+
keyType: KeyType.ECDSA_SECP256K1,
|
|
125
125
|
readOnly: false,
|
|
126
126
|
data: privateKey2.getPublicKey().bytes()
|
|
127
127
|
};
|
|
@@ -136,7 +136,7 @@ describe('Identity', () => {
|
|
|
136
136
|
type: 'instantLock'
|
|
137
137
|
}
|
|
138
138
|
});
|
|
139
|
-
identityCreateStateTransition.signByPrivateKey(privateKey1,
|
|
139
|
+
identityCreateStateTransition.signByPrivateKey(privateKey1, KeyType.ECDSA_SECP256K1);
|
|
140
140
|
identityPublicKeyInCreation1.signature = identityCreateStateTransition.signature;
|
|
141
141
|
// Set identity public key signature for public key 1
|
|
142
142
|
identityCreateStateTransition = sdk.identities.createStateTransition('create', {
|
|
@@ -148,7 +148,7 @@ describe('Identity', () => {
|
|
|
148
148
|
type: 'instantLock'
|
|
149
149
|
}
|
|
150
150
|
});
|
|
151
|
-
identityCreateStateTransition.signByPrivateKey(privateKey2,
|
|
151
|
+
identityCreateStateTransition.signByPrivateKey(privateKey2, KeyType.ECDSA_SECP256K1);
|
|
152
152
|
identityPublicKeyInCreation2.signature = identityCreateStateTransition.signature;
|
|
153
153
|
// Finalize
|
|
154
154
|
identityCreateStateTransition = sdk.identities.createStateTransition('create', {
|
|
@@ -160,7 +160,7 @@ describe('Identity', () => {
|
|
|
160
160
|
type: 'instantLock'
|
|
161
161
|
}
|
|
162
162
|
});
|
|
163
|
-
identityCreateStateTransition.signByPrivateKey(assetLockPrivateKey,
|
|
163
|
+
identityCreateStateTransition.signByPrivateKey(assetLockPrivateKey, KeyType.ECDSA_SECP256K1);
|
|
164
164
|
});
|
|
165
165
|
test('should be able to create IdentityTopUpTransition via InstantSend', async () => {
|
|
166
166
|
const transaction = '03000800011a468e6a7cf1c5111b09b7bca6743f2571a9bf13d2ff6d21d3d230fd1dea1e97000000006b483045022100fceb25c45e77e1a273660e4f4c9a09042fb858a57704806e14bf80a734af232a02201929893dd720cf5855e31dda577cda16df29520a9774b4f3e813a4cc468fe086012103e16ede6dc5c99f28e3a5733f47a7494992bc6ce4f98551c092645910b9888b8fffffffff0200e1f50500000000026a004054fa02000000001976a91416bbe230f46eea86fc4bf4dd550be45dc9adfcb488ac0000000024010100e1f505000000001976a9147f78813975a3282e09e284d97d93c083b202e34188ac';
|
|
@@ -177,7 +177,7 @@ describe('Identity', () => {
|
|
|
177
177
|
type: 'instantLock'
|
|
178
178
|
}
|
|
179
179
|
});
|
|
180
|
-
identityTopUpTransaction.signByPrivateKey(assetLockPrivateKey, undefined,
|
|
180
|
+
identityTopUpTransaction.signByPrivateKey(assetLockPrivateKey, undefined, KeyType.ECDSA_SECP256K1);
|
|
181
181
|
});
|
|
182
182
|
test('should be able to create IdentityTopUpTransition via ChainLock', async () => {
|
|
183
183
|
const txid = '61aede830477254876d435a317241ad46753c4b1350dc991a45ebcf19ab80a11';
|
|
@@ -194,7 +194,7 @@ describe('Identity', () => {
|
|
|
194
194
|
type: 'chainLock'
|
|
195
195
|
}
|
|
196
196
|
});
|
|
197
|
-
identityTopUpTransaction.signByPrivateKey(assetLockPrivateKey, undefined,
|
|
197
|
+
identityTopUpTransaction.signByPrivateKey(assetLockPrivateKey, undefined, KeyType.ECDSA_SECP256K1);
|
|
198
198
|
});
|
|
199
199
|
test('should be able to create IdentityUpdateTransition', async () => {
|
|
200
200
|
const identityId = 'HT3pUBM1Uv2mKgdPEN1gxa7A4PdsvNY89aJbdSKQb5wR';
|
|
@@ -207,9 +207,9 @@ describe('Identity', () => {
|
|
|
207
207
|
const identityPrivateKey = PrivateKeyWASM.fromHex('16f614c6242580628d849e3616491dda1eccce99642a85667eb9a364dc85324a', 'testnet');
|
|
208
208
|
const identityPublicKeyInCreation = {
|
|
209
209
|
id: keyId,
|
|
210
|
-
purpose:
|
|
211
|
-
securityLevel:
|
|
212
|
-
keyType:
|
|
210
|
+
purpose: Purpose.AUTHENTICATION,
|
|
211
|
+
securityLevel: SecurityLevel.HIGH,
|
|
212
|
+
keyType: KeyType.ECDSA_SECP256K1,
|
|
213
213
|
readOnly: false,
|
|
214
214
|
data: identityPrivateKey.getPublicKey().bytes()
|
|
215
215
|
};
|
|
@@ -219,7 +219,7 @@ describe('Identity', () => {
|
|
|
219
219
|
identityNonce,
|
|
220
220
|
addPublicKeys: [identityPublicKeyInCreation]
|
|
221
221
|
});
|
|
222
|
-
identityUpdateTransition.signByPrivateKey(masterPrivateKey, masterKeyId,
|
|
222
|
+
identityUpdateTransition.signByPrivateKey(masterPrivateKey, masterKeyId, KeyType.ECDSA_SECP256K1);
|
|
223
223
|
identityPublicKeyInCreation.signature = identityUpdateTransition.signature;
|
|
224
224
|
identityUpdateTransition = sdk.identities.createStateTransition('update', {
|
|
225
225
|
identityId,
|
|
@@ -227,7 +227,7 @@ describe('Identity', () => {
|
|
|
227
227
|
identityNonce,
|
|
228
228
|
addPublicKeys: [identityPublicKeyInCreation]
|
|
229
229
|
});
|
|
230
|
-
identityUpdateTransition.signByPrivateKey(masterPrivateKey, masterKeyId,
|
|
230
|
+
identityUpdateTransition.signByPrivateKey(masterPrivateKey, masterKeyId, KeyType.ECDSA_SECP256K1);
|
|
231
231
|
});
|
|
232
232
|
test('should able be create credit transfer transition', async () => {
|
|
233
233
|
const identityId = 'QMfCRPcjXoTnZa9sA9JR2KWgGxZXMRJ4akgS3Uia1Qv';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OrchardAddressWASM } from 'pshenmic-dpp';
|
|
1
2
|
import { DashPlatformSDK } from '../../src/DashPlatformSDK.js';
|
|
2
3
|
let sdk;
|
|
3
4
|
let mnemonic;
|
|
@@ -31,4 +32,35 @@ describe('KeyPair', () => {
|
|
|
31
32
|
expect(key.privateKey).toEqual(Uint8Array.from([89, 255, 64, 41, 202, 170, 83, 68, 135, 58, 161, 107, 130, 20, 3, 50, 69, 16, 108, 104, 32, 68, 13, 100, 225, 24, 79, 20, 193, 184, 238, 55]));
|
|
32
33
|
});
|
|
33
34
|
});
|
|
35
|
+
describe('shielded', () => {
|
|
36
|
+
const account = 0;
|
|
37
|
+
test('should be able to derive a shielded address from seed', () => {
|
|
38
|
+
const seed = sdk.keyPair.mnemonicToSeed(mnemonic);
|
|
39
|
+
const address = sdk.keyPair.deriveShieldedAddress(seed, 'testnet', account);
|
|
40
|
+
expect(address).toEqual(expect.any(OrchardAddressWASM));
|
|
41
|
+
expect(address.bytes()).toHaveLength(43);
|
|
42
|
+
// deterministic for the same seed/network/account
|
|
43
|
+
expect(address.bytes()).toEqual(sdk.keyPair.deriveShieldedAddress(seed, 'testnet', account).bytes());
|
|
44
|
+
});
|
|
45
|
+
test('should derive distinct shielded addresses per diversifier index', () => {
|
|
46
|
+
const seed = sdk.keyPair.mnemonicToSeed(mnemonic);
|
|
47
|
+
const first = sdk.keyPair.deriveShieldedAddress(seed, 'testnet', account, 0);
|
|
48
|
+
const second = sdk.keyPair.deriveShieldedAddress(seed, 'testnet', account, 1);
|
|
49
|
+
expect(first.bytes()).not.toEqual(second.bytes());
|
|
50
|
+
});
|
|
51
|
+
test('should derive distinct shielded addresses per network', () => {
|
|
52
|
+
const seed = sdk.keyPair.mnemonicToSeed(mnemonic);
|
|
53
|
+
const mainnet = sdk.keyPair.deriveShieldedAddress(seed, 'mainnet', account);
|
|
54
|
+
const testnet = sdk.keyPair.deriveShieldedAddress(seed, 'testnet', account);
|
|
55
|
+
expect(mainnet.bytes()).not.toEqual(testnet.bytes());
|
|
56
|
+
});
|
|
57
|
+
test('should be able to derive the outgoing viewing key from seed', () => {
|
|
58
|
+
const seed = sdk.keyPair.mnemonicToSeed(mnemonic);
|
|
59
|
+
const ovk = sdk.keyPair.deriveShieldedOutgoingViewingKey(seed, 'testnet', account);
|
|
60
|
+
expect(ovk).toBeInstanceOf(Uint8Array);
|
|
61
|
+
expect(ovk).toHaveLength(32);
|
|
62
|
+
// deterministic for the same seed/network/account
|
|
63
|
+
expect(ovk).toEqual(sdk.keyPair.deriveShieldedOutgoingViewingKey(seed, 'testnet', account));
|
|
64
|
+
});
|
|
65
|
+
});
|
|
34
66
|
});
|
|
@@ -5,16 +5,16 @@ describe('PlatformAddress', () => {
|
|
|
5
5
|
sdk = new DashPlatformSDK({ network: 'testnet' });
|
|
6
6
|
});
|
|
7
7
|
test('getAddressInfo', async () => {
|
|
8
|
-
const info = await sdk.platformAddresses.getAddressInfo('
|
|
8
|
+
const info = await sdk.platformAddresses.getAddressInfo('tdash1kzg5azscav69z7m6dfzr9ner0a5vt7pn9ca4sz8d');
|
|
9
9
|
expect(info.balance).toBeDefined();
|
|
10
10
|
expect(info.nonce).toBeDefined();
|
|
11
11
|
expect(info.address).toBeDefined();
|
|
12
12
|
});
|
|
13
13
|
test('getAddressesInfos', async () => {
|
|
14
14
|
const addresses = [
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
15
|
+
'tdash1kzg5azscav69z7m6dfzr9ner0a5vt7pn9ca4sz8d',
|
|
16
|
+
'tdash1kqr3cxhgel75ru0yrhj5eq8j8jt92m5enqrfajxw',
|
|
17
|
+
'tdash1kq9plfyacx9q26dtaxgwuw9lt78nyu2mzc3xwcxv'
|
|
18
18
|
];
|
|
19
19
|
const info = await sdk.platformAddresses.getAddressesInfos(addresses);
|
|
20
20
|
expect(info).toHaveLength(addresses.length);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|