Postchain client
Example:
let crypto = require("crypto");
let secp256k1 = require("secp256k1");
let signerPrivKeyA = Buffer.alloc(32, "a");
let signerPubKeyA = secp256k1.publicKeyCreate(signerPrivKeyA);
let signerPrivKeyB = Buffer.alloc(32, "b");
let signerPubKeyB = secp256k1.publicKeyCreate(signerPrivKeyB);
let restClient = require("postchain-client").restClient;
let gtxClient = require("postchain-client").gtxClient;
let blockchainRID = "7d565d92fd15bd1cdac2dc276cbcbc5581349d05a9e94ba919e1155ef4daf8f9";
let rest = restClient.createRestClient([`http://localhost:7741`], blockchainRID, 5, 1000);
let gtx = gtxClient.createClient(rest, blockchainRID, ["fun1", "fun2"]);
let req = gtx.newTransaction([signerPubKeyA, signerPubKeyB]);
req.fun1("arg1", ["arg2", [1, 2]], Buffer.from("hello"));
req.fun1("arg1");
req.fun2(1, 2);
req.sign(signerPrivKeyA, signerPubKeyA);
let bufferToSign = req.getBufferToSign();
let signatureFromB = askUserBToSign(bufferToSign);
req.addSignature(signerPubKeyB, signatureFromB);
req.send((error) => {
if (error) {
console.log(error);
}
});
let queryObject = { type: "findStuff", text: "arg1" };
let resultHandler = (error, result) => {
if (error) {
console.error(error);
return;
}
if (result.hits == 0) {
setTimeout(gtx.query(queryObject, resultHandler), 2000);
}
console.log(JSON.stringify(result));
};
gtx.query(queryObject, resultHandler);
req = gtx.newTransaction(blockchainRID, [signerPubKeyA]);
req.fun1("arg1");
req.sign(signerPrivKeyA);
req.send((error) => {
if (!error) {
done();
}
});
function sha256(buffer) {
return crypto.createHash("sha256").update(buffer).digest();
}
function askUserBToSign(buffer) {
var digest = sha256(sha256(buffer));
return secp256k1.sign(digest, signerPrivKeyB).signature;
}
A very simple backend for the above client might look like this:
module.exports.createSchema = async function (conn) {
console.log("Creating schema in backend");
await conn.query(
"CREATE TABLE IF NOT EXISTS example " +
"(id SERIAL PRIMARY KEY, stuff TEXT NOT NULL)"
);
};
module.exports.backendFunctions = {
fun1: async function (
conn,
tx_iid,
call_index,
signers,
stringArg,
arrayArg,
bufferArg
) {
console.log("fun1 called in backend");
},
fun2: async function (conn, tx_iid, call_index, signers, intArg1, intArg2) {
console.log("fun2 called in backend");
},
};
module.exports.backendQueries = {
findStuff: async function (readOnlyConn, queryObject) {
console.log("Search for " + queryObject.text);
if (queryObject.text === "giveMeHits") {
return { hits: 4 };
}
return { hits: 0 };
},
};
GTX architecture
Generic transactions were developed to make it easier to make user implementations of Postchain.
The user doesn't have to invent a binary format for it's transactions. With GTX you specify a
set of functions that you will call from the client, and the GTX client will serialize the
function calls, sign them and send to Postchain.
User
|
| req.fun1('arg1', 'arg2');
| req.fun2('arg1'); req.sign(privKeyA); req.send(err => {})
v
GtxClient
|
| <Buffer with serialized message>
v
RestClient
|
| POST http://localhost:7741/tx {tx: 'hex encoded message'}
v
RestApi
|
| <Buffer with serialized message>
v
Postchain
|
| backend.fun1(conn, tx_iid, 0, [pubKeyA], 'arg1', 'arg2');
| backend.fun2(conn, tx_iid, 1, [pubKeyA], 'arg1');
v
Backend
The first four arguments to backend.fun1 are
conn
is a database connection that the backend function can use to query/update the databasetx_iid
is the primary key of this postchain transaction.call_index
, 0 in this example. It's the index within the GTX of the current callsigners
, all signers of this GTX. The signatures from these signers are already verified by
the GTX framework when the backend function is called.
Release notes
1.0.0
- New logger accessible in index.ts.
- Enable the client to connect to a blockchain through multiple nodes running the blockchain.
- Load balancing by randomly distributing transactions and queries between nodes.
- Retry policy added for HTTP request to the blockchain.
- Enables you to discover the nodes running your blockchain by querying D1 with your dapp´s blockchain RID. Read more in the Chromia client providers Readme.
Breaking changes:
- Previously a rest client was initialized with one string containing the address of the node running your blockchain. Now an instance of the rest client is initiated with a list of strings representing the addresses of the nodes where your dapp is running.
- Previously a rest client query was called with two parameters; queryName and queryObject. Now this call only takes one parameter called queryObject, which is defined as:
{ type: string;
[arg: string]: RawGtv;
}
where type is what previously was called query name.
0.*.*
Early version of the postchain-client written in javascript.