ConsenSys/Local-Raiden

Name: Local-Raiden

Owner: ConsenSys

Description: Run a local Raiden network in docker containers for demo and testing purposes.

Created: 2017-12-22 16:08:05.0

Updated: 2018-01-11 23:14:12.0

Pushed: 2018-01-06 21:33:04.0

Homepage: null

Size: 92

Language: JavaScript

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

A local Raiden test network

Introduction

The goal is to set up a local Raiden network running in Docker containers, allowing for easier exploration and experimentation than using the Ropsten testnet version.

Advantages:

The set-up is a single Docker container representing the Ethereum blockchain, in the form of a geth --dev node, and N containers for Raiden clients that communicate with each other and the blockchain.

Raiden is at the “Developer Preview” stage, and comes with a disclaimer and notes. The official Dev Preview version is 0.2.0, but that has a bug around closing channels. For this reason we are using a more recent commit. Unfortunately, that breaks the nice Web GUI… we'll have to live without it for now.

Background reading on Raiden:

Setting up
Prerequisites

There are some prerequisites:

  1. Docker (with docker-compose), and
  2. Node.js, with web3 installed. web3 must be version 1.0.
  3. The Solidity compiler

My environment:

ame -a
x Lubuntu-Nov17 4.13.0-21-generic #24-Ubuntu SMP Mon Dec 18 17:29:16 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
cker --version
er version 17.12.0-ce, build c97c6d6
de --version
.3
m --version
0
m list web3
e/ben/Everything/Ethereum/Raiden/Local-Raiden
web3@1.0.0-beta.26
sr/local/bin/solc --version
, the solidity compiler commandline interface
ion: 0.4.19-develop.2018.1.3+commit.c4cbbb05.Linux.g++
Cloning

Since we include the Raiden repository as a submodule, you will likely need to do the following after cloning (or do a recursive clone).

submodule update --init
Docker images

We use the official Docker image for Geth, which you can get with,

er pull ethereum/client-go

We need to build an image for the Raiden client. Do the following in the Local-Raiden directory. This will take a while, but needs doing only once.

er build -t my/raiden .
Raiden contract deployment

Raiden essentially consists of two components.

  1. The Raiden client that we built above. This communicates with other Raiden clients to make payments, and occasionally with the blockchain to open, close and settle payment channels.

  2. A set of smart contracts that need to be deployed.

So, we need to deploy the contracts to our test network. Raiden provides a Python script for this, but my Python-fu is weak and I ended up with all sorts of dependency issues. So I reverted to something more familiar and made the deploy.js Node.js script instead.

Working in the Local-Raiden directory, first clear out any old Geth or Raiden data. Do not do this on your Mainnet configuration! This deletes the keystore.

rf keystore geth raiden_data geth.ipc

Now check the configuration options near the top of the deploy.js file and adjust anything that needs adjusting. For example, you may need to edit the path to the Solidity compiler, SOLC.

Start a Geth Docker container. All the config is already set up in the docker-compose.yml file, so we can just do the following.

er-compose run -u $UID geth

Finally, run the deployment script.

G=1 node deploy.js

This will output progress, and a summary of the final status. It will also create a .env file containing environment variables to be passed to docker-compose.

Finally, stop Geth (Ctrl-C will do it). All being well, everything is now set up on the blockchain within the geth directory. Don't delete this directory if you want to keep the blockchain state.

Discussion

The output should resemble the following, different addresses for accounts and contracts.

ary
===

oyment account: 0x78910ad1D145B20fdcd31B20D43D82dd998C194A
unt_0: 0x19E7E376E7C213B7E7e7e46cc70A5dD086DAff2A
lance: 2000000000000000000000
kens:  250000
unt_1: 0x1563915e194D8CfBA1943570603F7606A3115508
lance: 2000000000000000000000
kens:  250000
unt_2: 0x5CbDd86a2FA8Dc4bDdd8a8f69dBa48572EeC07FB
lance: 2000000000000000000000
kens:  250000
unt_3: 0x7564105E977516C53bE337314c7E53838967bDaC
lance: 2000000000000000000000
kens:  250000
overy contract: 0x019Ae5c6C16C1356ccDe9cc2DB5415a259a0F2C5
stry contract:  0xd1AFb72FFA57e4163175EFB9179bB63b500BB3b0
n contract:     0x2e8EdB207922794aEcB1A2cDC5a730612eefF034

The deployment script performs the following tasks.

  1. Transfers (test) Ether from Geth's pre-funded account to the four accounts that we have set up in keystore/.

  2. Deploys contracts as follows,

    a. the Discovery contract, EndpointRegistry.sol

    b. the Netting Channel Library contract, NettingChannelLibrary.sol

    c. the Channel Manager Library contract, ChannelManagerLibrary.sol

    d. the Registry contract, Registry.sol

    e. an ERC20 Token contract, Token.sol

  3. For all of the above, and in addition ChannelManagerContract.sol and NettingChannelContract.sol, the ABI is saved in the abis folder for later use.

  4. The tokens in the ERC20 token contract are split equally between the four accounts.

  5. Values required by docker-compose are written to the .env file. This .env file is also a useful reference for looking up account and contract addresses later.

Running Raiden

Now all we need to do to start the whole network is,

er-compose up -d

To shut it all down,

er compose down

Geth and Raiden will store their intermediate states in the geth and raiden_data directories respectively, so you can do this repeatedly.

This sets up two Raiden containers along with a Geth container, configured as follows.

You can see the console output of each container with docker logs. The container names are output by docker-compose, or by executing docker ps.

Exploring Raiden

The below is a just “getting started”. I hoping to work on some articles digging deeper into what's going on “under-the-hood”, including mediated transfers that involve multiple nodes. Watch this space…

Now that our Raiden nodes are running, it is possible to interact with them via their RPC interfaces directly from the shell command line:

rl http://172.13.0.3:5001/api/1/address
r_address": "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"}
rl http://172.13.0.4:5001/api/1/address
r_address": "0x1563915e194d8cfba1943570603f7606a3115508"}

But this quickly becomes tedious, especially for the more complex operations. I've created some JavaScript classes in the modules/ directory to make this easier. See its README for more info. We use the Node REPL:

de

First set some useful variables to keep from having to copy/paste long strings: the Ethereum addresses of our two nodes and the token contract. Insert the addresses from the deployment in the below: they will differ from the ones here.

r acct0 = '0x19E7E376E7C213B7E7e7e46cc70A5dD086DAff2A'
r acct1 = '0x1563915e194D8CfBA1943570603F7606A3115508'
r token_address = '0x02cAf13e4b645b3dBf27f6Ae1647356A2410210F'

Now we import the Raiden interface and make an instance for each node.

r Rdn = require('./modules/raiden.js')
r r0 = new Rdn('http://172.13.0.3:5001')
r r1 = new Rdn('http://172.13.0.4:5001')

We can call methods on these Raiden node objects. Everything is asynchronous (this is JavaScript), and all methods return Promises, hence the clunky .then(console.log) part. I've also edited the output to clean up all the Promise junk that gets printed. [If anyone knows how to (nicely) make synchronous calls to async functions from the REPL (await is not available), please get in touch!]

.address().then(console.log)
r_address: '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' }
.address().then(console.log)
r_address: '0x1563915e194d8cfba1943570603f7606a3115508' }

Now, let's register the token. The Registry contract will create a Channel Manager contract that will oversee all channels that exchange this token. This interacts with the blockchain, so takes a few seconds to resolve.

.tokens.register(token_address).then(console.log)
annel_manager_address: '0x7f799b2c9fc03f10e8cabdb06bf916402bab1a8f' }

With that done we can create a channel between Node0 and Node1 to allow tokens to be exchanged off-chain. This creates another smart contract called a Netting Channel that is responsible only for transfers of this token between these two nodes. It also makes a token transfer into the Netting Contract from Node0's balance (the deposit, 100 tokens in this case). Once again, it takes a few seconds.

.channels.open(acct1, token_address, 100, 30).then(console.log)
rtner_address: '0x1563915e194d8cfba1943570603f7606a3115508',
lance: 100,
veal_timeout: 10,
ttle_timeout: 30,
ate: 'opened',
annel_address: '0x6432ed34e54ccc4e3219e5ec65f398d02cff2b89',
ken_address: '0x02caf13e4b645b3dbf27f6ae1647356a2410210f' }

The settle_timeout is the last parameter and is the number of blocks that the settlement window remains open for challenges after a channel is closed.

We can now make some transfers!

Node0 sends 50 tokens to Node1. We check balances before and after:

r channel = '0x6432ed34e54ccc4e3219e5ec65f398d02cff2b89'
.channels.balance(channel).then(console.log)

.channels.balance(channel).then(console.log)


.transfer(token_address, acct1, 50).then(console.log)
target_address: '0x1563915e194d8cfba1943570603f7606a3115508',
ount: 50,
entifier: 6889929806137958000,
ken_address: '0x02caf13e4b645b3dbf27f6ae1647356a2410210f',
itiator_address: '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' }

.channels.balance(channel).then(console.log)

.channels.balance(channel).then(console.log)

This is instantaneous! The blockchain is not involved. We can check the token balance on the actual blockchain using a different module provided in the modules/ directory. This uses Web3 to access the Raiden contracts directly.

r Contracts = require('./modules/raiden-contracts.js')
r contracts = new Contracts('http://172.13.0.2:8545')
r token = new contracts.interface(token_address, 'abis/Token.json')
ken.balanceOf(acct0).then(console.log)
00
ken.balanceOf(acct1).then(console.log)
00

Node0's actual token balance has decreased from the initial 250000 by the 100 tokens initially lodged as a deposit. Node1's balance is unchanged. The 50 token Raiden transfer has not yet been reflected back to the blockchain, and won't be until the channel is settled.

We can continue to send tokens back and forth between the nodes, subject to a cap of original deposit + net tokens received in this channel. However, to release the tokens back to the blockchain and make the transfers “real”, we need to close the channel down. There is (currently) no way to keep the channel open and extract some of the balance.

Node1 will initiate the channel closing process. This involves a call to the Netting Contract, so takes a few seconds.

.channels.close(channel).then(console.log)
rtner_address: '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a',
lance: 50,
veal_timeout: 10,
ttle_timeout: 30,
ate: 'opened',
annel_address: '0x6432ed34e54ccc4e3219e5ec65f398d02cff2b89',
ken_address: '0x02caf13e4b645b3dbf27f6ae1647356a2410210f' }

Eventually, after waiting for settle_timeout blocks, the channel state is marked 'settled' and we can see the token balances of each account have been correctly updated on the blockchain. It is no longer possible to make transfers on this channel.

.channels.info(channel).then(console.log)
rtner_address: '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a',
lance: 50,
veal_timeout: 10,
ttle_timeout: 30,
ate: 'settled',
annel_address: '0x6432ed34e54ccc4e3219e5ec65f398d02cff2b89',
ken_address: '0x02caf13e4b645b3dbf27f6ae1647356a2410210f' }

ken.balanceOf(acct0).then(console.log)
50
ken.balanceOf(acct1).then(console.log)
50
Notes
Block times

The configuration here uses a fixed block time of 3 seconds. You can decrease that to make things quicker, or increase it to more closely match real network times. This line in docker-compose.yml,

  --dev.period 3

Although dev.period can be set to zero, meaning that Geth will produce blocks only on demand, I don't recommend it. Raiden channel settlement times are measured in blocks: if no blocks are produced, channels will never settle.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.