dash-platform-sdk 1.3.0-dev.5 → 1.3.0-dev.6
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/bundle.min.js +17 -17
- package/index.js +2 -0
- package/package.json +4 -4
- package/proto/generated/google/protobuf/wrappers.js +615 -0
- package/proto/generated/platform.client.js +175 -0
- package/proto/generated/platform.js +8277 -0
- package/src/DashPlatformSDK.js +101 -0
- package/src/constants.js +10 -0
- package/src/contestedResources/createStateTransition.js +6 -0
- package/src/contestedResources/getContestedResourceVoteState.js +72 -0
- package/src/contestedResources/index.js +30 -0
- package/src/dataContracts/create.js +11 -0
- package/src/dataContracts/createStateTransition.js +19 -0
- package/src/dataContracts/getDataContractByIdentifier.js +44 -0
- package/src/dataContracts/index.js +57 -0
- package/src/documents/create.js +4 -0
- package/src/documents/createStateTransition.js +52 -0
- package/src/documents/index.js +83 -0
- package/src/documents/query.js +62 -0
- package/src/grpcConnectionPool.js +79 -0
- package/src/identities/createStateTransition.js +34 -0
- package/src/identities/getIdentityBalance.js +40 -0
- package/src/identities/getIdentityByIdentifier.js +40 -0
- package/src/identities/getIdentityByNonUniquePublicKeyHash.js +44 -0
- package/src/identities/getIdentityByPublicKeyHash.js +40 -0
- package/src/identities/getIdentityContractNonce.js +43 -0
- package/src/identities/getIdentityNonce.js +41 -0
- package/src/identities/getIdentityPublicKeys.js +47 -0
- package/src/identities/index.js +141 -0
- package/src/keyPair/deriveChild.js +3 -0
- package/src/keyPair/derivePath.js +3 -0
- package/src/keyPair/index.js +89 -0
- package/src/keyPair/mnemonicToSeed.js +4 -0
- package/src/names/index.js +102 -0
- package/src/names/registerName.js +64 -0
- package/src/names/searchByIdentity.js +5 -0
- package/src/names/searchByName.js +16 -0
- package/src/names/testNameContested.js +3 -0
- package/src/names/validateName.js +11 -0
- package/src/node/epochs.js +46 -0
- package/src/node/index.js +43 -0
- package/src/node/status.js +41 -0
- package/src/node/totalCredits.js +35 -0
- package/src/signer/AbstractSigner.js +1 -0
- package/src/signer/PrivateKeySigner.d.ts +0 -0
- package/src/signer/PrivateKeySigner.js +64 -0
- package/src/signer/setSigner.js +5 -0
- package/src/stateTransitions/broadcast.js +10 -0
- package/src/stateTransitions/index.js +34 -0
- package/src/stateTransitions/waitForStateTransitionResult.js +8 -0
- package/src/tokens/createStateTransition.js +67 -0
- package/src/tokens/getIdentitiesTokenBalances.js +43 -0
- package/src/tokens/getIdentityTokensBalances.js +43 -0
- package/src/tokens/getTokenContractInfo.js +43 -0
- package/src/tokens/getTokenDirectPurchasePrices.js +40 -0
- package/src/tokens/getTokenTotalSupply.js +41 -0
- package/src/tokens/index.js +108 -0
- package/src/types.js +15 -0
- package/src/utils/base58ToBytes.js +4 -0
- package/src/utils/bytesToHex.js +3 -0
- package/src/utils/bytesToTypedArray.js +3 -0
- package/src/utils/calculateMsgHash.js +31 -0
- package/src/utils/calculateSignHash.js +8 -0
- package/src/utils/calculateStateIdHash.js +10 -0
- package/src/utils/convertToHomographSafeChars.js +11 -0
- package/src/utils/createVoterIdentityId.js +13 -0
- package/src/utils/getEvonodeList.js +12 -0
- package/src/utils/getQuorumPublicKey.js +18 -0
- package/src/utils/getRandomArrayItem.js +3 -0
- package/src/utils/getRandomBytes.js +4 -0
- package/src/utils/hexToBytes.js +3 -0
- package/src/utils/index.js +61 -0
- package/src/utils/indexBytesToString.js +4 -0
- package/src/utils/sha256.js +15 -0
- package/src/utils/signHash.js +22 -0
- package/src/utils/signRequestId.js +19 -0
- package/src/utils/sleep.js +3 -0
- package/src/utils/stringToIndexValueBytes.js +10 -0
- package/src/utils/verifyTenderdashProof.js +26 -0
- package/src/voting/createStateTransition.js +6 -0
- package/src/voting/createVote.js +5 -0
- package/src/voting/index.js +55 -0
- package/test/unit/ContestedResources.spec.js +259 -0
- package/test/unit/DataContract.spec.js +75 -0
- package/test/unit/Document.spec.js +109 -0
- package/test/unit/Identity.spec.js +232 -0
- package/test/unit/KeyPair.spec.js +34 -0
- package/test/unit/Names.spec.js +33 -0
- package/test/unit/Node.spec.js +58 -0
- package/test/unit/SDK.spec.js +10 -0
- package/test/unit/Tokens.spec.js +121 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { IdentifierWASM } from 'pshenmic-dpp';
|
|
2
|
+
import searchByName from './searchByName';
|
|
3
|
+
import searchByIdentity from './searchByIdentity';
|
|
4
|
+
import registerName from './registerName';
|
|
5
|
+
import validateName from './validateName';
|
|
6
|
+
import getIdentityByIdentifier from '../identities/getIdentityByIdentifier';
|
|
7
|
+
import convertToHomographSafeChars from '../utils/convertToHomographSafeChars';
|
|
8
|
+
import testNameContested from './testNameContested';
|
|
9
|
+
/**
|
|
10
|
+
* Functions related to DPNS names (usernames)
|
|
11
|
+
*
|
|
12
|
+
* @hideconstructor
|
|
13
|
+
*/
|
|
14
|
+
export class NamesController {
|
|
15
|
+
/** @ignore **/
|
|
16
|
+
grpcPool;
|
|
17
|
+
constructor(grpcPool) {
|
|
18
|
+
this.grpcPool = grpcPool;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Searches for a registered DPNS name in the network
|
|
22
|
+
*
|
|
23
|
+
* Should be in a human-readable format, for example pshenmic.dash
|
|
24
|
+
*
|
|
25
|
+
* Returns a {DocumentWASM} document of type 'domain' from DPNS system data contract if found,
|
|
26
|
+
* returns null if not found.
|
|
27
|
+
*
|
|
28
|
+
* https://testnet.platform-explorer.com/dataContract/GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec?tab=schema
|
|
29
|
+
*
|
|
30
|
+
* @param name {string}
|
|
31
|
+
*
|
|
32
|
+
* @return Promise<DocumentWASM | null>
|
|
33
|
+
*/
|
|
34
|
+
async searchByName(name) {
|
|
35
|
+
const validation = validateName(name);
|
|
36
|
+
if (validation != null) {
|
|
37
|
+
throw new Error(validation);
|
|
38
|
+
}
|
|
39
|
+
return await searchByName(this.grpcPool, name);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Tests a given username against contested names rules.
|
|
43
|
+
* Contested names includes an additional fee of 0.2 Dash
|
|
44
|
+
* as a voting resolution fee
|
|
45
|
+
*
|
|
46
|
+
* This function return boolean whether given username (f.e pshenmic.dash)
|
|
47
|
+
* falls under contested names rules.
|
|
48
|
+
* @param name
|
|
49
|
+
*/
|
|
50
|
+
testNameContested(name) {
|
|
51
|
+
const validation = validateName(name);
|
|
52
|
+
if (validation != null) {
|
|
53
|
+
throw new Error(validation);
|
|
54
|
+
}
|
|
55
|
+
const [label] = name.split('.');
|
|
56
|
+
const normalizedLabel = convertToHomographSafeChars(label);
|
|
57
|
+
return testNameContested(normalizedLabel);
|
|
58
|
+
}
|
|
59
|
+
async searchByIdentity(identifier) {
|
|
60
|
+
return await searchByIdentity(this.grpcPool, new IdentifierWASM(identifier));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Performs a DPNS name registration sequence
|
|
64
|
+
* Contested names are include additional fee of 0.2 Dash
|
|
65
|
+
* Check your name is contested with .testNameContested(name) method to check if additional fee will be charged
|
|
66
|
+
*
|
|
67
|
+
* @param name {string} username (ex. pshenmic.dash)
|
|
68
|
+
* @param identityId {IdentifierLike} identity identifier
|
|
69
|
+
* @param privateKey {PrivateKeyWASM} Authentication / High private key from your identity
|
|
70
|
+
*/
|
|
71
|
+
async registerName(name, identityId, privateKey) {
|
|
72
|
+
const validation = validateName(name);
|
|
73
|
+
if (validation != null) {
|
|
74
|
+
throw new Error(validation);
|
|
75
|
+
}
|
|
76
|
+
const identity = await getIdentityByIdentifier(this.grpcPool, identityId);
|
|
77
|
+
await registerName(this.grpcPool, name, identity, privateKey);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Converts DPNS name to normalized format (ex. alice.dash -> al1ce.dash)
|
|
81
|
+
*
|
|
82
|
+
* source: https://github.com/dashpay/platform/blob/master/packages/js-dash-sdk/src/utils/convertToHomographSafeChars.ts
|
|
83
|
+
*
|
|
84
|
+
*
|
|
85
|
+
* @param label {string}
|
|
86
|
+
*
|
|
87
|
+
* @return {string}
|
|
88
|
+
*/
|
|
89
|
+
normalizeLabel(label) {
|
|
90
|
+
return convertToHomographSafeChars(label);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Validates a DPNS name that you would like to register
|
|
94
|
+
*
|
|
95
|
+
* @param fullName {string} full DPNS name (ex. pshenmic.dash)
|
|
96
|
+
*
|
|
97
|
+
* @return {string} null if valid or string with a reason
|
|
98
|
+
*/
|
|
99
|
+
validateName(fullName) {
|
|
100
|
+
return validateName(fullName);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import convertToHomographSafeChars from '../utils/convertToHomographSafeChars';
|
|
2
|
+
import { PrefundedVotingBalanceWASM } from 'pshenmic-dpp';
|
|
3
|
+
import getRandomBytes from '../utils/getRandomBytes';
|
|
4
|
+
import sha256 from '../utils/sha256';
|
|
5
|
+
import createDocument from '../documents/create';
|
|
6
|
+
import createStateTransition from '../documents/createStateTransition';
|
|
7
|
+
import getIdentityContractNonce from '../identities/getIdentityContractNonce';
|
|
8
|
+
import broadcast from '../stateTransitions/broadcast';
|
|
9
|
+
import waitForStateTransitionResult from '../stateTransitions/waitForStateTransitionResult';
|
|
10
|
+
import testNameContested from './testNameContested';
|
|
11
|
+
import { DPNS_DATA_CONTRACT_ID } from '../constants';
|
|
12
|
+
export default async function registerName(grpcPool, name, identity, privateKey) {
|
|
13
|
+
const [identityPublicKey] = identity.getPublicKeys().filter(identityPublicKey => identityPublicKey.getPublicKeyHash() === privateKey.getPublicKeyHash());
|
|
14
|
+
if (identityPublicKey == null) {
|
|
15
|
+
throw new Error('Private key does not match the identity');
|
|
16
|
+
}
|
|
17
|
+
if (identityPublicKey.securityLevel !== 'HIGH' && identityPublicKey.purpose !== 'AUTHENTICATION') {
|
|
18
|
+
throw new Error('Wrong private key, must be from AUTHENTICATION HIGH identity public key');
|
|
19
|
+
}
|
|
20
|
+
const [label, parentDomainName] = name.split('.');
|
|
21
|
+
const salt = getRandomBytes(32);
|
|
22
|
+
const normalizedParentDomainName = convertToHomographSafeChars(parentDomainName);
|
|
23
|
+
const normalizedLabel = convertToHomographSafeChars(label);
|
|
24
|
+
const normalizedFullDomainName = `${normalizedLabel}.${normalizedParentDomainName}`;
|
|
25
|
+
const saltedDomainHash = await sha256(await sha256(new Uint8Array([
|
|
26
|
+
...salt,
|
|
27
|
+
...new TextEncoder().encode(normalizedFullDomainName)
|
|
28
|
+
])));
|
|
29
|
+
let document;
|
|
30
|
+
let stateTransition;
|
|
31
|
+
// 1. Create preorder document
|
|
32
|
+
const preorderData = {
|
|
33
|
+
saltedDomainHash: Array.from(saltedDomainHash)
|
|
34
|
+
};
|
|
35
|
+
const identityContractNonce = await getIdentityContractNonce(grpcPool, identity.id, DPNS_DATA_CONTRACT_ID);
|
|
36
|
+
document = createDocument(DPNS_DATA_CONTRACT_ID, 'preorder', preorderData, identity.id.base58());
|
|
37
|
+
stateTransition = createStateTransition(document, 'create', { identityContractNonce: identityContractNonce + BigInt(1) });
|
|
38
|
+
await stateTransition.sign(privateKey, identityPublicKey);
|
|
39
|
+
await broadcast(grpcPool, stateTransition);
|
|
40
|
+
// wait for state transition confirmation before next broadcast
|
|
41
|
+
await waitForStateTransitionResult(grpcPool, stateTransition);
|
|
42
|
+
// 2. Create domain document
|
|
43
|
+
const domainData = {
|
|
44
|
+
label,
|
|
45
|
+
normalizedLabel,
|
|
46
|
+
parentDomainName,
|
|
47
|
+
normalizedParentDomainName,
|
|
48
|
+
preorderSalt: Array.from(salt),
|
|
49
|
+
records: {
|
|
50
|
+
identity: Array.from(identity.id.bytes())
|
|
51
|
+
},
|
|
52
|
+
subdomainRules: {
|
|
53
|
+
allowSubdomains: false
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
document = createDocument(DPNS_DATA_CONTRACT_ID, 'domain', domainData, identity.id.base58());
|
|
57
|
+
stateTransition = createStateTransition(document, 'create', {
|
|
58
|
+
identityContractNonce: identityContractNonce + BigInt(2),
|
|
59
|
+
// @ts-expect-error
|
|
60
|
+
prefundedVotingBalance: testNameContested(normalizedLabel) ? new PrefundedVotingBalanceWASM('parentNameAndLabel', BigInt(20000000000)) : undefined
|
|
61
|
+
});
|
|
62
|
+
await stateTransition.sign(privateKey, identityPublicKey);
|
|
63
|
+
await broadcast(grpcPool, stateTransition);
|
|
64
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import query from '../documents/query';
|
|
2
|
+
const DPNS_DATA_CONTRACT_ID = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec';
|
|
3
|
+
export default async function searchByIdentity(grpcPool, identifier) {
|
|
4
|
+
return await query(grpcPool, DPNS_DATA_CONTRACT_ID, 'domain', [['records.identity', '=', identifier.base58()]], [['records.identity', 'asc']]);
|
|
5
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import convertToHomographSafeChars from '../utils/convertToHomographSafeChars';
|
|
2
|
+
import query from '../documents/query';
|
|
3
|
+
const DPNS_DATA_CONTRACT_ID = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec';
|
|
4
|
+
export default async function search(grpcPool, name) {
|
|
5
|
+
const [label, parentDomainName] = name.split('.');
|
|
6
|
+
const normalizedParentDomainName = convertToHomographSafeChars(parentDomainName);
|
|
7
|
+
const normalizedLabelPrefix = convertToHomographSafeChars(label);
|
|
8
|
+
const where = [
|
|
9
|
+
['normalizedParentDomainName', '==', normalizedParentDomainName],
|
|
10
|
+
['normalizedLabel', 'startsWith', normalizedLabelPrefix]
|
|
11
|
+
];
|
|
12
|
+
const orderBy = [
|
|
13
|
+
['normalizedLabel', 'asc']
|
|
14
|
+
];
|
|
15
|
+
return await query(grpcPool, DPNS_DATA_CONTRACT_ID, 'domain', where, orderBy);
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export default function validateName(fullName) {
|
|
2
|
+
const chunks = fullName.split('.');
|
|
3
|
+
if (chunks.length !== 2) {
|
|
4
|
+
return 'Name to search should be a full domain name (ex. pshenmic.dash)';
|
|
5
|
+
}
|
|
6
|
+
const [label, parentDomainName] = chunks;
|
|
7
|
+
if (parentDomainName !== 'dash') {
|
|
8
|
+
return 'Root domain must be .dash';
|
|
9
|
+
}
|
|
10
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$/.test(label) ? null : 'Unacceptable label name';
|
|
11
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { GetEpochsInfoRequest } from '../../proto/generated/platform';
|
|
2
|
+
import { PlatformVersionWASM, verifyEpochsInfoProof } from 'pshenmic-dpp';
|
|
3
|
+
import { getQuorumPublicKey } from '../utils/getQuorumPublicKey';
|
|
4
|
+
import bytesToHex from '../utils/bytesToHex';
|
|
5
|
+
import verifyTenderdashProof from '../utils/verifyTenderdashProof';
|
|
6
|
+
import { UInt32Value } from '../../proto/generated/google/protobuf/wrappers';
|
|
7
|
+
export default async function epochs(grpcPool, count, ascending, start) {
|
|
8
|
+
const getEpochsInfoRequest = GetEpochsInfoRequest.create({
|
|
9
|
+
version: {
|
|
10
|
+
oneofKind: 'v0',
|
|
11
|
+
v0: {
|
|
12
|
+
startEpoch: start != null ? UInt32Value.create({ value: start }) : undefined,
|
|
13
|
+
count,
|
|
14
|
+
ascending,
|
|
15
|
+
prove: true
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const { response } = await grpcPool.getClient().getEpochsInfo(getEpochsInfoRequest);
|
|
20
|
+
const { version } = response;
|
|
21
|
+
if (version.oneofKind !== 'v0') {
|
|
22
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
23
|
+
}
|
|
24
|
+
const { v0 } = version;
|
|
25
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
26
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
27
|
+
}
|
|
28
|
+
const { result: { proof }, metadata } = v0;
|
|
29
|
+
if (metadata == null) {
|
|
30
|
+
throw new Error('Metadata not found');
|
|
31
|
+
}
|
|
32
|
+
const { rootHash, epochsInfo } = verifyEpochsInfoProof(proof.grovedbProof, metadata.epoch, start, count, ascending, PlatformVersionWASM.PLATFORM_V9);
|
|
33
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
34
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
35
|
+
if (!verify) {
|
|
36
|
+
throw new Error('Failed to verify query');
|
|
37
|
+
}
|
|
38
|
+
return epochsInfo.map(info => ({
|
|
39
|
+
number: info.index,
|
|
40
|
+
firstBlockHeight: info.firstBlockHeight,
|
|
41
|
+
firstCoreBlockHeight: info.firstCoreBlockHeight,
|
|
42
|
+
startTime: info.firstBlockTime,
|
|
43
|
+
feeMultiplier: info.feeMultiplierPermille,
|
|
44
|
+
protocolVersion: info.protocolVersion
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import getStatus from './status';
|
|
2
|
+
import getEpochsInfo from './epochs';
|
|
3
|
+
import getTotalCredits from './totalCredits';
|
|
4
|
+
/**
|
|
5
|
+
* Node controller for requesting information about DAPI node
|
|
6
|
+
*
|
|
7
|
+
* @hideconstructor
|
|
8
|
+
*/
|
|
9
|
+
export class NodeController {
|
|
10
|
+
/** @ignore **/
|
|
11
|
+
grpcPool;
|
|
12
|
+
network;
|
|
13
|
+
constructor(grpcPool, network) {
|
|
14
|
+
this.grpcPool = grpcPool;
|
|
15
|
+
this.network = network;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves an info about node
|
|
19
|
+
* Includes information about genesis, chain, software versions
|
|
20
|
+
*
|
|
21
|
+
* @return {Promise<NodeStatus>}
|
|
22
|
+
*/
|
|
23
|
+
async status() {
|
|
24
|
+
return await getStatus(this.grpcPool);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns total credits amount in platform
|
|
28
|
+
*
|
|
29
|
+
* @return {Promise<bigint>}
|
|
30
|
+
*/
|
|
31
|
+
async totalCredits() {
|
|
32
|
+
return await getTotalCredits(this.grpcPool, this.network);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Retrieves an info about epochs
|
|
36
|
+
* Includes information about first block height, time, fee multiplier, number
|
|
37
|
+
*
|
|
38
|
+
* @return {Promise<EpochInfo[]>}
|
|
39
|
+
*/
|
|
40
|
+
async getEpochsInfo(count, ascending, start) {
|
|
41
|
+
return await getEpochsInfo(this.grpcPool, count, ascending, start);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { GetStatusRequest } from '../../proto/generated/platform';
|
|
2
|
+
import bytesToHex from '../utils/bytesToHex';
|
|
3
|
+
export default async function status(grpcPool) {
|
|
4
|
+
const getStatusRequest = GetStatusRequest.create({
|
|
5
|
+
version: {
|
|
6
|
+
oneofKind: 'v0',
|
|
7
|
+
v0: {}
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
const { response } = await grpcPool.getClient().getStatus(getStatusRequest);
|
|
11
|
+
const { version } = response;
|
|
12
|
+
if (version.oneofKind !== 'v0') {
|
|
13
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
14
|
+
}
|
|
15
|
+
const { v0 } = version;
|
|
16
|
+
return {
|
|
17
|
+
node: (v0.node != null)
|
|
18
|
+
? {
|
|
19
|
+
id: bytesToHex(v0.node.id),
|
|
20
|
+
proTxHash: v0.node.proTxHash != null ? bytesToHex(v0.node.proTxHash) : undefined
|
|
21
|
+
}
|
|
22
|
+
: undefined,
|
|
23
|
+
chain: (v0.chain != null)
|
|
24
|
+
? {
|
|
25
|
+
catchingUp: v0.chain.catchingUp,
|
|
26
|
+
latestBlockHeight: v0.chain.latestBlockHeight,
|
|
27
|
+
earliestBlockHeight: v0.chain.earliestBlockHeight,
|
|
28
|
+
maxPeerBlockHeight: v0.chain.maxPeerBlockHeight,
|
|
29
|
+
coreChainLockedHeight: v0.chain.coreChainLockedHeight,
|
|
30
|
+
latestBlockHash: v0.chain?.latestBlockHash != null ? bytesToHex(v0.chain?.latestBlockHash) : '',
|
|
31
|
+
latestAppHash: v0.chain?.latestAppHash != null ? bytesToHex(v0.chain?.latestAppHash) : '',
|
|
32
|
+
earliestBlockHash: v0.chain?.earliestBlockHash != null ? bytesToHex(v0.chain?.earliestBlockHash) : '',
|
|
33
|
+
earliestAppHash: v0.chain?.earliestAppHash != null ? bytesToHex(v0.chain?.earliestAppHash) : ''
|
|
34
|
+
}
|
|
35
|
+
: undefined,
|
|
36
|
+
version: v0.version,
|
|
37
|
+
network: v0.network,
|
|
38
|
+
stateSync: v0.stateSync,
|
|
39
|
+
time: v0.time
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { GetTotalCreditsInPlatformRequest } from '../../proto/generated/platform';
|
|
2
|
+
import { HALVING_INTERVAL, MAINNET_ACTIVATION_HEIGHT, TESTNET_ACTIVATION_HEIGHT } from '../constants';
|
|
3
|
+
import { PlatformVersionWASM, verifyTotalCreditsProof } from 'pshenmic-dpp';
|
|
4
|
+
import { getQuorumPublicKey } from '../utils/getQuorumPublicKey';
|
|
5
|
+
import bytesToHex from '../utils/bytesToHex';
|
|
6
|
+
import verifyTenderdashProof from '../utils/verifyTenderdashProof';
|
|
7
|
+
export default async function totalCredits(grpcPool, network) {
|
|
8
|
+
const getTotalCreditsInPlatformRequest = GetTotalCreditsInPlatformRequest.create({
|
|
9
|
+
version: {
|
|
10
|
+
oneofKind: 'v0',
|
|
11
|
+
v0: { prove: true }
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const { response } = await grpcPool.getClient().getTotalCreditsInPlatform(getTotalCreditsInPlatformRequest);
|
|
15
|
+
const { version } = response;
|
|
16
|
+
if (version.oneofKind !== 'v0') {
|
|
17
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
18
|
+
}
|
|
19
|
+
const { v0 } = version;
|
|
20
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
21
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
22
|
+
}
|
|
23
|
+
const { result: { proof }, metadata } = v0;
|
|
24
|
+
if (metadata == null) {
|
|
25
|
+
throw new Error('Metadata not found');
|
|
26
|
+
}
|
|
27
|
+
const activationHeight = network === 'testnet' ? TESTNET_ACTIVATION_HEIGHT : MAINNET_ACTIVATION_HEIGHT;
|
|
28
|
+
const { rootHash, totalCredits } = verifyTotalCreditsProof(proof.grovedbProof, HALVING_INTERVAL, activationHeight, Number(metadata.height), PlatformVersionWASM.PLATFORM_V9);
|
|
29
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
30
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
31
|
+
if (!verify) {
|
|
32
|
+
throw new Error('Failed to verify query');
|
|
33
|
+
}
|
|
34
|
+
return totalCredits;
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// import { IdentityWASM, NetworkWASM, PrivateKeyWASM, StateTransitionWASM } from 'pshenmic-dpp'
|
|
2
|
+
// import { base64 } from '@scure/base'
|
|
3
|
+
// import { IdentitiesController } from '../identities'
|
|
4
|
+
// import {AbstractSigner} from "./AbstractSigner";
|
|
5
|
+
//
|
|
6
|
+
// export class PrivateKeySigner implements AbstractSigner {
|
|
7
|
+
// privateKey: PrivateKeyWASM
|
|
8
|
+
// identity: IdentityWASM
|
|
9
|
+
// identities: IdentitiesController
|
|
10
|
+
//
|
|
11
|
+
// constructor (identitiesController: IdentitiesController, privateKey: string | PrivateKeyWASM, network?: NetworkWASM | string) {
|
|
12
|
+
// this.identities = identitiesController
|
|
13
|
+
//
|
|
14
|
+
// if (typeof privateKey === 'string') {
|
|
15
|
+
// let privateKeyWASM
|
|
16
|
+
//
|
|
17
|
+
// try {
|
|
18
|
+
// privateKeyWASM = PrivateKeyWASM.fromWIF(privateKey)
|
|
19
|
+
// } catch (e) {
|
|
20
|
+
// }
|
|
21
|
+
//
|
|
22
|
+
// if (privateKey.length === 64 && !network) {
|
|
23
|
+
// throw new Error('Network must be specified if hex or base64 private key being used')
|
|
24
|
+
// }
|
|
25
|
+
//
|
|
26
|
+
// try {
|
|
27
|
+
// privateKeyWASM = PrivateKeyWASM.fromHex(privateKey, network)
|
|
28
|
+
// } catch (e) {
|
|
29
|
+
// }
|
|
30
|
+
//
|
|
31
|
+
// try {
|
|
32
|
+
// privateKeyWASM = PrivateKeyWASM.fromBytes(base64.decode(privateKey), network)
|
|
33
|
+
// } catch (e) {
|
|
34
|
+
// }
|
|
35
|
+
//
|
|
36
|
+
// if (privateKeyWASM == null) {
|
|
37
|
+
// throw new Error('Could not decode private key')
|
|
38
|
+
// }
|
|
39
|
+
// } else if (privateKey?.__type === 'PrivateKeyWASM') {
|
|
40
|
+
// this.privateKey = privateKey
|
|
41
|
+
// } else {
|
|
42
|
+
// throw new Error('Unrecognized private key type')
|
|
43
|
+
// }
|
|
44
|
+
// }
|
|
45
|
+
//
|
|
46
|
+
// async connect (): Promise<void> {
|
|
47
|
+
// this.identity = await this.identities.getIdentityByPublicKeyHash(this.privateKey.getPublicKeyHash())
|
|
48
|
+
// }
|
|
49
|
+
//
|
|
50
|
+
// getCurrentIdentity (): IdentityWASM {
|
|
51
|
+
// return this.identity
|
|
52
|
+
// }
|
|
53
|
+
//
|
|
54
|
+
// signStateTransition (stateTransition: StateTransitionWASM): void {
|
|
55
|
+
// const [identityPublicKey] = this.identity.getPublicKeys()
|
|
56
|
+
// .filter(identityPublicKey => identityPublicKey.getPublicKeyHash() === this.privateKey.getPublicKeyHash())
|
|
57
|
+
//
|
|
58
|
+
// if (identityPublicKey == null) {
|
|
59
|
+
// throw new Error('Could not find an identity public key of identity matching this private key')
|
|
60
|
+
// }
|
|
61
|
+
//
|
|
62
|
+
// stateTransition.sign(this.privateKey, identityPublicKey)
|
|
63
|
+
// }
|
|
64
|
+
// }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BroadcastStateTransitionRequest } from '../../proto/generated/platform';
|
|
2
|
+
export default async function broadcast(grpcPool, stateTransition) {
|
|
3
|
+
if (stateTransition.signature.length === 0) {
|
|
4
|
+
throw new Error('State Transition is not signed');
|
|
5
|
+
}
|
|
6
|
+
const broadcastStateTransitionRequest = BroadcastStateTransitionRequest.create({
|
|
7
|
+
stateTransition: stateTransition.bytes()
|
|
8
|
+
});
|
|
9
|
+
await grpcPool.getClient().broadcastStateTransition(broadcastStateTransitionRequest);
|
|
10
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import waitForStateTransitionResult from './waitForStateTransitionResult';
|
|
2
|
+
import broadcast from './broadcast';
|
|
3
|
+
/**
|
|
4
|
+
* Collection of methods to perform state transitions (transactions) such like broadcast, wait for state transition result
|
|
5
|
+
*
|
|
6
|
+
* @hideconstructor
|
|
7
|
+
*/
|
|
8
|
+
export class StateTransitionsController {
|
|
9
|
+
/** @ignore **/
|
|
10
|
+
grpcPool;
|
|
11
|
+
constructor(grpcPool) {
|
|
12
|
+
this.grpcPool = grpcPool;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Broadcasts a state transition to a network
|
|
16
|
+
*
|
|
17
|
+
* @param stateTransition {StateTransitionWASM}
|
|
18
|
+
*
|
|
19
|
+
* @return {Promise<void>}
|
|
20
|
+
*/
|
|
21
|
+
async broadcast(stateTransition) {
|
|
22
|
+
return await broadcast(this.grpcPool, stateTransition);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Waits for a given state transition to finalize in the network (usually 1-3 sec)
|
|
26
|
+
*
|
|
27
|
+
* @param stateTransition {StateTransitionWASM}
|
|
28
|
+
*
|
|
29
|
+
* @return {Promise<void>}
|
|
30
|
+
*/
|
|
31
|
+
async waitForStateTransitionResult(stateTransition) {
|
|
32
|
+
return await waitForStateTransitionResult(this.grpcPool, stateTransition);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default async function waitForStateTransitionResult(grpcPool, stateTransition) {
|
|
2
|
+
const url = `https://${grpcPool.network === 'mainnet' ? '' : 'testnet.'}platform-explorer.pshenmic.dev/waitForStateTransitionResult/${stateTransition.hash(false)}`;
|
|
3
|
+
const resp = await fetch(url);
|
|
4
|
+
if (resp.status !== 200) {
|
|
5
|
+
console.log(await resp.json());
|
|
6
|
+
throw new Error('Internal server error while waiting for state transition result');
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { BatchedTransitionWASM, BatchTransitionWASM, TokenBurnTransitionWASM, TokenDestroyFrozenFundsTransitionWASM, TokenDirectPurchaseTransitionWASM, TokenEmergencyActionTransitionWASM, TokenFreezeTransitionWASM, TokenMintTransitionWASM, TokenSetPriceForDirectPurchaseTransitionWASM, TokenTransferTransitionWASM, TokenTransitionWASM, TokenUnFreezeTransitionWASM } from 'pshenmic-dpp';
|
|
2
|
+
const tokenTransitionsMap = {
|
|
3
|
+
burn: {
|
|
4
|
+
class: TokenBurnTransitionWASM,
|
|
5
|
+
arguments: ['amount', 'publicNote'],
|
|
6
|
+
optionalArguments: ['publicNote']
|
|
7
|
+
},
|
|
8
|
+
mint: {
|
|
9
|
+
class: TokenMintTransitionWASM,
|
|
10
|
+
arguments: ['identityId', 'amount', 'publicNote'],
|
|
11
|
+
optionalArguments: ['publicNote']
|
|
12
|
+
},
|
|
13
|
+
transfer: {
|
|
14
|
+
class: TokenTransferTransitionWASM,
|
|
15
|
+
arguments: ['identityId', 'amount', 'publicNote', 'sharedEncryptedNote', 'privateEncryptedNote'],
|
|
16
|
+
optionalArguments: ['publicNote', 'sharedEncryptedNote', 'privateEncryptedNote']
|
|
17
|
+
},
|
|
18
|
+
freeze: {
|
|
19
|
+
class: TokenFreezeTransitionWASM,
|
|
20
|
+
arguments: ['identityId', 'publicNote'],
|
|
21
|
+
optionalArguments: ['publicNote']
|
|
22
|
+
},
|
|
23
|
+
unfreeze: {
|
|
24
|
+
class: TokenUnFreezeTransitionWASM,
|
|
25
|
+
arguments: ['identityId', 'publicNote'],
|
|
26
|
+
optionalArguments: ['publicNote']
|
|
27
|
+
},
|
|
28
|
+
destroyFrozenFunds: {
|
|
29
|
+
class: TokenDestroyFrozenFundsTransitionWASM,
|
|
30
|
+
arguments: ['identityId', 'publicNote'],
|
|
31
|
+
optionalArguments: ['publicNote']
|
|
32
|
+
},
|
|
33
|
+
emergencyAction: {
|
|
34
|
+
class: TokenEmergencyActionTransitionWASM,
|
|
35
|
+
arguments: ['emergencyAction', 'publicNote'],
|
|
36
|
+
optionalArguments: ['publicNote']
|
|
37
|
+
},
|
|
38
|
+
directPurchase: {
|
|
39
|
+
class: TokenDirectPurchaseTransitionWASM,
|
|
40
|
+
arguments: ['amount', 'totalAgreedPrice'],
|
|
41
|
+
optionalArguments: []
|
|
42
|
+
},
|
|
43
|
+
setPriceForDirectPurchase: {
|
|
44
|
+
class: TokenSetPriceForDirectPurchaseTransitionWASM,
|
|
45
|
+
arguments: ['price', 'publicNote'],
|
|
46
|
+
optionalArguments: ['publicNote']
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
export default function createStateTransition(base, ownerId, type, params) {
|
|
50
|
+
const { class: TransitionClass, arguments: classArguments, optionalArguments } = tokenTransitionsMap[type];
|
|
51
|
+
if (TransitionClass == null) {
|
|
52
|
+
throw new Error(`Unimplemented transition type: ${type}`);
|
|
53
|
+
}
|
|
54
|
+
// check if all required params for token transition exists
|
|
55
|
+
const [missingArgument] = classArguments
|
|
56
|
+
.filter((classArgument) => params[classArgument] == null &&
|
|
57
|
+
!optionalArguments.includes(classArgument));
|
|
58
|
+
if (missingArgument != null) {
|
|
59
|
+
throw new Error(`Token transition param "${missingArgument}" is missing`);
|
|
60
|
+
}
|
|
61
|
+
const transitionParams = classArguments.map((classArgument) => params[classArgument]);
|
|
62
|
+
// @ts-expect-error
|
|
63
|
+
const tokenTransition = new TransitionClass(base, ...transitionParams);
|
|
64
|
+
const tokenTransitionWASM = new TokenTransitionWASM(tokenTransition);
|
|
65
|
+
const batchedTransition = new BatchedTransitionWASM(tokenTransitionWASM);
|
|
66
|
+
return BatchTransitionWASM.fromV1BatchedTransitions([batchedTransition], ownerId, 1).toStateTransition();
|
|
67
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { GetIdentitiesTokenBalancesRequest } from '../../proto/generated/platform';
|
|
2
|
+
import { IdentifierWASM, PlatformVersionWASM, verifyTokenBalancesForIdentitiesProof } from 'pshenmic-dpp';
|
|
3
|
+
import { getQuorumPublicKey } from '../utils/getQuorumPublicKey';
|
|
4
|
+
import bytesToHex from '../utils/bytesToHex';
|
|
5
|
+
import verifyTenderdashProof from '../utils/verifyTenderdashProof';
|
|
6
|
+
export default async function getIdentitiesTokenBalances(grpcPool, identifiers, tokenIdentifier) {
|
|
7
|
+
const ids = identifiers.map(identifier => new IdentifierWASM(identifier));
|
|
8
|
+
const tokenId = new IdentifierWASM(tokenIdentifier);
|
|
9
|
+
const getIdentitiesTokenBalancesRequest = GetIdentitiesTokenBalancesRequest.create({
|
|
10
|
+
version: {
|
|
11
|
+
oneofKind: 'v0',
|
|
12
|
+
v0: {
|
|
13
|
+
tokenId: tokenId.bytes(),
|
|
14
|
+
identityIds: ids.map(id => id.bytes()),
|
|
15
|
+
prove: true
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const { response } = await grpcPool.getClient().getIdentitiesTokenBalances(getIdentitiesTokenBalancesRequest);
|
|
20
|
+
const { version } = response;
|
|
21
|
+
if (version.oneofKind !== 'v0') {
|
|
22
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
23
|
+
}
|
|
24
|
+
const { v0 } = version;
|
|
25
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
26
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
27
|
+
}
|
|
28
|
+
const { result: { proof }, metadata } = v0;
|
|
29
|
+
if (metadata == null) {
|
|
30
|
+
throw new Error('Metadata not found');
|
|
31
|
+
}
|
|
32
|
+
const { rootHash, balances } = verifyTokenBalancesForIdentitiesProof(proof.grovedbProof, tokenId, true, ids, PlatformVersionWASM.PLATFORM_V9);
|
|
33
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
34
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
35
|
+
if (!verify) {
|
|
36
|
+
throw new Error('Failed to verify query');
|
|
37
|
+
}
|
|
38
|
+
return balances
|
|
39
|
+
.map((identityTokenBalance) => ({
|
|
40
|
+
identityId: new IdentifierWASM(identityTokenBalance.identityId),
|
|
41
|
+
balance: identityTokenBalance.balance
|
|
42
|
+
}));
|
|
43
|
+
}
|