Code Monkey home page Code Monkey logo

aergo's Introduction

Go Report Card MIT License Maintainability Test Coverage API Reference

Aergo - Distributed Trust at Scale

Official Chain Software of Aergo Protocol

We are developing the most practical and powerful platform for blockchain businesses. There are four main ideologies guiding this project:

  1. Developer-friendly
  2. Guaranteed performance
  3. Scalable architecture
  4. Connect with the world

You can see the current public network status on Aergoscan.

Official Documentation

Roadmaps

5th: Aergo Future

  • Will be updated

4th: Aergo Mainnet 2.0 (planned in 4Q, 2019)

  • DAO
  • Staking/Voting
  • Reward for Staking
  • Alpha net (SQL enabled) launched
  • Advanced performance features
  • RAFT for Private Chain
  • Authorization for Private Chain
  • 2Way Swap with Ethereum
  • 2Way Swap with Binance Dex

3rd: Aergo Mainnet (planned in March, 2019)

  • BFT-dPOS with 13 BPs
  • Aergo Scan
  • Aergo Connect

2nd: Aergo Testnet (28, Dec, 2018)

  • BFT-dPOS with Voting
    • The pre-test net has the function of agreeing blocks among the set BPs. TestNet has a function to select BP through voting.
  • Named Account
    • For user's convenience, Named Account function that can be accessed based on Name rather than Address is provided.
  • Expanded Aergo Lua
    • The Aergo Lua feature has been extended for more convenient development.
  • Advanced Client Framework
    • Provides a wallet interface that interacts with keystore and manages nonce.
    • Provides the ability to make smart contracts through interface calls.
    • Provides a contract library to issue tokens based on Aergo.
  • Hub Enterprise
    • Enterprise customers view management and monitoring of their networks as a prerequisite.
    • We provide Hub Enterprise control solution to solve this problem.
  • Merkle Bridge Verification
    • StateTrie Merkle proof verifications and delegated token transfers are now implemented in the merkle-bridge.
  • Various Smart Contract Examples
    • Provide some standard smart contracts
  • TestNet
    • Launch the Test Network to provide network for community and experment
    • We provide https://aergoscan.io.

1st: Aergo Alpha (31, Oct, 2018)

  • Consensus - BFT-dPOS (election not integrated)
    • We provide BFT by solving various problems that may occur in dpos.
  • Aergo SQL smart contract (Lua-jit)
    • It is a powerful smart contract language providing DB function.
  • Client - Ship
    • Client framework and development environment
    • Provides a package management and testing environment similar to NPM.
  • Client SDK
    • heraj (java)
    • herajs (javascript)
    • herapy (python)
  • Browser Wallet (1~2 weeks later)
    • Chrome Extension provides a coin transfer wallet.
  • Sub Project
    • Litetree
      • Improved SQLite is used to provide DB functionality in a block chain.
      • Provides higher performance through LMDB.
    • Sparse Merkle Tree
      • Provides fast, space-saving sparse merkle tree.
    • Pre-Testnet

beginning: Skeleton (31, July, 2018)

  • Platform framework
  • Stub consensus(dpos without voting)
  • Account model
  • Mempool
  • Networking - p2p/protocol
  • Cmd aergocli/aergosvr
  • Simple client API
  • Smart contract will not be released - you can see the prototype in coinstack3sp2

Key thoughts of the architecture

MVP based, Forward compatibility, Iteration

Information

Server port usages

Usage Port
gRPC 7845
P2P 7846

Installation

Prerequisites

Build

Building from Source

Contribution

Following Golang conventions TBD

License

All code is licensed under the MIT License (https://opensource.org/licenses/MIT).

aergo's People

Contributors

acktsap avatar ashen1dev avatar aspiers avatar asteroidable avatar chris2nd avatar eve2adam avatar everjs78 avatar graup avatar hanlsin avatar hayarobi avatar jinuk-shin avatar kjunu avatar kroggen avatar mlogue76 avatar paouvrard avatar qcollapse avatar rabbitprincess avatar sg31park avatar sunpuyo avatar ultrafellen avatar waltzforzizi avatar xiaomingloveqinger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aergo's Issues

Add block size to API

Please add a field size to both BlockMetadata and Block, included in the return types of ListBlockMetadata, ListBlockMetadataStream, GetBlockMetadata, GetBlock, ListBlockStream.

The field should contain the value returned by block.Size() or similar.

Question: The referenced method Size() seems to loop over all transactions, I wonder if that is very efficient. Maybe there is a more efficient way?

brick new command: timestamp

Proposal

Create a new command on brick: timestamp

It can receive 2 types of input:

  • A unix timestamp
  • An increment in time, in seconds

Example to specify a timestamp:

timestamp 1659873948

Example to increment the current timestamp:

timestamp +30

When this command is used, the behavior of brick is to use the given timestamp instead of reading the clock from the device.

This command is important for testings that depend on the result of system.getTimestamp() function.

brick doesn't make it obvious where errors are coming from

When brick fails due to errors in Lua, it outputs errors like this:

ERR execution fail error="[string \"0cca71fcf76698693693c6edc703ea42c82494bfafa1b6a14100aabaa7a1afb08f\"]:27: expected string but got nil" cmd=call module=brick

This looks like a bug; presumably the [string \"...\"] bit is supposed to be a line number and 27 is the column number? This makes it a lot harder to debug problems in Lua.

Plugin architecture

This is a proposal to implement a plugin architecture for aergosvr.

Why plugins?

  • non-essential features that not every user needs and private logic
  • don't bloat the core codebase → easier to maintain, smaller binary
  • more direct access than external code has
  • can reuse types and utils

What features does a plugin need?

Apart from registering plugins, we will need some additional features in the core.

  • register and init
  • high level:
    • subscribe to events, e.g. new block (also, contract events after they get implemented)
    • request data, e.g. hub.RequestFuture(message.ChainSvc, &message.GetBlockByNo{BlockNo: i})
  • low level:
    • actor middleware, e.g. for tracing

Plugins could just be actors, that way they could even change some existing behaviors instead of just adding features.

Example use cases

  • Database indexer: index transactions into external db
  • Monitoring, logging, reacting to events
  • Debugging, tracing

Implementation ideas / pseudocode

Go supports plugins. They can be built separately and injected at runtime.

There a working example for a feature that should be a plugin here: #13

config.toml

enableplugins = EsIndexerPlugin, FooBarPlugin

[EsIndexerPlugin]
pluginpath = ./path/to/aergoPluginEsIndexer.so
databaseurl = 127.0.0.1:9200
EsIndexerPlugin/plugin.go
package main

func init() {
  component = NewEsIndexer()
  hub := GetDefaultComponentHub()
  hub.Register(component)
  hub.Subscribe(event.NewBlock, component)
}


EsIndexerPlugin/EsIndexer.go
import (
	"github.com/aergoio/aergo/config"
	"github.com/aergoio/aergo/message"
	"github.com/aergoio/aergo/pkg/component"
	"github.com/aergoio/aergo/types"
)
type EsIndexer struct {
  conf *config.Config
  *component.BaseComponent
  hub *component.ComponentHub
}

EsIndexer.Receive
  switch msg := context.Message().(type) {
    case actor.Started:
      db = openDatabaseConnection(conf.EsIndexerPlugin.databaseurl)
      ...
      blockHeight = GetChainAccessor().GetBestBlock().GetHeader().GetBlockNo()
      ...

    case event.NewBlock:
      // sync new block
      db.index('block', msg.hash, msg.metadata)
      for tx in msg.txsList:
        db.index('tx', tx.hash, tx.metadata)
  }

Bad error message on contract.call

This line has a nil in the first argument:

local value = contract.call(nil, "get_value")

It fails with this error:

bad argument #2 to 'call' (string expected, got nil)
             /\

It should be #1 instead of #2

EC signature verification on Smart Contracts

Proposal: A function to verify EC signatures on smart contracts

Maybe supporting more than one curve, and accepting the curve id as an argument. But supporting secp256k1 would be a good start.

It could also automatically identify the signature serialization format by the size.

Example usage:

  ec.verify(ec.secp256k1, data, sig, pubkey)

where data is raw data. The function should calculate the hash.

[contract] system down: 'contract.call' with unparseable 'address'

panic: runtime error: slice bounds out of range

goroutine 217 [running, locked to thread]:
github.com/aergoio/aergo/vendor/github.com/anaskhan96/base58check.Decode(0x0, 0x0, 0xc0001cd0f8, 0x47437eb, 0x8840448, 0x0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/vendor/github.com/anaskhan96/base58check/base58check.go:72 +0x4c1
github.com/aergoio/aergo/types.DecodeAddress(0x0, 0x0, 0x0, 0x4073c82, 0x0, 0xc0000ba008, 0xc0001cd198)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/types/account.go:52 +0x39
github.com/aergoio/aergo/contract.LuaCallContract(0x8840378, 0xc00b4f5364, 0x8840448, 0x884a438, 0x6208130, 0x0, 0x0, 0x0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/contract/vm_callback.go:90 +0xee
github.com/aergoio/aergo/contract._cgoexpwrap_82f79b750651_LuaCallContract(0x8840378, 0xc00b4f5364, 0x8840448, 0x884a438, 0x6208130, 0x0, 0x0, 0x6208130)
        _cgo_gotypes.go:1522 +0x6a
github.com/aergoio/aergo/contract._Cfunc_vm_pcall(0x8840378, 0x1, 0xc0000ac7bc, 0x0)
        _cgo_gotypes.go:1347 +0x4e
github.com/aergoio/aergo/contract.(*Executor).call.func3(0x8840378, 0xc000000001, 0xc0000ac7bc, 0xc0001c7380)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/contract/vm.go:269 +0x68
github.com/aergoio/aergo/contract.(*Executor).call(0xc00b4cf2c0, 0xc00b4fee10, 0x0, 0xc00b4f5300)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/contract/vm.go:269 +0xf5
github.com/aergoio/aergo/contract.Call(0xc00b4cf220, 0xc00b59ef60, 0x28, 0x30, 0xc00b59ef30, 0x21, 0x30, 0xc00b4f5300, 0x0, 0x0, ...)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/contract/vm.go:419 +0x1d0
github.com/aergoio/aergo/contract.Execute(0xc0002f0990, 0xc000141c80, 0x2b35, 0x156ff91232cd4f48, 0xc00b53c360, 0xc00b53c3c0, 0x0, 0x80571d02481c690f, 0x66dc9834156bbadf, 0xb75bf11e75527a7, ...)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/contract/contract.go:103 +0x9d1
github.com/aergoio/aergo/chain.executeTx(0xc0002f0990, 0xc000141c80, 0x2b35, 0x156ff91232cd4f48, 0x0, 0x479a165, 0xc0001361b0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/chain/chainhandle.go:558 +0x1146
github.com/aergoio/aergo/chain.NewTxExecutor.func1(0xc0002f0990, 0xc000141c80, 0x0, 0x0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/chain/chainhandle.go:430 +0x9c
github.com/aergoio/aergo/consensus/impl/sbp.(*txExec).Apply(0xc0000ba538, 0xc0002f0990, 0xc000141c80, 0x0, 0x0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/impl/sbp/sbp.go:42 +0x3d
github.com/aergoio/aergo/consensus/chain.NewCompTxOp.func1(0xc0002f0990, 0xc000141c80, 0xc0001d8800, 0x10)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/chain/tx.go:63 +0x75
github.com/aergoio/aergo/consensus/chain.TxOpFn.Apply(0xc00b5d23c0, 0xc0002f0990, 0xc000141c80, 0x0, 0xc0001cdce0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/chain/tx.go:55 +0x3a
github.com/aergoio/aergo/consensus/chain.NewCompTxOp.func1(0xc0002f0990, 0xc000141c80, 0x4ce17c0, 0xc00b50a7a0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/chain/tx.go:63 +0x75
github.com/aergoio/aergo/consensus/chain.TxOpFn.Apply(0xc00b50a7a0, 0xc0002f0990, 0xc000141c80, 0x5043960, 0xc00b50a7a0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/chain/tx.go:55 +0x3a
github.com/aergoio/aergo/consensus/chain.GatherTXs(0x5041d00, 0xc0001fc780, 0xc0002f0990, 0x5043960, 0xc00b5d23c0, 0x100000, 0x0, 0x0, 0x0, 0x0, ...)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/chain/tx.go:132 +0x32c
github.com/aergoio/aergo/consensus/chain.GenerateBlock(0x5041d00, 0xc0001fc780, 0xc0000c99a0, 0xc0002f0990, 0x5043960, 0xc00b5d23c0, 0x156ff91232cd4f48, 0xc0000c99a0, 0x0, 0x0)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/chain/block.go:67 +0x80
github.com/aergoio/aergo/consensus/impl/sbp.(*SimpleBlockFactory).Start(0xc0001fc780)
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/impl/sbp/sbp.go:170 +0x315
created by github.com/aergoio/aergo/consensus.Start
        /Users/yp/work/blocko/go/src/github.com/aergoio/aergo/consensus/consensus.go:99 +0x74

brick expected_query_result assumes deterministic ordering of table keys

brick allows querying a smart contract and checking for an expected result:

query <contract_name> <func_name> <query_json_str> [expected_query_result] [expected_error]

(As mentioned in #104 it would be great if this was also possible for call, but that is a separate issue.)

However, if the query returns a Lua table, then the ordering of the keys is not deterministic, so it's possible to get a false error like:

ERR execution fail error="expected: {\"a\":1,\"b\":2}, but got: {\"b\":2,\"a\":1}" cmd=query module=brick

Of course it's possible to modify the smart contract to convert this to a lua "list" (ordered table), e.g.

{
    [1]={"key":"a","value":1},
    [2]={"key":"a","value":2},
}

and then compare against that, but this is not just a painful developer experience but might also go against the requirements of what the ABI needs to expose to the contract caller. So it would be nicer if there was an easier way to do the comparison which was built-in.

[contract] state.map() is not working with lua table type.

state.var {
  -- global map
  alpha = state.map(),
  beta = state.map(),
}

function constructor()
  system.print("in constructor")

  system.print("<<ALPHA>>")
  alpha["dict"] = {}
  -- with local variable
  local d = alpha["dict"]
  d["a"] = "A"
  system.print("NOT WORKING: " .. json:encode(alpha["dict"]))
  -- variable direct
  alpha["dict"]["b"] = "B"
  system.print("NOT WORKING: " .. json:encode(alpha["dict"]))

  system.print("<<BETA>>")
  beta["dict"] = {
    value=""
  }
  -- with local variable
  local d2 = beta["dict"]
  d2["a"] = "A"
  system.print("NOT WORKING: " .. json:encode(beta["dict"]))
  -- predefined variable with local variable
  d2["value"] = "v0"
  system.print("NOT WORKING: " .. json:encode(beta["dict"]))
  -- variable direct
  beta["dict"]["b"] = "B"
  system.print("NOT WORKING: " .. json:encode(beta["dict"]))
  -- predefined variable direct
  beta["dict"]["value"] = "v1"
  system.print("NOT WORKING: " .. json:encode(beta["dict"]))

  system.print("out constructor")
end

function abc()
  system.print("in function")

  system.print("<<ALPHA>>")
  -- with local variable
  local d = alpha["dict"]
  d["c"] = "C"
  system.print("NOT WORKING: " .. json:encode(alpha["dict"]))
  -- variable direct
  alpha["dict"]["d"] = "D"
  system.print("NOT WORKING: " .. json:encode(alpha["dict"]))

  system.print("<<BETA>>")
  -- with local variable
  local d = beta["dict"]
  d["a"] = "A"
  system.print("NOT WORKING: " .. json:encode(beta["dict"]))
  -- predefined variable with local variable
  d["value"] = "v2"
  system.print("NOT WORKING: " .. json:encode(beta["dict"]))
  -- variable direct
  beta["dict"]["b"] = "B"
  system.print("NOT WORKING: " .. json:encode(beta["dict"]))
  -- predefined variable direct
  beta["dict"]["value"] = "v3"
  system.print("NOT WORKING: " .. json:encode(beta["dict"]))

  system.print("out function")
end

abi.register(abc)

Cannot use the lua table with state.map?

Below is the log.

{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"in constructor\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"<<ALPHA>>\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"NOT WORKING: {}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"NOT WORKING: {}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"<<BETA>>\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"NOT WORKING: {\\\"value\\\":\\\"\\\"}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"NOT WORKING: {\\\"value\\\":\\\"\\\"}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"NOT WORKING: {\\\"value\\\":\\\"\\\"}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"NOT WORKING: {\\\"value\\\":\\\"\\\"}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:36Z","message":"\"out constructor\""}

........

{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"in function\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"<<ALPHA>>\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"NOT WORKING: {}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"NOT WORKING: {}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"<<BETA>>\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"NOT WORKING: {\\\"value\\\":\\\"\\\"}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"NOT WORKING: {\\\"value\\\":\\\"\\\"}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"NOT WORKING: {\\\"value\\\":\\\"\\\"}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"NOT WORKING: {\\\"value\\\":\\\"\\\"}\""}
{"level":"info","module":"statesql","Contract SystemPrint":"Amh5sFTPqhqUUomSRtbyFAtX2CE6EnkgSNyH1Zz8Zymi2vNo3sA2","time":"2018-12-13T21:05:39Z","message":"\"out function\""}

brick: include debug mode in the released binaries

Is it possible to run smart contracts in debug mode via the command line argument instead of having to recompile brick?

It would be better if we could just type:

brick --debug

So the same binary would support both release and debug modes.

If for some technical reason it is not possible to make this modification, then we could include a debug mode binary, named like brick-debug, in the releases

aergocli contract deploy missing docs for optional deployment arguments

aergocli can be used to deploy a contract with arguments:

aergocli contract deploy <creator> <bcfile> <abifile> [args]

However AFAICS the final optional args parameter is not documented anywhere; I had to hunt for it in the source code:

_, _ = fmt.Fprint(os.Stderr, "Usage: aergocli contract deploy <creator> <bcfile> <abifile> [args]")

For some reason, I can't even get aergocli to print this usage message even if I try. And the CLI output doesn't mention it either:

$ aergocli contract deploy
Error: requires at least 1 arg(s), only received 0
Usage:
  aergocli contract deploy [flags] --payload 'payload string' creator
  aergocli contract deploy [flags] creator bcfile abifile

Flags:
      --amount string     setting amount (default "0")
  -h, --help              help for deploy
      --password string   Password
      --path string       Path to account data directory (default "$HOME/.aergo/data")
      --payload string    result of compiling a contract
  -r, --redeploy string   redeploy the contract

Global Flags:
      --config string          config file (default is cliconfig.toml)
  -g, --gaslimit uint          Gas limit
      --home string            aergo cli home path
  -H, --host string            Host address to aergo server (default "localhost")
  -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

So I think it needs to be added to the docs as well as this usage text.

Actually, aergocli contract deploy is not even documented at all at https://docs.aergo.io/en/latest/tools/aergocli.html#usage

New transaction type for simple payload

Proposal

The system could have a new transaction type for transactions that just want to register a payload (like a hash) on the blockchain.

Currently this is achieved using type NORMAL, amount = 0 and filling the recipient with some address.

The proposal to use something like type: PAYLOAD and the recipient can be empty in this case.

Using the proposed approach (new type):

  • Easier to use (for the user)
  • Decreases the size of the transaction (no recipient)
  • Defines a clear intention of the user and then the engine can issue correct checking and error messages

`Lua-JIT` as a blockchain virtual machine

Hi

I saw the use of Lua-JIT as a virtual machine in the documentation, so how do you solve the problem that the gas consumption between each node in the JIT environment may be inconsistent?

Thanks.

brick: segmentation fault

The commands executed:

$ ./brick 
0> inject me 1000000
  INF inject an account successfully cmd=inject module=brick
1> deploy me 0 test ./test.lua
  INF deploy a smart contract successfully cmd=deploy events=[] gas=120394 module=brick
2> query test crc32 `["Testing"]`
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x47e4de0]

goroutine 1 [running]:
github.com/aergoio/aergo/contract.newVmContextQuery(0xc0001c2ea0, 0x0, 0x0, 0xc009652690, 0x21, 0x30, 0xc00018aba0, 0x1, 0x0, 0x0, ...)
	/Users/travis/gopath/src/github.com/aergoio/aergo/contract/vm.go:205 +0x80
github.com/aergoio/aergo/contract.Query(0xc009652690, 0x21, 0x30, 0xc0001c2ea0, 0x0, 0x0, 0xc00018aba0, 0xc0096526c0, 0x23, 0x30, ...)
	/Users/travis/gopath/src/github.com/aergoio/aergo/contract/vm.go:1102 +0x1b4
github.com/aergoio/aergo/contract.(*DummyChain).QueryOnly(0xc000292160, 0xc0001c0f80, 0x4, 0xc0096525d0, 0x23, 0x0, 0x0, 0xc0001c0f8c, 0xb, 0x0, ...)
	/Users/travis/gopath/src/github.com/aergoio/aergo/contract/vm_dummy.go:660 +0x27d
github.com/aergoio/aergo/cmd/brick/exec.(*queryContract).Run(0x5527970, 0xc0001c0f80, 0x18, 0x0, 0x0, 0x400dd19, 0xc0093b7b70, 0x4043c9c, 0x20, 0x4a34e60, ...)
	/Users/travis/gopath/src/github.com/aergoio/aergo/cmd/brick/exec/queryContract.go:84 +0x1ee
github.com/aergoio/aergo/cmd/brick/exec.Execute(0xc0001c0e80, 0x5, 0xc0001c0f80, 0x18)
	/Users/travis/gopath/src/github.com/aergoio/aergo/cmd/brick/exec/exec.go:81 +0x1fe
github.com/aergoio/aergo/cmd/brick/exec.Broker(0xc0001c0e80, 0x1e)
	/Users/travis/gopath/src/github.com/aergoio/aergo/cmd/brick/exec/exec.go:61 +0x7e
github.com/c-bata/go-prompt.(*Prompt).Run(0xc00001a000)
	/Users/travis/gopath/pkg/mod/github.com/c-bata/[email protected]/prompt.go:85 +0x7f2
main.main()
	/Users/travis/gopath/src/github.com/aergoio/aergo/cmd/brick/brick.go:92 +0x257

The smart contract used:

require("bit")

local CRC32 = {
  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 
  0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 
  0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 
  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 
  0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 
  0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 
  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 
  0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 
  0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 
  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 
  0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 
  0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 
  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 
  0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 
  0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 
  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 
  0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 
  0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 
  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 
  0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 
  0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 
  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 
  0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 
  0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 
  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 
  0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 
  0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 
  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 
  0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 
  0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 
  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 
  0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 
  0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 
  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 
  0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 
  0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 
  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 
  0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 
  0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 
  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 
  0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 
  0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 
  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 
  0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 
  0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 
  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 
  0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 
  0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 
  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 
  0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 
  0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 
  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 
  0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 
  0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 
  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 
  0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 
  0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 
  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 
  0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 
  0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 
  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 
  0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 
  0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 
  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
}

local xor = bit.bxor
local lshift = bit.lshift
local rshift = bit.rshift
local band = bit.band

function crc32(str)
  str = tostring(str)
  local count = string.len(str)
  local crc = 2 ^ 32 - 1
  local i = 1

  while count > 0 do
    local byte = string.byte(str, i)
    crc = xor(rshift(crc, 8), CRC32[xor(band(crc, 0xFF), byte) + 1])
    i = i + 1
    count = count - 1
  end
  crc = xor(crc, 0xFFFFFFFF)
  -- dirty hack for bitop return number < 0
  if crc < 0 then crc = crc + 2 ^ 32 end

  return crc
end

abi.register(crc32)

Deprecate transaction type NORMAL

Proposal:

  • Put in the documentation that the transaction type NORMAL is deprecated and should be avoided
  • Update all our client applications and wrappers (hera, libaergo...) to not use NORMAL transactions

The backwards compatibility brings many problems, so lets at least decrease the use of this type of transactions. Then later (some years?) we can remove support for them.

For simple payload check #144

brick doesn't provide db module

The brick README says:

  • supports state db, sql

but if I try to test a contract which references db, I get:

ERR execution fail error="[string \"0cca71fcf76698693693c6edc703ea42c82494bfafa1b6a14100aabaa7a1afb08f\"]:0: attempt to index global 'db' (a nil value)" cmd=deploy module=brick

I looked into the source, and I found:

chain, err := contract.LoadDummyChain(contract.OnPubNet)

which calls OnPubnet which sets PubNet to true:

aergo/contract/vm_dummy.go

Lines 677 to 684 in 87a379a

func OnPubNet(dc *DummyChain) {
flushLState := func() {
for i := 0; i <= lStateMaxSize; i++ {
s := getLState()
freeLState(s)
}
}
PubNet = true

This eventually prevents the db module from being loaded into the Lua environment:

aergo/contract/vm.c

Lines 37 to 39 in 87a379a

if (!isPublic()) {
luaopen_db(L);
}

I proved this theory by changing

	chain, err := contract.LoadDummyChain(contract.OnPubNet)

to:

	chain, err := contract.LoadDummyChain()

and recompiling, and then the error goes away.

As a side note, this suggests that brick is missing automated test coverage to ensure that it is correctly providing the built-in contract modules. I might file a separate issue for that.

Event and filtering system (high-level API)

We need an event system, not just for plugins like in #14, but also as general API so that clients can react without needing to scan all transactions.

In Go

How about making a system similar to the actor messages with pub/sub? Maybe a component could
Subscribe(events.Block)
and then

func (ns *SomeComponent) Receive(context actor.Context) {
    switch msg := context.Message().(type) {
        case *events.Block:
            ns.HandleBlock(msg.Block);
        default:
    }
}

For contract events, you probably want to subscribe to something like Ethereum-style topics.
SubscribeWithFilter(events.Transaction, { To: someAddress, Topic: someTopic } )

This is how it could look in Go, but more importantly this should also be accessible over GRPC, ideally supporting both streams for real-time and searching in the past.

End-to-end example for streams

Herajs

contract.events.Deposit({from: someAddress}).on('transaction', (tx) => { console.log(tx); });

GRPC

wss://127.0.0.1:7845/types.AergoRPCService/EventsStream
event=...   from=someAddress

Aergosvr

SubscribeWithFilter(events.Transaction, { To: someAddress, Topic: someTopic } )
Receive: 
    stream.Send(transaction)

(Need to think about performance here, what if there are a lot of clients connecting to the server at once?)

Filtering

Is there any problem with just implementing Ethereum-like filters? (Store logs in tx receipts and have bloom filters to quickly search for topics)

Some related discussion about topics/filters:

Account names - length and characters

Proposals

  1. Allow account names with less than 12 characters (the engine can pad up to 12 characters when making a lookup, if needed)
  2. Allow . or - or _ as word separator
  3. Also, if possible, allow names with more chars, like 20 or 30
  4. Allow to remove/unset a name. This also allows other user to use the name later
  5. Allow to set a name to a smart contract in a single call (second argument to v1createName)

Smart contracts: convert between address and public key

Suggestion for smart contracts: a function (or 2) to convert between an aergo address and the associated public key

address <-> pubkey

This is useful when we have content that was signed outside of Aergo (other networks or apps) and so we have only the public key, but we need the corresponding aergo address to verify the signature

brick should allow bypassing of quoting of log lines

When debugging via brick, it can be extremely helpful to add logging of multiline statements to the smart contract. For example maybe we want to see a stack trace at the current execution point:

system.print(debug.traceback())

But currently this results in something like this very long line:

INF "stack traceback:\u000a\u0009[string \"0cca71fcf76698693693c6edc703ea42c82494bfafa1b6a14100aabaa7a1afb08f\"]:773: in function 'f'\u000a\u0009[string \"0cca71fcf76698693693c6edc703ea42c82494bfafa1b6a14100aabaa7a1afb08f\"]:49: in function 'cancel'\u000a\u0009[string \"0cca71fcf76698693693c6edc703ea42c82494bfafa1b6a14100aabaa7a1afb08f\"]:794: in function <[string \"0cca71fcf76698693693c6edc703ea42c82494bfafa1b6a14100aabaa7a1afb08f\"]:787>\u000a\u0009[C]: at ?" Contract SystemPrint=AmhUpxjiT8KhQXX6N3QmZMwZJ4FoDzbwFogxnBNNF4SHs1btauXx module=contract 

Even if #113 is fixed, the fact that many characters are escaped makes this very hard to understand. The biggest problem is that newlines are rendered as \u000a, so it's really difficult to visually parse the stacktrace. But it would also be helpful if other characters like " were also not escaped.

I understand that this change would break the convention of one logging event per line, and maybe on a production blockchain that is unacceptable. But in brick when the emphasis is on testing and debugging it is more helpful. So I suggest either we change this for brick, or at least add a new option to brick which disables quoting.

Proposal: Composable Transactions

COMPOSABLE TRANSACTIONS

It is a method to call many contracts in a single transaction.

All the process is atomic. If some of the calls fail, all the transaction is rolled back.

It is used in cases where the user desires to process many things at the same time atomically.

Use Cases

1: Purchase airplane tickets and hotel reservation from different contracts, only if both succeed. If some of the purchases fail, the other is reverted (the user does not want one of them if the other is not acquired).

2: Swap token and buy something with it. If the purchase fails, the user does not want that token.

3: Swap tokens using a split route. If one of the routes fail, revert the other(s). Also check the minimum output in the transaction itself (by adding individual outputs) and revert if not satisfied.

4: Swap to an exact amount of the output token when using a multi-hop route, by either: a. doing a reverse swap of the surplus amount, or b. querying a contract for the right amount to send (between approved range) in the same transaction.

5: Add liquidity to a pool (2 tokens) in a single transaction

6: Trustless swap or purchase - check if the purchased token was received, otherwise revert the transaction

7: Transferring tokens (fungible and/or non-fungible) to multiple recipients at the same time

8: Transferring different tokens (fungible and/or non-fungible) to one recipient in a single transaction

9: Mint many non-fungible tokens (NFTs) in a single transaction

10: Approve contract B to use contract A (token) and then call a function on contract B that handles the resources on contract A (eg: approve and swap, approve and add liquidity) on a single transaction

11: Approve, use resource, and remove approval on a single transaction, with the guarantee that no other operation would happen in between while the resource/token is approved to contract B

Implementation

It may be possible to implement it with backwards compatibility with existing transactions, by adding a new transaction type:

type: COMPOSABLE_CALL
recipient: <empty>
amount: 0

And then the call information is stored in the payload in JSON format.

It would be a list of calls processed in order

[
  <call 1>
  <call 2>
  <call 3>
]

Each call containing the contract address, function to call, and arguments:

[
  [contract, function, arg1, arg2, arg3...],
  [contract, function, arg1, arg2, arg3...],
  [contract, function, arg1, arg2, arg3...]
]

Example uses:

Buy 2 products or services on a single transaction:

[
  ["<token 1>","transfer","<amount 1>","<shop 1>","buy","product 1"],
  ["<token 2>","transfer","<amount 2>","<shop 2>","buy","product 2"],
]

Acquire token2 via swap and buy a product/service with it:

[
  ["<token 1>","transfer","<amount 1>","<swap pair>","swap",{"exact_output":"<amount 2>"}],
  ["<token 2>","transfer","<amount 2>","<shop>","buy","product"],
]

Add liquidity to a swap pair:

[
  ["<token 1>","transfer","<amount 1>","<swap_pair>","store_token"],
  ["<token 2>","transfer","<amount 2>","<swap_pair>","add_liquidity"],
]

[V2] need to check TxType_TRANSFER?

I have a question for checking TX type in the below code of V2.

if (txBody.Type != types.TxType_NORMAL && txBody.Type != types.TxType_FEEDELEGATION) || len(recipient) == 0 {

Token transfer TX is also changed to TxType_TRANSFER, right?

If yes, should the above code check TRANSFER too?

Why does it need to keep NORMAL, btw?

Accept signatures serialized in compact format

Aergo could accept signatures serialized in compact format (as well as the currently supported DER format)

The engine can detect that the signature is serialized in compact format by its size: 64 bytes

This could be implemented in 2 places:

  • transactions
  • crypto.ecverify

Proposal for smart-contract address generation

Currently a smart-contract 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 : https://docs.aergo.io/en/v0.12/specs/addresses.html

In a near futur, users will be able to hold assets on different aergo sidechains and will most likely use the same private key to interact on those different chains.

Therefore a user might end up creating contracts with the same addresses on different chains.

Apart from possible confusion, and mistakes due to calling a contract on the wrong sidechain, other applications like multisigs and merkle-bridge would benefit from contract addresses being unique accross sidechains.

Currently, the merkle-bridge POC and standard-token use a custom contract id to prevent replay accross sidechains :
https://github.com/aergoio/merkle-bridge/blob/837e78d7bc87efaafec09b37ce96217efe505667/contracts/merkle_bridge.lua#L70

https://github.com/aergoio/merkle-bridge/blob/837e78d7bc87efaafec09b37ce96217efe505667/contracts/standard_token.lua#L45

But it would be better for applications that make use of signed nonces (multisigs, state channels ...) not to have to worry about creating a custom contract id for sidechain replay protection.

We can instead generate the contract address from Hash(sender_address + nonce + chainIDHash).

Feature/aergocli cluster remove bug

SIGSEGV occurred after attempting to remove the cluster with aergocli.

aergo_bp1@aergo_bp1:/blockchain/raft_local$ ./aergocli getconsensusinfo | jq
{
  "Type": "raft",
  "Info": {
    "Leader": "local3",
    "Total": 3,
    "Name": "local1",
    "RaftId": "87adff9d674ad15a",
    "Status": {
      "id": "87adff9d674ad15a",
      "term": 7,
      "vote": "5019815a18b608cb",
      "commit": 87646,
      "lead": "5019815a18b608cb",
      "raftState": "StateFollower",
      "applied": 87646,
      "progress": {},
      "leadtransferee": "0"
    }
  },
  "Bps": [
    {
      "Name": "local1",
      "RaftID": "87adff9d674ad15a",
      "PeerID": "16Uiu2HAkzuBCPFPQuBjHq5g8KDrACXdZ8NC6qHKJ15QCQM6bWeSf",
      "Addr": "/ip4/192.168.1.231/tcp/7846"
    },
    {
      "Name": "local2",
      "RaftID": "b4eb7ceaeda0a971",
      "PeerID": "16Uiu2HAmRM3cvKN1HVGMbPeCCbXQfAiF9dH5ZXLvsh7MEuLjjqXF",
      "Addr": "/ip4/192.168.1.242/tcp/7846"
    },
    {
      "Name": "local3",
      "RaftID": "5019815a18b608cb",
      "PeerID": "16Uiu2HAm7xYuxydz39E6jq4mG5p9ttzCuPKwsvdmsNN5nPGhLeTc",
      "Addr": "/ip4/192.168.1.243/tcp/7846"
    }
  ]
}

./aergocli cluster remove --nodeid 5019815a18b608cb
Failed to remove member: rpc error: code = Unknown desc = failed to change membership: this node is not leader
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xb8eb4a]
goroutine 1 [running]:
github.com/aergoio/aergo/cmd/aergocli/cmd.glob..func11(0x17eaa60, 0xc0002b1440, 0x0, 0x2)
        /home/travis/gopath/src/github.com/aergoio/aergo/cmd/aergocli/cmd/cluster.go:91 +0x12a
github.com/spf13/cobra.(*Command).execute(0x17eaa60, 0xc0002b13e0, 0x2, 0x2, 0x17eaa60, 0xc0002b13e0)
        /home/travis/gopath/pkg/mod/github.com/spf13/[email protected]/command.go:830 +0x2ae
github.com/spf13/cobra.(*Command).ExecuteC(0x17ecfe0, 0x0, 0x0, 0xb91e7d)
        /home/travis/gopath/pkg/mod/github.com/spf13/[email protected]/command.go:914 +0x2fc
github.com/spf13/cobra.(*Command).Execute(...)
        /home/travis/gopath/pkg/mod/github.com/spf13/[email protected]/command.go:864
github.com/aergoio/aergo/cmd/aergocli/cmd.Execute()
        /home/travis/gopath/src/github.com/aergoio/aergo/cmd/aergocli/cmd/root.go:114 +0x3d
main.main()
        /home/travis/gopath/src/github.com/aergoio/aergo/cmd/aergocli/aergocli.go:14 +0x20

And is a non-admin user a cluster-removable command?

./aergocli cluster remove --help
Remove raft node with given node id from cluster. This command can only be used for raft consensus.
Usage:
  aergocli cluster remove [flags]

Flags:
  -h, --help            help for remove
      --nodeid string   node id to remove to the cluster
[ADMIN EMPTY]


I have 4 questions about using cli

I have 4 questions about using cli

  1. How to send a sum aego transfer
  2. How to check the aergo balance
  3. How to send a contract tansfer
  4. How to check the contract balance

Add TODOs Badge to README

Hi there! I wanted to propose adding the following badge to the README to indicate how many TODO comments are in this codebase:

TODOs

The badge links to tickgit.com which is a free service that indexes and displays TODO comments in public github repos. It can help surface latent work and be a way for contributors to find areas of code to improve, that might not be otherwise documented.

The markdown is:

[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/aergoio/aergo)](https://www.tickgit.com/browse?repo=github.com/aergoio/aergo)

Thanks for considering, feel free to close this issue if it's not appropriate or you prefer not to!

(full disclosure, I am the creator/maintainer of tickgit)

Checkings on transaction processing

If we make a transaction with type CALL without payload and to an address that there is no smart contract the engine accepts the transaction.

Example:

https://testnet.aergoscan.io/transaction/CQPK53ftHwgEC8bRBce3vPHvnEZgBJquFXBe3Cv16Vo7/

Notice that the destination account is not a contract and the payload is empty.

If the transaction is a CALL or QUERY then the engine should make these 2 verifications:

  • The destination address must be a smart contract
  • The transaction must have a valid payload (empty payload calls the default function)

Better error message on transfer to smart contract

When making a simple transfer to a smart contract by using a transaction of type TRANSFER:

The transfer fails (as expected) if the contract does not have a payable and default function.

But the error message is strange as the engine treats the transaction of type TRANSFER as a transaction of type CALL:

https://testnet.aergoscan.io/transaction/Fu7eCFB6BnA7xKtuhYjtGkHfzBfZUah7p7iCeCpeqbXs?receipt=json&payload=formatted

"result": "not found function: "

Proposal

If the transaction is of type TRANSFER then the engine:

  • Could return a proper error message, like "the contract does not have a default payable function"
  • Could ignore the payload (maybe)

raft consensus breaks with "tx invalid chain id hash" when skipempty=true

I switched my local 3-node network from dpos to raft, reset all the data, created new genesis blocks etc., and then I found that I could not make any new transactions. The leader node shows this over and over in the logs:

2020-02-15 23:40:23 UTC INF go/aergo/mempool/mempool.go:168 > mempool metrics acc=1 len=1 module=mempool orphan=0
2020-02-15 23:40:24 UTC ERR go/aergo/chain/chainhandle.go:638 > tx failed error="tx invalid chain id hash" hash=33XATf6zr6u7RWXNFcGG7w9mzV7TzNWUGEH2nWYEr1yk module=chain
2020-02-15 23:40:24 UTC INF go/aergo/consensus/chain/tx.go:129 > transactions collected candidates=1 collected=0 module=consensus
2020-02-15 23:40:24 UTC ERR go/aergo/consensus/impl/raftv2/blockfactory.go:420 > failed to generate block error="no transactions in block" module=raft
2020-02-15 23:40:24 UTC INF go/aergo/consensus/impl/raftv2/blockfactory.go:557 > reset previous work of block factory module=raft prev proposed=proposed:empty
2020-02-15 23:40:24 UTC INF go/aergo/mempool/mempool.go:168 > mempool metrics acc=1 len=1 module=mempool orphan=0
2020-02-15 23:40:25 UTC ERR go/aergo/chain/chainhandle.go:638 > tx failed error="tx invalid chain id hash" hash=33XATf6zr6u7RWXNFcGG7w9mzV7TzNWUGEH2nWYEr1yk module=chain
2020-02-15 23:40:25 UTC INF go/aergo/consensus/chain/tx.go:129 > transactions collected candidates=1 collected=0 module=consensus
2020-02-15 23:40:25 UTC ERR go/aergo/consensus/impl/raftv2/blockfactory.go:420 > failed to generate block error="no transactions in block" module=raft
2020-02-15 23:40:25 UTC INF go/aergo/consensus/impl/raftv2/blockfactory.go:557 > reset previous work of block factory module=raft prev proposed=proposed:empty
2020-02-15 23:40:25 UTC INF go/aergo/mempool/mempool.go:168 > mempool metrics acc=1 len=1 module=mempool orphan=0
2020-02-15 23:40:26 UTC ERR go/aergo/chain/chainhandle.go:638 > tx failed error="tx invalid chain id hash" hash=33XATf6zr6u7RWXNFcGG7w9mzV7TzNWUGEH2nWYEr1yk module=chain

However when I removed skipempty=true from [consensus.raft] in config.toml then it started working again.

brick: display required amount for deploy

brick could inform the amount required for deploy, or this could be informed on the README.

The value 100 as shown in the README is not sufficient.

$ ./brick 
0> inject me 100
  INF inject an account successfully cmd=inject module=brick
1> getstate me 
  INF AmgExqUu6J4Za8VjyWMJANxoRaUvwgngGQJgemHgwWvuRSEd3wnE = 100 cmd=getstate module=brick
1> deploy me 0 test ./test.lua
  ERR execution fail error="not enough balance" cmd=deploy module=brick

Testnet not syncing

Hey guys , another issue , when i am sync the testnet , this is what i got , just stay like this forever

> ./aergosvr --home /Users/clark/workspace/golang/src/github.com/aergoio/aergo/node  --config ../node/testmode.toml --testnet

{"level":"info","module":"asvr","revision":"b253c4a7","branch":"develop","time":"2019-05-15T14:21:04+08:00","message":"AERGO SVR STARTED"}
{"level":"info","module":"asvr","time":"2019-05-15T14:21:04+08:00","message":"No private key file is configured, so use auto-generated pk file instead."}
{"level":"info","module":"asvr","pk_file":"/Users/clark/workspace/golang/src/github.com/aergoio/aergo/node/auth/aergo-peer.key","time":"2019-05-15T14:21:04+08:00","message":"Load existing generated private key file."}
{"level":"info","module":"chain","account":"uGwmzEMMYso2PjcsS1qiLMUzTYT5T4UKHJwDm4UFRjLS","str":"AmNWodMKkB4Ei3ihfmBpvMYVbgv1avB5aGmtETzom2mopw5BJPRb","time":"2019-05-15T14:21:04+08:00","message":"set coinbase account"}
{"level":"info","module":"chain","time":"2019-05-15T14:21:04+08:00","message":"started signverifier"}
{"level":"warn","module":"chain","time":"2019-05-15T14:21:04+08:00","message":"--testnet option will be ignored"}
{"level":"info","module":"chain","TotalBalance":"500000000000000000000000000","time":"2019-05-15T14:21:04+08:00","message":"set total from genesis"}
{"level":"info","module":"chain","chain id":"{\"public\":true,\"mainnet\":false,\"magic\":\"testnet.aergo.io\",\"consensus\":\"dpos\"}","hash":"5bSKqpcWnMgrr1GhU1Ed5yHajRC4WwZEZYxFtw3fVBmq","time":"2019-05-15T14:21:04+08:00","message":"chain initialized"}
{"level":"info","module":"chain","enablezerofee":false,"time":"2019-05-15T14:21:04+08:00","message":"fee"}
{"level":"info","module":"syncer","seq":1,"time":"2019-05-15T14:21:04+08:00","message":"Syncer started"}
{"level":"info","module":"pcs","time":"2019-05-15T14:21:04+08:00","message":"node discovery by polaris is disabled configuration."}
{"level":"info","module":"chain","time":"2019-05-15T14:21:04+08:00","message":"check for crash recovery"}
{"level":"info","module":"chain","time":"2019-05-15T14:21:04+08:00","message":"Chain Manager[1] started"}
{"level":"info","module":"rpc","time":"2019-05-15T14:21:04+08:00","message":"Starting RPC server listening on 0.0.0.0:7845, with TLS: false"}
{"level":"info","module":"chain","time":"2019-05-15T14:21:04+08:00","message":"Chain Worker[5] started"}
{"level":"info","module":"dpos","lastly produced block":0,"time":"2019-05-15T14:21:04+08:00","message":"start the block factory worker"}
{"level":"info","module":"mempool","showmetric":false,"fadeout":false,"evict period":"12h0m0s","number of verifier":8,"time":"2019-05-15T14:21:04+08:00","message":"mempool init"}
{"level":"info","module":"p2p","full_id":"16Uiu2HAmUdoiT5bb4W1i3ss2wQQmjNFTwPhZEvxmaB4WdjemGNeV","peer_id":"16*emGNeV","addr[0]":"/ip4/0.0.0.0/tcp/7846","time":"2019-05-15T14:21:05+08:00","message":"Set self node's pid, and listening for connections"}
{"level":"info","module":"p2p","time":"2019-05-15T14:21:05+08:00","message":"Starting p2p metrics manager "}
{"level":"info","module":"p2p","version":"/aergop2p/0.3","time":"2019-05-15T14:21:05+08:00","message":"Starting p2p listening"}

my config file looks like this :

datadir = "/tmp/aergo/data"
enableprofile = false
profileport = 6060
enabletestmode = false

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

[p2p]
netprotocoladdr = "127.0.0.1"
netprotocolport = 7846
npbindaddr = "0.0.0.0"
npbindport = 7846
nptls = false
npcert = ""
npkey = ""
npaddpeers = [
]
npmaxpeers = "0"
nppeerpool = "0"
npusepolaris = false

[blockchain]
# blockchain configurations
maxblocksize = 1048576
coinbaseaccount = "AmNWodMKkB4Ei3ihfmBpvMYVbgv1avB5aGmtETzom2mopw5BJPRb"

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

[consensus]
enablebp = true
enabledpos = false
blockinterval = 1
bpids = "16Uiu2HAmNsyv6RmvH8627aRWuWDCRMK1VwRt2QaoM59c1FAw5yey"

log file look like this:

level = "info"  # default log level
formatter = "console"  # format: console, console_no_color, json
caller = true  # enabling source file and line printer
timefieldformat = "Jan _2 15:04:05"

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

[dpos]
level = "debug"

[p2p]
level = "debug"

[consensus]
level = "debug"

[mempool]
level = "debug"

[contract]
level = "debug"

[syncer]
level = "debug"

[bp]
level = "debug"

brick should allow referencing accounts by name

When testing with brick, it's common to want to create some accounts via inject, and then use those accounts as parameters to call or query, both for invoking the smart contract and for checking the results.

Currently brick seems to rely on the fact that the account hash is deterministically generated from the account name given. So you have to find out the hash by doing getstate <account_name> after the inject, and then manually copy the generated hash into the rest of the .brick file so that it's hardcoded. For example:

inject user1 10000000  
# Now we have to remember user1 has this address:
# Amg25cfD4ibjmjPYbtWnMKocrF147gJJxKy5uuFymEBNF2YiPwzr

deploy user1 0 myContract mycontract.lua

# call addUser with user1 as the first argument
call user1 0 myContract addUser `["Amg25cfD4ibjmjPYbtWnMKocrF147gJJxKy5uuFymEBNF2YiPwzr","User's name","24/10/1990"]`

# query getUserByName and check that it returns user1
query myContract getUserByName `["User's name"]` `{"address":"Amg25cfD4ibjmjPYbtWnMKocrF147gJJxKy5uuFymEBNF2YiPwzr","dateOfBirth":"24/10/1990"}`

If I'm wrong please let me know! If I'm right then this should at least be documented in the README, because it's not completely obvious (IMHO).

However it would be much easier to read and write if the account name identifier could be used instead of the hash, e.g. like this:

inject user1 10000000  # We don't need to know the account address :-)

deploy user1 0 myContract mycontract.lua

# call addUser with user1 as the first argument
call user1 0 myContract addUser `[user1,"User's name","24/10/1990"]`

# query getUserByName and check that it returns user1
query myContract getUserByName `["User's name"]` `{"address":user1,"dateOfBirth":"24/10/1990"}`

libgmp.so can get built into libtool/lib64

contract/vm.go seems to assume that libgmp.so will always get built into libtool/lib/:

#cgo !darwin,!windows LDFLAGS: ${SRCDIR}/../libtool/lib/libluajit-5.1.a ${SRCDIR}/../libtool/lib/libgmp.so -lm

but on my (openSUSE) system, it gets built in libtool/lib64:

libtool/lib:
total 1.5M
-rw-r--r-- 1 adam users 471K Feb  8 23:19 liblmdb.a
-rwxr-xr-x 1 adam users 306K Feb  8 23:19 liblmdb.so*
-rw-r--r-- 1 adam users 741K Feb  8 23:19 libluajit-5.1.a
drwxr-xr-x 3 adam users 4.0K Feb  3 19:45 lua/
drwxr-xr-x 2 adam users 4.0K Feb  8 23:19 pkgconfig/

libtool/lib64:
total 528K
-rwxr-xr-x 1 adam users  932 Feb  8 23:19 libgmp.la*
lrwxrwxrwx 1 adam users   16 Feb  8 23:19 libgmp.so -> libgmp.so.10.3.2*
lrwxrwxrwx 1 adam users   16 Feb  8 23:19 libgmp.so.10 -> libgmp.so.10.3.2*
-rwxr-xr-x 1 adam users 521K Feb  8 23:19 libgmp.so.10.3.2*

This results in a build error:

# github.com/aergoio/aergo/contract
gcc: error: contract/../libtool/lib/libgmp.so: No such file or directory
make[3]: *** [CMakeFiles/aergosvr.dir/build.make:57: CMakeFiles/aergosvr] Error 2
make[2]: *** [CMakeFiles/Makefile2:260: CMakeFiles/aergosvr.dir/all] Error 2
make[1]: *** [Makefile:84: all] Error 2
make: *** [Makefile:32: all] Error 2

Changing the above line in contract/vm.go to:

#cgo !darwin,!windows LDFLAGS: ${SRCDIR}/../libtool/lib/libluajit-5.1.a ${SRCDIR}/../libtool/lib64/libgmp.so -lm

fixes the build, but presumably the lib64 component should be obtained dynamically from somewhere rather than just hardcoding it. I'm not sure if this is a problem on a lot of 64-bit Linux systems, or if there is something slightly different about mine vs. say, Ubuntu or Fedora. But either way, making assumptions about the value seems unreliable.

Build from the source error

So I installed cmake,glide , and go

> cmake --version
cmake version 3.14.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).
> glide --version
glide version 0.13.2
> go version
go version go1.11.2 darwin/amd64

after I did all the fetching code , I got this error when type the make command:

> make
make: Circular build <- build dependency dropped.
make[1]: *** No targets specified and no makefile found.  Stop.
make: *** [Makefile] Error 2

sbp node randomly hangs after DeadLetter warning

I've started to see some weird behaviour with sbp nodes. After a random amount of time they show a "future: timeout" error then a DeadLetter error, and after that the node will not create new blocks any more. Sometimes this can happen very quickly e.g. within the first 50 blocks - other times it doesn't happen for 20,000 blocks or more.

I've seen it on two separate Docker images - one by me based on the official Docker aergo/node, and another by @hanlsin.

Here is what the logs look like:

2020-03-24T00:32:34Z INF transactions collected candidates=0 collected=0 module=consensus
2020-03-24T00:32:34Z INF block produced TrieRoot=6n9ERSaGHWswduB4hd13Mtfxw7qGVuAKuPsVC1joo34P hash=7ysvVWKuLZxayAqZteWmWCT2VpHNhJ3yMtJExcDEFCEv module=sbp no=916
2020-03-24T00:32:35Z WRN flush takes long time callstack1=/go/src/github.com/hanlsin/aergo/state/statedb.go:517 callstack2=/go/src/github.com/hanlsin/aergo/chain/chainhandle.go:705 delCount=0 flushTime=834.188241 module=db name=/aergo/data/state prepareAndCommitTime=0.006934 setCount=1 setKeySize=32 setValueSize=2
2020-03-24T00:32:35Z ERR failed to connect block error="future: timeout" hash=7ysvVWKuLZxayAqZteWmWCT2VpHNhJ3yMtJExcDEFCEv module=consensus no=916 prev=GZSYYXsdq8VVVNvZfKSzAeFStPn5PAAStF4KfcRaKeWT
2020-03-24T00:32:35Z INF failed to produce block error="best block changed in chainservice" module=sbp
2020-03-24T00:32:35Z WRN commit takes long time callstack1=/go/src/github.com/hanlsin/aergo/chain/chainhandle.go:364 callstack2=/go/src/github.com/hanlsin/aergo/chain/chainhandle.go:345 delCount=0 module=db name=/aergo/data/chain prepareTime=0.015801 setCount=3 setKeySize=52 setValueSize=251 takenTime=208.834956
2020-03-24T00:32:35Z INF block added successfully best=916 hash=7ysvVWKuLZxayAqZteWmWCT2VpHNhJ3yMtJExcDEFCEv module=chain
2020-03-24T00:32:35Z WRN DeadLetter dead_receiver={"Address":"nonhost","Id":"consensus/chain/info.ConnectBlock$ut"} module=actor msg={"BlockHash":"Z7g5kDov4TDj/YfdPaRliwFmw0PAwaUCKpiuYIOmfnM=","BlockNo":916,"Err":null} sender=null
2020-03-24T00:32:36Z ERR fail to put at a mempool list error="tx with same nonce is already in mempool" module=mempool
2020-03-24T00:37:17Z WRN Failed to get bp vote result error="not supported by this consensus" module=p2p

brick should have a "fail fast" option

When running brick batch files, if a failure happens early on (such as failing to deploy the contract due to a syntax error) then brick continues regardless, typically causing lots more errors which were just side effects of the original error. These subsequent errors do not help - they simply make the root cause harder to find since you have to scroll up to find it. Therefore it would be better if brick stopped running after the first error. Since this is a change in behaviour which might confuse existing users, it could be offered via a new -f option rather than being the new default behaviour.

[V2] Is there any way to distinguish between V1 and V2 when connect?

To support both versions in the SDKs, it would be better to know about a node connected is V1 or V2.

For example, after connecting V2 and then if "rpc.GetServerInfo" returns a differentiable value such as "fork_version=2", the SDK can support both versions.

Without it, a client using V2 SDKs cannot communicate the older version (V1) node. Making the client to use V1 SDKs causes that newer features should be included in the older version and release it.

The current "version" field seems not enough to separate them.

Protobuf serialization in lua smart contracts

Currently, in order to store state data, it is first serialized with protobuf then hashed and stored in the trie for state authentication.

On chain merkle proof validation with lua contracts requires that lua code serializes data (passed as tx argument) with protobuf before hashing it to verify a merkle proof.
However there is no official lua support of protocol bufffers, only separate implementations as discussed here https://stackoverflow.com/questions/2098715/why-not-a-lua-implementation-of-googles-protocol-buffers-is-there-already-any.

So here are some options :

  • support protobuf serialization in aergo lua contracts by linking to the official C implementation
  • import another lua library like the ones mentioned in the stackoverflow thread above.
  • use a simpler custom serialization scheme like RLP.

More generally, the requirement is that we must be sure of having byte perfect consistency accross all implementations : golang node, lua contracts, and SDKs...
@mlogue76 , @eve2adam , @asteroidable , @ashen1dev

Decimal Support: 9 or 18

'AERGO' is main coin of Aergo Network. One advantage of cryptocurrency is that it can be divided smaller than the base unit.(Better Liquidity) and we want to define the minimum unit called AER. Aergo's smart contracts, SDK and AergoCLI will use AER as a basic unit. The problem is how much AER is smaller than AERGO. In initial discussion, we noticed that most Ether wallets actually use the GWei unit as a fee unit.

So we tried to define AER to be 10 ^ -9 of AERGO (until Release 0.8.1). But soon, we found the compatibility problem. In case of the ERC20 token transfer from the Ethereum to AERGO main network(via the Asset Bridge), token value will be truncated from 18 decimals to 9 decimals.

We will define AER as 10 ^ -18. However, in this case, we need to implement a separate big number data structure instead of using unsigned int 64 bits.

In addition, it is also better to be compatible with Ethereum by supporting up to 10 ^ -18 in terms of 'Forward Compatibility'.

grpc "GetTx" returns an error

Grpc GetTx returns an error
GetTX: rpc error: code = NotFound desc = not found

The same txhash
Grpc GetReceipt returns success
Current aergo version 1.2.0

[contract] return class object from contract.call makes PANIC and kills the system

Aergo Info

Tested version

  • tags/hotfix-0.9.10
  • develop (38d9f93)

config.toml

# aergo TOML Configuration File (https://github.com/toml-lang/toml)                                                                                                                                                                                                            
# base configurations                                                                                                                                                                                                                                                          
datadir = "/Users/yp/work/blocko/go/src/github.com/aergoio/aergo/bin/alone/data"                                                                                                                                                                                               
enableprofile = false                                                                                                                                                                                                                                                          
profileport = 6060                                                                                                                                                                                                                                                             
enablerest = false                                                                                                                                                                                                                                                             
enabletestmode = true                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                               
[rpc]                                                                                                                                                                                                                                                                          
netserviceaddr = "127.0.0.1"                                                                                                                                                                                                                                                   
netserviceport = 7845                                                                                                                                                                                                                                                          
nstls = false                                                                                                                                                                                                                                                                  
nscert = ""                                                                                                                                                                                                                                                                    
nskey = ""                                                                                                                                                                                                                                                                     
nsallowcors = false                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
[rest]                                                                                                                                                                                                                                                                         
restport = "8080"                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                               
[p2p]                                                                                                                                                                                                                                                                          
netprotocoladdr = "127.0.0.1"                                                                                                                                                                                                                                                  
netprotocolport = 7846                                                                                                                                                                                                                                                         
nptls = false                                                                                                                                                                                                                                                                  
npcert = ""                                                                                                                                                                                                                                                                    
npkey = ""                                                                                                                                                                                                                                                                     
npaddpeers = [                                                                                                                                                                                                                                                                 
]                                                                                                                                                                                                                                                                              
npmaxpeers = "100"                                                                                                                                                                                                                                                             
nppeerpool = "100"                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
[blockchain]                                                                                                                                                                                                                                                                   
# blockchain configurations                                                                                                                                                                                                                                                    
maxblocksize = 1048576                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                               
[mempool]                                                                                                                                                                                                                                                                      
showmetrics = false                                                                                                                                                                                                                                                            
dumpfilepath = "/Users/yp/work/blocko/go/src/github.com/aergoio/aergo/bin/alone/mempool.dump"                                                                                                                                                                                  
                                                                                                                                                                                                                                                                               
[consensus]                                                                                                                                                                                                                                                                    
enablebp = true                                                                                                                                                                                                                                                                
blockinterval = 3

Action

  1. Deploy the below smart contract
-- test_sc.lua
function test_die()
  return contract.call(system.getContractID(), "return_object")
end

function return_object()
  return db.query("select 1")
end

abi.register(test_die, return_object)
  1. Call or query "test_die", then all system dies

Symptoms

{"level":"debug","module":"contract","abi":"{\"name\":\"test_die\",\"args\":null}","time":"2019-01-08T16:49:46Z","message":"contract AmgkXyhYFegbwy5dj5iTUrKEMHsPkuqiPafZC6qH5BKFjyNeJ4rp"}
{"level":"debug","module":"statesql","rp":1,"time":"2019-01-08T16:49:46Z","message":"snapshot view, 0xc00b53a570"}
PANIC: unprotected error in call to Lua API (bad argument #9 to '?' (bignum bignumber expected, got userdata))

Comments

Need to prevent the system down.

Remove requirement of a contract address on ListEvents

Currently the Aergo GRPC ListEvents and ListEventStream services require a contract address in the FilterInfo for they to work.

We are needing to list all events from a specific block and the above limitation is not allowing this.

When one contract is called, it can call other contracts and they can call others... and any of these contracts can emit events. An ARC1 token can be the last to be called, and we need to catch events from all the contracts.

Proposal

Remove the requirement of a contract address on ListEvents GRPC service, and list all events in the supplied block range

The same can be applied to the ListEventStream service, being able to listen to all events from the blockchain

Later

When doing this on the server side, we need also to update the clients:

  • aergocli
  • herajs
  • herapy
  • heraphp
  • libaergo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.