What's QAN Platform

"The QAN private blockchain is ready and commercially available since October 14, 2020. Get your access key to the QAN private blockchain."

QAN is a multi-purpose smartcontract platform with the main goal to secure blockchains from quantum adversaries, meaning most of its cryptographic layer resists quantum computers.

In addition, QAN tries to address some practical limitations of current platforms by denominating platform fees in fiat currencies, having the option for a contract owner to pay its caller's fees, rewarding contract code reuse and having a fluid developer experience.

We aim respect and remain compatible with the practices made by ethereum and its most popular ERC standards, and also offer a migration tool from EVM1.0 type smartcontract into QAN.

At the time of writing this documentation, the two network types of QAN produced the following benchmarks:

  • PoA network with NATS overlay: 16kTPS
  • PoR network with our own gossip overlay: 1.6kTPS

Tests were performed in a docker network of 6 validator nodes each limited to:

  • 1.7Ghz CPU
  • 1Gb RAM
  • 100Gb Storage

Network

QAN has two types of network layers one the standard censorship-resistant epidemic-stype gossip network with an unbiased consensus and a more naive Proof of Authority network for enterprise players, aiming for transaction speed at the compromise of censorship-resistance.

Storage

We utilize facebook's RocksDB as our storage layer as it's the quickest and most scalable key-value store in the industry that's maintained by a reliable entity.

Cryptography

QAN's cryptographic stack consists of:

  • GLYPH for digital signatures
  • SWIFFTX for general hashes
  • SIS for special (length-regular) hashes

all of which are reduced to worst-case lattice problems.

These components are included in our Fence crate which in the case of a PoA network has to be licensed, and is included by default in the public network free of use.

Getting started

Using docker-compose

If you received an invitation key for our closed beta, here are the steps to fire up your node and blockchain explorer with compose. If not, get your access key here

Then head over to our GitHub repo and clone it.

git clone "https://github.com/QANplatform/privnet.git"

Then cd into the cloned repo directory:

cd privnet

Then simply issue this command WITH YOUR access token you obtained from us: (the one in the command won't work, it's for demonstrating purposes only):

bash test.sh deploy compose "192A4209-F2CC-4F81-9F17-E8C4FBC89D74"

Wait for a few seconds for the explorer to migrate, and check http://localhost:8080.

The explorer-frontend is behind a secure nginx proxy with modsec and fail2ban configured so you can safely deploy it to www.

Interacting with the CLI wallet

You can interact with the built-in CLI wallet by entering the docker environment as follows:

docker exec -it "qan_node_1" /usr/bin/wallet_cli/wallet_cli

Enter wallet path

The wallet will ask for a wallet file path. There is a test wallet already available for testing, simply enter testwallet and hit return.

Enter wallet password

The wallet will ask for the wallet password. The password for the testwallet is empty, so simply hit return.

Verify wallet address

To make sure that everything went well, type print_address, hit return and you should see 52c4ba9a2237cc9e03192cd448e8e5e9a17211dd printed to the console.

Installation

With cargo

QAN can be installed system-wide using cargo:

cargo install qanplatform

Make sure to have the following installed:

  • Rust (stable) > 1.44
  • Cargo > 1.44

From git

git clone https://git.qan.dev/silur/qanplatform.git
cd qanplatform
cargo build --release
sudo cp target/release/{qand,wallet_cli} /usr/bin

Configuration

Currently, the QAN daemon (qand) accepts a TOML config file which has the following configuration variables:

  • rpc_addr where the RPC should bind to, accepts an ipv4:port pair
  • data_dir where the ledger should be saved. Make sure to have plenty of space here as QAN's ledger is especially large!
  • boostrap_addr the initial peer we should use to get more peers from. In public networks it's discarded after having enough peers, in a private (PoA) setting this is the message broker node.
  • nats_path an optional flag that's only used in the PoA setting, pointing to the NATS binary that handles our overlay network.
  • chain_id a number indicating which chain (testnets, mainnet etc) should the node be operate on
  • genesis_path path to an ethereum-style JSON file containing the genesis block:
{
  "alloc": {
    "3017cdacfdd62c18258d0ecec94a9d625ec68d89": {
      "balance": "3735928559"
    },
    "34afd10b29b9b326ac829666fbb567a06314004a": {
      "balance": "3735928559"
    },
    "438fd045604618dd63f32e8308d4d666ac7620c2": {
      "balance": "3735928559"
    },
    "439c8d610f4a09988eee2f2b8594a064bd6565f8": {
      "balance": "3735928559"
    },
    "5ae8549d6a278ac289f773ed14fe41b1aef54eca": {
      "balance": "3735928559"
    },
    "5db9f9b0fcbccce9fcc54eada399237496b4154b": {
      "balance": "3735928559"
    },
    "863d6d60f3bd13589733c4c3857cc50701f10731": {
      "balance": "3735928559"
    },
    "9285c8d6a314d9bd579fe5d3012151d4cf6d6977": {
      "balance": "3735928559"
    },
    "af08e7a5d727e8974b4f479c6325fafbd50b7dda": {
      "balance": "3735928559"
    },
    "fa131cbfa8703f5a3dd46089807552012ddf5587": {
      "balance": "3735928559"
    },
    "52c4ba9a2237cc9e03192cd448e8e5e9a17211dd": {
      "balance": "3735928559"
    }
  },
  "coinbase": "0",
  "difficulty": "0",
  "extraData": "dedicato imperatum ultra articulo mortis",
  "gasLimit": "10000000",
  "gasUsed": "0",
  "mixHash": "0",
  "nonce": "0",
  "number": "0",
  "parentHash": "0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp": "1592997601671"
}

Creating a wallet

When running the QAN wallet_cli you are welcomed by the following message:

 _________
< welcome >
 ---------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Please enter a wallet path.
If it doesn't exist I'll create it for you
Wallet:

Enter a path for your wallet file here, after which you'll be asked to enter a password twice. This password encrypts your wallet with AES256 so make sure you'll remember this, we don't have the ability to recover wallets with lost passwords!

After your wallet is created type help to list available commands for you:

>help
Available commands:
        print_address
        print_pubkey
        get_tx <hash>
        get_block <hash|height>
        get_header <hash|height>
        get_height
        get_account <hash>
        get_balance <hash>
        list_blocks
        put_raw <serialized transaction>
        start_mining
        stop_mining
        send <nonce> <data> <token_value> <to_address>
            eg: 'send 01 faff 01 0000000000000000000000000000000000000000'
        callvm <nonce> <contract_hash> <function_name> <arguments>
        deploy <nonce> <wasm_file>
        start_mining
        stop_mining
        help

Sending transactions

To send QARKS to a user you only need their address, the 20-byte hash of their account that looks like this: 0x4d8c7689176e6c26a2f84e9f6fec8a197bf5ecff

From the CLI wallet use the send command to send your QARKs:

send 0.000001 0x4d8c7689176e6c26a2f84e9f6fec8a197bf5ecff

As a result you'll get the transaction's hash which you can keep track of in a blockchain explorer.

In contrast with ethereum and other account-based models, as public keys of the signature algorithm used in QAN cannot be extracted with a method like ECRECOVER, transaction/account nonces are handled slightly differently.

A non-existing account (in the ledger) indicates this address has not participated in a transaction from either side. An account with a 0 nonce means it has no outgoing transactions but have received QARKS. Every account with a 0 nonce has to make it's first outgoing transaction by appending it's signing public key to the transaction which serves as an introduction. Every forthcoming transaction of this account will be validated with this public key later on. Any transaction with a nonce larger than 1 that has no stored public key on a node is discraded on that node locally.

Calling a contract

Calling smart contracts also happen trough transactions, and only involve 2 additional parameters compared to simple QARK transfers.

One is the function's name you want to call within the function, and the second is a list of parameters that will be passed to this function as follows:

callvm e0367c4c43655c6f651a67a6fcce05548ff0affa add 40 2

Calling the calculator contract with the above address on function add should return 42 and you are also shown the transaction hash of this call.

Network types

QAN supports two overlay messaging and consensus models. PoA environments (commonly used in enterprise) to maximize TPS at the cost of censorship resistance and PoR for an unbiased public network which has slower transaction finalty.

The PoA layer is a coordinated and a synchronized protocol backed up by NATS. In the "private network" mode, qand starts the NATS binary and connects to it's RPC to form a cluster. This cluster will be the home of your private network peers who communicate in a synchronized manner. Note that synch network communication doesn't scale well so the promised tens of thousands of TPS drastically drops when reaching thousands of nodes in a cluster.

To build a "private" PoA network binary make sure the appropriate [[bin]] is uncommented in daemon/Cargo.toml:

[[bin]]
name = "qand-poa"
path = "src/poa_bin.rs"

By default cargo build will build only the PoR (public network) and the CLI wallet.

Note that quantum-resistant features are only available for the PoA setting upon request.

PoA

Our proof of authority model utilize NATS as a gossiping layer and utilize the same VRF used in the private network in a more naive way, as the adversarial model is bent. This mode does not enforce spice pricing and assume misuse of the execution engine is accountable by the authority.

Validators are required to register their VRF public key in the 0x0000000000000000000000000000000000000001 contract but block validation can simply time out of now VRF proof arrived in a preset epoch. Using epochs for block sortition is convenient in such small networks but the coordination (and possibly filtering) of them are in the hands of the "master node".

The "difficulty" here is adjusted directly to the number of registered public keys in the contract, and block proposers simply produce a VRF output and proof for their blocks.

Validators take the transaction senders public key from the 0x00...1 precompile and verify the VRF proof. If it's valid, it checks whether the VRF output is lower than the difficulty (as in hashcash). The key point of this straw-man VRF consensus is that by staking to public keys and using VRFs one can only submit one vote to a block and this eliminates computational waste from standard PoW.

PoR

The PoR or "public" setting has a stricter adversarial model at the cost of transaction speed. First, the PoR consensus and messaging layer is asynchronous which is expected to scale better.

Epochs are no longer coordinated by a master node but in a decentralized manner using Thresold Logical Clocks.

As the underlying gossip layer is async and peers only have knowledge of their neighbour, the FLP problem states we won't be able to distinguish adversarial/faulty behaviour from simple timeouts.

By making a compromise on communication speed, TLCs which work similarly as Vector Clocks are used in the messaging layer (without witnesses) meaning a node has to "earn the right" to broadcast a message by forwarding a predefined number of other peers' messages. Currently QAN does not utilize cryptographic witnesses for this method which we plan to address in future releases. As long as we can assume a majority of honest nodes, we can model the upper layers of the consensus protocol as if it were a synchronous one, and define the VRF proof timeouts in a TLC epoch rather than wall-clock time.

Difficulty in PoR is adjusted based on:

  • the number of stakeholders in the precompile contract
  • whether TLC timeouts happened
  • the number of ommers happened in previous block proposals

Smart contracts

Following recent practice, smart contracts in QAN are WebAssembly binaries because WASM is open, blazing-fast, primitive enough so determinism can be checked and browser-friendly.

Following ethereum's model, smart contracts are only capable of interacting with the external world trough transactions and don't have access to I/O and network calls that could cause nondeterminism. Any assumption in a contracts (or the execution engine's itself) threat model that rely on external data (like QARK/USD prices) are done trough oracles.

The QAN WASM virtual machine

WebAssembly currently have only four primitive types, namely I32, I64, F32 and F64. There is a proposal for "interface types" however as it's currently unstable and not standardized custom types and structs are handled in QANs execution engine trough CBOR serialization.

Functions with primitive bodies are exported as they are into a standard WASM export.

Functions with complex types (such as strings or user-defined structs) are wrapped into a (de)serializer function first. This step is hidden from contract developers and users and only the QAN-specific execution engine should ever call these internal functions directly.

External functions calls are provided to contracts trough WebAssembly "imports", and QAN's VM consists several of these, for example "REVERT", "CALL" etc.

QAN currently is capable of executing any WASM binary as a contract, meaning any language that can produce these is supported as long as they are not undeterministic. Note that WASI is not supported

QAN also provides SDKs with common types and annotators to ease contract development for the following languages:

  • Rust

Spice prices and execution fees

In a decentralized network, misuse of the protocol is not direclty accountable so execution of smart contracts in an adversarial manner should be bounded economically. Following Ethereums GAS model, the QAN VM is also metered. Prices for opcodes are denominated in Spice, and follows the same economical incentives as the GAS model.

The actual settlement of spice for a contract call however, is calculated in Fiat currency. This happens trough ChainLink which is an oracle service feeding the current fiat price of QARK tokens back into the network. This way smartcontract owners can calculate and plan ahead of infrastructural costs for decenralized applications without being exposed to the volatility of the tokens price.

For example, a contract call add which hypothetically costs 100 Spice, with a $1/QARK price and 1QARK/spice would cost $100 to call today. If the USD price of QARK jumps up to $2/QARK tomorrow, the network will still only subtract $100USD worth of QARKS from the caller wallet by using a 0.5QARK/spice value that we got from the 0x00000..1 precompile containing ChainLink data.

Writing contracts

QAN has developer kits containing blockchain-specific external calls and convenient types to ease contract development. Using this kit is not mandatory to develop QAN contracts, but definitely makes it easier as otherwise a developer has to implement serialization and non-primitive types manually. For smaller contracts however (for example a voting contract) an unmodified WASM binary is a valid smart contract.

A goal while developing these SDKs is to hide as much of the underlying blockchain target from developers as we can, while not abusing the language itself. This means the blockchain functionaly of our SDK is mostly visible only trough macros and annotators and the rest is standard Rust, Typescript etc.

See our examples and guides for various languages in subsections.

Rust

Dependencies

Our Rust SDK consist of two crates. rust-sdk and rust-sdk-macros.

To create a QAN contract initialize a standard rust project as follows:

cargo init --lib mycontract

Next, import the necessary crates in Cargo.toml:

[package]
name = "mycontract"
version = "0.1.0"
authors = ["Nyan <nyan@nyan.cat>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
qansdk-macros = "0.1.0"
qansdk = "0.1.0"
serde = {version = "1.0.114", default-features = false, features = ["derive"] }
wasm-bindgen = "0.2.64"
serde_cbor = {version = "0.11.1", default-features = false}

Coding and building contracts

With the dependencies in place, we are ready to edit src/lib.rs!

QAN smart contracts support almost the whole of rust_std! This means you don't need annotators, decorators, macros etc and every rust code with a (non-mangled) main is a valid smart contract!

The only trick you need to keep in mind is that to get parameters passed to the VM by the incomming transaction, you need to call get_param("paramname"). Rust can infer the type by default but it always nice if you help it out :)

To store and get a value on chain (into/from the account) use sstore and sload respectively like a simple key-value map.

use std::string::String; // we can use std!

pub fn hello(name: String) -> String {
    format!("hello {}", name)
}
pub fn add(a: i32, b: i32) -> i32 {
	a + b
}

#[no_mangle]
pub unsafe fn main() -> i32 {
    let p: i32 = get_arg("number");
    let caller: String = get_arg("caller_name");

    sstore("answer", p+1);
    sstore("greeting", hello(caller));
    return 0;
}

Let's build the contract with cargo build --release --target=wasm32-unknown-unknown

If you dont have the WASM target, use the following command to install it:

rustup target add wasm32-unknown-unknown

Deployment

With your contract binary ready at target/wasm32-unknown-unknown/release/mycontract.wasm let's deploy it into the network!

Fire up wallet_cli and unlock our wallet with the password:

 _________
< welcome >
 ---------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Please enter a wallet path.
If it doesn't exist I'll create it for you
Wallet: /tmp/mywallet
Enter wallet key:*********
>

After that use the deploy command with the binary path to send it as a transaction to the 0x00000..00 contract:

>deploy /home/nyan/mycontract/target/wasm32-unknown-unknown/release/mycontract.wasm
transaction_hash: 84aa5a3db0ea2ba66eb84ac3df0e3124bce087d8
contract_hash: 5269a904e785bbc4a32fa59aea09a92a902d8434

The two return hashes indicate the transaction made it into the mempool and we are now waiting for it to be confirmed.

The resulting contract address (contract_hash above) is not calculated from the contract bytecode but from the sender address and it's nonce.

QAN Fence

Fence is our PQC SDK that powers QAN platform.

It is available as a separate product too, and as an opt-in feature using cargo build --features=fence

To get a quote on fence: hello[aT]qanplatform[dOt]com

Cloud Deployment

This section will walk you through deploying QAN's Private Network to various clouds in the EASIEST way possible. Finally you can spend more time experimenting and having fun than trying to set up complex distributed systems in the cloud.

Supported providers

Below you can find the supported providers. More are on the way!

Amazon AWS Lightsail

To deploy to the Amazon AWS Lightsail provider, all you need is an AWS_ACCESS_KEY and AWS_SECRET_KEY with proper permissions. After generating above said keys, you just need to run our deployer container which will walk you through all steps required.

Obtaining access token

The first step will be acquiring the AWS_ACCESS_KEY and AWS_SECRET_KEY pairs as follows:

  1. Log in to the Amazon AWS Console
  2. Navigate to the IAM Security Credentials section
  3. Click on the "Access keys (access key ID and secret access key)" accordion

Add a new access key

Click the "Create New Access Key" button seen on the screenshot below:

Afterwards you will be presented with a pop-up like this one:

Now copy both the "Access Key ID" and "Secret Access Key" which is shown in the pop-up modal window to a text file.

Running the deployer

All it takes is a simple docker run ! It will drop you in an interactive session, where you will only need to enter the keys you obtained in the previous step. The deployer script will select the proper hardware for you, install all required dependencies and scaffold a fully featured private network on your server.

Command to run:

docker run --rm -it qanplatform/deploy:v1-1-1

Put in your access token when asked, sit back, have a coffee and ready you go in 10 minutes!

Linode Cloud

To deploy to the Linode Cloud provider, all you need is a Linode access key with proper permissions. After generating above said access key, you just need to run our deployer container which will walk you through all steps required.

Obtaining access token

  1. Log in to the Linode Cloud portal
  2. Navigate to the API Tokens section in your profile
  3. Add a new access token:
    • The "Label" can be anything you prefer
    • The "Linodes" permission is required in "Read & Write" mode (screenshot below)
  4. Click "Create Token"
  5. Copy the token which is shown in the pop-up modal window to your clipboard

Correct token permissions screenshot

Running the deployer

All it takes is a simple docker run ! It will drop you in an interactive session, where you will only need to enter your Linode Cloud access token you obtained in the previous step. The deployer script will select the proper hardware for you, install all required dependencies and scaffold a fully featured private network on your server.

Command to run:

docker run --rm -it qanplatform/deploy:v1-1-1

Put in your access token when asked, sit back, have a coffee and ready you go in 10 minutes!