Chapter 8. Managing smart contracts with Web3.js

This chapter covers

  • Deploying contracts and interacting with them through geth’s console
  • Simplifying console-based deployment with Node.js
  • Deploying on a private network
  • Deploying on a mock network

In chapter 4, you had a first taste of deploying a smart contract on the Ethereum network. You did so through the Ethereum wallet and through Remix (with MetaMask). It looked relatively easy, as it involved only a few clicks on a well-designed screen. On the other hand, the convenience and simplicity of the user interface hid from you the key steps that take place on the network when contract deployment is in progress. Did you ask yourself, for example, how the code you entered on the code editor (of the wallet or Remix) got compiled, packaged, and sent to the Ethereum network, and how it finally got installed on each node?

In the last three chapters, you learned in depth how to develop a smart contract with Solidity. It’s time to learn the deployment process in depth as well. It’s important knowledge to have if you want to truly understand how decentralized applications work under the hood, and if you want to be able to troubleshoot postdeployment issues you might experience.

In this chapter, you’ll learn how to deploy and communicate with a smart contract through the geth console, using explicit Web3.js instructions. By doing so, you’ll perform all the commands and operations the wallet UI did magically for you while deploying the contract or moving tokens between accounts. Not only will you deploy a contract the hard way on a public test network, but you’ll go the extra mile and even set up a full private network and redeploy the contract on it. All of this will take much more effort than clicking a few buttons on the wallet UI, but you’ll be rewarded by gaining a much deeper knowledge of how an Ethereum network and related blockchain are set up and configured, as well as firsthand insight into the deployment process and client-to-network interaction. Along the way, I’ll also show you how to simplify the deployment process a little by running operations on Node.js.

Finally, after you’ve deployed a contract on a public test network and a private test network, you’ll also learn how to deploy a contract on Ganache, a special client that emulates (or mocks) a full Ethereum network on a single host. This is a convenient environment for developing and system-testing your contracts without having to pay for the latency of operations that take place on a real network, such as waiting for transactions to be mined and added on a new block, especially through times of network instability.

Before leaving the chapter, we’ll revisit SimpleCoin. This time, I’ll take advantage of the fact that you’ll have already learned how to deploy it on the network and communicate with it through Web3.js commands on the geth console. I’ll be able to show you how to build a simple UI that uses the same Web3 commands, but from within an HTML page.

8.1. Revisiting deployment through geth’s interactive console

When you installed geth, in chapter 3, you were pointing to MAINNET, the production Ethereum network. During development, I strongly advise you to point your tools, including your Ethereum client, to a test network instead, at least to avoid spending any real Ether directly or indirectly through accidental transactions or gas consumption. Various test networks are available at any given time, with a couple current examples being Ropsten and Rinkeby. Each test network is related to a slightly different version of the Ethereum platform, and you might find you have to upgrade geth to be able to run on a specific network. For the purposes of this chapter, I’ll refer to a test network as TESTNET.

To point geth to TESTNET, you must shut down any instance running against MAINNET (if you have any) and restart geth from a new command shell with the --testnet option:

C:Program Filesgeth>geth --testnet

Geth will start synchronizing to the test network in exactly the same way it did when you started it in MAINNET.

Note

In case you want to experiment and point to the Rinkeby test network based on the Proof of Stake consensus algorithm, use the --rinkeby option instead of –-testnet by typing C:Program Filesgeth>geth –-rinkeby. The rest of the chapter assumes you’re using ––testnet.

Using bootnodes to connect to specific peers

Occasionally your geth client might take a long time to discover peers that can send you the blockchain blocks. In that case, try using the --bootnodes option, which will force geth to access the network by connecting to specific peers, rather than discovering all peers by itself. I’ve found these nodes to be up most of the time:

geth --testnet –-bootnodes "enode://145a93c5b1151911f1a232e04dd1a76708
 dd12694f952b8a180ced40e8c4d25a908a292bed3521b98bdd843147116a52ddb645
 [email protected]:30303,enode://2609b7ee28b
 51f2f493374fee6a2ab12deaf886c5daec948f122bc83716aca27840253d191b9e63
 [email protected]:3030
 3,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487
 f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f
 [email protected]:30303" --verbosity=4

If you want to sync up your blockchain more quickly, you can also add these options:

--syncmode "fast" --cache=1024

From a separate command shell, you can now start an interactive console pointing to the test network exactly as you did when you were pointing to the main network:

C:Program Filesgeth>geth attach ipc:\.pipegeth.ipc

(Note that since geth 1.8.0, you have to specify the IPC path.) When the console starts up, it shows environmental information about the network it’s pointing to. As you can see in figure 8.1, datadir is pointing to testnet, which confirms you’re now in TESTNET.

Figure 8.1. The geth interactive console showing it’s connected to TESTNET

Deploying a contract through geth’s console for the first time is slightly more involved than doing it with a few clicks on Ethereum’s wallet. The wallet hides some of the steps that happen under the hood. By performing those steps explicitly, you’ll learn how the contract build and deployment process works in detail. Also, once you’ve scripted the entire process, future redeployment will become much quicker and less error-prone.

8.1.1. Contract build and deployment process

The diagram in figure 8.2 shows all the main steps required to build and deploy the contract through the interactive console:

  1. Compile the contract with the Solidity compiler.
  2. Push the contract’s binary interface (abi) and bytecode (bin) from the compiler to an output file.
  3. Run a Web3.js command that creates a contract factory of the contract from its binary interface.
  4. Run a Web3.js command that instantiates the contract by invoking the new() method of the contract factory and feeding it the bytecode.
Figure 8.2. The contract build and deployment process: 1. the contract is compiled with the Solidity compiler; 2. the contract binary interface and bytecode are pushed to an output file; and 3. a JavaScript using Web3 is created. This does three things: a. creates a contract factory of the contract from its binary interface (ABI); b. instantiates the contract by invoking new() on the contract factory; and c. feeds the bytecode to it.

8.1.2. Deploying SimpleCoin through geth’s console

Let’s go through the steps from figure 8.2 in detail by redeploying SimpleCoin to the test network. Following the build and deployment diagram, you must first compile the contract using solc.

Compiling a contract using solc

You can download the solc compiler from this GitHub page: https://github.com/ethereum/solidity/releases. (I’ve downloaded solidity-windows.zip from the Version 0.4.24 section—please download an equivalent or later.) After downloading it, copy the executable into a folder; for example, C:Ethereumsolidity-windows. (Feel free to name the folder after your OS.)

Create a new folder for your SimpleCoin code: C:EthereumSimpleCoin. Then create a new text file called SimpleCoin.sol and, for simplicity, paste in it one of the early versions of the contract code you wrote, like the one shown in the following listing.

Listing 8.1. SimpleCoin.sol, containing an early version of SimpleCoin
pragma solidity ^0.4.24;
contract SimpleCoin {
   mapping (address => uint256) public coinBalance;
    
   event Transfer(address indexed from, address indexed to, uint256 value);

   constructor(uint256 _initialSupply) public {
      coinBalance[msg.sender] = _initialSupply;   
   }

   function transfer(address _to, uint256 _amount) public {
      require(coinBalance[msg.sender] > _amount);
      require(coinBalance[_to] + _amount >= coinBalance[_to] );
      coinBalance[msg.sender] -= _amount;  
      coinBalance[_to] += _amount;   
      emit Transfer(msg.sender, _to, _amount);  
   }  
}

Place this file in the SimpleCoin code folder you created. Then open a command-line shell and move to the SimpleCoin code folder. From there, invoke the compiler as follows:

C:EthereumSimpleCoin>..solidity-windowssolc.exe 
 --bin -o bin --combined-json abi,bin SimpleCoin.sol 

Table 8.1 explains the compiler options you’ve used.

Table 8.1. The solc compiler options used to compile SimpleCoin

Compiler option

Purpose

--bin Produces the binary of the contract in hex format
-o bin Creates the binary output in the bin folder
--combined-json abi,bin Produces JSON output, including the ABI interface and the binary

The JSON output shown here will be redirected to the bincombined.json file:

{"contracts":{"SimpleCoin.sol:SimpleCoin" :{"abi":"[{"constant":false,
"inputs":[{"name":"_to", "type":"address"},{"name":"_amount",
"type":"uint256"}],"name" :"transfer","outputs":[],"payable
":false,"type":"function"}, {"constant":true,"inputs":[{"name":""
,"type":"address"}],"name" :"coinBalance","outputs":[{"name":""
,"type":"uint256"}],"payable" :false,"type":"function"},{"inputs"
:[{"name":"_initialSupply","type" :"uint256"}],"payable":false, 
"type":"constructor"},{"anonymous" :false,"inputs":[{"indexed":true
,"name":"from","type":"address"}, {"indexed":true,"name":"to",
"type":"address"},{"indexed":false, "name":"value","type":
"uint256"}],"name":"Transfer","type": "event"}]","bin":" 60806040523
4801561001057600080fd5b50604051602080610399833981018060405281019080805190602
00190929190505050806000803373ffffffffffffffffffffffffffffffffffffffff1673fff
fffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505
0610313806100866000396000f30060806040526004361061004c576000357c0100000000000
000000000000000000000000000000000000000000000900463ffffffff168063a9059cbb146
10051578063fabde80c1461009e575b600080fd5b34801561005d57600080fd5b5061009c600
480360381019080803573ffffffffff...(shortened for brevity)"}}, "version"
:"0.4.24+commit.6ae8fb59.Windows.msvc" }

The JSON output includes two members:

  • abi—This is the contract application binary interface (ABI). It shows the API that your contract exposes and that client applications that interact with the contract should use.
  • bin—This is the contract bytecode in hex format. You’ll need the contract’s ABI and bytecode to deploy it.
Deploying a contract through the interactive console

Open geth’s interactive console, which is pointing to TESTNET, and enter the code in listing 8.2. (Please make sure you use the code from the listing 8.2 file downloaded from the book website, as I’ve shortened it for brevity here.) You might have noticed that simpleCoinAbi has been assigned to the content of abi from combined.json, and data has been assigned to the content of bin, prefixed by 0x.

Listing 8.2. The geth interactive JavaScript instructions to deploy a contract
var initialSupply = 10000;                                1

var simpleCoinAbi =                                       2

[{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name"
:"_amount","type":"uint256"}],"name":"transfer","outputs":[],"payable":false
,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}]
,"name":"coinBalance","outputs":[{"name":"","type":"uint256"}],"payable"
:false,"type":"function"},{"inputs":[{"name":"_initialSupply","type"
:"uint256"}],"payable":false,"type":"constructor"},{"anonymous":false
,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true
,"name":"to","type":"address"},{"indexed":false,"name":"value","type"
:"uint256"}],"name":"Transfer","type":"event"}];    //abi interface from
 solc output

var SimpleCoinContractFactory = 
   web3.eth.contract(simpleCoinAbi);                     3

var simpleCoinInstance = 
   SimpleCoinContractFactory.new(                        4
   initialSupply,
   {
     from: web3.eth.accounts[0], 
     data:                                               5
'0x608060405234801561001057600080fd5b506040516020806103998339
8101806040528101908080519060200190929190505050806000803373ffffffffffffffffff
ffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020
019081526020016000208190555050610313806100866000396000f300608060405260043610
61004c576000357c010000000000000000000000000000000000000000000000000000000090
0463ffffffff168063a9059cbb14610051578063fabde80c1461009e575b600080fd5b348015
61005d57600080fd5b5061009c600480360381019080803573ffffffff ...(shortened for
brevity)', 
     gas: '3000000'      
   }, function (e, contract){                            6
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' 
         + contract.address + ' transactionHash: ' 
         + contract.transactionHash);
    }
 });

  • 1 SimpleCoin constructor input
  • 2 Contract ABI, copied from the abi member of the SimpleCoin.out compilation output file
  • 3 Initializes contract factory with the contract ABI
  • 4 Instantiates the contract
  • 5 The contract bytecode, copied from the bin member of the SimpleCoin.out compilation output file
  • 6 Triggers registration of callback at completion of the deployment process

After executing these instructions, you’ll get the following error message:

Error: authentication needed: password or unlock undefined

To deploy the contract, you have to unlock the account it’s being deployed through, web3.eth.accounts[0] (specified in the from property in listing 8.2). This will become the account owner.

If you don’t remember what accounts[0] refers to, you can list on the interactive console the TESTNET accounts you created from the Ethereum wallet in the previous chapters. The first one in the results list is accounts[0], and it’s named in the wallet as Main Account. This is what I got:

> web3.eth.accounts
["0xedde06bc0e45645e2f105972bdefc220ed37ae10",
 "0x4e6c301547 68b6bc3da693b1b28c6bd14302b578",
 "0x70e36be8ab 8f6cf66c0c953cf9c63ab63f3fef02",
 "0xc99048e9b9 8d3fcf8b5f0d5644794b562f9a2ea4",
 "0x47e3d3948f 46144afa7df2c1aa67f6b1b1e35cf1",
 "0x70ff99d4bc 8054b2e09269bcbfdddf8e1ae7d155"]

Enter the following command to unlock the account:

>personal.unlockAccount(
"0xedDE06bC0e45645e2f105972BDefC220ED37Ae10", 
PASSWORD_OF_YOUR_ACCOUNT_0)
Secure account unlocking

The most secure way of unlocking an account is through a geth command in the operating system shell:

 
geth --unlock <YOUR_ACCOUNT_ADDRESS> --password <YOUR_PASSWORD>

This avoids potential security concerns due to the interactive console recording a history of all the operations that have taken place on it.

Try to re-execute the script in listing 8.2. This time, you won’t get any error message. Wait for a few seconds, and the geth console will show a message similar to the following one:

Contract mined! address: 0x4291f37 a727d32e5620a0a4ed61d27ffdad757af
 transactionHash: 0x2b s7d2a015ca3397c1ec2b2d8e14b6c8ca7e3c06340d759a10d0e535
 843532fe6

Well done! You’ve deployed SimpleCoin on TESTNET through the geth interactive console. You can now examine the content of simpleCoinInstance:

> simpleCoinInstance
{
  abi: [{
      constant: false,
      inputs: [{...}, {...}],
      name: "transfer",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: true,

      inputs: [{...}],
      name: "coinBalance",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      inputs: [{...}],
      payable: false,
      type: "constructor"
  }, {
      anonymous: false,
      inputs: [{...}, {...}, {...}],
      name: "Transfer",
      type: "event"
  }],
  address: "0x4291f37a727d32e5620a0a4ed61d27ffdad757af",
  transactionHash: "0x2b7d2a015ca3397c1ec2b2d8e14b6c8ca7e3c06340d759a10d0e53
5843532fe6",
  Transfer: function(),
  allEvents: function(),
  coinBalance: function(),
  transfer: function()
}

An important property of the simpleCoinInstance object, which you’ll reference later, is its blockchain address:

address: "0x4291f37a727d32e5620a0a4ed61d27ffdad757af"

For now, let’s start interacting with SimpleCoin through simpleCoinInstance.

8.2. Interacting with SimpleCoin through geth’s console

Once you’ve deployed SimpleCoin on TESTNET, you can perform the same operations you did manually through the wallet in chapter 4, section 4.2, but this time through the console.

8.2.1. Checking coin balances

First of all, check the balance of accounts[0] (or Main Account), which you used to deploy the contract, using the coinBalance getter property, as seen in the previous sections:

>simpleCoinInstance.coinBalance(eth.accounts[0])
10000

Then check the balance of all the other accounts in the same way. You’ll get, as expected, the balances shown in table 8.2.

Table 8.2. Expected account balances

Account address

Account balance

0xedDE06bC0e45645e2f105972BDefC220ED37Ae10 10,000
0x4e6C30154768b6bc3Da693b1B28C6bd14302b578 0
0x70e36bE8AB8f6Cf66C0C953cF9c63aB63f3FeF02 0
0xc99048E9B98D3FcF8b5f0D5644794B562f9A2ea4 0

Try to check, as you did in the wallet, the balance of an invalid address, again obtained by modifying the last digit of the Main Account address:

>simpleCoinInstance.coinBalance(eth.accounts[0])
0

In this case, you won’t get the validation error that the wallet returned—it doesn’t allow you to enter invalid addresses—you’ll get a zero balance. This is because the coin-Balance() getter doesn’t perform any validation on the input address. (The wallet performs that validation instead.) It returns a valid balance for valid addresses contained in the coinBalance mapping, and the default int256 value (0) for anything else.

8.2.2. Transferring coins

The next operation you’ll perform is a coin transfer. Try moving 150 coins from the Main Account to accounts[2] through SimpleCoin’s transfer() function. To execute this, you also have to specify the maximum gas amount allowed for the operation:

> simpleCoinInstance.transfer(eth.accounts[2], 150,
     {from:eth.accounts[0],gas:200000});

When you attempt this operation, you’ll get the following message:

Error: authentication needed: password or unlock

You must unlock the Main Account to digitally sign the transaction, as you did earlier (replace, as usual, your accounts[0] password):

>personal.unlockAccount(eth.accounts[0], 'PASSWORD_OF_ACCOUNT_0')
Warning

Although I’m showing you how to unlock an account through the geth console, this isn’t entirely safe because the password is in clear text (it’s not encrypted) and each operation is logged in the console history. See the “Secure account unlocking” sidebar earlier in this chapter for more information.

When you retry the transfer, the transaction will be successful, and you’ll get the transaction hash. In my case, I got

"0xccd8211bde9ac8075a6d43fc51d705cf60db5c7f0a25769cf7c8cff94103af7e"

Recheck the balances of all of your accounts. You’ll get, as expected, the balances shown in table 8.3.

Table 8.3. Expected new account balances

Account address

Account balance

0xedDE06bC0e45645e2f105972BDefC220ED37Ae10 9,850
0x4e6C30154768b6bc3Da693b1B28C6bd14302b578 0
0x70e36bE8AB8f6Cf66C0C953cF9c63aB63f3FeF02 150
0xc99048E9B98D3FcF8b5f0D5644794B562f9A2ea4 0

Now move 50 coins from accounts[2] to accounts[1] after unlocking accounts[2]:

>personal.unlockAccount(eth.accounts[2], PASSWORD_OF_ACCOUNTS[2])
>simpleCoinInstance. transfer(eth.accounts[1], 
50, {from:eth.accounts[2],gas:200000});

After getting the transaction hash, you can recheck the balances. You’ll get the balances shown in table 8.4.

Table 8.4. Expected account balances

Account address

Account balance

0xedDE06bC0e45645e 2f105972BDefC220ED37Ae10 9,850
0x4e6C30154768b6bc3Da693b1B28C6bd14302b578 50
0x70e36bE8AB8f6Cf66C0C953cF9c63aB63f3FeF02 100
0xc99048E9B98D3FcF8b5f0D5644794B562f9A2ea4 0

Before we close this section, you’ll learn how to reference a contract that has already been deployed, such as SimpleCoin. First you’ll close the geth console and reattach it.

8.2.3. Referencing a running contract

Close the interactive geth console and reattach it from the operating system command-line shell:

C:Program Filesgeth>geth attach ipc:\.pipegeth.ipc

As you’ll remember from the end of section 8.1.2, when you deployed SimpleCoin on TESTNET from the geth console, the console returned the contract address:

0x4291f37a727d32e5620a0a4ed61d27ffdad757af

If you now want to interact with that deployed instance of SimpleCoin from the geth console, you must create a proxy to contract from its ABI and connect to the remote instance by feeding the address of the deployed contract to the at() method:

var remoteSimpleCoinAddress = "0x4291f37a727d32e5620a0a4ed61d27ffdad757af";
var simpleCoinAbi =
      [{"constant":false,"inputs":[{"name":"_to","type":"address"},
{"name":"_amount","type":"uint256"}],
"name":"transfer","outputs" :[],"payable":false,"type":"function"},
{"constant":true,"inputs" :[{"name":"","type":"address"}],
"name":"coinBalance","outputs" :[{"name":"","type":"uint256"}],
"payable":false,"type" :"function"},
{"inputs":[{"name": "_initialSupply","type":"uint256"}],
"payable":false,"type": "constructor"},{"anonymous":false,
"inputs":[{"indexed":true, "name":"from","type":"address"},
{"indexed":true,"name":"to", "type":"address"},
{"indexed":false,"name":"value", "type":"uint256"}],
"name":"Transfer","type": "event"}];                       1
var SimpleCoinContractProxy = 
   web3.eth. contract(simpleCoinAbi);                      2
var simpleCoinInstance =                                   3
     SimpleCoinContractProxy. at(remoteSimpleCoinAddress);

  • 1 The abi interface from solc SimpleCoin output
  • 2 Creates a proxy to the SimpleCoin contract
  • 3 Connects to the instance of SimpleCoin deployed earlier

Connecting to a deployed contract from another contract is slightly different and is explained in the sidebar.

Referencing a deployed contract from another contract

In this chapter, we’re focusing on the interaction between a Web3.js client (such as the geth console or, as you’ll see later, the Node.js console or an HTML + JS web page) and a deployed Solidity contract. If you want to interact with a deployed Solidity contract from another Solidity contract, you’ll use a technique similar to the one you saw in the previous chapter for connecting to a deployed library from a contract:

contract SimpleCoinProxy {
    function transfer(address _to, uint256 _amount) public;
}
 
contract MyContract {
    SimpleCoinProxy simpleCoinProxy;
    
    function MyContract(address _simpleCoinAddress)
    {
        require(_simpleCoinAddress != 0x0);
        simpleCoinProxy = SimpleCoinProxy(_simpleCoinAddress); 
    }
    

    function transferSimpleCoin(address _to, uint256 _amount) {
        simpleCoinProxy.transfer(_to, _amount) ;
    }
}

In this case, you define a local proxy contract as an abstract contract mirroring the public interface of the remote contract. Then you instantiate the proxy by feeding the address of the deployed remote contract to its constructor.

To double-check that you’re truly connected to the previously deployed SimpleCoin instance, recheck the value of the coinBalance property against all accounts, starting from:

>simpleCoinInstance. coinBalance(
"0xedde06bc0e45645e2f105972bdefc220ed37ae10")

You’ll get the balances shown in table 8.5.

Table 8.5. Expected account balances

Account address

Account balance

0xedDE06bC0e45645e2f105972BDefC220ED37Ae10 9,850
0x4e6C30154768b6bc3Da693b1B28C6bd14302b578 50
0x70e36bE8AB8f6Cf66C0C953cF9c63aB63f3FeF02 100
0xc99048E9B98D3FcF8b5f0D5644794B562f9A2ea4 0

At this point, you can perform a new transfer operation; for example, move some coins from accounts[2] to accounts[3]. I leave it to you as an exercise.

In this chapter, you’ve learned how to deploy a contract on TESTNET from geth’s interactive console. Deploying on MAINNET, the production Ethereum network, is identical to deploying on TESTNET, except for needing real Ether to run transactions (to fund the gas consumed) on MAINNET.

You might have found command-based deployment through geth’s console inefficient and manually intensive: you had to compile the contract separately with the solc compiler, copy the ABI and bytecode manually from the compiler’s output, and paste them into some Web3 instructions. It’s possible to simplify deployment if you’re willing to change the toolset and start using Node.js instead of the geth interactive console. This is what we’ll explore in the next section.

8.3. Simplifying command-based deployment with Node.js

Node.js is a cross-platform runtime for developing server-side applications in Java-Script. If you’re not familiar with Node.js, the sidebar tells a little more about it. You might be wondering why Node.js is relevant for Ethereum development. It’s relevant because it can serve as an enhanced geth console that you can use to connect to the geth client and import many packages that will help improve and simplify your development efforts.

Node.js

Node.js is a server-side runtime environment for JavaScript. It’s based on an event-driven architecture capable of handling asynchronous I/O. It includes a set of standard modules that are libraries of functions for networking (including TCP/IP, HTTP), binary data, file system I/O operations, data streams, and many others. You can create and distribute custom modules through the node package manager, also known as npm.

The platform has two main objectives. The first is to allow JavaScript developers to write server-side applications using their favorite language. The second is to provide a server-side web scripting environment with increased scalability through asynchronous programming rather than explicit multithreading.

You can download Node.js from https://nodejs.org/.

If you haven’t already, I recommend you install the latest version of Node.js, if you can, or at least version 8.0 or higher. Once you’ve installed Node.js, it’s a good idea to install the Web3 module through the node package manager (npm), so you can reference it from any JavaScript code you run on Node.js.

8.3.1. Installing Web3 and solc on Node.js

Before installing Web3, create a new folder, and initialize it for npm as follows:

C:Ethereum>md SimpleCoinWithNode
C:Ethereum>cd SimpleCoinWithNode
C:EthereumSimpleCoinWithNode>npm init

You’ll be asked to set up various properties of the package.json file that the initialization command (npm init) is going to create. Set the name as simple_coin and the version as 1.0.0 and leave all the other fields blank:

name: (SimpleCoinWithNode): simple_coin
Version: (1.0.0) 1.0.0

When asked to confirm whether the file being created is correct, type yes and press Enter:

Is this ok? (yes) yes

Then you can install Web3 (version 0.20.4, as I have done), as follows:

C:EthereumSimpleCoinWithNode>npm install [email protected]

You’ll get output like this:

[email protected] C:EthereumSimpleCoinOnNodeJS
`-- [email protected]
  +-- [email protected]  (git+https://github.com/frozeman/bignumber.js
-nolookahead.git#57692b 3ecfc98bbdd6b3a516cb2353652ea49934)
  +-- [email protected]
  `-- [email protected]

npm WARN [email protected] No description
npm WARN [email protected] No repository field.

To test that Web3 is working as expected, you can try to retrieve a list of your TESTNET accounts through the Node.js console. Before doing so, you must have a geth instance running. You have to open a separate OS console, start geth on TESTNET, and expose RPC and various RPC interfaces:

C:Program Filesgeth>geth --testnet --rpc 
--rpcapi="db,eth,net,web3,personal,web3"

Remember to also use the –-bootnodes option from earlier if the geth console seems stale.

Now go back to the console you used to install the Web3 node package and start the interactive node console, as follows:

C:EthereumSimpleCoinWithNode>node

You’ll see a node console prompt:

> 

Then you can retrieve your TESTNET account addresses, as follows:

>const Web3 = require('web3');
>web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
>web3.eth.getAccounts(console.log);

You’ll get output like:

> null [ '0xedde06bc0e45645e2f105972bdefc220ed37ae10',
  '0x4e6c30154768b6bc3da693b1b28c6bd14302b578',
  '0x70e36be8ab8f6cf66c0c953cf9c63ab63f3fef02',
  '0xc99048e9b98d3fcf8b5f0d5644794b562f9a2ea4' ]

This confirms that Web3 is working as expected. Get out of the node console as follows (note the dot before exit):

>.exit

You should see the OS prompt:

C:EthereumSimpleCoinWithNode>

Then you can install the Solidity compiler solc (version 0.4.24, as I have done):

C:EthereumSimpleCoinWithNode>npm install [email protected]

Now you’re ready to create a deployment script to simplify the build and deployment process.

8.3.2. Building and deploying interactively through the Node.js console

The best way to create a build and deployment script is to try to compile and deploy a contract interactively on the Node.js shell and then place the sequence of commands you’ve proved to work in a file. You can later execute this file as a single task.

First of all, place SimpleCoin’s code from listing 8.1 in the following file:

C:EthereumSimpleCoinWithNodeSimpleCoin.sol

Now start the node console:

C:EthereumSimpleCoinWithNode>node

Then you can reference the node JavaScript packages you’ll be using, as follows:

>const fs = require('fs');          1
>const solc = require('solc');      2
>const Web3 = require('web3');      3

  • 1 File system package
  • 2 Solidity compiler package
  • 3 Web3 package

The following command creates an instance of the web3 object:

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

Set the initial SimpleCoin supply that will be fed to the SimpleCoin constructor:

>const initialSupply = 10000;

Then you can set account2 as the sender of the deployment transaction:

>const account2 = web3.eth.accounts[1];
>const sender = account2;
>const senderPassword = 'account2';

Load the source code of SimpleCoin and assign it to the source variable:

>const source = fs.readFileSync(
'c:/Ethereum/SimpleCoinWithNode/SimpleCoin.sol', 
'utf8');

Then you can compile the contract and assign it to compiledContract:

>const compiledContract = solc.compile(source, 1);

Extract the ABI and bytecode from the compiled contract and assign them to two new variables (note that you must place '0x' before the bytecode):

>const abi = compiledContract.contracts[':SimpleCoin'].interface;
>const bytecode = '0x' + 
compiledContract.contracts[':SimpleCoin'].bytecode;

Assign the gas estimate to a variable after having increased it, so you make sure the transaction runs to completion:

>const gasEstimate = web3.eth.estimateGas({ data: bytecode }) + 100000;

Now you can create a contract factory (or generator) initialized with SimpleCoin’s ABI:

>const SimpleCoinContractFactory = web3.eth.contract(JSON.parse(abi));

Before deploying the contract, unlock the account that will sign and send the deployment transaction:

>web3.personal.unlockAccount(sender, senderPassword);

You’re now ready to deploy the contract and instantiate it in a single operation with the new() function, which also takes two callbacks: one called after successful deployment, the other one in case of errors:

>const simpleCoinInstance = SimpleCoinContractFactory.new(initialSupply, {
    from: sender,
    data: bytecode,
    gas: gasEstimate
   }, function (e, contract){
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' 
         + contract.address 
         + ' transactionHash: ' 
         + contract.transactionHash);
    }
 });

In the meantime, observe the geth console (not the Node.js console!). You should see output similar to this:

INFO [08-20|09:50:17] Imported new chain segment blocks=1
 txs=3  mgas=1.493 elapsed=6.016ms  mgasps=248.107  number=1521353 hash=
 acbacb...7212ed

INFO [08-20|09:50:18] Submitted contract creation fullhash=
 0xb1a204653ba5f0cf5b2953eba15b3a55d3c73a358a1823f327f9cc02c4fc8a2e contract
 =0xa9d460c5aba794db20d005f54e8eefa80b76ff2e
INFO [08-20|09:50:26] Imported new chain segment blocks=1
 txs=0  mgas=0.000elapsed=2.527ms  mgasps=0.000    number=1521354 hash
 =2e95c3...7f9f9b

Then, after a few seconds, in the Node.js console you should see something like this:

Contract mined! address: 0xa9d460c5aba794db20d005f54e8eefa80b76ff2e
 transactionHash: 0xb1a204653ba5f0cf5b2953eba15b3a55d3c73a358a1823f327f9cc0
 2c4fc8a2e

Well done! Now that you’ve managed to deploy the SimpleCoin contract through the node console, you can automate all the steps you performed interactively.

8.3.3. Creating a build and deployment script

Copy all the commands you entered previously, collected in listing 8.3, into the following file:

C:EthereumSimpleCoinWithNodedeploySimpleCoin.js

(Make sure you replace the password of your account 2 in the senderPassword assignment.)

Listing 8.3. SimpleCoin Node.js deployment script
const fs = require('fs');
const solc = require('solc');
const Web3 = require('web3'); 
const web3 = new Web3(new
     Web3.providers.HttpProvider("http://localhost:8545")); 

const account2 = web3.eth.accounts[1];
const sender = account2;
const senderPassword = 'PASSWORD OF ACCOUNT 2';
const initialSupply = 10000;

const source =
     fs.readFileSync('c:/Ethereum/SimpleCoinWithNode/SimpleCoin.sol',
     'utf8');
const compiledContract = solc.compile(source, 1);
const abi = compiledContract.contracts[':SimpleCoin'].interface;
const bytecode = '0x' + compiledContract.contracts[':SimpleCoin'].bytecode;
const gasEstimate = web3.eth.estimateGas({ data: bytecode }) + 100000;

const SimpleCoinContractFactory = web3.eth.contract(JSON.parse(abi));

web3.personal.unlockAccount(sender, senderPassword);

const simpleCoinInstance = SimpleCoinContractFactory.new(initialSupply, {
    from: sender,
    data: bytecode,

    gas: gasEstimate
   }, function (e, contract){
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' + contract.address + '
     transactionHash: ' + contract.transactionHash);
    }
 });

Now you can redeploy (and reinstantiate) SimpleCoin by running the script you’ve created from the OS command shell, as follows:

C:EthereumSimpleCoinWithNode>node deploySimpleCoin.js

You should see the same output as before on both the geth and Node.js consoles:

Contract mined! address: 0x664e5f1df05e11bbf0c72c7c28419e1f8ed5821e
 transactionHash: 0x7191139eb5f164da7effbe9e5795fbd28fc212bfd629422da87dbebb
 eb13484c

You can adapt this script easily to compile other contracts, or multiple contracts, as required.

8.3.4. Interacting with a contract from Node.js

Once you’ve deployed the contract, you can interact with it through simpleCoin-Instance. For example, you can move some tokens from one account to another. Try to do this first interactively, so you can produce a script, as you did earlier.

First of all, reference the necessary JavaScript packages, as you did earlier:

>const fs = require('fs');
>const solc = require('solc');
>const Web3 = require('web3');
>const web3 = new
        Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

Then also create a contract factory, as you did earlier:

const source = fs.readFileSync(
'c:/Ethereum/SimpleCoinWithNode/SimpleCoin.sol', 
'utf8');
const compiledContract = solc.compile(source, 1);
const abi = compiledContract.contracts[':SimpleCoin'].interface;

const SimpleCoinContractFactory = web3.eth.contract(JSON.parse(abi));

Now assign the address of the SimpleCoin instance that you deployed interactively to an address variable:

const contractAddress =
'0xa9d460c5aba794db20d005f54e8eefa80b76ff2e'; 
//replace appropriately 

Then you can connect to that SimpleCoin instance with the at() function:

const simpleCoinInstance = SimpleCoinContractFactory.at(contractAddress);

Now assign accounts[1] and accounts[2] to two variables:

>const account2 = web3.eth.accounts[1]; 
>const account3 = web3.eth.accounts[2]; 

Then do the same for the related balances:

>var account2Balance = simpleCoinInstance.coinBalance(account2);
>var account3Balance = simpleCoinInstance.coinBalance(account3);

You can display these balances as follows:

>console.log('BALANCES BEFORE transferring tokens');
>console.log('Account 2 balance: ' + account2Balance);
>console.log('Account 3 balance: ' + account3Balance);

Finally, unlock account2 so you can sign and execute the transfer transaction from it:

>web3.personal.unlockAccount(account2, "account2");

Then you can execute the transfer transaction and assign its hash to a variable:

>var transactionHash = simpleCoinInstance.transfer(
account3, 20, {from:account2,gas:200000});
console.log(
'SUBMITTED transfer() transaction. Transaction hash: ' 
+ transactionHash);

Poll the status of the transaction until completion as follows:

>var transactionReceipt = null;
>while (transactionReceipt == null)
{
     transactionReceipt = web3.eth.getTransactionReceipt(transactionHash);
}

Then display information on the completed transaction:

>console.log('COMPLETED transfer() transaction. Transaction: ' +
transactionHash + 'has been consolidated on block: ' +
transactionReceipt.blockNumber);

You can now update the values of the account balance variables:

>account2Balance = simpleCoinInstance.coinBalance(account2);
>account3Balance = simpleCoinInstance.coinBalance(account3);

Finally, you can display the new balances after the transfer:

>console.log('BALANCES AFTER transferring tokens');
>console.log('Account 2 balance: ' + account2Balance);;
>console.log('Account 3 balance: ' + account3Balance);

Having proven that the commands you’ve entered work correctly, you can move them into a file called transferTokens.js. The code appears in the following listing.

Listing 8.4. transferTokens.js script, which transfers coins between two accounts
const fs = require('fs');
const solc = require('solc');
const Web3 = require('web3'); 
const web3 = new Web3(new
     Web3.providers.HttpProvider("http://localhost:8545")); 

const source = fs.readFileSync(
'c:/Ethereum/SimpleCoinWithNode/SimpleCoin.sol', 
'utf8');
const compiledContract = solc.compile(source, 1);
const abi = compiledContract.contracts[':SimpleCoin'].interface;

const SimpleCoinContractFactory = web3.eth.contract(JSON.parse(abi));
const contractAddress = 
'0xa9d460c5aba794db20d005f54e8eefa80b76ff2e'; 
//replace appropriately 

const simpleCoinInstance = SimpleCoinContractFactory.at(contractAddress);

const account2 = web3.eth.accounts[1]; //account2
const account3 = web3.eth.accounts[2]; //account3

var account2Balance = simpleCoinInstance.coinBalance(account2);
var account3Balance = simpleCoinInstance.coinBalance(account3);

console.log('Account 2 balance: ' + account2Balance);
console.log('Account 3 balance: ' + account3Balance);

web3.personal.unlockAccount(account2, "PASSWORD OF ACCOUNT 2");

var transactionHash = simpleCoinInstance.transfer(
account3, 20, {from:account2,gas:200000});
console.log(
'SUBMITTED transfer() transaction. Transaction hash: ' 
+ transactionHash);

var transactionReceipt = null;
while (transactionReceipt == null)
{
     transactionReceipt = web3.eth.getTransactionReceipt(transactionHash);
}

console.log(
'COMPLETED transfer() transaction. Transaction: ' 
+ transactionHash + 'has been consolidated on block: ' +
     transactionReceipt.blockNumber);

account2Balance = simpleCoinInstance.coinBalance(account2);
account3Balance = simpleCoinInstance.coinBalance(account3);

console.log('BALANCES AFTER transferring tokens');
console.log('Account 2 balance: ' + account2Balance);
console.log('Account 3 balance: ' + account3Balance);

Run it as follows:

C:EthereumSimpleCoinWithNode>node transferTokens.js

Before I close the topic of deployment, in the next section you’ll learn how to deploy a contract on a private network. A private network is an Ethereum environment completely under your control.

8.4. Deploying on a private network

A private network can be useful if, during development, you don’t want to deal with the delays involved in working with a public test network. For example, on the public test network, you generally have to wait a number of

  • minutes to mine Ether through CPU mining
  • minutes to resynchronize to the network, if you’ve disconnected and then reconnected after a few hours or days
  • seconds for contract deployment or transactions confirmation

Occasionally, when you reconnect after a few days, TESTNET doesn’t sync correctly due to major development or a fork that has happened in the meantime. In that case, you have to restart from scratch by deleting the whole blockchain stored on your node.

Another benefit of running against a private network is that you can efficiently test contracts whose logic depends on time-based expiration. Because contracts are isolated from external services, such as external clocks, you’ll commonly emulate time with a number of blocks, under the assumption that it takes around 14 seconds to generate a new block.

Setting up a private network means starting up a custom blockchain, disconnected from the official Ethereum TESTNET or MAINNET blockchains. Before doing so, you should learn how geth manages the blockchain—how it records its history and what happens during synchronization.

8.4.1. How geth accesses the blockchain

When you start it up, geth looks at your Ethereum data folder before connecting to the network. As you know from chapter 3, when I presented the keystore, the Ethereum data folder is located in one of the following directories:

  • Windows: C:Usersusername\%appdata%RoamingEthereum
  • Linux: ~/.ethereum/
  • Mac: ~/Library/Ethereum/

This folder contains the following folders:

Ethereum
|---geth               1
|---keystore           2
|---testnet            3
     |---geth          4
     |---keystore      4
|---rinkeby            5
     |---geth          4
     |---keystore      4

  • 1 The geth data folder for public production network
  • 2 The keystore folder for public production network
  • 3 Folder for the Ropsten public test network
  • 4 Each test folder contains a geth data folder and a keystore folder.
  • 6 Folder for the Rinkeby public test network

You already examined the content of the keystore back in chapter 3. Let’s have a look at the content of each geth folder:

geth
|---chaindata    1
|---ethash       2
|---nodes        3

  • 1 The chaindata folder contains the blockchain data files.
  • 2 The ethash folder contains data structure files necessary for mining.
  • 3 The nodes folder contains data about discovered network nodes.
Note

As for the keystore, each geth folder is specific to a single network, so ethereum/geth and ethereum/testnet/geth contain completely different data. As a result, if you want to back up the blockchain and keystore for a specific environment, you need to take a copy of only the relevant geth and keystore folders.

8.4.2. Setting up a private test network

Now that you’ve learned where the public (test or main) blockchain is stored on your geth node, you’ll be able to understand more easily the steps needed to set up a test network. We’ll go through each of these steps in detail:

  1. Create a custom genesis file.
  2. Create a custom blockchain data folder.
  3. Choose a name to identify your node.
  4. Launch geth with a custom configuration to generate the genesis block.
  5. Launch geth with a custom configuration to run a private network node.
  6. Create the Etherbase account.
  7. Start mining to get Ether on the Etherbase account.
Creating a custom genesis file

A blockchain starts from a master or genesis block that has no parent and seeds the chain. All nodes in your private network will have to reference the same genesis block to agree with each other.

Create a new folder—for example, called C:privatenetdev—and create within it a file called genesis.json. Then paste into it the following content:

{
    "config": {                   1
        "chainId": 10101010,      2
        "homesteadBlock": 0,      3
        "eip155Block": 0,         3
        "eip158Block": 0          3
    },
    "difficulty": "4000",         4
    "gasLimit": "3141592",        5
    "alloc": {                    6
          
    }
}

  • 1 Parameters to configure a specific Ethereum protocol version (Homestead, for example)
  • 2 Identifier of the network being created
  • 3 Protocol-specific parameters
  • 4 Difficulty: how easy it is for a miner to find a valid nonce; the lower, the easier
  • 5 Maximum gas limit allowed; the lower the limit, the more likely transactions can fail
  • 6 You can preallocate Ether to specific addresses in this section.

The most important settings are explained in table 8.6.

Table 8.6. Genesis file settings

Setting

Description

chainId After you create this identifier, you can connect to the private network through it.
difficulty A higher value increases the number of attempts a mining node needs to perform before finding a valid nonce so it can add a new block to the blockchain. To give you an idea, a value of 4000, for example, makes mining easy because it allows any miner to generate a new block roughly every five seconds. You’d have to increment the difficulty value exponentially if you wanted a node to be able to generate a new block only every few minutes or hours.
gasLimit This is the maximum limit allowed for a transaction, regardless of the limit set on the transaction itself. The higher it is, the less likely it is that transactions will fail with the following error: “Error: exceeds block gas limit.”
Creating a custom blockchain data folder

Create a folder in a different area with respect to the standard Ethereum data. An example would be C:privatenet.

Choosing a name for the test node

Assigning a name to the first node of your private network makes it easy to identify if you decide to add more nodes later on. For this example, try PrivateNetMaster.

Launching geth with custom configuration to generate the genesis block

So far, you’ve always started geth with the default configuration. At most, you’ve specified whether to start it against testnet, with the --testnet option, or in console mode, by specifying the console or attach command.

To generate the custom genesis block of your private network, launch geth with the init command, as follows:

C:program filesgeth>geth --networkid 10101010 
--identity "PrivateNetMaster" --rpc 
--rpcapi="db,eth,net,web3,personal,web3" 
--datadir "C:privatenet" --port "30303" --nodiscover 
--maxpeers 0 init C:privatenetdevgenesis.json

Table 8.7 describes every option this geth launch command used. After launching geth, you should see output similar to that in the screenshot in figure 8.3.

Table 8.7. The options used to start geth against a private network

geth option

Description

--networkid This identifies a specific Ethereum network. For example, TESTNET and MAINNET have their own networkid (default: 1, which is MAINNET).
--identity This is useful to identify a specific node of the private network.
--rpc This enables the JSON-RPC interface on the node (default: enabled).
--rpcapi You enable the API over RPC (default: web3).
--datadir The blockchain data folder
--port The network listening port other peers of the same network use to connect to this node
--nodiscover This disables discovery of the node by clients that are pointing to the same networkid and are referencing the same genesis block. You must add other nodes to this network manually.
--maxpeers The maximum number of peers allowed from this node. By setting it to zero, you explicitly state you’ll have only one node. If you need to add more network nodes, you must change this setting later.
Figure 8.3. The output from geth after generating the genesis block of your private network

Launching geth with custom configuration to run a private network node

Now that you’ve generated the genesis block, you can launch geth using the same parameters as before, except from the init command:

C:program filesgeth>geth --networkid 10101010 
--identity "PrivateNetMaster" --rpc 
--rpcapi="db,eth,net,web3,personal,web3" 
--datadir "C:privatenet" --port "30303" 
--nodiscover --maxpeers 0
Tip

You might have to close down any running geth instances pointing to MAINNET or TESTNET and/or any running wallet instance, depending on how you’ve configured port numbers.

If geth starts successfully, you should see a screen similar to the screenshot in figure 8.4. You’ll notice the screen doesn’t show any progress—no blocks are being generated. I’m sure you know why. It’s because yours is the only node of the private network, and it’s not mining!

You don’t have any new accounts yet on your brand new blockchain. Before starting mining, you must create at least the Etherbase (or coinbase) account.

Figure 8.4. The output from geth after launching it against the private network you’ve created

Creating the Etherbase account

Attach to the private network node by opening a new command-line console and then launching geth attach as usual. When the geth console opens, it will display the name and data directory of your private network, respectively, in the instance and datadir output, which confirms you’re attached to the private network node:

C:Program FilesGeth>geth attach ipc:\.pipegeth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/PrivateNetMaster/v1.7.3-stable-4bb3c89d/windows-amd64/go1.9
at block: 1 (Mon, 26 Jun 2017 09:31:42 BST)
 datadir: C:privatenet
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0
 txpool:1.0 web3:1.0

You can now create an account as you saw in chapter 3, section 3.4.3 (entering your own password in place of the text “PASSWORD OF ETHERBASE ACCOUNT”):

>personal.newAccount("PASSWORD OF ETHERBASE ACCOUNT")
"0x3f9e54337cce348607b76200fc19f3f6e3e8e358"

You can set this account as an Etherbase account with the setEtherbase method of the miner object. Pass the address of the account you’ve created as an input:

>miner.setEtherbase("0x3f9e54337cce348607b76200fc19f3f6e3e8e358")

Before starting to mine, you can create additional accounts. For the moment, create one more account. You’ll use it to interact with SimpleCoin on the private network (also here entering your own password instead of the text “PASSWORD OF ACCOUNT 2”):

>personal.newAccount("PASSWORD OF ACCOUNT 2")
"0x336a008e3a3b099bcd125cd54fc193070fe1d177"
Start mining to get Ether on the Etherbase account

You can now start mining as you saw in the CPU mining section of chapter 3:

>miner.start() 

Let the miner run for a few seconds and check the output from the main geth shell. You’ll see the DAG gets generated (see chapter 3, section 3.3.4 to refresh your memory on the DAG, if needed), as shown in the screenshot in figure 8.5.

Figure 8.5. As soon as you launch mining for the first time, the DAG gets generated.

If you check the balance of the Etherbase account in the interactive shell, it will still be zero:

> eth.getBalance(eth.coinbase).toNumber();
0

After a few seconds you should see the first blocks being mined in the main geth shell, as shown in the screenshot in figure 8.6.

Note

You might wonder where these initial blocks are coming from, as you haven’t submitted any transactions yet. A miner will always try to create a block even if no transaction is available in its memory pool. Creating an empty block is perfectly legal and can happen in periods of low transaction activity. As you know, though, a miner is encouraged to include in a block as many transactions as possible through the collection of transaction fees, so empty blocks are rare in practice and tend to appear mainly in private networks.

Figure 8.6. After the DAG has been generated and the first blocks get created

The Etherbase balance will now show Ether (expressed in Wei)

>eth.getBalance(eth.coinbase).toNumber();
5000000000000000000

and you can stop the mining, if you want:

>miner.stop()

You’ve created your local private network. Now you can try to deploy SimpleCoin on it.

8.4.3. Deploying SimpleCoin on the private network

Working on a private network is identical to working on a public network, so you should be able to deploy the contract by yourself. But you should check to make sure everything goes as expected. Table 8.8 shows the accounts you have in the private network.

Table 8.8. Accounts in the private network

Account

Address

Main account 0x3f9e54337cce348607b76200fc19f3f6e3e8e358
Account 2 0x336a008e3a3b099bcd125cd54fc193070fe1d177

Make sure account 2 has some Ether, so you can deploy from it. You can get some from the Main account, which has acquired Ether through mining:

> personal.unlockAccount(eth.coinbase, "PASSWORD OF ETHERBASE ACCOUNT");
> eth.sendTransaction({from:eth.coinbase, 
to:eth.accounts[1], value: web3.toWei(2.0, "ether")})

You can now deploy from account 2 by running the script you created in section 8.3.3 from the OS command shell, as follows:

C:EthereumSimpleCoinWithNode>node deploySimpleCoin.js

Although you don’t get any error messages, something seems to have gone wrong: the contract address appears as undefined. Remember, when you deployed on TESTNET, after a few seconds you got this output:

Contract mined! address: 0x4291f37a727d32e5620a0a4ed61d27ffdad757af
 transactionHash: 0x2b7d2a015ca3397c1ec2b2d8e14b6c8ca7e3c06340d759a10d0e5358
 43532fe6

But now you haven’t received confirmation of the contract address. If you check on the output of the main geth shell, you’ll see the screen hanging on

INFO [06-26|09:19:52] Submitted contract creation fullhash=
 0x2db88eadcd908f8c66294f2d427825e46bf820f089277f436ed5165f739efbbd
 contract=0xd144854e0d90e49726fab8e613115c217ee5262c

Think about what might be causing this problem. Yes, you’re right: no mining is taking place! To complete the deployment transaction, this needs to get mined.

Go back to the geth interactive console and restart mining:

> miner.start()

(Remember, earlier you stopped mining through miner.stop().) The expected completion message will appear immediately:

Contract mined! address: 0xd144854e0d90e49726fab8e613115c217ee5262c
 transactionHash: 0x2db88eadcd908f8c66294f2d427825e46bf820f089277f436ed5165f
 739efbbd

If you now inspect simpleCoinInstance, it contains the contract address:

...

  address: "0xd144854e0d90e49726fab8e613115c217ee5262c",
  transactionHash:
 "0x2db88eadcd908f8c66294f2d427825e46bf820f089277f436ed5165f739efbbd",
  Transfer: function(),

  allEvents: function(),
  coinBalance: function(),
  transfer: function()
}

Now that you’ve deployed the contract, if you want to save on electricity and keep your CPU cool, you can stop mining. Remember, though, that to interact with the contract, mining must be on; otherwise, transactions will never complete.

As an exercise, try to move some SimpleCoins from the Main Account to Account 2 while no mining is taking place, then check the coin balance of your two accounts. You’ll prove that the transaction will get completed only when you switch mining back on.

8.5. Making development more efficient by deploying on mock networks

Although running a contract on a private network makes development relatively faster, especially if you configure the mining difficulty level appropriately, you still need to manage the private network correctly. For example, accounts you’re using for development or testing must have some Ether, even if only test Ether, to make sure you can execute transactions to completion. Also, at least one node of the test network must perform some mining continuously.

One way to improve the efficiency of your development cycle is to deploy your contract on a mock network. A mock network, such as Ganache, runs in-memory, generally on the developer computer, and emulates or bypasses, where applicable, all infrastructural aspects of an Ethereum network, such as account management, transaction costs, mining, and connectivity. By deploying your contract on Ganache, you can focus only on the development and testing of the functionality of your contract, and by doing so you can speed up your development cycle considerably. But once the contract is working as expected from a functional point of view, you still need to retest it within a private network and ultimately, on the public test network, to make sure the contract is also sound from an infrastructural point of view.

8.5.1. Installing and starting up Ganache

It turns out that installing and setting up Ganache is easier than setting up a private network: Ganache is written in JavaScript, it uses Ethereumjs to emulate client behavior, and it’s distributed as a Node.js package, which makes its installation easy.

Assuming you have an instance of Node.js (you must have at least version 6.9.1 at the time of writing), you can install Ganache with the Node.js console, as follows:

C:EthereumSimpleCoinWithNode>npm install -g [email protected]

That’s it! Now you should stop geth if it’s running, and you can start Ganache from a new OS console:

c:>ganache-cli

On start-up, Ganache will list the accounts it’s going to support, as shown in the screenshot in figure 8.7.

Figure 8.7. Ganache accounts and related private keys, shown at startup

8.5.2. Deploying SimpleCoin on Ganache

Because Ganache is emulating the Ethereum network, you can’t use the geth console to deploy SimpleCoin’s contract on it. Your new console to the (mocked) Ethereum network is now the Node.js console.

Now you can deploy SimpleCoin exactly as you did when deploying on a public or private test network, as shown in the following listing, which has been adapted slightly for Ganache from listing 8.3.

Listing 8.5. Deploying SimpleCoinOnGanache.js
const fs = require('fs');
const solc = require('solc');

const Web3 = require('web3');
const web3 = new Web3(
new Web3.providers.HttpProvider("http://localhost:8545"));

const account2 = web3.eth.accounts[1];
const sender = account2;

const initialSupply = 10000;

const source = fs.readFileSync(
'c:/Ethereum/SimpleCoin/SimpleCoin.sol', 'utf8');
const compiledContract = solc.compile(source, 1);
const abi = compiledContract.contracts[':SimpleCoin'].interface;
const bytecode = '0x' + compiledContract.contracts[':SimpleCoin'].bytecode;
const gasEstimate = web3.eth.estimateGas({ data: bytecode }) + 100000;

const SimpleCoinContractFactory = web3.eth.contract(JSON.parse(abi));

const simpleCoinInstance = SimpleCoinContractFactory.new(initialSupply, {
    from: sender,
    data: bytecode,
    gas: gasEstimate
   }, function (e, contract){
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' 
         + contract.address 
         + ' transactionHash: ' + contract.transactionHash);
    }
 });

If you have a running geth instance, stop it. Make sure Ganache is running in a separate OS shell. Otherwise, start it again with

C:>ganache-cli

Then run the deployment script in this way:

C:EthereumSimpleCoinWithNode>node deployingSimpleCoinOnGanache.js

In the meantime, observe the OS shell running Ganache. You should see the following:

Listening on localhost:8545
eth_accounts
eth_estimateGas
eth_sendTransaction

  Transaction: 0x25a3ee4ef5f71e72ab1800a78782d42676aec61503eaab1ef8beaf
2e54993038
  Contract created: 0xdc6d598f56cf80201d95b4e9494e83bab8aa479e
  Gas usage: 509317
  Block Number: 1
  Block Time: Wed Feb 21 2018 01:49:20 GMT+0000 (GMT Standard Time)


eth_newBlockFilter
eth_getFilterChanges
eth_getTransactionReceipt
eth_getCode
eth_uninstallFilter 

Then you should see this in the Node.js console almost immediately:

Contract mined! address: 0xedaa9632746aa82b0f1f73185c38a437643116af
 transactionHash: 0xfa4d8a6e526d53ace153b2619c47f9e29359125dbf28e4d97d8f5af0
 cdd051d7

Can you spot the differences between deploying a contract on a public or private network and deploying it on Ganache? Yes, you’re right:

  • Deployment on Ganache was instantaneous, whereas deployment on a network was performed with some latency.
  • You didn’t need to unlock the account deploying the contract, contrary to what you did when deploying on a public or private network.

Here’s an exercise for you before we continue. Try transferring coins between two accounts on Ganache, either interactively or through a script.

8.6. Smoother interaction with SimpleCoin through a web UI

So far, you’ve been interacting with deployed Ethereum smart contracts through various development tools, as shown in figure 8.8: manually through the Ethereum wallet and Remix (with the injected Web3 option) and through explicit Web3.js instructions from a geth console or a Node.js console. An end user can also interact with an Ethereum contract through Web3.js indirectly from a web UI. In this section, you’ll build a web UI for SimpleCoin and, as a result, complete a minimal end-to-end decentralized application for the first time.

Figure 8.8. It’s possible to interact with Ethereum smart contracts through development tools such as the Ethereum wallet and Remix. Alternatively, you can perform contract operations through explicit Web3.js instructions issued from the geth console or the Node.js console. You can execute the same Web3.js instructions implicitly from an HTML web UI.

8.6.1. Building a minimalistic web UI for SimpleCoin

To keep things simple, you’ll initially connect the web UI to an instance of SimpleCoin deployed on Ganache. If you’ve shut down Ganache, please start it up again and redeploy SimpleCoin on it, following the steps described in section 8.4.3.

Once SimpleCoin gets deployed, you should see, as before, a confirmation message similar to this:

Contract mined! address: 0xedaa9632746aa82b0f1f73185c38a437643116af
 transactionHash: 0xfa4d8a6e526d53ace153b2619c47f9e29359125dbf28e4d97d8f5af0
 cdd051d7
Note

Take a note of the contract address. You’ll need to place it in the JavaScript code you’ll be writing shortly.

You can build a minimalistic web UI with a small piece of HTML referencing a Java-Script script that handles coin transfers between accounts. In short, you need to implement two files:

  • simplecoin.js
  • simplecoin.html
Simplecoin.js

The JavaScript code required to handle the coin transfers between accounts, shown in listing 8.6, is similar to what you’ve already executed several times in this chapter—for example, what you saw earlier in listing 8.2. Make sure you replace the address of the contract in listing 8.6 with the one from the contract you deployed earlier on Ganache.

Listing 8.6. simplecoin.js
var web3 = new Web3(
new Web3.providers.HttpProvider("http://localhost:8545"));

var abi = "[{"constant":false,"inputs":[{"name":"_to","type":
"address"},{"name":"_amount","type":"uint256"}],"name":
"transfer","outputs":[],"payable":false,"type":"function"},{
"constant":true,"inputs":[{"name":"","type":"address"}],"name
":"coinBalance","outputs":[{"name":"","type":"uint256"}],
"payable":false,"type":"function"},{"inputs":[{"name":
"_initialSupply","type":"uint256"}],"payable":false,"type":
"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name
":"from","type":"address"},{"indexed":true,"name":"to","type
":"address"},{"indexed":false,"name":"value","type":"uint256
"}],"name":"Transfer","type":"event"}]";
var SimpleCoinContractFactory = web3.eth.contract(JSON.parse(abi));
var simpleCoinContractInstance = SimpleCoinContractFactory.at(
'0xedaa9632746aa82b0f1f73185c38a437643116af');                   1
var accounts = web3.eth.accounts;

function refreshAccountsTable() {                                2
     var innerHtml = 
        "<tr><td>Account</td><td>Balance</td>";                  3

     for (var i = 0; i < accounts.length; i++) {                 4
            var account = accounts[i];
            var balance = 
                simpleCoinContractInstance
                 .coinBalance(account);                          5
            innerHtml = innerHtml + 
              "<tr><td>" + 
              account + "</td><td>" 
              + balance + "</td></tr>";
     }
     
     $("#accountsBalanceTable").html(innerHtml);
}

function transferCoins() {                                       6
     var sender = $("#from").val();
     var recipient = $("#to").val();
     var tokensToTransfer = $("#amount").val();
     simpleCoinContractInstance.transfer(                        7
        recipient, 
        tokensToTransfer, 
        {from:sender,gas:200000},
        function(error, result){
           if(!error)
              refreshAccountsTable();                            8
           else
              console.error(error);
        }
     );
}

$( document ).ready(function() {                                 9
     refreshAccountsTable();
});

  • 1 Replace this with the address of the SimpleCoin contract you just deployed on Ganache.
  • 2 Reports an updated account balance
  • 3 Builds the HTML account balance table dynamically
  • 4 All accounts are iterated to build the account balance HTML.
  • 5 Calls the coin balance getter
  • 6 Gets the input from the UI and feeds it to the coin transfer contract function
  • 7 Invokes the coin transfer contract function
  • 8 The callback associated with a successful transfer refreshes the account balance table.
  • 9 Renders the account balance table on opening the page

Create a folder on your machine, for example named C:EthereumSimpleCoinWebUI. Within this folder, create a file called simplecoin.js and copy into it the code from listing 8.6.

Simplecoin.html

The HTML page required to collect the input and show the outcome of coin transfer operations is basic. It contains a few text boxes to gather the input and a button to trigger the transfer. Apart from the basic HTML, you need to reference the Web3.js and jQuery JavaScript libraries and the simplecoin.js script you just created. You can import Web3.js and jQuery locally with Bower (https://bower.io/), a package manager for building websites. Install Bower with npm as follows:

C:EthereumSimpleCoinWebUI>npm install -g bower

Now import the Web3.js and JQuery libraries in the current directory:

C:EthereumSimpleVotingWebUI>bower install web3#0.20.6
C:EthereumSimpleVotingWebUI>bower install jquery

At this point, Bower will have downloaded Web3 and jQuery into respective directories within the bower_components folder:

bower_components
   |--web3
   |--jquery

You can now reference the JavaScript libraries as shown at the top of the following listing, which contains the entire HTML code you need.

Listing 8.7. simplecoin.html
<html>
<head>   
   <script src="bower_components/web3/dist/web3.min.js"></script>

   <script src="bower_components/jquery/dist/jquery.min.js"></script>
   <script src="./simplecoin.js"></script>
</head>
<body>
   <table>
      <tr><b>SimpleCoin</b></tr>
       <tr><table border="0" cellpadding="0" width="200"
     id='accountsBalanceTable'> </table></tr>
       <tr/>
       <tr/>
       <tr>Transfer coins</tr>
       <tr>
            <table border="0" cellpadding="0" width="200" id='transferCoins'> 
               <tr>
                 <td>From:</td><td><input type="text" id="from" width="400"
     /></td>
                 <td>To:</td><td><input type="text" id="to" width="400"
     /></td>
                 <td>Amount:</td><td><input type="text" id="amount" /></td>
                 <td><button onclick="transferCoins()">Transfer</button></td>
               </tr>
            </table>
       </tr>
   </table>
</body>
</html>

Within the SimpleCoinWebUI folder, create a file called simplecoin.html. Copy into it the code from listing 8.7.

8.6.2. Running the SimpleCoin web UI

Open simplecoin.html with your browser. You’ll see the screen shown in figure 8.9.

Figure 8.9. SimpleCoin web UI

The screen shows the balance of all the accounts Ganache supports. As specified on the deployment script shown in listing 8.5, accounts[1], which is the contract owner, has 10,000 SimpleCoin tokens, and the other accounts have a nil balance.

Performing a transfer through the UI is simple. Specify the source and destination address, respectively, in the From and To text boxes and the number of tokens you want to transfer in the Amount text box. Then click the Transfer button. As an exercise, perform this transfer:

  • From: 0xbb6ae4d3af3112374f570509347dd470866c1495
  • To: 0x495961050c21bb2511f0550315aa9b070e90fa4e
  • Amount: 150

After you click Transfer, the transfer() contract function is invoked and, if the operation completes successfully, the associated callback (mapped to the local refresh-AccountsTable() JavaScript function) updates the account balances table, as you can see in figure 8.10.

Figure 8.10. The balance of the source and destination accounts have changed following a successful coin transfer.

Summary

  • Tools such as the Ethereum wallet and Remix allow you to deploy contracts and perform operations on them, but they hide the communication between the client and the Ethereum network.
  • You can deploy contracts with explicit Web3.js instructions from geth’s console.
  • You also can execute contract operations through Web3.js instructions from geth’s console.
  • Because of the limitations of geth’s console, deploying contracts and performing operations on them from it is a manually intensive and time-consuming process that isn’t easy to automate.
  • It’s easier to compile and deploy contracts from Node.js, which also allows you to completely automate build and deployment Web3.js scripts.
  • You can improve the development cycle by deploying contracts on a private Ethereum network completely under the control of the development team.
  • You can further improve the development cycle by deploying contracts on a mock network, such as Ganache, which emulates accounts and bypasses high-latency operations such as mining.
  • It’s possible to execute contract operations from an HTML UI, through the same Web3.js instructions performed using a geth or Node.js console.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset