Neutron Polarization Manipulator

    exonum-client

    0.18.4 • Public • Published

    Light Client for Exonum Blockchain

    Build status npm version Coverage Status js-standard-style

    A JavaScript library to work with Exonum blockchain from browser and Node.js. Used to sign transactions before sending to blockchain and verify blockchain responses using cryptographic proofs. Contains numerous helper functions. Find out more information about the architecture and tasks of light clients in Exonum.

    If you are using Exonum in your project and want to be listed on our website & GitHub list — write us a line to contact@exonum.com.

    Library compatibility with Exonum core:

    JavaScript light client Exonum core
    0.18.4 1.0.*
    0.18.3 1.0.0-rc.1
    0.17.1 0.12.*
    0.16.9 0.11.*
    0.16.9 0.10.*
    0.13.0 0.9.*
    0.10.2 0.8.*
    0.9.0 0.7.*
    0.6.1 0.6.*
    0.6.1 0.5.*
    0.3.0 0.4.0
    0.3.0 0.3.0
    0.2.0 0.2.0
    0.1.1 0.1.*

    Getting started

    There are several options to include light client library in the application:

    The preferred way is to install Exonum Client as a package from npm registry:

    npm install exonum-client

    Otherwise you can download the source code from GitHub and compile it before use in browser.

    Include in browser:

    <script src="node_modules/exonum-client/dist/exonum-client.min.js"></script>

    Usage in Node.js:

    let Exonum = require('exonum-client')

    Data types

    Exonum uses protobufjs library to serialize structured data into protobuf format.

    Each transaction is signed before sending into blockchain. Before the transaction is signed it is converted into byte array under the hood.

    The data received from the blockchain should be converted into byte array under the hood before it will be possible to verify proof of its existence using cryptographic algorithm.

    Developer can both define data structures on the fly or use precompiled stubs with data structures.

    To define Protobuf structures use protobufjs library.

    Example:

    const MessageSchema = new Type('CustomMessage')
      .add(new Field('balance', 1, 'uint32'))
      .add(new Field('name', 2, 'string'))
    const Message = Exonum.newType(MessageSchema)

    Exonum.newType function requires a single argument of protobuf.Type type.

    Hash

    Exonum uses cryptographic hashes of certain data for transactions and proofs.

    Different signatures of the hash function are possible:

    Exonum.hash(data, type)
    type.hash(data)
    Argument Description Type
    data Data to be processed using a hash function. Object
    type Definition of the data type. Custom data type or transaction.

    An example of hash calculation:

    // Define a data structure
    const Message = new Type('User')
      .add(new Field('balance', 1, 'uint32'))
      .add(new Field('name', 2, 'string'))
    // Define a data type
    const User = Exonum.newType(Message)
    
    // Data to hash
    const data = {
      balance: 100,
      name: 'John Doe'
    }
    // Get a hash
    const hash = User.hash(data)

    It is also possible to get a hash from byte array:

    Exonum.hash(buffer)
    Argument Description Type
    buffer Byte array. Array or Uint8Array.

    An example of byte array hash calculation:

    const arr = [8, 100, 18, 8, 74, 111, 104, 110, 32, 68, 111, 101]
    const hash = Exonum.hash(arr)

    Signature

    The procedure for signing data using signing key pair and verifying of obtained signature is commonly used in the process of data exchange between the client and the service.

    Built-in Exonum.keyPair helper function can be used to generate a new random signing key pair.

    Sign data

    The signature can be obtained using the secret key of the signing pair.

    There are three possible signatures of the sign function:

    Exonum.sign(secretKey, data, type)
    type.sign(secretKey, data)
    Exonum.sign(secretKey, buffer)
    Argument Description Type
    secretKey Secret key as hexadecimal string. String
    data Data to be signed. Object
    type Definition of the data type. Custom data type.
    buffer Byte array. Array or Uint8Array.

    The sign function returns value as hexadecimal String.

    Verify signature

    The signature can be verified using the author's public key.

    There are two possible signatures of the verifySignature function:

    Exonum.verifySignature(signature, publicKey, data, type)
    type.verifySignature(signature, publicKey, data)
    Argument Description Type
    signature Signature as hexadecimal string. String
    publicKey Author's public key as hexadecimal string. String
    data Data that has been signed. Object
    type Definition of the data type. Custom data type.

    The verifySignature function returns value of Boolean type.

    An example of signature creation and verification:

    // Define a data structure
    const Message = new Type('User')
      .add(new Field('balance', 1, 'uint32'))
      .add(new Field('name', 2, 'string'))
    const User = Exonum.newType(Message)
    
    // Define a signing key pair
    const keyPair = Exonum.keyPair()
    
    // Data that has been hashed
    const data = {
      balance: 100,
      name: 'John Doe'
    }
    // Signature obtained upon signing using secret key
    const signature = Exonum.sign(keyPair.secretKey, data, User)
    // Verify the signature
    const result = Exonum.verifySignature(signature, keyPair.publicKey, data, User)

    Transactions

    Transaction in Exonum is an operation to change the data stored in blockchain. Transaction processing rules is a part of business logic implemented in a service.

    Sending data to the blockchain from a light client consist of 3 steps:

    1. Describe the fields of transaction using custom data types
    2. Sign data of transaction using signing key pair
    3. Send transaction to the blockchain

    Read more about transactions in Exonum, or see the example of their usage.

    Define transaction

    An example of a transaction definition:

    const Transaction = new Type('CustomMessage')
      .add(new Field('to', 2, 'string'))
      .add(new Field('amount', 3, 'uint32'))
    
    const SendFunds = new Exonum.Transaction({
      schema: Transaction,
      service_id: 130,
      method_id: 0
    })

    Exonum.Transaction constructor requires a single argument of Object type with the next structure:

    Property Description Type
    schema Protobuf data structure. Object
    service_id Service ID. Number
    method_id Method ID. Number

    schema structure is identical to that of custom data type.

    Sign transaction

    An example of a transaction signing:

    // Signing key pair
    const keyPair = Exonum.keyPair()
    
    // Transaction data to be signed
    const data = {
      from: 'John',
      to: 'Adam',
      amount: 50
    }
    
    // Create a signed transaction
    const signed = SendFunds.create(data, keyPair)

    Send transaction

    To submit transaction to the blockchain send function can be used.

    Exonum.send(explorerBasePath, transaction, attempts, timeout)
    Property Description Type
    explorerBasePath API address of transaction explorer on a blockchain node. String
    transaction Signed transaction bytes. String, Uint8Array or Array-like
    attempts Number of attempts to check transaction status. Pass 0 in case you do not need to verify if the transaction is accepted to the block. Optional. Default value is 10. Number
    timeout Timeout between attempts to check transaction status. Optional. Default value is 500. Number

    The send function returns a Promise with the transaction hash. The promise resolves when the transaction is committed (accepted to a block).

    An example of a transaction sending:

    // Define transaction explorer address
    const explorerBasePath = 'http://127.0.0.1:8200/api/explorer/v1/transactions'
    const transactionHash = await Exonum.send(explorerBasePath, signed.serialize())

    Send multiple transactions

    To submit multiple transactions to the blockchain sendQueue function can be used. Transactions will be sent in the order specified by the caller. Each transaction from the queue will be sent to the blockchain only after the previous transaction is committed.

    Exonum.sendQueue(explorerBasePath, transactions, attempts, timeout)
    Property Description Type
    explorerBasePath API address of transaction explorer on a blockchain node. String
    transactions List of transactions. Array
    attempts Number of attempts to check each transaction status. Pass 0 in case you do not need to verify if the transactions are accepted to the block. Optional. Default value is 10. Number
    timeout Timeout between attempts to check each transaction status. Optional. Default value is 500. Number

    The sendQueue function returns a Promise with an array of transaction hashes. The promise resolves when all transactions are committed.

    Cryptographic proofs

    A cryptographic proof is a format in which a Exonum node can provide sensitive data from a blockchain. These proofs are based on Merkle trees and their variants.

    Light client library validates the cryptographic proof and can prove the integrity and reliability of the received data.

    Read more about design of cryptographic proofs in Exonum.

    Merkle tree proof

    const proof = new Exonum.ListProof(json, ValueType)
    console.log(proof.entries)

    The ListProof class is used to validate proofs for Merkelized lists.

    Argument Description Type
    json The JSON presentation of the proof obtained from a full node. Object
    ValueType Data type for values in the Merkelized list. Custom data type

    The returned object has the following fields:

    Field Description Type
    merkleRoot Hexadecimal hash of the root of the underlying Merkelized list String
    entries Elements that are proven to exist in the list, together with their indexes Array<{ index: number, value: V }>
    length List length Number

    See an example of using a ListProof.

    Map proof

    const proof = new Exonum.MapProof(json, KeyType, ValueType)
    console.log(proof.entries)

    The MapProof class is used to validate proofs for Merkelized maps.

    Argument Description Type
    json The JSON presentation of the proof obtained from a full node. Object
    KeyType Data type for keys in the Merkelized map. Custom or built-in data type
    ValueType Data type for values in the Merkelized map. Custom data type

    Keys in a map proof can either be hashed (which is the default option) or raw. To obtain a raw version for KeyType, use MapProof.rawKey(KeyType). The key type is determined by the service developer when the service schema is created. Raw keys minimize the amount of hashing, but require that the underlying type has fixed-width binary serialization.

    The returned object has the following fields:

    Field Description Type
    merkleRoot Hexadecimal hash of the root of the underlying Merkelized map String
    missingKeys Set of keys which the proof asserts as missing from the map Set<KeyType>
    entries Map of key-value pairs that the are proven to exist in the map Map<KeyType, ValueType>

    See an example of using a MapProof.

    Integrity checks

    Verify block

    Exonum.verifyBlock(data, validators)

    Each new block in Exonum blockchain is signed by validators. To prove the integrity and reliability of the block, it is necessary to verify their signatures. The signature of each validator are stored in the precommits.

    The verifyBlock function throws an error if a block is invalid.

    Argument Description Type
    data Structure with block and precommits. Object
    validators An array of validators public keys as a hexadecimal strings. Array

    Verify table

    Exonum.verifyTable(proof, stateHash, fullTableName)

    Verify table existence in the root tree.

    Returns root hash for the table as hexadecimal String.

    Argument Description Type
    proof The JSON presentation of the proof obtained from a full node. Object
    stateHash Hash of current blockchain state stored in each block. String
    fullTableName Name of the table, such as token.wallets. String

    Built-in structures

    The library exports Protobuf declarations from the core crate. Consult Protobuf files included into the library for more details.

    Helpers

    Generate key pair

    const pair = Exonum.keyPair()
    {
      publicKey: "...", // 32-byte public key
      secretKey: "..." // 64-byte secret key
    }

    Exonum.keyPair function generates a new random Ed25519 signing key pair using the TweetNaCl cryptographic library.

    Get random number

    const rand = Exonum.randomUint64()

    Exonum.randomUint64 function generates a new random Uint64 number of cryptographic quality using the TweetNaCl cryptographic library.

    Converters

    Hexadecimal to Uint8Array

    const hex = '674718178bd97d3ac5953d0d8e5649ea373c4d98b3b61befd5699800eaa8513b'
    Exonum.hexadecimalToUint8Array(hex)

    Hexadecimal to String

    const hex = '674718178bd97d3ac5953d0d8e5649ea373c4d98b3b61befd5699800eaa8513b'
    Exonum.hexadecimalToBinaryString(hex)

    Uint8Array to Hexadecimal

    const arr = new Uint8Array([103, 71, 24, 23, 139, 217, 125, 58, 197, 149, 61])
    Exonum.uint8ArrayToHexadecimal(arr)

    Uint8Array to Binary String

    const arr = new Uint8Array([103, 71, 24, 23, 139, 217, 125, 58, 197, 149, 61])
    Exonum.uint8ArrayToBinaryString(arr)

    Binary String to Uint8Array

    const str = '0110011101000111000110000001011110001011110110010111110100111010'
    Exonum.binaryStringToUint8Array(str)

    Binary String to Hexadecimal

    const str = '0110011101000111000110000001011110001011110110010111110100111010'
    Exonum.binaryStringToHexadecimal(str)

    String to Uint8Array

    const str = 'Hello world'
    Exonum.stringToUint8Array(str)

    Contributing

    The contributing to the Exonum Client is based on the same principles and rules as the contributing to exonum-core.

    Coding standards

    The coding standards are described in the .eslintrc file.

    To help developers define and maintain consistent coding styles between different editors and IDEs we used .editorconfig configuration file.

    Test coverage

    All functions must include relevant unit tests. This applies to both of adding new features and fixing existed bugs.

    Changelog

    Detailed changes for each release are documented in the CHANGELOG file.

    Other languages support

    Light Clients for Java and Python

    License

    Exonum Client is licensed under the Apache License (Version 2.0). See LICENSE for details.

    Install

    npm i exonum-client

    DownloadsWeekly Downloads

    212

    Version

    0.18.4

    License

    Apache-2.0

    Unpacked Size

    1.37 MB

    Total Files

    33

    Last publish

    Collaborators

    • deniskolodin
    • qvantor
    • slowli
    • boguslavsky
    • exonum-ci