Knowledge Base Home

Connecting with Truffle

Truffle is an easy to use development environment, testing framework and asset pipeline that greatly simplifies development against an Ethereum ecosystem. The framework provides:

  • Built-in smart contract compilation, linking, deployment and binary management.
  • Automated contract testing for rapid development.
  • Scriptable, extensible deployment & migrations framework.
  • Network management for deploying to any number of public & private networks.
  • Package management with EthPM & NPM, using the ERC190 standard.
  • Interactive console for direct contract communication.
  • Configurable build pipeline with support for tight integration.
  • External script runner that executes scripts within a Truffle environment.

The forthcoming tutorial is by no means a deep dive into truffle. Rather, it is a lightweight walkthrough leveraging the package to communicate with an external Ethereum network. For more information on the nuances of truffle and its capabilities, please refer to the formal truffle documentation.

Prerequisites

This sample assumes that you have a bootstrapped environment and are familiar with the process of generating basic auth credentials and building accessible URL endpoints against the web3 javascript API. It also makes use of private transactions, a feature offered through the Quorum client’s constellation module. If any of these concepts are foreign to you, please visit Create your network for instructions on building a network, Connect to your node for details on authentication and connection, and Public and Private Transactions for elaboration on transaction classes.

The upcoming scenario features three member organizations each running a single node and demonstrates usage of the truffle library to drive private transactions.

Install truffle globally

Open a terminal and install truffle onto your machine:

npm install -g truffle@4.1.15

Create the project

Make a working directory for the sample truffle project and change into the newly created workspace:

mkdir truffleProject && cd truffleProject

Next, initialize the project:

truffle init

The init command provides a baseline for the project. Specifically, it creates the following subdirectories:

  • contracts/ – for Solidity code
  • migrations/ – for scriptable deployment files
  • test/ – for test files to drive applications and smart contracts

It also generates a configuration file – truffle.js – that is used to enumerate network endpoints. You should be left with a directory that resembles the following:

Kaleido-MacBook-Pro:truffleProject kaleido$ ls
contracts		 migrations		  test			truffle-config.js	    truffle.js

Populate the directories

Before continuing, we need to prime the directories with some solidity code and a migration file that will deploy the smart contract. First, copy the following contract into a new file, named SimpleStorage.sol, and park it in your contracts/ subdirectory:

pragma solidity ^0.4.17;

contract SimpleStorage {
  uint public storedData;

  function SimpleStorage(uint initVal) public {
    storedData = initVal;
  }

  function set(uint x) public {
    storedData = x;
  }

  function get() constant public returns (uint retVal) {
    return storedData;
  }
}

Next, create a new migration file called 2_deploy_simplestorage.js within your migrations/directory:

// File: `./migrations/2_deploy_simplestorage.js`

var SimpleStorage = artifacts.require("SimpleStorage");

module.exports = function(deployer) {
  // Pass 123 to the contract as the first constructor parameter
  deployer.deploy(SimpleStorage, 123, {privateFor: ["AQNPV7cpCge4p9OAkZOaauKaPDIT0o3QThtj1kXQrRc="]})
};

The sample migration file makes use of the privateFor parameter, a privacy feature offered through the Quorum client’s constellation module. Note that this field has no attachment to truffle and is entirely optional. If you wish to submit public transactions, simply remove this parameter from the constructor request. For example:

var SimpleStorage = artifacts.require("SimpleStorage");

module.exports = function(deployer) {
  // Pass 123 to the contract as the first constructor parameter
  deployer.deploy(SimpleStorage, 123)
};

NOTE: This tutorial will proceed with the demonstration of private transactions. We strongly recommend you follow the example environment configuration of Quorum + IBFT. An orchestration of Quorum + Raft has repeatedly exhibited erratic behaviors, with commands consistently hanging. For those interested in leveraging the truffle framework to send public transactions, an environment running Geth + PoA will be suitable.

Configure for your network

The truffle project needs some details about the backend environment in order to properly communicate with nodes in your network. This information is specified in the truffle.js configuration file.

Let’s assume an environment with the following specifications:

  • Quorum client with IBFT consensus
  • 3 member organizations – SupplierShipper and Retailer
  • 3 nodes, 1 per member – supplier_nodeshipper_node and retailer_node

Where the basic auth credentials for each member are as follows:

  • Supplier – username:ack6vvdt password:hqaxsnnV5ZQyID-bDlaTZcYmwvifMKLsnOl5IPYGzf4
  • Shipper – username:h1te2497 password:CffP8BkBE7R3-iYxwbQJKQH61JXjOdp5aRjsjxKnrvA
  • Retailer – username:wpwfk84h password:iZOpC_HxgAwNymHZ5s0yNntiFHiPPQVkgF1v7GAfcaw

The RPC endpoints for each node are as follows:

  • supplier_node https://vp8ixo5o-yzilyrdm-rpc.us-east-2.kaleido.io
  • shipper_node https://vp8ixo5o-n0qm2eg2-rpc.us-east-2.kaleido.io
  • retailer_node https://vp8ixo5o-yomclk7p-rpc.us-east-2.kaleido.io

And the transaction manager private addresses for each node are as follows:

  • supplier_node d6910JFVtEfFWtFiMWwNWxOf+A9uEwqQOshAXHhb/wo=
  • shipper_node g0vFQ34JtVZ64fOL62ciDWanu8HED6UT3beC58dmS2U=
  • retailer_node AQNPV7cpCge4p9OAkZOaauKaPDIT0o3QThtj1kXQrRc=

Using this information, we are able to generate a truffle.js config file that leverages the web3 javascript API to set the HTTP provider and establish connection. A file for connecting to the above environment is as follows:

var Web3 = require('web3');

module.exports = {
networks: {
    supplier_node: {
      provider: () => {
        return new Web3.providers.HttpProvider('https://vp8ixo5o-yzilyrdm-rpc.us-east-2.kaleido.io', 0, 'ack6vvdt', 'hqaxsnnV5ZQyID-bDlaTZcYmwvifMKLsnOl5IPYGzf4');
      },
      network_id: "*", // Match any network id
      gasPrice: 0,
      gas: 4500000
    },
    shipper_node: {
      provider: () => {
        return new Web3.providers.HttpProvider('https://vp8ixo5o-n0qm2eg2-rpc.us-east-2.kaleido.io', 0, 'h1te2497', 'CffP8BkBE7R3-iYxwbQJKQH61JXjOdp5aRjsjxKnrvA');
      },
      network_id: "*", // Match any network id
      gasPrice: 0,
      gas: 4500000
    },
    retailer_node: {
      provider: () => {
        return new Web3.providers.HttpProvider('https://vp8ixo5o-yomclk7p-rpc.us-east-2.kaleido.io', 0, 'wpwfk84h', 'iZOpC_HxgAwNymHZ5s0yNntiFHiPPQVkgF1v7GAfcaw');
      },
      network_id: "*", // Match any network id
      gasPrice: 0,
      gas: 4500000
    }
  }
};

Replace the empty truffle.js file with the following template, being sure to overwrite the placeholder values for node name, RPC URL and basic auth credentials.

Add the web3 modules

From the above configuration, we can see that this program needs access to the web3 APIs. First, create and save the following package.json at the root of your project:

{
  "name": "truffle",
  "version": "1.0.0",
  "dependencies": {
    "bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934",
    "crypto-js": "^3.1.8",
    "utf8": "^2.1.2",
    "web3": "^0.20.6",
    "xhr2": "^0.1.4",
    "xmlhttprequest": "^1.8.0"
  }
}

Then install the node modules:

npm install

Speedier migration

Investigation has revealed that Quorum networks, especially those running Raft consensus, are oftentimes slow to accept truffle’s migration logic and subsequent smart contract deployment. We have found that a duplicate migrate call remedies this issue.

Here is a quick bash script for the workaround. Create and save the following truffle_migrate.sh at the root of your project, being sure to replace the supplier_node placeholder value with whichever node you want to deploy the smart contract to:

#!/bin/bash
truffle migrate --network supplier_node --reset > /dev/null &
sleep 1
set -x
truffle migrate --network supplier_node --reset

Make the script executable:

chmod +x truffle_migrate.sh

Your project directory should now resemble the following:

Kaleido-MacBook-Pro:truffleProject kaleido$ ls
migrations    package-lock.json     test    truffle.js
contracts		node_modules		package.json		truffle-config.js
truffle_migrate.sh

Define a private recipient

Before “migrating” the simple storage smart contract, open your 2_deploy_simplestorage.js migration file and edit the privateFor parameter to reflect the private address of one of your nodes. The code snippets in this scenario exhibit a private transaction between the supplier_node and the retailer_node, leaving the shipper_node with no purview into the transaction inputs and ensuing state. A quick inspection of this migration file shows that we are passing “123” as our initialization value to the simple storage smart contract. As a result, we can expect the Supplier and Retailer nodes to clearly see the state for the storedData variable, whereas the Shipper node should have receive a null response upon query. Let’s try it out…

Deploy the smart contract

Use the truffle_migrate.sh script to deploy the private smart contract to the network. Truffle will automatically compile the two pieces of solidity code in your contracts/ directory and output the JSON interface into a newly created build/ directory:

./truffle_migrate.sh

The expected output is as follows:

Kaleido-MacBook-Pro:truffleProject kaleido$ ./truffle_migrate.sh
+ truffle migrate --network supplier_node --reset
Using network 'supplier_node'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x6810485f71a7a87e04781be6764f9f79ef5d21285c8d8c13b135b0c22c3559c8
  Migrations: 0xbbc3823a32458a5e95b4c2ff9bccfbc19fad1be5
Saving successful migration to network...
  ... 0x7d5b88d8874ffcbbed03fd675042ef28ae5056aeba55538eeea6f211859cef56
Saving artifacts...
Running migration: 2_deploy_simplestorage.js
  Deploying SimpleStorage...
  ... 0x997119be49b05cdccc850d7408adf988a4b7dcb08acc9488db017b86d0561798
  SimpleStorage: 0x25b092e73385144c0ed746995a9ec3ed2e1d29b2
Saving successful migration to network...
  ... 0x2ae4f1b1b0ac239da8bed165786ae82cd965dcafd5860df95b939865300112b9
Saving artifacts...

We can see the transaction hashes and addresses for each of the two smart contracts.

Query the state

Truffle offers a very useful “console” feature that allows you to interact with specific nodes. Let’s check the state of the stored integer on the Supplier’s node by calling the get function. Issue the following command to interface with the supplier_node:

truffle console --network supplier_node

Returns:

truffle(supplier_node)>

Now we can ask for the state by passing the following string within the console:

SimpleStorage.deployed().then(function(instance) { return instance.get(); })

This should return an array similar to the following: BigNumber { s: 1, e: 2, c: [ 123 ] }

Next let’s try asking the node that was not privy to the transaction for its state (in our case the shipper_node). Issue a .exit in the console to logout and then sign in as shipper_node:

truffle console --network shipper_node

Pass the same string. This should return an array similar to the following: BigNumber { s: 1, e: 0, c: [ 0 ] }

Notice that the shipper_node receives back “0” as opposed to the “123” returned to thesupplier_node. This is because the node was not specified within the privateFor parameter, and as a result has no data related to this smart contract address in its private state DB.

Lastly, exit the console and sign in as the retailer_node. The same query should return the original array with “123” because the Retailer node’s private address was specified in the 2_deploy_simplestorage.js migration file.

Contract abstraction

Truffle offers a handy javascript contract abstraction layer to call any functions available within your smart contracts. These abstractions were used in the migration files, as well as in the previous console interaction that called the get function. At the root of your project directory create a new file named sampletx.js and fill it with the following code:

var SimpleStorage = artifacts.require("SimpleStorage");

module.exports = function(done) {
  console.log("Getting deployed version of SimpleStorage...")
  SimpleStorage.deployed().then(function(instance) {
    console.log("Setting value to 12345...");
    return instance.set(12345, {privateFor: ["AQNPV7cpCge4p9OAkZOaauKaPDIT0o3QThtj1kXQrRc="]});
  }).then(function(result) {
    console.log("Transaction:", result.tx);
    console.log("Finished!");
    done();
  }).catch(function(e) {
    console.log(e);
    done();
  });
};

You see that the program first grabs the current deployed version of the simple storage smart contract, and then calls the set function passing an unsigned integer (“12345”) and privateFor argument to the constructor parameter. The private recipient specified above is still the retailer_node, so after running the program we expect to see “12345” applied to the private Patricia Trie of the Supplier and Retailer nodes, with the shipper_node still retaining no insight into the value.

Specify the target node within your network and use the exec command to run the program:

truffle exec --network supplier_node sampletx.js

The expected output is as follows:

Using network 'supplier_node'.

Getting deployed version of SimpleStorage...
Setting value to 12345...
Transaction: 0x0c37250a834ca388b094d594fee504876c42dc0af0262b412234ae324e4730f9
Finished!

Proceed to query the state for storedData by following the previously demonstrated truffle console interface approach.

Conclusion & troubleshooting

This was an extremely simplistic example of using truffle to interact with a Kaleido network. Truffle offers very extensive resources for building dApps and writing unit tests if you’re interested in leveraging truffle for your development. The key takeaway from this sample is the construction of the truffle.js configuration file and the usage of the web3 javascript API to set the HTTP provider. Additionally, take note of the fact that a specific node must be specified for deploy and set calls by passing --network <node_name>.

Prev Public and Private Transactions Next Connect to your node with Kaleido Connect