Python Flask How to develop an Ethereum smart contract

Source: Internet
Author: User
Tags curl object serialization python script virtual environment virtualenv flask restful



Storing data in a database is an integral part of any software application. Regardless of the control of the database there is a master control of that data. Blockchain technology stores data in chunks within a blockchain network. As a result, as long as a node is synchronized with the network, they get a copy of the data in the chunk. Therefore, there is no specific data master in this technology.



In this tutorial, we'll write a smart contract (which I'll explain further) to keep the user data on the blockchain. We will use Python web3 (Web3 's Python library) to develop and deploy smart contracts. Once we have a smart contract deployed on the blockchain. We will use the Flask API to interact with the smart contract to store some data/information. We store it on a blockchain and it's immutable.





Environmental requirements


Python 3.6


Installation


1. Create a Python virtual environment.



Instead of forcing you to install packages on a system-wide scale, VIRTUALENV will localize your Python package in your project's virtual environment.


$ virtualenv -p /usr/bin/python3.6 venv$ source venv/bin/activate


2. Now we needGanachethat ethereum test chain.



Ganache is a personal blockchain developed by Ethereum that can be used to deploy contracts, develop applications, and run tests.


$ npm install -g ganache-cli


3. Install Python web3



web3.py is a Python library for interacting with Ethereum. Its API is derived from the Web3.js Javascript API and should be familiar to people who have used web3.js.


$ pip3 install web3


4.Flask



Flask is a Python lightweight framework.


$ pip3 install flask


5.Flask Restful



Flask-restful is a Flask extension that adds support for the fast build rest API.


$ pip3 install flask-restful
    1. Flask Marshmallow


Flask Marshmallow is a library of object serialization/deserialization.


$ pip3 install flask-marshmallow
Launch Ethereum test Blockchain server


To deploy the smart contract, we should start the test Ethereum server. We are testing using ganache. Type the following command in the terminal:


$ ganache-cli





Ganache provides us with 10 default test accounts, 100 fake ether per account, for trading. We will use these accounts to deploy and set various values in the contract.






We can see gas prices and limits as well as deploymentganachehost:port. We need this when we deploy the contract.


Create a User.sol file


Now we will write the smart contract with solidity. Solidity is the language for writing smart contracts on Ethereum. Smart contracts include optional validation functions for data, data, and getter methods that we will store on the blockchain, and the setter method for accessing the data.



For example, to register for attendance on a blockchain, you will have a set of user objects. It will have access to the user's Getter,setter method. Because each user can only mark one attendance per day, you need a validation feature to check that the smart contract is very similar to the one we typically develop in any other language.



In the following file, we use the Getter,setter function to build a simple user contract.



1. Declare the solidity compiler version in the. Sol file.


pragma solidity ^ 0.4.21;


Learn about the compiler version used.


$ solidity?—?version


2. import library file. We should use the library for common utility functions. Libraries can be compiled once and reused (click here for some good library resources).


import“stringUtils.sol”;


3. Declaring a contract for the user


contract userRecords {}


4. For the basic demo, we will now store the user's name and gender information. Therefore, these two variables are initialized with the struct and enum data types.

// Enumerated type variables to store user gender
enum genderType {male, female}
// The actual user object we will store in the Ethereum contract
struct user {
    string name; genderType gender;
}
5. Now we will declare a user object of type user (struct). It can also be declared public to access it from outside the contract (for visible scope, click here).

user user_obj;
6. Now add getter and setter methods to the user object. We will keep each user's information on the blockchain. We should always expose this method because we will access them from outside the contract.

// Set user public functions
// This is similar to persistent objects in db.
function setUser (string name, string gender) public {
    genderType gender_type = getGenderFromString (gender);
    user_obj = user ((name: name, gender: gender_type));
}
// Get user public functions
// This is similar to getting an object from db.
function getUser () public returns (string, string) {
    return (user_obj.name, getGenderToString (user_obj.gender));
}
7. Please note that we use two internal functions getGenderFromString () and getGenderToString (). Let's add this internal function. Declare them as internal because we will not use them outside.

// Internal function for converting genderType enumeration from string
function getGenderFromString (string gender) internal returns (genderType) {
    if (StringUtils.equal (gender, "male")) {
        return genderType.male;
    } else {
        return genderType.female;
    }
}
// Internal function that converts the genderType enumeration to a string
(string) {
    if (gender == genderType.male) {
        return "male";
    } else {
        return "female";
    }
}
We are using the stringUtils.equal () library function. Because this version of solidity does not support string comparison using (==).

8. Now our user.sol file contract looks like this:

pragma solidity ^ 0.4.21;
// import library file
import "stringUtils.sol";
contract userRecords {
  // enum type variable to store user gender
  enum genderType {male, female};
  // Actual user object which we will store
  struct user {
    string name;
    genderType gender;
  }
  // user object
  user user_obj;
  // Internal function to conver genderType enum from string
  function getGenderFromString (string gender) internal returns (genderType) {
    if (StringUtils.equal (gender, "male")) {
      return genderType.male;
    } else {
      return genderType.female;
    }
  }
  // Internal function to convert genderType enum to string
  function getGenderToString (genderType gender) internal returns (string) {
    if (gender == genderType.male) {
      return "male";
    } else {
      return "female";
    }
  }
  // set user public function
  // This is similar to persisting object in db.
  function setUser (string name, string gender) public {
    genderType gender_type = getGenderFromString (gender);
    user_obj = user ((name: name, gender: gender_type));
  }
  
  // get user public function
  // This is similar to getting object from db.
  function getUser () public returns (string, string) {
    return (user_obj.name, getGenderToString (user_obj.gender));
  }
}
Compile and deploy solidity files using a python script.
1. In the following python script, we need to instantiate a python-web3 test Ethereum node. We will set the ganche url as the test Ethereum node. We will use the following w3 object to deploy the contract.

from web3 import Web3
# web3.py instance
w3 = Web3 (Web3.HTTPProvider ("http://127.0.0.1:8545"))
2. Now we will compile the solidity code. To compile solidity code, we use py-solc, which is a python extension for the solidity compiler.

from solc import compile_files
# Compile all contract files
contracts = compile_files ([‘user.sol’, ‘stringUtils.sol’])
# Separate master and link files
main_contract = contracts.pop ("user.sol: userRecords")
library_link = contracts.pop ("stringUtils.sol: StringUtils")
3. Whenever you compile a .sol file using an import statement. We also need to link the deployment address of the import file and the main contract. So for deploying all links first compile it (if already deployed and then save the address) see the bin of the main contract.

When you compile the main contract, if you see its bin section, you will find _stringUtils.sol: StringUtils ___________ of the library we are importing (it can also be used for contracts). In this part we should replace it with the library address before deploying the contract.

4. Then we associate the library address with the main contract.

from solc import link_code
def deploy_contract (contract_interface):
    #Instantiation and deployment contracts
    contract = w3.eth.contract (
        abi = contract_interface [‘abi‘],
        bytecode = contract_interface [‘bin’]
    )
    #Get transaction hash from deployed contract
    tx_hash = contract.deploy (
        transaction = {‘from‘: w3.eth.accounts [1]}
    )
    #Get tx receipt to get contract address
    tx_receipt = w3.eth.getTransactionReceipt (tx_hash)
    return tx_receipt [‘contractAddress‘]
library_address = {
    "stringUtils.sol: StringUtils": deploy_contract (library_link)
}
main_contract [‘bin’] = link_code (
    main_contract [‘bin’], library_address
)
See the main contract bin after linking:

You will see that the bin of the import library has been added.

5. Now deploy the master contract using our w3 object. Deploy using the default address of ethereum account {‘from‘: w3.eth.accounts [1]}.

def deploy_contract (contract_interface):
    # Instantiate and deploy contracts
    contract = w3.eth.contract (
        abi = contract_interface [‘abi‘],
        bytecode = contract_interface [‘bin’]
    )
    # Get transaction hash from deployed contract
    tx_hash = contract.deploy (
        transaction = {‘from‘: w3.eth.accounts [1]}
    )
    # Get tx receipt for contract address
    tx_receipt = w3.eth.getTransactionReceipt (tx_hash)
    return tx_receipt [‘contractAddress‘]
contract_address = deploy_contract (main_contract)
You will see the following line in the tab running the ganache test server:

This is the same information obtained in tx_receipt after the contract is deployed.

6. Now store abi and contract_address in json file. This way we can use it in the flask api to store user objects in the contract.

# Add abi (application binary interface) and transaction receipt in the json file
with open (‘data.json’, ‘w’) as outfile:
    data = {
       "abi": main_contract [‘abi‘],
       "contract_address": deploy_contract (main_contract)
    }
    json.dump (data, outfile, indent = 4, sort_keys = True)
7. Now our complete script looks like this:

import json
from web3 import Web3
from solc import compile_files, link_code, compile_source
# web3.py instance
w3 = Web3 (Web3.HTTPProvider ("http://127.0.0.1:8545"))
def deploy_contract (contract_interface):
    # Instantiate and deploy contract
    contract = w3.eth.contract (
        abi = contract_interface [‘abi‘],
        bytecode = contract_interface [‘bin’]
    )
    # Get transaction hash from deployed contract
    tx_hash = contract.deploy (transaction {‘from‘: w3.eth.accounts [1]})
    # Get tx receipt to get contract address
    tx_receipt = w3.eth.getTransactionReceipt (tx_hash)
    return tx_receipt [‘contractAddress‘]
# compile all contract files
contracts = compile_files ([‘user.sol’, ‘stringUtils.sol’])
# separate main file and link file
main_contract = contracts.pop ("user.sol: userRecords")
library_link = contracts.pop ("stringUtils.sol: StringUtils")
# print bin part in console you will see ‘stringUtils’ in that we need to link library address in that bin code.
# to that we have to deploy library code first then link it
library_address = {
    "stringUtils.sol: StringUtils": deploy_contract (library_link)
}
main_contract [‘bin’] = link_code (
    main_contract [‘bin’], library_address)
# add abi (application binary interface) and transaction reciept in json file
with open (‘data.json’, ‘w’) as outfile:
    data = {
        "abi": main_contract [‘abi‘],
        "contract_address": deploy_contract (main_contract)
    }
    json.dump (data, outfile, indent = 4, sort_keys = True)
Create flask api to store different values for users
You only need to deploy the contract once. But using its address, you will store data again and again. Similarly, in the world of db, you only need to define the model / schema once, but you will add different rows / documents in db.

We will use the flask post api to get the user information of the user and return success.

from flask import Flask, Response, request, jsonify
from marshmallow import Schema, fields, ValidationError
def check_gender (data):
    valid_list = ["male", "female"]
    if data not in valid_list:
        raise ValidationError (
            ‘Invalid gender. Valid choices are’ + valid_list
        )
#For api validations
class UserSchema (Schema):
    name = fields.String (required = True)
    gender = fields.String (required = True, validate = check_gender)
# Initializing flask app
app = Flask (__ name__)
# api to set new user every api call
@ app.route ("/ blockchain / user", methods = [‘POST‘])
def user ():
    body = request.get_json ()
    result, error = UserSchema (). load (body)
    if error:
        return jsonify (error), 422
    return jsonify ({"data": result}), 200
Since this is not a flask tutorial, I will not elaborate on this. Our API users will fetch data from the client (curl request) and validate it and return it to the client (curl request)

2. Now we will initialize the web3 object to communicate with the deployed user contract.

from web3 import Web3
# web3.py instance
w3 = Web3 (Web3.HTTPProvider ("http://127.0.0.1:8545"))
3. Now we will get the abi and contract address previously stored in the data.json file.

with open ("data.json", ‘r’) as f:
     datastore = json.load (f)
     abi = datastore ["abi"]
     contract_address = datastore ["contract_address"]
4. Select the default account address for the transaction. Set a new value for the user in the contract every time. You will take out some gas from your wallet.

w3.eth.defaultAccount = w3.eth.accounts [1]
5. Finally, you will set the value obtained when the API calls the user object in the Ethereum contract.

@ app.route ("/ blockchain / user", methods = [‘POST‘])
def user ():
    # Create the contract instance with the newly-deployed address
    user = w3.eth.contract (address = contract_address, abi = abi)
    body = request.get_json ()
    result, error = UserSchema (). load (body)
    if error:
        return jsonify (error), 422
    tx_hash = user.functions.setUser (
        result [‘name’], result [‘gender’]
    )
    tx_hash = tx_hash.transact ()
    # Wait for transaction to be mined ...
    w3.eth.waitForTransactionReceipt (tx_hash)
    user_data = user.functions.getUser (). call ()
    return jsonify ({"data": user_data}), 200
We first use abi and contract_address to obtain the deployment contract.

user = w3.eth.contract (address = contract_address, abi = abi)
We can then use the contract instance to call any contract public function. After setting the value for the user, we will make it public using the transact method. This will add new user values to the Ethereum block.

tx_hash = user.functions.setUser (
    result [‘name’], result [‘gender’]
) .transact ()
Now we can use the call method to get the value that has been set in the contract, which will call the contract function without adding any blocks to the blockchain.

user_data = user.functions.getUser (). call ()
The final code of our api file is shown below. Save it as app.py.

import json
from flask import Flask, Response, request, jsonify
from marshmallow import Schema, fields, ValidationError
from web3 import Web3
# web3.py instance
w3 = Web3 (Web3.HTTPProvider ("http://127.0.0.1:8545"))
w3.eth.defaultAccount = w3.eth.accounts [1]
# Get stored abi and contract_address
with open ("data.json", ‘r’) as f:
    datastore = json.load (f)
    abi = datastore ["abi"]
    contract_address = datastore ["contract_address"]
def check_gender (data):
    valid_list = ["male", "female"]
    if data not in valid_list:
        raise ValidationError (
            ‘Invalid gender. Valid choices are’ + valid_list
        )
#For api validations
class UserSchema (Schema):
    name = fields.String (required = True)
    gender = fields.String (required = True, validate = check_gender)
# Initializing flask app
app = Flask (__ name__)
# api to set new user every api call
@ app.route ("/ blockchain / user", methods = [‘POST‘])
def user ():
    # Create the contract instance with the newly-deployed address
    user = w3.eth.contract (address = contract_address, abi = abi)
    body = request.get_json ()
    result, error = UserSchema (). load (body)
    if error:
        return jsonify (error), 422
    tx_hash = user.functions.setUser (
        result [‘name’], result [‘gender’]
    ) .transact ()
    # Wait for transaction to be mined ...
    receipt = w3.eth.waitForTransactionReceipt (tx_hash)
    user_data = user.functions.getUser (). call ()
     return jsonify ({"data": user_data}), 200
Run the following command to start the server.

$ FLASK_APP = app.py flask run
Calling API with curl
$ curl -H "Content-Type: application / json" --request POST -d ‘{" name ":" John Doe "," gender ":" male "}‘ http: // localhost: 5000 / blockchain / user
You can also find the complete code here.

Python uses the web3.py library to develop Ethereum. It is very convenient. If you need to learn, you can also take a look here: http://t.cn/RdXcpVD

How Python Flask develops Ethereum smart contracts

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.