今天就跟大家聊聊有關(guān)如何用Python和Flask框架開發(fā)以太坊智能合約,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
創(chuàng)新互聯(lián)建站是一家以網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計、品牌設(shè)計、軟件運維、seo優(yōu)化排名、小程序App開發(fā)等移動開發(fā)為一體互聯(lián)網(wǎng)公司。已累計為塑料袋等眾行業(yè)中小客戶提供優(yōu)質(zhì)的互聯(lián)網(wǎng)建站和軟件開發(fā)服務(wù)。
將數(shù)據(jù)存儲在數(shù)據(jù)庫中是任何軟件應(yīng)用程序不可或缺的一部分。無論如何控制該數(shù)據(jù)庫都有一個該數(shù)據(jù)的主控。區(qū)塊鏈技術(shù)將數(shù)據(jù)存儲到區(qū)塊鏈網(wǎng)絡(luò)內(nèi)的區(qū)塊中。因此,只要某個節(jié)點與網(wǎng)絡(luò)同步,它們就會獲得區(qū)塊中數(shù)據(jù)的副本。因此,該技術(shù)中沒有特定的數(shù)據(jù)主控。
我們將編寫一份智能合約(我將進一步解釋),以便在區(qū)塊鏈上保留用戶數(shù)據(jù)。我們將使用python web3(web3的python庫)來開發(fā)和部署智能合約。一旦我們在區(qū)塊鏈上部署了智能合約。我們將使用flask API與智能合約進行交互以存儲一些數(shù)據(jù)/信息。我們將它存儲在區(qū)塊鏈上,它是不可變的。
Python 3.6
1.創(chuàng)建一個python虛擬環(huán)境。
Virtualenv將你的Python軟件包本地化保存在你項目的虛擬環(huán)境中,而不是強迫你在系統(tǒng)范圍內(nèi)安裝軟件包。
$ virtualenv -p /usr/bin/python3.6 venv $ source venv/bin/activate
2.現(xiàn)在我們需要Ganache
那樣的以太坊測試鏈。
Ganache是以太坊開發(fā)的個人區(qū)塊鏈,可用于部署合約,開發(fā)應(yīng)用程序和運行測試。
$ npm install -g ganache-cli
3.安裝python web3
Web3.py是一個用于與以太坊交互的python庫。它的API源自Web3.js Javascript API,對于使用過web3.js的人來說應(yīng)該很熟悉。
$ pip3 install web3
4.Flask
Flask是一個python輕量級框架。
$ pip3 install flask
5.Flask Restful
Flask-RESTful是Flask的擴展,增加了對快速構(gòu)建REST API的支持。
$ pip3 install flask-restful
Flask Marshmallow
Flask marshmallow是一個對象序列化/反序列化庫。
$ pip3 install flask-marshmallow
要部署智能合約,我們應(yīng)該啟動測試以太坊服務(wù)器。我們正在使用ganache進行測試。在終端中鍵入以下命令:
$ ganache-cli
Ganache為我們提供了10個默認測試帳戶,每個帳戶中有100個假ether,用于交易。我們將使用這些帳戶在合約中部署和設(shè)置各種值。
我們可以看到gas價格和限制以及部署ganache
的host:port
。我們在部署合約時需要這個。
現(xiàn)在我們將用Solidity編寫智能合約。Solidity是在ethereum上編寫智能合約的語言。智能合約包括我們將在區(qū)塊鏈上存儲的數(shù)據(jù),數(shù)據(jù)和getter方法的可選驗證函數(shù),訪問數(shù)據(jù)的setter方法。
例如,要在區(qū)塊鏈上進行考勤注冊,你將擁有一組用戶對象。它將可以訪問用戶的getter,setter方法。由于每個用戶每天只能標記一次出勤,因此你需要一個驗證功能來檢查,智能合約與我們通常用其他任何語言開發(fā)的應(yīng)用程序非常相似。
在下面的文件中,我們使用getter,setter函數(shù)構(gòu)建簡單的用戶合約。
1.在.sol文件中聲明solidity編譯器版本。
pragma solidity ^ 0.4.21;
了解使用的編譯器版本。
$ solidity?—?version
2.導(dǎo)入庫文件Import library。我們應(yīng)該將庫用于常用的實用程序函數(shù)。庫可以只編譯一次并反復(fù)使用(點擊這里獲取一些好的庫資源)。
import“stringUtils.sol”;
3.為用戶聲明合約
contract userRecords {}
4.現(xiàn)在,對于基本演示,我們將存儲有關(guān)用戶的名稱和性別信息。因此,使用struct和enum數(shù)據(jù)類型初始化這兩個變量。
//枚舉類型變量來存儲用戶性別 enum genderType { male, female } //我們將存儲在以太坊合約中的實際用戶對象 struct user{ string name; genderType gender; }
5.現(xiàn)在我們將聲明user(struct)
類型的用戶對象。也可以將其聲明為public,以便從合約外部訪問它(有關(guān)可見范圍,請單擊此處)。
user user_obj;
6.現(xiàn)在為用戶對象添加getter,setter方法。我們將在區(qū)塊鏈上保留每個用戶的信息。我們應(yīng)該始終公開此方法,因為我們將從合約外部訪問它們。
//設(shè)置用戶公共功能 //這類似于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.請注意,我們使用了兩個內(nèi)部函數(shù)getGenderFromString()
和getGenderToString()
。讓我們添加這個內(nèi)部函數(shù)。將它們聲明為內(nèi)部,因為我們不會在外面使用它們。
//用于從string中轉(zhuǎn)換genderType枚舉的內(nèi)部函數(shù) function getGenderFromString(string gender) internal returns(genderType) { if(StringUtils.equal(gender, "male")) { return genderType.male; } else { return genderType.female; } } //將genderType枚舉轉(zhuǎn)換為字符串的內(nèi)部函數(shù) (string) { if(gender == genderType.male) { return "male"; } else { return "female"; } }
我們正在使用stringUtils.equal()
庫函數(shù)。由于此版本的solidity不支持使用(==)進行字符串比較。
8.現(xiàn)在我們的user.sol文件合約如下所示:
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)); } }
1.在下面的python腳本中,我們需要實例化python-web3測試以太坊節(jié)點。我們將設(shè)置ganche url為測試以太坊節(jié)點。我們將使用下面的w3對象來部署合約。
from web3 import Web3 # web3.py instance w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
2.現(xiàn)在我們將編譯solidity
代碼。為了編譯solidity
代碼,我們使用py-solc,它是用于solidity
編譯器的python擴展。
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.每當使用import語句編譯.sol文件時。我們還需要鏈接導(dǎo)入文件的部署地址以及主合約。 因此,對于部署所有鏈接首先通過編譯它(如果已經(jīng)部署然后保存地址)請參見下圖主合約的bin。
當你編譯主合約時,如果你看到它的bin部分,你將找到我們正在導(dǎo)入的庫的_stringUtils.sol:StringUtils ___________
(它也可以用于合約)。 這部分我們應(yīng)該通過在部署合約之前的庫地址來替換它。
4.然后我們將庫地址與主合約相關(guān)聯(lián)。
from solc import link_code 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收據(jù)以獲取合約地址 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 )
鏈接后主合約bin的見下圖:
你將看到導(dǎo)入庫的bin已添加。
5.現(xiàn)在使用我們的w3對象部署主合約。使用ethereum account {'from':w3.eth.accounts [1]}
的默認地址進行部署。
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收據(jù)以獲取合同地址 tx_receipt = w3.eth.getTransactionReceipt(tx_hash) return tx_receipt['contractAddress'] contract_address = deploy_contract(main_contract)
你將在運行g(shù)anache測試服務(wù)器的選項卡中看到以下這行:
這與合約部署后在tx_receipt
中獲得的信息相同。
6.現(xiàn)在將abi和contract_address存儲在json文件中。這樣我們以后可以在flask api中使用它來存儲合約中的用戶對象。
# 在json文件中添加abi(應(yīng)用程序二進制接口)和交易收據(jù) 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.現(xiàn)在我們的完整腳本如下所示:
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)
你只需部署一次合約。但是使用它的地址,你會一次又一次地存儲數(shù)據(jù)。同樣,在db的世界中,你只需定義一次模型/模式,但你將在db中添加不同的行/文檔。
我們將使用flask post api來獲取用戶的用戶信息并返回成功。
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
由于這不是flask教程,我不會詳細說明這一點,如果flask不熟悉可以看這個flask教程學(xué)習(xí)下。我們的API用戶將從客戶端獲取數(shù)據(jù)(curl請求)并對其進行驗證將其返回給客戶端(curl請求)
2.現(xiàn)在我們將初始化web3對象以與已部署的用戶合約進行通信。
from web3 import Web3 # web3.py instance w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
3.現(xiàn)在我們將獲得之前存儲在data.json
文件中的abi和合約地址。
with open("data.json", 'r') as f: datastore = json.load(f) abi = datastore["abi"] contract_address = datastore["contract_address"]
4.選擇交易的默認帳戶地址。每次在合約中為用戶設(shè)置新值。你會從錢包里拿出一些gas。
w3.eth.defaultAccount = w3.eth.accounts[1]
5.最后,你將在以太坊合約中設(shè)置api調(diào)用用戶對象時獲得的值。
@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
我們首先使用abi和contract_address獲得部署合約。
user = w3.eth.contract(address=contract_address, abi=abi)
然后我們可以使用合約實例調(diào)用任何合約公共函數(shù)。在為用戶設(shè)置值之后,我們將使用transact方法將其公之于眾。這將在以太坊區(qū)塊中添加新的用戶值。
tx_hash = user.functions.setUser( result['name'],result['gender'] ).transact()
現(xiàn)在我們可以使用call方法獲得已在合約中設(shè)置的值,這將調(diào)用合約函數(shù)而不在區(qū)塊鏈中添加任何區(qū)塊。
user_data = user.functions.getUser().call()
我們的api文件的最終代碼如下所示。將其另存為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
運行以下命令以啟動服務(wù)器。
$ FLASK_APP=app.py flask run
$ curl -H "Content-Type: application/json" --request POST -d '{"name":"John Doe","gender":"male"}' http://localhost:5000/blockchain/user
看完上述內(nèi)容,你們對如何用Python和Flask框架開發(fā)以太坊智能合約有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。
分享名稱:如何用Python和Flask框架開發(fā)以太坊智能合約
當前路徑:http://aaarwkj.com/article8/pdhiop.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計公司、微信小程序、網(wǎng)站改版、搜索引擎優(yōu)化、網(wǎng)站設(shè)計、微信公眾號
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)