Smart Contracts

Smart contracts are units of code that perform logic on the blockchain in response to input. With a Dragonchain business chain (L1), 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 business chain (L1). 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 (dctl), and software developer kits (SDK) for multiple languages.

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 from a repository. To create a smart contract, use the dctl contract create command…

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

A smart contract running on a Dragonchain business chain can also be versioned and updated as a business needs. Dragonchain 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 dctl contract update command:

$ 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 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 Dragonchain 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.

$ 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="
        }
      },
  }
}

Smart Contract State - 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.