Getting Started with Raiinmaker Smart Contracts

Smart Contracts

Smart contracts are units of code that perform logic on the blockchain in response to input. With a Coiin enterprise chain, any transaction can be used to invoke a contract by it’s transaction type.

A major pain point with how other blockchains handle smart contracts involves the distributed nature of the execution environment; once a smart contract is deployed, it is public information and any bugs are not only visible, but permanent. Coiin solves this problem by keeping smart contract logic private to the enterprise chain. This not only reduces attack vectors on smart contract code, but also allows for a more powerful development environment, rapid deployment, testing, and updating of smart contracts.

Because of its hybrid architecture, a developer can use any executable language to develop a smart contract (such as Python, NodeJS/Javascript, Golang, C#, Java, and BASH shell). The system itself provides a RESTful interface, a command line interface (coiin), and software developer kits (SDK) for multiple languages.

Deployment

Raiinmaker Network EAL executes smart contracts as a docker image. These images can be hosted anywhere, public or private. Private images require authorization to pull the image from a repository. To create a smart contract, use the coiin contract create command…

$ coiin contract create "my-contract" "my-contract:latest" "node" "index.js"
{
  "status": 202,
  "response": {
    "dcrn": "SmartContract::L1::AtRest",
    "version": "1",
    "txn_type": "my-contract",
    "id": "b7165579-c9bb-46f7-8853-ae1c1939883f",
    "status": {
      "state": "Pending",
      "msg": "Contract creating",
      "timestamp": "2019-11-05 21:25:09.806836"
    },
    "image": "my-contract:latest",
    "auth_key_id": null,
    "image_digest": null,
    "cmd": "node",
    "args": [
      "index.js"
    ],
    "env": null,
    "existing_secrets": null,
    "cron": null,
    "seconds": null,
    "execution_order": "parallel"
  },
  "ok": true
}

A smart contract running on a Raiinmaker Network EAL business chain can also be versioned and updated as a business needs. Raiinmaker Network EAL recommends using image tags so that a smart contract does not unexpectedly update to a newer version. The version of the image in use can be updated using the coiin contract update command:

$ coiin contract update "b7165579-c9bb-46f7-8853-ae1c1939883f" -I "coiin/btc-publisher:1.0.1-prod"
{
  "status": 202,
  "response": {
    "dcrn": "SmartContract::L1::AtRest",
    "version": "1",
    "txn_type": "my-contract",
    "id": "b7165579-c9bb-46f7-8853-ae1c1939883f",
    "status": {
      "state": "updating",
      "msg": "",
      "timestamp": "2019-11-08 19:01:17.055077"
    },
    ...
}

Docker

Smart contracts take their command and arguments as parameters when being deployed. These fields can be updated freely. Any ENTRYPOINT line specified in the Dockerfile will be ignored.

Raiinmaker Network EAL has a maximum size on smart contract images (1GB). It’s recommended that images are as small as possible and only necessary packages are installed. In general, follow Docker best practices.

Execution Environment

Raiinmaker Network EAL smart contracts accept user input from stdin and report output using stdout. If there is information that needs to be printed without being part of smart contract output, use stderr.

Options

Usage guidelines of coiin commands can be viewed by using -h or --help.

Users can specify environment variables and secrets for a smart contract. This allows parameters specified at deployment to configure the contract’s behavior. For example, the Raiinmaker Network EAL token smart contract takes an environment variable that specifies the total token supply for a token.

The registry credentials parameter provides a way to pull images from a private docker repository without exposing those credentials to the chain or other smart contracts. This field accepts a base64 encoded basic auth string.

Custom indexes allow control over what data is indexed and searchable through transactions. Learn more about custom indexes in the transactions section.

See below for schedule and cron parameters for use in scheduling automatic smart contract executions on an interval or schedule.

Cron Smart Contracts, Time Based Execution, and Smart Contract Oracles

A unique feature of Raiinmaker Network EAL smart contracts is that they can be invoked automatically on a scheduled time interval or based on a crontab configuration. The schedule parameter takes a number (in seconds) that specifies the interval between each automatic invocation. This is intended for use cases that need to run more frequently than once per minute. The cron parameter accepts a cron string and provides a more robust solution for scheduling automatic invocations. These parameters are mutually exclusive. With these options, a smart contract can act as a “self oracle” in a system wherein a business chain can pull its own data on any necessary schedule.

Invocation

To directly invoke a smart contract, post a transaction with the smart contract name as the transaction type. The payload will be passed directly to the smart contract, executed, and the result ledgered on chain.

$ coiin transaction create "my-contract" '{ "name": "banana" }'
{
  "status": 201,
  "response": {
    "transaction_id": "a436a108-4505-44dd-b5bf-71e3ed3eed70"
  },
  "ok": true
}

Query

Smart contracts can queried in a similar way to blocks. Contract details can be read from chain directly using the unique ID:

$ coiin contract get "b7165579-c9bb-46f7-8853-ae1c1939883f"
{
  "status": 200,
  "response": {
    "dcrn": "SmartContract::L1::AtRest",
    "version": "1",
    "txn_type": "my-contract",
    "id": "b7165579-c9bb-46f7-8853-ae1c1939883f",
    "status": {
      "state": "active",
      "msg": "Contract update success!",
      "timestamp": "2019-11-08 19:01:26.960557"
    },
    ...
  },
  "ok": true
}

To list all smart contracts that exist on a chain, use the list command:

$ coiin contract ls
{
  "status": 200,
  "response": {
    "smart_contracts": [
      {
        "dcrn": "SmartContract::L1::AtRest",
        "version": "1",
        "txn_type": "my-contract",
        "id": "b7165579-c9bb-46f7-8853-ae1c1939883f",
        "status": {
          "state": "active",
          "msg": "Contract update success!",
          "timestamp": "2019-11-08 19:01:26.960557"
        },
        ...
      },
      {
        "dcrn": "SmartContract::L1::AtRest",
        "version": "1",
        "txn_type": "biggermoney",
        "id": "1868e4e9-fded-4d46-ac4f-e7f301195b8b",
        "status": {
          "state": "active",
          "msg": "Creation success",
          "timestamp": "2019-10-02 20:10:28.767723"
        },
        ...
      },
    ]
  },
  "ok": true
}

Invocation Order

Raiinmaker Network EAL uses a delayed invocation system to execute smart contracts. The input transaction or scheduled invocation causes the Raiinmaker Network EAL to request invocation of the smart contract, which is put into a queue. Serial contracts process one request at a time in the order they were received, while parallel contracts process as many requests as possible in parallel. Both types of contract have a potential delay between invocation request and invocation response when the smart contract finishes executing. This could cause a noticeable gap if a smart contract takes a significant amount of time to complete or has a large backlog of invocation requests to process.

To find the response to a request, Raiinmaker Network EAL immutably links invocation request to invocation response with the invocation field in a transaction header. Smart contract invocation responses can be queried using the invocation header:

$ coiin transaction create "my-contract" '{ "name": "banana" }'
{
  "status": 201,
  "response": {
    "transaction_id": "dfd9601f-b36a-4b0e-ba9d-7ad36e6c7453"
  },
  "ok": true
}

$ coiin t q 'my-contract' '{"tnx_type": "my-contract", "invoker": "ed0faad1-b3a0-44c8-93a2-212bf42d3357"}'
{
  "status": 200,
  "response": {
    "total": 1,
    "results": [
      {
        "version": "2",
        "dcrn": "Transaction::L1::FullTransaction",
        "header": {
          "txn_type": "my-contract",
          "dc_id": "28GiivQE5m8a9oyvFD33JgwnBpgyZp2RxtTVFGjcRPVDJ",
          "txn_id": "62789d91-def8-48b8-b239-7b709783ab9e",
          "block_id": "28201268",
          "timestamp": "1573244557",
          "tag": "",
          "invoker": "dfd9601f-b36a-4b0e-ba9d-7ad36e6c7453"
        },
        "payload": {
          "rawResponse": "hello world"
        },
        "proof": {
          "full": "v882YkuVjkhbG0oUFDk9+XNE0otVK/ENGQXdb3OK5Rs=",
          "stripped": "MEUCIQD1CzCK5Qf+rdejBpDvq3cJd41NQRa80vVbdLSxpRLNMgIgNmwo8FguPH2brkCwVJO5IfJEH0itdcuze9nfMGs+8Mw="
        }
      },
  }
}

Smart Contract State - Heap

Another benefit of Raiinmaker Network EAL smart contracts is they can easily become stateful by using the built in heap. By default, all smart contract output that is valid JSON is parsed and put into the heap. This behavior can be changed using the OUTPUT_TO_HEAP key in smart contract output.

Heap Mechanics

Heap data is stored encrypted on disk or in the Raiinmaker Network EAL managed service. The heap can be accessed with coiin or the Raiinmaker Network EAL SDKs. Smart contracts can use the get smart contract object method to read values from their heap. Smart contracts do not directly write to the heap, but instead return updated values for the chain to commit in a future block. The heap has a similar structure to unix file systems.

$ coiin contract object ls --smartContractId "06b84b55-fac8-461d-86bb-22b55c40b8df"
{
  "status": 200,
  "response": [
    "/apple",
    "/banana",
    "/error",
    "/rawResponse",
    "/version"
  ],
  "ok": true
}

$ coiin contract object get --smartContractId "06b84b55-fac8-461d-86bb-22b55c40b8df" '/apple'
{
  "status": 200,
  "response": "4",
  "ok": true
}

Last updated