dash-platform-sdk 1.4.0 → 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 +15 -15
- package/bundle.min.js +17 -17
- package/package.json +2 -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 +1 -0
- package/src/constants.js +1 -0
- 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/test/unit/KeyPair.spec.js +32 -0
- package/test/unit/Shielded.spec.d.ts +1 -0
- package/test/unit/Shielded.spec.js +236 -0
- package/types.d.ts +85 -1
package/src/DashPlatformSDK.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { TokensController } from './tokens/index.js';
|
|
|
12
12
|
import { VotingController } from './voting/index.js';
|
|
13
13
|
import { Network } from '../types.js';
|
|
14
14
|
import { PlatformAddressesController } from './platformAddresses/index.js';
|
|
15
|
+
import { ShieldedController } from './shielded/index.js';
|
|
15
16
|
export interface GRPCOptions {
|
|
16
17
|
poolLimit: 5;
|
|
17
18
|
dapiUrl?: string | string[];
|
|
@@ -33,6 +34,7 @@ export declare class DashPlatformSDK {
|
|
|
33
34
|
options?: SDKOptions;
|
|
34
35
|
contestedResources: ContestedResourcesController;
|
|
35
36
|
platformAddresses: PlatformAddressesController;
|
|
37
|
+
shielded: ShieldedController;
|
|
36
38
|
stateTransitions: StateTransitionsController;
|
|
37
39
|
dataContracts: DataContractsController;
|
|
38
40
|
identities: IdentitiesController;
|
package/src/DashPlatformSDK.js
CHANGED
|
@@ -11,6 +11,7 @@ import { ContestedResourcesController } from './contestedResources/index.js';
|
|
|
11
11
|
import { TokensController } from './tokens/index.js';
|
|
12
12
|
import { VotingController } from './voting/index.js';
|
|
13
13
|
import { PlatformAddressesController } from './platformAddresses/index.js';
|
|
14
|
+
import { ShieldedController } from './shielded/index.js';
|
|
14
15
|
/**
|
|
15
16
|
* Javascript SDK for that let you interact with a Dash Platform blockchain
|
|
16
17
|
*/
|
|
@@ -22,6 +23,7 @@ export class DashPlatformSDK {
|
|
|
22
23
|
options;
|
|
23
24
|
contestedResources;
|
|
24
25
|
platformAddresses;
|
|
26
|
+
shielded;
|
|
25
27
|
stateTransitions;
|
|
26
28
|
dataContracts;
|
|
27
29
|
identities;
|
|
@@ -65,6 +67,7 @@ export class DashPlatformSDK {
|
|
|
65
67
|
this.grpcPool = grpcPool;
|
|
66
68
|
this.contestedResources = new ContestedResourcesController(grpcPool);
|
|
67
69
|
this.platformAddresses = new PlatformAddressesController(grpcPool);
|
|
70
|
+
this.shielded = new ShieldedController(grpcPool);
|
|
68
71
|
this.stateTransitions = new StateTransitionsController(grpcPool);
|
|
69
72
|
this.dataContracts = new DataContractsController(grpcPool);
|
|
70
73
|
this.identities = new IdentitiesController(grpcPool);
|
package/src/constants.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { PlatformVersionWASM } from 'pshenmic-dpp';
|
|
|
3
3
|
* Default amount of documents to retrieve from DAPI
|
|
4
4
|
*/
|
|
5
5
|
export declare const DAPI_DEFAULT_LIMIT = 100;
|
|
6
|
+
export declare const SHIELDED_MAX_NOTES_PER_QUERY = 8192;
|
|
6
7
|
export declare const HALVING_INTERVAL = 210240;
|
|
7
8
|
export declare const TESTNET_ACTIVATION_HEIGHT = 1066900;
|
|
8
9
|
export declare const MAINNET_ACTIVATION_HEIGHT = 2128896;
|
package/src/constants.js
CHANGED
|
@@ -3,6 +3,7 @@ import { PlatformVersionWASM } from 'pshenmic-dpp';
|
|
|
3
3
|
* Default amount of documents to retrieve from DAPI
|
|
4
4
|
*/
|
|
5
5
|
export const DAPI_DEFAULT_LIMIT = 100;
|
|
6
|
+
export const SHIELDED_MAX_NOTES_PER_QUERY = 8192;
|
|
6
7
|
export const HALVING_INTERVAL = 210240;
|
|
7
8
|
export const TESTNET_ACTIVATION_HEIGHT = 1066900;
|
|
8
9
|
export const MAINNET_ACTIVATION_HEIGHT = 2128896;
|
package/src/keyPair/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HDKey } from '@scure/bip32';
|
|
2
2
|
import { Network } from '../../types.js';
|
|
3
|
+
import { OrchardAddressWASM } from 'pshenmic-dpp';
|
|
3
4
|
/**
|
|
4
5
|
* Collection of functions to work with private keys and seed phrases
|
|
5
6
|
*
|
|
@@ -64,4 +65,28 @@ export declare class KeyPairController {
|
|
|
64
65
|
* @returns {string}
|
|
65
66
|
*/
|
|
66
67
|
p2pkhAddress(publicKey: Uint8Array, network: Network): string;
|
|
68
|
+
/**
|
|
69
|
+
* Derives a shielded (Orchard) address from a BIP-39 seed via ZIP-32
|
|
70
|
+
* (m/32'/coinType'/account').
|
|
71
|
+
*
|
|
72
|
+
* @param seed {Uint8Array} - BIP-39 seed bytes
|
|
73
|
+
* @param network {Network} - network (selects the SLIP-44 coin type)
|
|
74
|
+
* @param account {number} - ZIP-32 account index
|
|
75
|
+
* @param diversifierIndex {number=} - optional diversifier index
|
|
76
|
+
*
|
|
77
|
+
* @returns {OrchardAddressWASM}
|
|
78
|
+
*/
|
|
79
|
+
deriveShieldedAddress(seed: Uint8Array, network: Network, account: number, diversifierIndex?: number): OrchardAddressWASM;
|
|
80
|
+
/**
|
|
81
|
+
* Derives the sender's Orchard outgoing viewing key (OVK, 32 bytes) from a
|
|
82
|
+
* BIP-39 seed via ZIP-32 (m/32'/coinType'/account'). Used to recover the
|
|
83
|
+
* outgoing notes a wallet sent.
|
|
84
|
+
*
|
|
85
|
+
* @param seed {Uint8Array} - BIP-39 seed bytes
|
|
86
|
+
* @param network {Network} - network (selects the SLIP-44 coin type)
|
|
87
|
+
* @param account {number} - ZIP-32 account index
|
|
88
|
+
*
|
|
89
|
+
* @returns {Uint8Array} 32-byte outgoing viewing key
|
|
90
|
+
*/
|
|
91
|
+
deriveShieldedOutgoingViewingKey(seed: Uint8Array, network: Network, account: number): Uint8Array;
|
|
67
92
|
}
|
package/src/keyPair/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import mnemonicToSeed from './mnemonicToSeed.js';
|
|
|
3
3
|
import deriveChild from './deriveChild.js';
|
|
4
4
|
import derivePath from './derivePath.js';
|
|
5
5
|
import { p2pkh } from '@scure/btc-signer';
|
|
6
|
+
import { OrchardAddressWASM, orchardOvkFromSeed } from 'pshenmic-dpp';
|
|
6
7
|
const DASH_VERSIONS = {
|
|
7
8
|
mainnet: { pubKeyHash: 0x4c, scriptHash: 0x10, bech32: 'dc', wif: 0xcc, private: 0x0488ade4, public: 0x0488b21e },
|
|
8
9
|
testnet: { pubKeyHash: 0x8c, scriptHash: 0x13, bech32: 'dc', wif: 0xef, private: 0x04358394, public: 0x043587cf }
|
|
@@ -86,4 +87,36 @@ export class KeyPairController {
|
|
|
86
87
|
const P2PKH = p2pkh(publicKey, DASH_VERSIONS[network]);
|
|
87
88
|
return P2PKH.address;
|
|
88
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Derives a shielded (Orchard) address from a BIP-39 seed via ZIP-32
|
|
92
|
+
* (m/32'/coinType'/account').
|
|
93
|
+
*
|
|
94
|
+
* @param seed {Uint8Array} - BIP-39 seed bytes
|
|
95
|
+
* @param network {Network} - network (selects the SLIP-44 coin type)
|
|
96
|
+
* @param account {number} - ZIP-32 account index
|
|
97
|
+
* @param diversifierIndex {number=} - optional diversifier index
|
|
98
|
+
*
|
|
99
|
+
* @returns {OrchardAddressWASM}
|
|
100
|
+
*/
|
|
101
|
+
deriveShieldedAddress(seed, network, account, diversifierIndex) {
|
|
102
|
+
// SLIP-44 coin type: 5 = Dash mainnet, 1 = testnets. Orchard derives via ZIP-32
|
|
103
|
+
const coinType = network === 'mainnet' ? 5 : 1;
|
|
104
|
+
return OrchardAddressWASM.fromSeed(seed, coinType, account, diversifierIndex);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Derives the sender's Orchard outgoing viewing key (OVK, 32 bytes) from a
|
|
108
|
+
* BIP-39 seed via ZIP-32 (m/32'/coinType'/account'). Used to recover the
|
|
109
|
+
* outgoing notes a wallet sent.
|
|
110
|
+
*
|
|
111
|
+
* @param seed {Uint8Array} - BIP-39 seed bytes
|
|
112
|
+
* @param network {Network} - network (selects the SLIP-44 coin type)
|
|
113
|
+
* @param account {number} - ZIP-32 account index
|
|
114
|
+
*
|
|
115
|
+
* @returns {Uint8Array} 32-byte outgoing viewing key
|
|
116
|
+
*/
|
|
117
|
+
deriveShieldedOutgoingViewingKey(seed, network, account) {
|
|
118
|
+
// SLIP-44 coin type: 5 = Dash mainnet, 1 = testnets. Orchard derives via ZIP-32
|
|
119
|
+
const coinType = network === 'mainnet' ? 5 : 1;
|
|
120
|
+
return orchardOvkFromSeed(seed, coinType, account);
|
|
121
|
+
}
|
|
89
122
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ShieldedBuilderWASM, StateTransitionWASM } from 'pshenmic-dpp';
|
|
2
|
+
import { ShieldedTransitionParamsMap, ShieldedTransitionType } from '../../types.js';
|
|
3
|
+
export default function createStateTransition<K extends ShieldedTransitionType>(builder: ShieldedBuilderWASM, type: K, params: ShieldedTransitionParamsMap[K]): StateTransitionWASM;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { IdentityCreateFromShieldedPoolTransitionWASM, ShieldedBuilderWASM, ShieldedWithdrawalResultWASM, StateTransitionWASM } from 'pshenmic-dpp';
|
|
2
|
+
import { LATEST_PLATFORM_VERSION } from '../constants.js';
|
|
3
|
+
const shieldedTransitionsMap = {
|
|
4
|
+
shield: {
|
|
5
|
+
method: ShieldedBuilderWASM.prototype.shield,
|
|
6
|
+
arguments: ['recipient', 'shieldAmount', 'inputs', 'privateKeys', 'feeStrategy', 'userFeeIncrease', 'memo'],
|
|
7
|
+
optionalArguments: ['senderOvk']
|
|
8
|
+
},
|
|
9
|
+
shieldFromAssetLock: {
|
|
10
|
+
method: ShieldedBuilderWASM.prototype.shieldFromAssetLock,
|
|
11
|
+
arguments: ['recipient', 'shieldAmount', 'assetLockProof', 'privateKey', 'memo', 'dummyOutputs'],
|
|
12
|
+
optionalArguments: ['senderOvk', 'surplusOutput']
|
|
13
|
+
},
|
|
14
|
+
shieldedWithdrawal: {
|
|
15
|
+
method: ShieldedBuilderWASM.prototype.shieldedWithdrawal,
|
|
16
|
+
arguments: ['spends', 'withdrawalAmount', 'outputScript', 'coreFeePerByte', 'pooling', 'changeAddress', 'seed', 'coinType', 'account', 'anchor', 'memo'],
|
|
17
|
+
optionalArguments: []
|
|
18
|
+
},
|
|
19
|
+
unshield: {
|
|
20
|
+
method: ShieldedBuilderWASM.prototype.unshield,
|
|
21
|
+
arguments: ['spends', 'outputAddress', 'unshieldAmount', 'changeAddress', 'seed', 'coinType', 'account', 'anchor', 'memo'],
|
|
22
|
+
optionalArguments: []
|
|
23
|
+
},
|
|
24
|
+
shieldedTransfer: {
|
|
25
|
+
method: ShieldedBuilderWASM.prototype.shieldedTransfer,
|
|
26
|
+
arguments: ['spends', 'recipient', 'transferAmount', 'changeAddress', 'seed', 'coinType', 'account', 'anchor', 'memo'],
|
|
27
|
+
optionalArguments: []
|
|
28
|
+
},
|
|
29
|
+
identityCreateFromShieldedPool: {
|
|
30
|
+
method: ShieldedBuilderWASM.prototype.identityCreateFromShieldedPool,
|
|
31
|
+
arguments: ['publicKeys', 'privateKeys', 'denomination', 'sendToAddressOnCreationFailure', 'spends', 'changeAddress', 'seed', 'coinType', 'account', 'anchor', 'memo'],
|
|
32
|
+
optionalArguments: []
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export default function createStateTransition(builder, type, params) {
|
|
36
|
+
const { method: builderMethod, arguments: classArguments, optionalArguments } = shieldedTransitionsMap[type];
|
|
37
|
+
if (builderMethod == null) {
|
|
38
|
+
throw new Error(`Unimplemented shielded transition type: ${type}`);
|
|
39
|
+
}
|
|
40
|
+
const [missingArgument] = classArguments
|
|
41
|
+
.filter((classArgument) => params[classArgument] == null);
|
|
42
|
+
if (missingArgument != null) {
|
|
43
|
+
throw new Error(`Shielded transition param "${missingArgument}" is missing`);
|
|
44
|
+
}
|
|
45
|
+
const transitionParams = classArguments.concat(optionalArguments).map((classArgument) => params[classArgument]);
|
|
46
|
+
const result = builderMethod.apply(builder, [...transitionParams, params.platformVersion ?? LATEST_PLATFORM_VERSION]);
|
|
47
|
+
if (result instanceof StateTransitionWASM) {
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
if (result instanceof ShieldedWithdrawalResultWASM) {
|
|
51
|
+
return result.stateTransition;
|
|
52
|
+
}
|
|
53
|
+
const { denomination, sendToAddressOnCreationFailure } = params;
|
|
54
|
+
return new IdentityCreateFromShieldedPoolTransitionWASM(result.publicKeys, denomination, result.actions, result.anchor, result.proof, result.bindingsSignature, sendToAddressOnCreationFailure, result.identityId).toStateTransition();
|
|
55
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { GetMostRecentShieldedAnchorRequest } from '../../proto/generated/platform.js';
|
|
2
|
+
import { verifyMostRecentShieldedAnchorProof } from "pshenmic-dpp";
|
|
3
|
+
import { LATEST_PLATFORM_VERSION } from "../constants.js";
|
|
4
|
+
import { getQuorumPublicKey } from "../utils/getQuorumPublicKey.js";
|
|
5
|
+
import bytesToHex from "../utils/bytesToHex.js";
|
|
6
|
+
import verifyTenderdashProof from "../utils/verifyTenderdashProof.js";
|
|
7
|
+
export default async function getMostRecentShieldedAnchor(grpcPool) {
|
|
8
|
+
const getMostRecentShieldedAnchorRequest = GetMostRecentShieldedAnchorRequest.create({
|
|
9
|
+
version: {
|
|
10
|
+
oneofKind: 'v0',
|
|
11
|
+
v0: {
|
|
12
|
+
prove: true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const { response } = await grpcPool.getClient().getMostRecentShieldedAnchor(getMostRecentShieldedAnchorRequest);
|
|
17
|
+
const { version } = response;
|
|
18
|
+
if (version.oneofKind !== 'v0') {
|
|
19
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
20
|
+
}
|
|
21
|
+
const { v0 } = version;
|
|
22
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
23
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
24
|
+
}
|
|
25
|
+
const { result: { proof }, metadata } = v0;
|
|
26
|
+
if (metadata == null) {
|
|
27
|
+
throw new Error('Metadata not found');
|
|
28
|
+
}
|
|
29
|
+
const { rootHash, anchor } = verifyMostRecentShieldedAnchorProof(proof.grovedbProof, true, LATEST_PLATFORM_VERSION);
|
|
30
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
31
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
32
|
+
if (!verify) {
|
|
33
|
+
throw new Error('Failed to verify query');
|
|
34
|
+
}
|
|
35
|
+
return anchor;
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { GetShieldedAnchorsRequest } from '../../proto/generated/platform.js';
|
|
2
|
+
import { verifyShieldedAnchorsProof } from "pshenmic-dpp";
|
|
3
|
+
import { LATEST_PLATFORM_VERSION } from "../constants.js";
|
|
4
|
+
import { getQuorumPublicKey } from "../utils/getQuorumPublicKey.js";
|
|
5
|
+
import bytesToHex from "../utils/bytesToHex.js";
|
|
6
|
+
import verifyTenderdashProof from "../utils/verifyTenderdashProof.js";
|
|
7
|
+
export default async function getShieldedAnchors(grpcPool) {
|
|
8
|
+
const getShieldedAnchorsRequest = GetShieldedAnchorsRequest.create({
|
|
9
|
+
version: {
|
|
10
|
+
oneofKind: 'v0',
|
|
11
|
+
v0: {
|
|
12
|
+
prove: true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const { response } = await grpcPool.getClient().getShieldedAnchors(getShieldedAnchorsRequest);
|
|
17
|
+
const { version } = response;
|
|
18
|
+
if (version.oneofKind !== 'v0') {
|
|
19
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
20
|
+
}
|
|
21
|
+
const { v0 } = version;
|
|
22
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
23
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
24
|
+
}
|
|
25
|
+
const { result: { proof }, metadata } = v0;
|
|
26
|
+
if (metadata == null) {
|
|
27
|
+
throw new Error('Metadata not found');
|
|
28
|
+
}
|
|
29
|
+
const { rootHash, anchors } = verifyShieldedAnchorsProof(proof.grovedbProof, true, LATEST_PLATFORM_VERSION);
|
|
30
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
31
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
32
|
+
if (!verify) {
|
|
33
|
+
throw new Error('Failed to verify query');
|
|
34
|
+
}
|
|
35
|
+
return anchors;
|
|
36
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { GetShieldedEncryptedNotesRequest } from '../../proto/generated/platform.js';
|
|
2
|
+
import { verifyShieldedEncryptedNotesProof } from "pshenmic-dpp";
|
|
3
|
+
import { LATEST_PLATFORM_VERSION, SHIELDED_MAX_NOTES_PER_QUERY } from "../constants.js";
|
|
4
|
+
import { getQuorumPublicKey } from "../utils/getQuorumPublicKey.js";
|
|
5
|
+
import bytesToHex from "../utils/bytesToHex.js";
|
|
6
|
+
import verifyTenderdashProof from "../utils/verifyTenderdashProof.js";
|
|
7
|
+
export default async function getShieldedEncryptedNotes(grpcPool, startIndex, count) {
|
|
8
|
+
const getShieldedEncryptedNotesRequest = GetShieldedEncryptedNotesRequest.create({
|
|
9
|
+
version: {
|
|
10
|
+
oneofKind: 'v0',
|
|
11
|
+
v0: {
|
|
12
|
+
startIndex: startIndex.toString(),
|
|
13
|
+
count,
|
|
14
|
+
prove: true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
const { response } = await grpcPool.getClient().getShieldedEncryptedNotes(getShieldedEncryptedNotesRequest);
|
|
19
|
+
const { version } = response;
|
|
20
|
+
if (version.oneofKind !== 'v0') {
|
|
21
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
22
|
+
}
|
|
23
|
+
const { v0 } = version;
|
|
24
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
25
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
26
|
+
}
|
|
27
|
+
const { result: { proof }, metadata } = v0;
|
|
28
|
+
if (metadata == null) {
|
|
29
|
+
throw new Error('Metadata not found');
|
|
30
|
+
}
|
|
31
|
+
const { rootHash, notes } = verifyShieldedEncryptedNotesProof(proof.grovedbProof, startIndex, count, SHIELDED_MAX_NOTES_PER_QUERY, true, LATEST_PLATFORM_VERSION);
|
|
32
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
33
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
34
|
+
if (!verify) {
|
|
35
|
+
throw new Error('Failed to verify query');
|
|
36
|
+
}
|
|
37
|
+
return notes.map(note => ({
|
|
38
|
+
nullifier: note.nullifier,
|
|
39
|
+
cmx: note.cmx,
|
|
40
|
+
encryptedNote: note.encryptedNote,
|
|
41
|
+
cvNet: note.cvNet
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { GetShieldedNotesCountRequest } from '../../proto/generated/platform.js';
|
|
2
|
+
import { verifyShieldedNotesCountProof } from "pshenmic-dpp";
|
|
3
|
+
import { LATEST_PLATFORM_VERSION } from "../constants.js";
|
|
4
|
+
import { getQuorumPublicKey } from "../utils/getQuorumPublicKey.js";
|
|
5
|
+
import bytesToHex from "../utils/bytesToHex.js";
|
|
6
|
+
import verifyTenderdashProof from "../utils/verifyTenderdashProof.js";
|
|
7
|
+
export default async function getShieldedNotesCount(grpcPool) {
|
|
8
|
+
const getShieldedNotesCountRequest = GetShieldedNotesCountRequest.create({
|
|
9
|
+
version: {
|
|
10
|
+
oneofKind: 'v0',
|
|
11
|
+
v0: {
|
|
12
|
+
prove: true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const { response } = await grpcPool.getClient().getShieldedNotesCount(getShieldedNotesCountRequest);
|
|
17
|
+
const { version } = response;
|
|
18
|
+
if (version.oneofKind !== 'v0') {
|
|
19
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
20
|
+
}
|
|
21
|
+
const { v0 } = version;
|
|
22
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
23
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
24
|
+
}
|
|
25
|
+
const { result: { proof }, metadata } = v0;
|
|
26
|
+
if (metadata == null) {
|
|
27
|
+
throw new Error('Metadata not found');
|
|
28
|
+
}
|
|
29
|
+
const { rootHash, count } = verifyShieldedNotesCountProof(proof.grovedbProof, true, LATEST_PLATFORM_VERSION);
|
|
30
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
31
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
32
|
+
if (!verify) {
|
|
33
|
+
throw new Error('Failed to verify query');
|
|
34
|
+
}
|
|
35
|
+
return count;
|
|
36
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { GetShieldedNullifiersRequest } from '../../proto/generated/platform.js';
|
|
2
|
+
import { verifyShieldedNullifiersProof } from "pshenmic-dpp";
|
|
3
|
+
import { LATEST_PLATFORM_VERSION } from "../constants.js";
|
|
4
|
+
import { getQuorumPublicKey } from "../utils/getQuorumPublicKey.js";
|
|
5
|
+
import bytesToHex from "../utils/bytesToHex.js";
|
|
6
|
+
import verifyTenderdashProof from "../utils/verifyTenderdashProof.js";
|
|
7
|
+
export default async function getShieldedNullifiers(grpcPool, nullifiers) {
|
|
8
|
+
const getShieldedNullifiersRequest = GetShieldedNullifiersRequest.create({
|
|
9
|
+
version: {
|
|
10
|
+
oneofKind: 'v0',
|
|
11
|
+
v0: {
|
|
12
|
+
nullifiers,
|
|
13
|
+
prove: true
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const { response } = await grpcPool.getClient().getShieldedNullifiers(getShieldedNullifiersRequest);
|
|
18
|
+
const { version } = response;
|
|
19
|
+
if (version.oneofKind !== 'v0') {
|
|
20
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
21
|
+
}
|
|
22
|
+
const { v0 } = version;
|
|
23
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
24
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
25
|
+
}
|
|
26
|
+
const { result: { proof }, metadata } = v0;
|
|
27
|
+
if (metadata == null) {
|
|
28
|
+
throw new Error('Metadata not found');
|
|
29
|
+
}
|
|
30
|
+
const { rootHash, nullifiers: nullifiersStatuses } = verifyShieldedNullifiersProof(proof.grovedbProof, nullifiers, true, LATEST_PLATFORM_VERSION);
|
|
31
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
32
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
33
|
+
if (!verify) {
|
|
34
|
+
throw new Error('Failed to verify query');
|
|
35
|
+
}
|
|
36
|
+
return nullifiersStatuses.map(entry => ({
|
|
37
|
+
nullifier: entry.nullifier,
|
|
38
|
+
isSpent: entry.isSpent
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { GetShieldedPoolStateRequest } from '../../proto/generated/platform.js';
|
|
2
|
+
import { getQuorumPublicKey } from "../utils/getQuorumPublicKey.js";
|
|
3
|
+
import bytesToHex from "../utils/bytesToHex.js";
|
|
4
|
+
import verifyTenderdashProof from "../utils/verifyTenderdashProof.js";
|
|
5
|
+
import { verifyShieldedPoolStateProof } from "pshenmic-dpp";
|
|
6
|
+
import { LATEST_PLATFORM_VERSION } from "../constants.js";
|
|
7
|
+
export default async function getShieldedPoolState(grpcPool) {
|
|
8
|
+
const getShieldedPoolStateRequest = GetShieldedPoolStateRequest.create({
|
|
9
|
+
version: {
|
|
10
|
+
oneofKind: 'v0',
|
|
11
|
+
v0: {
|
|
12
|
+
prove: true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const { response } = await grpcPool.getClient().getShieldedPoolState(getShieldedPoolStateRequest);
|
|
17
|
+
const { version } = response;
|
|
18
|
+
if (version.oneofKind !== 'v0') {
|
|
19
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
20
|
+
}
|
|
21
|
+
const { v0 } = version;
|
|
22
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
23
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
24
|
+
}
|
|
25
|
+
const { result: { proof }, metadata } = v0;
|
|
26
|
+
if (metadata == null) {
|
|
27
|
+
throw new Error('Metadata not found');
|
|
28
|
+
}
|
|
29
|
+
const { rootHash, totalBalance } = verifyShieldedPoolStateProof(proof.grovedbProof, true, LATEST_PLATFORM_VERSION);
|
|
30
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
31
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
32
|
+
if (!verify) {
|
|
33
|
+
throw new Error('Failed to verify query');
|
|
34
|
+
}
|
|
35
|
+
return totalBalance;
|
|
36
|
+
}
|
|
@@ -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
|
+
}
|