Blockchain Technology Tutorial Part #2: Blockchain Development

{getToc} $title={Table of Contents}

Blockchain Technology Tutorial Part #2: Blockchain Development


Introduction:


Hello everyone! 😊, welcome back to our blockchain technology tutorial series. In the first part #1, we covered the fundamentals and benefits of blockchain technology. We also discussed some of the real-world applications and examples of blockchain in various sectors such as DeFi, smart contracts, digital identity and supply chain management. And gave you an overview of some of the existing blockchain platforms and projects that you can check out. 

In this part #2, we will focus on the development side of blockchain technology. And we'll show you some of the tools and frameworks that you can use to build and interact with blockchain applications. And we also teach you how to use programming languages such as Solidity, JavaScript, Python etc. to code and deploy a simple blockchain application. 

By the end of this part, you will have a clearer idea of how to develop blockchain applications and what are the challenges and opportunities. So, let’s begin!


Blockchain Development:


Now, we will introduce some of the tools and frameworks for developing blockchain applications. And we'll also explain how to create and interact with a blockchain using programming languages such as Solidity, JavaScript, Python etc. And we will demonstrate how to test and deploy a simple blockchain application.

Tools and Frameworks:


Building blockchain applications requires a combination of programming languages, tools, frameworks and libraries. Depending on the type and purpose of your blockchain application, you may need different tools and frameworks to suit your needs. 

Here are some of the best tools, frameworks and libraries for building blockchain applications:


  • Ethereum: the most popular and widely used blockchain development platform in the world. It introduced a revolutionary feature known as the smart contract which is a program that runs on the blockchain and can execute transactions and logic according to predefined rules. Ethereum also provides the Ethereum Virtual Machine (EVM) which is a runtime environment that can execute smart contracts written in various languages such as Solidity, Vyper or Serpent.

  • Hyperledger Fabric: a blockchain framework designed for creating highly scalable and secure blockchain applications for enterprises. It supports private and permissioned blockchains where only authorized participants can join and access the network. And it also supports modular architecture where different components such as consensus algorithms, membership services or smart contract engines can be plugged and unplugged as needed.

  • IPFS: stands for Inter Planetary File System which is a distributed file system that can store and share data across a peer-to-peer network. It can be used to store large files or data that are not suitable for storing on the blockchain such as images, videos or documents. IPFS can also provide content addressing which means that each file or data has a unique identifier that can be used to locate it on the network.

  • Truffle: a development environment and testing framework for Ethereum smart contracts. It provides features such as smart contract compilation, deployment, testing, debugging and scripting. Truffle also integrates with other tools such as Ganache, Remix or OpenZeppelin to provide a comprehensive development experience.

  • Web3.js: is a JavaScript library that allows developers to interact with Ethereum nodes using HTTP or WebSocket protocols. It provides an API to access various functions of the Ethereum blockchain such as accounts, transactions, contracts, events or filters. Web3.js also supports various Ethereum-compatible networks such as Mainnet, Ropsten, Rinkeby or Kovan.

  • Ganache: is a personal blockchain that can be used for testing and development purposes. It allows developers to create and run a local Ethereum network with customizable settings such as block time, gas limit or accounts. Ganache also provides a graphical user interface (GUI) that displays the network status, transactions history, balances, logs and more.

  • Remix: is an online IDE (Integrated Development Environment) that allows developers to write, compile, debug and deploy smart contracts using a web browser. It supports various languages such as Solidity or Vyper. Remix also provides features such as syntax highlighting, auto-completion, code analysis and testing tools.

  • OpenZeppelin: a framework that provides reusable smart contract components and best practices for developers. It offers various modules and libraries for implementing common functionalities and security features in smart contracts such as ownership, access control, token standards and math operations etc.

How to Create a Smart Contract Using Solidity:


Solidity is the main programming language for writing smart contracts for the Ethereum blockchain. It is a contract-oriented language which means that smart contracts are responsible for storing all of the programming logic that transacts with the blockchain. Solidity is a high-level programming language that looks a lot like JavaScript, Python and C++.

To create a smart contract using Solidity, we need to follow these steps:


  • Define version of Solidity to use with the pragma directive.
  • Define name and structure of the contract with the contract keyword.
  • Define state variables and functions of the contract.
  • Compile contract into bytecode and ABI using a compiler such as solc.
  • Deploy contract to a blockchain network using a tool such as web3.js or truffle.

Let’s see an example of a simple smart contract that can store and retrieve a message:

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.4.17 <0.9.0;
contract Message {
    // A state variable to store the message
    string public message;
    
    // A constructor function to initialize the message    
    constructor(string memory initialMessage) {
        message = initialMessage;
    }
    // A function to update the message
    function setMessage(string memory newMessage) public {
        message = newMessage;

    }
    // A function to get the message
    function getMessage() public view returns (string memory) {
        return message;
    }
}

In this example, we have defined a contract named Message that has one state variable called message of type string. And we have also defined a constructor function that takes an initial message as an argument and assigns it to the state variable. And defined two functions: one to set a new message and another one to get the current message. We have used some modifiers such as public, memory and view to specify the visibility, location and behavior of the variables and functions.

Compile 


To compile this contract, we can use a tool such as Remix IDE which is an online IDE for Solidity development. And we can create a new file named Message.sol and paste our code there. Then click on the Solidity compiler tab and select our file and version. And click on compile to generate the bytecode and ABI of our contract.

Deploy 


To deploy this contract, we can use a tool such as Truffle, which is a development environment and testing framework for Ethereum smart contracts. And we can create a new project folder and run truffle init to initialize it. Then copy our contract file to the contracts folder and create a migration file in the migrations folder. A migration file is a script that tells Truffle how to deploy our contract to a network. 

Here is an example of a migration file for our contract:

//Migrations/2_deploy_message.js
const Message = artifacts.require("Message");

module.exports = function (deployer) {
    // Deploy the Message contract with an initial message
    deployer.deploy(Message, "Hello, world!");
};

Here, we have imported our contract artifact from the build folder and used the deployer object to deploy it with an argument for the constructor function. To run this migration, we need to have a network configuration in our truffle-config.js file. 

For example, we can use Ganache as our local development network by installing it with npm install -g ganache-cli and running it with ganache-cli. And then add this configuration to our file:

// truffle-config.js
module.exports = {

  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*";
    },
  },
};

Next, let's run truffle migrate to deploy our contract to our local network. We should see something like this:

$ truffle migrate

Compiling your contracts...
===========================
> Compiling ./contracts/Message.sol
> Artifacts written to /home/user/message/build/contracts
> Compiled successfully using:
    - solc: 0.8.10+commit.fc410830.Emscripten.clang

Starting migrations...
======================
> Network name:


How to Test and Deploy a Blockchain Application:


After creating a smart contract using Solidity, we need to test and deploy it to a blockchain network. Testing is important to ensure that our smart contract works as expected and does not contain any bugs or vulnerabilities. Deploying is the process of sending our smart contract to a specific address on the blockchain network where it can be accessed and executed by other users.

There are different tools and methods for testing and deploying a blockchain application depending on the type and purpose of the application. For example, we can use Truffle to test and deploy our smart contract to a local development network such as Ganache or to a public test network such as Ropsten or Rinkeby. We can also use Remix IDE to test and deploy our smart contract using a web browser and an extension such as MetaMask.

Let’s see an example of how to test and deploy our Message smart contract using Truffle and Ganache:

1. Test our smart contract:

We need to write some test cases using a testing framework such as Mocha or Jest. We can create a new file named test/message.js in our project folder and write some test cases using JavaScript and an assertion library such as Chai. 

Here is an example of a test file for our contract:

// test/message.js
const Message = artifacts.require("Message");

// Use Chai assertion library
const { expect } = require("chai");

// Write a contract test suite
contract("Message", (accounts) => {
    // Define a variable to hold the contract instance
    let message;

    // Before each test, create a new contract instance    
    beforeEach(async () => {
        message = await Message.new("Hello, world!");
});
// Test the constructor function
it("should initialize the message with the initial value", async () => {
    // Get the message value from the contract
    const messageValue = await message.message();

    // Assert that the message value is equal to the initial value
    expect(messageValue).to.equal("Hello, world!");
});
// Test the setMessage function
it("should update the message with the new value", async () => {
    
    // Call the setMessage function with a new value
    await message.setMessage("Hello, blockchain!");

    // Get the updated message value from the contract
    const messageValue = await message.message();

    // Assert that the message value is equal to the new value
    expect(messageValue).to.equal("Hello, blockchain!");
});

2. Run our test:

We need to have Ganache running as our local development network. And start Ganache by running ganache-cli in another terminal window. Then run truffle test in our project folder to execute our tests. We should see something like this:

$ truffle test
Compiling your contracts...
===========================
> Compiling ./contracts/Message.sol
> Artifacts written to /tmp/test--21195-0x3qy9X7Z1m4n
> Compiled successfully using:
    - solc: 0.8.10+commit.fc410830.Emscripten.clang
    Contract: Message
    ✓ should initialize the message with the initial value (66ms)
    ✓ should update the message with the new value (80ms)
  2 passing (2s)

3. Deploy our smart contract:

We need to have a network configuration in our truffle-config.js file. For example, we can use Ganache as our local development network by adding this configuration to our file:

// truffle-config.js
module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*",
    },
  },
};

4. Run our migration:

We need to have a migration file in our migrations folder. A migration file is a script that tells Truffle how to deploy our smart contract to a network. And I have already provided an example of a migration file for our contract in the above section (How to Create a Smart Contract Using Solidity).

5. Deployment:

I have already provided an example of running truffle migration to deploy our contract to our local network (How to Create a Smart Contract Using Solidity).


How to Interact with a Deployed Smart Contract:


After deploying our smart contract to a blockchain network, we can interact with it by using various methods and tools. Interacting with a smart contract means calling its functions and reading or writing its state variables. Depending on the type and purpose of our smart contract. We may want to interact with it from a web browser, a mobile app, a command line interface or another smart contract.

One of the most common ways to interact with a deployed smart contract is using a web browser and an extension such as MetaMask. It is a browser extension that allows users to connect to Ethereum-compatible networks and manage their accounts and transactions. MetaMask also provides a web3.js API that allows web developers to interact with smart contracts using JavaScript.

To interact with our Message smart contract using MetaMask and web3.js we need to follow these steps:


  • Install MetaMask on our browser and create or import an account.
  • Connect MetaMask to the network where we deployed our smart contract (eg, Ganache or Ropsten).
  • Get the address and ABI of our deployed smart contract.
  • Create a web page that can load web3.js and communicate with MetaMask.
  • Create an instance of our smart contract using web3.js and call its functions.

Let’s see an example of a simple web page that can interact with our Message smart contract:


<!-- index.html -->
<html>
  <head>
    <title>Message Dapp</title>
    <!-- Load web3.js from CDN -->
    <script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js"></script>
  </head>
  <body>
    <h1>Message Dapp</h1>
    <p id="status">Loading...</p>
    <p id="message"></p>
    <input id="input" type="text" placeholder="Enter new message" />
    <button id="button">Update message</button>
    <script>
        // Define the address and ABI of the deployed smart contract
        const contractAddress = "0x123456789abcdef0123456789abcdef012345678";
        const contractABI = [
            {
                constant: true,
                inputs: [],
                name: "message",
                outputs: [
                    {
                        name: "",
                        type: "string"
                    },
                ],
                payable: false,
                stateMutability: "view",     
                type: "function"
            },
            {
                constant: false,
                inputs: [
                    {
                        name: "newMessage",
                        type: "string",
                    },
                ],
                name: "setMessage",    
                outputs: [],
                payable: false,
                stateMutability: "nonpayable",           type: "function"
            },
            {
                inputs: [
                    {
                        name: "initialMessage",                         type: "string"
                    },
                ],
                payable: false,
                stateMutability: "nonpayable",
                type: "constructor"
            },
          ];

        // Define some variables to hold the elements
        let status = document.getElementById("status");
        let message = document.getElementById("message");
        let input = document.getElementById("input");
        let button = document.getElementById("button");

        // Define a variable to hold the web3 instance
        let web3;
        
        // Define a variable to hold the contract instance        let contract;

        // Define a variable to hold the user account    
        let account;
        // Define a function to initialize the web3 instance
        async function initWeb3() {
        
            // Check if MetaMask is installed and enabled
            if (window.ethereum) {
                // Create a new web3 instance using MetaMask provider
                web3 = new Web3(window.ethereum);
                try {
                    // Request account access from MetaMask
                    await window.ethereum.enable();

                    // Get the user account from MetaMask
                    account = (await web3.eth.getAccounts())[0];

                    // Update the status message
                    status.innerHTML = "Connected";
                } catch (error) {
                    // Handle user rejection or any other error        
console.error(error);
                    status.innerHTML = "Rejected or Error";
          }
        } else {
            // Handle the case where MetaMask is not installed
            console.error("MetaMask not found");
            status.innerHTML = "MetaMask not found";
        }
      }

        // Define a function to initialize the contract
        async function initContract() {
            // Create a new contract instance using the address and ABI
            contract = new web3.eth.Contract(contractABI, contractAddress);

            // Update the status message
            status.innerHTML = "Contract loaded";
        }

        // Define a function to get the message from the contract
        async function getMessage() {
            // Call the message function of the contract
            const messageValue = await contract.methods.message().call();

            // Update the message element with the value
            message.innerHTML = messageValue;
        }

        // Define a function to update the message in the contract
        async function updateMessage() {
        
        // Get the input value from the element
        const newMessage = input.value;

        // Check if the input is not empty
        if (newMessage) {
            // Call the setMessage function of the contract with the new value    
            await contract.methods.setMessage(newMessage).send({ from: account});
            // Update the status message
            status.innerHTML = "Message updated";

            // Clear the input value
            input.value = "";
            // Get the updated message from the contract    
            getMessage();
        } else {
            // Update the status message
            status.innerHTML = "Please enter a message";
        }
      }
    
    // Define a function to add an event listener to the button
    function addListener() {
        // Add a click event listener to the button element
        button.addEventListener("click", updateMessage);
    }

    // Define an async function to initialize the app
    async function initApp() {
        // Initialize web3
        await initWeb3();
    
        // Initialize contract
        await initContract();

        // Get message from contract
        await getMessage();

        // Add listener to button
        addListener();
    }
    // Call the initApp function when the window loads    
    window.addEventListener("load", initApp);
        </script>
    </body>
</html>

In this example we have created a simple web page that can display and update the message stored in our smart contract. We have used web3.js to create a web3 instance using MetaMask provider, a contract instance using our address and ABI and call our contract functions using web3 contract methods. And then we have used some HTML elements and JavaScript functions to create a basic user interface and logic.

To run this web page, we need to serve it using a local server such as http-server. We can install it using npm: npm install -g http-server

Then, we can run it in our project folder: http-server.

We should see something like this:

Starting up http-server, serving .

Available on:
  http://127.0.0.1:8080
  http://192.168.1.100:8080
Hit CTRL-C to stop the server

Next, open our browser and go to http://127.0.0.1:8080/index.html.

You will see that our web page is connected to MetaMask and displays our initial message “Hello, world!”. We can also enter a new message in the input field and click on the button to update it.

This is just one example of how to interact with a deployed smart contract using MetaMask and web3.js. There are many other ways and tools to interact with smart contracts such as using mobile apps, command line interfaces or other smart contracts. The main idea is to use a provider that can connect to a blockchain network and a library that can encode and decode smart contract calls.


How to Create and Interact with a Blockchain using Python:


We will use the Flask framework to create a web application that will expose our blockchain to the network. And we will use Postman to make requests to our blockchain and test its functionality.

Create a Blockchain with Python:


To create a blockchain with Python, we will use the following libraries:

  • datetime: attach a timestamp to each block that is created or mined.
  • json: encode and decode the block data before hashing it.
  • hashlib: hash the block data using the SHA-256 algorithm.
  • flask: create a web application that will expose our blockchain and allow us to interact with it.

Creating the Block Class:


The first step is to create a Block class that will represent each block in our blockchain. And then we will define the following attributes and methods for our Block class:

  • init: initialize the block object with the given parameters: index, timestamp, data, previous_hash, nonce and hash. The index is the position of the block in the chain. Timestamp is the time when the block was created or mined. Data is the list of transactions in the block. The previous_hash is the hash of the previous block in the chain. the nonce is a random number used for proof-of-work. And the hash is the hash of the block data.
  • calculate_hash: this method will calculate and return the hash of the block data using the SHA-256 algorithm. And use json.dumps to encode the block data into a string before hashing it.
  • str: this method will return a string representation of the block object for printing purposes.

Our Block class should look like this:


import datetime
import json
import hashlib
class Block:
    def __init__(self, index, timestamp, data, previous_hash):
        self.index = index
        self.timestamp = timestamp
        self.data = data
        self.previous_hash = previous_hash
        self.nonce = 0
        self.hash = self.calculate_hash()
    def calculate_hash(self):
        block_string = json.dumps(self.__dict__, sort_keys=True)
        return hashlib.sha256(block_string.encode()).hexdigest()
    def __str__(self):
        return "Block " +             str(self.index) + "\nTimestamp: " + str(self.timestamp) +             "\nData: " + str(self.data) + "\nPrevious Hash: " +             str(self.previous_hash) + "\nNonce: " + str(self.nonce) +
            "\nHash: " + str(self.hash)


Creating the Blockchain Class:


The next step is to create a Blockchain class that will represent our blockchain. We will define the following attributes and methods for our Blockchain class:

  • init: this method will initialize the blockchain object with an empty list of blocks (chain) and an initial difficulty for proof-of-work (difficulty). And create our genesis block (the first block in the chain) by calling the create_block method with default parameters.

  • create_block: this method will create and return a new block object with the given parameters: proof (the nonce value for proof-of-work) and previous_hash. And use datetime.datetime.now to get the current timestamp and len(self.chain) to get the index of the new block. We will also append the new block to our chain list.

  • get_previous_block: this method will return the last block in our chain list.

  • proof_of_work: implement the proof-of-work algorithm to find a nonce value that satisfies the difficulty condition. The difficulty condition is that the hash of the block data must start with a certain number of zeros which is determined by the difficulty attribute. And use a while loop to increment the nonce value until we find a valid hash. And return the nonce value as the proof.

  • is_valid_proof: check if a given proof (nonce value) is valid for a given block and difficulty. And then create a copy of the block object and assign the proof to its nonce attribute. Then we will calculate the hash of the block data and compare it with the difficulty condition. And return True if the hash is valid and False otherwise.

  • is_chain_valid: check if our chain is valid by iterating over each block and verifying its hash and proof. We will use a for loop to go through each block in our chain list. Starting from the second block (the first block is the genesis block which we assume to be valid). For each block, we will check if its previous_hash matches the hash of the previous block and if its proof is valid according to the proof_of_work method. And return False if any of these checks fail and True otherwise.

Our Blockchain class should look like this:


class Blockchain:
    def __init__(self):
        self.chain = []
        self.difficulty = 4
        self.create_block(proof=1, previous_hash='0')
    
    def create_block(self, proof, previous_hash):
        block = Block(index=len(self.chain),
timestamp=datetime.datetime.now(), data="Some data", previous_hash=previous_hash) block.nonce = proof
        block.hash = block.calculate_hash()
        self.chain.append(block)
        return block
    def get_previous_block(self):
        return self.chain[-1]
    def proof_of_work(self, block):
        block.nonce = 0
        while not self.is_valid_proof(block, self.difficulty):
            block.nonce += 1
       return block.nonce
    def is_valid_proof(self, block, difficulty): return block.hash.startswith('0' * difficulty)
    def is_chain_valid(self):
        for i in range(1, len(self.chain)):
            current_block = self.chain[i]
            previous_block = self.chain[i-1]
            if current_block.previous_hash != previous_block.hash:
                return False
            if not self.is_valid_proof(current_block, self.difficulty):
                return False
        return True


Creating the Web Application:


The final step is to create a web application that will expose our blockchain and allow us to interact with it. And use Flask to create a simple web server that will handle GET and POST requests from Postman request. We will define the following routes for our web application:

  • /mine_block: this route will mine a new block by calling the proof_of_work method on the previous block and passing the proof and previous_hash to the create_block method. And return a JSON response with the details of the new block and a success message.

  • /get_chain: this route will return a JSON response with the length of our chain and the list of blocks in our chain.

  • /is_valid: this route will check if our chain is valid by calling the is_chain_valid method. And return a JSON response with a boolean value and a message.

Our web application should look like this:


from flask import Flask, jsonify
app = Flask(__name__)
blockchain = Blockchain()
@app.route('/mine_block', methods=['GET'])
def mine_block():
    previous_block = blockchain.get_previous_block()
    proof = blockchain.proof_of_work(previous_block)
    previous_hash = previous_block.hash
    new_block = blockchain.create_block(proof, previous_hash)
    response = {
        'message': 'Congratulations, you just mined a block!',
        'index': new_block.index,
        'timestamp': new_block.timestamp,
        'data': new_block.data,
        'previous_hash': new_block.previous_hash,
        'nonce': new_block.nonce,
        'hash': new_block.hash
    }
    return jsonify(response), 200
@app.route('/get_chain', methods=['GET'])
def get_chain():
    response = {
        'length': len(blockchain.chain),         'chain': [str(block) for block in blockchain.chain]
    }
    return jsonify(response), 200
@app.route('/is_valid', methods=['GET'])
def is_valid():
    valid = blockchain.is_chain_valid()
    response = {
        'valid': valid,
        'message': 'The blockchain is valid' if valid else 'The blockchain is not valid'
    }
    return jsonify(response), 200
if __name__ == '__main__':
    app.run(debug=True)


How to Interact with a Blockchain using Python?


We will use Postman to make requests to our web application. And use the following steps:

1. Run the web application: 

Run our web application by executing the python blockchain.py command in our terminal. We should see a message like this:

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 123-456-789


2. Mine a block: 

We will mine a new block by sending a GET request to the /mine_block route using Postman. We should see a JSON response like this:

{
    "message": "Congratulations, you just mined a block!",
    "index": 1, "timestamp": "2022-01-28 12:34:56.789000",
    "data": "Some data",
    "previous_hash": "0",
    "nonce": 123456,
    "hash": "0000abcdef1234567890"
}


3. Get the chain: 

We will get the chain by sending a GET request to the /get_chain route using Postman. We should see a JSON response like this:

{
    "length": 1,
    "chain": [
        "Block 1\nTimestamp: 2022-01-28 12:34:56.789000\nData: Some data\nPrevious Hash: 0\nNonce: 123456\nHash: 0000abcdef1234567890"
    ]
}


4. Check the validity: 

We can check the validity of our chain by sending a GET request to the /is_valid route using Postman. We should see a JSON response like this:

{             
    "valid": true,
    "message": "The blockchain is valid"
}

We can repeat these steps to mine more blocks and interact with our blockchain.


Conclusion:


In this blog post, we learned about the blockchain development with some tools and frameworks. And have learned:

  • How to create a smart contract using Solidity.
  • How to test and deploy a Blockchain Application.
  • How to interact with a deployed smart contract.
  • How to create and interact with a blockchain using Python. 

We hope you enjoyed this tutorial and learned something new. If you have any questions or feedback.
Please feel free to leave a comment below. Thank you for reading! 😊


Previous Post Next Post