How to develop Ethereum contract using Python Flask?

How to develop Ethereum contract using Python Flask?

ethereum-contract

Storing data in a database is an integral part of any software application.Whoever has the control of that database is a master of that data. Blockchain technology stores data into the block inside blockchain network.So whenever some node syncs to the network they will get the copy of the data in the block. So there is no particular master of the data in this technology.

 

In this tutorial, we will write a smart contract(I will explain this further) for persisting user data on the blockchain. We will use python web3(python library for web3) for making and deploying smart contract. Once we have deployed smart contract on the blockchain. We will interact with smart contract using flask API to store some data/information. We are storing that on blockchain so that it will be immutable.

 

 

Requirements:

Python 3.6

 

Installation:

1. Create a python virtual environment.

 

Virtualenv (http://virtualenv.readthedocs.io/en/latest/) keeps your Python packages in a virtual environment localized to your project, instead of forcing you to install your packages system-wide.

 

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

2. Now we need ethereum test chain like ganache.

 

Ganache (https://github.com/trufflesuite/ganache-cli/blob/master/README.md) is a personal blockchain for Ethereum development you can use to deploy contracts, develop your applications, and run tests.

 

$ npm install -g ganache-cli

3. Install python web3.

 

Web3.py (http://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 anyone who has used web3.js.

 

$ pip3 install web3

4. Flask:

 

Flask (http://flask.pocoo.org/) is a microframework for Python

 

$ pip3 install flask

5. Flask Restful

 

Flask-RESTful (https://flask-restful.readthedocs.io/en/latest/) is an extension for Flask that adds support for quickly building REST APIs.

 

$ pip3 install flask-restful

6. Flask Marshmallow

 

Flask marshmallow is an object serialization/deserialization library

$ pip3 install flask-marshmallow

 

Start Ethereum Test blockchain server.

To deploy smart contract we should start test ethereum server. We are using ganache for testing. Type below command in terminal.

 

$ ganache-cli

 

Ganache gives us 10 default test accounts with 100 fake ethers in each account for transaction. We will use these accounts for deploying and setting values in contracts.

 

 

We can see the gas prize and limit along with host:port on which ganache is deployed. We will need this while deploying contract.

 

Create user.sol file

Now we will write the smart contract in solidity.Solidity is the language to write smart contract on ethereum.Smart contract consists of data which we are going to store on blockchain with optional validation functions on the data and getter, setter method for accessing data.

 

For example, to make an attendance register on blockchain you will have an array of user objects.It will have the getter, setter method for accessing user.Since each user can mark her attendance only once per day, you need a validation function to check it.The smart contract is very similar to the application which we normally develop in any other language.

 

In below file, we are building simple user contract with the getter, setter functions.

 

1. Declare solidity compiler version in .sol file.

pragma solidity ^0.4.21;

To know which compiler version used

$ solidity — version

2. Import library file. we should use libraries for common utility functions.Libraries are only compiled once and used again and again(click here for some good library resources).

import “stringUtils.sol”;

3. Declare contract for user

contract userRecords {}

4. Now for the basic demo, we are going to store name and gender information about the user. So initialize this two variables using struct and enum data types.

// enum type variable to store user gender
enum genderType { male, female }
// Actual user object which we will store in ethereum contract
struct user{
string name; genderType gender;
}

5. Now we will declare user object of type user(struct). You can also declare it as public to access it from outside the contract(For visibility scope click here).

user user_obj;

6. Now add getter, setter methods for the user object. We are going to persist every user’s information on blockchain.We should always make this method public as we will access them from outside the contract.

// 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));
}

7. Notice that we are using two internal helper functions getGenderFromString() and getGenderToString(). Lets add this internal functions. Declare them internal as we are not going to use them outside.

// Internal function to convert 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”;
}
}

We are using stringUtils.equal() library function. As this version of solidity doesn’t support string compare using (==).

 

8. Now our contract that is user.sol file will look like below:

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 above solidity file using python script.

1. In the below python script we need to instantiate test ethereum node using python-web3. We are setting ganche url as a test ethereum node. We will use below w3 object for deploying contract.

from web3 import Web3
# web3.py instance
w3 = Web3(Web3.HTTPProvider(“http://127.0.0.1:8545”))

2. Now we will compile solidity code. To compile solidity code we are using py-solc that is python extension for solidity compiler.

from solc import compile_files
# 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”)

3. Whenever you compile a .sol file with the import statement. We also need to link deploy address of the import file along with the main contract. So for that deploy all links first by compiling it(If already deployed then save the address) See below image main contract bin.

compile contract bin

When you compile your main contract and if you see bin part of it you will find “_stringUtils.sol:StringUtils___________” for the library(it can be for the contract as well) which we are importing. This part we should replace with the library address by deploying it before contract.

4. Then we will link the library address with the main contract.

from solc import link_code
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’]
library_address = {
“stringUtils.sol:StringUtils”: deploy_contract(library_link)
}
main_contract[‘bin’] = link_code(
main_contract[‘bin’], library_address
)

 

After linking see below image of main contract bin

 

main contract bin 2

You will see import library bin has been added.

 

5. Now deploy main contract using our w3 object. Use default address from ethereum account {‘from’: w3.eth.accounts[1]} for deployment.

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’]
contract_address = deploy_contract(main_contract)

You will see below lines in the tab where your ganache test server is running.

 

Ganache receipt

This is the same info which you will get in tx_receipt after contract deployment.

 

6. Now store abi and contract_address in json file. so that we can use it later in flask api for storing user object in contract.

# 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)

 

7. Now our complete script will look like below

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 user.

You will deploy contract only once. But using its address you will store data again and again. Similarly, in the world of DB, you will define model/schema only once but you will add different rows/document in db.

 

1. We will make flask post api to get user info from 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

As this is not flask tutorial I will not elaborate much on this. Our API user will get data from client(curl request) and validate it and return the same to client(curl request)

 

2. Now we will initialize web3 object for communicating with 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 abi and contract address which we have stored earlier in “data.json” file.

with open(“data.json”, ‘r’) as f:
datastore = json.load(f)
abi = datastore[“abi”]
contract_address = datastore[“contract_address”]

4. Chose default account address for transactions. Each time you set new values for user in contract. You will give some gas from your wallet.

w3.eth.defaultAccount = w3.eth.accounts[1]

5. Finally you will set the values you are getting in api call to user object in 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 are first getting deployed contract using abi and contract_address.

user = w3.eth.contract(address=contract_address, abi=abi)

Then we can call any contract public functions using the contract instance.After setting values for user we will make it public by using transact() method.This will make new user value added in ethereum block.

tx_hash = user.functions.setUser(
result[‘name’],result[‘gender’]
).transact()

Now we can get already set values in the contract using call() method this will call contract function without adding any block in blockchain.

user_data = user.functions.getUser().call()

Our final code for api file will look like below. Save this 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 below commands to start your server.

$ FLASK_APP=app.py flask run

 

Call api using curl.

 

$ curl -H “Content-Type: application/json” –request POST -d ‘{“name”:”John Doe”,”gender”:”male”}’ http://localhost:5000/blockchain/user

You can find whole code here (https://github.com/NehaGhogale/basic_user_contract).

Resources:

1. http://web3py.readthedocs.io/en/stable/quickstart.html
2. https://www.ethereum.org/greeter
3. http://solidity.readthedocs.io/en/latest/

Author

Neha Ghogale

Software Developer

Book a Free Consultation