Smart Contracts

Smart contracts are units of code that perform logic on the blockchain in response to input. With a Dragonchain L1 business node, 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. Dragonchain solves this problem by keeping smart contract logic private to the L1 chain. This not only reduces attack vectors on smart contract code, but allows for rapid deployment, testing, and updating of smart contracts.

Deployment

Dragonchain executes smart contracts as a docker image. These images can be hosted anywhere, public or private. Private images require authorization to pull the image.

$ dctl 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
}

Dragonchain recommends using image tags so that a smart contract code does not unexpectedly update to a newer version. The version of the image in use can be updated using dctl:

$ dctl contract update "b7165579-c9bb-46f7-8853-ae1c1939883f" -I "dragonchain/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.

Dragonchain 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

Dragonchain 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. Example contracts using input from a Dragonchain can be found here.

Options

Usage guidelines of dctl 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 Dragonchain token smart contract takes an environment variable that specifies the total token supply the contract mints.

Smart contracts can be invoked automatically on an interval or cron. 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.

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.

Invocation

To 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 on, and the result ledgered on chain.

$ dctl 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:

$ dctl 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:

$ dctl 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

Dragonchain uses a delayed invocation system to execute smart contracts. The input transaction or scheduled invocation causes the Dragonchain 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, Dragonchain 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:

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

$ dctl transaction query "my-contract" '@invocation:{dfd9601f\\-b36a\\-4b0e\\-ba9d\\-7ad36e6c7453}'
{
  "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="
        }
      },
  }
}

Heap

Another benefit of Dragonchain 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 Dragonchain managed service. The heap can be accessed with dctl or the Dragonchain 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.

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

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

Demonstration

To see a full demonstration of the smart contract heap, view our example smart contracts.