Tezos ecosystem - October 2019

le 25/11/2019 par Frank Hillard
Tags: Évènements

[DISCLAIMER: This snapshot has been made in October 2019. The ecosystem is really moving a lot therefore everything can change very quickly. Moreover, few versioning principles are really used so we can’t list the stack that we used for our study]

This article is a technological deep-dive to the Tezos world, with a holistic presentation of how to develop Dapps on this promising blockchain.

We saw that few articles provide a extended insight of all the tooling. At OCTO, we aim to provide to the community the understanding of all the high-level languages, testing frameworks and toolings : Tezos CLI, Michelson, LIGO, SmartPy, etc.

This article describes the status of all these technologies at the end of october 2019. The community of developers is growing and working hard to improve the ecosystem of Tezos (testing frameworks and compilers for high-level languages) and many existing issues might be solved while your reading this.

As a reminder, Tezos is a blockchain technology (meaning a decentralized network) with ability to deploy and execute code over this ledger. The related white paper is available here.

This article is a follow-up of a (french version) that introduce the on-chain governance.

I - Overview of the core blockchain protocol

Consensus

Tezos is using a Proof Of Stake (PoS) consensus algorithm which is consuming less electricity than Proof of Work algorithm. Improvement of consensus algorithm has been a key feature for the last 6 years in the world of blockchains (NXT (2013), Ethereum is working on PoS for the last years, and many other new blockchains are heading toward PoS).

Language

Tezos uses a Virtual Machine that interprets Michelson language. This language is a low-level assembly-like stack-based language which has the particularity to allow formal proof. Ensuring community of the proper-functioning scripts might be a key element to create trust.

High-level languages and tools

Because low-level languages are harder to read and maintain, other new high-level languages and tools are being developed. The choice of this low-level is to provide the formal verification of the execution of the smart contract. This is a key feature that notably the financial sector is keen to have.

An overview can be found on the Tezos developer portal and in this article we will describe some of them.

II - Languages

Michelson (low-level)

Link : https://tezos.gitlab.io/whitedoc/michelson.html

Who : Nomadic labs

To develop smart contracts, the main language is Michelson,  a stack-based language.

https://gist.github.com/frankhillard/00eb5b2428901a66b26123a7a99c7558

Code available on GitHub : https://github.com/vnea/michelson-samples/blob/master/src/contracts/counter.tz

In a Michelson code there are 3 sections :

  • storage : like in Solidity (for Ethereum), it contains the data of the contract.
  • parameter : the external information/data to pass to the contract.
  • code : it contains the logic of the contract, it can manipulate the parameter and the storage, do computations and then update the storage.

As you can see, in the “code” section, it is not possible to define different functions like in Solidity but you you can define what is called an entrypoint with annotations (%increaseCounterBy et %decreaseCounterBy) in the above example. And then in the code, with the IF_LEFT Michelson instruction, we can do the implementation of each entrypoint/function.

If you want to learn Michelson language, a tutorial is available here and a lot of example are available here.

The most (+)

Strong typed language

It is mandatory to do a check after each division (to prevent division per 0), after trying to get an element of a map (to avoid “null pointer exception”, there is no, implicit casts, no overflow, etc.)

Example of getting an element of a map

https://gist.github.com/frankhillard/d8c88957272ba5150a65efcd7264f6fd

Language at the core of the evolution of the protocol

Each time a new Core tezos protocol is released with new Michelson instructions, it is possible to use them. With a high level language, we actually have to wait for an implementation to cover these new instructions.

In the long term, we don’t see this as a key differentiator for the business.

Formal verification

One of the main feature of tezos is to use Michelson language for implementing smart contracts. This choice of language is due to the ability of making a formal verification of a smart contract code.

Some tools and language have been developed by the Caml community (INRIA, CNRS, …) in order to make formal verification. One of them is Coq, a proof assistant using Gallina language.

Nomadic labs is working on Mi-Cho-Coq which is the specification of Michelson in Coq (a proof assistant using Gallina language). So, Mi-Cho-Coq is a Coq framework for verifying the functional correctness of Michelson smart contracts. Mi-Cho-Coq allows users to produce a proof of their smart contract (written in Michelson).

Immutability

Each time an instruction is run, it never modify an existing element of the stack : it can only add or remove elements. It avoid side effects like having an invalid state of a data : the data is defined once in a valid state, and we know that it will not change to an invalid state during the whole execution.

The least (-)

No variable

As it is a stack-based language, whenever you have to do some computations with a data in the parameter or the storage, you have to use some instructions to navigate through the stack to get this specific data. It sometime be tricky. There is an online tool called Try Michelson which allows to see the evolution of the stack. There is also a plugin for IntelliJ and one for Emacs if you prefer to have a local environnement.

Michelson stack view with variable annotations

Hard to read the code

As there is no variable, it is not possible to name things. So if a new developer starts to work an existing Michelson code and he has no context about the business, it will take a lot of time for him to understand the logic.

If we take the same counter contract but written in Solidity, in 20 seconds we can understand the purpose of the contract.

Example (solidity):

https://gist.github.com/frankhillard/f12fd7b14989336b84438e64469912dd

In Michelson, without comments and annotations, it will take more time to understand :

Example (michelson):

https://gist.github.com/frankhillard/8d9df3598c09853d571aa6e39710e9e1

So here it is just a simple contract. But imagine how complex it can be with a larger contract.

Hard to maintain

Hard to read the code implies hard to maintain it. One important thing to understand, is at the end of the code, the top element on the stack must be a pair of list of operations (~= transactions in Ethereum) and the storage. So if you want to add new properties/attributes in the structure of the storage, almost all the code must be updated as Michelson instructions depend on what is on the stack. For example, if on the top of the stack there is an integer which is the storage and the next instruction is GT (=checks that the top element of the stack is greater than zero), if the storage is changed to a pair of a string and an int, the code needs to be updated to split this pair and only keep the integer. So defining the structure of the storage at the beginning is a good practise. The same goes for the parameter.

Let’s get the previous contract example and let’s keep the previous counter after each update.

In Solidity, it is simple :

Example (solidity):

https://gist.github.com/frankhillard/a37905470bbb789dbe7eb514a6b3091c

Before seeing the solution, you can try by yourself to update the Michelson code.

Here the solution In Michelson :

Example (michelson):

https://gist.github.com/frankhillard/361a791d66ce29cda930c59c7fa46f82

We can see that if we read the Michelson code, if a bit harder to directly understand the new feature.

For information, it is a good practise to use the variable annotation @, it will be easier to understand what is in the stack :

Michelson stack view with variable annotations

No getter

From a contract, it is not possible to have an entrypoint which returns a data (after a computation or from the storage) outside the contract. So if you have a contract A calling a contract B, B cannot return any value to contract A. There is a pattern called continuation passing style where contract A calls contract B by passing its address and then contract B can call contract A and it gives its storage. But it can cost a lot of gas.

Contact

GitLab : https://gitlab.com/nomadic-labs

Mail : contact@nomadic-labs.com

Slack : https://tezos-dev.slack.com (need an invitation)

LIGO (high-level)

Link : https://ligolang.org/

Who : Stove Labs

LIGO language is a functional programming language based on Pascal/OCaml to write smart contracts. The LIGO compiler produces Michelson code from a LIGO code.

Example (ligo):

https://gist.github.com/frankhillard/b5ae7464a0b2cfe4363ced7bc9c9f493

And here is how one’s can invoke the contract :

ligo dry-run counter.ligo main Increment(5) 0

The ligo tutorial example taco-shop is available here. It focuses on how to use a map as storage and how to read/modify the storage. This following command line shows you how to initialize a map storage.

https://gist.github.com/frankhillard/935d3207fe353036cc3e80dd26d4ecca

This command line (above) simulates execution of the smart contract entrypoint.  It shows how one’s can specify the initial storage state.

The most (+)

Assignments of embedded maps has been fixed recently

It is easier to manipulate complex data structure (records containing maps).

Communication between contracts

LIGO implements a way to call contract from another contract which means it is possible to implement some casual patterns (such as proxy contract, delegation, continuation passing style (callbacks)).

Good reactivity

As the tool is young, the dev team quickly answers when have some issues (on Slack).

Strong types language

LIGO is based on Pascal/OCaml which implies that it is a strong typed language :

  • prevent bugs and untreated cases or invalid type casting.
  • case instruction is very user friendly feature.

Interesting features

It is possible to split implementation into multiple files (#include instruction). It can be nice if we want to define the types in a different file for example.

An online IDE for testing a single contract is available and allow to avoid local installation.

The least (-)

Error message

Lack of maturity concerning error message. Sometimes error line is not mentioned, nor in which function the problem is located.

For example, if you try to compile this code, only “not an option” will be mentioned :

var addr: address := None;

LIGO code with not helpful error message

Invalid Michelson code generated sometimes

The compiler has an issue when generating michelson with statements like get_contract/transaction (returns an option but expect contract). This error can be fixed manually inside the Michelson generated source code by adding an ASSERT_SOME or IF_SOME Michelson instruction.

Formal verification

In the future, the strategy is to also provide a framework allowing to verify the functional correctness of smart contract written in Ligo. (This framework is not yet available).

Lack of documentation

There is :

  • no mention of big_map.
  • no enough tips and tricks / work around :
    • nat - nat => int but can be recasted to nat using abs LIGO function.
  • how to fix LIGO compiler bugs (for example, add ASSERT_SOME or IF_SOME instruction before contract instruction).
  • no release notes : it is difficult to know what is currently working or not and what has been fixed.

Contact

Discord : https://discord.gg/9rhYaEt (all messages of Slack are re-sent here by a bot)

Slack : https://tezos-dev.slack.com/archives/CFX0B8Q3X (need an invitation)

SmartPy (high-level)

Link : http://smartpy.io

SmartPy : François Maurel and Roland Zumkeller

Warning :

A new version has been released the 11th November : http://smartpy.io/babylonTest/

It has not tested so maybe the lacking features are now implemented is this version.

SmartPy allows to develop smart contracts in Python and then compile then into Michelson.

https://gist.github.com/frankhillard/1c1206587e962f4a9f0284d2ac21a5ec

The most (+)

Python

It is an easy language to learn and it exists since a long time so it will not be difficult for a new developer to get into SmartPy.

Online editor

If you need to test a piece of code quickly, it is not needed to setup a whole environment on the computer.

Error messages

The SmartPy compiler generates explicit error messages. Those error messages are easily understandable and thus provide a good help for debugging smart contracts. For example, here is a message error saying there is a missing parameter : “Error: Type error, non matching names [y] and [z] in records {x: nat, y: intOrNat} and {x: nat, z: nat} Bad params type in line 51”.

It can be confusing to mentioned this type of thing but it is a new language.

Examples

You can find some patterns and functions that are not specified in the documentation so it is a good idea to check all the example before starting to develop.

Lots of examples are provided by the SmartPy IDE which allow users to load/test template contracts. Click on Menu then Load templates :

SmartPy examples

The least (-)

Testing framework

It is possible to write unit tests to test the SmartPy code. As you can see in the previous snippet of code, the end of the snippet of code contains tests wrapped as a scenario. The scenario simulate invocation of contract entrypoints and verify that the storage has been modified accordingly.

Inheritance

The inheritance between contracts is applied only to functions but not to class attributes. Once the mother contract has defined a storage structure there is no overriding of the storage structure by the child contract (storage is not shared).

Lack of/incoherent documentation

It is not mentioned how to defined a embedded of map : if you try it will generate Michelson with error messages inside it (a tip to do it is given further in the article).

In an entrypoint function, it is not said that it can only have one argument. If need to pass multiple parameters.

```

@sp.entryPoint

def add(self, firstValue, secondValue):

self.data.value = firstValue + secondValue

```

Do not work

Solution :

```

@sp.entryPoint

def add(self, params):

self.data.value = params.firstValue + params.secondValue

```

Works.

The documentation does not specify that it is possible to define the type of a map like this :

```

sp.bigMap(tkey = sp.TAddress, tvalue = TInt) // If TInt does not work, use TSimple(“int”)

```

One BigMap per contract

In SmartPy, a Map type has a limit of number of entry that can be stored. Above 10 elements in the map, the SmartPy compiler will expect a big_map type instead of map.

It can also reach the size limit if the structure used in the map is a record with fields, or multiple embedded maps.

Cannot call another contract

The SmartPy language does not support the transaction instruction which allows a contract to send a transaction to another contract.

Warning

This feature is now normally supported in the new version released in 11th november.

Everything in one file

even if it is possible to write “tests” in SmartPy, implementation and tests has to be put in the same file. As a developer, we usually prefer to split them in different files. Even if there are several contracts, in the case you want to use inheritance, all the contracts have to be put in the same file. When searching for something, it is then more difficult to find the information in a large file.

No changelog

When downloading SmartPy or using the online editor, it is very hard to know what can be used and what has been fixed. And we are only inform about new versions on Telegram or Tweeter.

Formal verification

There is no plan for providing a framework allowing to verify the functional correctness of smart contract written in SmartPy.

Tips & Tricks

When using embedded maps , the SmartPy compiler may not be able to deduce the type of key and values of inner maps. A setType function can be useful to specify the types used in maps and embedded maps. In IDE , it displays a warning when some types cannot be deduced. Actually the generated michelson code might be invalid when warnings occur.

When evaluating a boolean condition , use the “~” operator for negation.

Contact

Telegram : https://t.me/SmartPy_io

Twitter : https://twitter.com/SmartPy_io

III - Testing

PyTezos

Link : https://github.com/baking-bad/pytezos

Who : Baking Bad

PyTezos is a Python SDK for Tezos: RPC, cryptography, operations, smart contract interaction. There are also other SDK like ConseilJS, Taquito, etc.

But one thing interesting is the ability to test Michelson code :

https://gist.github.com/frankhillard/2be0380bebd5fad874dd099309b37691

https://github.com/vnea/michelson-samples/blob/master/src/tests/test_counter.py

The most (+)

Test the Michelson

Whatever the high level language chosen, it will produce Michelson. So if the high level language does not provides a testing framework, the tests can be written with PyTezos and then run with Pytest.

No need to set up a node to run the test PyTezos has their own public node (with the last protocol Babylon), when the test is run, the data (Michelson code + test data) are sent to this node and then the code will just be interpreted. The contract is not deployed so it is really fast. It is then possible to put it in a CI. Of course, it is possible use another node. Under the hood, it is the run_code RPC call which executes the code.

Reactivity

As the tool is still young, there can still be some issues that can slow down your development. Baking Bad is quite reactive (just open an issue on GitHub).

Handling exceptions

It is possible write a test which expects for an exception to be thrown. It can happen in the case you want to test some preconditions, like if the given value is positive, if the caller is the administrator, etc.

From the counter example, we did another counter contract with an additional check and here it is an example on how you can write the expect exception in a test :

https://gist.github.com/frankhillard/e37818167c9e33090387bc214ae8be2e

https://github.com/vnea/dev-smart-contracts-tezos/blob/master/michelson/tests/test_counter_with_check.py

The least (-)

Lack of documentation

There is an article about how to write tests with PyTezos but it is not complete. It only shows a simple case where the contract has only one element in the storage. But when the storage is more complex, or if there is no annotation for the storage/entrypoints in the Michelson code, the syntax will not be exactly the same. Without annotations, you will have to pass an array and with annotations, you will have to pass a JSON object.

As it is not written in the official documentation or in the article, you have to look for the examples.

Unexpected bugs

Sometimes, we lost a lot of time because depending on the value passed to an entrypoint, an error can be thrown for no reason. For example, if we look again at the test test_increaseCounterBy_should_increase_counter_in_storage_by_increase_value and if we change the increase_value from 5 to 0, an error without any sense will be raised.

Contact

GitHub : https://github.com/baking-bad/pytezos

Telegram : https://t.me/baking_bad_chat

Twitter: https://twitter.com/tezosbulletin

IV. Infrastructure

Tezos CLI

Tezos provides tools for synchronizing (running a node of the network) and interacting with the blockchain (via RPC calls or smart contract invocation). Among these tools we must mention :

  • tezos-node: the tezos daemon itself which is running the tezos node;
  • tezos-client: a command-line client allowing users to interact with blockchain.
    • listing of registered account or deployed contracts
    • monitoring peer to peer layer / connect nodes
    • list of rpc entrypoint;
    • rpc call for inspecting genesis block
    • rpc call for checking node status
    • rpc call for listing protocols
    • (full reference is available here: https://tezos.gitlab.io/tezos/api/rpc.html)
  • tezos-admin-client: a command-line administration tool for the node;
  • tezos-alpha-baker: Among the peer to peer network some nodes are responsible for baking transactions. This tool contains a client (for command line purposes) and a daemon to bake on the Tezos network;
  • tezos-protocol-compiler: a protocol compiler used for developing new version of the economic protocol.

All information about Tezos node /client are available here

Contact

GitLab : https://gitlab.com/nomadic-labs

Mail : contact@nomadic-labs.com

Slack : https://tezos-dev.slack.com (need an invitation)

Granary

Link : https://stove-labs.github.io/granary

Who : Stove labs

Granary provides a comprehensive set of tools that you can use to develop Smart Contracts and Dapps on the Tezos platform. It provides a local sandboxed node where you have full control over the running blockchain, with block explorers and other supportive tools pre-configured & ready to use.

The most (+)

Easy installation / setup of environment

It automatically registers an account with some tez, and inject the protocol in the local sandboxed node with just some command line, the node run in docker container.

Easy use of command line instructions: very similar to tezos client CLI.

‘’’

granary client - list known contracts

‘’’

Uses a docker image for running the tezos node in sandbox mode

Granary provides commands for initializing/starting/restarting a tezos-node.

Please refer to  granary documentation (here).

The least (-)

Babylon protocol

For the moment, it is not possible to inject the Babylon protocol, which is the last one.

Lack of documentation

User permission problem when starting the node (must change permissions with following commands):

  • chmod -R 777 my-granary-project
  • chmod 777 .granary

When invoking contract entrypoints, string parameters must be escaped (call parameters and storage).

Contact

Github : https://github.com/stove-labs/granary

V - Additional tools

Tplus

Link : https://tplus.dev

Who : TulipTools

Tplus is a all in one tool that helps you manage Tezos environments (sandboxed and public nodes) for use for development on top of Tezos. It is a all in one tool :

  • sandboxed local node with Babylon protocol.
  • a Web interface with VScode with an access to a Terminal.
  • be able to run all Tezos CLI command from this Terminal.
  • LIGO and SmartPy CLI are also accessible from this Terminal.

Contact

Twitter : https://twitter.com/TulipToolsOU

Conclusion

To sum up, if you are starting to implement a Dapp for Tezos , it is very relevant to ask yourself which language should be used and how to test and deploy.

Language

You have multiple choices : Michelson, LIGO, SmartPy, Archetype, Morley / Lorentz (write smart contract in Haskell). We did not work with the last two languages so we can’t give you information about them but they seem very promising.

First, we won’t recommend to write smart contracts in Michelson for few reasons :

  1. code modification : if the smart contract is about to change (adding a parameter to a function or adding a new field in the storage structure) then its implementation will be quite different and thus implies lots of modification of code because it will change the state of the stack and it is a bit hard to find where to add instructions to re-arrange.
  2. readability : for most of developers it is not effortless to read and understand low-level languages. Being able to trust a smart contract will improve attractiveness of the service. It will take a lot of times to understand the business logic of the contract if there are no comments neither annotations.

So using a high level language would save time when implementing incrementally your smart contract and provide source code that can be read and understood by more people, thus creating trust on the DApp. Now choosing between SmartPy and LIGO depends on the architecture of contracts that need to be deployed.

FeatureSmartPyLIGO
Many contracts in single fileokko
Include other fileskook
Need to call another contractko (ok since 11th november)ok
Inheritance / overriding functionsPartially okko
Complex data structureokok
Use of big maponly 1multiple
formal verification frameworkNonemi-cho-coq for ligo (coming soon)

Note : we only recommend to use Michelson if you need an instruction that is not supported yet by a high level language. It can happen when a new protocol introduces new Michelson instructions.

Unit tests

With LIGO, there is no testing framework (yet ?).

With SmartPy, there is a testing framework but it is not perfect.

You have to options to write tests :

  1. Write tests in the online editor and the output will be displayed on the right : it is nice if we want to test something quickly be it can’t be integrated in a CI.
  2. Write tests but use SmartPyBasic which will run the tests and put the result (with additional information) in an output file : the output will contains a result as  OK or KO for each assertion. It is not a good thing to open then read this file to check if the tests are valid or not, this output should be in the Terminal and give more specific information about the error. If we want to integrate it in a CI, a script or a tool must be developed to parse the file and returns an error in case of a failing test.

In both cases, it is not possible to write a test which expects a error. For example if an entrypoint but you expect that only the owner of the contract can call it, you would want to write a test to cover this case but it is not possible.

As LIGO does not have a testing framework and as SmartPy testing framework has some drawbacks, we suggest you to use PyTezos to write the unit tests. It will directly tests the Michelson code (interpreted by the public node of PyTezos) and it is also possible to write a test expecting an error (like the case describe above) even if it is not perfect. And of course, the tests can be run in a CI.

Integration tests

Once the michelson has been generated (by ligo or SmartPy) it can be tested on a real running tezos-node.

We recommend to use PyTezos for testing your Smart contract

We recommend to use directly tezos-node if you are about to deploy on Babylonnet.

Since Granary does not support yet the Babylon protocol activation, it can be used on older protocols (alphanet, …).