.

Key Concepts

Introduction

Nano (formerly known as RaiBlocks) is a cryptocurrency that offers no fees, near-instant transactions and extremely high scalability. Unlike traditional cryptocurrencies which use blockchains, Nano uses a novel block lattice approach, in which each account has its own blockchain, and only the account owner can modify its blockchain.

The usage of a block lattice means that in order to complete a transaction, the only parties who have to complete any work are the sender and the receiver. This is a huge contrast to protocols like Bitcoin where the global network has to agree on the state of each and every transaction, resulting in huge energy costs and slow transactions.

There are many more details to be found in the official whitepaper, which is certainly worth reading, but this documentation attempts to provide a much simpler, summarized version of what's in the whitepaper that's more approachable to normal programmers. It also includes a lot of details that, as far as I know, are not documented anywhere else. Enjoy!

Wallets

A wallet is a cryptographic data structure that contains an arbitrary number of deterministically indexed accounts. A wallet has no function or purpose other than holding accounts.

Seed

Each wallet is generated from the hash of a seed, a 32-byte uppercase hex string (like 9F0E444C69F77A49BD0BE89DB92C38FE713E0963165CCA12FAF5712D7657120F) that can be generated by a node or passed in by the user.

Anyone who has access to a wallet's seed can access all of its accounts from any node in the network. Therefore it's extremely important to safeguard your wallet seed in order to ensure you that don't lose access to your account, and that nobody else gains access to your account.

Accounts

An account (also known as a frontier) is an entity in a wallet that is able to send and receive transactions and retain a balance. Each account has a 64-byte address starting with xrb_ and its own blockchain, which only the account holder can modify. See Blocks below to find out what types of transactions accounts can make.

Index

Each account is generated deterministically from a wallet seed and an index. For example, the seed 9F0E444C69F77A49BD0BE89DB92C38FE713E0963165CCA12FAF5712D7657120F has the following addresses:

IndexAddress
0xrb_3yiqftdf6t4s9nwhtpjpqr1sd5yinyupa3m54fh7c1mxy53bkpecczshr4uy
1xrb_3x3z1ak17y4edwccme1bkc9nrniy49moiumd1ernhh341ctm14ccsopkknnw
1019 - 1xrb_1jw3tw7tsmkoexcgz4ubyzwqprsx3c9wkxuor5bb6oh8f5k81d9odhnh1ukj

Many services that integrate Nano choose to assign a unique index to each user of the service, thus giving each user their own unique deposit address without any special processing required. A wallet can hold up to 1019 - 1 accounts, which should be enough for any purpose.

Private key

An account's private key is used for offline signing of blocks. Anyone with access to an account's private key can send and receive transactions on behalf of that account from any node.

Public key

An account's public key is used to derive its address and as the initial work hash for the account's open block (see below).

Opening an account

An account's first transaction must always be an open block. An open block is like a receive block--it must have an accompanying send transaction in order to be valid. In other words, you need to send Nano to an account's address before you can open it.

Representatives

Representatives are accounts which cast votes in the case of a fork in the network. Account holders who are unable to reliably participate in voting for connectivity reasons can name a representative who can vote with the weight of their balance, but do not otherwise have any access to their funds. Each account may name one representative.

A good representative is a node that has high uptime (so it can vote frequently) and a locally stored wallet containing accounts that other users can delegate to. Maximizing the number and diversity of representatives increases network resiliency.

Changing representatives

An account holder can change representatives any time by sending a change block to the network.

Blocks

A block represents a transaction made by an account in the network. There are four types: send, receive, open and change. All block types share a few common parameters:

typeThe block's type (send, receive, open or change).
previousThe hash of the previous block in the account's blockchain, or the account's public key if this is its first block.
workA Proof of Work hash generated based on the previous block's hash.
signatureA unique ED25519 (with Blake2b digest) signature generated based on the block's contents that is easily verified.

Send block

A send block is used to send funds to another account. Once a send block is published to the network, the amount in the send block is irreversibly deducted from the balance of the sending account. A send is considered to be "pocketed" when the receiving account publishes a receive block (see below).

accountThe address of the account to send funds to.
balanceThe sending account's current balance, minus the amount sent.
typesend
accountxrb_1updxgcgxkxrrpdcydfd7yxowfmbohdnbr3pmumk191cbru61spczr9jgmhf
balance2007125000000000000000000000000
previousA286FD300598BF0C8CCC1196943B9CEB94F268CC89F2010B7F7EE4055CC6AB8C
work1e6b3b4c936d327e
signature8BB727F2ED60F17877117D1B2E823C1484DF67DA9454CE80A4BC84E8465568F1A26E5A5EECD79A2B133DF8F250E270AAC52D9583441BDC6125766FDAA7818109

Receive block

A receive block is used to complete (or "pocket") a transfer of value. It is only valid if it's matched with a send block (source) addressed to the receiving account.

sourceThe hash of the send block that is being received.
typereceive
sourceDED555CE596E54F783424BA3DEC0B6EEF26E9E865DE1FCA3B58264555FC184E8
previous81D04E3188A5E8CF6BD98BBE836156B4CA0E395D3188AAF1BA20A15172E375D2
work60f1e62e2f5d0561
signature1BD5AFE968F37C1FDA96B0C2CB5381377BD2CAD4D6D6331B6160B304F6A1C54076DAB885EF4DC1F0BB79AE7B58575140BEF709E32D8A1DCD5D4210684C567E0C

Open block

An open block is used to open account and is functionally the same as a receive block. An open block must be the first block in every account's blockchain, and must name the account's representative.

sourceThe hash of the send block that is being received.
representativeThe address of the representative that this account will delegate to (can be its own address).
typeopen
source69161915A1715B26BE22C7BD8DBFA4A282D0CDC09AA45552773FB42581263C2A
previous2F93C5B0A5B1A6500220E50FA35402DB714C2B2A6CE826CED52D8CB78096D396
representativexrb_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs
work228a08d5e9b1b4c5
signatureDBA741223C3EF29C6434B7DE996F71EA9F6C572BCA9A4E1F5003FD4BE9005FA8F71D522BF15D69FC820AA2C6D24772468B90CC03C8FFE763D8D537924BF6AA03

Change block

A change block is used to change an account's representative. There is no cost associated with this action.

representativeThe address of the new representative that this account will delegate to (can be its own address).
typechange
previous7605DBF66FBA3A0BD92B81303AE613193F1E8CB990FC3894E6AAEA8E1C83B849
representativexrb_3pczxuorp48td8645bs3m6c3xotxd3idskrenmi65rbrga5zmkemzhwkaznh
workfe7cb9eeb491ddbb
signatureF9EF13E84621386C6182B7CBE40D88EC518E3A6F8509B0AE0E7402C2DF161C22CF515BA148CDB62D18046329A34D86DEBA448240330DF5B5CED6BB80420C3204

Proof of Work

As an anti-spam measure, each block published to the network must include a valid work value, which is a 64-bit integer (displayed in hex format in block explorers and RPC commands).

The Proof of Work algorithm works by iterating through random 64-bit integers, or "work values". It concatenates the previous block hash (or the account's public key, if the account has no blocks yet) with the work value and runs the concatenated value through a Blake2b hash until the resulting hash exceeds the work_threshold agreed upon by nodes in the network, which is 0xffffffc000000000 as of February 2018. You can see a C++ implementation of this here.

Proof of Work is not meant to take a long time--it can take as little as 0.2 seconds on average on the fastest GPUs available in early 2018. It is simply meant to prevent abuse of the network. Since every block an account publishes must include work generated from the previous block's hash, an account can send a maximum of 5 transactions per second with high-end hardware.


RPC Guide

This guide will show you how to send and receive transactions on the Nano network step-by-step. In order to access the network, you'll need to have access to a full Nano node with RPC enabled. The easiest way to do this is to sign up for the Nanode Node API, which will give you instant RPC access.

You'll learn how to transact with Nano using raw RPC calls with node.js 9.x and the axios HTTP client. If you prefer an even easier solution, we've created an easy-to-use  NANO client library for node.js that will allow you to send and receive transactions with just a couple lines of code.

Connecting to RPC

Let's create an axios HTTP client that connects to our RPC endpoint. If you're using our Node API, you'll need to supply your API Key in the Authorization header. If you're running your own node, you won't need any headers.

We can test our connection to the node by sending an HTTP POST with the RPC action block_count, which will return the number of blocks in the node's ledger. All RPC calls take a JSON object with an action parameter and any applicable parameters for the requested action.

import axios from 'axios'

const rpc = axios.create({
  baseURL: 'https://api.nanode.co',
  headers: {
    Authorization: 'YOUR_API_KEY'
  }
})

const res = await rpc.post('/', {action: 'block_count'})
console.log(res.data) // Output: {count: "5648377", unchecked: "2906"}

Opening an account

In order to transact with Nano, you'll need to use an account. Each account is publicly referred to by an address, a 64-byte alphanumeric string beginning with xrb_. You can easily generate a new account by calling the key_create RPC command.

const res = await rpc.post('/', { action: 'key_create' })
console.log(res.data)

// Output:
// {
//   "private": "4F69E61AB017298A1192544A31E1966571A1D274987BCEDE53EF9C45A98E887C",
//   "public": "5CA743D7809377A04D61EAC3CDF92A1438A5A2091AA9D6D9A5A62AE84AB1B90F",
//   "account": "xrb_1q79ahdr36uqn38p5tp5sqwkn73rnpj1k8obtuetdbjcx37d5gahhd1u9cuh"
// }
Safeguard your private key! If you lose it, you will lose all access to the funds in your account. Similarly, if anyone gets a hold of your private key, they will be able to do anything they want with your funds, from any node in the network.

Great, we now have an address (account param) our account's public and private keys! However, the Nano network doesn't know that this address has actually been claimed yet. We'll need to generate an open block and broadcast it to the network to prove our ownership of this account.

Before we can open this account, we'll need to send some Nano to its address. Any amount will do. Send some Nano from your existing wallet and put your new account's address into your favorite block explorer to confirm that there is a pending transaction.

Now you can construct and publish your open block using the steps below. Check out the open block reference for details about the parameters passed into the block_create RPC call.

Note: An open block is functionally the same as a receive block in that it will add the amount of the corresponding send block to your account's balance.

// Address and keys for the account we wish to open
const accountAddress = 'xrb_1q79ahdr36uqn38p5tp5sqwkn73rnpj1k8obtuetdbjcx37d5gahhd1u9cuh'
const accountPublicKey = '5CA743D7809377A04D61EAC3CDF92A1438A5A2091AA9D6D9A5A62AE84AB1B90F'
const accountPrivateKey = '4F69E61AB017298A1192544A31E1966571A1D274987BCEDE53EF9C45A98E887C'

// Step 1. Retrieve hash of the send block that you sent from your wallet
const pending = await rpc.post('/', {
  action: 'accounts_pending',
  accounts: [accountAddress]
})

const sendBlockHash = pending.data.blocks[accountAddress][0]

// Step 2. Generate Proof of Work from your account's public key
const workResult = await rpc.post('/', {
  action: 'work_generate',
  hash: accountPublicKey
})

// Step 3. Generate an open block for your new account using "block_create"
const newBlock = await rpc.post('/', {
  action: 'block_create',
  type: 'open',
  previous: accountPublicKey,
  key: accountPrivateKey,
  account: accountAddress,
  source: sendBlockHash,
  work: workResult.data.work,
  representative:
    'xrb_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg'
})

// Step 4. Publish your open block to the network using "process"
const processResult = await rpc.post('/', {
  action: 'process',
  block: newBlock.data.block
})
console.log(processResult.data.hash) // The hash of your newly published open block

Congratulations, you've opened your new account by publishing your first block! 🎉

To summarize, opening a Nano account lets the network know a few things:

  • The creator of the account (you) has acknowledged ownership by publishing an open block signed with the account's private key.
  • You have completed the send transaction by receiving the amount sent.
  • You have authorized a representative account to vote on your behalf in the event of a network fork.

Sending Nano

Sending Nano using RPC is a 5-step process. See the example below.

// Address and private key for the account we are sending from
const accountAddress = 'xrb_1q79ahdr36uqn38p5tp5sqwkn73rnpj1k8obtuetdbjcx37d5gahhd1u9cuh'
const accountPrivateKey = '4F69E61AB017298A1192544A31E1966571A1D274987BCEDE53EF9C45A98E887C'

// Address we are sending to
const toAccountAddress = 'xrb_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg'

// Amount we want to send in Nano
const nanoAmount = 0.01

// Step 1. Convert Nano amount to raw 128-bit stringified integer. Since converion
// RPC methods don't support decimal amounts, we will multiply our amount
// by 1000 and convert from krai to raw.
const conversionResult = await rpc.post('/', {
  action: 'krai_to_raw',
  amount: (nanoAmount * 1000).toString()
})

const rawAmount = conversionResult.data.amount

// Step 2. Retrieve your account info to get your latest block hash (frontier)
// and balance
const info = await rpc.post('/', {
  action: 'account_info'
  account: accountAddress,
  count: 1
})

// Step 3. Generate Proof of Work from your account's frontier
const workResult = await rpc.post('/', {
  action: 'work_generate',
  hash: info.data.frontier
})

// Step 4. Generate a send block using "block_create"
const newBlock = await rpc.post('/', {
  action: 'block_create',
  type: 'send',
  key: accountPrivateKey,
  account: accountAddress,
  destination: toAccountAddress,
  balance: info.data.balance,
  amount: rawAmount,
  previous: info.data.frontier,
  work: workResult.data.work
})

// Step 5. Publish your send block to the network using "process"
const processResult = await rpc.post('/', {
  action: 'process',
  block: newBlock.data.block
})
console.log(processResult.data.hash) // The hash of your newly published send block