postchain-client 1.3.2 → 1.4.0
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 +244 -167
- package/built/cjs/index.js +588 -45
- package/built/cjs/index.js.map +1 -1
- package/built/esm/index.js +32255 -34938
- package/built/esm/index.js.map +1 -1
- package/built/index.d.ts +2 -0
- package/built/index.js +2 -0
- package/built/index.js.map +1 -1
- package/built/src/blockchainClient/blockchainClient.d.ts +3 -0
- package/built/src/blockchainClient/blockchainClient.js +251 -0
- package/built/src/blockchainClient/blockchainClient.js.map +1 -0
- package/built/src/blockchainClient/errors.d.ts +15 -0
- package/built/src/blockchainClient/errors.js +26 -0
- package/built/src/blockchainClient/errors.js.map +1 -0
- package/built/src/blockchainClient/interface.d.ts +108 -0
- package/built/src/blockchainClient/interface.js +2 -0
- package/built/src/blockchainClient/interface.js.map +1 -0
- package/built/src/blockchainClient/types.d.ts +91 -0
- package/built/src/blockchainClient/types.js +8 -0
- package/built/src/blockchainClient/types.js.map +1 -0
- package/built/src/blockchainClient/utils.d.ts +18 -0
- package/built/src/blockchainClient/utils.js +157 -0
- package/built/src/blockchainClient/utils.js.map +1 -0
- package/built/src/gtx/errors.js +1 -1
- package/built/src/gtx/errors.js.map +1 -1
- package/built/src/gtx/gtx.d.ts +4 -3
- package/built/src/gtx/gtx.js +17 -9
- package/built/src/gtx/gtx.js.map +1 -1
- package/built/src/promiEvent/promiEventEmitter.d.ts +24 -0
- package/built/src/promiEvent/promiEventEmitter.js +56 -0
- package/built/src/promiEvent/promiEventEmitter.js.map +1 -0
- package/built/src/promiEvent/promiEvents.d.ts +12 -0
- package/built/src/promiEvent/promiEvents.js +61 -0
- package/built/src/promiEvent/promiEvents.js.map +1 -0
- package/built/src/restclient/errors.d.ts +1 -1
- package/built/src/restclient/errors.js +6 -2
- package/built/src/restclient/errors.js.map +1 -1
- package/built/src/restclient/restclient.d.ts +15 -0
- package/built/src/restclient/restclient.js +10 -6
- package/built/src/restclient/restclient.js.map +1 -1
- package/built/src/restclient/restclientutil.d.ts +2 -1
- package/built/src/restclient/restclientutil.js.map +1 -1
- package/built/umd/index.js +32255 -34936
- package/built/umd/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,209 +1,286 @@
|
|
|
1
|
-
# Postchain
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// default pool size is 10. Applications that do hundreds of requests
|
|
42
|
-
// per second may consider setting this a bit higher, for example 100.
|
|
43
|
-
// It *may* speed things up a bit. You may also set an opitional
|
|
44
|
-
// poolinginterval in milliseconds, default is set to 500, a fail over
|
|
45
|
-
// config wich include attemps per endpoint and attempt interval, default is 3 and 500.
|
|
46
|
-
let rest = restClient.createRestClient([`http://localhost:7741`], blockchainRID, 5, 1000);
|
|
47
|
-
|
|
48
|
-
// Create an instance of the higher-level gtx client. It will
|
|
49
|
-
// use the rest client instance and it will allow calls to functions
|
|
50
|
-
// fun1 and fun2. The backend implementation in Postchain must
|
|
51
|
-
// provide those functions.
|
|
52
|
-
let gtx = gtxClient.createClient(rest, blockchainRID, ["fun1", "fun2"]);
|
|
53
|
-
|
|
54
|
-
// Start a new request. A request instance is created.
|
|
55
|
-
// The public keys are the keys that must sign the request
|
|
56
|
-
// before sending it to postchain. Can be empty.
|
|
57
|
-
let req = gtx.newTransaction([signatureProviderA.pubKey, signerPubKeyB]);
|
|
58
|
-
|
|
59
|
-
// call fun1 with three arguments: a string, an array and a Buffer
|
|
60
|
-
req.fun1("arg1", ["arg2", [1, 2]], Buffer.from("hello"));
|
|
61
|
-
// call the same function with only one argument
|
|
62
|
-
req.fun1("arg1");
|
|
63
|
-
// call fun2
|
|
64
|
-
req.fun2(1, 2);
|
|
65
|
-
|
|
66
|
-
// Signing can be done either through the sign() function ...
|
|
67
|
-
// The signatureProvider method sign() will be called, and
|
|
68
|
-
// it's expected to return the signature of the digest it's given
|
|
69
|
-
// as input (of type Buffer). It needs to return the output of
|
|
70
|
-
// secp256k1.ecdsaSign(...params).signature
|
|
71
|
-
await req.sign(signatureProviderA);
|
|
72
|
-
// You can also use this, if you don't need a signature provider
|
|
73
|
-
// The second parameter is optional, it will be generated if not given
|
|
74
|
-
//await req.sign(signerPrivKeyA, signerPubKeyA);
|
|
75
|
-
|
|
76
|
-
// ... or by the addSignature() function
|
|
77
|
-
let digestToSign = req.getDigestToSign();
|
|
78
|
-
// Sign the buffer externally.
|
|
79
|
-
let signatureFromB = askUserBToSign(digestToSign);
|
|
80
|
-
// and add the signature to the request
|
|
81
|
-
req.addSignature(signerPubKeyB, signatureFromB);
|
|
82
|
-
|
|
83
|
-
// Finally send the request and supply an error callback
|
|
84
|
-
req.send((error) => {
|
|
85
|
-
if (error) {
|
|
86
|
-
console.log(error);
|
|
87
|
-
}
|
|
1
|
+
# Postchain Client
|
|
2
|
+
|
|
3
|
+
Postchain Client is a set of predefined functions and utilities offering a convenient and simplified interface for interacting with a decentralized application (dapp) built using the Postchain blockchain framework, also known as Chromia.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
The Postchain Client is compatible with both JavaScript and TypeScript. You can install the library from npm via https://www.npmjs.com/package/postchain-client.
|
|
8
|
+
|
|
9
|
+
## Initializing the Client
|
|
10
|
+
|
|
11
|
+
Firstly, import the required libraries.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import crypto from "crypto-browserify";
|
|
15
|
+
import secp256k1 from "secp256k1";
|
|
16
|
+
import { encryption, createClient, newSignatureProvider } from "postchain-client";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then, create some dummy keys.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
const signerPrivKeyA = Buffer.alloc(32, "a");
|
|
23
|
+
const signerPubKeyA = secp256k1.publicKeyCreate(signerPrivKeyA);
|
|
24
|
+
const signerPrivKeyB = Buffer.alloc(32, "b");
|
|
25
|
+
const signerPubKeyB = secp256k1.publicKeyCreate(signerPrivKeyB);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Each blockchain has a Blockchain RID (`blockchainRID`) that identifies the specific blockchain we wish to interact with. This blockchainRID should match the Blockchain RID encoded into the first block of the blockchain. How the blockchainRID is structured depends on the blockchain's creator. In this example, we use the Linux command: `echo "A blockchain example"| sha256sum`.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
const blockchainRID =
|
|
32
|
+
"7d565d92fd15bd1cdac2dc276cbcbc5581349d05a9e94ba919e1155ef4daf8f9";
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Next, create a Chromia client instance and configure it with a specific set of base URLs, the `blockchainRID`, and so forth. If you connect to a local node, the `nodeURLPool` property accepts an array or a string of URLs to nodes running the target dapp.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
const chromiaClient = createClient({
|
|
39
|
+
nodeURLPool: "http://localhost:7740",
|
|
40
|
+
blockchainRID,
|
|
88
41
|
});
|
|
42
|
+
```
|
|
89
43
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// This will make a request with a single operation
|
|
98
|
-
// and a single signature.
|
|
99
|
-
req = gtx.newTransaction(blockchainRID, [signatureProviderA.pubkey]);
|
|
100
|
-
req.fun1("arg1");
|
|
101
|
-
await req.sign(signatureProviderA);
|
|
102
|
-
req.send((error) => {
|
|
103
|
-
if (!error) {
|
|
104
|
-
done();
|
|
105
|
-
}
|
|
44
|
+
Connecting to a network is achieved through the Directory System Chain. The `directoryNodeURLPool` property accepts an array of URLs to system nodes running the directory chain. This directory chain is automatically queried to determine the URLs of the nodes in the network running the target dapp.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const chromiaClient = createClient({
|
|
48
|
+
directoryNodeURLPool: ["url1", "url2", "url3", "etc."],
|
|
49
|
+
blockchainRID,
|
|
106
50
|
});
|
|
51
|
+
```
|
|
107
52
|
|
|
108
|
-
|
|
109
|
-
return crypto.createHash("sha256").update(buffer).digest();
|
|
110
|
-
}
|
|
53
|
+
## Queries
|
|
111
54
|
|
|
112
|
-
|
|
113
|
-
// mechanisms. It could be a complex function, requiring you
|
|
114
|
-
// to sign from your phone, another device, or something else again
|
|
115
|
-
function askUserBToSign(buffer) {
|
|
116
|
-
// The signed digest is a double sha-256
|
|
117
|
-
var digest = sha256(sha256(buffer));
|
|
118
|
-
return secp256k1.sign(digest, signerPrivKeyB).signature;
|
|
119
|
-
}
|
|
55
|
+
### Query Option 1
|
|
120
56
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
57
|
+
Use the query function to send a query to a dapp written in Rell. The function takes the query's name and an object of query arguments.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
chromiaClient.query("get_foobar", {
|
|
61
|
+
foo: 1,
|
|
62
|
+
bar: 2,
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Query Option 2
|
|
67
|
+
|
|
68
|
+
Alternatively, the query function can take an object with a `name` property and an `args` property.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
chromiaClient.query({
|
|
72
|
+
name: "get_foobar",
|
|
73
|
+
args: {
|
|
74
|
+
foo: 1,
|
|
75
|
+
bar: 2,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
128
78
|
```
|
|
129
79
|
|
|
130
|
-
|
|
80
|
+
### Typed Query
|
|
131
81
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
82
|
+
You can specify argument and return types for a given query in TypeScript.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
type ArgumentsType = {
|
|
86
|
+
foo: number;
|
|
87
|
+
bar: number;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
type ReturnType = {
|
|
91
|
+
foobar: string;
|
|
139
92
|
};
|
|
140
93
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
94
|
+
const result = await chromiaClient.query<ArgumentsType, ReturnType>(
|
|
95
|
+
"get_foobar",
|
|
96
|
+
{
|
|
97
|
+
foo: 1,
|
|
98
|
+
bar: 2,
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Transactions
|
|
104
|
+
|
|
105
|
+
To send transactions, begin by creating a simple signature provider. The signature provider is used to sign transactions. More details on usage are provided further below.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const signatureProviderA = newSignatureProvider({ privKey: signerPrivKeyA });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Simple Transaction
|
|
112
|
+
|
|
113
|
+
The `signAndSendUniqueTransaction` function streamlines the process of sending a transaction in three steps. It adds a "nop" (no operation) with a random number that ensures the transaction is unique, signs it with a signature provider or private key, and sends it. The function generates a receipt that includes a status code, status, and transactionRID. The status code indicates whether the server successfully processed the transaction. The status represents the current stage of the transaction on the blockchain, which can be one of the following: `Waiting`, `Rejected`, `Confirmed`, or `Unknown`.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const { status, statusCode, transactionRID } = await chromiaClient.signAndSendUniqueTransaction(
|
|
117
|
+
{
|
|
118
|
+
operations: [
|
|
119
|
+
{
|
|
120
|
+
name: "my_operation",
|
|
121
|
+
args: ["arg1", "arg2"],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
signers: [signatureProviderA.pubkey],
|
|
153
125
|
},
|
|
154
|
-
|
|
155
|
-
|
|
126
|
+
signatureProviderA
|
|
127
|
+
);
|
|
128
|
+
```
|
|
129
|
+
It is also possible to pass a single operation.
|
|
130
|
+
```typescript
|
|
131
|
+
const { status, statusCode, transactionRID } = await chromiaClient.signAndSendUniqueTransaction(
|
|
132
|
+
{
|
|
133
|
+
name: "my_operation",
|
|
134
|
+
args: ["arg1", "arg2"],
|
|
156
135
|
},
|
|
157
|
-
|
|
136
|
+
signatureProviderA
|
|
137
|
+
);
|
|
138
|
+
```
|
|
158
139
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
140
|
+
### Signing a Transaction
|
|
141
|
+
|
|
142
|
+
Signs a transaction using the provided signing method. This can be a SignatureProvider or a key pair. A signature provider must contain a public key and a `sign` function that returns the signature of a digest transaction.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const signedTx = await chromiaClient.signTransaction(
|
|
146
|
+
{
|
|
147
|
+
operations: [
|
|
148
|
+
{
|
|
149
|
+
name: "my_operation",
|
|
150
|
+
args: ["arg1"],
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
signers: [signatureProviderA.pubkey],
|
|
166
154
|
},
|
|
155
|
+
signatureProviderA
|
|
156
|
+
);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Sending an Unsigned Transaction
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const receipt = await chromiaClient.sendTransaction({
|
|
163
|
+
name: "my_operation",
|
|
164
|
+
args: ["arg1", "arg2"],
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
### Sending a Signed Transaction
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
chromiaClient.sendTransaction(signedTx);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Advanced Transaction
|
|
174
|
+
|
|
175
|
+
Create a transaction object.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const tx = {
|
|
179
|
+
operations: [
|
|
180
|
+
{
|
|
181
|
+
name: "my_operation_1",
|
|
182
|
+
arguments: ["arg1", "arg2"],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "my_operation_2",
|
|
186
|
+
arguments: ["arg1", "arg2"],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
signers: ["signer1", "signer2"],
|
|
167
190
|
};
|
|
168
191
|
```
|
|
169
192
|
|
|
170
|
-
|
|
193
|
+
You can modify the object to add operations or signers.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
tx.operations.push({
|
|
197
|
+
name: "my_operation_3",
|
|
198
|
+
arguments: ["arg1", "arg2"],
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
tx.signers.push("signer3");
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
A nop can be added to make the transaction unique. It can be added manually to the transaction object or by using the `addNop` function.
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const uniqueTx = chromiaClient.addNop(tx);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Sign and send the transaction.
|
|
171
211
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
212
|
+
```typescript
|
|
213
|
+
const signedTx = await chromiaClient.signTransaction(
|
|
214
|
+
uniqueTx,
|
|
215
|
+
signatureProviderA
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const receipt = await chromiaClient.sendTransaction(signedTx);
|
|
219
|
+
```
|
|
220
|
+
## PromiEvent
|
|
221
|
+
When using functions that involve sending a transaction, you have the option to either wait for a promise or act on an event. The return value in this case is a "PromiEvent," which combines the functionalities of both a "Promise" and an "Event." This combination allows you to handle asynchronous operations. You can treat it as a Promise by utilizing the .then() and .catch() methods to handle the result or any potential errors. Moreover, it emits an event when a transaction is sent, providing you with the ability to listen for the event and execute custom logic based on your specific needs.
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
chromiaClient.sendTransaction({
|
|
225
|
+
name: "my_operation",
|
|
226
|
+
args: ["arg1", "arg2"],
|
|
227
|
+
}).on("sent", (receipt: TransactionReceipt) => {
|
|
228
|
+
console.log("The transaction is sent")
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
## External Signing Example
|
|
232
|
+
|
|
233
|
+
This example demonstrates that you can use external signing mechanisms. It could involve a complex function requiring you to sign from your phone, another device, or a different method.
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
function askUserBToSign(buffer) {
|
|
237
|
+
// The signed digest is a double sha-256
|
|
238
|
+
var digest = sha256(sha256(buffer));
|
|
239
|
+
return secp256k1.sign(digest, signerPrivKeyB).signature;
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
This complex signature process can be implemented in a SignatureProvider. Once you have a callback like the one above, creating a signature provider is straightforward:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
const signatureProviderB = {
|
|
247
|
+
pubKey: signerPubKeyB,
|
|
248
|
+
sign: askUserBToSign,
|
|
249
|
+
};
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Architecture
|
|
253
|
+
|
|
254
|
+
In the Postchain client, Generic Transactions (GTX) are used to simplify user implementations of Postchain. Users do not need to invent a binary format for their transactions. The client will serialize the function calls, sign them, and send them to Postchain. Read more about GTX in the [docs](https://docs.chromia.com/category/gtx).
|
|
176
255
|
|
|
177
256
|
```
|
|
178
257
|
User
|
|
179
258
|
|
|
|
180
|
-
|
|
|
181
|
-
|
|
|
259
|
+
| chromiaClient.sendTransaction()
|
|
260
|
+
|
|
|
182
261
|
v
|
|
183
|
-
GtxClient
|
|
184
262
|
|
|
|
185
263
|
| <Buffer with serialized message>
|
|
264
|
+
|
|
|
186
265
|
v
|
|
187
|
-
RestClient
|
|
188
266
|
|
|
|
189
|
-
| POST http://localhost:7741/tx {tx: 'hex
|
|
267
|
+
| POST http://localhost:7741/tx {tx: 'hex-encoded message'}
|
|
268
|
+
|
|
|
190
269
|
v
|
|
191
270
|
RestApi
|
|
192
271
|
|
|
|
193
272
|
| <Buffer with serialized message>
|
|
273
|
+
|
|
|
194
274
|
v
|
|
195
275
|
Postchain
|
|
196
276
|
|
|
|
197
277
|
| backend.fun1(conn, tx_iid, 0, [pubKeyA], 'arg1', 'arg2');
|
|
198
278
|
| backend.fun2(conn, tx_iid, 1, [pubKeyA], 'arg1');
|
|
279
|
+
|
|
|
199
280
|
v
|
|
200
281
|
Backend
|
|
201
282
|
```
|
|
202
283
|
|
|
203
|
-
|
|
284
|
+
## Contributing to the Project
|
|
204
285
|
|
|
205
|
-
|
|
206
|
-
- `tx_iid` is the primary key of this postchain transaction.
|
|
207
|
-
- `call_index`, 0 in this example. It's the index within the GTX of the current call
|
|
208
|
-
- `signers`, all signers of this GTX. The signatures from these signers are already verified by
|
|
209
|
-
the GTX framework when the backend function is called.
|
|
286
|
+
Please refer to the project's contribution guidelines to learn how you can help improve the Postchain client.
|