Skip to content
This repository was archived by the owner on Jul 12, 2022. It is now read-only.

Commit 99860b3

Browse files
authored
feat: wait for state transition result (#180)
1 parent 70c63b9 commit 99860b3

17 files changed

+778
-588
lines changed

package-lock.json

Lines changed: 270 additions & 323 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131
},
3232
"homepage": "https://github.com/dashevo/DashJS#readme",
3333
"dependencies": {
34-
"@dashevo/dapi-client": "~0.17.2",
34+
"@dashevo/dapi-client": "0.18.0-dev.2",
3535
"@dashevo/dashcore-lib": "~0.19.19",
36-
"@dashevo/dpp": "~0.17.0",
37-
"@dashevo/wallet-lib": "~7.17.2",
36+
"@dashevo/dpp": "~0.18.0-dev.1",
37+
"@dashevo/wallet-lib": "~7.18.0-dev.1",
3838
"bs58": "^4.0.1"
3939
},
4040
"devDependencies": {

src/SDK/Client/Client.spec.ts

Lines changed: 200 additions & 124 deletions
Large diffs are not rendered by default.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface IPlatformStateProof {
2+
rootTreeProof: Buffer,
3+
storeTreeProof: Buffer,
4+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { IPlatformStateProof } from "./IPlatformStateProof";
2+
3+
export interface IStateTransitionResult {
4+
proof?: IPlatformStateProof,
5+
error?: {
6+
code: number,
7+
message: string,
8+
data: any,
9+
}
10+
}

src/SDK/Client/Platform/Platform.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import registerName from "./methods/names/register";
1919
import resolveName from "./methods/names/resolve";
2020
import resolveNameByRecord from "./methods/names/resolveByRecord";
2121
import searchName from "./methods/names/search";
22+
import broadcastStateTransition from "./broadcastStateTransition";
23+
import { IPlatformStateProof } from "./IPlatformStateProof";
2224

2325
/**
2426
* Interface for PlatformOpts
@@ -91,6 +93,14 @@ export class Platform {
9193
*/
9294
public contracts: Records;
9395

96+
/**
97+
* Broadcasts state transition
98+
* @param {Object} stateTransition
99+
*/
100+
public broadcastStateTransition(stateTransition: any): Promise<IPlatformStateProof|void> {
101+
return broadcastStateTransition(this, stateTransition);
102+
};
103+
94104
client: Client;
95105

96106
/**
Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,61 @@
1+
import crypto from "crypto";
12
import { Platform } from "./Platform";
2-
import { wait } from "../../../utils/wait";
3+
import { StateTransitionBroadcastError } from "../../../errors/StateTransitionBroadcastError";
4+
import { IStateTransitionResult } from "./IStateTransitionResult";
5+
import { IPlatformStateProof } from "./IPlatformStateProof";
36

47
/**
58
* @param {Platform} platform
69
* @param stateTransition
7-
* @param identity
8-
* @param {number} [keyIndex=0]
910
*/
10-
export default async function broadcastStateTransition(platform: Platform, stateTransition: any, identity: any, keyIndex : number = 0) {
11+
export default async function broadcastStateTransition(platform: Platform, stateTransition: any): Promise<IPlatformStateProof|void> {
1112
const { client, dpp } = platform;
1213

13-
const account = await client.getWalletAccount();
14-
15-
// @ts-ignore
16-
const { privateKey } = account.getIdentityHDKeyById(
17-
identity.getId().toString(),
18-
keyIndex,
19-
);
20-
21-
stateTransition.sign(
22-
identity.getPublicKeyById(keyIndex),
23-
privateKey,
24-
);
25-
2614
const result = await dpp.stateTransition.validateStructure(stateTransition);
2715

2816
if (!result.isValid()) {
2917
throw new Error(`StateTransition is invalid - ${JSON.stringify(result.getErrors())}`);
3018
}
3119

32-
await client.getDAPIClient().platform.broadcastStateTransition(stateTransition.toBuffer());
20+
// Subscribing to future result
21+
const hash = crypto.createHash('sha256')
22+
.update(stateTransition.toBuffer())
23+
.digest();
24+
25+
const stateTransitionResultPromise: IStateTransitionResult = client.getDAPIClient().platform.waitForStateTransitionResult(hash, { prove: true });
26+
27+
// Broadcasting state transition
28+
try {
29+
await client.getDAPIClient().platform.broadcastStateTransition(stateTransition.toBuffer());
30+
} catch (e) {
31+
let data;
32+
let message;
33+
34+
if (e.data) {
35+
data = e.data;
36+
} else if (e.metadata) {
37+
const errors = e.metadata.get('errors');
38+
data = {};
39+
data.errors = errors && errors.length > 0 ? JSON.parse(errors) : errors;
40+
}
41+
42+
if (e.details) {
43+
message = e.details;
44+
} else {
45+
message = e.message;
46+
}
47+
48+
throw new StateTransitionBroadcastError(e.code, message, data);
49+
}
50+
51+
// Waiting for result to return
52+
const stateTransitionResult = await stateTransitionResultPromise;
53+
54+
let { error } = stateTransitionResult;
55+
56+
if (error) {
57+
throw new StateTransitionBroadcastError(error.code, error.message, error.data);
58+
}
3359

34-
// Wait some time for propagation
35-
await wait(1000);
60+
return stateTransitionResult.proof;
3661
}
Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { Platform } from "../../Platform";
2+
import broadcastStateTransition from "../../broadcastStateTransition";
3+
import { signStateTransition } from "../../signStateTransition";
4+
15
/**
26
* Broadcast contract onto the platform
37
*
@@ -6,26 +10,13 @@
610
* @param identity - identity
711
* @return dataContract
812
*/
9-
import { wait } from '../../../../../utils/wait';
10-
import { Platform } from "../../Platform";
11-
import broadcastStateTransition from "../../broadcastStateTransition";
12-
1313
export default async function broadcast(this: Platform, dataContract: any, identity: any): Promise<any> {
1414
const { dpp } = this;
1515

1616
const dataContractCreateTransition = dpp.dataContract.createStateTransition(dataContract);
1717

18-
await broadcastStateTransition(this, dataContractCreateTransition, identity);
19-
20-
// Wait some time for propagation
21-
await wait(6000);
22-
23-
let fetchedContract;
24-
do {
25-
await wait(1000);
26-
27-
fetchedContract = await this.client.getDAPIClient().platform.getDataContract(dataContract.getId());
28-
} while (!fetchedContract);
18+
await signStateTransition(this, dataContractCreateTransition, identity);
19+
await broadcastStateTransition(this, dataContractCreateTransition);
2920

3021
return dataContractCreateTransition;
3122
}
Lines changed: 5 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,7 @@
1-
import { wait } from '../../../../../utils/wait';
2-
import {Platform} from "../../Platform";
1+
import { Platform } from "../../Platform";
32
import broadcastStateTransition from '../../broadcastStateTransition';
43
import Document from '@dashevo/dpp/lib/document/Document';
5-
6-
/**
7-
* get Document DataContract name
8-
*
9-
* @param {Platform} platform
10-
* @param {Document} document
11-
*
12-
*/
13-
function getDataContractName(platform: Platform, document: Document): string {
14-
const appNames = platform.client.getApps().getNames();
15-
const contractId = document.getDataContractId();
16-
const dataContractName = appNames.find((name) => {
17-
const clientAppDefinition = platform.client.getApps().get(name);
18-
19-
return clientAppDefinition.contractId.equals(contractId);
20-
});
21-
22-
if (dataContractName === undefined) {
23-
// we will never reach this code
24-
throw new Error('DataContractAppDefinition was not found');
25-
}
26-
27-
return dataContractName;
28-
}
29-
30-
31-
/**
32-
*
33-
* @param {Platform} platform
34-
* @param {Object} documents
35-
* @param {Document[]} [documents.create]
36-
* @param {Document[]} [documents.replace]
37-
* @param {Document[]} [documents.delete]
38-
*/
39-
async function waitForPropagation(platform: Platform, documents: { create?: Document[], replace?: Document[], delete?: Document[]}): Promise<void> {
40-
await wait(6000);
41-
42-
let fetchedDocuments;
43-
44-
if (documents.create && documents.create.length > 0) {
45-
const [document] = documents.create;
46-
const dataContractName = getDataContractName(platform, document);
47-
48-
do {
49-
await wait(1000);
50-
51-
// @ts-ignore
52-
fetchedDocuments = await platform.client.platform.documents.get(
53-
`${dataContractName}.${document.getType()}`,
54-
{ where: [['$id', '==', document.getId()]] },
55-
);
56-
} while(fetchedDocuments.length === 0);
57-
} else if (documents.replace && documents.replace.length > 0) {
58-
const [document] = documents.replace;
59-
const dataContractName = getDataContractName(platform, document);
60-
61-
let revision;
62-
63-
do {
64-
await wait(1000);
65-
66-
// @ts-ignore
67-
fetchedDocuments = await platform.client.platform.documents.get(
68-
`${dataContractName}.${document.getType()}`,
69-
{ where: [['$id', '==', document.getId()]] },
70-
);
71-
72-
revision = fetchedDocuments[0]?.revision || document.revision;
73-
} while(revision === document.revision);
74-
} else if (documents.delete && documents.delete.length > 0) {
75-
const [document] = documents.delete;
76-
const dataContractName = getDataContractName(platform, document);
77-
78-
do {
79-
await wait(1000);
80-
81-
// @ts-ignore
82-
fetchedDocuments = await platform.client.platform.documents.get(
83-
`${dataContractName}.${document.getType()}`,
84-
{ where: [['$id', '==', document.getId()]] },
85-
);
86-
} while(fetchedDocuments.length > 0);
87-
}
88-
}
4+
import { signStateTransition } from "../../signStateTransition";
895

906
/**
917
* Broadcast document onto the platform
@@ -102,10 +18,10 @@ export default async function broadcast(this: Platform, documents: { create?: Do
10218

10319
const documentsBatchTransition = dpp.document.createStateTransition(documents);
10420

105-
await broadcastStateTransition(this, documentsBatchTransition, identity);
21+
await signStateTransition(this, documentsBatchTransition, identity);
10622

107-
// Wait some time for propagation
108-
await waitForPropagation(this, documents);
23+
// Broadcast state transition also wait for the result to be obtained
24+
await broadcastStateTransition(this, documentsBatchTransition);
10925

11026
return documentsBatchTransition;
11127
}

src/SDK/Client/Platform/methods/identities/register.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Platform } from "../../Platform";
2-
import { wait } from "../../../../../utils/wait";
32
import createAssetLockTransaction from "../../createAssetLockTransaction";
43
import createIdentityCreateTransition from "./internal/createIdentityCreateTransition";
54
import createAssetLockProof from "./internal/createAssetLockProof";
5+
import broadcastStateTransition from "../../broadcastStateTransition";
66

77
/**
88
* Register identities to the platform
@@ -32,7 +32,7 @@ export default async function register(
3232
this, assetLockTransaction, assetLockOutputIndex, assetLockProof, assetLockPrivateKey
3333
);
3434

35-
await client.getDAPIClient().platform.broadcastStateTransition(identityCreateTransition.toBuffer());
35+
await broadcastStateTransition(this, identityCreateTransition);
3636

3737
// If state transition was broadcast without any errors, import identity to the account
3838
account.storage.insertIdentityIdAtIndex(
@@ -41,12 +41,5 @@ export default async function register(
4141
identityIndex,
4242
);
4343

44-
let fetchedIdentity;
45-
do {
46-
await wait(1000);
47-
48-
fetchedIdentity = await this.client.getDAPIClient().platform.getIdentity(identity.getId());
49-
} while (!fetchedIdentity);
50-
5144
return identity;
5245
}

src/SDK/Client/Platform/methods/identities/topUp.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import Identifier from "@dashevo/dpp/lib/Identifier";
33
import {Platform} from "../../Platform";
44

5-
import { wait } from "../../../../../utils/wait";
65
import createAssetLockTransaction from "../../createAssetLockTransaction";
76
import createAssetLockProof from "./internal/createAssetLockProof";
87
import createIdentityTopUpTransition from "./internal/createIdnetityTopUpTransition";
8+
import broadcastStateTransition from "../../broadcastStateTransition";
99

1010
/**
1111
* Register identities to the platform
@@ -37,10 +37,7 @@ export async function topUp(this: Platform, identityId: Identifier | string, amo
3737
const identityTopUpTransition = await createIdentityTopUpTransition(this, assetLockTransaction, assetLockOutputIndex, assetLockProof, assetLockPrivateKey, identityId);
3838

3939
// Broadcast ST
40-
await client.getDAPIClient().platform.broadcastStateTransition(identityTopUpTransition.toBuffer());
41-
42-
// Wait some time for propagation
43-
await wait(1000);
40+
await broadcastStateTransition(this, identityTopUpTransition);
4441

4542
return true;
4643
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {Platform} from "./Platform";
2+
3+
/**
4+
*
5+
* @param {Platform} platform
6+
* @param {AbstractStateTransition} stateTransition
7+
* @param {Identity} identity
8+
* @param {number} [keyIndex]
9+
* @return {AbstractStateTransition}
10+
*/
11+
export async function signStateTransition(platform: Platform, stateTransition: any, identity: any, keyIndex: number = 0): Promise<any> {
12+
const { client } = platform;
13+
14+
const account = await client.getWalletAccount();
15+
16+
// @ts-ignore
17+
const { privateKey } = account.getIdentityHDKeyById(
18+
identity.getId().toString(),
19+
keyIndex,
20+
);
21+
22+
stateTransition.sign(
23+
identity.getPublicKeyById(keyIndex),
24+
privateKey,
25+
);
26+
27+
return stateTransition;
28+
}

0 commit comments

Comments
 (0)