(image)

Programming Hotmoka
A tutorial on Hotmoka and smart contracts in Takamaka

8.5 Node services

(See the io-hotmoka-tutorial-examples-runs project in https://github.com/Hotmoka/hotmoka)

This section shows how we can publish a Hotmoka node online, by using Java code, so that it becomes a network service that can be used, concurrently, by many remote clients. Namely, we will show how to publish a blockchain node based on Tendermint, but the code is similar if you want to publish a memory Hotmoka node or any other Hotmoka node.

Remember that we have already published our nodes online, as network services, by using the command moka nodes [disk|mokamint|tendermint] init. Here, however, we want to do the same operation in code.

Create a class Publisher.java inside the package io.hotmoka.tutorial.examples.runs of the io-hotmoka-tutorial-examples-runs project. Use the following code for the class:

package io.hotmoka.tutorial.examples.runs;


import java.math.BigInteger;
import java.nio.file.Paths;
import java.security.KeyPair;


import io.hotmoka.crypto.Entropies;
import io.hotmoka.crypto.SignatureAlgorithms;
import io.hotmoka.node.service.NodeServices;
import io.hotmoka.node.tendermint.TendermintConsensusConfigBuilders;
import io.hotmoka.node.tendermint.TendermintInitializedNodes;
import io.hotmoka.node.tendermint.TendermintNodeConfigBuilders;
import io.hotmoka.node.tendermint.TendermintNodes;
import io.takamaka.code.constants.Constants;


public class Publisher {
    public static void main(String[] args) throws Exception {
        var config = TendermintNodeConfigBuilders.defaults().build();


        // the path of the runtime Takamaka jar, inside Maven's cache
        var takamakaCodePath = Paths.get
            (System.getProperty("user.home") +
            "/.m2/repository/io/hotmoka/io-takamaka-code/" + Constants.TAKAMAKA_VERSION +
            "/io-takamaka-code-" + Constants.TAKAMAKA_VERSION + ".jar");


        // create a key pair for the gamete
        var signature = SignatureAlgorithms.ed25519();
        var entropy = Entropies.random();
        KeyPair keys = entropy.keys("password", signature);
        var consensus = TendermintConsensusConfigBuilders.defaults()
            .setPublicKeyOfGamete(keys.getPublic())
            .setInitialSupply(BigInteger.valueOf(100_000_000))
            .build();


        try (var original = TendermintNodes.init(config);
            // uncomment the next line if you want to publish an initialized node
            // var initialized = TendermintInitializedNodes.of(original, consensus, takamakaCodePath);
            var service = NodeServices.of(original, 8001)) {


            System.out.println("\nPress ENTER to turn off the server and exit this program");
            System.in.read();
        }
    }
}

We have already seen that original is a Hotmoka node based on Tendermint. The following line makes the feat:

var service = NodeServices.of(original, 8001);

Variable service holds a Hotmoka node service, that is, an actual network service that adapts the original node to a web API that is published at localhost, at port 8001. The service is an AutoCloseable object: it starts when it is created and gets shut down when its close() method is invoked, which occurs, implicitly, at the end of the scope of the try-with-resources. Hence, this service remains online until the user presses the ENTER key and terminates the service (and the program).

Run class Publisher:

cd io-hotmoka-tutorial-examples-runs
mvn clean install exec:exec -Dexec.executable="java" -Dexec.args="-cp %classpath io.hotmoka.tutorial.examples.runs.Publisher"

It should work for a few seconds and then start waiting for the ENTER key. Do not press such key yet! Since original is not initialized yet, it has no manifest and no gamete. Its store is just empty at the moment. You can verify that by running moka nodes manifest show, whose result should be:

The remote service is misbehaving: are you sure that it is actually published at ws://localhost:8001 and that it is initialized and accessible?

since it cannot find a manifest in the node.

Therefore, let us initialize the node before publishing it, so that it is already initialized when published. Press ENTER to terminate the service, then modify the Publisher.java class by uncommenting the use of the InitializedNode decorator, whose goal is to create manifest and gamete of the node and install the basic classes of the Takamaka runtime inside the node.

Note that we have published original:

var service = NodeServices.of(original, 8001);

but we could have published initialized instead:

var service = NodeServices.of(initialized, 8001);

The result would be the same, since both are views of the same node object. Moreover, note that we have initialized the node inside the try-with-resources, before publishing the service as the last of the three resources. This ensures that the node, when published, is already initialized. In principle, publishing an uninitialized node, as done previously, exposes to the risk that somebody else might initialize the node, hence taking its control since he will set the keys of the gamete.

If you re-run class Publisher now and retry the moka nodes manifest show command, you should see the manifest of the now initialized node on the screen.

A Hotmoka node, once published, can be accessed by many users, concurrently. This is not a problem, since Hotmoka nodes are thread-safe and can be used in parallel by many users. Of course, this does not mean that there are no race conditions at the application level. As a simple example, if two users operate with the same paying externally owned account, their wallets might suffer from race conditions on the nonce of the account and they might see requests rejected because of an incorrect nonce. The situation is the same here as in Ethereum, for instance. In practice, each externally owned account should be controlled by a single wallet at a time.