Skip to main content
Version: 3.4 (unsupported)

README

ScalarDL Web Client SDK​

This is a library for web applications by which the applications can interact with a ScalarDL network.

Node version used for development and testing​

This package has been developed and tested using Node LTS v14.16.0. named "fermium". This means we cannot guarantee the package nominal behaviour when using other Node versions.

Install​

We can use package manager to install this library. For example, to install with NPM:

NPM

npm install @scalar-labs/scalardl-web-client-sdk

You can also find a bundle scalardl-web-client-sdk.bundle.js which can be imported statically in @scalar-labs/scalardl-web-client-sdk/dist.

HOWTO​

Create ClientService instance​

ClientService class is the main class of this package. It provides following functions to request ScalarDL network.

NameUse
registerCertificateTo register a client's certificate to a ScalarDL network
registerContractTo register the contracts to the registered client of the ScalarDL network
listContractsTo list all registered contracts of the client
executeContractTo execute a registered contract of the client
validateLedgerTo validate an asset of the ScalarDL network to determine if it is tampered

If an error occurs when executing one of the above methods, a ClientError will be thrown. The ClientError.statusCode provides additional context. Please refer to the Runtime error section below for the status code specification.

Use the code snippet below to create a ClientService instance.

import { ClientService } from '@scalar-labs/scalardl-web-client-sdk';
const clientService = new ClientService(clientProperties);

Or, if you use the static release, try following

<head>
<meta charset="utf-8">
<script src="scalardl-web-client-sdk.bundle.js"></script>
</head>

<script>
const clientService = new Scalar.ClientService(clientProperties);
</script>

The clientProperties argument is mandatory for the constructor. This is a properties example that a user foo@example.com would use to try to connect to the server scalardl.example.com:50051 of the ScalarDL network.

{
'scalar.dl.client.server.host': 'scalardl.example.com',
'scalar.dl.client.server.port': 50051,
'scalar.dl.client.server.privileged_port': 50052,
'scalar.dl.client.cert_holder_id': 'foo@example.com',
'scalar.dl.client.private_key_pem': "-----BEGIN EC PRIVATE KEY-----\nMHc...",
'scalar.dl.client.cert_pem': "-----BEGIN CERTIFICATE-----\nMIICjTCCAj...n",
'scalar.dl.client.cert_version': 1,
'scalar.dl.client.tls.enabled': false,
}

If the auditor capability is enabled on the ScalarDL network, specify additional properties like the following example. In this example, the client interacts with the auditor scalardl-auditor.example.com and detects Byzantine faults including data tampering when executing contracts.

{
'scalar.dl.client.auditor.enabled': true,
'scalar.dl.client.auditor.host': 'scalardl-auditor.example.com',
'scalar.dl.client.auditor.port': 40051,
'scalar.dl.client.auditor.privileged_port': 40052,
}

In what follows assume that we have a clientService instance.

Register the certificate​

Use the registerCertificate function to register a certificate on the ScalarDL network.

await clientService.registerCertificate();

Please refer to the Status code section below for the details of status.

Register contracts​

Use the registerContract function to register a contract.

await clientService.registerContract('contractId', 'com.example.contract.contractName', contractUint8Array, propertiesObject);

Register functions​

Use the registerFunction function to register a function.

await clientService.registerFunction('functionId, 'com.example.function.functionName', functionUint8Array);

List registered contracts​

Use listContracts function to list all registered contracts.

const constracts = await clientService.listContracts();

Execute a contract​

Use executeContract function to execute a registered contract. It will also execute a function if _functions_ is given in the argument.

const response = await clientService.executeContract('contractId', argumentObject);
const executionResult = response.getResult();
const proofsList = response.getProofs();
const response = await clientService.executeContract('contractId', { 'arg1': 'a', '_functions_': [functionId] }, { 'arg2': 'b' });

{ 'arg1': 'a', will be passed via contractArgument, while { 'arg2': 'b' } will be passed via functionArgument.

Validate an asset​

Use the validateLedger function to validate an asset in the ScalarDL network.

const response = await clientService.validateLedger('assetId');
const status = response.getCode();
const proof = response.getProof();

Validate an asset linearizably​

The default ledger validation in a Auditor-enabled ScalarDL network is non-linearizable; i.e., there might be cases where Ledger and Auditor look inconsistent temporarily. ScalarDL supports linearizable ledger validation. To use it, we can configure the properties as follows

{
'scalar.dl.client.auditor.enabled': true,
...
'scalar.dl.client.auditor.linearizable_validation.enabled': true,
'scalar.dl.client.auditor.linearizable_validation.contract_id': '<choose a contract ID>',
}

and, register the ValidateLedger contract as the contract ID we specified in the properties. Then, the ClientService.validateLedger function can provide linearizable ledger validation.

Runtime error​

Error thrown by the client present a status code.

try {
await clientService.registerCertificate();
} catch (clientError) {
const message = clientError.message;
const statusCode = clientError.code;
}

Enumeration StatusCode enumerates all the possible status.

StatusCode = {
OK: 200,
INVALID_HASH: 300,
INVALID_PREV_HASH: 301,
INVALID_CONTRACT: 302,
INVALID_OUTPUT: 303,
INVALID_NONCE: 304,
INCONSISTENT_STATES: 305,
INVALID_SIGNATURE: 400,
UNLOADABLE_KEY: 401,
UNLOADABLE_CONTRACT: 402,
CERTIFICATE_NOT_FOUND: 403,
CONTRACT_NOT_FOUND: 404,
CERTIFICATE_ALREADY_REGISTERED: 405,
CONTRACT_ALREADY_REGISTERED: 406,
INVALID_REQUEST: 407,
CONTRACT_CONTEXTUAL_ERROR: 408,
ASSET_NOT_FOUND: 409,
FUNCTION_NOT_FOUND: 410,
UNLOADABLE_FUNCTION: 411,
INVALID_FUNCTION: 412,
DATABASE_ERROR: 500,
UNKNOWN_TRANSACTION_STATUS: 501,
RUNTIME_ERROR: 502,
CLIENT_IO_ERROR: 600,
CLIENT_DATABASE_ERROR: 601,
CLIENT_RUNTIME_ERROR: 602,
};

IndexedDB support​

This library supports storing private keys in the browsers' IndexedDB. To use the feature, please decorate ClientService object with ClientServiceWithIndexedDb as follows.

const clientService = await new ClientServiceWithIndexedDb(new ClientService(properties));

ClientServiceWithIndexedDb stores a private key in IndexedDB if the key is specified in client properties and reads a private key from the IndexedDB if the key is not specified in client properties.

Based on the behavior, it is recommended to use it as follows. If a private key is not found, IndexedDbKeyNotFoundError will be thrown and the application needs to get a private key from an external service.

let properties = {
'scalar.dl.client.cert_holder_id': 'foo@example.com',
'scalar.dl.client.cert_version': 1,
...
}; // Not specify 'scalar.dl.client.private_key_pem' or 'scalar.dl.client.private_key_cryptokey'

let clientService;
try {
// It tries to read a private key from IndexedDB
clientService = await new ClientServiceWithIndexedDb(new ClientService(properties));
} catch (err) {
if (err instanceof IndexedDbKeyNotFoundError) {
properties['scalar.dl.client.private_key_pem'] = /* from some place */
// This time, it stores the specified private key in IndexedDB
clientService = await new ClientServiceWithIndexedDb(new ClientService(properties));
} else {
throw err; // How to handle the error should be decided by application side
}
}

deleteIndexedDb​

deleteIndexedDb removes a private key in IndexedDB according to scalar.dl.client.cert_holder_id and scalar.dl.client.cert_version in client properties.

clientService = await new ClientServiceWithIndexedDb(new ClientService(properties));
clientService.deleteIndexedDb(); // Remove stored key in indexedDb

Envoy configuration​

ScalarDLT server (grpc) uses a custom header called rpc.status-bin to share error metadata with the client. This means envoy needs to be configured to expose the header to clients. More specifically, rpc.status-bin needs to be added to the expose-headers field of the cors configuration.

Contributing​

This library is mainly maintained by the Scalar Engineering Team, but of course we appreciate any help.

  • For asking questions, finding answers and helping other users, please go to stackoverflow and use scalardl tag.
  • For filing bugs, suggesting improvements, or requesting new features, help us out by opening an issue.

License​

ScalarDL client SDK is dual-licensed under both the AGPL (found in the LICENSE file in the root directory) and a commercial license. You may select, at your option, one of the above-listed licenses. Regarding the commercial license, please contact us for more information.