Author's Posts

Introduction

In this article, we will build a simple blockchain network with Hyperledger Fabric. Fabric allows us to build a private blockchain. There are no cryptocurrencies involved. After this, you will have a decent understanding of the different parts needed to start your own private blockchain. In this example, our network will be composed of 3 organisations. Each organisation will have two peers each.
Continue reading Your first Hyperledger Fabric network

Read more

Introduction

React Native allows you to write code for the iOS and Android platforms using the Javascript and React syntaxes. With this logic, you will want to re-use as much code as possible for both platforms. However, at one point, you will have to write code for a specific platform. In this article, we will see how React native makes this possible.

Continue reading React Native: Write code depending on the plateforms

Read more

Introduction

In my last two articles, I explored the Solidity language. This language is designed to work with the Ethereum Virtual machine (EVM). In this article, we will move past the language theory and use a test environment for our contract. With this, we will be able to understand better how Ethereum works.

The tools

In this first article, we will use truffle and ganache-cli. Truffle is a framework for Ethereum development. It allows you to create an test environment, write tests for your contracts and other things. In this article however, we will only use it to create a test environment.

ganache-cli will be used with truffle to create a fully functioning test environment. What we will do here won’t be quite like a real production blockchain, but it will allow us to see how things work.

Installing the tools

  • truffle: [sudo] npm install -g truffle
  • ganache-cli: [sudo] npm install -g ganache-cli

Getting things ready

First, we will initialize a new truffle project. To do this, create a new directory and inside it, run: truffle init.

You will now have several folders. First, open the truffle-config.js and paste this inside:

module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
    // to customize your Truffle configuration!
    networks: {
        development: {
            host: '127.0.0.1',
            port: 7545,
            network_id: '*'
        }
    }
}

For Windows users, you will have to remove the truffle.js file to avoid conflicts. For others, you can keep both and put this code in truffle.js, or do like a Windows user, doesn’t matter.

This file indicates that our development network will be running on localhost:7545.

Next, in the contracts folder, create a new DeveloperFactory.sol file. This is where our contract will be written. Put the following inside:


pragma solidity ^0.4.18; contract DeveloperFactory { // Let's create a Developer! event NewDeveloper(uint devId, string name, uint age); uint maxAge = 100; uint minAge = 5; struct Developer { string name; uint id; uint age; } Developer[] public developers; mapping (uint => address) public devToOwner; mapping (address => uint) public ownerDevCount; function _createDeveloper( string _name, uint _id, uint _age ) private{ uint id = developers.push( Developer( _name, _id, _age ) ) - 1; ownerDevCount[msg.sender]++; devToOwner[id] = msg.sender; NewDeveloper(id, _name, _age); } function _generateRandomId( string _str ) private pure returns (uint){ uint rand = uint(keccak256(_str)); return rand; } function createRandomDeveloper( string _name, uint _age ) public payable { require(_age > minAge); require(_age < maxAge); require(msg.value == 5000000000000000000); uint randId = _generateRandomId( _name ); _createDeveloper(_name, randId, _age ); } function getAllDevelopers() public view returns (uint) { return developers.length; } }

If you want to know what is happening here in more details, I covered it in my first and second article about Solidity. But, in short, this contract can be called to create a Developer struct with a name and an age. In our example, creating a new Developer will require 5 ether or ( 5000000000000000000 wei, the lowest denomination possible in Ethereum ).

Next, go inside the migrations folder and create a file called 2_deploy_contracts.js:

const DeveloperFactory = artifacts.require('./DeveloperFactory.sol')

module.exports = function(deployer){
    deployer.deploy(DeveloperFactory)
}

In this file, we import our contract and deploy it to our blockchain.

Launching our test environment

Open a new terminal window and run ganache-cli -p 7545. This will run ganache-cli on port 7545 ( the same we specified in our truffle-config.js file ) and create some accounts for us. Each account will have 100 ether by default. You should see something like this in your console:


Available Accounts ================== (0) 0x473c0be352f997aa0b194786c27d26e29a3f75b1 (1) 0x9657290da5570b17a03198f490b0a2d7eea84ecf (2) 0x516c0e0152d7b85facb7e3da2d30f67e42a80ca9 (3) 0xf81be8bbe99d2302b85f7cb0f60103c435ae703b (4) 0xcfacf5ac5567cfdd70ee5a8a9fe4bf7f74d80b02 (5) 0x623e18e34b2de07933fe179862f038230cc69012 (6) 0xd7100dbc1d6f72777ae2a6f5d95c4b8d71f7ce07 (7) 0x7f40df6c6042888a37124821130910e77051b1cf (8) 0x26a2c2be1f31571f289b7fb60e41f31f7c57a5be (9) 0x08a945825a28166466987d5fc77b016fe3d80aa5

Of course, the accounts’ addresses will be different for you, but you will get 10 accounts to play with.

Now, go back to your first terminal window. Make sure you are inside the folder you created for the truffle project and run: truffle compile, then run truffle migrate --network development. This will compile our code in a language that the Ethereum Virtual Machine (EVM) can understand. Here, ganache will emulate the EVM.

If everything went well, your terminal should have shown this:

truffle migrate --network development
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0xc83617394674cd65f751ff9c05438e16339414ccf1e1662ba66479d79335af13
  Migrations: 0xe982e78028e0dfcbdb135e7a3c1e1ed3d98e36e5
Saving successful migration to network...
  ... 0x78bdff98e4dac310de4650048a0856075a460bed9de0c4d4ea879ea399d142c4
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying DeveloperFactory...
  ... 0x0a314c5ed99c772019ea358ac98e002a1442e26903122528d705bf3ff7ed02ed
  DeveloperFactory: 0xc34cc3e53850673db1dea31d267ea1738edc629f
Saving successful migration to network...
  ... 0x95a3cd861f067cdbe8c96f13477526eacd2a7936662f31724e1354922af49664
Saving artifacts...

Notice in the ganache-cli output that our contract has been instantiated:

Transaction: 0xc83617394674cd65f751ff9c05438e16339414ccf1e1662ba66479d79335af13
  Contract created: 0xe982e78028e0dfcbdb135e7a3c1e1ed3d98e36e5
  Gas usage: 269607
  Block Number: 1
  Block Time: Thu May 03 2018 21:04:55 GMT+0200 (CEST)

Console and playing around

Now, in the same terminal window you ran the truffle commands, run truffle console --network development. This will launch the truffle console and allow you to interact with our blockchain. We will use the Web3 Javascript API to make it easier. First, let’s take one of the accounts and put it in a variable:

account = web3.eth.accounts[4]

Next, let’s run the following command:

DeveloperFactory.deployed().then(inst => {Factory = inst})

This will assign a contract’s instance inside the Factory variable. Let’s make sure our account has 100 ether:

truffle(development)> web3.fromWei(web3.eth.getBalance(account).toNumber())
'100'
truffle(development)>

The method getBalance will return a BigNumber type. toNumber() will give us the account’s balance in Wei. We convert it in ether with fromWei().

  • Creating a Developer

Alright, so now we are going to call the function createRandomDeveloper. As you can see, this function takes two parameters, a string _name and a uint _age. Because we also require 5 ether to call this function, we will need to specify this in our function call:

truffle(development)> Factory.createRandomDeveloper('Damien', 26, {from: account, value: web3.toWei(5, "ether")})

Factory is our contract instance. We give three parameters to our function. Damien is the _name, 26 is the _age. The third is an object with a key *from* to know which account is calling it, and a key *value* that specify the value sent by the account. Here, the *from* value is *account*, the variable we created earlier. We are converting 5 ether to Wei to match the required value in our contract.

Your terminal should show something like this:

{ tx: '0xa3792da93311fdf60054f8a30e7624dd385ccf36cc639881eeb25308ddad5e0e',
  receipt:
   { transactionHash: '0xa3792da93311fdf60054f8a30e7624dd385ccf36cc639881eeb25308ddad5e0e',
     transactionIndex: 0,
     blockHash: '0x3ef1c41cbc79d65c1282a86da3a68120a5c069709a6a5bd3e206ed85d9c270c5',
     blockNumber: 5,
     gasUsed: 148160,
     cumulativeGasUsed: 148160,
     contractAddress: null,
     logs: [ [Object] ],
     status: '0x01',
     logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000200000000000000000000000000000000000000000000000000000002000000000000000000000' },
  logs:
   [ { logIndex: 0,
       transactionIndex: 0,
       transactionHash: '0xa3792da93311fdf60054f8a30e7624dd385ccf36cc639881eeb25308ddad5e0e',
       blockHash: '0x3ef1c41cbc79d65c1282a86da3a68120a5c069709a6a5bd3e206ed85d9c270c5',
       blockNumber: 5,
       address: '0x033711f6fd408b10cc94a21a3e8c20f0e75a4615',
       type: 'mined',
       event: 'NewDeveloper',
       args: [Object] } ] }
truffle(development)>

The transaction has been successful. There are a lot of informations here. We can see that the event ‘NewDeveloper’ has been fired, as expected. We have the transaction’s hash, the block hash, the gas used… Let’s check our account’s balance now:

truffle(development)> web3.fromWei(web3.eth.getBalance(account).toNumber())
'94.985184'

Notice that it isn’t 95 ether. This is because when you interact with a contract, you also have to pay additional ether to run a transaction. We have in our transaction informations the cumulativeGasUsed ( 148160 ). This means that 148160 Wei have been used to complete this transaction. We need to multiply this by the gasPrice. Each transaction has a gasPrice. We can retrieve it with the transactionHash, and multiply it by the cumulativeGasUsed to get the transaction’s cost in ether:

truffle(development)> web3.eth.getTransaction('0xa3792da93311fdf60054f8a30e7624dd385ccf36cc639881eeb25308ddad5e0e').gasPrice.toNumber() * 148160
14816000000000000
truffle(development)> web3.fromWei(14816000000000000)
'0.014816'
truffle(development)> 100 - 0.014816
99.985184

And we are able to calculate our balance this way! We can also make sure that our contract’s balance is 5 ether:

truffle(development)> web3.fromWei(web3.eth.getBalance('0x033711f6fd408b10cc94a21a3e8c20f0e75a4615').toNumber())
'5'

You will get your contract’s address in the transaction’s logs field address above. Finally, what would happen if we didn’t sent 5 ether to our contract? Let’s get a new account:

truffle(development)> account1 = web3.eth.accounts[9]
'0x5e273389dba808789a27cb792faaf31429c8de8c'
truffle(development)> web3.fromWei(web3.eth.getBalance(account1).toNumber())
'100'

Now, let’s call our createRandomDeveloper function:

truffle(development)> Factory.createRandomDeveloper('Johnny', 43, {from: account1, value: web3.toWei(10, "ether")})
Error: VM Exception while processing transaction: revert
    at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:41484:16)

truffle(development)> web3.fromWei(web3.eth.getBalance(account1).toNumber())
'99.9976828'

We get an error. But, the gas used to start the transaction is lost anyway! You can see that the account’s balance is not 100 ether anymore.

Conclusion

In the next article, we will explore something close to a real environment, by using geth and mist. I hope this article gave you a better understanding at how you could get started with Ethereum development.

Have fun!

Read more

Introduction

In my last article, we got started with Solidity by creating a simple contract and exploring some simple structures and concepts. We will pick up where we left off.

Mappings

After struct and arrays, we can also store data in mappings. Mappings are a key-value store. For example:

mapping(uint => string) public keyUintStringValue;
mapping(address => uint) public addressToUint;

Here, we have two public mappings. The first one has a uint key and a string value. The second has an address key and a uint value.

Addresses

The Ethereum blockchain is made up of addresses. Each account has an unique address. It takes the following form: 0x0cE440255306E921F41612C46F1v6df9Cc969183. Each address has a certain amount of Ether, which is the cryptocurrency used on the blockchain, and can receive or send Ether to other addresses.

With that in mind, let’s create a new mapping for our DeveloperFactory:

pragma solidity ^0.4.22;

contract DeveloperFactory {

    event NewDeveloper(uint devId, string name, uint age);

    uint maxAge = 100;
    uint minAge = 5;

    struct Developer {
        string name;
        uint id;
        uint age;
    }

    Developer[] public developers;

    mapping (address => uint) public ownerDevCount;
    mapping (uint => address) public devToOwner;

    function _createDeveloper( string _name, uint _id, uint _age ) private{
        uint id = developers.push( Developer( _name, _id, _age ) ) - 1;
        NewDeveloper(id, _name, _age);
    }

    function _generateRandomId( string _str ) private pure returns (uint){
        uint rand = uint(keccak256(_str));
        return rand;
    }

    function createRandomDeveloper( string _name, uint _age ) public view {
        require(_age > minAge);
        require(_age < maxAge);
        uint randId = _generateRandomId( _name );
        _createDeveloper(_name, randId, _age );
    }
}

In the first mapping, we will keep track of the number of devs each account ( address ) created. In the second, we will keep track of the owners for each dev.

msg.sender

Each contract is passive, they don’t do anything until someone triggers them. msg.sender is a global variable that allows us to know which address is responsible for the triggering. It could be a account or another smart contract.

With that information, we can update our mappings appropriately. In the _createDeveloper function, we will increase the ownerDevCount for this particular address. In the devToOwner mapping, we will indicate that the newly created developer is owned by the msg.sender address.

pragma solidity ^0.4.22;

contract DeveloperFactory {

    event NewDeveloper(uint devId, string name, uint age);

    uint maxAge = 100;
    uint minAge = 5;

    struct Developer {
        string name;
        uint id;
        uint age;
    }

    Developer[] public developers;

    mapping (address => uint) public ownerDevCount;
    mapping (uint => address) public devToOwner;

    function _createDeveloper( string _name, uint _id, uint _age ) private{
        uint id = developers.push( Developer( _name, _id, _age ) ) - 1;
        ownerDevCount[msg.sender]++;
        devToOwner[id] = msg.sender;
        NewDeveloper(id, _name, _age);
    }

    function _generateRandomId( string _str ) private pure returns (uint){
        uint rand = uint(keccak256(_str));
        return rand;
    }

    function createRandomDeveloper( string _name, uint _age ) public view {
        require(_age > minAge);
        require(_age < maxAge);
        uint randId = _generateRandomId( _name );
        _createDeveloper(_name, randId, _age );
    }
}

Notice the ++ notation to increase the ownerDevCount[msg.sender], just live Javascript!

Inheritance and import

Contracts can inherit from other contracts. If you open a new file call DeveloperLearning.sol, you can make it inherit from DeveloperFactory.sol:

pragma solidity ^0.4.22;

import "./DeveloperFactory.sol";

contract DeveloperLearning is DeveloperFactory {
    // I have now access to DeveloperFactory functions
}

Notice how we imported the DeveloperFactory contract ( assuming it was in the same folder ). To declare inheritance, we use the keyword is.

payable

In order for a contract to receive ether, we need to have the payable keyword to a function. The amount sent will be accessible in the global variable msg.value. So we could make sure that a certain amount of ether is sent to the contract before the creation of a developer:

pragma solidity ^0.4.22;

contract DeveloperFactory {

    event NewDeveloper(uint devId, string name, uint age);

    uint maxAge = 100;
    uint minAge = 5;

    struct Developer {
        string name;
        uint id;
        uint age;
    }

    Developer[] public developers;

    mapping (address => uint) public ownerDevCount;
    mapping (uint => address) public devToOwner;

    function _createDeveloper( string _name, uint _id, uint _age ) private{
        uint id = developers.push( Developer( _name, _id, _age ) ) - 1;
        ownerDevCount[msg.sender]++;
        devToOwner[id] = msg.sender;
        NewDeveloper(id, _name, _age);
    }

    function _generateRandomId( string _str ) private pure returns (uint){
        uint rand = uint(keccak256(_str));
        return rand;
    }

    function createRandomDeveloper( string _name, uint _age ) public payable {
        require(_age > minAge);
        require(_age < maxAge);
        require(msg.value == 5);
        uint randId = _generateRandomId( _name );
        _createDeveloper(_name, randId, _age );
    }
}

Memory and Storage

In Solidity, there are two places where variables are stored: in storage or in memory. A variable stored in memory is temporary, it exists while the function is used, then it is discarded. A variable stored in storage exists permanently on the blockchain. Most of the time, you don’t have to worry about where to store your variables, as Solidity handles it for you.

For example, state variables ( maxAge, minAge, Developer ), declared outside of functions, are stored in storage. Variables like randId, id, rand are stored in memory.

But, in some cases, you want to explicitly declare where to store certain variables. Solidity allows you to do that with memory and storage keywords.

Read more

Introduction

Solidity is a high-level language used to implement smart contracts. This is an object oriented language designed to target the Ethereum Virtual Machine. Let’s explore it!

Let’s go!!!

Let’s create a file called Contract.sol
First, you must define the version you are using. This is an information the compiler needs.

pragma solidity ^0.4.22;

All code in Ethereum belongs to a Contract. Let’s create a contract and define a few variables inside it.

pragma solidity ^0.4.22;

contract DeveloperFactory {
    // Let's create a Developer!
    uint dnaDigits = 16;
    uint ageDigits = 2;
}

Solidity is a typed language. uint stand for Unsigned Integer ( non negative integers ). These variables are state variables. They will be permanently stored in the contract storage ( in the Ethereum Blockchain ). Our Developer has a dna of 16 digits and an age of 2 digits.

Let’s keep going!

Struct and arrays

pragma solidity ^0.4.22;

contract DeveloperFactory {
    // Let's create a Developer!
    uint dnaDigits = 16;
    uint ageDigits = 2;

    struct Developer {
        string name;
        uint dna;
        uint age;
    }

    Developer[] public developers;
}

The struct variable allows us to define more complex data structures. Here the Developer struc takes a string called name, a uint called dna and a uint called age.

Solidity also has arrays. You can create dynamic or fixed arrays. Here, our Developer array is dynamic because we do not specify a length. So we can keep adding Developers to our army without any limitations.

Developer[5] public developers is a fixed array that can contain 5 Developer struct.

Functions

A function would look something like this:

pragma solidity ^0.4.22;

contract DeveloperFactory {
    // Let's create a Developer!
    uint maxAge = 100;
    uint minAge = 5;

    struct Developer {
        string name;
        uint id;
        uint age;
    }

    Developer[] public developers;

    function _createDeveloper( string _name, uint _id, uint _age ) private{
        developers.push( Developer( _name, _id, _age ) );
    }

    function _generateRandomId( string _str ) private pure returns (uint){
        uint rand = uint(keccak256(_str));
        return rand;
    }

    function createRandomDeveloper( string _name, uint _age ) public view {
        require(_age > minAge);
        require(_age < maxAge);
        uint randId = _generateRandomId( _name );
        _createDeveloper(_name, randId, _age );
    }
}

We create functions with the function keyword. Functions can take parameters. By default, functions are public. I added the private keyword to make this function private. I also chose to add an underscore before a private function or variable to distinguish them from public variables. You don’t have to do this, I just find it easier to read.

Ethereum has the hash function keccak256 built in. This is a version of SHA3. Pass it any string and you get a 256-bit hexadecimal number.

As you can see, we are typecasting the keccak256 value into a uint value and we return it.

Aside from the private keyword, there are several things you can add to a function:

  • The function returns something: Use the returns keyword and specify the type the function returns. Here, it will return a uint type.

  • The view keyword means that our function needs to look at some of our contract’s variables, but not modify them. Our function createRandomDeveloper needs to look at minAge and maxAge variables.

  • The pure keyword means that the function is not accessing any data in the app. It only returns something depending on its parameters. Our _generateRandomId is pure.

We have three functions. _generateRandomId generates a random Id for our developer by using the keccak256 built in function. _createDeveloper creates and pushes a new Developer struct into our developers array. createRandomDeveloper is the only public function. It checks if the age provided is correct. The require statements will throw errors if it is not the case ( age greater than 100 and lower than 5 in our case ). So, this last function is the one that can be called from outside our contract.

Events

You can also create events so you can communicate what happens on the blockchain to your front-end app. Your app would then listen to those events and react accordingly.

pragma solidity ^0.4.22;

contract DeveloperFactory {
    // Let's create a Developer!

    event NewDeveloper(uint devId, string name, uint age);

    uint maxAge = 100;
    uint minAge = 5;

    struct Developer {
        string name;
        uint id;
        uint age;
    }

    Developer[] public developers;

    function _createDeveloper( string _name, uint _id, uint _age ) private{
        uint id = developers.push( Developer( _name, _id, _age ) ) - 1;
        newDeveloper(id, _name, _age);
    }

    function _generateRandomId( string _str ) private pure returns (uint){
        uint rand = uint(keccak256(_str));
        return rand;
    }

    function createRandomDeveloper( string _name, uint _age ) public view {
        require(_age > minAge);
        require(_age < maxAge);
        uint randId = _generateRandomId( _name );
        _createDeveloper(_name, randId, _age );
    }
}

We create an event with the event keyword. In our contract, the event will have three parameters, the devId, the name and the age. We will fire our event in our _createDeveloper function. It works like a function. The id is retrieved thanks to the push method on the developers array. It will return the new length of the array. Because the array start at index 0, we subtract 1 to get the developer id.

Note: This series is inspired by the CryptoZombies tutorial. Don’t hesitate to check it out, it’s amazing!

Note bis: You can use the Remix IDE in your browser if you want to play around in Solidity

Conclusion

We have explored some concepts of Solidity. In the next article, we will go in more depth into what Solidity allows you to do. Stay tuned!

Read more

Introduction

Lately, I spent a lot of time in private blockchain’s world. When you are learning a new technology like this one, you come across certain concepts or principles that you have to understand in order to move on. Docker and containers seems to be one of them right now for me. So, in a good old let’s write about what I learn fashion, I’m trying to explain what Docker does and how I’m getting started with it.

Containers?

Docker is a platform for developers to develop and deploy applications with containers. Docker didn’t invent containers or containerization, but popularised the concept, so they are sometimes used to describe the same thing.

Containers are launched by running an image. An image is an executable that explains everything the application needs to run, and where/how to find them. A container is a running instance of an image. This way of doing things takes less resources than Virtual Machines (VM) that provides a full “virtual” operating system, which represents more resources than most applications need. By containerizing your application and its dependencies, differences in OS distributions and underlying infrastructure are abstracted away.

Docker and NodeJS

Enough theory, let’s see how we could use Docker to create an image for a NodeJS application.

First, install Docker by following the instructions here. Once this is done, run docker --version in your terminal. You should have something like:

Docker version 17.12.0-ce, build c97c6d6

If you want to make sure everything is working, you can run: docker run hello-world. This will pull the hello-world image for you and launch a container.

You can see a list of the images you downloaded with docker image ls.

You can see a list of running containers with docker container ls, or you can have all the containers with docker container ls --all. Remember than containers are instances of the images you downloaded.

So, if you run the hello-world image, assuming you didn’t have any containers running before, you will see one container in this list. If you run hello-world 5 times, you will have 5 containers ( instances of the hello-world image ).

Note: To stop containers, run docker kill $(docker ps -q). You will still see these containers with “docker container ls –all. To remove them completely, rundocker rm $(docker ps -a -q)`.

The NodeJS application

Let’s do something very simple. An express app with 2 routes that renders 2 html pages. Create a new directory called express-app:

mkdir express-app && cd express-app

Run npm init with the defaults. Then, run npm install express --save.

Create 3 files: index.js, index.html, about.html.

  • index.js
const express = require('express')
const app = express()

app.get('/', ( req, res ) => {
    res.sendFile(`${__dirname}/index.html`)
})

app.get('/about', (req, res) => {
    res.sendFile(`${__dirname}/about.html`)
})

app.listen(3000, () => {
    console.log('Listening on port 3000!')
})

Create an express app, 2 routes for our html files and listen on port 3000.

  • index.html
<html>
    <body>
        <h1>Hello Docker from index</h1>
    </body>
</html>
  • about.html
<html>
    <body>
        <h1>About page</h1>
    </body>
</html>

Good, our app is done. If you run node index.js, you will see our html pages on localhost:3000/ and localhost:3000/about.

Dockerfile

To define the environment inside your container, we will use a Dockerfile. This is a list of instructions that tells Docker what it must do to create the image we want.

Create a Dockerfile in your directory with touch Dockerfile:


FROM node:carbon WORKDIR /usr/src/app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD ["node", "index.js"]

What’s happening here? The first line indicates that we want to use the latest node version to build our image. This is the image we start from. node:carbon being the latest Long Term Support version available.

The second line creates a directory to hold our application’s code inside the image.

The third and fourth line copy the package.json and run the npm install command. The first line gives us node.js and npm. So we install our dependencies, in this case, express.js only. Note that we will NOT copy the /node_modules file.

The COPY instruction bundles our app inside the Docker image, so our html files and index.js file in our case.

The EXPOSE instruction exposes the 3000 port that our app uses.

Finally, the CMD instruction specifies which command needs to be run for our app to start.

Build

Everything is now ready, we can build the app.

Run docker build -t node-app .

The -t tag allows you to specify a friendly name to your Docker image. You should see something like this in your terminal:

Sending build context to Docker daemon   21.5kB
Step 1/7 : FROM node:carbon
 ---> 41a1f5b81103
Step 2/7 : WORKDIR /usr/src/app
 ---> Using cache
 ---> ffe57744035c
Step 3/7 : COPY package*.json ./
 ---> Using cache
 ---> c094297a56c2
Step 4/7 : RUN npm install
 ---> Using cache
 ---> 148ba6bb6f25
Step 5/7 : COPY . .
 ---> Using cache
 ---> 0f3f6d8f42fc
Step 6/7 : EXPOSE 3000
 ---> Using cache
 ---> 15d9ee5bda9b
Step 7/7 : CMD ["node", "index.js"]
 ---> Using cache
 ---> 154d4cd7e768
Successfully built 154d4cd7e768
Successfully tagged node-app:latest

Now, if you run docker image ls. You will see your node-app in the list.

Launch the container(s)

We can now launch our containers. Run docker run -p 8080:3000 -d node-app

The -d flag runs the app in detached mode. -p 8080:3000 redirects a public port to a private port. 8080 being the private port, 3000 the public port we exported.

Go to localhost:8080 and your app is running!

Now, run docker run -p 10000:3000 -d node-app, then docker run -p 4000:3000 -d node-app.

Open localhost:10000 and localhost:4000 and see that you have three different instances of your node-app image running at the same time! To make sure of it, you can run docker container ls and see your 3 containers running the same image on different ports.

Well, that was a quick introduction to Docker. Have fun!

Read more

Introduction

In my last article, I gave a quick overview of the Hyperledger Composer framework to build a business network with a private blockchain technology. I used a land registry network to show how the framework works. We then used a React application to use the REST API provided.

This time, instead of using the REST API, I made a little command line application using the Javascript API. The concept is simple. You enter commands in your terminal to trigger actions ( retrieve data, create assets and/or transactions ). We will re-use the same land registry network I used in the previous article.

Continue reading Private Blockchains: Hyperledger Composer Javascript API

Read more