Ontosight® – Biweekly NewsletterJune 17th – June 30th, 2024 –Read More
How to develop Ethereum contract using Python Flask?
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.
$ 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.
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.
4. Flask:
Flask (http://flask.pocoo.org/) is a microframework for Python
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.
6. Flask Marshmallow
Flask marshmallow is an object serialization/deserialization library
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 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.
To know which compiler version used
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).
3. Declare contract for user
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 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).
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.
// 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.
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:
// 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.
# 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.
# 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.
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.
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
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.
# 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.
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.
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
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 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.
# 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.
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.
5. Finally you will set the values you are getting in api call to user object in ethereum contract.
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.
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.
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.
Our final code for api file will look like below. Save this as “app.py”
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.
Call api using curl.
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/
Featured Blogs
Machine learning as an indispensable tool for Biopharma
The cost of developing a new drug roughly doubles every nine years (inflation-adjusted) aka Eroom’s law. As the volume of data…
Find biological associations between ‘never thought before to be linked’
There was a time when science depended on manual efforts by scientists and researchers. Then, came an avalanche of data…
Find key opinion leaders and influencers to drive your therapy’s
Collaboration with key opinion leaders and influencers becomes crucial at various stages of the drug development chain. When a pharmaceutical…
Impact of AI and Digitalization on R&D in Biopharmaceutical Industry
Data are not the new gold – but the ability to put them together in a relevant and analyzable way…
Why AI Is a Practical Solution for Pharma
Artificial intelligence, or AI, is gaining more attention in the pharma space these days. At one time evoking images from…
How can AI help in Transforming the Drug Development Cycle?
Artificial intelligence (AI) is transforming the pharmaceutical industry with extraordinary innovations that are automating processes at every stage of drug…
How Will AI Disrupt the Pharma Industry?
There is a lot of buzz these days about how artificial intelligence (AI) is going to disrupt the pharmaceutical industry….
Revolutionizing Drug Discovery with AI-Powered Solutions
Drug discovery plays a key role in the pharma and biotech industries. Discovering unmet needs, pinpointing the target, identifying the…
Leveraging the Role of AI for More Successful Clinical Trials
The pharmaceutical industry spends billions on R&D each year. Clinical trials require tremendous amounts of effort, from identifying sites and…
Understanding the Language of Life Sciences
Training algorithms to identify and extract Life Sciences-specific data The English dictionary is full of words and definitions that can be…
Understanding the Computer Vision Technology
The early 1970s introduced the world to the idea of computer vision, a promising technology automating tasks that would otherwise…
AI Is All Hype If We Don’t Have Access to
Summary: AI could potentially speed drug discovery and save time in rejecting treatments that are unlikely to yield worthwhile resultsAI has…