Docker Smart Contracts

The Waves Enterprise platform provides the ability to develop and use Turing-complete smart contracts.

Smart contracts on the Waves Enterprise platform

Turing-complete smart contracts allow you to implement any logic embedded in the program code. To separate the launch and operation of smart contracts themselves from the Waves Enterprise platform, Docker-based containerization is used. However, any programming language can be used to write a smart contract. Each smart contract is run in a Docker container to isolate its operation and manage the resources of the running smart contract.

When a smart contract is launched in a blockchain network, its code cannot be arbitrarily changed, replaced, or prohibited from being executed without interfering with the entire network. This property makes smart contracts an almost irreplaceable tool in the blockchain network.

Docker Registry is used for storing smart contracts with read access to Docker images for machines with nodes. Waves Enterprise provides an open repository for Docker smart contracts, where any developer can add their own smart contract. The open repository is located at the registry.wavesenterprise.com/waves-enterprise-public address. To add your smart contract to the open repository, you need to write a request to our technical support. After the request is approved, the smart contract will be added to the open repository, and you can call it from the client or the node’s REST API.

If you use a private blockchain network, you need to have your own Docker repository for publishing and calling smart contracts.

The node state can be accessed through a REST API or gRPC.

../../_images/docker-1.png

A smart contract can be created and called by any network participant, regardless of whether there is a mining node or not. You need just to register in the Mainnet network via client interface.

Creating a contract

Creating a smart contract starts with the preparation of a Docker image, which consists of the contract program code, the required environment, and the special scenario Dockerfile. A prepared Docker image (a build) is then assembled and sent to Docker Registry. To send the new smart contract, create a request on the technical support portal. After verifying the smart contract, technical support staff places it in an open Docker repository. The settings of the docker-engine section for working with the open Docker repository are already presented by default in the node configuration file. Also the recommended parameter values are set by default for optimal operation of smart contracts in the Mainnet blockchain network.

Dockerfile sample for REST API usage:

FROM python:alpine3.8
ADD contract.py /
ADD run.sh /
RUN chmod +x run.sh
CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"

Dockerfile sample for gRPC usage:

FROM python:3.9-rc-buster
RUN pip3 install grpcio-tools
ADD src/contract.py /
ADD src/protobuf/common_pb2.py /protobuf/
ADD src/protobuf/contract_pb2.py /protobuf/
ADD src/protobuf/contract_pb2_grpc.py /protobuf/
ADD run.sh /
RUN chmod +x run.sh
ENTRYPOINT ["/run.sh"]

The contract is created by publishing a special (CreateContractTransaction) transaction containing a link to the image in Docker Registry. To use the REST API or gRPC, please, specify the transaction version 103. After the transaction is received, the node downloads the image using the link specified in the “image” field, the image is checked and launched as a Docker container.

Executing a Contract

Smart contract execution is initiated by a special (CallContractTransaction) transaction containing the contract ID and call parameters. The transaction ID defines the Docker container. The container is executed unless it has been launched before. The contract launch parameters are transferred to the container. | Smart contracts change their state by updating the key-value pairs.

Parallel contract execution

The Waves Enterprise platform allows you to run multiple Docker contracts simultaneously. This option is only supported by gRPC contracts. How it works:

  1. The developer of the smart contract specifies an async-factor parameter in the contract code (for more information, see Creating a smart contract). This parameter defines the allowed number of simultaneous transactions for a smart contract.

  2. Right after starting the contract passes the value of the async-factor parameter to the node.

  3. When contract execution starts, the buffer for contracts starts filling up. Raw transactions with contracts are taken from the UTX pool until the buffer is full.

  4. Then the selected transactions are divided into groups by contract IDs. Only one group can be executed at a time and within the group, parallel contract processing is defined by the async-factor parameter.

  5. When the next contract goes to execution, one cell in the buffer is released, and when a transaction is received from the UTX pool, the cell is blocked. In this way, buffer filling and contract call processing operations occur in parallel, which avoids pauses for pulling transactions up when all current transactions have already been processed.

  6. Parallel execution of contracts is affected by the value of the pulling-buffer-size parameter. This parameter is configured in the docker-engine section of the node configuration file and indicates the buffer size for processing transactions with contracts.

The diagram below shows an approximate principle of parallel processing of smart contracts.

../../_images/docker-parallel.png

The buffer allows you to have a stock of contract transactions which are ready for processing. In addition, the buffer size affects the maximum amount of time that can be used to process contracts from a single group. The larger the buffer size, the longer this time is. Therefore, the buffer size is also a limit for the number of transactions waiting to be processed. If this limit is exceeded, transaction processing is moved to the next group.

The smart contract code’s logic, as well as the selected development tools (the programming language in which the contract is written), should take into account the specifics of parallel processing of the contract. For example, if a smart contract with the increment function is executed in parallel, the result is incorrect because a shared key is used during each contract call. A contract that implements the voting procedure on the Waves Enterprise platform does not use shared keys and supports parallel execution, which increases the efficiency of its processing and guarantees faster results.

Read more about creating a smart contract on the Creating a smart contract page.

Updating Contract

Only the developer of the Docker smart contract can update this contract. The developer should keep the contract_developer role during the contract update and should be the 103 transaction creator. 107 transaction is using for the contract update. And it is necessary that the contract is active.

All the mining nodes download the contract image and run it for the checking after the 107 transaction includes into the block. Then the 105 transaction is issued within the 107 transaction inside it.

Contract Call Disabling

If necessary, the contract developer can disable calling the contract. To do this, a special (DisableContractTransaction) transaction is published specifying the Contract ID. The contract becomes unavailable after its disconnection, but you can get information about the contract from the the blockchain later.

Description of Transactions

The following transactions are implemented to ensure the interaction between the blockchain and the Docker Contract:

Code

Transaction type

Purpose

103

CreateContractTransaction

Initiates the Contract. Transaction is signed by a user with the role “contract_developer”

104

CallContractTransaction

Calls the Contract. Transaction is signed by the initiator of contract execution

105

ExecutedContractTransaction

Records the contract execution result in the contract state. |br| Transaction is signed by the block generating node

106

DisableContractTransaction

Disables calling a contract. |br| Transaction is signed by a user with the role “contract_developer”

107

UpdateContractTransaction

Updates a contract. |br| Transaction is signed by a user with the role “contract_developer” |br| Only the contract developer and 103 transaction issuer can update the contract

Node configuration

Downloading and execution of Docker Contracts initiated by transactions with codes 103-107 are performed on nodes with enabled option docker-engine.enable = yes (for details see module “Node configuration” > “Docker configuration”).

REST API

The REST API methods description for the Docker contract usage is represented on the API methods available to smart contract page.

gRPC

The gRPC methods description for the Docker contract usage is represented on the gRPC services available to smart contract page.

Implementation examples