Building a Multi-Node Ethereum Private Chain with Geth: Mining and Smart Contract Deployment

·

Geth, officially known as Go Ethereum, is a command-line interface client written in the Go programming language. It serves as a gateway to the Ethereum network, allowing users to run nodes, mine ether, execute smart contracts, and manage accounts.

This guide walks through the process of setting up a private, multi-node Ethereum blockchain using Geth. You will initialize the genesis block, configure multiple nodes, begin mining, and ultimately deploy and interact with a smart contract—all on your own isolated network.

Initializing the Genesis Block

Every Ethereum network begins with a genesis block. This foundational block contains the initial configuration parameters for your entire blockchain.

Create a file named genesis.json with the following content:

{
 "config": {
 "chainId": 666,
 "homesteadBlock": 0,
 "eip155Block": 0,
 "eip158Block": 0
 },
 "coinbase" : "0x0000000000000000000000000000000000000000",
 "difficulty" : "0x1",
 "extraData" : "",
 "gasLimit" : "0x2fefd8",
 "nonce" : "0x0000000000000042",
 "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
 "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
 "timestamp" : "0x00",
 "alloc" : {}
}

Key Configuration Parameters Explained

To initialize the blockchain with this genesis file, run the following command in your terminal, replacing the path with your desired data directory:

geth --datadir /path/to/your/ethdata init genesis.json

The --datadir flag specifies where all your blockchain data, including keystores and the chain itself, will be stored.

Launching the First Node

With the genesis block initialized, you can now start your first node and enter the interactive JavaScript console.

geth --datadir /path/to/your/ethdata \
--networkid 666 \
--identity "bootnode" \
--port 30303 \
--rpc \
--rpcport 8545 \
--rpccorsdomain "*" \
--nodiscover \
--verbosity 4 \
console 2>> eth.log

Command Flag Overview

Once running, you can verify the genesis block was created correctly by executing eth.getBlock(0) within the Geth console.

Account Creation and Mining

A blockchain is useless without accounts and value transfer. Inside the Geth console, you can create new accounts.

> personal.newAccount("your_secure_password_here")
"0xyour_new_account_address_will_appear_here"
> eth.accounts // This lists all accounts on the node
["0xyour_new_account_address"]

To start generating ether, you simply need to begin mining. The mined ether will be deposited into the first account on your node (typically the coinbase account, which is the first account by default).

> miner.start() // Begin mining
null
> // Wait for a few blocks to be mined...
> miner.stop() // Stop mining
null
> eth.getBalance(eth.accounts[0]) // Check the balance of your account
50000000000000000000 // This represents 50 ether in wei (the smallest unit of ETH)

👉 Explore more strategies for managing your blockchain

Building a Multi-Node Network

A single-node blockchain is functional, but a multi-node setup demonstrates the true peer-to-peer (P2P) nature of blockchain technology. To add a second node, repeat the initialization process on the same machine (using a different data directory and port) or on a separate machine.

Initialize the second node:

geth --datadir /path/to/node2/data init genesis.json

Start the second node:

geth --datadir /path/to/node2/data \
--networkid 666 \
--identity "node2" \
--port 30304 \  // Must be different from the first node's port
--rpc \
--rpcport 8546 \ // Must be different from the first node's RPC port
--rpccorsdomain "*" \
--nodiscover \
console 2>> node2.log

Connecting the Nodes

Nodes find each other using enode URLs. In the console of your first node, retrieve its enode:

> admin.nodeInfo.enode
"enode://...@[::]:30303?discport=0"

Then, in the console of the second node, add the first node as a peer (replace the example with your actual enode):

> admin.addPeer("enode://[email protected]:30303")
true
> admin.peers // Verify the connection
// A list of connected peers should appear

Deploying a Smart Contract

Smart contracts are self-executing contracts with the terms of the agreement directly written into code. Let's deploy a simple one.

First, write your contract in Solidity. Here is a basic example (Test.sol):

pragma solidity >=0.5.1;
contract Test {
 uint256 public A = 0;
 function set(uint256 a) public { A = a; }
 function get() view public returns(uint256) { return A; }
 function sum(uint256 b) view public returns(uint256) { return A + b; }
}

Compile this contract using the Remix IDE. Remix will provide you with two crucial pieces of information:

  1. ABI (Application Binary Interface): A JSON array that describes the contract's interface—its functions and variables.
  2. Bytecode: The compiled code that will be deployed to the blockchain.

Deployment via Geth Console

Within your Geth console, follow these steps:

  1. Unlock your account to allow it to send the deployment transaction.

    > personal.unlockAccount(eth.accounts[0], "your_password", 300)
    true
  2. Define the ABI and Bytecode as variables.

    > var abi = [...]; // Paste the ABI array from Remix here
    > var bytecode = "0x..."; // Paste the bytecode string from Remix here, prefixed with 0x
  3. Create a contract object and deploy it. This sends a transaction to the network.

    > var testContract = eth.contract(abi);
    > var deployedContract = testContract.new({from: eth.accounts[0], data: bytecode, gas: 1000000});
  4. Mine a block to include the deployment transaction. If mining is already running, this will happen automatically.

    > miner.start(1); admin.sleepBlocks(1); miner.stop();
  5. Interact with your contract once it's mined. The transaction receipt will contain its address.

    > var myContract = testContract.at(deployedContract.address);
    > myContract.get.call(); // Should return 0
    > myContract.set.sendTransaction(42, {from: eth.accounts[0], gas: 100000});
    > // Mine another block to include the transaction...
    > myContract.get.call(); // Should now return 42
    > myContract.sum.call(10); // Should return 52

Frequently Asked Questions

What is the main purpose of running a private Ethereum chain?
A private Ethereum chain is ideal for development, testing, and learning. It provides a risk-free environment where you can experiment with smart contracts, deploy dApps, and understand blockchain mechanics without spending real ether or interacting with the public mainnet.

Why is my admin.peers list empty after trying to connect nodes?
The most common reasons are a firewall blocking the port (e.g., 30303), an incorrect IP address in the enode URL (use the machine's internal LAN IP, not 127.0.0.1, for cross-machine connections), or a mismatch in the networkid between the two nodes. Ensure both commands and the genesis file use the same ID.

I deployed my contract but getCode returns "0x". What happened?
This means the contract deployment transaction has not been mined into a block yet. You need to start the miner (miner.start()) to process the transaction. After a block is mined, the code will be available at the contract address.

How can I ensure my private chain is secure?
For a true private network, use the --nodiscover flag and explicitly connect nodes via admin.addPeer() or a static-nodes.json file. Control access to the RPC interface carefully using --rpcaddr and avoid using --rpccorsdomain "*" in production.

What is gas, and why do I need to specify it for transactions?
Gas is the unit that measures the computational effort required to execute operations, like calling a contract function. Every transaction consumes gas, which is paid for in ether. Specifying a gas limit prevents transactions from getting stuck and consuming infinite resources if there is an error in the contract code.

Can I use Metamask with my private Geth chain?
Yes. You can add your private network to Metamask. Use your node's IP and RPC port (e.g., http://192.168.1.100:8545) as the custom RPC endpoint. You will also need to import the private key of an account from your Geth node into Metamask.