How to develop with Python and flask-Ethereum smart Contracts

Source: Internet
Author: User
Tags 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 need Ganache that 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 deployment ganache host: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.

//枚举类型变量来存储用户性别enum genderType { male, female } //我们将存储在以太坊合约中的实际用户对象struct user{     string name; genderType gender; }

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

user user_obj;

6. Now add the Getter,setter method 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.

//设置用户公共功能//这类似于db中的持久对象。function setUser(string name, string gender) public {    genderType gender_type = getGenderFromString(gender);    user_obj = user({name:name, gender: gender_type});}//获取用户公共功能//这类似于从db获取对象。function getUser() public returns (string, string) {     return (user_obj.name, getGenderToString(user_obj.gender));}

7. Note that we have used two intrinsic functions getGenderFromString() and getGenderToString() . Let's add this inner function. Declare them as internal because we are not going to use them outside.

//用于从string中转换genderType枚举的内部函数function getGenderFromString(string gender) internal returns(genderType) {    if(StringUtils.equal(gender, "male")) {        return genderType.male;    } else {        return genderType.female;    }}//将genderType枚举转换为字符串的内部函数(string) {    if(gender == genderType.male) {        return "male";    } else {        return "female";    }}

We are using stringUtils.equal() library functions. Because this version of solidity does not support string comparisons using (= =).

8. Our User.sol file contract is now as follows:

pragma solidity ^0.4.21;//import library FileImport "Stringutils.sol"; contract userrecords {//enum type variable to St  Ore user gender enum Gendertype {male, female};    Actual User object which we'll 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 RET    Urns (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 the solidity file using a Python script.

1. In the following Python script, we need to instantiate the 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 instancew3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))

2. Now we will compile the solidity code. In order to compile the solidity code, we use PY-SOLC, which is solidity the python extension for the compiler.

from solc import compile_files# 编译所有合约文件contracts = compile_files([‘user.sol‘, ‘stringUtils.sol‘])# 单独的主文件和链接文件main_contract = contracts.pop("user.sol:userRecords")library_link = contracts.pop("stringUtils.sol:StringUtils")

3. Whenever the. Sol file is compiled with the import statement. We also need to link the deployment address of the import file with the main contract. Therefore, for deploying all links first by compiling it (if you have already deployed and then save the address), see the bin of the main contract.

When you compile the main contract, if you see the bin portion of it, you will find the library we are importing _stringUtils.sol:StringUtils ___________ (it can also be used for the contract). This part should be replaced by the library address before the contract is deployed.

4. We then associate the library address with the main contract.

from solc import link_codedef deploy_contract(contract_interface):    #实例化和部署合约    contract = w3.eth.contract(        abi=contract_interface[‘abi‘],        bytecode=contract_interface[‘bin‘]    )    #从已部署的合约中获取交易哈希    tx_hash = contract.deploy(        transaction={‘from‘: w3.eth.accounts[1]}    )    #获取tx收据以获取合约地址    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 link to the contract bin:

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

5. Now use our W3 object to deploy the master contract. Use ethereum account {‘from‘:w3.eth.accounts [1]} The default address to deploy.

def deploy_contract(contract_interface):    # 实例化和部署合约    contract = w3.eth.contract(        abi=contract_interface[‘abi‘],        bytecode=contract_interface[‘bin‘]    )    # 从部署的合约中获取交易哈希    tx_hash = contract.deploy(        transaction={‘from‘: w3.eth.accounts[1]}    )    # 获取tx收据以获取合同地址    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 that is obtained in the contract after deployment tx_receipt .

6. The ABI and contract_address are now stored in the JSON file. This allows us to use it later in the Flask API to store the user objects in the contract.

# 在json文件中添加abi(应用程序二进制接口)和交易收据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 jsonfrom web3 import web3from solc import compile_files, Link_code, compile_source# web3.py instancew3 = Web3 (Web3. Httpprovider ("http://127.0.0.1:8545")) def deploy_contract (Contract_interface): # Instantiate and deploy contract con tract = w3.eth.contract (abi=contract_interface[' Abi '), bytecode=contract_interface[' bin ']) # Get tra Nsaction hash from deployed contract Tx_hash =contract.deploy (transaction{' from ': W3.eth.accounts[1]}) # Get TX recei PT to get contract address Tx_receipt = W3.eth.getTransactionReceipt (tx_hash) return tx_receipt[' contractaddress ']# Compile all contract filescontracts = Compile_files ([' User.sol ', ' Stringutils.sol ']) # Separate main file and link Filemain _contract = Contracts.pop ("user.sol:userRecords") Library_link = Contracts.pop ("stringUtils.sol:StringUtils") # Print  Bin part in the console you'll see ' stringutils ' in the that we need to link library address in this bin code.# to the We have To deploy library code firstThen link itlibrary_address = {"StringUtils.sol:StringUtils": Deploy_contract (library_link)}main_contract[' bin '] = Li Nk_code (main_contract[' bin '], library_address) # Add ABI (Application Binary interface) and transaction reciept in JSON Filewith open (' Data.json ', ' W ') as Outfile:data = {"Abi": main_contract[' Abi '], "contract_address": Dep Loy_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 one contract. But with its address, you will store the data again and again. Similarly, in the world of DB, you only have to define the model/schema once, but you will add different lines/documents to the DB.

We will use the Flask Post API to get the user's user information and return to success.

from flask import Flask, Response, request, jsonifyfrom marshmallow import Schema, fields, ValidationErrordef check_gender(data):    valid_list = ["male", "female"]    if data not in valid_list:        raise ValidationError(            ‘Invalid gender. Valid choices are‘+ valid_list        )#For api validationsclass UserSchema(Schema):    name = fields.String(required=True)    gender = fields.String(required=True, validate=check_gender)# Initializing flask appapp = 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, if flask unfamiliar can watch this flask tutorial under study. Our API users will get the data (Curl request) from the client and validate it back to the client (Curl request)

2. We will now initialize the Web3 object to communicate with the deployed user contract.

from web3 import Web3# web3.py instancew3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))

3. We will now get the ABI and contract addresses that were 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. Each time a new value is set for the user in the contract. You'll get some gas out of your purse.

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

5. Finally, you will set the values that are obtained when the API invokes 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 the ABI and contract_address to get the deployment contract.

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

Then we can invoke any contract public function using the contract instance. After setting the value for the user, we will use the Transact method to make it public. This will add a new user value 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 already set in the contract, which invokes the contract function without adding any chunks to the blockchain.

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

The final code for our API file is shown below. Save it as app.py .

Import jsonfrom flask Import flask, Response, request, Jsonifyfrom Marshmallow Import Schema, fields, Validationerrorfrom WEB3 Import web3# web3.py instancew3 = Web3 (Web3.httpprovider ("http://127.0.0.1:8545")) W3.eth.defaultAccount = w3.eth.accounts[1]# Get stored Abi and Contract_addresswith open ("Data.json", ' R ') as F:datastore = Json.load (f) AB i = 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 is ' + valid_list) #For API Validationsclass Userschema (Schema): name = fields. String (required=true) gender = fields. String (Required=true, Validate=check_gender) # Initializing Flask Appapp = Flask (__name__) # API to set new user every API C All@app.route ("/blockchain/user", methods=[' POST ']) def user (): # Create The contract instance with the newly-deployed a ddress user = W3.eth.contract (addresS=contract_address, Abi=abi) BODY = Request.get_json () result, error = Userschema (). Load (body) if error:r  Eturn 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 the 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 is very handy for developing ethereum with the web3.py library, and interested users can focus on the Python Ethereum tutorial, which is intended for Python engineers using web3.py for blockchain development.

How to develop with Python and flask-Ethereum smart Contracts

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.