# Postchain Client

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.

## Usage

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.

## Initializing the Client

Firstly, import the required libraries.

```typescript
import crypto from "crypto-browserify";
import secp256k1 from "secp256k1";
import { encryption, createClient, newSignatureProvider } from "postchain-client";
```

Then, create some dummy keys.

```typescript
const signerPrivKeyA = Buffer.alloc(32, "a");
const signerPubKeyA = secp256k1.publicKeyCreate(signerPrivKeyA);
const signerPrivKeyB = Buffer.alloc(32, "b");
const signerPubKeyB = secp256k1.publicKeyCreate(signerPrivKeyB);
```

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`.

```typescript
const blockchainRID =
  "7d565d92fd15bd1cdac2dc276cbcbc5581349d05a9e94ba919e1155ef4daf8f9";
```

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.

```typescript
const chromiaClient = await createClient({
  nodeURLPool: "http://localhost:7740",
  blockchainRID,
});
```

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.

```typescript
const chromiaClient = await createClient({
  directoryNodeURLPool: ["url1", "url2", "url3", "etc."],
  blockchainRID,
});
```

## Queries

### Query Option 1

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.

```typescript
chromiaClient.query("get_foobar", {
  foo: 1,
  bar: 2,
});
```

### Query Option 2

Alternatively, the query function can take an object with a `name` property and an `args` property.

```typescript
chromiaClient.query({
  name: "get_foobar",
  args: {
    foo: 1,
    bar: 2,
  },
});
```

### Typed Query

You can specify argument and return types for a given query in TypeScript.

```typescript
type ArgumentsType = {
  foo: number;
  bar: number;
};

type ReturnType = {
  foobar: string;
};

const result = await chromiaClient.query<ArgumentsType, ReturnType>(
  "get_foobar",
  {
    foo: 1,
    bar: 2,
  }
);
```

## Transactions

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.

```typescript
const signatureProviderA = newSignatureProvider({ privKey: signerPrivKeyA });
```

### Simple Transaction

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`.   

```typescript
const { status, statusCode, transactionRID } = await chromiaClient.signAndSendUniqueTransaction(
  {
    operations: [
      {
        name: "my_operation",
        args: ["arg1", "arg2"],
      },
    ],
    signers: [signatureProviderA.pubkey],
  },
  signatureProviderA
);
```
It is also possible to pass a single operation.
```typescript
const { status, statusCode, transactionRID } = await chromiaClient.signAndSendUniqueTransaction(
  {
    name: "my_operation",
    args: ["arg1", "arg2"],
  },
  signatureProviderA
);
```

### Signing a Transaction

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.

```typescript
const signedTx = await chromiaClient.signTransaction(
  {
    operations: [
      {
        name: "my_operation",
        args: ["arg1"],
      },
    ],
    signers: [signatureProviderA.pubkey],
  },
  signatureProviderA
);
```

### Sending an Unsigned Transaction

```typescript
const receipt = await chromiaClient.sendTransaction({
  name: "my_operation",
  args: ["arg1", "arg2"],
});
```
### Sending a Signed Transaction

```typescript
chromiaClient.sendTransaction(signedTx);
```

### Advanced Transaction

Create a transaction object.

```typescript
const tx = {
  operations: [
    {
      name: "my_operation_1",
      arguments: ["arg1", "arg2"],
    },
    {
      name: "my_operation_2",
      arguments: ["arg1", "arg2"],
    },
  ],
  signers: ["signer1", "signer2"],
};
```

You can modify the object to add operations or signers.

```typescript
tx.operations.push({
  name: "my_operation_3",
  arguments: ["arg1", "arg2"],
});

tx.signers.push("signer3");
```

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.

```typescript
const uniqueTx = chromiaClient.addNop(tx);
```

Sign and send the transaction.

```typescript
const signedTx = await chromiaClient.signTransaction(
  uniqueTx,
  signatureProviderA
);

const receipt = await chromiaClient.sendTransaction(signedTx);
```
## PromiEvent
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.

```typescript
chromiaClient.sendTransaction({
          name: "my_operation",
          args: ["arg1", "arg2"],
        }).on("sent", (receipt: TransactionReceipt) => {
          console.log("The transaction is sent")
          });
```
## External Signing Example

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.

```typescript
function askUserBToSign(buffer) {
  // The signed digest is a double sha-256
  var digest = sha256(sha256(buffer));
  return secp256k1.sign(digest, signerPrivKeyB).signature;
}
```

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:

```typescript
const signatureProviderB = {
  pubKey: signerPubKeyB,
  sign: askUserBToSign,
};
```

## Architecture

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).

```
User
 |
 | chromiaClient.sendTransaction()
 |
 v
 |
 | <Buffer with serialized message>
 |
 v
 |
 | 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
```

## Contributing to the Project

### Run tests
Unit tests:

``npm run test:unit``

Integration tests:

1. Make sure a postgres database is running. Read more [here](https://docs.chromia.com/getting-started/dev-setup/database-setup).
2. Start blockchain

    ```cd resources``` 

    ```chr node start --wipe```

3. Run tests

    ```npm run test:integration```
