📜 ⬆️ ⬇️

Smart Contract Calling System in the Ethereum blockchain

Task

Centralized solution

Often, smart contract developers under Ethereum are faced with a seemingly simple problem - calling a smart contract code in the future or on a schedule. But there is no suitable solution, and you have to develop a separate service for invoking contracts.

Developing contact templates for the MyWish platform , we immediately faced this limitation. And we set a goal to get around this restriction “beautifully” - with the help of a decentralized solution. This system must put contracts into action, and therefore decided to name it Joule, in honor of the English physicist James Joule .

In this article I would like to talk about the development of Joule, about the decisions that formed the basis of the system and the results obtained. We want to believe that the development, to some extent, is innovative and the applied solutions may be interesting and useful for the Habr audience.

Before describing the solution, it is necessary to tell the basic things about Ethereum blockchain smart contracts.
')

Smart contracts


Etherium smart network contracts are programs running in the EVM (ethereum virtual machine) environment. Each such contract has a unique address, balance of funds (ethers), state (permanent memory) and a set of functions available for calling (or without functions).

The code, balance and status of the contract is stored in the blockchain.

The user can call the contract function using a transaction. Or without a transaction, if the function does not change the state of the contract or someone else’s balance.

The contract method may have any, in complexity, implementation, and is limited only by the gas required to carry out each instruction. A contract may change its balance, may apply to other contracts, may receive funds from other network participants, etc.

Etherium is so arranged that only a user can launch a transaction into the network with a contract call. If, for example, in order for a contract to work, it is necessary to make a call to its method at some particular moment, then the user should be concerned about this.

Call system


The first obvious solution is a service that will publish a transaction to the network at the right time.

Centralized solution
This solution has a key drawback - this solution is centralized. If something happens to the server where the service is located, for example, it is disconnected for non-payment, it will be fined, etc., then calls will not be made. So the contracts will not be executed, which is unacceptable.

To circumvent this drawback, our team came up with the Joule system, which is implemented on the basis of smart contracts.

Joule


Joule is a smart contract on the Ethereum network that allows you to register a call to any contract at a specific point in time. Joule provides the opportunity for any user of the Ethereum network to call registered contracts.

To motivate network members to make a call, a reward and bonus are paid, which are set during call registration. The calculation of the amount required for the reward is done by Joule . The amount of remuneration fully covers the cost of gas spent on the call. In the form of a bonus part, WISH tokens are paid for the call (not implemented in the current version).

The system allows you to register multiple calls of contracts at the same time stamp. Also, the same contract can be registered at different times. The only restriction is that you cannot do registration with a completely identical set of parameters (contract address, time, gas and gas cost).

Joule , like any ethereum network contract, allows you to simultaneously call yourself to multiple users. And it may happen that there will be more calls to Joule than there are contracts available to call. In this case, only the first caller will receive a reward.

At any time, the user (using the smart contract method or the web interface) can find out which contracts are queued for a call and at what point the call must be made. The amount of remuneration and the amount of gas required to fulfill the contract are also available from the Joule contract.

Joule guarantees that the contract will not be called before the time specified at registration, and does not allow to disturb the order of calls.

Technical difficulties of implementation


To develop a contract as complex as Joule, it is necessary to solve a large number of technical problems. One of such tasks is the issue of storing a set of addresses of contracts and time to call them (and other parameters). Receipt of the first in line to call a contract must be made in constant time (independent of the number of contracts registered in the system).

Additional complexity in the design process necessitates the choice of algorithms, with minimal gas consumption. For example, you cannot use solutions that have an algorithmic complexity greater than O(ln(n)). In addition to the cost of gas, there is still a limit on the number of gas. For example, if the algorithm with the complexity of O(n/2), and the search of each element will require 50,000 gas, then after adding 185 elements, the contract will not be able to search (with the current limit of 4,600,000 gas per transaction).

Decision


This chapter will describe the key technical solutions included in the Joule system.

Record structure


To store the state (except for balance), the contract has access to a key-value type repository. The key in the storage can be any 256-bit number. In fact, before saving, EVM calculates a key checksum using the keccak (sha-3) algorithm . The value is also a 256-bit number (the compiler allows you to put values ​​in a larger state by adding an offset parameter to the key). Thus, the contract can store 2 ^ 256 unique values ​​of 256 bits each, which exceeds the requirements by many orders of magnitude.

The key-value storage can be considered as a random access memory with a dimension of 2 ^ 256. In this case, the key is a reference to the memory cell. The data structure necessary for storing call recording consists of the following fields: contract address, time the contract is called, the number of gas required to call the contract and the cost of gas. All this data can fit in 256 bits (32 bytes):

Record structure

Restrictions


Using this data storage structure, you must accept some restrictions:


Storing a chain of records


The smart contract supports two storage structures for a set of data: an array with access by index and a ratio (mapping) with access by key. But in reality, the array is stored as a mapping, where the key is an index, and does not provide any advantages in terms of access speed. In addition, inserting elements into the middle of an array requires shifting all elements and this may require an unacceptable amount of gas. Therefore, a solution based on linked lists is better suited for this task.

The following technology is used to store the list in the ratio: the value of the record (32 bytes) is considered as a key (or a link, if we draw an analogy with the C ++ language) to the next value.

Chain of records

The image on the left shows the registration records R and key acquisition functions. K(R)=Rin the form of a chain of values. On the right is a key-value store in the form of a table. See that the value K0is lost because it is only the key to the next value. The contract is not possible to read the key from the relationship. For this, the table contains a zero record (Head), which always indicates the value of the beginning of the chain.

The order of entries in the chain is the reverse of chronological, for instantaneous receipt of the next contract to be called. Same registration times are ordered one after another in the order of addition.

Index


To maintain the list of registrations in the sorted order, entries must be inserted according to the date. To speed up the search for a place to insert a new value, a tree-based index was developed.

Tree

With this approach, the search for elements in the tree gives a constant value of complexity that does not depend on the number of elements in Devere O(1). There is a worst-case scenario, which in the current implementation is 168 iterations. The balance of the tree can be selected by changing the number of levels and multipliers.

The last level, similar to the previous one, contains the values ​​of the maximum and minimum time stamp.

Placement and components


The code of contracts in the blockchain is not changeable. And in the event of changes, you must post a new version of the contract. At the same time from the old version you need to transfer data (state). This may not always be easy to do, since the state of the contact is not accessible from the outside. And if the contract does not initially have functions to access the state, then the transfer will be virtually impossible. Similar to the state, it is impossible to transfer funds from a contract without functions previously prepared for this.

To solve these problems, the Joule contract is divided into several separate contracts:

Structure

The Joule Ecosystem consists of the following contracts:


This architecture allows you to change the logic of Joule without losing data. No need to transfer or copy data. No need to make mechanisms for transferring funds (which may be vulnerable to hacker attacks).

Using the proxy contract simplifies integration with the Joule system for external developers. They do not need to change the address of the contract if changes occur in Joule . It will simplify the check in the registered contracts that the call comes from Joule .

Using


In this part we describe how you can use the resulting system.

Mining


Any user on the network who has an address and enough funds to make a call can multiply their funds with the help of Joule .

To do this, it is sufficient at the right time to make a transaction with a call to Joule (method invoke or invokeOnce ). During the transaction, Joule will transfer to the caller the reward that was reserved during the registration of the contract.

The moment you should make a call can be easily determined using the getTop method. This method returns the current status of the queue in Joule : the time of the nearest call, the minimum gas, the amount of remuneration for each contract and other values.

The gas size for the transaction that the miner indicates should be no less than the value specified in the invokeGas field for the nearest contract. Gas can be assigned with a reserve - unused gas will be returned back (funds for it will not be written off). There are cases that there are several contracts ready to be called up for execution. In this case, it is better to put gas equal to the amount of gas of all contracts. Then (in the case of the invoke method) several contracts will be called up by one transaction, and the miner will receive a reward for each.

When registering a contract, the developer sets the estimated value of the cost of gas (but not less than the value specified at least). To call a miner must pick up such a value of gas that his transaction was faster than others (if any), but was not more expensive than the amount of remuneration. The value of the gas cost and the amount of remuneration can be found using the getTop method.

In case of a successful call, Joule publishes the Invoked event, by which you can find all your successful calls and find out the exact amount of the reward.

Register your contracts


If for some purposes there is a need to get a call to the contract at the appointed time - then the best solution to this problem is Joule .

To register a call for a contract for a specific time, you must call the register method with the following parameters:


It is necessary to transfer the sum in the air for the remuneration for the call together with the register call. The exact amount of the sum can be obtained using the getPrice method. In case an excess amount is transferred, the balance will be returned to the caller.

The contract must implement the check method. If early calls can violate the logic of the contract or create a vulnerability, then you should add a check that the call was from Joule . If the contract is already in the network, and there is no possibility to add the check method to it, then you can use the intermediary contract that implements the desired method and invokes the target contract. Then when registering at Joule, you must specify the address of the contract of intermediary.

In case of successful registration, Joule publishes the Registered event, with which you can see all your registrations.

Both types of interaction can be implemented directly by working with a contract through an Ethereum network client, for example, Parity or Geth (Mist) .

Also, for convenience, Joule has a web interface that allows you to make a call or register your contract using only a browser with an extension installed in it, such as Metamask or without an extension at all, for example, through MEW .

Conclusion


The result is a solution that allows you to bypass the platform restriction of smart contracts - to call the contract at the appointed time without the direct participation of the user.

Joule code is available on github .

Source: https://habr.com/ru/post/348284/


All Articles