NAV
Go Shell Ruby Python JavaScript C# Java

Introduction

Chainspace is a smart contract platform offering speedy consensus and unlimited horizontal scalability. Our goal is to build a blockchain platform that scales like the web scales: adding machines should increase overall capacity, and there should be no built-in scalability limitations.

At present, our running code has two main components:

Originally, both of these components existed within the same Go codebase. As of this writing, we are halfway between splitting these two components, so Blockmania exists in two places:

A project wanting only fast consensus, but no sharding, should be able use Blockmania by itself. For projects that need the added horizontal scalability of sharding, the Chainspace component would be added. From a platform development point of view, getting a build working with the extracted Blockmania codebase in the blockmania repo is a big priority. But at least this way you can use Blockmania on its own, if that’s what you want.

Quickstart

Installation

If everything worked, the chainspace command should be installed and available. Running it will give an overview of available functionality. Help flags are available, have a look.

Initialize a network

The chainspace init {networkname} command, by default, creates a network consisting of 12 nodes grouped into 3 shards of 4 nodes each.

Initialize a new Chainspace network called “foonet”

chainspace init foonet

The setup you get from that is heavily skewed towards convenient development rather than production use. It will change as we get closer to production.

Have a look at the config files for the network you’ve generated (stored by default in ~/.chainspace/{networkname}). The network.yaml file contains public signing keys and transport encryption certificates for each node in the network. Everything in network.yaml is public, and for the moment it defines network topology. Later, it will be replaced by a directory component.

Each node also gets its own named configuration directory, containing:

Running nodes

In the default localhost setup, nodes 1, 4, 7, and 10 comprise shard 1. Run those node numbers if you’re only interested in seeing consensus working. Otherwise, start all nodes to see sharding working as well.

Here’s how you can init a network and start each node individually

make contract # you only need to do this once, it installs a dummy Docker contract
chainspace contracts foonet create # you only need to do this once, it links the Docker contract to your nodes
rm -rf ~/.chainspace/foonet
chainspace init foonet
chainspace run foonet 1
chainspace run foonet 4
chainspace run foonet 7
chainspace run foonet 10

A convenient script runner is also included, so you don’t need to start nodes individually in development. The short way to run it is

make contract # you only need to do this once, it installs a dummy Docker contract
chainspace contracts foonet create # you only need to do this once, it links the Docker contract to your nodes
rm -rf ~/.chainspace/foonet
chainspace init foonet
script/run-testnet foonet

This will fire up a single shard which runs consensus, and make it available for use.

REST documentation

Many parts of the system are available to poke at via a RESTful HTTP server interface. After starting a node locally, you can see what’s available by going to http://localhost:9001/swagger/index.html - where 9001 is the node’s HTTP server port as defined in ~/.chainspace/{networkname}/node-{nodenumber}/node.yaml. In the default generated config, REST port number is http://localhost:{9000+nodenumber} for each node.

Consensus

Introduction

Consensus algorithms allow nodes in a distributed system to agree on a specific order of transactions without a central authority.

Incoming transactions arrive at nodes in an arbitrary order, potentially even at the same time. Participating nodes then talk to each other, and agree on an ordering of transactions which is guaranteed to be the same for all of them.

Blockmania is a Byzantine Fault Tolerant consensus algorithm. It functions correctly even if 13 participants are faulty or actively acting as attackers, with no loss of liveness or safety. In conditions where more than 13 of nodes are bad, Blockmania prioritises safety over liveness. Attackers or faulty nodes can stop processing for the cluster, but they cannot inject bad data.

Blockmania nodes group incoming transactions into blocks, and exchange signed data and witness statements which are then propagated to all participating nodes. Each node writes out the same signed sequence of blocks, or blockchain, as every other node participating in a given consensus group. A chain of concatenated block hashes ensures that data cannot be arbitrarily changed by any participating nodes, even dishonest ones.

If you’re curious about how it works at a deeper level, please read the Blockmania academic paper.

The Chainspace codebase builds Blockmania into itself, but doesn’t expose any network interface for sending transactions. If you’re a Go coder, you can however access it programmatically using Go, something like this:

Sending data to consensus whne you have compiled a client. This only works in Go.

import "chainspace.io/chainspace-go/node"

s, err := node.Run(cfg)
if err != nil {
  log.Fatal(err)
}
s.Broadcast.AddTransaction(txdata, fee)

Using Blockmania by itself

It is also possible to use Blockmania by itself.

  1. Clone the Blockmania repo
  2. Run make install
  3. Run blockmania init -network-name foomania

This will generate, by default, a 4-node Blockmania network. Config files are at ~/.blockmania/foomania. Each node gets its own separate configuration.

You can run each node (1-4) by doing:

blockmania run -network-name foomania -node-id 1 blockmania run -network-name foomania -node-id 2 blockmania run -network-name foomania -node-id 3 blockmania run -network-name foomania -node-id 4

For development purposes, there’s also scripts/run-testnet which will generate a testnet and run it in a tmux.

Using REST

Each Blockmania node starts a REST API by default. Docs are available at http://localhost:8001 (for node 1, each successive node increments the port number by 1).

Swagger docs are available at http://localhost:8001/swagger/index.html

Subscribing to Blockmania events

You should be able to see the websocket routes in the included Swagger documentation on each node (although you won’t be able to try it out as our Swagger server doesn’t support websockets). For standalone Blockmania, the websockets server(s) start on http://localhost:7001/api/pubsub/ws (for node 1, each successive node increment the port number by 1).

Socket.io websockets don’t work, as they’re incompatible with the Go websocket implementation we’re using. Other websockets should work.

Alternately, you can use the pubsublistener, a Go utility we’ve included for you to watch events as they happen. It works like this:

pubsublistener -addrs 1=localhost:7001,2=localhost:7002,3=localhost:7003,4=localhost:7004

Try sending a transaction like this one through the POST /api/transaction endpoint in Swagger:

{
  "tx": [
    1
  ]
}

You should see the pubsublistener print out the Base64-encoded value of your input a few seconds after you send it.

Sharding

Introduction

Chainspace allows multiple Blockmania blockchains to talk to each other. Each grouping of Blockmania nodes becomes a Chainspace shard. Shards can operate independently of each other, so the system as a whole can scale horizontally and benefit from parallelism. Chainspace is a protocol which coordinates operations across more than one shard. Crucially, operations are atomic across shards: a transaction touching multiple contracts in multiple shards will either succeed or fail in its entirety.

Architectural overview

There are a few different moving parts:

  1. Clients
  2. Contracts
  3. Checkers
  4. Subscriptions

The basic application flow is as follows:

  1. The client sends information to a contract as a network request (currently implemented as a REST service). The contract checks its local datastore, and returns a transaction.
  2. The client sends the transaction to all nodes in the shard. Each node runs a checker on the transaction; the checker validates the transaction against the current state of the chain, signs the transaction and returns it.
  3. The client bundles up all the transaction signatures, and sends the bundled signatures and transaction data to one of the nodes in the shard. If more than 23 of nodes agreed that the transaction is valid, the transaction is then sequenced into the blockchain.
  4. Clients may subscribe to the blockchain to receive speedy notifications of updates.

Clients, contracts, and checkers can be implemented in any language.

Your first application

Assuming you know one of the languages we’ve documented, follow along by selecting it from the code examples on the right hand side of the page. The easiest way to learn is by doing, so we’ll show you how to build a smart contract for coins as a learning exercise, and explain Chainspace in more detail along the way.

Clients and client libraries

Client libraries exist in multiple languages, and we’re in the process of writing more. Please let us know if you’re interested in contributing a client in your favourite language.

Contract

Checker

Emulator

Sending transactions

TODO. Stu’s got a transaction we can use now I think.

This will be where we introduce the idea of different clients for each language. We can make that point first (hopefully to reduce cognitive load on our users). But later, we can detail how the whole process works, so people can develop their own clients in languages we don’t yet support.

Using clients

Using REST

Determinism

Key-Value store

The Key-Value store allows you to retrieve the current value of a given key.

To use it:

Building

Build the docker image for your contracts. You can do this using the makefile at the root of the repository:

$ make contract

In the future chainspace will pull the docker image directly from a docker registry according to which contract is being run. At present during development we’ve simply hard-coded in a dummy contract inside a Docker container we’ve made.

You may need to initialize the contract with your network: run chainspace contracts <yournetworkname> create.

Next, run the script script/run-sharding-testnet.

$ ./script/run-sharding-testnet 1

This script will:

The generated configuration exposes a HTTP REST API on all nodes. Node 1 starts on port 8001, Node 2 on port 8002 and so on.

Seeding trial objects using httptest

In order to test the key value store you can use the httptest binary (which is installed at the same time as the chainspace binary when you run make install).

$ httptest -addr "0.0.0.0:8001" -workers 1 -objects 3 -duration 20

This will run httptest for 20 seconds, with one worker creating 3 objects per transaction.

When it starts running you should see output like this on stdout:

seeding objects for worker 0 with 0.0.0.0:8001
new seed key: djEAAAAA7rrTmXyvwexDRbDXWAU4n/gJPBJOkB8BiXYe4+VKmaNHpYCMrXNVoA2Siiau+e9ouPOZOG5CNLhiCDQ2KAzU+9+36tPibLbBwYx/B7M9TpGbDgD7VBL5XakoVf87VQWx
new seed key: djEAAAAAhXIV02TbSOW4+H1I/qOQ+8hOYnXRb/xVEAgkSzuEPgHM/BjK7g1Rv8IE5LmwmfxGnMrBMlO2XNX3W1wiZNxYkDB1ywDd210TGUt7Q7ZEqCqa/SCB7L3q6tfk2hy22cCU
new seed key: djEAAAAATS95HL0ehRGfXleJaTkfdR8SqFSwtC1G34YhoGRhu4gqeqi6LMlzVkxTkbN/niEXcQI7dpFwSfcVuUQBmfHWZf8ZRuNNhyDqWHiR2nOEb5Y1vNiQPu3PVepaoaJFYZN4
creating new object with label: label:0:0
creating new object with label: label:0:1
creating new object with label: label:0:2
seeds generated successfully

This shows you the different labels which are created by the tests and associated to objects. You can then use these to get the id / value of your objects.

Retrieving objects

Call this http endpoint in order to retrieve the chainspace object’s versionId associated to your label.

curl -v http://0.0.0.0:8001/kv/get-versionid -X POST -H 'Content-Type: application/json' -d '{"label": "label:0:1"}'

Call this http endpoint to retrieve the object associated to your label:

curl -v http://0.0.0.0:8001/kv/get -X POST -H 'Content-Type: application/json' -d '{"label": "label:0:1"}'

You can see that the versionId associated to your label evolves over time, when transactions are consuming them.

Multiple shards

Running the Key-Value store with multiple shards should work fine, but there’s a caveat. Objects will change their versionIds on each update, and currently we’re sharding on versionId. So if you’re retrieving using versionId, your object may appear to migrate between shards whenever you update it. If you retrieve your object by label instead, you should be able to retrieve it, but only if you know which shard it’s living in. TODO: this interacts with the planned Directory component.

Pubsub

Interested clients can receive streaming notifications of changes to the blockchain through either websockets or raw TCP sockets.

This allows developers to:

Websockets

--enable-websockets

chainspace footest --shard-count 1 --enable-pubsub true

You should be able to see the websocket routes in the included Swagger documentation on each node (although you won’t be able to try it out as our Swagger server doesn’t support websockets).

Assuming you’re connecting on the first node on your local machine, you can make a websocket connection:

<script>
   var ws = new WebSocket("ws://0.0.0.0:9001/api/pubsub/ws");
   ws.onmessage = function (event) {
       console.log(event.data);
   }
</script>

TCP sockets

Port {7000 + nodenumber} by default (increments with the nodenumber). You can specify the port

Smart contracts

Overview

The moving parts:

Contracts

Checkers

Emulator

Clients

Note: we have a healthcheck URL at /healthcheck for all contracts.

Running a testnet

So far, this documentation has assumed that all nodes are running on a single machine. What if you want to run a testnet, with each node on a separate machine on a local network, or across the internet?

We have not yet implemented seed nodes, or a cryptographically secure method of peer discovery. So we have implemented some simple methods for stitching together networks until we are ready to commit to a final system.

Nodes currently find each other in two ways:

  1. mDNS discovery
  2. registry

mDNS discovery

In development or on private networks, nodes can discover each other using mDNS broadcast. This allows zero-configuration setups for nodes that are all on the same subnet.

Registry

Use the Registry when configuring nodes across the public internet. We run a public registry at https://registry.chainspace.io.

You can run your own Registry if you want. Init your network with the --registry flag if you plan to use a Registry server:

chainspace init foonet --registry registry.chainspace.io

The Registry will then appear in each node’s node.yaml:

registries:
- host: registry.chainspace.io
- token: 05b16f5d45377baff52c25e2c154a00b126f7b75b7345794d3e15535b49a03f955b9c355

The randomly-generated registry token ensures that a unique shared secret is used on a per-network basis so that multiple networks can share the same registry without any additional setup.

Nodes will automatically register themselves with the network’s registry server when they start up.

It is possible to use both the Registry and mDNS discovery at the same time, and have a sort of mixed network in operation.

Performance testing

Consensus

The chainspace genload {networkname} {nodenumber} command starts up the specified node, and additionally a Go client which floods the consensus interface with simulated transactions (100 bytes by default).

To get some consensus performance numbers, run this in 4 separate terminals:

Starting genload on nodes manually (make sure you’ve already got nodes running!)

rm -rf ~/.chainspace/foonet
chainspace init foonet
chainspace genload foonet 1
chainspace genload foonet 4
chainspace genload foonet 7
chainspace genload foonet 10

The client keeps increasing load until it detects that the node is unable to handle the transaction rate, based on timing drift when waking up to generate epochs. At that point the client backs off, and in general a stable equilibrium is reached. The genload console logs then report on average, current, and highest transactions per second throughput.

A convenient script runner is included.

Using the convenience script, you don’t need to start the nodes yourself; the script will handle it

rm -rf ~/.chainspace/foonet
chainspace init foonet
script/genload-testnet foonet

This will start nodes 1, 4, 7 and 10 in tmux terminals, pumping transactions through automatically.

Sharding

[TODO: Jeremy’s got some perf test scripts which we can document.]

FAQs

Blockmania

If other consensus protocols could work, why did you build Blockmania?

When we needed a consensus protocol for Chainspace, we couldn’t find a PBFT implementation we liked in a language we liked. Tendermint was the best candidate available, but at the time there wasn’t really formal description of how it works (this was remedied in summer 2018). So we decided to come up with our own consensus protocol, which we understood from the ground up.

What’s the difference between Blockmania and similar protocols?

  1. Blockmania is leaderless
  2. Blockmania radically separates network code from consensus code

Leaderlessness

A lot of the complexity in other PBFT-descended protocols comes from the fact that each round of consensus is typically determined by a single leader. For the next round, or block, leadership then moves to another leader. This is fine when the leader is honest; but if the leader is an attacker, the other nodes all need to be able (a) to detect that the leader is an attacker, and (b) elect a new leader, while the older leader is sending potentially confusing or contradictory messages. The code needed to handle that is complex.

We started with a simple idea. If leaders add a lot of complexity, can we get rid of leaders? And we found that we could. This allowed us to write simpler code. Simpler code is typically more secure and also easier to implement, so it was a big win.

Separation of networking and consensus

The fact that Blockmania is leaderless also allowed us to separate networking code from consensus. Nodes simply take transactions in, make blocks, and broadcast them to other nodes, as fast as they can. A separate step, totally separate from network code, allows each node to independently reach a view of what other nodes have seen based on a simulation and their signed witness statements.

The complete separation of network code from consensus again simplifies the protocol, making it easier to implement and probably more secure than it would otherwise have been.

Developer documentation

TODO: there’s platform dev, and also a need for SBAC client dev in multiple languages.

Platform development

You will need to the following to get Chainspace running locally:

To test that Docker is working run docker run hello-world in a shell. Fix any errors before proceeding.

With these requirements met, run make install. This will build and install the chainspace binary as well as the httptest load generator. You can generate a new set of shards, start the nodes, and hit them with a load test.

See the help documentation (chainspace -h and httptest -h) for each binary.

Committing

Please use Git Flow - work in feature branches, and send merge requests to develop.

Versioning

The version of the Chainspace application is set in the VERSION file found in the root of the project. Please update it when creating either a release or hotfix using the installed Git hooks mentioned above.

Adding dependencies

Dependency management is done via dep. To add a dependency, do the standard dep ensure -add <dependency>. We attempted to use go mod (and will go back to it when it stabilises). Right now mod breaks too many of our tools.

Client development

We currently have a Chainspace client for JavaScript. More would be nice.