Developer Guide

In this tutorial, we will guide you through how to create new ENI operations with libENI in C++.

Prerequisites

In order to build your ENI operations, you need to install libeni-dev first.

See Getting Started for more information.

Implement an ENI Operation

Here, we use examples/eni/reverse as an example. In this example, we will create an ENI operation called reverse that takes a string, and returns the reversed string.

The below code piece shows how developers use this ENI operation when writing a contract in Solidity.

string memory reversed;
reversed = eni("reverse", "The string to be reversed.");

Subclass EniBase

In order to implement an ENI operation, you need to #include <eni.h>, create a subclass of eni::EniBase, and implement the following functions.

  1. A constructor that takes a string as its parameter. Remember to pass the string to the constructor of the superclass, eni::EniBase, which will convert the raw string into a json::Array containing the arguments for your ENI operation.
  2. A destructor.
  3. Three pure virtual functions, which should be implement privately.
    • parse to parse the arguments.
    • gas to calculate gas consumption from the arguments.
    • run to execute your ENI operation with the arguments.
#include <eni.h>
class Reverse : public eni::EniBase {
public:
  Reverse(const std::string& pArgStr)
    : eni::EniBase(pArgStr) { ... }

  ~Reverse() { ... }

private:
  bool parse(const json::Array& pArgs) override { ... }

  eni::Gas gas() const override { ... }

  bool run(json::Array& pRetVal) override { ... }
};

The parse function takes a json::Array containing the arguments given to your ENI operation. To ensure the other two functions gas and run process the arguments in the same way, please validate, preprocess, and store the arguments into member variables in the parse function.

The parse function should return true when all arguments are good, and return false otherwise. (i.e. when the given arguments are not correct, e.g., lacking arguments, or wrong type).

In this example, the json::Array constructed by eni::EniBase contains only the argument string for ENI operation reverse.

["The string to be reversed."]

Here we just take the first argument and convert it to a string.

class Reverse : public eni::EniBase {
  ...
private:
  bool parse(const json::Array& pArgs) override {
    m_Str = pArgs[0].toString();
    return true;
  }

  std::string m_Str;
};

Check the documentation to see more detail about how arguments are converted into a json::Array.

Before your ENI operation is run, you need to estimate how much gas it will cost. Override the pure virtual function gas, and return your estimated gas cost.

In this example, we use the string length as its gas consumption.

class Reverse : public eni::EniBase {
  ...
private:
  eni::Gas gas() const override {
    return m_Str.length();
  }
};

Return 0 when error occurs (e.g., gas is incalculable).

Override the pure virtual function run, and push the result of your ENI operation back into the json::Array.

class Reverse : public eni::EniBase {
  ...
private:
  bool run(json::Array& pRetVal) override {
    std::string ret(m_Str.rbegin(), m_Str.rend());
    pRetVal.emplace_back(ret);
    return true;
  }
};

Return true only when your ENI operation is successfully executed.

Export the ENI Operation with C Interface

Your ENI operation will be called via its C interface, so be sure to export the C interface with ENI_C_INTERFACE(OP, CLASS), where OP is your ENI operation name (i.e., reverse in this example), and CLASS is the name of implemented class (i.e., Reverse in this example).

ENI_C_INTERFACE(reverse, Reverse)

Build the ENI Operations Into a Shared Library

Please add these flags -std=c++11 -fPIC when compiling your ENI operation into a shared library. See GCC Option Summary for explanation to these flags.

Specify the path to libENI headers with -I${LIBENI_PATH}/include.

You might also want to link to libENI by specifying the path -L${LIBENI_PATH}/lib, and the library name -leni.

Here is an example Makefile for examples/eni/reverse. Please be aware that the flags and commands might differ if you’re using different compilers.

CPPFLAGS=-I${LIBENI_PATH}/include
CXXFLAGS=-std=c++11 -fPIC
LDFLAGS=-L${LIBENI_PATH}/lib
LDADD=-leni

all:
      g++ ${CPPFLAGS} ${CXXFLAGS} ${LDFLAGS} -shared -oeni_reverse.so eni_reverse.cpp ${LDADD}

Test Your ENI Operations

Test From EniBase Interface

Your ENI operations will only be accessed from the two public member functions of eni::EniBase.

  • Gas getGas() should return the gas cost of your ENI operation.
  • char* start() should run your ENI operation and return the results in JSON format.

You may test your subclass through these two public functions.

eni::EniBase* functor = new Reverse("[\"Hello World\"]");
ASSERT_NE(functor, nullptr);
EXPECT_EQ(functor->getGas(), 12);
char* ret = functor->start();
EXPECT_EQ(::strcmp(ret, "[\"!dlroW olleH\"]"), 0);
free(ret);
delete functor;

Test From Shared Library Interface

See the documentation for how to test the shared libraries of your ENI operations.