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,101 @@
|
|
|
1
|
+
import GRPCConnectionPool from './grpcConnectionPool';
|
|
2
|
+
import { IdentitiesController } from './identities';
|
|
3
|
+
import { StateTransitionsController } from './stateTransitions';
|
|
4
|
+
import { DocumentsController } from './documents';
|
|
5
|
+
import { UtilsController } from './utils';
|
|
6
|
+
import { KeyPairController } from './keyPair';
|
|
7
|
+
import { NodeController } from './node';
|
|
8
|
+
import { NamesController } from './names';
|
|
9
|
+
import { DataContractsController } from './dataContracts';
|
|
10
|
+
import { ContestedResourcesController } from './contestedResources';
|
|
11
|
+
import { TokensController } from './tokens';
|
|
12
|
+
import { VotingController } from './voting';
|
|
13
|
+
/**
|
|
14
|
+
* Javascript SDK for that let you interact with a Dash Platform blockchain
|
|
15
|
+
*/
|
|
16
|
+
export class DashPlatformSDK {
|
|
17
|
+
network;
|
|
18
|
+
/** @ignore **/
|
|
19
|
+
grpcPool;
|
|
20
|
+
/** @ignore **/
|
|
21
|
+
options;
|
|
22
|
+
contestedResources;
|
|
23
|
+
stateTransitions;
|
|
24
|
+
dataContracts;
|
|
25
|
+
identities;
|
|
26
|
+
documents;
|
|
27
|
+
keyPair;
|
|
28
|
+
voting;
|
|
29
|
+
tokens;
|
|
30
|
+
utils;
|
|
31
|
+
names;
|
|
32
|
+
signer;
|
|
33
|
+
node;
|
|
34
|
+
/**
|
|
35
|
+
* Constructs a new DashPlatformSDK instance, optionally pass options
|
|
36
|
+
* if you want to configure the SDK instance (network, dapiUrl, signer)
|
|
37
|
+
*
|
|
38
|
+
* @param options {SDKOptions=}
|
|
39
|
+
*/
|
|
40
|
+
constructor(options) {
|
|
41
|
+
if (options != null && (options.network == null || !['testnet', 'mainnet'].includes(options.network))) {
|
|
42
|
+
throw new Error('If options is passed, network must be set (either mainnet or testnet)');
|
|
43
|
+
}
|
|
44
|
+
this.network = options?.network ?? 'mainnet';
|
|
45
|
+
this.signer = options?.signer;
|
|
46
|
+
this.options = options;
|
|
47
|
+
this.utils = new UtilsController();
|
|
48
|
+
// Compatibility
|
|
49
|
+
if (options?.dapiUrl != null && ((options?.grpc) == null)) {
|
|
50
|
+
// @ts-expect-error
|
|
51
|
+
this.options.grpc = { dapiUrl: options.dapiUrl };
|
|
52
|
+
}
|
|
53
|
+
this.grpcPool = new GRPCConnectionPool(this.network, this.options?.grpc);
|
|
54
|
+
this._initialize(this.grpcPool, this.network);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* @private
|
|
58
|
+
*
|
|
59
|
+
* Internal function to initialize SDK GRPC connection pool. Is not meant to be used outside the SDK
|
|
60
|
+
*
|
|
61
|
+
* @param grpcPool
|
|
62
|
+
* @param network
|
|
63
|
+
*/
|
|
64
|
+
_initialize(grpcPool, network) {
|
|
65
|
+
this.grpcPool = grpcPool;
|
|
66
|
+
this.stateTransitions = new StateTransitionsController(grpcPool);
|
|
67
|
+
this.contestedResources = new ContestedResourcesController(grpcPool);
|
|
68
|
+
this.dataContracts = new DataContractsController(grpcPool);
|
|
69
|
+
this.identities = new IdentitiesController(grpcPool);
|
|
70
|
+
this.documents = new DocumentsController(grpcPool);
|
|
71
|
+
this.voting = new VotingController();
|
|
72
|
+
this.node = new NodeController(grpcPool, network);
|
|
73
|
+
this.tokens = new TokensController(grpcPool);
|
|
74
|
+
this.names = new NamesController(grpcPool);
|
|
75
|
+
this.keyPair = new KeyPairController();
|
|
76
|
+
}
|
|
77
|
+
setSigner(signer) {
|
|
78
|
+
this.signer = signer;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get currently used network
|
|
82
|
+
*
|
|
83
|
+
* @return {string}
|
|
84
|
+
*/
|
|
85
|
+
getNetwork() {
|
|
86
|
+
return this.network;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Switches a network that SDK is currently connected to
|
|
90
|
+
*
|
|
91
|
+
* @param network {string}
|
|
92
|
+
*/
|
|
93
|
+
setNetwork(network) {
|
|
94
|
+
if (network !== 'testnet' && network !== 'mainnet') {
|
|
95
|
+
throw new Error('Unknown network, should be mainnet or testnet');
|
|
96
|
+
}
|
|
97
|
+
this.network = network;
|
|
98
|
+
const grpcPool = new GRPCConnectionPool(this.network, this.options?.grpc);
|
|
99
|
+
this._initialize(grpcPool, this.network);
|
|
100
|
+
}
|
|
101
|
+
}
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default amount of documents to retrieve from DAPI
|
|
3
|
+
*/
|
|
4
|
+
export const DAPI_DEFAULT_LIMIT = 100;
|
|
5
|
+
export const HALVING_INTERVAL = 210240;
|
|
6
|
+
export const TESTNET_ACTIVATION_HEIGHT = 1066900;
|
|
7
|
+
export const MAINNET_ACTIVATION_HEIGHT = 2128896;
|
|
8
|
+
export const GRPC_DEFAULT_POOL_LIMIT = 5;
|
|
9
|
+
export const DPNS_DATA_CONTRACT_ID = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec';
|
|
10
|
+
export const DPNS_DATA_CONTRACT_BYTES = 'AeZoxlmvZq7h5ywYbd57W34KHXEqCcQNVyH2Ir9TxTFVAAAAAAABAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGZG9tYWluFgsSEGRvY3VtZW50c011dGFibGUTABIMY2FuQmVEZWxldGVkEwESDHRyYW5zZmVyYWJsZQIBEgl0cmFkZU1vZGUCARIEdHlwZRIGb2JqZWN0EgdpbmRpY2VzFQIWBBIEbmFtZRIScGFyZW50TmFtZUFuZExhYmVsEgpwcm9wZXJ0aWVzFQIWARIabm9ybWFsaXplZFBhcmVudERvbWFpbk5hbWUSA2FzYxYBEg9ub3JtYWxpemVkTGFiZWwSA2FzYxIGdW5pcXVlEwESCWNvbnRlc3RlZBYDEgxmaWVsZE1hdGNoZXMVARYCEgVmaWVsZBIPbm9ybWFsaXplZExhYmVsEgxyZWdleFBhdHRlcm4SE15bYS16QS1aMDEtXXszLDE5fSQSCnJlc29sdXRpb24CABILZGVzY3JpcHRpb24SqklmIHRoZSBub3JtYWxpemVkIGxhYmVsIHBhcnQgb2YgdGhpcyBpbmRleCBpcyBsZXNzIHRoYW4gMjAgY2hhcmFjdGVycyAoYWxsIGFscGhhYmV0IGEteiwgQS1aLCAwLCAxLCBhbmQgLSkgdGhlbiBhIG1hc3Rlcm5vZGUgdm90ZSBjb250ZXN0IHRha2VzIHBsYWNlIHRvIGdpdmUgb3V0IHRoZSBuYW1lFgMSBG5hbWUSCmlkZW50aXR5SWQSDm51bGxTZWFyY2hhYmxlEwASCnByb3BlcnRpZXMVARYBEhByZWNvcmRzLmlkZW50aXR5EgNhc2MSCnByb3BlcnRpZXMWBxIFbGFiZWwWBhIEdHlwZRIGc3RyaW5nEgdwYXR0ZXJuEipeW2EtekEtWjAtOV1bYS16QS1aMC05LV17MCw2MX1bYS16QS1aMC05XSQSCW1pbkxlbmd0aAIDEgltYXhMZW5ndGgCPxIIcG9zaXRpb24CABILZGVzY3JpcHRpb24SGURvbWFpbiBsYWJlbC4gZS5nLiAnQm9iJy4SD25vcm1hbGl6ZWRMYWJlbBYGEgR0eXBlEgZzdHJpbmcSB3BhdHRlcm4SPF5bYS1oai1rbS1ucC16MC05XVthLWhqLWttLW5wLXowLTktXXswLDYxfVthLWhqLWttLW5wLXowLTldJBIJbWF4TGVuZ3RoAj8SCHBvc2l0aW9uAgESC2Rlc2NyaXB0aW9uEqNEb21haW4gbGFiZWwgY29udmVydGVkIHRvIGxvd2VyY2FzZSBmb3IgY2FzZS1pbnNlbnNpdGl2ZSB1bmlxdWVuZXNzIHZhbGlkYXRpb24uICJvIiwgImkiIGFuZCAibCIgcmVwbGFjZWQgd2l0aCAiMCIgYW5kICIxIiB0byBtaXRpZ2F0ZSBob21vZ3JhcGggYXR0YWNrLiBlLmcuICdiMGInEggkY29tbWVudBJcTXVzdCBiZSBlcXVhbCB0byB0aGUgbGFiZWwgaW4gbG93ZXJjYXNlLiAibyIsICJpIiBhbmQgImwiIG11c3QgYmUgcmVwbGFjZWQgd2l0aCAiMCIgYW5kICIxIi4SEHBhcmVudERvbWFpbk5hbWUWBhIEdHlwZRIGc3RyaW5nEgdwYXR0ZXJuEi1eJHxeW2EtekEtWjAtOV1bYS16QS1aMC05LV17MCw2MX1bYS16QS1aMC05XSQSCW1pbkxlbmd0aAIAEgltYXhMZW5ndGgCPxIIcG9zaXRpb24CAhILZGVzY3JpcHRpb24SJ0EgZnVsbCBwYXJlbnQgZG9tYWluIG5hbWUuIGUuZy4gJ2Rhc2gnLhIabm9ybWFsaXplZFBhcmVudERvbWFpbk5hbWUWBxIEdHlwZRIGc3RyaW5nEgdwYXR0ZXJuEkFeJHxeW2EtaGota20tbnAtejAtOV1bYS1oai1rbS1ucC16MC05LVwuXXswLDYxfVthLWhqLWttLW5wLXowLTldJBIJbWluTGVuZ3RoAgASCW1heExlbmd0aAI/Eghwb3NpdGlvbgIDEgtkZXNjcmlwdGlvbhKiQSBwYXJlbnQgZG9tYWluIG5hbWUgaW4gbG93ZXJjYXNlIGZvciBjYXNlLWluc2Vuc2l0aXZlIHVuaXF1ZW5lc3MgdmFsaWRhdGlvbi4gIm8iLCAiaSIgYW5kICJsIiByZXBsYWNlZCB3aXRoICIwIiBhbmQgIjEiIHRvIG1pdGlnYXRlIGhvbW9ncmFwaCBhdHRhY2suIGUuZy4gJ2Rhc2gnEggkY29tbWVudBLATXVzdCBlaXRoZXIgYmUgZXF1YWwgdG8gYW4gZXhpc3RpbmcgZG9tYWluIG9yIGVtcHR5IHRvIGNyZWF0ZSBhIHRvcCBsZXZlbCBkb21haW4uICJvIiwgImkiIGFuZCAibCIgbXVzdCBiZSByZXBsYWNlZCB3aXRoICIwIiBhbmQgIjEiLiBPbmx5IHRoZSBkYXRhIGNvbnRyYWN0IG93bmVyIGNhbiBjcmVhdGUgdG9wIGxldmVsIGRvbWFpbnMuEgxwcmVvcmRlclNhbHQWBhIEdHlwZRIFYXJyYXkSCWJ5dGVBcnJheRMBEghtaW5JdGVtcwIgEghtYXhJdGVtcwIgEghwb3NpdGlvbgIEEgtkZXNjcmlwdGlvbhIiU2FsdCB1c2VkIGluIHRoZSBwcmVvcmRlciBkb2N1bWVudBIHcmVjb3JkcxYFEgR0eXBlEgZvYmplY3QSCnByb3BlcnRpZXMWARIIaWRlbnRpdHkWBxIEdHlwZRIFYXJyYXkSCWJ5dGVBcnJheRMBEghtaW5JdGVtcwIgEghtYXhJdGVtcwIgEghwb3NpdGlvbgIBEhBjb250ZW50TWVkaWFUeXBlEiFhcHBsaWNhdGlvbi94LmRhc2guZHBwLmlkZW50aWZpZXISC2Rlc2NyaXB0aW9uEjFJZGVudGlmaWVyIG5hbWUgcmVjb3JkIHRoYXQgcmVmZXJzIHRvIGFuIElkZW50aXR5Eg1taW5Qcm9wZXJ0aWVzAgESCHBvc2l0aW9uAgUSFGFkZGl0aW9uYWxQcm9wZXJ0aWVzEwASDnN1YmRvbWFpblJ1bGVzFgYSBHR5cGUSBm9iamVjdBIKcHJvcGVydGllcxYBEg9hbGxvd1N1YmRvbWFpbnMWBBIEdHlwZRIHYm9vbGVhbhILZGVzY3JpcHRpb24SW1RoaXMgb3B0aW9uIGRlZmluZXMgd2hvIGNhbiBjcmVhdGUgc3ViZG9tYWluczogdHJ1ZSAtIGFueW9uZTsgZmFsc2UgLSBvbmx5IHRoZSBkb21haW4gb3duZXISCCRjb21tZW50Ek9Pbmx5IHRoZSBkb21haW4gb3duZXIgaXMgYWxsb3dlZCB0byBjcmVhdGUgc3ViZG9tYWlucyBmb3Igbm9uIHRvcC1sZXZlbCBkb21haW5zEghwb3NpdGlvbgIAEghwb3NpdGlvbgIGEgtkZXNjcmlwdGlvbhJCU3ViZG9tYWluIHJ1bGVzIGFsbG93IGRvbWFpbiBvd25lcnMgdG8gZGVmaW5lIHJ1bGVzIGZvciBzdWJkb21haW5zEhRhZGRpdGlvbmFsUHJvcGVydGllcxMAEghyZXF1aXJlZBUBEg9hbGxvd1N1YmRvbWFpbnMSCHJlcXVpcmVkFQkSCiRjcmVhdGVkQXQSCiR1cGRhdGVkQXQSDiR0cmFuc2ZlcnJlZEF0EgVsYWJlbBIPbm9ybWFsaXplZExhYmVsEhpub3JtYWxpemVkUGFyZW50RG9tYWluTmFtZRIMcHJlb3JkZXJTYWx0EgdyZWNvcmRzEg5zdWJkb21haW5SdWxlcxIJdHJhbnNpZW50FQESDHByZW9yZGVyU2FsdBIUYWRkaXRpb25hbFByb3BlcnRpZXMTABIIJGNvbW1lbnQS+wE3SW4gb3JkZXIgdG8gcmVnaXN0ZXIgYSBkb21haW4geW91IG5lZWQgdG8gY3JlYXRlIGEgcHJlb3JkZXIuIFRoZSBwcmVvcmRlciBzdGVwIGlzIG5lZWRlZCB0byBwcmV2ZW50IG1hbi1pbi10aGUtbWlkZGxlIGF0dGFja3MuIG5vcm1hbGl6ZWRMYWJlbCArICcuJyArIG5vcm1hbGl6ZWRQYXJlbnREb21haW4gbXVzdCBub3QgYmUgbG9uZ2VyIHRoYW4gMjUzIGNoYXJzIGxlbmd0aCBhcyBkZWZpbmVkIGJ5IFJGQyAxMDM1LiBEb21haW4gZG9jdW1lbnRzIGFyZSBpbW11dGFibGU6IG1vZGlmaWNhdGlvbiBhbmQgZGVsZXRpb24gYXJlIHJlc3RyaWN0ZWQIcHJlb3JkZXIWCBIQZG9jdW1lbnRzTXV0YWJsZRMAEgxjYW5CZURlbGV0ZWQTARIEdHlwZRIGb2JqZWN0EgdpbmRpY2VzFQEWAxIEbmFtZRIKc2FsdGVkSGFzaBIKcHJvcGVydGllcxUBFgESEHNhbHRlZERvbWFpbkhhc2gSA2FzYxIGdW5pcXVlEwESCnByb3BlcnRpZXMWARIQc2FsdGVkRG9tYWluSGFzaBYGEgR0eXBlEgVhcnJheRIJYnl0ZUFycmF5EwESCG1pbkl0ZW1zAiASCG1heEl0ZW1zAiASCHBvc2l0aW9uAgASC2Rlc2NyaXB0aW9uEllEb3VibGUgc2hhLTI1NiBvZiB0aGUgY29uY2F0ZW5hdGlvbiBvZiBhIDMyIGJ5dGUgcmFuZG9tIHNhbHQgYW5kIGEgbm9ybWFsaXplZCBkb21haW4gbmFtZRIIcmVxdWlyZWQVARIQc2FsdGVkRG9tYWluSGFzaBIUYWRkaXRpb25hbFByb3BlcnRpZXMTABIIJGNvbW1lbnQSSlByZW9yZGVyIGRvY3VtZW50cyBhcmUgaW1tdXRhYmxlOiBtb2RpZmljYXRpb24gYW5kIGRlbGV0aW9uIGFyZSByZXN0cmljdGVkAAAAAAAAAAAAAA==';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { IdentifierWASM, MasternodeVoteTransitionWASM } from 'pshenmic-dpp';
|
|
2
|
+
export default function createStateTransition(voteWASM, proTxHash, identityNonce) {
|
|
3
|
+
const voterIdentity = IdentifierWASM.fromHex(proTxHash);
|
|
4
|
+
const masternodeVoteTransition = new MasternodeVoteTransitionWASM(proTxHash, voterIdentity, voteWASM, identityNonce);
|
|
5
|
+
return masternodeVoteTransition.toStateTransition();
|
|
6
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { GetContestedResourceVoteStateRequest } from '../../proto/generated/platform';
|
|
2
|
+
import { DocumentWASM, IdentifierWASM, PlatformVersionWASM, verifyVotePollVoteStateProof } from 'pshenmic-dpp';
|
|
3
|
+
import verifyTenderdashProof from '../utils/verifyTenderdashProof';
|
|
4
|
+
import { getQuorumPublicKey } from '../utils/getQuorumPublicKey';
|
|
5
|
+
import bytesToHex from '../utils/bytesToHex';
|
|
6
|
+
export default async function getContestedResourceVoteState(grpcPool, contract, documentTypeName, indexName, indexValues, resultType, allowIncludeLockedAndAbstainingVoteTally, startAtIdentifierInfo, count) {
|
|
7
|
+
if (startAtIdentifierInfo != null) {
|
|
8
|
+
startAtIdentifierInfo = {
|
|
9
|
+
startIdentifier: (new IdentifierWASM(startAtIdentifierInfo.startIdentifier)).bytes(),
|
|
10
|
+
startIdentifierIncluded: startAtIdentifierInfo.startIdentifierIncluded
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const getContestedResourceVoteStateRequest = GetContestedResourceVoteStateRequest.create({
|
|
14
|
+
version: {
|
|
15
|
+
oneofKind: 'v0',
|
|
16
|
+
v0: {
|
|
17
|
+
contractId: contract.id.bytes(),
|
|
18
|
+
documentTypeName,
|
|
19
|
+
indexName,
|
|
20
|
+
indexValues,
|
|
21
|
+
resultType: resultType,
|
|
22
|
+
allowIncludeLockedAndAbstainingVoteTally,
|
|
23
|
+
startAtIdentifierInfo,
|
|
24
|
+
count,
|
|
25
|
+
prove: true
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const { response } = await grpcPool.getClient().getContestedResourceVoteState(getContestedResourceVoteStateRequest);
|
|
30
|
+
const { version } = response;
|
|
31
|
+
if (version.oneofKind !== 'v0') {
|
|
32
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
33
|
+
}
|
|
34
|
+
const { v0 } = version;
|
|
35
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
36
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
37
|
+
}
|
|
38
|
+
const { result: { proof }, metadata } = v0;
|
|
39
|
+
if (metadata == null) {
|
|
40
|
+
throw new Error('Metadata not found');
|
|
41
|
+
}
|
|
42
|
+
const { rootHash, result } = verifyVotePollVoteStateProof(proof.grovedbProof, contract, documentTypeName, indexName, indexValues, resultType, allowIncludeLockedAndAbstainingVoteTally, count, startAtIdentifierInfo, PlatformVersionWASM.PLATFORM_V9);
|
|
43
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
44
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
45
|
+
if (!verify) {
|
|
46
|
+
throw new Error('Failed to verify query');
|
|
47
|
+
}
|
|
48
|
+
const { contenders } = result ?? { contenders: [] };
|
|
49
|
+
const { winner } = result ?? { winner: undefined };
|
|
50
|
+
if (contenders.length === 0) {
|
|
51
|
+
throw new Error('Vote state not found');
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
contenders: contenders.map(contender => ({
|
|
55
|
+
...contender,
|
|
56
|
+
identifier: contender.identityId,
|
|
57
|
+
document: contender.serializedDocument != null ? DocumentWASM.fromBytes(contender.serializedDocument, contract, documentTypeName, PlatformVersionWASM.PLATFORM_V9) : undefined
|
|
58
|
+
})),
|
|
59
|
+
abstainVoteTally: result?.abstainingVoteTally ?? 0,
|
|
60
|
+
lockVoteTally: result?.lockedVoteTally ?? 0,
|
|
61
|
+
finishedVoteInfo: (winner != null)
|
|
62
|
+
? {
|
|
63
|
+
type: winner.type,
|
|
64
|
+
wonByIdentityId: winner.identityId != null ? new IdentifierWASM(winner.identityId) : undefined,
|
|
65
|
+
finishedAtBlockHeight: winner.blockInfo.height,
|
|
66
|
+
finishedAtCoreBlockHeight: winner.blockInfo.coreHeight,
|
|
67
|
+
finishedAtBlockTimeMs: winner.blockInfo.timeMs,
|
|
68
|
+
finishedAtEpoch: winner.blockInfo.epoch
|
|
69
|
+
}
|
|
70
|
+
: undefined
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import getContestedResourceVoteState from './getContestedResourceVoteState';
|
|
2
|
+
/**
|
|
3
|
+
* Contested Resources controller for requesting information about contested resources
|
|
4
|
+
*
|
|
5
|
+
* @hideconstructor
|
|
6
|
+
*/
|
|
7
|
+
export class ContestedResourcesController {
|
|
8
|
+
/** @ignore **/
|
|
9
|
+
grpcPool;
|
|
10
|
+
constructor(grpcPool) {
|
|
11
|
+
this.grpcPool = grpcPool;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Retrieves an info about vote state for contested resource
|
|
15
|
+
*
|
|
16
|
+
* @param contract {DataContractWASM} - instance of contract with contested resource
|
|
17
|
+
* @param documentTypeName {string} - document type name of contested resource
|
|
18
|
+
* @param indexName {string} - index name of contested resource
|
|
19
|
+
* @param indexValuesBytes {Uint8Array[]} - Array of contested values in bytes
|
|
20
|
+
* @param resultType {ContestedResourceVoteState} - enum for result info
|
|
21
|
+
* @param allowIncludeLockedAndAbstainingVoteTally {boolean}
|
|
22
|
+
* @param startAtIdentifierInfo {StartAtIdentifierInfo=}
|
|
23
|
+
* @param count {number=}
|
|
24
|
+
*
|
|
25
|
+
* @return {Promise<ContestedResourceVoteState>}
|
|
26
|
+
*/
|
|
27
|
+
async getContestedResourceVoteState(contract, documentTypeName, indexName, indexValuesBytes, resultType, allowIncludeLockedAndAbstainingVoteTally, startAtIdentifierInfo, count) {
|
|
28
|
+
return await getContestedResourceVoteState(this.grpcPool, contract, documentTypeName, indexName, indexValuesBytes, resultType, allowIncludeLockedAndAbstainingVoteTally, startAtIdentifierInfo, count);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DataContractWASM, IdentifierWASM, PlatformVersionWASM } from 'pshenmic-dpp';
|
|
2
|
+
export default function createDataContract(ownerId, identityNonce, schema, tokenConfiguration, config, fullValidation, platformVersion) {
|
|
3
|
+
const id = new IdentifierWASM(ownerId);
|
|
4
|
+
const dataContract = new DataContractWASM(id, identityNonce, schema,
|
|
5
|
+
// we don't know what that param means yet
|
|
6
|
+
null, tokenConfiguration, fullValidation ?? true, platformVersion ?? PlatformVersionWASM.PLATFORM_V9);
|
|
7
|
+
if (config != null) {
|
|
8
|
+
dataContract.setConfig(config, platformVersion);
|
|
9
|
+
}
|
|
10
|
+
return dataContract;
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DataContractCreateTransitionWASM, DataContractUpdateTransitionWASM } from 'pshenmic-dpp';
|
|
2
|
+
export var DataContractTransitionType;
|
|
3
|
+
(function (DataContractTransitionType) {
|
|
4
|
+
DataContractTransitionType[DataContractTransitionType["Create"] = 0] = "Create";
|
|
5
|
+
DataContractTransitionType[DataContractTransitionType["Update"] = 1] = "Update";
|
|
6
|
+
})(DataContractTransitionType || (DataContractTransitionType = {}));
|
|
7
|
+
const dataContractTransitionsMap = {
|
|
8
|
+
create: DataContractCreateTransitionWASM,
|
|
9
|
+
update: DataContractUpdateTransitionWASM
|
|
10
|
+
};
|
|
11
|
+
export default function createStateTransition(dataContract, type, identityNonce) {
|
|
12
|
+
const TransitionClass = dataContractTransitionsMap[type];
|
|
13
|
+
if (TransitionClass == null) {
|
|
14
|
+
throw new Error(`Unknown DataContract transition type: ${type}. Should be 'create' or 'update'.`);
|
|
15
|
+
}
|
|
16
|
+
// @ts-expect-error
|
|
17
|
+
const dataContractTransition = new TransitionClass(dataContract, identityNonce);
|
|
18
|
+
return dataContractTransition.toStateTransition();
|
|
19
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DataContractWASM, IdentifierWASM, PlatformVersionWASM, verifyContractProof } from 'pshenmic-dpp';
|
|
2
|
+
import { GetDataContractRequest } from '../../proto/generated/platform';
|
|
3
|
+
import { getQuorumPublicKey } from '../utils/getQuorumPublicKey';
|
|
4
|
+
import bytesToHex from '../utils/bytesToHex';
|
|
5
|
+
import verifyTenderdashProof from '../utils/verifyTenderdashProof';
|
|
6
|
+
import { DPNS_DATA_CONTRACT_BYTES, DPNS_DATA_CONTRACT_ID } from '../constants';
|
|
7
|
+
export default async function getByIdentifier(grpcPool, identifier) {
|
|
8
|
+
const id = new IdentifierWASM(identifier);
|
|
9
|
+
if (id.base58() === DPNS_DATA_CONTRACT_ID) {
|
|
10
|
+
return DataContractWASM.fromBase64(DPNS_DATA_CONTRACT_BYTES, true, 9);
|
|
11
|
+
}
|
|
12
|
+
const getDataContractRequest = GetDataContractRequest.create({
|
|
13
|
+
version: {
|
|
14
|
+
oneofKind: 'v0',
|
|
15
|
+
v0: {
|
|
16
|
+
id: id.bytes(),
|
|
17
|
+
prove: true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const { response } = await grpcPool.getClient().getDataContract(getDataContractRequest);
|
|
22
|
+
const { version } = response;
|
|
23
|
+
if (version.oneofKind !== 'v0') {
|
|
24
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
25
|
+
}
|
|
26
|
+
const { v0 } = version;
|
|
27
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
28
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
29
|
+
}
|
|
30
|
+
const { result: { proof }, metadata } = v0;
|
|
31
|
+
if (metadata == null) {
|
|
32
|
+
throw new Error('Metadata not found');
|
|
33
|
+
}
|
|
34
|
+
const { rootHash, dataContract } = verifyContractProof(proof.grovedbProof, undefined, false, false, id.bytes(), PlatformVersionWASM.PLATFORM_V9);
|
|
35
|
+
if (dataContract == null) {
|
|
36
|
+
throw new Error(`Data Contract with identifier ${id.base58()} not found`);
|
|
37
|
+
}
|
|
38
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
39
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
40
|
+
if (!verify) {
|
|
41
|
+
throw new Error('Failed to verify query');
|
|
42
|
+
}
|
|
43
|
+
return dataContract;
|
|
44
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import getDataContractByIdentifier from './getDataContractByIdentifier';
|
|
2
|
+
import createDataContract from './create';
|
|
3
|
+
import createStateTransition from './createStateTransition';
|
|
4
|
+
/**
|
|
5
|
+
* Collection of methods necessary to work with Data Contracts in the network,
|
|
6
|
+
* such as data contract creation or retrieval
|
|
7
|
+
*
|
|
8
|
+
* @hideconstructor
|
|
9
|
+
*/
|
|
10
|
+
export class DataContractsController {
|
|
11
|
+
/** @ignore */
|
|
12
|
+
grpcPool;
|
|
13
|
+
constructor(grpcPool) {
|
|
14
|
+
this.grpcPool = grpcPool;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates an instance of {DataContractWASM} that can be used to
|
|
18
|
+
* register a data contract in the network.
|
|
19
|
+
*
|
|
20
|
+
* @param ownerId {IdentifierLike} Identifier of the Data Contract's owner
|
|
21
|
+
* @param identityNonce {bigint} Identity nonce
|
|
22
|
+
* @param schema {object} Data Contract schema in json
|
|
23
|
+
* @param tokenConfiguration {object=} Token configuration
|
|
24
|
+
* @param config {DataContractConfig=} Data Contract config
|
|
25
|
+
* @param fullValidation {true=} Full validation (omit it)
|
|
26
|
+
* @param platformVersion {PlatformVersionWASM=} version of the platform
|
|
27
|
+
*
|
|
28
|
+
* @return {DataContractWASM}
|
|
29
|
+
*/
|
|
30
|
+
create(ownerId, identityNonce, schema, fullValidation, tokenConfiguration, config, platformVersion) {
|
|
31
|
+
return createDataContract(ownerId, identityNonce, schema, tokenConfiguration, config, fullValidation, platformVersion);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Retrieves a Data Contract by an identifier from the network
|
|
35
|
+
*
|
|
36
|
+
* @param identifier {IdentifierLike} Identifier of the Data Contract
|
|
37
|
+
*
|
|
38
|
+
* @return {Promise<DataContractWASM>}
|
|
39
|
+
*/
|
|
40
|
+
async getDataContractByIdentifier(identifier) {
|
|
41
|
+
return await getDataContractByIdentifier(this.grpcPool, identifier);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Helper function to create a state transition from a {DataContractWASM} instance
|
|
45
|
+
* Pass {DataContractTransitionType} in type param to specify which type of operation you want
|
|
46
|
+
* to make: create or update.
|
|
47
|
+
*
|
|
48
|
+
* @param dataContract {DataContractWASM} An instance of DataContractWASM to create or update
|
|
49
|
+
* @param type {string} type of identity state transition to do, must be 'create' or 'update'
|
|
50
|
+
* @param identityNonce {bigint} identity contract nonce
|
|
51
|
+
*
|
|
52
|
+
* @return {StateTransitionWASM}
|
|
53
|
+
*/
|
|
54
|
+
createStateTransition(dataContract, type, identityNonce) {
|
|
55
|
+
return createStateTransition(dataContract, type, identityNonce);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BatchedTransitionWASM, BatchTransitionWASM, DocumentCreateTransitionWASM, DocumentDeleteTransitionWASM, DocumentPurchaseTransitionWASM, DocumentReplaceTransitionWASM, DocumentTransferTransitionWASM, DocumentUpdatePriceTransitionWASM } from 'pshenmic-dpp';
|
|
2
|
+
const documentTransitionsMap = {
|
|
3
|
+
create: {
|
|
4
|
+
class: DocumentCreateTransitionWASM,
|
|
5
|
+
arguments: ['identityContractNonce'],
|
|
6
|
+
optionalArguments: ['prefundedVotingBalance', 'tokenPaymentInfo']
|
|
7
|
+
},
|
|
8
|
+
replace: {
|
|
9
|
+
class: DocumentReplaceTransitionWASM,
|
|
10
|
+
arguments: ['identityContractNonce'],
|
|
11
|
+
optionalArguments: ['tokenPaymentInfo']
|
|
12
|
+
},
|
|
13
|
+
delete: {
|
|
14
|
+
class: DocumentDeleteTransitionWASM,
|
|
15
|
+
arguments: ['identityContractNonce'],
|
|
16
|
+
optionalArguments: ['tokenPaymentInfo']
|
|
17
|
+
},
|
|
18
|
+
updatePrice: {
|
|
19
|
+
class: DocumentUpdatePriceTransitionWASM,
|
|
20
|
+
arguments: ['identityContractNonce', 'price'],
|
|
21
|
+
optionalArguments: ['tokenPaymentInfo']
|
|
22
|
+
},
|
|
23
|
+
transfer: {
|
|
24
|
+
class: DocumentTransferTransitionWASM,
|
|
25
|
+
arguments: ['identityContractNonce', 'recipientId'],
|
|
26
|
+
optionalArguments: ['tokenPaymentInfo']
|
|
27
|
+
},
|
|
28
|
+
purchase: {
|
|
29
|
+
class: DocumentPurchaseTransitionWASM,
|
|
30
|
+
arguments: ['identityContractNonce', 'amount'],
|
|
31
|
+
optionalArguments: ['tokenPaymentInfo']
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
export default function createStateTransition(document, type, params) {
|
|
35
|
+
const { class: TransitionClass, arguments: classArguments, optionalArguments } = documentTransitionsMap[type];
|
|
36
|
+
if (TransitionClass == null) {
|
|
37
|
+
throw new Error(`Unimplemented transition type: ${type}`);
|
|
38
|
+
}
|
|
39
|
+
// check if all required params for document transition exists
|
|
40
|
+
const [missingArgument] = classArguments
|
|
41
|
+
.filter((classArgument) => params[classArgument] == null &&
|
|
42
|
+
!(optionalArguments).includes(classArgument));
|
|
43
|
+
if (missingArgument != null) {
|
|
44
|
+
throw new Error(`Document transition param "${missingArgument}" is missing`);
|
|
45
|
+
}
|
|
46
|
+
const transitionParams = classArguments.concat(optionalArguments).map((classArgument) => params[classArgument]);
|
|
47
|
+
// @ts-expect-error
|
|
48
|
+
const documentTransition = new TransitionClass(document, ...transitionParams).toDocumentTransition();
|
|
49
|
+
const batchedTransition = new BatchedTransitionWASM(documentTransition);
|
|
50
|
+
const batch = BatchTransitionWASM.fromV1BatchedTransitions([batchedTransition], document.ownerId, 1);
|
|
51
|
+
return batch.toStateTransition();
|
|
52
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import createDocument from './create';
|
|
2
|
+
import { IdentifierWASM, PrefundedVotingBalanceWASM, TokenPaymentInfoWASM } from 'pshenmic-dpp';
|
|
3
|
+
import createStateTransition from './createStateTransition';
|
|
4
|
+
import query from './query';
|
|
5
|
+
/**
|
|
6
|
+
* Collection of methods to work with documents like creation, querying or preparing a transition action
|
|
7
|
+
*
|
|
8
|
+
* @hideconstructor
|
|
9
|
+
*/
|
|
10
|
+
export class DocumentsController {
|
|
11
|
+
/** @ignore **/
|
|
12
|
+
grpcPool;
|
|
13
|
+
constructor(grpcPool) {
|
|
14
|
+
this.grpcPool = grpcPool;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates a {DocumentWASM} instance that can be used to submit a document in the network.
|
|
18
|
+
*
|
|
19
|
+
* @param dataContractId {IdentifierLike} Identifier of the Data Contract
|
|
20
|
+
* @param documentType {string} Document type as string
|
|
21
|
+
* @param data {object} Data as JSON object
|
|
22
|
+
* @param owner {IdentifierLike} Identifier of the document's owner
|
|
23
|
+
* @param revision {bigint=} revision (optional)
|
|
24
|
+
*
|
|
25
|
+
* @return {DataContractWASM}
|
|
26
|
+
*/
|
|
27
|
+
create(dataContractId, documentType, data, owner, revision) {
|
|
28
|
+
return createDocument(new IdentifierWASM(dataContractId), documentType, data, new IdentifierWASM(owner), revision);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Make a query for documents retrieval from the network.
|
|
32
|
+
*
|
|
33
|
+
* @param dataContractId {IdentifierLike} Identifier of the Data Contract where fetch documents from
|
|
34
|
+
* @param documentType {string} Document type of the data contract
|
|
35
|
+
* @param where {object=} Where query clauses according syntax https://docs.dash.org/projects/platform/en/stable/docs/reference/query-syntax.html
|
|
36
|
+
* @param orderBy {object=} Order by clauses according syntax https://docs.dash.org/projects/platform/en/stable/docs/reference/query-syntax.html
|
|
37
|
+
* @param limit {number=} Limit amount of documents per request
|
|
38
|
+
* @param startAt {IdentifierLike=} Optional offset, where startAt is an identifier of the document. Use identifier of last item in previous response
|
|
39
|
+
* @param startAfter {IdentifierLike=} Same as previous, but with exclusion. Cannot be set if startAt already provided
|
|
40
|
+
*/
|
|
41
|
+
async query(dataContractId, documentType, where, orderBy, limit, startAt, startAfter) {
|
|
42
|
+
if (startAfter != null && startAt !== null) {
|
|
43
|
+
throw new Error('You may only set either startAfter or startAt at once');
|
|
44
|
+
}
|
|
45
|
+
return await query(this.grpcPool, new IdentifierWASM(dataContractId), documentType, where, orderBy, limit ?? 100, startAt, startAfter);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Helper function for creating {StateTransitionWASM} from documents. It may be used to create any of 6 Document Transition actions:
|
|
49
|
+
*
|
|
50
|
+
* 1) Create - creates a document
|
|
51
|
+
* 2) Replace - replaces a document
|
|
52
|
+
* 3) Delete - deletes a document
|
|
53
|
+
* 4) Transfer - transfer a document from one identity to another
|
|
54
|
+
* 5) SetPrice - set a price for a document in credits
|
|
55
|
+
* 6) Purchase - purchase a document from identity (if price was set)
|
|
56
|
+
*
|
|
57
|
+
* @param document {DocumentWASM} Instance of the document to make transition with
|
|
58
|
+
* @param type {string} Type of the document transition, must be a one of ('create' | 'replace' | 'delete' | 'updatePrice' |'transfer' | 'purchase')
|
|
59
|
+
* @param params {DocumentTransitionParams} params
|
|
60
|
+
*/
|
|
61
|
+
createStateTransition(document, type, params) {
|
|
62
|
+
if (type === 'transfer' && params.recipientId == null) {
|
|
63
|
+
throw new Error('Recipient is required for transfer transition');
|
|
64
|
+
}
|
|
65
|
+
if (type === 'updatePrice' && params.price == null) {
|
|
66
|
+
throw new Error('Price is required for updatePrice transition');
|
|
67
|
+
}
|
|
68
|
+
if (type === 'purchase' && params.amount == null) {
|
|
69
|
+
throw new Error('Amount is required for updatePrice transition');
|
|
70
|
+
}
|
|
71
|
+
if (params.prefundedVotingBalance != null) {
|
|
72
|
+
const { indexName, amount } = params.prefundedVotingBalance;
|
|
73
|
+
// @ts-expect-error
|
|
74
|
+
params.prefundedVotingBalance = new PrefundedVotingBalanceWASM(indexName, amount);
|
|
75
|
+
}
|
|
76
|
+
if (params.tokenPaymentInfo != null) {
|
|
77
|
+
const { tokenContractId, tokenContractPosition, minimumTokenCost, maximumTokenCost, gasFeesPaidBy } = params.tokenPaymentInfo;
|
|
78
|
+
// @ts-expect-error
|
|
79
|
+
params.tokenPaymentInfo = new TokenPaymentInfoWASM(new IdentifierWASM(tokenContractId), tokenContractPosition, minimumTokenCost, maximumTokenCost, gasFeesPaidBy);
|
|
80
|
+
}
|
|
81
|
+
return createStateTransition(document, type, params);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { GetDocumentsRequest } from '../../proto/generated/platform';
|
|
2
|
+
import { IdentifierWASM, PlatformVersionWASM, verifyDocumentsProof } from 'pshenmic-dpp';
|
|
3
|
+
import { DAPI_DEFAULT_LIMIT } from '../constants';
|
|
4
|
+
import { getQuorumPublicKey } from '../utils/getQuorumPublicKey';
|
|
5
|
+
import bytesToHex from '../utils/bytesToHex';
|
|
6
|
+
import verifyTenderdashProof from '../utils/verifyTenderdashProof';
|
|
7
|
+
import { encode } from 'cbor-x';
|
|
8
|
+
import getDataContractByIdentifier from '../dataContracts/getDataContractByIdentifier';
|
|
9
|
+
export default async function query(grpcPool, dataContractId, documentTypeName, where, orderBy, limit = 100, startAt, startAfter) {
|
|
10
|
+
if ([startAt, startAfter].filter(e => e != null).length === 2) {
|
|
11
|
+
throw new Error('Only startAt or startAfter could be specified at one time');
|
|
12
|
+
}
|
|
13
|
+
let start;
|
|
14
|
+
if (startAt != null) {
|
|
15
|
+
start = {
|
|
16
|
+
oneofKind: 'startAt',
|
|
17
|
+
startAt: startAt.base58()
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (startAfter != null) {
|
|
21
|
+
start = {
|
|
22
|
+
oneofKind: 'startAfter',
|
|
23
|
+
startAt: startAfter.base58()
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const getDocumentsRequest = GetDocumentsRequest.create({
|
|
27
|
+
version: {
|
|
28
|
+
oneofKind: 'v0',
|
|
29
|
+
v0: {
|
|
30
|
+
dataContractId: (new IdentifierWASM(dataContractId)).bytes(),
|
|
31
|
+
documentType: documentTypeName,
|
|
32
|
+
where: encode(where),
|
|
33
|
+
orderBy: encode(orderBy),
|
|
34
|
+
limit: limit ?? DAPI_DEFAULT_LIMIT,
|
|
35
|
+
start,
|
|
36
|
+
prove: true
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const dataContract = await getDataContractByIdentifier(grpcPool, dataContractId);
|
|
41
|
+
const { response } = await grpcPool.getClient().getDocuments(getDocumentsRequest);
|
|
42
|
+
const { version } = response;
|
|
43
|
+
if (version.oneofKind !== 'v0') {
|
|
44
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be v0)');
|
|
45
|
+
}
|
|
46
|
+
const { v0 } = version;
|
|
47
|
+
if (v0.result.oneofKind !== 'proof') {
|
|
48
|
+
throw new Error('Unexpected oneOf type returned from DAPI (must be proof)');
|
|
49
|
+
}
|
|
50
|
+
const { result: { proof }, metadata } = v0;
|
|
51
|
+
if (metadata == null) {
|
|
52
|
+
throw new Error('Metadata not found');
|
|
53
|
+
}
|
|
54
|
+
const startAtIncluded = startAt != null;
|
|
55
|
+
const { rootHash, documents } = verifyDocumentsProof(proof.grovedbProof, dataContract, documentTypeName, where, orderBy, limit, startAt?.bytes(), startAtIncluded, BigInt(metadata?.timeMs), PlatformVersionWASM.PLATFORM_V9);
|
|
56
|
+
const quorumPublicKey = await getQuorumPublicKey(grpcPool.network, proof.quorumType, bytesToHex(proof.quorumHash));
|
|
57
|
+
const verify = await verifyTenderdashProof(proof, metadata, rootHash, quorumPublicKey);
|
|
58
|
+
if (!verify) {
|
|
59
|
+
throw new Error('Failed to verify query');
|
|
60
|
+
}
|
|
61
|
+
return documents ?? [];
|
|
62
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport';
|
|
2
|
+
import { PlatformClient } from '../proto/generated/platform.client';
|
|
3
|
+
import getEvonodeList from './utils/getEvonodeList';
|
|
4
|
+
import { GetStatusRequest } from '../proto/generated/platform';
|
|
5
|
+
import getRandomArrayItem from './utils/getRandomArrayItem';
|
|
6
|
+
const GRPC_DEFAULT_POOL_LIMIT = 5;
|
|
7
|
+
const seedNodes = {
|
|
8
|
+
testnet: [
|
|
9
|
+
// seed-1.pshenmic.dev
|
|
10
|
+
'https://158.160.14.115:1443'
|
|
11
|
+
],
|
|
12
|
+
mainnet: [
|
|
13
|
+
// seed-1.pshenmic.dev
|
|
14
|
+
'https://158.160.14.115:443'
|
|
15
|
+
// mainnet dcg seeds
|
|
16
|
+
// 'https://158.160.14.115',
|
|
17
|
+
// 'https://3.0.60.103',
|
|
18
|
+
// 'https://34.211.174.194'
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
const createClient = (url, abortController) => {
|
|
22
|
+
return new PlatformClient(new GrpcWebFetchTransport({
|
|
23
|
+
baseUrl: url,
|
|
24
|
+
abort: abortController?.signal
|
|
25
|
+
}));
|
|
26
|
+
};
|
|
27
|
+
export default class GRPCConnectionPool {
|
|
28
|
+
dapiUrls;
|
|
29
|
+
network;
|
|
30
|
+
constructor(network, grpcOptions) {
|
|
31
|
+
const grpcPoolLimit = grpcOptions?.poolLimit ?? GRPC_DEFAULT_POOL_LIMIT;
|
|
32
|
+
this.network = network;
|
|
33
|
+
this._initialize(network, grpcPoolLimit, grpcOptions?.dapiUrl).catch(console.error);
|
|
34
|
+
}
|
|
35
|
+
async _initialize(network, poolLimit, dapiUrl) {
|
|
36
|
+
if (typeof dapiUrl === 'string') {
|
|
37
|
+
this.dapiUrls = [dapiUrl];
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(dapiUrl)) {
|
|
41
|
+
this.dapiUrls = dapiUrl;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (dapiUrl != null) {
|
|
45
|
+
throw new Error('Unrecognized DAPI URL');
|
|
46
|
+
}
|
|
47
|
+
// Add default seed nodes
|
|
48
|
+
this.dapiUrls = seedNodes[network];
|
|
49
|
+
// retrieve last evonodes list
|
|
50
|
+
const evonodeList = await getEvonodeList(network);
|
|
51
|
+
// map it to array of dapiUrls
|
|
52
|
+
const networkDAPIUrls = Object.entries(evonodeList)
|
|
53
|
+
.map(([, info]) => info)
|
|
54
|
+
.filter((info) => info.status === 'ENABLED')
|
|
55
|
+
.map((info) => {
|
|
56
|
+
const [host] = info.address.split(':');
|
|
57
|
+
return `https://${host}:${info.platformHTTPPort}`;
|
|
58
|
+
});
|
|
59
|
+
// healthcheck nodes
|
|
60
|
+
for (const url of networkDAPIUrls) {
|
|
61
|
+
if (this.dapiUrls.length > poolLimit) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const client = createClient(url);
|
|
66
|
+
const { response } = await client.getStatus(GetStatusRequest.create({}));
|
|
67
|
+
if (response.version.oneofKind === 'v0' && response.version.v0.chain != null) {
|
|
68
|
+
this.dapiUrls.push(url);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
getClient(abortController) {
|
|
76
|
+
const dapiUrl = getRandomArrayItem(this.dapiUrls);
|
|
77
|
+
return createClient(dapiUrl, abortController);
|
|
78
|
+
}
|
|
79
|
+
}
|