Ethereum Development Guide — Part 2

Stefan Beyer
Jan 4, 2018 · 9 min read

Interacting with smart contracts

In part 1 of this tutorial series, we developed a smart contract for the Ethereum blockchain, which allows saving a timestamped SHA-256 hash value of a file to the blockchain and to retrieve such a record to prove integrity of a file.

In part 2 we will build on this code to develop a command line tool that interacts with the contract and we will deploy the contract onto the Ethereum test network.

In order to follow this tutorial you should have also followed part 1, or at least have the contract code from that tutorial and understand it:

As in the previous tutorial, we will use Javascript and Node.js, so some working knowledge of this is required.

After completing this tutorial you will know how to:

  • Run your own Ethereum node
  • Deploy a contract onto the Ethereum blockchain
  • Develop Javascript bindings to connect to your deployed contract
  • Develop a complete command line tool for blockchain-based document certification

Let’s develop a command line interface for our contract. First start up Ganache again and use Truffle to deploy the contract on it. Take note of the contract address, we will need this soon. If you don’t know how to perform these steps, please review part 1 of this tutorial series.

Now, we create a subdirectory named cli_app and enter the directory. Initialise the project:

npm init

Specify the requested parameters whichever way you wish, but you may wish to choose app.js as an entry point, as that is what I have used.

Next, install some library modules we require:

npm install web3 --save
npm install jssha --save
npm install command-line-args --save

The web3 library is what we will use to connect to our Ethereum node’s RPC interface and communicate with the contract. Note, that at the time of writing this will install a beta version of the upcoming Web3 1.0 release. There have been many changes to the library’s interface and the code of this tutorial will not work with previous versions.

Also note, that we could use a higher level Truffle interface to connect to our contract, but we will use Web3 directly, because it gives more control and you can later use the same code in browser based web applications.

jsSHA is a Javascript implementation of the SHA hashing algorithm family and command-line-args will help us to work with command line arguments.

Let’s create a module to interact with our contract in the cli_app directory with the following code:

The first part or this code imports the library modules we need, and declares two variables to hold references to the connected web3 instance and the contract.

We initialise the Ethereum node connection and the contract binding in the init() function. First of all we connect to the node:

let provider = new Web3.providers.HttpProvider("http://localhost:8545");web3 = new Web3(provider);

Next, in order to create a proxy object for our contract, which we can use to execute the contract functions from Javascript, we need to know the address of the contract and the ABI:

let abi = [
    {
      "constant": false,
      "inputs": [
        {
          "name": "hash",
          "type": "bytes32"
        }
      ],
      "name": "addDocHash",
      "outputs": [],
      "payable": false,
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "constant": true,
      "inputs": [
        {
          "name": "hash",
          "type": "bytes32"
        }
      ],
      "name": "findDocHash",
      "outputs": [
        {
          "name": "",
          "type": "uint256"
        },
        {
          "name": "",
          "type": "uint256"
        }
      ],
      "payable": false,
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "payable": false,
      "stateMutability": "nonpayable",
      "type": "constructor"
    }
  ];let address = "0xd121f94184Da71908123a1e08F72cAB8573b9363";

In the above code we have declared the ABI and address in the code. We could just read them from the JSON file created by Truffle in the build directory. Remember to replace the above address with the one assigned by your Ganache test blockchain.

Finally, we create the contract object:

contract = new web3.eth.Contract(abi, address);

The calculateHash function does not require explaining as it reads the content of a file and then uses the calculateHashBytes helper function to actually calculate our hash value:

function calculateHashBytes (data) {
  let  shaObj = new jsSHA("SHA-256", "ARRAYBUFFER");
  shaObj.update(data);
  let hash = "0x" + shaObj.getHash("HEX");
  return hash;
};

The above code uses the jsSHA library to calculate a SHA-256 hash value, which is provided in hexadecimal string representation. Note, that we add a leading “0x”, in order to have a string which can be recognised by Web3 as a HEX string, which can be automatically converted to the bytes32 Solidity type.

Now we declare a function for sending a hash value to the contract:

function sendHash (hash, callback) {
    web3.eth.getAccounts(function (error, accounts) {
      contract.methods.addDocHash(hash).send({
        from: accounts[0]
      },function(error, tx) {
        if (error) callback(error, null);
        else callback(null, tx);
      });
    });
};

There are a few things to note here:

  • The function is asynchronous and uses nested callbacks
  • We access the addDocHash function through the methods field of the contract object.
  • Calling the Javascript version of addDocHash returns a transaction object
  • We send the transaction calling send
  • Send requires an options object as an argument, in which we specify the account to use as a sender address for our transaction. We use the first address in the accounts array.
  • The existing accounts are obtained by the web3.eth.getAccounts call, which is the outer call in our callback structure.

The function for verifying a hash value is similar:

unction findHash (hash, callback) {
  contract.methods.findDocHash(hash).call( function (error, result) {
    if (error) callback(error, null);
    else {
      let resultObj = {
        mineTime:  new Date(result[0] * 1000),
        blockNumber: result[1]
      }
      callback(null, resultObj);
    }
  });
};

The main difference here is, that we do not need to create and send a transaction as the findDocHash function of our contract does not modify state and can be executed in a call instead of a transaction, i.e. the state can be read from our local Ethereum node’s copy of the blockchain state.

The rest of the code just exposes the modules interface and does not require explanation.

We can now use our contract wrapper module to write our command line tool. The following is the complete source code:

We will not discuss this code in detail, as all the Ethereum specific code is in our contract wrapper module and has already been explained.

We can now store the hash value of a file by typing:

node cli_app/app.js -a <filename>

Similarly, we can verify a file’s hash value using:

node cli_app/app.js -f <filename>

Let’s now deploy our contract on a real Ethereum blockchain. In order to deploy a contract a network we need to run some Ethereum node software.

There are a few options to run your own Ethereum node. You may download the official Ethereum wallet, you may use the underlying geth command line node without graphical user interface, or you may use alternative software, such as Parity. We will will use the official geth reference implementation in this guide.

You can download the software from the Ethereum web site at https://www.ethereum.org/cli. Follow the instructions for your system.

In order to test our contract on a real network, but without real money we will use the Rinkeby test network.There are several test networks available. Rinkeby works only with geth and the official wallet (which uses geth below the hood), Kovan works only with Parity and Ropsten works with both. We are using Rinkeby in this case, because it is more stable than Ropsten. Test networks may suffer denial of service attacks, as the Ether involved does not have any real value and Ropsten is more susceptible to this, because of the consensus algorithm used.

Once installed, we can start geth as follows:

geth --rinkeby --syncmode fast --rpc --datadir=$HOME/rinkeby

This tells geth to connect to Rinkeby, use the fast synchronisation mode (which speeds up the initial blockchain download by not replaying the entire transaction history), enables the RPC interface (which we need to connect to the node programatically) and to store the blockchain data in a rinkeby sub-directory of our home directory. I have added this last argument for the purpose of this tutorial, to make sure we know where the data will be, as the default location varies between operating systems.

The node cannot be used for transactions yet, until your node is fully synced, i.e. once it has imported the blockchain state. This may take a long time (maybe an hour in Rinkeby’s case, longer for the main network)

In the meantime we can create an account and get some test Ether. In a different terminal type the following to create an account:

geth account new --datadir="$HOME/rinkeby/"

After defining a password your keys will be created and added to your key store in the data directory (you should always backup your keys). The account’s address will be displayed.

You will need some Rinkeby test Ether credited to your account, in order to send transactions. This can be requested at the Rinkeby faucet. Follow the link and complete the instructions to have some Ether transferred to your account. The balance on your node will not update until your node is in sync, but you can search for your account on https://rinkeby.etherscan.io/.

Once we have done all this, we just have to wait for our node to catch up with the blockchain. We can connect to the running geth instance’s interactive console by typing the following in the command line:

geth attach ipc:/$HOME/rinkeby/geth.ipc

The geth.ipc file is automatically created for inter process communication. The console is a Javascript console with Web3 injected. We can query the node to see wether it is still syncing by typing:

eth.syncing

Whilst this returns true the node is still catching up. When syncing is complete true is returned and you should then also be able to see latest block number with:

eth.blockNumber

Before we can execute any transaction we need to unlock the account we are using. This can be achieved in the interactive console by typing:

personal.unlockAccount(address, "password", 300)

Substitute address for your account address (in double quotes) and your password for your password. The third arguments indicates the number of seconds the account should remain unlocked.

To avoid doing this every time, we can also keep an account unlocked for the whole time a node is running, by adding a command line argument:

geth --rinkeby --syncmode fast --rpc --password <(echo password) --unlock 0 --datadir="$HOME/rinkeby/"

The above is our start up command, modified to unlock the fist account in the account list (index 0) with the password supplied.

Once you have some Ether and your node is in sync, we can deploy our notary contract. We will use truffle for this.

As in part 1 of this tutorial we need to edit the truffle.js file to add the network details:

module.exports = {
  networks: {
    development: {
       host: "localhost",
       port: 7545,
       network_id: "*"
    },
    rinkeby: {
       host: "localhost",
       port: 8545,
       network_id: "4",
       gas: 4000000
    }
  }
};

You already know the format of this file, but here we have added network id 4, which is Rinkeby and have also supplied the maximum amount of gas we are happy to spend for contract deployment. The other relevant parameter, the gas price, could also be specified, but we will use the default value.

Let’s deploy the contract with:

truffle migrate --network rinkeby

This should take longer now, as we are connecting to a real network and executing real transactions. We will receive deploy addresses for both the Migrations and the Notary contracts. Take note of the Notary contract’s address, as we will need this to connect to it.

In order to connect our command line tool the contract deployed on Rinkeby, we have to change our initialisation code to point to the contract address on Rinkeby, which we have just received from the deploy operation. In the code below, substitute the address for your own (the actual address is valid, as I have deployed an instance of the contract there).

address = "0xd121f94184Da71908123a1e08F72cAB8573b9363";

Furthermore, instead of connecting to Ganache we now need to connect to our real Ethereum node’s RPC interface. To do so, we need to change the port number of our Web3 provider to whatever our node uses (8545 for geth by default):

let provider = new Web3.providers.HttpProvider("http://localhost:8545");web3 = new Web3(provider);

Our command line tool now work as previously, but connects to the real blockchain. Transactions now don’t get mined instantly, so you may have to wait a bit before being able to verify a fingerprint, until the file’s fingerprint been sealed in a new block.

We now have a working command line interface connecting to our contract deployed on a real blockchain through our own Ethereum node.

Now, every transaction executed costs us our own money, so if we were to offer this service to the public (through a web interface for example), we could either charge for our service to cover this cost, or find another way to make sure those wishing to certify documents spend there own Ether.

In part 3 of this tutorial, we will look at how to do this by making end-users sign transactions with their own accounts.

The source code for this tutorial can be found on GitHub:

Cryptonics

Blockchain Technology Insights

Stefan Beyer

Written by

Computer Scientist with research background in Operating Systems, Distributed Systems, Fault Tolerance and Cybersecurity.

Cryptonics

Blockchain Technology Insights