Welcome to the Aergo Developers Guide!

Aergo is an open blockchain platform that allows businesses to build innovative applications and services by sharing data on a trustless and distributed IT ecosystem. Aergo combines the best of public and private blockchains to be scalable, enterprise-ready, and easy to develop with.

This website contains guides for developers to get started developing with Aergo and detailed references.

Introduction

Aergo is developing a practical blockchain platform, frameworks, libraries, and tools.

Design goals

There are four main ideologies regarding this project.

  • Developer-friendly
  • Guaranteed performance
  • Scalable architecture
  • Open, extensible, inter-connectable network

Current features

  • BFT-dPOS with voting

  • Name system

  • Aergo Lua smart contract

  • Ship (development framework, package management)

  • Simple client API

  • Client SDKs

  • Sub projects

    • Litetree
    • Sparse Merkle Tree
  • Hub Enterprise (management and monitoring)

  • Merkle bridge verification

  • Testnet deployment

Roadmap

These features are under active development or planned.

  • Aergo SQL
  • Parallelism (inter-contract)
  • Simple branching (2WP or simple Plasma)
  • Orchestration with Aergo Horde
  • Service with Aergo Hub
  • Advanced performance features

Dapp Development

This article gives an introduction into the high level approach to developing dapps on the AERGO blockchain.

Development cycle

These are components of today’s blockchain application development cycle that Aergo offers new solutions for.

  1. Design (UX patterns, economic models)
  2. Clients (interacting with the blockchain)
  3. Smart contracts (new programming patterns, debugging and testing)
  4. Infrastructure (deployment cost, security, privacy, monitoring)

Design

The possibilities of designing applications for Aergo are endless: social networks, distributed organizations, games, monetary systems, asset management, trading, databases, public deliberation, crowdsourcing, …

Aergo tries to not limit designers’ and developers’ creativity to create novel applications. However, the platform aims to offer reasonable guidelines, standard protocols, and well-tested models to make designing dapps easier than ever.

Clients

Using the Hera SDK family, client applications can easily interact with the blockchain.

Smart contracts

Smart contracts are the main backend component of distributed applications. As other blockchain platforms had a serious limitation in terms of cost and scalability, it used to be not feasible to develop large scale, complex systems. With Aergo and the use of embedded sidechains and proof systems, smart contracts can really shine.

Aergo also offers a development toolchain that includes package management, Aergo Ship.

Infrastructure

Aergo is currently available as a public testnet and separate private installations. In the future, a network of blockchains can be orchestrated using Aergo Horde. Aergo Hub will offer an easy configuration and monitoring interface. With these features, developers will not have to worry about the deployment of their applications.

Running a Node

This section includes general instructions for running Aergo nodes.

If you want to connect to an existing Aergo network (such as the public mainnet and testnet), please also refer to this section for more details.

Quickstart

Using Docker

The easiest way to run a local Aergo server is using Docker. An up-to-date Docker image is always available on Docker Hub.

docker run -p 7845:7845 aergo/node

You can pass arguments to the server like this:

docker run -p 7845:7845 aergo/node aergosvr --testmode

To supply your own config file, use:

docker run -p 7845:7845 -v $(pwd)/config.toml:/aergo/config.toml aergo/node aergosvr --config /aergo/config.toml

Syncing with public networks: Please refer to the syncing guide for further instructions.

Using binaries

You can download the most recent binary releases from Github.

Building from source

You can also build the binaries yourself from source. See here for details.

Configuration

This page explains the possible ways to configure an Aergo node.

There are three common modes of operation for aergo server:

  • BP Node - Block provider node, which mines and propagates blocks
  • Agent Node - Node to improve security and block propagation of BP node, added since Aergo v2.0
  • Watcher Node - Node to get and validate all blocks, previously named to Full Node before Aergo v2.0
  • Single Node - BP Node without peer, which uses to something like test.

We describe only single node configuration in this. More will updated for the BP Node and full node.

Single Aergo Server

We will show default configuration file next. You can check the each fields’ meaning in here

# aergo TOML Configuration File (https://github.com/toml-lang/toml)
# base configurations
datadir = ".aergo/data"
enableprofile = false
profileport = 6060
enablerest = false
enabletestmode = false
authdir = ".aergo/auth"

[rpc]
netserviceaddr = "127.0.0.1"
netserviceport = 7845
nstls = false
nscacert = ""
nscert = ""
nskey = ""
nsallowcors = false

[rest]
restport = "8080"

[p2p]
# Set address and port to which the inbound peers connect, and don't set loopback address or private network unless used in local network 
netprotocoladdr = "" 
netprotocolport = 7846
npbindaddr = ""
npbindport = -1 
# Set file path of key file
npkey = ""
npaddpeers = [
]
nphiddenpeers = [
]
npmaxpeers = 100
nppeerpool = 100
npexposeself = true
npusepolaris = true
npaddpolarises = [
]

[blockchain]
# blockchain configurations
maxblocksize = 1048576

[mempool]
showmetrics = false
dumpfilepath = ".aergo/mempool.dump"

[consensus]
enablebp = true

Testmode

To enable testmode, either pass the command line option --testmode to aergosvr or set enabletestmode = true in the configuration.

In testmode, all new accounts are assigned a high number of Aergo tokens by default, basically circumventing balance checks. This means you can send any transaction without first pre-funding hard-coded accounts using a genesis block.

Testmode MUST NOT be used in production.

Reference

Module Property name Meaning
(default) datadir data files' base path
enableprofile whether use profie
profileport profile port
enablerest whether use REST API
enabletestmode whether test mode
personal enable personal account API
rpc netserviceaddr rpc hostname or address
netserviceport rpc port number
nstls whether applying tls to rpc
nscacert ca certificate file path for rpc tls
nscert certificate file path for rpc tls
nskey key file path for rpc tls
nsallowcors whether allows CORS
rest restport port number for REST API
p2p netprotocoladdr ip address or domain name to which other peers connect
netprotocolport port number for connect
npbindaddr listener binding address
npbindport listener binding port
npkey key file path for p2p tls
npaddpeers initial peer list on start-up
nphiddenpeers peerid list which will not inform to other peers
npexposeself whether to advertise node to Polaris <../tools/polaris.html>__ or other peers.
npdiscoverpeers whether to discover unknown peers and try connect to them
npusepolaris whether to connect Polaris for finding other peers
npaddpolarises list of addresses of custom polaris
peerrole role of peer. producer, agent or watcher
producers list of peer ids of block producers, valid for agent only
internalzones address ranges of internal zone, valid for agent only
agent peerid of agent, valid for block producer only
blockchain maxblocksize maximum block size
coinbaseaccount address where is send rewards for mining
mempool showmetrics whether if turn periodic log on
dumpfilepath file path for recording mempool at process termination
verifiers number of concurrency for sign verifying
consensus enablebp whether this node is BP

Logging options

It is possible to customize the log output format of all Aergo CLI tools using a file called arglog.toml placed in the current working directory.

This file is specified here.

level = "info"  # default log level
formatter = "json"  # format: console, console_no_color, json
caller = true  # enabling source file and line printer
timefieldformat = "RFC3339"

[chain]
level = "info"  # optional, log level for 'chain' module

[dpos]
level = "info"

[p2p]
level = "info"

[consensus]
level = "info"

[mempool]
level = "info"

[contract]
level = "info"

[syncer]
level = "info"

[bp]
level = "info"

Account storage

Changed in version 2.2.0.

Since version 2.2.0, aergosvr and aergocli use a new keystore file format for storing accounts. Before, accounts were saved as encrypted strings in a BadgerDB. The new keystore storage offers better portability and improved security.

For backwards compatability, aergocli and aergosvr will continue to use the old storage if it already exists (i.e. if there is an accounts directory in the data directory). If you want to migrate your existing installation to take advantage of the improved storage, you need to export all existing accounts, delete the ‘accounts’ directory inside the data directory, and then re-import your accounts. Please check the aergocli help for how to do that.

New installations will use the new storage method by default.

Monitoring

Aergo server has 5 major subcomponents: Account, Chain, MemPool, RPC, and P2P.

We can get status of component using aergocli node command (see here for detail).

$ aergocli node 
{
        "AccountsSvc": {
                "status": "started",
                "acc_processed_msg": 3,
                "msg_queue_len": 0,
                "msg_latency": "69.851µs",
                "error": "",
                "actor": null
        },
        "ChainSvc": {
                "status": "started",
                "acc_processed_msg": 238,
                "msg_queue_len": 0,
                "msg_latency": "68.849µs",
                "error": "",
                "actor": {
                        "orphan": 0
                }
        },
        "MemPoolSvc": {
                "status": "started",
                "acc_processed_msg": 237,
                "msg_queue_len": 0,
                "msg_latency": "59.462µs",
                "error": "",
                "actor": {
                        "cache_len": 0,
                        "dead": 0,
                        "orphan": 0
                }
        },
        "RPCSvc": {
                "status": "started",
                "acc_processed_msg": 120,
                "msg_queue_len": 0,
                "msg_latency": "71.785µs",
                "error": "",
                "actor": null
        },
        "p2pSvc": {
                "status": "started",
                "acc_processed_msg": 120,
                "msg_queue_len": 0,
                "msg_latency": "54.783µs",
                "error": "",
                "actor": null
        }
}

As it shown above, each component has 6 values, which are

  • status *
  • acc_processed_msg
    • number of message processed by the component
    • message is basic unit for communicate with other components
  • msg_queue_len
    • number of pending messages for the component
  • msg_latency
    • average latency for processing a message
  • error *
  • actor
    • component specific statistics are shown
    • for example, ChainSvc “actor”: { “orphan”: 0 } orphan represents number of orphan block the component has

Configuring a Network

This article explains the steps needed to configure a network of multiple block producers. You can follow this guide to setup a private Aergo blockchain network.

This guide specifically requires no prior setup, so it should be easy to follow along with new bare machines running Ubuntu. It has been tested with AWS EC2 instances. We will setup three block producers, but you can adjust the procedure to any number of BPs.

Per-machine setup

Install Docker

You will need Docker for this setup, so let’s install that now.

sudo apt-get update && sudo apt-get install docker.io
sudo usermod -aG docker ubuntu

NTP

Timing is critical in a blockchain, so it’s better to configure the machines’ time setup manually:

sudo bash
apt-get install chrony
vi /etc/chrony/chrony.conf
server time1.google.com iburst
server time2.google.com iburst
server time3.google.com iburst
server time4.google.com iburst
systemctl restart chronyd
chronyc makestep

Generate BP accounts and keys

You can use any machine (e.g. your own local machine) for this.

Start by installing aergocli if you haven’t already.

Generate accounts

aergocli account new --password yourpassword --path genesis
(displays generated address)

aergocli account new --password yourpassword --path bp01
(displays generated address)

aergocli account new --password yourpassword --path bp02
(displays generated address)

aergocli account new --password yourpassword --path bp03
(displays generated address)

Export accounts

aergocli account export --address [insert address from genesis]  --password yourpassword --path genesis
(displays exported private key)

aergocli account export --address [insert address from bp01]  --password yourpassword --path bp01
(displays exported private key)

aergocli account export --address [insert address from bp02]  --password yourpassword --path bp02
(displays exported private key)

aergocli account export --address [insert address from bp03]  --password yourpassword --path bp03
(displays exported private key)

Generate peer keys

aergocli keygen bp01
Wrote files bp01.{key,pub,id}.

aergocli keygen bp02
Wrote files bp02.{key,pub,id}.

aergocli keygen bp03
Wrote files bp03.{key,pub,id}.

Write configuration files

genesis.json (same for all machines)

{
    "chain_id":{
        "magic": "[insert an identifier string for your network]",
        "public": false,
        "mainnet": false,
        "consensus": "dpos"
    },
    "timestamp": 1548918000000000000,
    "balance": {
        "[insert address from genesis]": "470000000000000000000000000",
        "[insert address from bp01]": "10000000000000000000000000",
        "[insert address from bp02]": "10000000000000000000000000",
        "[insert address from bp03]": "10000000000000000000000000"
    },
    "bps": [
        "[insert text from bp01.id]",
        "[insert text from bp02.id]",
        "[insert text from bp03.id]"
    ]
}

config.toml (one per machine)

# aergo TOML Configration File (https://github.com/toml-lang/toml)
# base configurations
datadir = "./data"
enableprofile = true
profileport = 6060
enablerest = true
personal = false

[rpc]
netserviceaddr = "0.0.0.0"
netserviceport = 7845
nstls = false
nscacert = ""
nscert = ""
nskey = ""
nsallowcors = false

[p2p]
netprotocoladdr = "{LOCAL_IP}"  # Insert IP address from this machine
netprotocolport = 7846
npbindaddr = "0.0.0.0"
npbindport = 7846
nptls = false
npcert = ""
npkey = "bp{01,02,03}.key"  # Name of key file of node
npaddpeers = [
    "/ip4/[IP ADDRESS FROM BP 01]/tcp/7846/p2p/[PEER ID FROM BP 01]",
    "/ip4/[IP ADDRESS FROM BP 02]/tcp/7846/p2p/[PEER ID FROM BP 02]",
    "/ip4/[IP ADDRESS FROM BP 03]/tcp/7846/p2p/[PEER ID FROM BP 03]"
]

npexposeself = false
npusepolaris = false
nphiddenpeers= [
    "[PEER ID FROM BP 01]",
    "[PEER ID FROM BP 02]",
    "[PEER ID FROM BP 03]"
]

[blockchain]
usefastsyncer = true
blockchainplaceholder = false
coinbaseaccount = "[ADDRESS FROM THIS PEER]"

[mempool]
showmetrics = true
dumpfilepath = "./data/mempool.dump"

[consensus]
enablebp = true

Running

We are going to use the Docker image aergo/node to run the server. Please refer to the Docker documentation for learn about the available run options.

Check the directory contents

After following the above steps, you should now have these files in one directory for each machine. In this example we use the path /blockchain, but you can use any directory as long as you substitute its local path in the Docker run commands below.

/blockchain/
    bp{01,02,03}.key
    genesis.json
    config.toml

Create genesis block

docker run --rm \
    -v /blockchain:/aergo \
    aergo/node \
    aergosvr init --genesis /aergo/genesis.json --home /aergo --config /aergo/config.toml

Start the node

docker run -d --log-driver json-file --log-opt max-size=1000m --log-opt max-file=7 \
    -v /blockchain:/aergo \
    -p 7845:7845 -p 7846:7846 -p 6060:6060 \
    --restart="always" --name aergo-node \
    aergo/node \
    aergosvr --home /aergo --config /aergo/config.toml

Further reading

You may now want to setup further full nodes as well as Polaris for automatic node discovery.

Advanced Topics

Genesis block configuration

The genesis block is the first block created on a new chain according to a predefined specification. If you want to bootstrap your own Aergo blockchain, you will need to configure and create the genesis block. All nodes producing blocks and syncing with the network need to use the same genesis block.

You can find an example genesis configuration json file here. It is the one used for the public testnet.

Key Type Explanation
chain_id obj  
– magic string A name identifier used to distinguish different chains. Example: testnet.aergo.io
– public bool If this chain is for a public (true) or private network. This may affect certain features.
– mainnet bool If this is the main network (true) or a sidechain for another network.
– consensus string This blockchain’s consensus type (dpos)
– coinbasefee string The default transaction fee amount
timestamp number The genesis block’s creation timestamp in nanoseconds
balance obj A mapping of addresses and allocated balances
bps list A list of fallback block producer peer ids.

Manually creating peer identification

Every peer (i.e. instance of aergosvr) is identified by a peer id, derived from its public key. If unspecified, aergosvr creates these automatically, but you may want to do it manually to control the generation of keys and make backups. You can use the CLI to do that:

aergocli keygen your-keyname

# Using Docker:
docker run --rm -v $(pwd):/tools/ aergo/tools aergocli keygen your-keyname

You then need to specify the path to the generated your-keyname.key file in the Aergo configuration:

...
[p2p]
npkey = "/aergo/your-keyname.key"
...

Configuring Aergo Network 2

Aergo Network 2.0 added a new node configuration concept to protect BP more securely and spread the produced blocks faster.

In the actual case of Aergo Network, the BP node is not directly exposed to the network, but is connected to the network through the Full node on the front. By doing so, BP can focus computing resources on block production. However, the connection between BPs in different region has a disadvantage of increasing propagation time as the intermediate delivery stage occurs. And, for example, communications in regions that are physically far away, such as Asia and South America, may take a lot of time just by simply delaying the network, so it may be difficult to meet the requirements for block production.

Agent actively discover other Agents or BPs in the public chain network to establish direct connections. The block produced by client BP is passed to the Agent and BP of the public network in a way that focuses on speed and delay time, and the new block notification from the Agent and BP of public network is passed to the client BP. On the other hand, the Watcher is informed in a way that focuses on data efficiency.

Each node uses BP information and voting results recorded in the chain to verify the qualifications of a legitimate BP, and the Agent checks the qualifications of the agent through certificates issued by a legitimate BP. It combines with PeerID-based identification, which is also applied to Aergo Network 1.0, to enable accurate identification and credentials.

_images/aergo2network.png
Internal Zones

There may be several BPs in one private network; these BPs can be connected directly to spread the blocks without having to pass through Agents. And these BPs can be more efficient in resource use by sharing Agents than by each BP having Agents.

For efficient use of network and computing resources, Agent categorizes networks to internal and external, delivering only internal to external, or vise versa.

The Agent node connects the client BPs in the internal network with other BPs or Agents in the external network. The Agent passes blocks generated by a client to the BPs or Agents of the external network as quickly as possible, while the blocks notified by the external BPs or Agents are passed to the client BPs.

Configuration Example

NOTE: configuring BP and Agent are advanced topic, and wrong configuration can cause performance degradation or abnormal operation. So this configrations are more strictly checked than other settings; read the document carefully and set it up.

Suppose there is a foundation named Colchis, and the foundation decided to participate in the Aergo network. The foundation establishes three BP nodes, one Agent node, and one node for TX registration and monitoring in the private network. The private address and peer ID of each node are as follows.

BP1    : 192.168.25.21 / 16Uiu2HAmH1Eup6qLSQg3Bk7B5QbrVPk6Bzw7upHhZ74ZTS8kM
BP2    : 192.168.25.22 / 16Uiu2HAkzW5j6c6w9BhRnuudHuYc3Kqk4aNi4KbuoaYwVDx3HkS
BP3    : 192.168.25.23 / 16Uiu2HAkvwrouZCrCSG8BqMM1v9GRj11jfjfjfhby5fAXgbCg
Agent  : 192.168.25.31 / 16Uiu2HAm7P7quSBCxPHe89wNUYwCki1M8F45kMMRjw5M6bQdVWEv
Watcher: 192.168.25.32 / 16Uiu2HAkvQYs1e6jVbpAeMfTNpDgUYhxry6gMRQnMAqymVL2Z45

The foundation registered the aergonode.colchis.org domain to join the chain network and forwarded the IP address of the domain to the agent machine.

BPs and Watcher allow access only from the Foundation’s internal network and are linked to the public network only through Agent.

The connection between Agent and client BP is built on the secured environment that the network manager cares about; BP and Agent must specify each other correctly in configuraion.

configuration for BP

BP disables npexposeself, npdiscoverpeers, and npusepolaris settings so that they are not exposed to the outside and do not automatically connect to external nodes. BP node also set agent field to peer id of agent node.

# aergo TOML Configration File (https://github.com/toml-lang/toml)
# base configurations
datadir = "./data"
enableprofile = false
personal = false

[p2p]
npkey = "bp01.key"  # Name of key file of node
npaddpeers = [
    "/ip4/192.168.25.22/tcp/7846/p2p/16Uiu2HAkzW5j6c6w9BhRnuudHuYc3Kqk4aNi4KbuoaYwVDx3HkS",
    "/ip4/192.168.25.23/tcp/7846/p2p/16Uiu2HAkvwrouZCrCSG8BqMM1v9GRj11jfjfjfhby5fAXgbCg",
    "/ip4/192.168.25.11/tcp/7846/p2p/16Uiu2HAm7P7quSBCxPHe89wNUYwCki1M8F45kMMRjw5M6bQdVWEv",
    "/ip4/192.168.25.12/tcp/7846/p2p/16Uiu2HAkvQYs1e6jVbpAeMfTNpDgUYhxry6gMRQnMAqymVL2Z45"
]
npusepolaris = false
npexposeself = false
npdiscoverpeers = false # peer will not try to discover and connect other peers except for peers listed in npaddpeers
peerrole = "producer"
agent = "16Uiu2HAm7P7quSBCxPHe89wNUYwCki1M8F45kMMRjw5M6bQdVWEv"

[blockchain]
blockchainplaceholder = false
coinbaseaccount = "[ADDRESS OF CONBASE ACCOUNT]"

[mempool]
showmetrics = true
dumpfilepath = "./data/mempool.dump"

[consensus]
enablebp = true
configuration for Agent

Agent activates npexposeself, npdiscoverpeers, npusepolaris settings, and also sets addresses that can be accessed from the outside, so that they can connect to other nodes in the public network. Also set producers field to a list of the client BP’s peer ids and internal zones.

# aergo TOML Configration File (https://github.com/toml-lang/toml)
# base configurations
datadir = "./data"
enableprofile = false
personal = false

[p2p]
netprotocoladdr = "agent.colchis.org"
npkey = "agent01.key"  # Name of key file of node
npaddpeers = [
    "/ip4/192.168.25.21/tcp/7846/p2p/16Uiu2HAmH1Eup6qLSQg3Bk7B5QbrVPk6Bzw7upHhZ74ZTS8kM",
    "/ip4/192.168.25.22/tcp/7846/p2p/16Uiu2HAkzW5j6c6w9BhRnuudHuYc3Kqk4aNi4KbuoaYwVDx3HkS",
    "/ip4/192.168.25.23/tcp/7846/p2p/16Uiu2HAkvwrouZCrCSG8BqMM1v9GRj11jfjfjfhby5fAXgbCg",
    "/ip4/192.168.25.32/tcp/7846/p2p/16Uiu2HAkvQYs1e6jVbpAeMfTNpDgUYhxry6gMRQnMAqymVL2Z45"
]

npexposeself = true  # peer is advertised by polaris or other peers
npusepolaris = true
peerrole = "agent"
producers = [
    "16Uiu2HAmH1Eup6qLSQg3Bk7B5QbrVPk6Bzw7upHhZ74ZTS8kM",
    "16Uiu2HAkzW5j6c6w9BhRnuudHuYc3Kqk4aNi4KbuoaYwVDx3HkS",
    "16Uiu2HAkvwrouZCrCSG8BqMM1v9GRj11jfjfjfhby5fAXgbCg"
]
internalzones = ["192.168.25.1/24"]

[blockchain]

[mempool]
showmetrics = true
dumpfilepath = "./data/mempool.dump"

[consensus]
enablebp = false

Troubleshooting

Storage errors

DB initilaize failure

Aergo uses Badger DB as its internal storage. If the DB creation fails for Badger at initial boot, boot process will fail.

In this case, you can see the following error when checking the server’s log.

failed to initialize chaindb
failed to initialize statedb

You need to make sure that you have write access to the path you want to create the data directory on the config file If the database server was rebooted while it was running, it is possible that the DB datafile has been crashed. In this case, a reinstallation is required.

check previously running processes

The DB initialize problem is most likely to occur when a new aergosvr is run without shutting down the existing aergosvr completely. In this case, you can check with the following log.

panic: Fail to Create New DB: Cannot acquire directory lock on "config data directory". 
Another process is using this Badger database.: resource temporarily unavailable`

In this case, terminate the existing aergosvr completely and restart it.

Miscellaneous DB errors

If an error occurs in DB, the following log is output.

Database Error: description...

In this case, it’s a good idea to first check to see if there is a problem with the filesystem .If there is no problem with the filesystem, the DB datafile can be seen as corrupt. In this case, reinstallation is required.

Disk performance problem

The BP node generates a block every 1 second. When a block is created, it is stored in the DB and Disk I / O occurs at this time.

If the disk I / O is too slow, the block created by the BP will not be added to the chain. This is because the block generated by the node is propagated to another node but the block of the corresponding height is already generated in the next BP. Therefore, the block arriving late comes to be discarded.

Check the disk I / O execution time through dd. This allows you to check for disk errors.

Network trouble

peer connection failure

If there is a problem with the network configuration, the peer connections may not be normal. you can check peer connections as follows.

  1. You need to make sure that the entire peer set in the config is connected. If there is an unconnected peer, check the network configuration with the peer.

    You can check the connection status of peers with the following command:

    aergocli -p $ServerPort getpeers
    [
            {
                    "Address": "127.0.0.1",
                    "PeerID": "16Uiu2HAkxUPj8zUnSkBzP8f4mw9WvJ4s5DeMVarzsqBuyrDy6dmD",
                    "Port": "11002",
                    "State": "RUNNING"
            }
    ]
    
network performance problem

aergo uses DPOS as a consensus algorithm. Blocks generated by one node must be transmitted to other nodes at a high speed. If the block transmission is too slow due to the network problem, the block created by the BP will not be included in the chain.

You can track the block creation time and the time passed to other nodes.

Oct 30 15:50:57.009 |INFO| block produced BP=16Uiu2HAmF5fpuk9ZWQbzHUuu9C1jwtqKnZdtkQYYDapA7AyoibWP caller=/Users/everjs/go/src/github.com/aergoio/aergo/consensus/impl/dpos/blockfactory.go:213 confirms=2 id=6mdjTYFEKXJ4UQ3xdKEu5RybnVWh3Hio6VG8a5SAXPbp lpb=1356 module=dpos no=1358 sroot=9hbmSFxzGj58YQuM2642zyo8jDAuu5qTWNG8JCMb78iZO`
Oct 30 15:50:57.012 |INFO| added block successfully. best=1358
Oct 30 15:50:57.013 |DEBUG| add block chainservice blockNo=1358 caller=/Users/ev
erjs/go/src/github.com/aergoio/aergo/chain/chainservice.go:252 hash=6mdjTYFEKXJ4UQ3xdKEu5RybnVWh3Hio6VG8a5SAXPbp module=chain

Server Process Hang

The aergo server was developed using the proto actor framework. Each module in the server receives a request using a message, performs an action on it, and returns a response.

At this time, if a specific module does not operate normally and hangs, it can be checked with the following command.

aergocli -p 10001 node
{
        "AccountsSvc": {
                "status": "started",
                "acc_processed_msg": 202,
                "msg_queue_len": 0,
                "msg_latency": "55.623µs",
                "error": "",
                "actor": null
        },

...

}

If a particular module hangs, the value of msg_queue_len field is high.

The details can be analyzed through the log by increasing the log level of the module.

Tracking status of Transaction

A transaction is added to a mempool and then executed when it is included in a block. Transactions are not included in block if validation fails due to nonce, balance, etc.. All transactions in the block will have a receipt. If the VM fails to execute contract, the error is recorded in the receipt. Therefore, the state of transaction may be as follows.

  • State of transaction
    • pending in mempool
    • included in block
      • sucess
      • fail if contract
check status of transaction

Use the following command to track the status of the transaction.

aergocli -p $PORT gettx $TXHASH

If the transaction stays in the pending state in the mempool, the following status is output.

aergocli -p $PORT gettx $TXHASH
Pending:  {
 "Hash": "BTY3zrsBkVHTqFbpqKtwbXwgfpGwRoiAmKRrdFKfZRna",
 "Body": {
  "Nonce": 101,
  "Account": "AmM5wCL3XhDngGAyYk6pbd2AYKSomVAKRqLBhnP8QsBMRRoA62fv",
  "Recipient": "AmNeY5fDqvGAfpYYLxhy7AhSsFzCSL96iYBApR2aHvRzMY5D34Ky",
  "Amount": 2560000,
  "Payload": "",
  "Limit": 0,
  "Price": 0,
  "Type": 0,
  "Sign": "381yXZRAPSGAFbGYndN5xU3s9RXHddFbCxyutNtUYvyT4DbDt7jWyia4oM6CLGtXYeQqtQBvqTaNU2gwpdrCm5FjwFP6fZCQ"
 }
}
check account

First, check the nonce and balance of the corresponding account. The status of the current account can be seen by the command below.

aergocli -p $PORT  getstate --address $ADDRESS

Ensure that the nonce and balance of transaction are consistent with the account status.

check status of mempool

There are too many transactions in mempool, which can take a long time to be included in the block. To see the current status of mempool, try the following command.

aergocli -p $PORT  node

In the cache_len field, you can see the total number of tx’s in mempool.

check receipt

If the transaction is contained in a block, you can use the receipt to check the execution result. Receipt can be seen using transaction hash

aergocli -p $PORT receipt get $TXHASH
{
 "contractAddress": "AmNeY5fDqvGAfpYYLxhy7AhSsFzCSL96iYBApR2aHvRzMY5D34Ky",
 "status": "CREATED",
 "ret": ""
}

Using Public Networks

These pages explain how you can connect to and sync with public Aergo networks.

Connecting to Well-known Nodes

Mainnet

Use mainnet-api.aergo.io and the default port 7845 to connect to a public GRPC server for the mainnet.

docker run --rm aergo/tools aergocli -H mainnet-api.aergo.io blockchain

A public blockchain explorer is available at mainnet.aergoscan.io.

Testnet

Use testnet-api.aergo.io and the default port 7845 to connect to a public GRPC server for the testnet.

docker run --rm aergo/tools aergocli -H testnet-api.aergo.io blockchain

A public blockchain explorer is available at testnet.aergoscan.io.

Alpha

Use alpha-api.aergo.io and the default port 7845 to connect to a public GRPC server for the alpha network.

docker run --rm aergo/tools aergocli -H alpha-api.aergo.io blockchain

A public blockchain explorer is available at alpha.aergoscan.io.

Syncing

This setup assumes some basic knowledge about using Docker. Using Docker is the recommended way to run an Aergo node as it requires minimal configuration and enables you to use Docker features such as logging and restart policies.

Note

When you run the Docker image named aergo/node for the first time, Docker downloads the latest image automatically. To update the image to the latest version, run docker pull aergo/node. You can also specify a specific version by replacing aergo/node with, for example, aergo/node:1.0. Refer to Docker Hub to see a list of available tags.

Running

This is the main command to run a node syncing with the main Aergo network:

docker run -v $(pwd)/data:/aergo/data -p 7845:7845 -p 7846:7846 aergo/node

The -p argument maps ports from the aergo server inside the container to your host machine. 7846 is for the peer to peer protocol. 7845 is for the RPC API for connecting clients to the server; you can remove this port binding to disallow access to the API.

When running this command for the first time, it will create the default genesis block (block number 0). Afterwards it automatically starts synchronizing.

Note

If your machine is behind a NAT (such as a router), you need to setup manual forwarding of the port 7846 to allow other peers to sync with your node.

Testnet

To sync with the testnet instead, use the testnet flag:

docker run -v $(pwd)/data:/aergo/data -p 7845:7845 -p 7846:7846 aergo/node aergosvr --home /aergo --testnet

Configuration

The above commands use default configuration files suitable to connect to officially supported networks. If you want to override configuration parameters, you can supply a custom config file using a Docker volume, for example:

docker run -v $(pwd)/data:/aergo/data -v $(pwd)/config.toml:/aergo/config.toml -p 7846:7846 aergo/node aergosvr --home /aergo --config /aergo/config.toml

Please refer to Configuration for a detailed explanation of the available settings.

Note

To customize the log format, place an arglog.toml file in the container’s working directory: -v $(pwd)/arglog.toml:/aergo/arglog.toml.

Server key file

Every node is identified by a key and peer id. In the default configuration, a key is generated every time you launch the docker container. This may not be what you want. You can persist the generated key by adding a volume -v $(pwd)/auth:/aergo/auth. You can also supply the key manually by placing aergo-peer.key and aergo-peer.id files in that volume.

Refer to Configuration for details about server keys.

Tip

Instead of binding several volumes, you can use one combined volume, e.g. -v $(pwd)/aergo:/aergo.

Node discovery

Without any specific settings, the server connects to Polaris, registers itself, obtains addresses of other nodes, and automatically attempts to connect to those nodes. If the server is in a NAT environment or has multiple NICs, additional settings are required for external nodes to access the server. You have to setup the external address in the external network connection and set the internal address for binding address.

netprotocoladdr = "211.12.34.56" # external address to which other peer can connect
netprotocolport = 7846
npbindaddr = "192.168.0.2" # no config element or empty string means using same address as external
npbindport = 17846 # negative number means it is same as external port, in this case 7846

Creating Accounts

Accounts are identified by addresses that belong to private keys. To own an account, all you need is access to its private key. Always make sure that nobody except you gains access to your keys!

There are several methods to create accounts.

  1. If you are running your own node on your machine, you can create a local account:

    aergocli account new
    
    # Or using Docker:
    docker run --rm --net=host aergo/tools aergocli account new
    
  2. To create an account without a node (aka offline):

    aergocli account new --path ./wallet
    
    # Or using Docker:
    docker run --rm -it -v $(pwd)/wallet:/wallet aergo/tools aergocli account new --path /wallet
    

    The output shows the address of your new account. It is saved in account/data.db in the given path.

    You can export this new account:

    aergocli account export --address your_address --password your_password --path ./wallet
    
    # Or using Docker:
    docker run --rm -it -v $(pwd)/wallet:/wallet aergo/tools aergocli account export --address your_address --path /wallet
    

    The output shows the encrypted private key belonging to your address which can be imported in other wallets.

Note

When connecting to public nodes, creating accounts on that node is impossible. On public nodes, personal account features are disabled using personal = False. Always create accounts using local nodes or offline by one of the methods above.

Funding Accounts

Testnet

To create transactions on the public testnet, you need an account with a positive balance. After creating an account, this is how can fund your account for testing.

  1. Go to faucet.aergoscan.io, enter your account address, and click Request Tokens.
  2. Depending on demand, you need to wait for a moment. If the queue is full, try again a bit later.
  3. Check your account on Aergoscan. You should have received some AERGO tokens for your testing purposes.

These AERGO tokens are only valid on the testnet and cannot be sold or bought. They are only useful for developing and testing applications. Please only request as many tokens as you actually need.

If you have any trouble creating an account or receiving testnet tokens, please ask for help on our Discord channel.

Mainnet

You can swap AERGO ERC-20 tokens to AERGO Mainnet native tokens. Please refer to this blog post: Aergo 2-way native token swap to ERC-20

Monitoring

Aergoscan is an explorer for the officially supported networks.

You can monitor blocks as they get produced and check transactions and accounts.

Snapshots

To quickly bootstrap a new Mainnet full node, you can start syncronizing from a snapshot.

  1. Download the latest snapshot from snapshot.aergo.io.
  2. Extract the archive, e.g. tar zxvf 672571_20190423.tar.gz.
  3. Start the node, pointing it to the extracted data directory: docker run -v $(pwd)/data:/aergo/data -p 7845:7845 -p 7846:7846 aergo/node

Troubleshooting

Unavailable personal feature

Failed: rpc error: code = Unavailable desc = Unavailable personal feature

This means that on the node you connected to, personal account features are disabled using the setting personal = False. This setting is usually used on public nodes, so you cannot use personal account features when connecting to public nodes. Always create and use accounts using local nodes or offline, e.g. by generating private keys using one of Aergo’s SDKs. See also: Creating Accounts

Using Raft consensus

These pages explain how you can configure and manage nodes using raft consensus

Note

RAFT consensus can only be used for private networks.

Configuration

This page describes the configuration and logging options related to Raft consensus.

See also

See Config Reference for a detailed explanation of common config reference.

Config Reference

Module Property name Meaning
consensus.raft name raft node name. name should be unique in cluster
  newcluster initialize a new raft cluster if it doesn’t already exist
  skipempty skip producing block if there is no transactions in block
  heartbeattick heartbeat time(millisec) of raft node
  electiontickcount number of heartbeattick to wait before becomeing a candidate without heartbeat
  blockfactorytickms interval(millisec) to check if block factory should run new task
  blockintervalms block generation interval(millisec).It overrides BlockInterval of consensus
  usebackup use backup datafiles for initializing a new cluster or joining an existing cluster
  snapfrequency frequency which raft make snapshot with log
  recoverbp bp info for initializing a new cluster from backup data files
  slownodegap Max difference of chain height for determining if a node is a slow node

Logging options

To see the log related to Raft consensus, add a raft section to arglog.toml.

[raft]
level = "info"

Configuring a Network Using RAFT

This article describes the process for setting up a network of multiple block producers using RAFT Consensus.

Note

RAFT consensus can only be used for private networks.

A block chain network using RAFT consensus is called a RAFT Cluster. All the settings below assume a case where a blockchain network consisting of 3 nodes using RAFT consensus.

To create a blockchain network using RAFT consensus, you need to configure the genesis block and config file. Other settings are the same as the private network configuration using DPoS.

Common Configuration (per machines)

To configure aergo node as a block producer, it requires common configuration as descriped on Configuring a Network page.

However, additional configuration requires in genesis block and config file.

Write configuration files for RAFT

The initial nodes of a new RAFT cluster should be added to the genesis block. Every node in the cluster is a candidate to be a block producer. Only one of these nodes becomes a leader to create a block. The remaining nodes become followers who receive instructions from the leader. If a leader crashes, one of the follower nodes becomes the new leader.

Nodes that cannot be block producers can be used by adding them as fullnodes. These nodes communicate via the P2P protocol but are not included in the RAFT cluster.

genesis.json (same for all machines)

  • RAFT is not supported in the public network, so chain_id should be set to public and maninet should be set to false.
  • Add the nodes to be included in the RAFT cluster to the enterprise_bps entry. Add the name, address, and peerid of the desired nodes to the enterprise_bps. The address field uses the p2p peer format of the config file.
  • set consensus field to "raft".
  • set the bps entry to an empty list.
{
    "chain_id":{
        "magic": "[insert an identifier string for your network]",
        "public": false,
        "mainnet": false,
        "consensus": "raft"
    },
    "timestamp": 1548918000000000000,
    "balance": {
        "[insert address from genesis]": "470000000000000000000000000",
        "[insert address from bp01]": "10000000000000000000000000",
        "[insert address from bp02]": "10000000000000000000000000",
        "[insert address from bp03]": "10000000000000000000000000"        },
    "bps": [
    ],
    "enterprise_bps": [
      {
        "name": "[NICKNAME OF BP 01]",
        "address": "/ip4/[IP ADDRESS FROM BP 01]/tcp/7846",
        "peerid": "[PEER ID FROM BP 01]",
      },
      {
        "name": "[NICKNAME OF BP 02]",
        "address": "/ip4/[IP ADDRESS FROM BP 02]/tcp/7846",
        "peerid": "[PEER ID FROM BP 02]",

      },
      {
        "name": "[NICKNAME OF BP 03]",
        "address": "/ip4/[IP ADDRESS FROM BP 03]/tcp/7846",
        "peerid": "[PEER ID FROM BP 03]",
      }
    ]
}

config.toml (one per machine)

# aergo TOML Configration File (https://github.com/toml-lang/toml)
...  insert common configuration for block producer ...

[p2p]
npaddpeers = [
]
...
...
# configuration for block producer using RAFT
[consensus.raft]
newcluster=true
name="[NICKNAME OF THIS BP]"

Basically, the settings when using DPoS should be applied to the config in the same way. But you don’t need to set npaddpeers of [p2p]. Nodes use the address value of enterprise_bps to connect to others.

To use RAFT, [consensus.raft] section should be added. name is the current node’s nickname. The name must be included in the enterprise_bps of the genesis block. When you create a new RAFT cluster, you must set newcluster to true.

Other settings

To change block creation cycle

[consensus]
blockinterval=[insert value in seconds]

To change heartbeat cycle

[consensus.raft]
heartbeattick=[insert value in milliseconds ]

To skip creating empty block

[consensus.raft]
skipempty=true

Dynamic Membership Changement

Blockchain that uses the RAFT consensus can add or remove members dynamically through enterprise transactions. You do not have to restart running nodes or change the config file at this time. For a description of enterprise transactions, see transacion-types.

This article assumes the Aergo blockchain using raft consensus consisting of 3 nodes.

Abstaction

Note

Membership changes must be made after the completion of one. When adding a new node, do not add another node again until blockchain synchronization is over, since the new node becomes unavailable until the chain is synchronized and up to date. If more than half of the nodes are out of sync, the cluster stops.

Validation

When executing a membership change transaction, leader node checks if the current cluster status is changeable.

  • When adding a new node

    If any node is not properly synchronized in the whole network, it will fail. The node is either in a shutdown state or a state where synchronization is slowed down due to a failure.

  • When removing a node

    If the cluster can be stopped when the node is excluded, the delete request will fail. Deletion to a failed node always succeeds.

Prework

You need set up and unlock ENTERPRISE ADMIN ACCOUNT to execute commands with Enterprise Transaction. See the page transacion-types.

Add Member

Execute Add Node command

Send an enterprise transaction to one of the cluster nodes.

aergocli -p [RPC PORT] contract call --governance [ADMIN ACCOUNT] aergo.enterprise changeCluster [ { "command": "add", "name": "[NODE NAME]", "address": "[PEER ADDRESS]", "peerid":"[PEER ID]" } ]

example> aergocli -p 10001 contract call --governance AmQJ6HTxMzq5eios54xubENE2wAe4puF3GDuaFxBzXkWa4KpfnWr aergo.enterprise changeCluster [ { "command": "add", "name": "raft4", "address": "/ip4/127.0.0.1/tcp/11004", "peerid":"16Uiu2HAmQti7HLHC9rXqkeABtauv2YsCPG3Uo1WLqbXmbuxpbjmF" } ]
Check the command result

Query enterprise transaction results on one of the cluster nodes. The hash of enterprise transaction must be given as an argument.

aergocli -p [RPC PORT] enterprise tx "[ENTERPRISE TX HASH]" --timeout=[VALUE IN SECONDS]

The changed cluster status can be checked using getconsensusinfo RPC. "Total" represents the total number of nodes and "Bps" contains the block producer list.

aergocli -p 10001 getconsensusinfo
Write configuration for a new node (new node only)

In the config file of the node to add, set the peer addresses of the block producers to the [p2p] npaddpeers. If possible, register the peer address of all members currently registered in the raft cluster. However if necessary, only one connectable node can be set.

[p2p]
...
npaddpeers = [
"/ip4/[IP ADDRESS FROM BP 01]/tcp/7846/p2p/[PEER ID FROM BP 01]",
"/ip4/[IP ADDRESS FROM BP 02]/tcp/7846/p2p/[PEER ID FROM BP 02]",
"/ip4/[IP ADDRESS FROM BP 03]/tcp/7846/p2p/[PEER ID FROM BP 03]"
]
...

[consensus.raft]
newcluster=false
name="[NICKNAME OF NEW BP]"
Running

See the page Configuring a Network to start a new node.

Note

You should wait for the added node to finish synchronizing with the existing blockchain.

  • Check if the new node recognized the raft leader node

    If the leader node is recognized, ConsensusInfo.Status.Leader is set to one of the node names.

    aergocli -p 10004 blockchain
    > {"Hash":"3ivEw2o97WkpEvarBkVEYWCAQXhQ7BpHwmzs2L4WXUzJ","Height":7302,"ConsensusInfo":{"Type":"raft","Status":{"Leader":"raft1","Total":3,"Name":"raft4","RaftId":"dd44cf1a06727dc5","Status":null}},"ChainIdHash":"A9gpZUsFS9BVEfpUy6gbJ7YtGuHsYAmzamWkhDo54cak"}
    
  • Make sure the BlockChain Height of the new node matches the Leader

    aergocli -p [RPC PORT OF LEADER] blockchain
    aergocli -p [RPC PORT OF NEW NODE] blockchain
    

Remove Member

Get RAFT ID to remove

To delete a node, you should get RAFT ID of the node. RAFT ID is a value assigned after a node is added to RAFT. It is a value to idenity each node in RAFT Consensus.

You can get it in the result of blockchain RPC. In the example above, dd44cf1a06727dc5 is the Raft ID of the “raft4” node.

aergocli -p 10004 blockchain
> {"Hash":"...","Height":7302,"ConsensusInfo":{"Type":"raft","Status":{"Leader":"raft1","Total":4,"Name":"raft4","RaftId":"dd44cf1a06727dc5","Status":null}},"ChainIdHash":"..."}

Run enterprise transaction

aergocli -p [RPC PORT] contract call --governance [ADMIN ACCOUNT] aergo.enterprise changeCluster [ { "command": "remove", "id": "[RAFT ID]" } ]

example> aergocli -p 10001 contract call --governance AmQJ6HTxMzq5eios54xubENE2wAe4puF3GDuaFxBzXkWa4KpfnWr aergo.enterprise changeCluster '[ { "command": "remove", "id": "dd44cf1a06727dc5" } ]'

Check result of enterprise transaction

As with adding a node case, you should check “Total” and “Bps” in the results of the getconsensusinfo RPC.

aergocli -p 10001 getconsensusinfo

Wait shutdown removed node

If Enterprise Transaction is executed on the node to be deleted, the node terminates itself.

Add Member with Backup

After adding a new node, it takes a long time to synchronize the existing blockchain. To reduce this time, you can add a new node by using backup data files obtained from existing member nodes. There is no conflict because the new node resets all the cluster-related information from the given backup data files at the first startup.

Prepare backup datafiles

The bakcup datafiles can be obtained by shutting down an existing node and copying the data directory.

Note

Since the blockchain is already created in the data file, there is no need to create the genesis block.

Write Configuration file

When using backup data files, usebackup must be set to true. Set the backup data directory to datadir field.

datadir="[copied data directory]"

[p2p]
...
npaddpeers = [
"/ip4/[IP ADDRESS FROM BP 01]/tcp/7846/p2p/[PEER ID FROM BP 01]",
"/ip4/[IP ADDRESS FROM BP 02]/tcp/7846/p2p/[PEER ID FROM BP 02]",
"/ip4/[IP ADDRESS FROM BP 03]/tcp/7846/p2p/[PEER ID FROM BP 03]"
]
...

[consensus.raft]
newcluster=false
    usebackup=true
name="[NICKNAME OF NEW BP]"

The rest of the process is the same as adding members without using backup.

Recovery

If more than half of the cluster’s nodes become unrecoverable, the raft cluster can no longer delete or add any node. This is because more than half of nodes cannot agree to the change.

In this case, if you want to keep the data of BlockChain as it is, you can create a new cluster by using backup of existing node data. However, RAFT cluster information cannot be recycled.

This is called recovery.

There are some differences between recovery and general cluster creation.

  1. When creating a cluster using the recovery function, there must be one initial member node.
  2. Since genesis block is already created in blockchain, you should set initial node in config file instead of genesis block.
  3. If you want to configure N-node clusters, you need to add members dynamically from the second member.

The cluster related information is deleted in the backup files. The newly created cluster creates a block following blockchain that has already created.

Prepare backup datafiles

Copy backup datafiles from the old node to storage on the new node.

Write Configuration file(only initial node)

datadir="[copied data directory]"

[p2p]
...
npaddpeers = [
]
...

[consensus.raft]
newcluster=true
usebackup=true
name="[NICKNAME OF NEW BP]"
recoverbp={name="[NICKNAME OF NEW BP]",address="/ip4/[IP ADDRESS FROM BP 01]/tcp/7846",peerid="[PEER ID FROM NEW BP 01]"}
  • To create a new cluster, you need to set newcluster to true.
  • Set usebackup to true to use backup data files.
  • Set recoverbp to the member of the new cluster. At this time, member must only be one node.

Running a node

Start the node. After that, add nodes one by one using Dynamic membership change.

Smart Contracts

Aergo provides its own smart contract platform for implementing various business logic on the blockchain.

Currently, you can create smart contracts using the Lua programming language.

Lua

To create smart contracts for Aergo, you can use the Lua scripting language.

Lua is a powerful, efficient, lightweight, embeddable scripting language. It has a simple procedural syntax with a powerful data description structure. Lua supports a variety of programming methods: procedural programming, object-oriented programming, functional programming.

We use LuaJIT 2.1.0 as the VM. LuaJIT is a Just-In-Time Compiler (JIT) for the Lua programming language.

You can learn the Lua programming language through the following documents:

Table of contents

Hello World

This is the most basic lua smart contract to store and retrieve states in aergo. You can save a name on the blockchain with the contract call function. And you can print ‘hello …’ with the query function.

-- Define global state variables
state.var {
  Name = state.value(),
  My_map = state.map()
}

-- Initialize the state variables
function constructor()
  -- a constructor is called only once at the contract deployment
  Name:set("world")
  My_map["key"] = "value"
end

-- Update the name
-- @call
-- @param name          string: new name
function set_name(name)
  Name:set(name)
end

-- Say hello
-- @query
-- @return              string: 'hello ' + name
function hello()
  return "hello " .. Name:get()
end

-- register functions to expose
abi.register(set_name, hello)
Tutorial

This is explained based on using cli. Variables used in this example are

  • Account to deploy and execute a contract: AmPbWrQbtQrCaJqLWdMtfk2KiN83m2HFpBbQQSTxqqchVv58o82i
  • cli commands in this page need a aergosvr with enable personal feature
Check Account and Balance

First, you need an account with enough balance to deploy and execute smart contracts. (If you don’t) Import or Unlock Account to aergo server.

Compile Contract

Copy above code and save it to a file (e.g. helloword.lua). And Compile using the aergoluac compiler

./aergoluac --payload helloworld.lua
37mGLDoCPNDQw7HbCG5WPfcM3E3cLhqhgE2V2UJKwQp9QZ5nJhT14nkCdGFcmN91fewB2ZuMZ5NWJUPyD4G4G2beaTeE1cigLzyNdGuuU4Y7cY2A6MUMq5weoAGGJdyf6PUfzgQ7k1cwjjDRe7P8bN5tNR8xxiibYk1hoeV2fuG4ZGVUwosutqFYePonLAtvK57N2MphJWVkTVxkySkXBnKywCiKB2pqv93VVQbBepYrUwURAeaj5aGhuFa8sxbSjuZfrvdy3i7dAEesf1jPHyeoN6CHWEB3teQJHUUh5K89p8vZ7nXKYPSa7MbzEg3YCUx8uLYmkJPjqA5YT1dxcKWCmHV4M2nYxmv3h9v3viPFkkFuMdpJboCTV2iqkMr3robxT6okSJtDdVUk2PprZKuiipS6tbKmDmxuicJqFvtP653qdWpu4tvWQDBb5k3UJcMTcEDdrjqqtNcE8dujFxr4TWfDu9Nwb4FedLrb5z7CoRWvXi5eZBkujJdDBm5MowamWLy9Pu6UmjAmBHfkDpQiagocrFzZrDPvNbaon8r5Rph8gFHAzwaUdM3dpt3dghePsiH1jbz8j93aMhMxgE6Lgap
Deploy Contract

With the payload generated above, Deploy contract

./aergocli contract deploy AmPbWrQbtQrCaJqLWdMtfk2KiN83m2HFpBbQQSTxqqchVv58o82i --payload 37mGLDoCPNDQw7HbCG5WPfcM3E3cLhqhgE2V2UJK
wQp9QZ5nJhT14nkCdGFcmN91fewB2ZuMZ5NWJUPyD4G4G2beaTeE1cigLzyNdGuuU4Y7cY2A6MUMq5weoAGGJdyf6PUfzgQ7k1cwjjDRe7P8bN5tNR8xxiibYk1hoeV2fuG4ZGVUwosutqFYePonLAtvK57N2MphJWVkTVxkySkXBnKywCiKB2pqv93VVQbBepYrUwURAeaj5aGhuFa8sxbSjuZfrvdy3i7dAEesf1jPHyeoN6CHWEB3teQJHUUh5K89p8vZ7nXKYPSa7MbzEg3YCUx8uLYmkJPjqA5YT1dxcKWCmHV4M2nYxmv3h9v3viPFkkFuMdpJboCTV2iqkMr3robxT6okSJtDdVUk2PprZKuiipS6tbKmDmxuicJqFvtP653qdWpu4tvWQDBb5k3UJcMTcEDdrjqqtNcE8dujFxr4TWfDu9Nwb4FedLrb5z7CoRWvXi5eZBkujJdDBm5MowamWLy9Pu6UmjAmBHfkDpQiagocrFzZrDPvNbaon8r5Rph8gFHAzwaUdM3dpt3dghePsiH1jbz8j93aMhMxgE6Lgap
1 : FPqA3kNQHoVXqKJv8JNpUSsh8F8id87yvRr5UzQFoCcH TX_OK
Get receipt of contract

Look up the actual contract address with the transaction ID above.

./aergocli receipt get FPqA3kNQHoVXqKJv8JNpUSsh8F8id87yvRr5UzQFoCcH
{
 "BlokNo": 317,
 "BlockHash": "48zceVwBZt5dpzuEFMtJB9icPXUu7YG1Xkxvw5N92yFW",
 "contractAddress": "AmfzX3SHXVTBU9NSEWXaLxxjN11KsUpm1Gb3YjF7kmsHrgmL41WU",
 "status": "CREATED",
 "ret": {},
 "txHash": "AWeaoCTpohuQpBMTFaW3qFpZqWwuTehXA8ZkAX59UjMV",
 "txIndex": 0,
 "from": "AmPbWrQbtQrCaJqLWdMtfk2KiN83m2HFpBbQQSTxqqchVv58o82i",
 "to": "",
 "usedFee": 10,
 "events": []
}

If the status is not ‘CREATED’, it may not be included in the block yet, or there may be an error. Wait a while until the transaction is included in the block. Or check the server’s error log.

Get ABI of contract

Look up ABI of contract with the contract address above.

./aergocli contract abi AmfzX3SHXVTBU9NSEWXaLxxjN11KsUpm1Gb3YjF7kmsHrgmL41WU
{
 "version": "0.2",
 "language": "lua",
 "functions": [
  {
   "name": "hello"
  },
  {
   "name": "set_name",
   "arguments": [
    {
     "name": "name"
    }
   ]
  },
  {
   "name": "constructor"
  }
 ],
 "state_variables": [
  {
   "name": "Name",
   "type": "value"
  },
  {
   "name": "My_map",
   "type": "map"
  }
 ]
}
Query Initial State

You can query the generated contract in the following way.

./aergocli contract query AmfzX3SHXVTBU9NSEWXaLxxjN11KsUpm1Gb3YjF7kmsHrgmL41WU hello
value:"\"hello world\""

You can see that the name ‘world’ assigned by the constructor is output.

Call Contract

You can change the name recorded in the block chain as follows:

./aergocli contract call AmPbWrQbtQrCaJqLWdMtfk2KiN83m2HFpBbQQSTxqqchVv58o82i AmfzX3SHXVTBU9NSEWXaLxxjN11KsUpm1Gb3YjF7kmsHrgmL41WU
 set_name '["aergo"]'
1 : 8mcuEFNxNCF6h4Q3FJk3mGN356R1AmWgGptAgJHNfaKs TX_OK
Query Changed State

If you look at the results again, it has changed.

./aergocli contract query AmfzX3SHXVTBU9NSEWXaLxxjN11KsUpm1Gb3YjF7kmsHrgmL41WU hello
value:"\"hello aergo\""
Query contract variable with merkle proof

Value

./aergocli contract statequery AmfzX3SHXVTBU9NSEWXaLxxjN11KsUpm1Gb3YjF7kmsHrgmL41WU Name --compressed

Map

./aergocli contract statequery AmfzX3SHXVTBU9NSEWXaLxxjN11KsUpm1Gb3YjF7kmsHrgmL41WU My_map key --compressed

Array

./aergocli contract statequery AmfzX3SHXVTBU9NSEWXaLxxjN11KsUpm1Gb3YjF7kmsHrgmL41WU array_name array_index --compressed

By default, the returned state is the one at the latest block, but you may specify any past block’s state root.

./aergocli contract statequery AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ var_name --root "9NBSjkcNTdE5ciBxfb52RmsVW7vgX5voRsv6KcosiNjE"

Programming Guide

Lua provides useful functions and libraries. So you can easily create smart contracts using these functions.

Please refer to the Lua Reference Manual for detailed syntax, explanation, basic built-in functions and libraries.

Aergo currently uses Lua version 5.1.

Restrictions

Because Lua smart contract is performed by the aergo system, OS related functions including input/output are not provided for stability and security.

These are the base functions that are not available:

print, dofile, loadfile, module, require

You can replace print with system.print.

And you can use import instead of require. import is not a Lua syntax.

Use SHIP to build and deploy smart contracts using multiple files.

These are the default libraries that are not available:

coroutine, io, os, debug, jit

The string, math, bit and table packages are available. All functions from these packages are available, except for the math package. These are the functions available in the math package:

abs, ceil, floor, pow, max, min

There are no restrictions on literals, expressions and statements.

After the version 2.0, the Lua virtual machine can internally check the block execution timeout (about 0.25s ~ 0.75s per block) and return an error upon block execution timeout. The transaction which causes timeout is not included into the block like the pre-2.0 version. Additionally, if it is the only transaction in the block, it will be evicted from the mempool for the stable operation of the block producer.

Libraries

We provide libraries for smart contract as follows:

  • Blockchain API
    • system module
    • contract module
    • state module
    • db module
    • abi module
  • Utils
    • json module
    • crypto module
    • bignum module

You can find detailed descriptions for libraries on this page

Smart Contract
Layout
import "./path/to/library"

state.var {
  Value = state.value(),
  Map = state.map(),
  Array = state.array(10)
}

function constructor(init_value)
  Value:set(init_value)
end

function contract1(name, id)
  Map["name"] = name
  Map["ID"] = id
end

function contract2()
  local sum = 0
  for i, v in state.array_pairs(Array) do
    if v ~= nil then
      sum = sum + v
    end
  end
  return sum
end

abi.register(contract1, contract2) -- , contract3, ...
import

This replaces the require function.

It allows you to divide and develop one smart contract into multiple modules (files).

This is not a Lua feature. You should use SHIP to build and deploy smart contracts using multiple files.

state variable

The state.var function defines global state variables.

Three types of state variables can be defined:

value

This type stores any Lua value.

You can define a state value with the syntax var_name = state.value(). It has get and set methods for reading and writing data.

var_name:set("data")
local data = var_name:get()
map

The type map implements associative arrays.

It can be indexed only with string, but the value of a map element can be of any type.

You can define a state map with the syntax var_name = state.map(). The index operator is used for reading and inserting elements.

You can delete an element of a map by using the delete method, with the syntax var_name:delete(key)

state.var {
  Map_var = state.map()
}

function contract_func()
  Map_var["name"] = "kslee"
  Map_var["age"] = 38
  -- ...
  local age = Map_var["age"]
end

function delete_elem(key)
  Map_var:delete(key)
end

The state map supports multiple dimensions, with syntax var_name = state.map(dimension)

The multi index operator is used for reading and inserting elements.

state.var {
  Map_var = state.map(2)
}

function contract_func()
  Map_var["kslee"]["age"] = 38
  Map_var["kslee"]["birth"] = "1999/09/09"
  -- ...
  local kslee = Map_var["kslee"]
  local age = kslee["age"]
  local birth = kslee["birth"]
  
  kslee["birth"] = "1970/10/9"
end
Restrictions
  • max dimensions: 5
  • it does not support setting intermediate dimension element, like this:
state.var {
  Map_var = state.map(2)
  person_var = state.map()
}

function contract_func()
  person_var["age"] = 38
  person_var["birth"] = "1999/09/09"
  Map_var["kslee"] = person_var   -- NOT SUPPORTED!
end
array

The type array is a fixed-length ordinary array.

It can be indexed only with integer, but the value of an array element can be of any type. The index starts at 1.

You can define a state array with the syntax var_name = state.array(size)

The index operator is used for reading and inserting elements.

state.var {
  Arr_var = state.array(3)
}

function contract()
  Arr_var[1] = 1
  Arr_var[2] = 2
  Arr_var[3] = 3

  local sum1 = 0
  for i, v in Arr_var:ipairs() do
    if v ~= nil then
      sum1 = sum1 + v
    end
  end

  local sum2 = 0
  for i = 1, #Arr_var do
    if Arr_var[i] ~= nil then
      sum2 = sum2 + Arr_var[i]
    end
  end

  if sum1 == sum2 then
  -- ...
  end

end

The state array supports multiple dimensions, with syntax var_name = state.array(1st_ncount, 2nd_ncount,....)

The multi index operator is used for reading and inserting elements.

state.var {
  -- declare 2 dimensional array 
  Arr_var = state.array(2, 3)
}

function contract_func()
  Arr_var[1][1] = 1
  Arr_var[1][2] = 2
  Arr_var[1][3] = 3

  -- ...
  local Arr2 = Arr_var[2]
  Arr2[1] = 4
  Arr2[2] = 5
  Arr2[3] = 6

  local sum1 = 0
  for i, v in Arr_var:ipairs() do
   for j, k in v:ipairs() do
    if k ~= nil then
      sum1 = sum1 + k
    end
   end
  end

  local sum2 = 0
  for i = 1, #Arr_var do
   for j = 1, #Arr_var[i] do
    if Arr_var[i][j] ~= nil then
      sum2 = sum2 + Arr_var[i][j]
    end
   end
  end

  if sum1 == sum2 then
  -- ...
  end
end
Restrictions
  • max dimensions: 5
  • it does not support setting intermediate dimension element, like this:
state.var {
  Marr_var = state.map(2,3)
  Arr_var = state.map(3)
}

function contract_func()
  Arr_var[1] = 1
  Arr_var[2] = 2
  Arr_var[3] = 3

  Marr_var[1] = Arr_var    -- NOT SUPPORTED!
end

Note: The state variables are just syntax sugar that replace system.getItem(), system.setItem() functions.

The fields of state variables that are directly modified cannot update the state db.

See InvalidUpdateAge() and ValidUpdateAge() functions in the example.

state.var{
   Person = state.value()
}

function constructor()
  Person:set({ name = "kslee", age = 38, address = "blahblah..." })
end

function InvalidUpdateAge(age)
  Person:get().age = age
end

function ValidUpdateAge(age)
  local p = Person:get()
  p.age = age
  Person:set(p)
end

function GetPerson()
  return Person:get()
end

abi.register(InvalidUpdateAge, ValidUpdateAge, GetPerson)
Private functions

These functions cannot be accessed from outside the smart contract. You can use them as helper functions.

Exported functions

These are the functions that can be called from contract call and query commands. They should be registered using abi.register().

You can restrict who can call these functions by checking the caller using system.getOrigin() or system.getSender(). Example:

function my_restricted_function()
  assert(system.getOrigin() == system.getCreator(), "permission denied")
  ...
end

abi.register(my_restricted_function)
Special functions
constructor

The constructor function is executed only once during the contract deployment. It does not need to be exported with abi.register().

It can have arguments, that should be passed at the contract deployment.

It can also write to the contract state, generally initializing it. Example:

state.var {
  my_number = state.value()
}

function constructor(value)
  my_number:set(value)
end

...
default

The default function is called when the function name cannot be found or when the transaction has no call information. It does not need to be exported through abi.register(). default is used internally by the VM. You should not use default for any other purpose.

You can define a default function as follows:

...
function default()
  ...
end
...

You can call this default function. This happens when a transaction of type call is sent to the contract without information about which function to call.

./aergocli contract call <sender> <contract>
payable

The payable is a property of a function. Only payable functions can receive Aergos sent from a sender. We can make a payable function using abi.payable(). payable functions are automatically exported. Therefore, you do not need to register them using abi.register(). constructor and default are not payable functions by default. They can be made payable functions by using abi.payable().

On the example below we can call the ReceiveAergo function with aergo, but we cannot call the NotReceiveAergo function with aergo:

...
function ReceiveAergo()
  ...
end

function NotReceiveAergo()
  ...
end

abi.register(NotReceiveAergo)
abi.payable(ReceiveAergo)
./aergocli contract call --amount=10 <sender> <contract> ReceiveAergo     # success
./aergocli contract call --amount=10 <sender> <contract> NotReceiveAergo  # fail
view

The view is a property of a function. Functions can be declared view in which case they promise not to modify the state (send aergo, emit event, set state, etc…). We can make a view function using abi.register_view(). register_view functions are automatically exported. Therefore, you do not need to register them using abi.register().

On the example below we cannot call the sendAergo function:

...
function sendAergo()
  contract.send(addr, "1 aergo")
end

abi.register_view(sendAergo)
./aergocli contract call <sender> <contract> sendAergo  # fail
SQL

Aergo smart contract has db library that supports SQL features.

Note: The db package is currently only available on private networks and publicly on SQL TestNet.

The code below is an example of creating a table and inserting a row using db.exec()

-- creates a customer table
function createTable()
  db.exec([[create table if not exists customer(
        id text,
        passwd text,
        name text,
        birth text,
        mobile text
    )]])
end

-- insert a row to the customer table
function insert(id, passwd, name, birth, mobile)
  db.exec("insert into customer values (?, ?, ?, ?, ?)",
          id, passwd, name, birth, mobile)
end

The db.query() function returns a result set. You can fetch rows from the result set.

function query(name)
  local rt = {}
  local rs = db.query("select * from customer where name like '%' || ? || '%'", name)
  while rs:next() do 
    local col1, col2, col3, col4, col5 = rs:get()
    local item = {
        id = col1,
        passwd = col2,
        name = col3,
        birth = col4,
        mobile = col5
    }
    table.insert(rt, item)
  end
  return rt
end

You can also use prepared statements:

function insert(id , passwd, name, birth, mobile)
  stmt = db.prepare("insert into customer values (?, ?, ?, ?, ?)")
  stmt:exec(id, passwd, name, birth, mobile)
end

function insert_contacts(contacts)
  local stmt = db.prepare("insert into contacts (name, email) values (?, ?)")
  for _,contact in ipairs(contacts) do
    stmt:exec(contact.name, contact.email)
  end
end

function query_names(ids)
  local names = {}
  local stmt = db.prepare("select name from customer where id=?")
  for _,id in ipairs(ids) do
    local rs = stmt:query(id)
    if rs:next() then
      table.insert(names, rs:get())
    else
      table.insert(names, "")
    end
  end
  return names
end

.. warning:: DO NOT concatenate values when building SQL commands!

This would make your smart contract vulnerable to `SQL injection` attacks.

These are bad examples that should *NOT* be used:
  -- WRONG! AVOID THIS:
  db.exec("insert into customer values ('" .. id .. "', '"
      .. passwd .. "', '"
      .. name .. "', '"
      .. birth .. "', '"
      .. mobile .. "')")

  -- WRONG! AVOID THIS:
  local rs = db.query("select * from customer where id like '%'" .. id .. "'%'")
Restrictions

LiteTree is used as the SQL processing engine for Aergo smart contracts. LiteTree is implemented based on SQLite.

Detailed SQL usage can be found at https://sqlite.org/lang.html and https://sqlite.org/lang_corefunc.html

However, we do not provide full SQL functionality. There are some limitations due to stability and security.

Data types

Allow only SQL datatypes corresponding to Lua strings and numbers(int, float).

  • text
  • integer
  • real
  • null
  • date, datetime

SQL statements

You can execute the following SQL statements. However, DDL and DML can not be run on smart contract queries.

  • DDL
    • TABLE: ALTER, CREATE, DROP
    • INDEX: CREATE, DROP
  • DML
    • INSERT, UPDATE, DELETE, REPLACE
  • Query
    • SELECT

functions

Here is a list of functions that are not available:

  • load_XXX functions
  • random function
  • sqlite_XXX functions
  • data and time related functions can be used, except now timestring and localtime modifier.

A list of other functions and descriptions is available via the links below.

  • basic : https://www.sqlite.org/lang_corefunc.html
  • data and time : https://www.sqlite.org/lang_datefunc.html
  • aggregation : https://www.sqlite.org/lang_aggfunc.html

contraints

You can use the following contraints:

  • NOT NULL
  • DEFAULT
  • UNIQUE
  • PRIMARY KEY (FOREIGN KEY)
  • CHECK
Tools
aergoluac

aergoluac is a compiler for Lua smart contracts.

aergocli

aergocli is a command line tool that interfaces with the GRPC exposed by aergosvr.

It provides smart contract-related commands as follows:

  • contract deploy/call/query/abi/statequery
  • receipt get
  • event list/stream
  • Reference
brick

Toy for Contract Developers. You can use it to test smart contracts.

Style conventions

It is good to adopt a consistent coding style for readability. We recommend the Lua style guide.

Reference

Overview

Aergo uses Lua, a lightweight scripting language, as its smart contract language. The following is an example of a simple coinstack smart contract written in Lua that stores key-value pairs in a blockchain state store and reads the values:

-- Storing key-values in the state store
function set(key, value)
    system.setItem(key, value)
end

-- Returns the value corresponding to the key in the state store
function get(key)
    return system.getItem(key)
end

-- Output the hash value of the previous block. The output is printed on the server log.
function printPrevBlock()
    system.print(system.getPrevBlockHash())
end

-- Functions to be called for contract declare as abi
abi.register(set, get, printBlock)

To read and write data on a blockchain within a contract, you must use the system package. In the above example, the smart contract provides the function to access the key-value repository through the setItem and getItem functions of the system package and store the data permanently. In addition, a simple debug message can be output to the node’s log file via the print function.

system package

This packages provides blockchain information and store/get state

getBlockheight()

This function returns the block number that contains the current contract transaction.

getPrevBlockHash()

This function returns the hash of the previous block

getTimestamp()

This function returns the creation start time of the block that contains current contract transaction.

getTxhash()

This function returns the id of the current contract transaction.

getAmount()

This function returns number of AER sent with contract call. Return type is string.

isFeeDelegation()

This function returns whether the transaction is using fee delegation.

getContractID()

This function returns the current contract address.

getCreator()

This function returns creator address of current contract

getOrigin()

This function returns sender address of current transaction

getSender()

This function returns caller address of current contract

isContract(address)

This function return if address is contract then true else false. when address is invalid then raise error

setItem(key, value)

This function sets the value corresponding to key to the storage belonging to current contract

Restrictions:

  • key type can only be string (number type is implicitly converted to string)
  • value type can be: number, string, table
getItem(key)

This function returns the value corresponding to key in storage belonging to current contract

  • If there is no value corresponding to key, it returns nil.
contract package

This packages provides contract operation

send(address, amount)

This function transfers an amount of coins from this contract to the given address. The Amount can be in string, number or bignum formats. The default unit is AER.

contract.send("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", 1)
contract.send("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "1 aergo 10 gaer")
contract.send("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", bignum.number("999999999999999"))
deploy(code, args…)

The deploy function creates a contract account using code and args, and returns the corresponding address and the returned value(s) from the constructor function.

If you use an existing address instead of code, it will be deployed with the code from the given address.

In addition, you can call the value function to send coins to the new contract. The value function accepts a string, a number or a bignum as the argument like the send function.

    src = [[
        function hello(say)
            return "Hello " .. say .. system.getItem("made")
        end
        function constructor(check)
            system.setItem("made", check)
        end
        abi.register(hello)
        abi.payable(constructor)
    ]]
    addr = contract.deploy.value("1 aergo")(src, system.getContractID())
call(address, function_name, args…)

The call function executes a function on another contract and returns the result. The call is executed in the context of the target contract’s state.

In addition, we can call the value function to send an amount of coins to the target address. The value function accepts a string, a number or a bignum as the argument like the send function.

contract.call("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "inc", 1)
contract.call.value(10)("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "inc", 2)
Restrictions

Before the fee system for smart contracts is applied to the mainnet, there are the following restrictions:

  • limit the call depth of call or delegatecall in one transaction to 5
delegatecall(address, function_name, args…)

The delegatecall function loads the code from the target address and executes it in the context of the calling contract.

Storage, current address and balance still refer to the calling contract, only the code is taken from the called address.

Notice that the call can change the state of the current contract.

This is used to implement the “library” feature, with reusable library code.

contract.delegatecall("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "inc", 1)
Restrictions

Before the fee system for smart contracts is applied to the mainnet, there are the following restrictions:

  • limit the call depth of call or delegatecall in one transaction to 5
pcall(fn, args…)

It is an error handling function that works just like pcall in Lua. For both functions, when the called function fails, no error is raised and the contract execution continues. It just returns false to indicate the failure.

The difference is that with contract.pcall when an error occurs the state and balance modified by the called function rolls back to the state before the call. This does not happen when using just pcall.

If no pcall or contract.pcall is used, then the contract execution stops on exceptions.

success = contract.pcall(contract.send, "Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod", "1 AERGO")
if success == false then
    return 0
end
return 1
balance(address)

This function returns the balance of the given address in AER. The return type is string. If address is nil then it returns the balance of the current address.

contract.balance()  -- get balance of current contract address 
contract.balance("Amh4S9pZgoJpxdCoMGg6SXEpAstTaTQNfQdZFsE26NpkqPwmaWod")
event(eventName, args…)

This function fires an event containing eventName and args. The event is recorded in the blockchain in the contract result receipt. It is also broadcasted to all the listening applications connected to the blockchain network that have subscribed to this event.

The user can search for events that happened in the past and also receive notifications of new events by using RPC.

contract.event("send", 1, "toaddress") 
Restrictions

Before the fee system for smart contracts is applied to the mainnet, there are the following restrictions:

  • limit length of event name to 64
  • limit size of event argument to 4k
  • limit the number of events in one transaction to 50
Examples: searching for event and receiving notification with aergocli

Searching for events with name “send” from the contract address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ

./aergocli event list --address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ --event send

Searching for events where argument 0 is 1 and argument 1 is “toaddress” in the contract address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ

./aergocli event list --address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ --argfilter '{"0":1, "1":"toaddress"}'

Getting notifications for events with name “send” for contract AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ

./aergocli event stream --address AmhbdCEg4TUFm6Hpdoz8d81eSdzRncsekBLN3mYgLCbAVdPnu1MZ --event send
stake(amount), unstake(amount), vote([bps,…]), voteDao(arg1, arg2,…)

You can do governance (stake, unstake, vote, voteDao) in the smart contract, using the contract address

contract.stake("1 aergo") 
contract.vote(["<bp1 address>", "<bp2 address>"....)
contract.voteDao("gasprice", "5")
contract.unstake("1 aergo")
Built-in Functions

Lua provides the language itself as a useful function and basic package. It provides useful functions such as string management functions, so you can easily create smart contracts using these functions. Please refer to the Lua Reference Manual for detailed syntax, explanation, basic built-in functions and packages.

Since Lua Smart Contract is performed in Blockchain, OS related functions including input / output are not provided for stability and security.

Here is a list of the main functions that are not available.

print, loadstring, dofile, loadfile and module

Here is a list of the default packages that are not available.

coroutine, io, os and debug

The string, math, and table packages are available. However, you can not use the random, randomseed functions in the math package.

DB package

If the smart contract is handling simple types of data, it would not be difficult to implement using only the basic APIs (getItem(), setItem()). However, complex data structures, data association, scope queries, filtering, sorting, and other features require the complexity and size of the data logic so developers can not focus on critical business logic. To solve this problem, Aergo supports SQL. This section details the types and usage of SQL APIs available in smart contracts

Note: The db package is currently only available on private networks and publicly on SQL TestNet.
exec(sql)

This function performs DDL or DML statements

query(sql)

This function performs SELECT statements and returns a result set object

prepare(sql)

This function creates a prepared statement and returns a statement object

-- create customer table 
function createTable()
  db.exec([[create table if not exists customer(
        id text,
        passwd text,
        name text,
        birth text,
        mobile text
    )]])
end

function insert(id, passwd, name, birth, mobile)
  db.exec("insert into customer values (?, ?, ?, ?, ?)",
      id, passwd, name, birth, mobile)
end
Functions of result set object

Object functions must be called with the : operator

get

This function returns a result row

function query(id)
  local rt = {}
  local rs = db.query("select * from customer where id like '%' || ? || '%'", id)
  while rs:next() do 
    local col1, col2, col3, col4, col5 = rs:get()
    local item = {
        id = col1,
        passwd = col2,
        name = col3,
        birth = col4,
        mobile = col5
    }
    table.insert(rt, item)
  end
  return rt
end
Functions of prepared statement object

You can use the parameters in the SELECT or DML statement to view, add, modify, or delete information.

query(bind1, bind2, ….)

This function executes a SELECT statement using the specified argument values corresponding to bind parameters and returns a result set object

exec(bind1, bind2, ….)

This function executes a DML statement using the specified argument values corresponding to bind parameters

SQL Restrictions

Smart Contract’s SQL processing engine is built on SQLite. Therefore, detailed SQL usage grammar can be found at https://sqlite.org/lang.html and https://sqlite.org/lang_corefunc.html. However, because of the stability and security of the Aergo, not all SQL is allowed.

types

Allow only SQL datatypes corresponding to Lua strings and numbers (int, float).

  • text
  • integer
  • real
  • null
  • date, datetime
SQL statement

The allowed SQL statements are listed below. However, DDL and DML are only allowed in smart contract transactions.

  • DDL
    • TABLE: ALTER, CREATE, DROP
    • VIEW: CREATE, DROP
    • INDEX: CREATE, DROP
  • DML
    • INSERT, UPDATE, DELETE, REPLACE
  • Query
    • SELECT
Function

The following is an unavailable list

  • Data and time related functions can be used except ‘now’ timestring and ‘localtime’ modifier
  • load_XXX function
  • random function
  • sqlite_XXX function

For a list of other functions and descriptions, please refer to the links below.

Constraint

The following constraints can be used.

  • NOT NULL
  • DEFAULT
  • UNIQUE
  • PRIMARY KEY(FOREIGN KEY)
  • CHECK
function init_database()
    db.exec("drop table if exists customer")
    db.exec("create table customer (cid integer PRIMARY KEY ASC AUTOINCREMENT , passwd text , cname text, birthdate date, rgdate date)")
    db.exec("insert into customer (cid, passwd, cname, birthdate, rgdate) values (100 ,'passwd1','홍길동', date('1988-01-03'),date('2018-05-30'))")
    db.exec("insert into customer (passwd, cname, birthdate, rgdate) values ('passwd2','김철수', date('1978-11-03'),date('2018-05-30'))")
    db.exec("insert into customer (passwd, cname, birthdate, rgdate) values ('passwd3','이영미', date('1938-04-23'),date('2018-05-30'))")

    db.exec("drop table if exists product")
    db.exec("create table product (pid integer PRIMARY KEY ASC AUTOINCREMENT, pname text, price real, rgdate date)")
    db.exec("insert into product (pid, pname, price, rgdate) values (1000 ,'사과',1000, date('2018-05-30'))")
    db.exec("insert into product (pname, price, rgdate) values ('수박',10000, date('2018-05-30'))")
    db.exec("insert into product (pname, price, rgdate) values ('포도',3500, date('2018-05-30'))")

    db.exec("drop table if exists order_hist")
    db.exec("create table order_hist (oid integer PRIMARY KEY ASC AUTOINCREMENT, cid integer, pid integer, p_cnt integer, total_price real, rgtime datetime, FOREIGN KEY(cid) REFERENCES customer(cid), FOREIGN KEY(pid) REFERENCES product(pid) )")
    db.exec("insert into order_hist(oid, cid, pid,  p_cnt, total_price,rgtime) values(10000,100,1000,3,3000, datetime('2018-05-30 16:00:00'))")
    db.exec("insert into order_hist(cid, pid, p_cnt, total_price, rgtime) values(100,1000,3,3000, datetime('2018-05-30 17:00:00'))")
    db.exec("insert into order_hist(cid, pid, p_cnt, total_price, rgtime) values(100,1000,3,3000, datetime('2018-05-30 18:00:00'))")
end
json package

Json package is provided for user convenience in input and output. This package allows automatic conversion between JSON format strings and Lua Table structures.

encode(arg)

This function returns a JSON-formatted string with the given lua value.

decode(string)

This function converts a string in JSON format to the corresponding Lua structure and returns it

crypto package
sha256(arg)

This function computes the SHA-256 hash of the argument.

ecverify(message, signature, address)

This function verifies the address associated with the public key from elliptic curve signature.

function validate_sig(data, signature, address)
    msg = crypto.sha256(data)
    return crypto.ecverify(msg, signature, address)
end
bignum package

Since the lua number type has a limit on the range that can be represented by an integer (less than 2 ^ 53), the bignum module is used to provide an exact operation for larger numbers.

  • Notice
    • == Operations on bignum and other types always return false.
    • Bignum does not allow a decimal point.
    • Bignum value range : -(2^256 - 1) ~ (2^256 -1)
number(x)

This function make bignum object with argument x(string or number)

isneg(x)

Check bignum x if negative than return true else false

iszero(x)

Check bignum x if zero than return true else false

tonumber(x)

Convert bignum x to lua number

tostring(x) (bignum.tostring(x) same as tostring(x))

Convert bignum x to lua string

neg(x) (same as -x)

Negate bignum x and return as bignum

sqrt(x)

Returns the square root of a positive number as bignum. not permitted negative value to x

compare(x, y)

Compare two big numbers. Return value is 0 if equal, -1 if x is less than y and +1 if x is greater than y.

add(x, y) (same as x + y)

Add two big numbers and return bignum

sub(x, y) (same as x - y)

Subtract two big numbers and return bignum

mul(x, y) (same as x * y)

Multiply two big numbers and return bignum

mod(x, y) (same as x % y)

Returns the bignum remainder after bignum x is divided by bignum y

div(x, y) (same as x / y)

Divide two big numbers and return bignum

pow(x, y) (same as x ^ y)

Power of two big numbers and return bignum. not permitted negative value to y

divmod(x, y)

Returns a pair of big numbers consisting of their quotient and remainder

powmod(x, y, m)

Return the bignum remainder after pow(bignum x,bignum y) is divided by bignum m. not permitted negative value to y

isbignum(x)

Return true if x is bignum else false

tobyte(x)

Return byte string of bignum x. not permitted negative value to x

frombyte(x)

Return bignum from byte string x

function factorial(n,f)
 for i=2,n do f=f*i end
 return f
end
for i=1,30 do
  local b=factorial(i,bignum.number(1))
  return bignum.mod(b, 10)
end

Fee delegation

Generally the user who sends a transaction needs to pay the transaction execution fee. The fee delegation is a feature that allows the contract to pay the execution, so the user does not need to pay for it.

In order to use this feature, the contract needs to support fee delegation and the transaction must use the FeeDelegation type. The contract also needs to have sufficient balance to pay the execution.

Registering the functions

The functions to be executed using fee delegation must be specified with abi.fee_delegation()

Example:

abi.fee_delegation(function1, function2)
check_delegation function

The check_delegation is a special function used to check whether the transaction can be processed using fee delegation. In another words: if the contract will pay for this specific transaction execution or not.

It is called by the engine when a transaction with fee delegation type arrives (a transaction asking to be paid by the contract). The arguments to this function are:

  • the name of the function that the transaction wants to call/execute
  • the arguments to that function

You can use any logic to determine if the transaction will be paid by the contract. It is common to check who is sending the transaction with system.getOrigin() or system.getSender().

This function should return a boolean (true=accept or false=reject) and it is read-only (it should not write to the state).

Differing from normal calls

Notice that the function marked to be called using fee delegation can also be called with transactions that pay themselves for the execution.

You can check whether the transaction is using fee delegation with the system.isFeeDelegation() function.

Transfering coins to the smart contract

Currently the existing method is by using a transaction with type CALL and setting the desired amount.

The smart contract must have a function registered with abi.payable(), generally the default function. In this case no payload is required, otherwise the transaction must specify the name of the function that will receive the transfer.

Contract example

Here is a very basic example of a contract that implements fee delegation:

state.var{
    whitelist = state.map(),
    item = state.value()
}

function reg(user)
    if (k == nil) then
        whitelist[system.getSender()] = true
    else
        whitelist[user] = true
    end
end

function work(arg0)
    if (system.isFeeDelegation() == true) then
        whitelist[system.getSender()] = false
    end
    item:set(arg0)
end

function check_delegation(fname, arg0)
    if (fname == "work") then
        return whitelist[system.getSender()]
    end
    return false
end

function default()
end

abi.register(reg, work)
abi.payable(default)
abi.fee_delegation(work)
aergocli example
aergocli contract call --delegation AmPbWrQbtQrCaJqLWdMtfk2KiN83m2HFpBbQQSTxqqchVv58o82i Amh6aHxfoMrCmXd2GrV3Yem1zmcqgAiPaJAhsux3wedpEVUCGowx work
1 : HtyNZdJzJNYkeCQCrz8xozPvGLn1xhde9ExNEEiEKPy4 TX_OK
Receipt
aergocli receipt get HtyNZdJzJNYkeCQCrz8xozPvGLn1xhde9ExNEEiEKPy4
{
 "BlokNo": 9673,
 "BlockHash": "Az8pJvDso44nP9Lq5Wj7QoU6n9yZcFkCcmJaiRKyq9Qz",
 "contractAddress": "Amh6aHxfoMrCmXd2GrV3Yem1zmcqgAiPaJAhsux3wedpEVUCGowx",
 "status": "SUCCESS",
 "ret": "",
 "txHash": "HtyNZdJzJNYkeCQCrz8xozPvGLn1xhde9ExNEEiEKPy4",
 "txIndex": 0,
 "from": "AmPbWrQbtQrCaJqLWdMtfk2KiN83m2HFpBbQQSTxqqchVv58o82i",
 "to": "Amh6aHxfoMrCmXd2GrV3Yem1zmcqgAiPaJAhsux3wedpEVUCGowx",
 "usedFee": 2000000000000000,
 "feeDelegation": true,
 "events": []
}

Aergo SCL

Note

This article describes a feature currently under development.

Aergo Smart Contract Language (Aergo SCL, we pronounce it “askel”) is a language dedicated to smart contact for Aergo, designed to make it easier for developers to develop smart contracts. Although the specification is less than traditional languages, it aims to be faster and more powerful. Aergo SCL has the following characteristics.

  • Strongly typed language
  • Object-oriented language (except for inheritance and abstraction)
  • Support SQL extension for database access
  • Support extension for blockchain access
  • Compiled with WebAssembly

Aergo SCL is currently not supported on the testnet but under active development.

Java Database Connectivity (JDBC)

The Aergo smart contract engine has rich SQL support and is compatible with JDBC adapters. With this you can connect to a smart contract just like any other traditional SQL database.

The JDBC adapter removes the need for creating and sending blockchain transactions manually. Instead, you can run SQL queries directly.

How to

  1. Download the required JDBC adapter and Lua smart contract from github.com/aergoio/aergojdbc
  2. Compile and deploy the smart contract on a Aergo network that supports SQL operations (private networks or the public Alpha network at alpha-api.aergo.io). The deployed smart contract acts as your database instance on the blockchain.
  3. Use a JDBC compatible software, such as the database connection in Eclipse or SQuirrelSQL, and connect using the provided adapter, an encrypted private key, and password.

Connection

  • Driver: aergo-jdbc-version.jar, org.aergojdbc.JDBC
  • URL: jdbc:aergo:<aergo node ip>:<port>@<jdbc contract address>
  • User: encrypted private key
  • Password: passphrase used to encrypt private key
  • changed at jdbc v1.1.0
    support aergo keystore with keystore property and when using keystore, use address for user property

Limitations

  • Maximum response size: 4mb. If the resulting amount of data exceeds this size, adjust the fetch size using Statement.setFetchSize (default: 10 rows).
  • Sqlite: Since the SQL engine in Aergo is Sqlite, only operations supported by Sqlite are possible.
  • Unsupported features:
    • Transaction commit, rollback: transactions always auto commit
    • Scrollable cursor
    • CallableStatement

Technical Specifications

These articles describe technical specifications in detail.

Addresses

Client-side

Account and contract addresses are Base58-check encoded strings that look like this:

AmQA7dHJFiA4mXXxTV5sAniLpxMrankdW4Cow3ykj1UM5G14QKL5

The prefix used for encoding Aergo addresses in base58-check is 0x42.

Internal

Internally (in the server and over the wire, i.e. in GRPC requests) addresses are byte arrays with a length of 33. They represent the compressed public key of an account.

In the case of smart contracts, the address is generated from a hash of the creator’s account and the creating transaction’s nonce, prefixed with the byte 0x0C to arrive at a compatible length of 33 bytes. As a developer, you don’t have to worry about this: smart contract addresses can be used just the same as account addresses.

Token Units

1 aergo = 1 * 10^18 aer = 1 * 10^9 gaer

The Aergo CLI and client libraries have support for these units, i.e. you can specify transaction amounts as 1 aergo instead of 1000000000000000000.

Note that amounts in the base unit aer exceed the range of 64-bit integers. You need some implemention of Big Integer to deal with these numbers. Aergo SDKs come bundled with a recommended way to do that. In most cases, you can just use strings instead of numbers. For example, when creating a JSON transaction, set

{
    "amount": "1000000000000000000"
}

Name System

Aergo contains a name service to translate addresses into easy to remember short names.

One name maps to exactly one address. Names are currently fixed to a length of 12 characters.

Names include an owner and a destination. The owner is used to determine who can change the name, the destination is the actual adress the name should resolve to. In case of normal accounts, these two values will be identical, but they differ in case of smart contracts.

Creating and updating names

The easiest way to create and update names is the aergocli.

Registering and updating names requires spending a cetrain amount of aergo determined by the DAO vote “nameprice”.

To register a new name:

aergocli name new --from my_unlocked_account_address --name my12charname

To change the destination of the name:

aergocli name update --from my_unlocked_account_address --to account_address_or_contract_address --name my12charname

Warning

The update command affects both the destination and the owner of a name. If the new destination is a regular account, both owner and destination are set to that address. If the new destination is a smart contract address, the owner is set to the address of that contract’s creator.

Lookups

To retrieve the owner and destination of a registered name:

aergocli name owner --name my12charname

The destination field contains the resolved address.

This method is also available over RPC and in the various SDKs.

Reverse lookups

Aergo server currently supports no way to do reverse lookups, i.e. finding all names associated with an address. On public networks, you can use block explorers like Aergoscan to find that information.

Technical details

Names are stored in the state of the special account aergo.name. They are created and updated using special governance transactions. Refer to transaction types for the technical specification of these actions. Governance transactions don’t require any fee, but the name system requires an amount of aergo to be included in the transaction. This amount is determined by the DAO vote “nameprice”. You can check the current name price in the ChainInfo.

Transactions that create or update names are effective after they are included in a block. That means that you can only refer to new or updated names in the following block.

Consensus Algorithm

The objective of all blockchain protocols is to replicate a blockchain and its associated state across participating nodes. To achieve such an agreement, each blockchain protocol deploys a consensus algorithm.

The public Aergo network uses Delegated Proof of Stake (DPoS) for blockchain consensus.

BP Election

In DPoS, blocks are generated only by a limited number of nodes called Block Producers (BPs). BPs are elected via voting, where the voting power is weighted by staked tokens.

BPs are re-elected round by round (blocks per round = 100). In each round, time is split into slots and each slot is assigned to one of the elected BPs. Only the permitted BP can produce a block in a time slot.

Staking & Voting

Staking means locking up one’s tokens for a minimum period of time. Any user wanting to vote must stake their tokens since the voting power is weighted by the number of staked tokens, as remarked above.

All of these requests are performed via a transaction. Therefore, all proccesses are transparently recorded in the blockchain and can be verified by anyone.

After the voting transaction is included in the block, the results are caculated immediately. But there is a slight delay until it is applied. The current BPs are elected based on the voting result gathered at the block number: (<current block number> / 100 - 1) * <the total number of BPs>.

In other words, the voting results gathered in the past (approximately 1 round before) are used for stability (recent blocks may be roll-backed via a reorganization).

Votes are locked for a certain period of time to prevent users from spamming votes. On the public Aergo network, this is currently approx. 1 day (after 60 * 60 * 24 block number). That means, after casting a vote, a user can only change their vote after 1 day has passed.

Last Irreversible Block (LIB)

In some blockchain protocols, a blockchain may branch into two or more, which is called a fork. Later, only one of them is chosen as the main branch via a set of rules defined by the protocol. Such reorganizations limit each block’s finality and, in turn, transaction’s finality. For example, a transaction, included in a block at one time, might be rejected later.

DPoS also allows blockchain forks. However, a block becomes a last irreversible block (LIB) when it is (double time) confirmed by a majority (2/3+) of BPs. Once a block is determined as a LIB, it cannot be rolled back, i.e. it achieves finality.

Transactions

Tx

Field Type Description
Hash bytes Hash of body
Body Nonce uint64 Increasing number used only once per sender account
Account bytes Decoded sender account address
Recipient bytes Decoded receiver account address
Amount bytes Amount of transfer
Payload bytes Smart contract data
Limit uint64 Maximum limit of gas to be used (0 = no limit)
Price bytes Reserved
Type int See below
ChainIDHash bytes Hash of chain ID
Sign bytes ECDSA signature with secp256k1

Payload

A payload can be any kind of binary data, but is most often used with JSON strings for smart contract calls.

Transaction types

There are several kinds of transactions.

Normal type (0)

Normal transactions are used to transfer tokens and calling smart contracts. Since version 2.0, there are more precise transaction types that should be used instead.

Governance type (1)

Governance transactions are used for calling system contracts, such as staking and voting. Transactions of this type have a special payload format and recipient. See below for details.

Transfer type (4)

Transactions that only transfer value. For backwards compatability, the Normal type can also be used.

Call type (5)

Smart contract calls should be denoted using the Call transaction type. For backwards compatability, the Normal type can also be used.

FeeDelegation type (3)

FeeDelegation transactions are used for calling smart contract while charging fees to the contract. This only works if the contract supports the fee delegation interface.

Deploy type (6)

Used to deploy contracts. For backwards compatability, the Normal type can also be used.

Redeploy type (2)

Used to re-deploy a contract.

Governance transaction details

The following table shows the specification for each field of the transaction body of a governance transaction.

Action Recipient Amount Payload
staking aergo.system amount to stake {"Name":"v1stake"}
unstaking aergo.system amount to unstake {"Name":"v1unstake"}
voting aergo.system 0 {"Name":"v1voteBP","Args":[<peer IDs>]}
voting DAO aergo.system 0 {"Name":"v1voteDAO","Args":[<DAO ID>,<candidate>]}
create name aergo.name 20 aergo * {"Name":"v1createName","Args":[<a name string>]}
update name aergo.name 20 aergo * {"Name":"v1updateName","Args":[<a name string>, <new owner address>]}
add admin aergo.enterprise 0 aergo {"Name":"appendAdmin","Args":[<new admin address>]}
remove admin aergo.enterprise 0 aergo {"Name":"removeAdmin","Args":[<admin address>]}
change raft cluster aergo.enterprise 0 aergo {"Name":"changeCluster","Args":[{"command":"add","name":"[node name]","address":"[peer address]","peerid":"[peer id]"}]}
add config aergo.enterprise 0 aergo {"Name":"appendConf","Args":[<config key>,<config value>]}
enable config aergo.enterprise 0 aergo {"Name":"enableConf","Args":[<config key>,<true|false>]}
remove config aergo.enterprise 0 aergo {"Name":"removeConf","Args":[<config key>,<config value>]}

* The amount needed for name transactions is determiend by the NAMEPRICE vote. It is currently 20 aergo on Mainnet. You can retrieve this information by accessing the network’s ChainInfo.

The aergo.system transactions, including staking, unstaking and voting, can be sent once per 24 hours per account. The only exception is when you first vote. For staking and unstaking, there is a limit to the amount of requests. It must be over 10000 aergo based on the amount of staked. Therefore, the first staking request should exceed 10000 aergo, and in the case of the unstaking request, more than 10000 must be left or withdrawn altogether.

When voting for DAO, there are IDs by paramter. It will be changed to the new value when the first place gets 2/3 of the staking total.

DAO ID Value Description
BPCOUNT 1 to 100 The number of block producer
STAKINGMIN 1 to 500000000000000000000000000 The amount of staking minimum
GASPRICE 1 to 500000000000000000000000000 The price of gas
NAMEPRICE 1 to 500000000000000000000000000 The price of name transactions

The aergo.enterprise transactions are only for the private blockchain network. if you want to enable aergo.enterprise, you should make the genesis block with "public":"false"

Transaction receipts

See also

See API → Receipt for a detailed explanation of all the receipt data.

Every transaction generates a receipt upon succesful execution which contains the result and metadata such as fee and gas used. The status can be one of three values:

SUCCESS
Simple value transfer transactions and succesful contract executions. For contract calls, the result is available in ret.
ERROR
Failed contract execution. The error message can be found in ret.
CREATED
Succesful contract deployment transaction. The created address can be found in contractAddress.

Smart Contracts

Contracts are special kinds of accounts that have code, an ABI, and state.

This is a technical description of the protocol and execution. It is recommended to interact with contracts using higher level API supported by aergocli and the various SDKs.

Deployment

Contracts are deployed by sending a transaction with the contract code as payload to the null receiver (empty receiver address). Upon execution, the contract state is created at an address calculated from the sender and the nonce of the deployment transaction. You can find out the created contract address in the transaction receipt.

See here for details about address generation.

Calling Contracts

To call contract methods, send a transaction to the contract’s address specifying the function name and arguments as JSON payload.

Events

Contracts can log events during execution. This is the preferred way to notify the outside world of important state changes.

Logged events can be found as part of the transaction receipt and also easily searched for using the GetEvents API.

Transaction Fees

The Aergo protocol includes transaction fees that need to be paid according to the configuration of the network.

Versions prior to 2.0 (see 1.3 docs) included a simplified transaction fee scheme, taking into account payload and state database usage size.

This is not fair because it does not consider the actual execution time and resource usage of the transaction. In version 2.0, a gas system was introduced to correct this problem.

Gas is a numerical representation of execution and storage costs in the transaction and contract. But gas is not a fee. To calculate the fee, we need the gas price.

Gas Price

Gas price is the AER value corresponding to 1 gas. All transactions in a block have the same gas price. Gas price is determined by a DAO vote.

Gas Limit

The gas limit allows the user to specify the maximum amount of gas used for transactions and contracts. For backward compatibility with previous versions that did not specify a gas limit, a gas limit of 0 automatically calculates the maximum amount of gas available from an account’s balance.

Gas Table

Transaction

Transactions other than governance transactions consume 100,000 gas by default. If you have a payload, additional gas will be used depending on the size.

Payload gas: (Bytes of a payload - 200) * 5

Instructions

The table below shows gas usage for Lua bytecode.

Name GAS
ISLT, ISGE, ISLE, ISGT, ISEQV, ISNEV, ISEQS, ISNES, ISEQN, ISNEN, ISEQP, ISNEP 2
ISTC, ISFC, IST, ISF, ISTYPE, ISNUM 1
MOV 2
NOT, UNM 1
LEN 3
ADDVN, SUBVN 2
MULVN, DIVVN, MODVN 3
ADDNV, SUBVN 2
MULNV, DIVNV, MODNV 3
ADDVV, SUBVV 2
MULVV, DIVVV, MODVV 3
POW 3
CAT 3
KSTR, KCDATGA, KSHORT, KNUM, KPRI, KNIL 1
UGET, USETV, USETS, USETN, USETP, UCLO, FNEW 2
TNEW 2
TDUP 5
GGET, GSET 3
TGETV, TGETS, TGETB, TGETR, TSETV, TSETS, TSETB, TSETM, TSETR 2
CALLM, CALL, CALLMT, CALLT, ITERC, ITERN 10
VARG 5
ISNEXT 2
RETM 5
RET, RET0, RET1 3
FORI, FORL, ITERL, LOOP, JMP 2
Built-in Functions

Gas usage for built-in functions provided by the Lua VM

Name GAS
assert 3
error 5
getfenv 5
getmetable 3
ipairs 3
next 3
pairs 3
pcall 10
rawequal 5
rawget 3
rawset 5
select 3 (length), 5 (get an element)
setfenv 5
setmetatable 3
tonumber 5
tostring 5
type 2
unpack 5 + (2 * number of arguments)
xpcall 10
string.byte, string.char 3 + (1 * number of string units (arguments))
string.dump 10
string.find, string.gmatch, string.gsub, string.match 3 + (5 * number of matching strings)
string.format 3 + (2 * number of format modifiers)
string.lower 3 + (1 * number of string units (argument))
string.rep 3 + (2 * number of concatenation)
string.reverse 3 + (1 * number of string units (argument))
string.sub 3 + (1 * number of string units (argument))
string.upper 3 + (1 * number of string units (argument))
table.concat 3 + (3 * number of arguments)
table.insert 3 + (3 * number of cells to move)
table.maxn, table.minn 3 + (3 * comparison count)
table.sort 3 + (5 * comparison count)
math.abs, math.ceil, math.floor, math.pow 3
math.max, math.min 3 + (1 * number of arguments)
bit.tobit 3
bit.tohex 5
bit.bnot 2
bit.bor, bit.band, bit.xor 3 + (2 * number of arguments)
bit.lshift, bit.rshift, bit.ashift, bit.rol, bit.ror 3
bit.bswap 2
Aergo-extension Functions

Gas usage for aergo-extension functions

Name GAS
system.getSender 1000
system.getBlockheight 300
system.getTxhash 500
system.getTimestamp 300
system.getContractID 1000
system.setItem 100 + (5 * bytes of data)
system.getItem 100
system.getAmount 300
system.getCreator 500
system.getOrigin 1000
system.getPrevBlockHash 500
contract.balance, contract.send, contract.pcall 300
contract.event 500
contract.deploy 5000
contract.call 2000
contract.delegatecall 2000
contract.stake, contract.unstack. contract.vote, contract.voteDao 500
json.encode, json.decode 50 + (50 * number of objects)
crypto.sha256 500
crypto.ecverify 5000
bignum.number 50
bignum.isneg, bignum.iszero 10
bignum.tonumber, bignum.tostring 50
bignum.neg 100
bignum.sqrt 300
bignum.compare 50
bignum.add, bignum.sub 100
bignum.mul, bignum.div, bignum.mod 300
bignum.pow, bignum.divmod, bignum.powmd 500

Gas estimation using Brick

Although there is a gas usage tables, it is not easy to calculate the exact gas limit. The simplest way to estimate is to use the Brick. The Brick allows you to deploy and perform contracts without running an actual blockchain. Using Brick, you can check contract call results, gas usage, etc. It also has functions for debugging the contract.

Blocks

Block header

Field Type Description
ChainID []byte identifier of chain
PrevBlockHash []byte hash of previous block
BlockNo uint64 height of block
Timestamp int64 block timestamp
BlocksRootHash []byte root hash of State SMT(sparse merkle tree)
TxsRootHash []byte Merkle root of transactions
ReceiptsRootHash []byte Merkle root of transaction receipts
Confirms uint64 indicates how many block is confirmed by block in DPOS consensus
PubKey []byte public key of the block producer (BP)
CoinbaseAccount []byte the account address to which block reward is given
Sign []byte BP’s Signature for block header
ChainID

It is used to prevent the wrong block transmission from other chains.

Field Type Description
Version int32 version number of chain
PublicNet bool true if public net
MainNet bool true if main net
CoinbaseFee string Fee consumed per tx. Fixed value
Magic string Magic string, arbitrarily chosen by each blockchain
Consensus string dpos or sbp
Version

Version is used to identify when the block format changes or when the function of the chain changes.

PublicNet

Differentiate between public and private networks.

MainNet

Differentiate between main net and other test net or other use net.

CoinbaseFee

CoinbaseFee is used to set the peak per Tx.

Magic

Magic can be considered as a name of a blockchain. The two blockchains with different magic strings reject each other’s blocks (they are seperate, indenpendent blockchains).

Consensus

Specify the concensus method name used in the chain.

Block body

Field Type Description
Txs []Tx Transactions

P2P

P2P wire protocol

This page describe the p2p wire protocol for protocol version 0.3.3

Note: Some contents are work in progress and will be implemented before the launch of the mainnet.

Messages
Handshake message

A handshake message is used once at starting handshake. The outbound peer sends HSReqHeader and the inbound peer send HSResp to select perper handshake protocol version. As of Aergo v1.2.0, the protocol version is 0.3.2 and can accept 0.3.1 and 0.3.0

HSReq is variable length byte stream which contains accepted protocol versions

  • 4 : Magic
  • 4 : count of accepted protocol versions
  • 4*n : list of accepted versions. The former is perfered version (actually the newest version is the first)

HSResp is 8 bytes length byte stream

  • 4 : The same Magic as HSReq request or zero if handshaking is not possible
  • 4 : The selected protocol version or error code if first 4 byte value is zero
Handshake response error codes
Name Code Remark
HSCodeWrongHSReq 0001 Request header is wrong
HSCodeNoMatchedVersion 0002 The inbound server have no prefer protocol version
HSCodeAuthFail 0003 Wrong peer identiry
P2P version notation
  • 2 : Major verion
  • 1 : Minor version
  • 1 : Patch version
Normal message

Header (48bytes) + Payload (variable size)

Message header
  • 4 : code number of subprotocol . Big endian number
  • 4 : payload size. Big endian number.
  • 8 : creation time of this message. unix timestamp with precision of nanosecond . Big endian number
  • 16 : message id. binary form of uuid.
  • 16 : original request id. only meaningful if message is response message. binary form of uuid.
Message payload

Payload is serialized form of protobuf struct. The size and struct type is differ by subprotocol.

List of Subprotocols

Code is hexadecimal number. Refer to Subprotocols for detailed information of each subprotocol.

Name Code Summary
StatusRequest 0001 Used in handshake
PingRequest 0002 Ping including last block hash and number
PingResponse 0003 Response ping
GoAway 0004 Disconnect notice with reason
AddressesRequest 0005 Query request to get list of connected peers
AddressesResponse 0006 Response for AddressesRequest.
GetBlocksRequest 0010 request for getting datas of blocks
GetBlocksResponse 0011 response of GetBlocksRequest. Multiple responses for a single request if size of blocks is too big
GetBlockHeadersRequest 0012 request list of headers of consecutive blocks.
GetBlockHeadersResponse 0013 response of GetBlockHeadersRequest
NewBlockNotice 0016 notice of block which the sender was not produced; i.e. it is relay of block notice.
GetAncestorRequest 0017 request for finding common ancestor. used for peer sync
GetAncestorResponse 0018 response of GetAncestorRequest
GetHashesRequest 0019 request for get hashes of consecutive blocks.
GetHashesResponse 001A response of GetHashesRequest
GetHashByNoRequest 001B request for get hash of single block by number
GetHashByNoResponse 001C response of GetHashByNoRequest
GetTXsRequest 0020 request for getting datas of txs
GetTxsResponse 0021 response of GetTXsRequest. Multiple responses for a single request if size of blocks is too big
NewTxNotice 0022 notice of valid tx
BlockProducedNotice 0030 block notice from block producer
BlockProducedNotice 0030 A block is produced. This notice is created only in BP and sent to other trusted BP or FULL nodes.
List of Response Status

Some subprotocols for responsing other message have ResultStatus property.

Name Code Remark
OK 0 OK is returned on success.
CANCELED 1 when operation was canceled
UNKNOWN 2  
INVALID_ARGUMENT 3 INVALID_ARGUMENT is missing or wrong value of argument
DEADLINE_EXCEEDED 4 timeout
NOT_FOUND 5 Resource is not found
ALREADY_EXISTS 6  
PERMISSION_DENIED 7  
RESOURCE_EXHAUSTED 8  
FAILED_PRECONDITION 9  
ABORTED 10  
OUT_OF_RANGE 11  
UNIMPLEMENTED 12 indicates operation is not implemented or not supported/enabled in this service.
INTERNAL 13  
UNAVAILABLE 14 Unavailable indicates the service is currently unavailable.
DATA_LOSS 15  
UNAUTHENTICATED 16 indicates the request does not have valid authentication credentials for the operation.
Payload of Subprotocols
StatusRequest
  • sender: information of sender (address, port, peerID or etc)
  • bestBlockHash: current best block of sender
  • bestHeight: current best block height of sender
  • chainID: ChainID which sender is storing
  • genesis: hash of genesis block, added since protocol version v0.3.2
PingRequest
  • bestBlockHash: current best block of sender
  • bestHeight: current best block height of sender
GoAway
  • reason: description text
AddressesRequest
  • sender: address information of requester
  • maxSize: limit of response size
AddressesResponse
  • status: response status code
  • peers: list of peers
GetBlocksRequest
  • hashes: array of block hashes
GetBlocksResponse
  • status: response status code
  • blocks: list of block data
  • hasNext: boolean flag indicating there are more response(s) for the request
GetBlockHeadersRequest
  • hash: starting hash to get.
  • height: starting height to get. height is ignored if hash is not empty.
  • size: maximum header count to get.
GetBlockHeadersResponse
  • status: response status code
  • hashes: array of block hashes which the response contains.
  • headers: list of block headers. the order of hashes and headers is matching
  • hasNext: boolean flag indicating there are more response(s) for the request
NewBlockNotice
  • blockHash: hash of new block
  • blockNo: block number
GetAncestorRequest
  • hashes: list of block hashes
GetAncestorResponse
  • status: response status code
  • ancestorHash: block hash of common ancestor
  • ancestorNo: block number of common ancestor
GetHashesRequest
  • prevHash: block hash of starting point. the hash and number must match to actual block
  • prevNumber: block number of starting point
  • size: maximum hash count to get.
GetHashesResponse
  • status: response status code
  • hashes: array of block hashes which the response contains.
GetHashByNoRequest
  • blockNo: block number
GetHashByNoResponse
  • status: response status code
  • blockHash: hash of requested block
GetTXsRequest
  • hashes: array of tx hashes
GetTXsResponse
  • status: response status code
  • hashes: array of tx hashes which the response contains.
  • txs: list of tx data. the order of hashes and txs is matching
  • hasNext: boolean flag indicating there are more response(s) for the request

Legacy version infomation

v0.3.0
Handshake message

-A handshake message is used once at starting handshake. It contains two 4-byte number. Both outbound peer send HSReq

+HSReq is 8 byte stream which p2p protocol version

+4 : Magic +4 : p2p protocol version of outbound peer. The inbound peer accept handshake if version is matching or close connection if not.

Peer Connect

NOTE: Some document is not translated yet.

Node Discovery

When the Aergo server starts running, you need a way to connect to the network. To do this, you need to know and connect to other Aergo server nodes already connected to the chain. Aergo does this using several methods.

Query polaris

Polaris keep list of aergo server nodes, such like DNS. Aergo server connect and query to official Aergo Polaris automitically, if the chain is Official AergoMainNet or AergoTestNet.

You can build and run custom Polaris for your private chain or custom public chain. You should configure to use custom Polaris by modifying config file.

Designate Peer

You can add a list of designated known peers to connect to at boot time in configuration file using the option ‘npaddpeers’.

Dynamic peer discovery

Aergo server requests peer list from other connected peers as well as Polaris if it cannot find enough peers.

Choosing remote peers

(This feature is currently work in progress)

The list of peers to connect to is managed in Kademlia-like manner. The number of peers that are closer to the peer is larger and the number of peers that are connected to the peer is smaller. The peer’s distance is based on the difference based on the hash value, not the physical distance.

Peer connect process

The network communication of Aergo server operates on libp2p basis, and libp2p is responsible for encryption and node distinction at transmission level. After a tcp session is created, both peers start the handshake operation.

Peer Handshake

In the Handshake phase, peers exchanges each other’s version, chain ID and state to determine whether to connect. If the other node version is not supported by the current server or the operating chain ID is different, the connection is stopped. Once the handshake is successful, the difference in block height is compared and synchronization started.

Keep Alive

Aergo server maintains a connection-based communication. Both peers disconnect when the internally defined retention score increases beyond a certain level. This score decreases to a low level when a query is requested, and increases when a bad block or TX notification is sent.

Peer blacklist

(This feature is currently work in progress)

When an internally defined ban score exceeds a certain level, the Aergo server blocks the peer’s connection. This score increases due to the connection being disconnected due to exceeding the connection maintenance score, etc., and the blocking period is also changed by calculating the score or the number of blocking times. You can also permanently block by specifying a block address in the configuration file.

Keystore

Note

If you or your users are storing significant amounts of values in an Aergo account, be advised that the only really secure way of storing private keys is using dedicated, offline hardware like a hardware wallet. This article is not a operations security guide. Please do your own research about private key security.

Securing private keys is the responsibility of client software, but Aergo has a recommended storage format for increased portability. This storage format is also used internally when storing accounts in aergocli or aergosvr.

This so-called Keystore format defines a file format (json) and encryption scheme.

File format

Filename: {address}__keystore.txt

{
  "aergo_address": "Amdxxxx",
  "ks_version": "1",
  "kdf": {
    "algorithm": "algorithm_name",
    "params": {
      "some_param": "some_value"
    },
    "mac": "mesasge_authentication_code"
  },
  "cipher": {
    "algorithm": "algorithm_name",
    "params": {
      "some_param": "some_value"
    },
    "ciphertext": "dxxxx",
  }
}

Rules

  • Version field is for choosing cipher algorithm. If version updates, cipher algorithm would be updated (also format can change).
  • Every file has its own algorithm and parameters per version. If any of the parameters break, an error should be thrown.
  • File name is ‘{address}__keystore.txt’ or ‘{alias}__keystore.txt’
  • File name is used to identify keystore file. Duplicates are not allowed.
  • For the alias file, you can have same keystore file representing same account using different alias. That’s fine.
  • Address must be a valid base58-check encoded address. Alias must be form of [a-zA-Z0-9]+

Version 1 encryption scheme

Example implementations: Javascript

Encrypt

encryptionKey = Scrypt(rawPassword, scryptParams)
cipherText = AES_CTR.encrypt(rawPrivateKey, encryptionKey[0:16], nonce)
mac = Sha256(encryptionKey[16:32] + cipherText)
keystore = {
  "aergo_address": "Amdxxxx",
  "ks_version": "1",
  "kdf": {
    "algorithm": "scrypt",
    "params": scryptParams,
    "mac": mac
  },
  "cipher": {
    "algorithm": "aes-128-ctr",
    "params": {
      "iv": nonce
    },
    "ciphertext": cipherText
  }
}

Decrypt

encryptionKey = Scrypt(rawPassword, keystore.kdf.params)
mac = Sha256(encryptionKey[16:32] + json.cipher.ciphertext)
if keystore.kdf.mac != mac {
  throw "invalid mac"
}
rawPrivateKey = AES_CTR.decrypt(json.cipher.ciphertext, encryptionKey[0:16], json.cipher.params.iv)

Recommended parameters

{
    kdfAlgorithm: 'scrypt',
    cipherAlgorithm: 'aes-128-ctr',
    kdfParams: {
        dklen: 32,
        n: 1 << 18,
        p: 1,
        r: 8,
    },
}

Block Management

This article describes implementation details of management of blocks in the Aergo server. It is aimed at blockchain server developers.

Block generation

The block factory is responsible for generating blocks and part of the consensus engine.

The consensus engine manages when blocks are created on which nodes. AERGO uses DPoS by default. A new block producer is selected every second.

The process for creating blocks in the block factory is as follows.

  1. Select transactions from from mempool
  2. Perform a transaction one by one. If the execution is successful, the transaction is included in the block.
  3. If the block size and time limit are exceeded, the transaction is no longer included.
  4. Create blocks that contain executed transactions and add signatures to blocks.

The block factory sends the generated blocks to the chain service for propagation and storage.

Notify block

Blocks passed to the chain service are propagated to the connected peer nodes and stored in the database.

The chain service of a node that received the block verifies the block, executes the transactions, and adds the block to its own database.

Save block

The chain service stores the generated blocks in the DB for permanent storage. It also stores additional meta information to store connection information about the chain.

AERGO uses the Badger database as its permanent storage. Badger stores data in key/value form and supports the concept of transactions similar to a typical database.

Information stored in the DB is as follows.

The DB is managed in two separate categories, ChainDB and StateDB. This article describes the ChainDB used to store chain information.

Block info

This information is used to search for blocks in a chain using a hash. Typically, when a block is requested as a hash from another node, the Hash is searched for and responded to the block with a key.

Key Value
Hash of Block serialized data of Block
Chain info

Chain info is the information that indicates when a block is connected to the main branch.

If the main branch is not connected, that is, only block info is saved and the chain info is not stored.

You can search the blocks of every height of the main branch using the chain info. You can know the height of the top block of the chain.

Chain info is also used to determine whether the block belongs to the main branch or the sub branch.

Key Value
Number of Block block hash
Latest block Key latest block number of main branch
Transaction Info

tx info is meta information of the transaction that was performed in the main branch. If you search the DB with transaction hash, you can see which block the transaction is stored in and where the transaction is stored in the block.

The contents of the transaction are not saved separately. Therefore, in order to know the contents of the actual transaction, you must search Block info using Block hash and index found in tx info.

Key Value
Transaction hash (Block Hash, index of transaction in block)

Chain Management

This article describes implementation details of chain management in the Aergo server. It is aimed at blockchain server developers.

The chain service is responsible for managing the chain. It performs the following tasks in large part:

  1. Validation
  2. Insert block to chain
  3. Reorganization
  4. Syncronization

Block validation

The blocks received in the network may not be valid, so a number of checks are made.

Pre-execution execution

The validator module ensures that the block was generated from a valid BP and the transaction contained in the block was not forged.

  • Consensus validation

    Validate the block generated by the valid BP through block creation time and signature.

  • Transaction merkle validation

    Validate that the transactions were not forged. The verifier module generates a merkle tree with the transactions and checks if it is the same as the transaction merkle root stored in the block header.

Post-execution validation

These checks ensure that the results of the transaction contained in the block are the same as the results of the BP node that generated the block.

  • State root validation

    Checks if the changed state root node hash is the same as the blocksRootHash stored in the block head.

  • Receipt merkle validation

    Generates a merkle tree with the receipts generated as a result of the transactions and checks if they are identical to the receipts stored in the block header.

Insert block into chain

There are two cases in which the chain service adds blocks:

  1. Block generated by block factory

When it is a BP node’s turn, it generates new blocks in the block factory.

The block factory performs all transactions and forwards the block to the chain service. The execution results are included with the block.

The execution results include the results of the transaction, receipt, and state changed entry indicating the change to the account.

The chain service stores information related to blocks in the ChainDB and information about accounts in the StateDB.

  1. Block received from another peer node

For blocks received from other peer nodes, there are three main cases:

  • Orphan block

    This is the case if the parent block has not yet been stored in the DB. Orphan blocks are stored in the orphan block cache on memory. Then, when the parent block is received, it is removed from the organ block cache and reprocessed.

  • Side branch block

    In case the parent block is stored but is not part of the main branch. In this case, the block is not performed and only the block info{hash, block} is stored.

  • Main branch block

    In case the head of the main branch is the next block. In this case, the blocks are stored after performing the transactions.

The process for storing blocks in the main branch is as follows:

  1. Validation before execution
  2. Execute transactions
  3. Apply changed account entries state merkle tree
  4. Validate after execution
  5. Save state merkle tree to StateDB
  6. Save chain meta data to ChainDB

Reorganization

The chain service selects and maintains the longest chain as the main branch. The side branches are not executed and only the block info is stored in the DB. If the side branch received from another peer is longer than the main branch held by the node, the side branch is changed to the main branch. This is called the reorgnize process.

The reorgnization is performed as follows.

  1. Find common ancestor
Syncer finds the last common ancestor block of the main branch and side branch.
  1. Rollback master branch
State is reset to the point at which the common ancestor block was executed
  1. Rollforward side branch
Syncer runs from the next number of the common ancestor block to the head block of the side branch. At this time, only StateDB is changed and Chain info and Tx info are not changed.
  1. Swap chain meta

Syncer do not change the chain info during rollback and rollforward to atomically change the chain. Change the chain meta information after the previous process has been successfully completed. At this time, chain info and transaction info are deleted for the rollbacked block, and new chain info and transaction info are added for the rollforwarded block.

Transactions belonging to rollbacked blocks but not included in rollforwarded blocks are returned to mempool. This is to prevent transaction loss.

Syncronization

When you add a new node or restart a node that was temporarily stopped, you need to get the latest chain information from existing nodes. This is called the synchonization process.

The situation that causes sync is as follows:

  • When the peer goes through a handshake process to connect, the height of the chain of the remote peer is higher than the current node
  • If the height of the block notified in the peer is higher than the head of the current main branch

The syncer specifies the node that sent the block that caused the sync to the target node and synchronizes with the chain of that node.

To synchronize a long chain, a large amount of block information must be received from the peer node. This is likely to cause a performance degrade at the peer node. Therefore, it gets information from as many peers as possible to distribute the load.

Synchronize step
  1. Find common ancestor: Syncer finds the last common ancestor of the current node chain and the target node chain.
  2. Get hashes: It gets the hashes of the block after the common ancestor from the target node.
  3. Get blocks: N blocks are requested from all valid peers connected to the current node.
  4. Insert blocks to chain: The received block is added to the chain using the chain service.

2, 3, and 4 are performed in parallel. Most of the time is spent in the insert part of the chain.

Contributing

As AERGO is an open-source platform, individual contributions are vital.

There are many ways that you can help, no matter if you are a novice or advanced programmer or user.

Ways to Contribute

These are just some examples for welcome contributions.

  • Improve this documentation
  • File a bug report
  • Submit a pull request
  • Discuss protocol changes
  • Help answering questions

Join the Community

An open-source platform lives from its community. You can reach out to the Aergo team and other members of the developer community via these channels:

Building from Source

This page explains how you can build aergo from source.

If you are only interested in running the aergo tools, it is easier to use pre-built binaries or Docker images instead, as explained in Quickstart.

Prerequisites

Linux

  1. Install dependencies

    apk update
    apk add git build-base go libgcc
    
  2. Install aergo

    go get -d github.com/aergoio/aergo
    cd ${GOPATH}/src/github.com/aergoio/aergo
    git submodule init && git submodule update
    make
    
  3. Set environment

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${GOPATH}/src/github.com/aergoio/aergo/libtool/lib
    
  4. Run server

    ./bin/aergosvr
    

macOS (Do not support anymore)

  1. If you haven’t already, install homebrew.

  2. Install dependencies

    brew install go
    brew install cmake  # optional
    brew install protobuf  # optional
    
  3. Install aergo

    go get -d github.com/aergoio/aergo
    cd `go env GOPATH`/src/github.com/aergoio/aergo
    git submodule init && git submodule update
    make
    
  4. Set environment

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${GOPATH}/src/github.com/aergoio/aergo/libtool/lib
    
  5. Run server

    ./bin/aergosvr
    

Windows

Building on Windows is currently not supported.

Generating protobuf files

If you changed the protobuf files, you need to re-compile the type definitions.

  1. Install protoc-gen-go

    GIT_TAG="v1.1.0" # set version of protoc-gen-go to 1.1.0, on which aergo v0.8.x depends.
    go get -u github.com/golang/protobuf/protoc-gen-go
    git -C "$(go env GOPATH)"/src/github.com/golang/protobuf checkout $GIT_TAG
    go install github.com/golang/protobuf/protoc-gen-go
    
  2. Generate type definitions from protobuf files

    make protoc
    

SDKs

Aergo offers language-specific SDKs to make it easy to develop Dapps.

_images/dapp-overview.png

Other Languages

If you are using another language that is currently not supported by a decicated SDK, you can still interact with AERGO using the API directly.

Please refer to the API reference.

You can also contact us with a request to implement support for your requirements.

Tools

Aergocli

aergocli is a command line tool that interfaces with the GRPC exposed by aergosvr.

Examples

With all account actions, you will be prompted for a password. For automation, you can also pass it as the --password flag, but that is less secure depending on your terminal environment.

Changed in version 2.2.0: Account actions now use a local keystore. The path can be configured using --keystore (default: $HOME/.aergo). In previous versions, this used a remote keystore on the node you connected to. You can restore the previous behavior using the --node-keystore option. See below for examples.

Create account
$ aergocli account new
Enter Password:
Repeat Password:
AmP96xd6WYRe1jrWixC3VyFXRCzwZxAJtU3Uc54vn182xG9Yn9Cs

Please remember the password carefully because there is no way to retrieve the password again.

Send transaction
$ ./aergocli sendtx --from AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q --to AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ --amount 1aergo
{
 "hash": "AkjqFcayutenhonnZPU4X5QB1fNBTZv3o2fNzMLNQR3q"
}
Look up transaction

Use gettx command. If transaction is in block, return transaction with index that represent where it’s included.

When transaction is in mempool, gettx result is like below

$ aergocli gettx 9cAphBMD2zJCD13QfCn7rmxh5iDfrj6M9Wmo54TPNPCg
{
 "Hash": "9cAphBMD2zJCD13QfCn7rmxh5iDfrj6M9Wmo54TPNPCg",
 "Body": {
  "Nonce": 2,
  "Account": "AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q",
  "Recipient": "AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ",
  "Amount": "1230000000000000000",
  "Payload": "",
  "Limit": 0,
  "Price": "0",
  "Type": 0,
  "Sign": "AN1rKvt8EZHKEE2wNPXAhGcDA4pMo7yRRjTWCcpyrW9QCMv6nMhvhqriWujHdaDJgJ6ft6VLDActEFtUFA2pRnRJFVFSWxPSR"
 }
}

When transaction is in block, gettx result is like below

$ aergocli gettx 9cAphBMD2zJCD13QfCn7rmxh5iDfrj6M9Wmo54TPNPCg
{
 "TxIdx": {
  "BlockHash": "ECVG696Jc7FvUL86sDSxT28akh4Hf1RXFRzmGqtAv9zU",
  "Idx": 1
 },
 "Tx": {
  "Hash": "9cAphBMD2zJCD13QfCn7rmxh5iDfrj6M9Wmo54TPNPCg",
  "Body": {
   "Nonce": 2,
   "Account": "AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q",
   "Recipient": "AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ",
   "Amount": "1230000000000000000",
   "Payload": "",
   "Limit": 0,
   "Price": "0",
   "Type": 0,
   "Sign": "AN1rKvt8EZHKEE2wNPXAhGcDA4pMo7yRRjTWCcpyrW9QCMv6nMhvhqriWujHdaDJgJ6ft6VLDActEFtUFA2pRnRJFVFSWxPSR"
  }
 }
}
Check block
$ aergocli getblock --hash GGT9wahqcKKGKUncMuhRLLL3JaCs2MEBx7V8UdrK9JNi
{
 "Hash": "FwBq14HiBPMPoGV6jYxW4AaGHsgoD9UJjmYyWwnQR6xU",
 "Header": {
   "ChainID": "1111117eaxEoT4pDHzFTCyKv93acfDHbsytUPK3oqU6",
   "PrevBlockHash": "GocCiGXUVV4ygsbEi1VaJKkBwyvRsV4W4Qj72J5ZciZK",
   "BlockNo": 17655,
   "Timestamp": 1548314097754698000,
   "BlockRootHash": "5etqxP9HTtzgN8a3tN5Ev8ka4aG3aQM5GYNikwmsV41q",
   "TxRootHash": "AkjqFcayutenhonnZPU4X5QB1fNBTZv3o2fNzMLNQR3q",
   "ReceiptsRootHash": "7ZpYoMA2feXoXAit5J2FFuX16cwz9hp8twGwu7rUHCRZ",
   "Confirms": 2,
   "PubKey": "GZsJqUTtFVJTJ5SbwuFac4NxZFWgTRjpioPD76UL1DHZLhxmWg",
   "Sign": "AN1rKvtSPyBqB34TGmEQ6FQ8Lz61dvQ4zVAHodAnPGTnXD7gihmSkgrWcyeEoXNeS6zLTtgjukcTiwSKTHmMATGe8PXgwcJJK",
   "CoinbaseAccount": ""
  },
  "Body": {
   "Txs": [
    {
     "Hash": "AkjqFcayutenhonnZPU4X5QB1fNBTZv3o2fNzMLNQR3q",
     "Body": {
      "Nonce": 3,
      "Account": "AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q",
      "Recipient": "AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ",
      "Amount": "1000000000000000000",
      "Payload": "",
      "Limit": 0,
      "Price": "0",
      "Type": 0,
      "Sign": "381yXZAWNW8ZiST7PCLmywUsNVQvzgwhiyu9pxVDSvUhwyLHmoL7BQGXQhCNp7QZQycvbwT2nQJ7etscArfRbHu98Qi5MSmY"
     }
    }
   ]
  }
 }
Sign transaction
$ aergocli signtx --address AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q --jsontx "{ \
\"Nonce\": 2, \
\"Account\": \"AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q\", \
\"Recipient\": \"AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ\", \
\"Amount\": \"1.23aergo\", \
\"Payload\": \"\", \
\"Limit\": 0, \
\"Price\": \"0\", \
\"Type\": 0 }"
Enter Password:
{
 "Hash": "9cAphBMD2zJCD13QfCn7rmxh5iDfrj6M9Wmo54TPNPCg",
 "Body": {
  "Nonce": 2,
  "Account": "AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q",
  "Recipient": "AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ",
  "Amount": "1230000000000000000",
  "Payload": "",
  "Limit": 0,
  "Price": "0",
  "Type": 0,
  "Sign": "AN1rKvt8EZHKEE2wNPXAhGcDA4pMo7yRRjTWCcpyrW9QCMv6nMhvhqriWujHdaDJgJ6ft6VLDActEFtUFA2pRnRJFVFSWxPSR"
 }
}
Commit Transaction

Send given transactions to aergosvr

$ aergocli committx --jsontx "{
\"Hash\": \"9cAphBMD2zJCD13QfCn7rmxh5iDfrj6M9Wmo54TPNPCg\",
\"Body\": {
  \"Nonce\": 2,
  \"Account\": \"AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q\",
  \"Recipient\": \"AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ\",
  \"Amount\": \"1230000000000000000\",
  \"Payload\": \"\",
  \"Limit\": 0,
  \"Price\": \"0\",
  \"Type\": 0,
  \"Sign\": \"AN1rKvt8EZHKEE2wNPXAhGcDA4pMo7yRRjTWCcpyrW9QCMv6nMhvhqriWujHdaDJgJ6ft6VLDActEFtUFA2pRnRJFVFSWxPSR\"
}
}"
{
 "results": [
  {
   "hash": "9cAphBMD2zJCD13QfCn7rmxh5iDfrj6M9Wmo54TPNPCg"
  }
 ]
}
Get Account state

Check account’s state (nonce, balance)

$ aergocli getstate --address "AmNvFyqKFGVWvQ3MTi3eMFiNB9zvL9cK43B9c9bzcA732YZjZgfn"

Get state with a compressed merkle proof.

$ aergocli getstate --address "AmNvFyqKFGVWvQ3MTi3eMFiNB9zvL9cK43B9c9bzcA732YZjZgfn" --proof --compressed

By default, the returned state is the one at the latest block, but you may specify any past block’s state root.

$ aergocli getstate --address "AmNvFyqKFGVWvQ3MTi3eMFiNB9zvL9cK43B9c9bzcA732YZjZgfn" --root "9NBSjkcNTdE5ciBxfb52RmsVW7vgX5voRsv6KcosiNjE"
Show connected peers

Use getpeers to show list of peers connected to a aergosvr.

It shows remote peers by default, but with --self options, local aergosvr itself is shown in list. You can find it by checking Self property. If you want to see only opened peers, use with --nohidden options. The option --detail is added in v2, which change output information level. --detail=0 or no option shows same as prvious version, --detail=-1 shows shortened version, and --detail=1 shows more information about peers

normal output

[
 {
  "Role": "Producer",
  "Address": {
   "Address": "13.124.83.51",
   "Port": "7846",
   "PeerId": "16Uiu2HAmQn3nFBGhJM7TnZRguLhgUx1HnpNL2easdt2JrxdbFjtb"
  },
  "BestBlock": {
   "BlockHash": "BXAFbTbwEuywukzBqRKdCsuUpinnNQsVJCAXNVJAR6F4",
   "BlockNo": 1251651
  },
  "LastCheck": "2019-02-27T11:26:38.481451+09:00",
  "State": "RUNNING",
  "Hidden": false,
  "Self": true
  "Version": "v2.0.0-dev1-14-g138d4f04"
 },
 {
  "Role": "Producer",
  "Address": {
   "Address": "13.211.156.203",
   "Port": "7846",
   "PeerId": "16Uiu2HAkvbHmK1Ke1hqAHmahwTGE4ndkdMdXJeXFE3kgBs17k2oQ"
  },
  "BestBlock": {
   "BlockHash": "AF68dtMMHd1h5LjxSyYY9AomMve6Qk2kBRSoQuuuSkhM",
   "BlockNo": 1251650
  },
  "LastCheck": "2019-02-27T11:26:38.349938+09:00",
  "State": "RUNNING",
  "Hidden": false,
  "Self": false
  "Version": "v2.0.0-dev1-14-g138d4f04"
 },
 {
  "Role": "Producer",
  "Address": {
   "Address": "192.168.2.21",
   "Port": "7846",
   "PeerId": "16Uiu2HAmAnQ5jjk7huhepfFtDFFCreuJ21nHYBApVpg8G7EBdwme"
  },
  "BestBlock": {
   "BlockHash": "BXAFbTbwEuywukzBqRKdCsuUpinnNQsVJCAXNVJAR6F4",
   "BlockNo": 1251651
  },
  "LastCheck": "2019-02-27T11:26:38.364262+09:00",
  "State": "RUNNING",
  "Hidden": true,
  "Self": false
  "Version": "v2.0.0-dev1-14-g138d4f04"
 },
 {
  "Role": "Agent",
  "Address": {
   "Address": "192.168.2.11",
   "Port": "7846",
   "PeerId": "16Uiu2HAm7e2TBhLDrsEyRVTnqqeyZznLxfpHcWQTdCZd8r4DhF9Q"
  },
  "BestBlock": {
   "BlockHash": "BXAFbTbwEuywukzBqRKdCsuUpinnNQsVJCAXNVJAR6F4",
   "BlockNo": 1251651
  },
  "LastCheck": "2019-02-27T11:26:38.342138+09:00",
  "State": "RUNNING",
  "Hidden": false,
  "Self": false,
  "Version": "v2.0.0-dev1-14-g138d4f04"
 }
]

shortened output

[
 "16*dbFjtb;13.124.83.51/7846;Producer;1251651",
 "16*17k2oQ;13.211.156.203/7846;Producer;1251650",
 "16*EBdwme;192.168.2.21/7846;Producer;1251651",
 "16*4DhF9Q;192.168.2.11/7846;Agent;1251651"
]
Using remote node-based accounts

Deprecated since version 2.2.0: To be removed in future versions, use local keystores (the new default) instead.

You can also store accounts on a remote node. When using remote account management, pass the --node-keystore flag to all commands. In versions prior to 2.2.0, this was the default behavior.

Create, Export, Import account
$ aergocli account new --node-keystore
Enter Password:
Repeat Password:
AmNFcocofUvmyLtXA6WgpANbjiF7RScGvQ4memNyNzS4ARJox3yq

This account can be exported and imported.

$ aergocli account export --address AmNFcocofUvmyLtXA6WgpANbjiF7RScGvQ4memNyNzS4ARJox3yq --node-keystore > AmNFcocofUvmyLtXA6WgpANbjiF7RScGvQ4memNyNzS4ARJox3yq__keystore.txt
Enter Password:
$ aergocli account import --node-keystore --path AmNFcocofUvmyLtXA6WgpANbjiF7RScGvQ4memNyNzS4ARJox3yq__keystore
AmNFcocofUvmyLtXA6WgpANbjiF7RScGvQ4memNyNzS4ARJox3yq
Unlock

Before request send transaction you must unlock your account first

$ aergocli account unlock --node-keystore --address AmQFgm1gCvoRw2RfBXnipRmeCLEc6tTQ1kBMmLEzHjp91xYnXK78
Enter Password:
AmQFgm1gCvoRw2RfBXnipRmeCLEc6tTQ1kBMmLEzHjp91xYnXK78
Sign transaction

With --node-keystore option, aergocli can sign the transaction using private key of account on the remote node.

$ aergocli -p 17845 signtx --address AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q --jsontx "{ \
\"Nonce\": 2, \
\"Account\": \"AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q\", \
\"Recipient\": \"AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ\", \
\"Amount\": \"1.23aergo\", \
\"Payload\": \"\", \
\"Limit\": 0, \
\"Price\": \"0\", \
\"Type\": 0 }" --node-keystore
Enter Password:
{
 "Hash": "9cAphBMD2zJCD13QfCn7rmxh5iDfrj6M9Wmo54TPNPCg",
 "Body": {
  "Nonce": 2,
  "Account": "AmPLWBzx4tAYt91JM3jKWFs3aYWHSvKpYYzdUQuQMNa7jAw5t65q",
  "Recipient": "AmLnVfGwq49etaa7dnzfGJTbaZWV7aVmrxFes4KmWukXwtooVZPJ",
  "Amount": "1230000000000000000",
  "Payload": "",
  "Limit": 0,
  "Price": "0",
  "Type": 0,
  "Sign": "AN1rKvt8EZHKEE2wNPXAhGcDA4pMo7yRRjTWCcpyrW9QCMv6nMhvhqriWujHdaDJgJ6ft6VLDActEFtUFA2pRnRJFVFSWxPSR"
 }
}

Installation

Using the binary

You can find the latest binary release on Github. Just download and extract the archive.

Using Docker

You can also use the Docker image aergo/tools. Example: docker run --rm aergo/tools aergocli version

Usage

In order to use all features of aergocli you will need to have the end point (IP address and port number) to a aergosvr instance.

For a list of all commands known to aergocli, simply run it with no arguments. To see the usage of sub commands, run those without arguments, e.g. aergocli account.

Usage:
  aergocli [command]

Available Commands:
  account          Account command
  blockchain       Print current blockchain status
  bp               show BP list
  chaininfo        Print current blockchain information
  chainstat        Print chain statistics
  cluster          Cluster command for raft consensus
  committx         commit transaction to aergo server
  contract         Contract command
  enterprise       Enterprise command
  event            Get event
  getblock         Get block information
  getconsensusinfo Print consensus info
  getpeers         Get Peer list
  getstate         Get account state
  gettx            Get transaction information
  help             Help about any command
  keygen           Generate private key
  listblocks       Get block headers list
  metric           Show metric informations
  name             Name command
  node             Show internal metric
  receipt          Receipt command
  sendtx           Send transaction
  serverinfo       Show configs and status of server
  signtx           Sign transaction
  verifytx         Verify transaction
  version          Print the version number of Aergocli
  votestat         show voting stat

Flags:
      --config string          config file (default is cliconfig.toml)
  -h, --help                   help for aergocli
      --home string            aergo cli home path
  -H, --host string            Host address to aergo server (default "localhost")
      --keystore string        Path to keystore (default "$HOME/.aergo")
      --node-keystore          use node keystore
  -p, --port int32             Port number to aergo server (default 7845)
      --tlscacert string       aergosvr CA certification file for TLS
      --tlscert string         client certification file for TLS
      --tlskey string          client key file for TLS
      --tlsservername string   aergosvr name for TLS

Use "aergocli [command] --help" for more information about a command.

Local or remote keystore?

Changed in version 2.2.0.

Since version 2.2.0, aergocli uses a local keystore by default. When you create or import accounts, they are saved on the local machine in an encrypted keystore file format. The default storage directory is $HOME/.aergo, but you can change it by passing the --keystore flag. Whenever you sign or send a transaction, these locally saved accounts are used.

In aergocli versions before 2.2.0, account actions would use an account that is stored on the remote server (full node) you are connected to. This is fine if you are running your own full node, but for the vast majority of users who want to connect to publicly available nodes, this is not useful. That is why the default behavior was changed to use local keystores.

Continue using the old method (server-based accounts)

To use the pre-2.2 method of selecting accounts from the connected server, pass the --node-keystore flag to aergocli. Remember you then have to unlock accounts before using them on the server. Check out the examples.

Migration from remote to local keystore

To migrate from the old to the new storage format, first export an account from your node as a keystore file, and then import it into your local keystore:

# Export from remote
$ aergocli -H YOUR_NODE_IP --node-keystore account export --address YOUR_ADDRESS > YOUR_ADDRESS__keystore.txt

# Import locally
$ aergocli account import --path YOUR_ADDRESS__keystore.txt

See also

In version 2.2.0, the internal keystore method was also changed. See here for details.

Aergoluac

aergoluac is a command line tool that compile lua script to binary code for contract.

For a list of all commands known to aergoluac, simply run it with no arguments.

Usage:
aergoluac [flags] srcfile bcfile

Flags:
-a, --abi string   abi filename
-h, --help         help for aergoluac
    --payload      print the compilation result consisting of bytecode and abi

Examples

out to file
aergoluac test.lua test.bc -a test.abi
out to console
aergoluac test.lua --payload

Ship

Ship is a tool for developing lua smart contracts. Its main features are:

  • Manage project structure
  • Install and publish packages
  • Build source fragments
  • Run unit test cases
  • Deployment, execution and querying of a contract.

Please refer to the Ship Github Wiki for details.

Brick

Brick is an interactive shell program to communicate with an aergo VM for testing. This also provides a batch function to test and help to develop smart contracts.

The main advantage of using this tool is that you don’t need a real blockchain network. Instead, you can run operations directly in the aergo virtual machine.

See Brick reference

Polaris

Polaris is a server that provides node discovery for Aergo server.

For bootstrapping new nodes, the npaddpeers configuration option can be used to connect to designated peers.

However, since Aergo version 0.11, the new Polarais server has added the ability to automatically connect to nodes belonging to the specified block chain without manually creating the node list information.

Features

  • Aergo nodes can query addresses of other Aergo nodes. In this case, the chain of the Aergo node and the chain of Polaris must be the same.
  • Aergo nodes can register itself with Polaris. Polaris checks to see if it can connect to the Aergo node and adds it to the node list.
  • One Polaris server per designated block chain

Building Polaris

This section describes how to build Polaris from source without using the Docker. Polaris is available as a sub-module in the aergo project currently in version 0.11.

  1. Get the source from github.com/aergoio/aergo.
  2. Build the polaris executable with make polaris.

Configuration

Four files are used to set Polaris behavior.

  1. Polaris configuration file: Determines the overall operation of Polaris. It also specifies the path to other configuration related files.
  2. Private key file: PK file to use for Polaris communication. It uses the same format as aergosvr’s private key file.
  3. Genesis file: Contains the chain information of nodes to be provided by Polaris. Use the same format as the genesis file used to initialize aergosvr.
  4. Log configuration file: The file name is arglog.toml, and it uses the same format as the file used by aergosvr.
Configuration file creation and example
Create private key file

It can be generated by aergocli using the keygen command.

aergocli keygen mychain-polaris
Wrote files mychain-polaris.{key,pub,id}.
Genesis file creation
{
    "chain_id":{
        "magic": "[insert an identifier string for your network]",
        "public": false,
        "mainnet": false,
        "coinbasefee": "1000000000",
        "consensus": "dpos"
    },
    "timestamp": 1548918000000000000,
    "balance": {
    },
    "bps": [
    ]
}
Polaris configuration file
authdir = "/blockchain/polaris/auth"       # base directory for files about authentication and authorization

[rpc]
netserviceaddr = "127.0.0.1"               # RPC access address. The default setting is 127.0.0.1, which allows RPC access only on the local machine and blocks RPC connections remotely.
netserviceport = 8915

[p2p]
netprotocoladdr = "[real IP address]"      # An externally accessible IP address or domain name
netprotocolport = 8916
npbindaddr = ""
npkey = "mychain-polaris.key"              # Location of private key file

[polaris]
allowprivate = true                        # Whether to allow the private address of the node's access address. Used when building Polaris for private chains operated within a test or private network.
genesisfile = "[location of genesis file]" # Genesis file location
enableblacklist = false                    # Whether to turn on blacklist or not. blacklist entries will be saved in <authdir>
Log configuration file

Refer to the arglog documentation.

Running Polaris

Using Docker
docker run -d -w /tools -v /blockchain/polaris:/tools -p 8916:8916 -p 8915:8915 --restart="always" --name polaris-node aergo/polaris polaris --home /tools --config /tools/polaris-conf.toml
Manually

Manually build and run the live polaris executable in the following format:

./polaris --config polaris-conf.toml

Connecting to Polaris

To connect to a Polaris server, supply its address in the aergosvr configuration file.

See Node Configuration for details.

    [p2p]
    ...
    npusepolaris= true
    npaddpolarises = [
        "/ip4/192.168.0.2/tcp/8915/p2p/16Uiu2HAmJCmxe7CrgTbJBgzyG8rx5Z5vybXPWQHHGQ7aRJfBsoFs"
    ]
...

Colaris

Colaris is a client for Polaris RPC connection.

Building colaris

Like Polaris, build as a sub-module of aergo.

  1. Get the source from github.com/aergoio/aergo.
  2. Build the executable with make colaris.
Usage

It is the same interface as aergocli.

./colaris [flags] <command> [[arg1]...]
Flags
  1. -H <hostname> Address to remote server when requesting. The default value is localhost (127.0.0.1)
  2. -p <portnumber> RPC port number, default is 8915
Commands

node: returns the actor state of Polaris.

current: returns list of nodes registered in Polaris.

Example:

ubuntu@mypolaris:/blockchain/polaris$ ./colaris current
{
 "total": 1,
 "peers": [
  {
   "address": {
    "address": "52.231.31.38",
    "port": 7846,
    "peerID": "16Uiu2HAmBfFABqQ2eWwNMv1A2WJCqVykgPS2sz72jrYTHeZgyors"
   },
   "connected": 1549526282,
   "lastCheck": 1549526463
  }
 ]
}

config: set or get dynamic configurations of polaris. There is just a single config ‘blacklist’, in version 1.2.3.

blacklist: set or get blacklist configuration. blacklist entry can be set by peerID, ip address, cidr or combined. NOTE: it is NOT for security rather avoiding unintentional fork, since changing ip address or peer id is very easy.

Example:

ubuntu@mypolaris:/blockchain/polaris$ ./colaris config blacklist show
ubuntu@mypolaris:/blockchain/polaris$ ./colaris config blacklist add --peerid 16Uiu2HAmDFV41vku39rsMtXBaFT1MFUDyHxXiDJrUDt7gJycSKnX --address "192.168.1.11"
ubuntu@mypolaris:/blockchain/polaris$ ./colaris config blacklist add --address "192.168.1.11"
ubuntu@mypolaris:/blockchain/polaris$ ./colaris config blacklist add --cidr "2001:0db8:0123:4567:89ab:cdef:1234:5678/96"
ubuntu@mypolaris:/blockchain/polaris$ ./colaris config blacklist rm 2

API

The Aergo server exposes an RPC API over gRPC.

Using gRPC

gRPC is a standard for RPC APIs using protobuffer messages for data exchange. The main advantages are typed messages and efficiency: data is packed tightly and sent over HTTP2.

If you want to use the gRPC API directly, please refer to the official documentation to get started. You can find the protobuf definitions in aergoio/aergo-protobuf.

Alternatively, you can use one of the SDKs which wrap the same functionality in language-specific styles.

Protocol Documentation

This reference is auto-generated from aergoio/aergo-protobuf.

Top

rpc.proto

AergoRPCService

AergoRPCService is the main RPC service providing endpoints to interact with the node and blockchain. If not otherwise noted, methods are unary requests.

NodeState

Request Type: NodeReq
Response Type: SingleBytes

Returns the current state of this node

Metric

Request Type: MetricsRequest
Response Type: Metrics

Returns node metrics according to request

Blockchain

Request Type: Empty
Response Type: BlockchainStatus

Returns current blockchain status (best block’s height and hash)

GetChainInfo

Request Type: Empty
Response Type: ChainInfo

Returns current blockchain’s basic information

ChainStat

Request Type: Empty
Response Type: ChainStats

Returns current chain statistics

ListBlockHeaders

Request Type: ListParams
Response Type: BlockHeaderList

Returns list of Blocks without body according to request

ListBlockMetadata

Request Type: ListParams
Response Type: BlockMetadataList

Returns list of block metadata (hash, header, and number of transactions) according to request

ListBlockStream

Request Type: Empty
Response Type: Block

Returns a stream of new blocks as they get added to the blockchain

ListBlockMetadataStream

Request Type: Empty
Response Type: BlockMetadata

Returns a stream of new block’s metadata as they get added to the blockchain

GetBlock

Request Type: SingleBytes
Response Type: Block

Return a single block incl. header and body, queried by hash or number

GetBlockMetadata

Request Type: SingleBytes
Response Type: BlockMetadata

Return a single block’s metdata (hash, header, and number of transactions), queried by hash or number

GetBlockBody

Request Type: BlockBodyParams
Response Type: BlockBodyPaged

Return a single block’s body, queried by hash or number and list parameters

GetTX

Request Type: SingleBytes
Response Type: Tx

Return a single transaction, queried by transaction hash

GetBlockTX

Request Type: SingleBytes
Response Type: TxInBlock

Return information about transaction in block, queried by transaction hash

GetReceipt

Request Type: SingleBytes
Response Type: Receipt

Return transaction receipt, queried by transaction hash

GetABI

Request Type: SingleBytes
Response Type: ABI

Return ABI stored at contract address

SendTX

Request Type: Tx
Response Type: CommitResult

Sign and send a transaction from an unlocked account

SignTX

Request Type: Tx
Response Type: Tx

Sign transaction with unlocked account

VerifyTX

Request Type: Tx
Response Type: VerifyResult

Verify validity of transaction

CommitTX

Request Type: TxList
Response Type: CommitResultList

Commit a signed transaction

GetState

Request Type: SingleBytes
Response Type: State

Return state of account

GetStateAndProof

Request Type: AccountAndRoot
Response Type: AccountProof

Return state of account, including merkle proof

CreateAccount

Request Type: Personal
Response Type: Account

Create a new account in this node

GetAccounts

Request Type: Empty
Response Type: AccountList

Return list of accounts in this node

LockAccount

Request Type: Personal
Response Type: Account

Lock account in this node

UnlockAccount

Request Type: Personal
Response Type: Account

Unlock account in this node

ImportAccount

Request Type: ImportFormat
Response Type: Account

Import account to this node

ExportAccount

Request Type: Personal
Response Type: SingleBytes

Export account stored in this node as wif format

ExportAccountKeystore

Request Type: Personal
Response Type: SingleBytes

Export account stored in this node as keystore format

QueryContract

Request Type: Query
Response Type: SingleBytes

Query a contract method

QueryContractState

Request Type: StateQuery
Response Type: StateQueryProof

Query contract state

GetPeers

Request Type: PeersParams
Response Type: PeerList

Return list of peers of this node and their state

GetVotes

Request Type: VoteParams
Response Type: VoteList

Return result of vote

GetAccountVotes

Request Type: AccountAddress
Response Type: AccountVoteInfo

Return staking, voting info for account

GetStaking

Request Type: AccountAddress
Response Type: Staking

Return staking information

GetNameInfo

Request Type: Name
Response Type: NameInfo

Return name information

ListEventStream

Request Type: FilterInfo
Response Type: Event

Returns a stream of event as they get added to the blockchain

ListEvents

Request Type: FilterInfo
Response Type: EventList

Returns list of event

GetServerInfo

Request Type: KeyParams
Response Type: ServerInfo

Returns configs and statuses of server

GetConsensusInfo

Request Type: Empty
Response Type: ConsensusInfo

Returns status of consensus and bps

ChangeMembership

Request Type: MembershipChange
Response Type: MembershipChangeReply

Add & remove member of raft cluster

GetEnterpriseConfig

Request Type: EnterpriseConfigKey
Response Type: EnterpriseConfig

Returns enterprise config

GetConfChangeProgress

Request Type: SingleBytes
Response Type: ConfChangeProgress

Return a status of changeCluster enterprise tx, queried by requestID

AccountAddress
Field Type Label Description
value bytes

AccountAndRoot
Field Type Label Description
Account bytes
Root bytes
Compressed bool

AccountVoteInfo
Field Type Label Description
staking Staking
voting VoteInfo repeated

BlockBodyPaged
Field Type Label Description
total uint32
offset uint32
size uint32
body BlockBody

BlockBodyParams
Field Type Label Description
hashornumber bytes
paging PageParams

BlockHeaderList
Field Type Label Description
blocks Block repeated

BlockMetadata
Field Type Label Description
hash bytes
header BlockHeader
txcount int32
size int64 blocksize in bytes

BlockMetadataList
Field Type Label Description
blocks BlockMetadata repeated

BlockchainStatus

BlockchainStatus is current status of blockchain

Field Type Label Description
best_block_hash bytes
best_height uint64
consensus_info string
best_chain_id_hash bytes
chain_info ChainInfo

ChainId
Field Type Label Description
magic string
public bool
mainnet bool
consensus string
version int32

ChainInfo

ChainInfo returns chain configuration

Field Type Label Description
id ChainId
bpNumber uint32
maxblocksize uint64
maxtokens bytes
stakingminimum bytes
totalstaking bytes
gasprice bytes
nameprice bytes
totalvotingpower bytes
votingreward bytes

ChainStats

ChainStats corresponds to a chain statistics report.

Field Type Label Description
report string

CommitResult
Field Type Label Description
hash bytes
error CommitStatus
detail string

CommitResultList
Field Type Label Description
results CommitResult repeated

ConfigItem
Field Type Label Description
props ConfigItem.PropsEntry repeated

ConfigItem.PropsEntry
Field Type Label Description
key string
value string

ConsensusInfo

info and bps is json string

Field Type Label Description
type string
info string
bps string repeated

Empty

EnterpriseConfig
Field Type Label Description
key string
on bool
values string repeated

EnterpriseConfigKey
Field Type Label Description
key string

EventList
Field Type Label Description
events Event repeated

ImportFormat
Field Type Label Description
wif SingleBytes
oldpass string
newpass string
keystore SingleBytes

Input
Field Type Label Description
hash bytes
address bytes repeated
value bytes
script bytes

KeyParams
Field Type Label Description
key string repeated

ListParams
Field Type Label Description
hash bytes
height uint64
size uint32
offset uint32
asc bool

Name
Field Type Label Description
name string
blockNo uint64

NameInfo
Field Type Label Description
name Name
owner bytes
destination bytes

NodeReq
Field Type Label Description
timeout bytes
component bytes

Output
Field Type Label Description
index uint32
address bytes
value bytes
script bytes

PageParams
Field Type Label Description
offset uint32
size uint32

Peer
Field Type Label Description
address PeerAddress
bestblock NewBlockNotice
state int32
hidden bool
lashCheck int64
selfpeer bool
version string
certificates AgentCertificate repeated
acceptedRole PeerRole

PeerList
Field Type Label Description
peers Peer repeated

PeersParams
Field Type Label Description
noHidden bool
showSelf bool

Personal
Field Type Label Description
passphrase string
account Account

ServerInfo
Field Type Label Description
status ServerInfo.StatusEntry repeated
config ServerInfo.ConfigEntry repeated

ServerInfo.ConfigEntry
Field Type Label Description
key string
value ConfigItem

ServerInfo.StatusEntry
Field Type Label Description
key string
value string

SingleBytes
Field Type Label Description
value bytes

SingleString
Field Type Label Description
value string

Staking
Field Type Label Description
amount bytes
when uint64

VerifyResult
Field Type Label Description
tx Tx
error VerifyStatus

Vote
Field Type Label Description
candidate bytes
amount bytes

VoteInfo
Field Type Label Description
id string
candidates string repeated
amount string

VoteList
Field Type Label Description
votes Vote repeated
id string

VoteParams
Field Type Label Description
id string
count uint32

CommitStatus
Name Number Description
TX_OK 0
TX_NONCE_TOO_LOW 1
TX_ALREADY_EXISTS 2
TX_INVALID_HASH 3
TX_INVALID_SIGN 4
TX_INVALID_FORMAT 5
TX_INSUFFICIENT_BALANCE 6
TX_HAS_SAME_NONCE 7
TX_INTERNAL_ERROR 9

VerifyStatus
Name Number Description
VERIFY_STATUS_OK 0
VERIFY_STATUS_SIGN_NOT_MATCH 1
VERIFY_STATUS_INVALID_HASH 2 TODO: not yet impl

Top

blockchain.proto

ABI
Field Type Label Description
version string
language string
functions Function repeated
state_variables StateVar repeated

AccountProof
Field Type Label Description
state State
inclusion bool
key bytes
proofKey bytes
proofVal bytes
bitmap bytes
height uint32
auditPath bytes repeated

Block
Field Type Label Description
hash bytes
header BlockHeader
body BlockBody

BlockBody
Field Type Label Description
txs Tx repeated

BlockHeader
Field Type Label Description
chainID bytes chain identifier
prevBlockHash bytes hash of previous block
blockNo uint64 block number
timestamp int64 block creation time stamp
blocksRootHash bytes hash of root of block merkle tree
txsRootHash bytes hash of root of transaction merkle tree
receiptsRootHash bytes hash of root of receipt merkle tree
confirms uint64 number of blocks this block is able to confirm
pubKey bytes block producer's public key
coinbaseAccount bytes address of account to receive fees
sign bytes block producer's signature of BlockHeader
consensus bytes consensus meta

ContractVarProof
Field Type Label Description
value bytes
inclusion bool
proofKey bytes
proofVal bytes
bitmap bytes
height uint32
auditPath bytes repeated
key bytes

Event
Field Type Label Description
contractAddress bytes
eventName string
jsonArgs string
eventIdx int32
txHash bytes
blockHash bytes
blockNo uint64
txIndex int32

FilterInfo
Field Type Label Description
contractAddress bytes
eventName string
blockfrom uint64
blockto uint64
desc bool
argFilter bytes
recentBlockCnt int32

FnArgument
Field Type Label Description
name string

Function
Field Type Label Description
name string
arguments FnArgument repeated
payable bool
view bool
fee_delegation bool

Proposal
Field Type Label Description
id string
description string
multipleChoice uint32

Query
Field Type Label Description
contractAddress bytes
queryinfo bytes

Receipt
Field Type Label Description
contractAddress bytes
status string
ret string
txHash bytes
feeUsed bytes
cumulativeFeeUsed bytes
bloom bytes
events Event repeated
blockNo uint64
blockHash bytes
txIndex int32
from bytes
to bytes
feeDelegation bool
gasUsed uint64

State
Field Type Label Description
nonce uint64
balance bytes
codeHash bytes
storageRoot bytes
sqlRecoveryPoint uint64

StateQuery
Field Type Label Description
contractAddress bytes
root bytes
compressed bool
storageKeys bytes repeated

StateQueryProof
Field Type Label Description
contractProof AccountProof
varProofs ContractVarProof repeated

StateVar
Field Type Label Description
name string
type string
len int32

Tx
Field Type Label Description
hash bytes
body TxBody

TxBody
Field Type Label Description
nonce uint64 increasing number used only once per sender account
account bytes decoded account address
recipient bytes decoded account address
amount bytes variable-length big integer
payload bytes
gasLimit uint64 maximum gas used for this transaction. 0 = no limit
gasPrice bytes variable-length big integer. currently not used
type TxType
chainIdHash bytes hash value of chain identifier in the block
sign bytes sender's signature for this TxBody

TxIdx

TxIdx specifies a transaction’s block hash and index within the block body

Field Type Label Description
blockHash bytes
idx int32

TxInBlock
Field Type Label Description
txIdx TxIdx
tx Tx

TxList
Field Type Label Description
txs Tx repeated

TxType
Name Number Description
NORMAL 0
GOVERNANCE 1
REDEPLOY 2
FEEDELEGATION 3
TRANSFER 4
CALL 5
DEPLOY 6

Top

metric.proto

Metrics
Field Type Label Description
peers PeerMetric repeated

MetricsRequest
Field Type Label Description
types MetricType repeated

PeerMetric
Field Type Label Description
peerID bytes
sumIn int64
avrIn int64
sumOut int64
avrOut int64

MetricType
Name Number Description
NOTHING 0 NOTHING should not be used.
P2P_NETWORK 1 Metric for p2p network transfer

Scalar Value Types

.proto Type Notes C++ Type Java Type Python Type
double double double float
float float float float
int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. int32 int int
int64 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. int64 long int/long
uint32 Uses variable-length encoding. uint32 int int/long
uint64 Uses variable-length encoding. uint64 long int/long
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int int
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. int64 long int/long
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 2^28. uint32 int int
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 2^56. uint64 long int/long
sfixed32 Always four bytes. int32 int int
sfixed64 Always eight bytes. int64 long int/long
bool bool boolean boolean
string A string must always contain UTF-8 encoded or 7-bit ASCII text. string String str/unicode
bytes May contain any arbitrary sequence of bytes. string ByteString str