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.
Files changed (45) hide show
  1. package/README.md +244 -167
  2. package/built/cjs/index.js +588 -45
  3. package/built/cjs/index.js.map +1 -1
  4. package/built/esm/index.js +32255 -34938
  5. package/built/esm/index.js.map +1 -1
  6. package/built/index.d.ts +2 -0
  7. package/built/index.js +2 -0
  8. package/built/index.js.map +1 -1
  9. package/built/src/blockchainClient/blockchainClient.d.ts +3 -0
  10. package/built/src/blockchainClient/blockchainClient.js +251 -0
  11. package/built/src/blockchainClient/blockchainClient.js.map +1 -0
  12. package/built/src/blockchainClient/errors.d.ts +15 -0
  13. package/built/src/blockchainClient/errors.js +26 -0
  14. package/built/src/blockchainClient/errors.js.map +1 -0
  15. package/built/src/blockchainClient/interface.d.ts +108 -0
  16. package/built/src/blockchainClient/interface.js +2 -0
  17. package/built/src/blockchainClient/interface.js.map +1 -0
  18. package/built/src/blockchainClient/types.d.ts +91 -0
  19. package/built/src/blockchainClient/types.js +8 -0
  20. package/built/src/blockchainClient/types.js.map +1 -0
  21. package/built/src/blockchainClient/utils.d.ts +18 -0
  22. package/built/src/blockchainClient/utils.js +157 -0
  23. package/built/src/blockchainClient/utils.js.map +1 -0
  24. package/built/src/gtx/errors.js +1 -1
  25. package/built/src/gtx/errors.js.map +1 -1
  26. package/built/src/gtx/gtx.d.ts +4 -3
  27. package/built/src/gtx/gtx.js +17 -9
  28. package/built/src/gtx/gtx.js.map +1 -1
  29. package/built/src/promiEvent/promiEventEmitter.d.ts +24 -0
  30. package/built/src/promiEvent/promiEventEmitter.js +56 -0
  31. package/built/src/promiEvent/promiEventEmitter.js.map +1 -0
  32. package/built/src/promiEvent/promiEvents.d.ts +12 -0
  33. package/built/src/promiEvent/promiEvents.js +61 -0
  34. package/built/src/promiEvent/promiEvents.js.map +1 -0
  35. package/built/src/restclient/errors.d.ts +1 -1
  36. package/built/src/restclient/errors.js +6 -2
  37. package/built/src/restclient/errors.js.map +1 -1
  38. package/built/src/restclient/restclient.d.ts +15 -0
  39. package/built/src/restclient/restclient.js +10 -6
  40. package/built/src/restclient/restclient.js.map +1 -1
  41. package/built/src/restclient/restclientutil.d.ts +2 -1
  42. package/built/src/restclient/restclientutil.js.map +1 -1
  43. package/built/umd/index.js +32255 -34936
  44. package/built/umd/index.js.map +1 -1
  45. package/package.json +3 -2
package/README.md CHANGED
@@ -1,209 +1,286 @@
1
- # Postchain client
2
-
3
- ## Example:
4
-
5
- ```javascript
6
- let crypto = require("crypto");
7
- let secp256k1 = require("secp256k1");
8
- let { encryption } = require("postchain-client")
9
-
10
- // Create some dummy keys
11
- let signerPrivKeyA = Buffer.alloc(32, "a");
12
- let signerPubKeyA = secp256k1.publicKeyCreate(signerPrivKeyA);
13
- let signerPrivKeyB = Buffer.alloc(32, "b");
14
- let signerPubKeyB = secp256k1.publicKeyCreate(signerPrivKeyB);
15
-
16
- // Create a simple signature provider.
17
- // This is the standard way of signing a message using the gtxClient,
18
- // and it can be used with the restClient too. Look further below for
19
- // more details on how to use it.
20
- let signatureProviderA = newSignatureProvider({privKey: signerPrivKeyA})
21
-
22
- // The lower-level client that can be used for any
23
- // postchain client messages. It only handles binary data.
24
- let restClient = require("postchain-client").restClient;
25
-
26
- // The higher-level client that is used for generic transactions, GTX.
27
- // This utilizes the GTX format described below to pass function calls
28
- // from the client to the postchain backend implementation.
29
- let gtxClient = require("postchain-client").gtxClient;
30
-
31
- // Each blockchain has a blockchainRID, that identifies the blockchain
32
- // we want to work with. This blockchainRID must match the blockchain RID
33
- // encoded into the first block of the blockchain. How the blockchainRID
34
- // is constructed is up to the creator of the blockchain. In this example
35
- // we use the linux command:
36
- // echo "A blockchain example"| sha256sum
37
- let blockchainRID = "7d565d92fd15bd1cdac2dc276cbcbc5581349d05a9e94ba919e1155ef4daf8f9";
38
-
39
- // Create an instance of the rest client and configure it for a specific set of
40
- // base urls and a blockchinRID. You may set an optional pool size for the connection pool,
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
- // Now we can query Postchain. The backend must have a method
91
- // query method named "findStuff" (readOnlyConn, queryObject) that can
92
- // understand the query object and typically perform a search using
93
- // the database connection readOnlyConn. The backend query function
94
- // can return any serializable result object you chose
95
- gtx.query("findStuff", { text: "arg1" });
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
- function sha256(buffer) {
109
- return crypto.createHash("sha256").update(buffer).digest();
110
- }
53
+ ## Queries
111
54
 
112
- // This is to demonstrate that you can use external signing
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
- // The complex signature process can, however, even be implemented in
122
- // a signatureProvider. Once you have a callback like the one above,
123
- // it's a simple matter of making a signature provider:
124
- let signatureProviderB {
125
- pubKey: signerPubKeyB,
126
- sign: askUserBToSign
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
- A very simple backend for the above client might look like this:
80
+ ### Typed Query
131
81
 
132
- ```javascript
133
- module.exports.createSchema = async function (conn) {
134
- console.log("Creating schema in backend");
135
- await conn.query(
136
- "CREATE TABLE IF NOT EXISTS example " +
137
- "(id SERIAL PRIMARY KEY, stuff TEXT NOT NULL)"
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
- // Example backend implementation that doesn't do anything but log the function calls
142
- module.exports.backendFunctions = {
143
- fun1: async function (
144
- conn,
145
- tx_iid,
146
- call_index,
147
- signers,
148
- stringArg,
149
- arrayArg,
150
- bufferArg
151
- ) {
152
- console.log("fun1 called in backend");
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
- fun2: async function (conn, tx_iid, call_index, signers, intArg1, intArg2) {
155
- console.log("fun2 called in backend");
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
- module.exports.backendQueries = {
160
- findStuff: async function (readOnlyConn, queryObject) {
161
- console.log("Search for " + queryObject.text);
162
- if (queryObject.text === "giveMeHits") {
163
- return { hits: 4 };
164
- }
165
- return { hits: 0 };
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
- ## GTX architecture
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
- Generic transactions were developed to make it easier to make user implementations of Postchain.
173
- The user doesn't have to invent a binary format for it's transactions. With GTX you specify a
174
- set of functions that you will call from the client, and the GTX client will serialize the
175
- function calls, sign them and send to Postchain.
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
- | req.fun1('arg1', 'arg2');
181
- | req.fun2('arg1'); req.sign(privKeyA); req.send(err => {})
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 encoded message'}
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
- The first four arguments to backend.fun1 are
284
+ ## Contributing to the Project
204
285
 
205
- - `conn` is a database connection that the backend function can use to query/update the database
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.