Python Flask如何開發以太坊智能合約

來源:互聯網
上載者:User

標籤:on()   error   可變   庫函數   通過   first   pos   常用   route   

將資料存放區在資料庫中是任何軟體應用程式不可或缺的一部分。無論如何控制該資料庫都有一個該資料的主控。區塊鏈技術將資料存放區到區塊鏈網路內的區塊中。因此,只要某個節點與網路同步,它們就會獲得區塊中資料的副本。因此,該技術中沒有特定的資料主控。

在本教程中,我們將編寫一份智能合約(我將進一步解釋),以便在區塊鏈上保留使用者資料。我們將使用python web3(web3的python庫)來開發和部署智能合約。一旦我們在區塊鏈上部署了智能合約。我們將使用flask API與智能合約進行互動以儲存一些資料/資訊。我們將它儲存在區塊鏈上,它是不可變的。

環境要求

Python 3.6

安裝

1.建立一個python虛擬環境。

Virtualenv將你的Python軟體包本地化儲存在你項目的虛擬環境中,而不是強迫你在系統範圍內安裝軟體包。

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

2.現在我們需要Ganache那樣的以太坊測試鏈。

Ganache是以太坊開發的個人區塊鏈,可用於部署合約,開發應用程式和運行測試。

$ npm install -g ganache-cli

3.安裝python web3

Web3.py是一個用於與以太坊互動的python庫。它的API源自Web3.js Javascript API,對於使用過web3.js的人來說應該很熟悉。

$ pip3 install web3

4.Flask

Flask是一個python輕量級架構。

$ pip3 install flask

5.Flask Restful

Flask-RESTful是Flask的擴充,增加了對快速構建REST API的支援。

$ pip3 install flask-restful
  1. Flask Marshmallow

Flask marshmallow是一個對象序列化/還原序列化庫。

$ pip3 install flask-marshmallow
啟動以太坊測試區塊鏈伺服器

要部署智能合約,我們應該啟動測試以太坊伺服器。我們正在使用ganache進行測試。在終端中鍵入以下命令:

$ ganache-cli

Ganache為我們提供了10個預設測試帳戶,每個帳戶中有100個假ether,用於交易。我們將使用這些帳戶在合約中部署和設定各種值。

我們可以看到gas價格和限制以及部署ganachehost:port。我們在部署合約時需要這個。

建立user.sol檔案

現在我們將用Solidity編寫智能合約。Solidity是在ethereum上編寫智能合約的語言。智能合約包括我們將在區塊鏈上儲存的資料,資料和getter方法的可選驗證函式,訪問資料的setter方法。

例如,要在區塊鏈上進行考勤註冊,你將擁有一組使用者物件。它將可以訪問使用者的getter,setter方法。由於每個使用者每天只能標記一次出勤,因此你需要一個驗證功能來檢查,智能合約與我們通常用其他任何語言開發的應用程式非常相似。

在下面的檔案中,我們使用getter,setter函數構建簡單的使用者合約。

1.在.sol檔案中聲明solidity編譯器版本。

pragma solidity ^ 0.4.21;

瞭解使用的編譯器版本。

$ solidity?—?version

2.匯入庫檔案Import library。我們應該將庫用於常用的公用程式函數。庫可以只編譯一次並反覆使用(點擊這裡擷取一些好的庫資源)。

import“stringUtils.sol”;

3.為使用者聲明合約

contract userRecords {}

4.現在,對於基本示範,我們將儲存有關使用者的名稱和性別資訊。因此,使用struct和enum資料類型初始化這兩個變數。

//枚舉類型變數來儲存使用者性別enum genderType { male, female } //我們將儲存在以太坊合約中的實際使用者物件struct user{     string name; genderType gender; }

5.現在我們將聲明user(struct)類型的使用者物件。也可以將其聲明為public,以便從合約外部存取它(有關可見範圍,請單擊此處)。

user user_obj;

6.現在為使用者物件添加getter,setter方法。我們將在區塊鏈上保留每個使用者的資訊。我們應該始終公開此方法,因為我們將從合約外部存取它們。

//設定使用者公用功能//這類似於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.請注意,我們使用了兩個內建函式getGenderFromString()getGenderToString()。讓我們添加這個內建函式。將它們聲明為內部,因為我們不會在外面使用它們。

//用於從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";    }}

我們正在使用stringUtils.equal()庫函數。由於此版本的solidity不支援使用(==)進行字串比較。

8.現在我們的user.sol檔案合約如下所示:

pragma solidity ^0.4.21;// import library fileimport "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));  }}
使用python指令碼編譯和部署solidity檔案。

1.在下面的python指令碼中,我們需要執行個體化python-web3測試以太坊節點。我們將設定ganche url為測試以太坊節點。我們將使用下面的w3對象來部署合約。

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

2.現在我們將編譯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檔案時。我們還需要連結匯入檔案的部署地址以及主合約。 因此,對於部署所有連結首先通過編譯它(如果已經部署然後儲存地址)請參見主合約的bin。

當你編譯主合約時,如果你看到它的bin部分,你將找到我們正在匯入的庫的_stringUtils.sol:StringUtils ___________(它也可以用於合約)。 這部分我們應該通過在部署合約之前的庫地址來替換它。

4.然後我們將庫地址與主合約相關聯。

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)

連結後主合約bin的見:

你將看到匯入庫的bin已添加。

5.現在使用我們的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收據以擷取合約地址    tx_receipt = w3.eth.getTransactionReceipt(tx_hash)    return tx_receipt[‘contractAddress‘]contract_address = deploy_contract(main_contract)

你將在運行ganache測試伺服器的選項卡中看到以下這行:

這與合約部署後在tx_receipt中獲得的資訊相同。

6.現在將abi和contract_address儲存在json檔案中。這樣我們以後可以在flask api中使用它來儲存合約中的使用者物件。

# 在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.現在我們的完整指令碼如下所示:

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    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 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  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 itlibrary_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 filewith 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)
建立flask api以為使用者儲存不同的值

你只需部署一次合約。但是使用它的地址,你會一次又一次地儲存資料。同樣,在db的世界中,你只需定義一次模型/模式,但你將在db中添加不同的行/文檔。

我們將使用flask post api來擷取使用者的使用者資訊並返回成功。

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

由於這不是flask教程,我不會詳細說明這一點。我們的API使用者將從用戶端擷取資料(curl請求)並對其進行驗證將其返回給用戶端(curl請求)

2.現在我們將初始化web3對象以與已部署的使用者合約進行通訊。

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

3.現在我們將獲得之前儲存在data.json檔案中的abi和合約地址。

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

4.選擇交易的預設帳戶地址。每次在合約中為使用者佈建新值。你會從錢包裡拿出一些gas。

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

5.最後,你將在以太坊合約中設定api調用使用者物件時獲得的值。

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

然後我們可以使用合約執行個體調用任何合約公用函數。在為使用者佈建值之後,我們將使用transact方法將其公之於眾。這將在以太坊區塊中添加新的使用者值。

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

現在我們可以使用call方法獲得已在合約中設定的值,這將調用合約函數而不在區塊鏈中添加任何區塊。

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

我們的api檔案的最終代碼如下所示。將其另存新檔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)    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 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():    # 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

運行以下命令以啟動伺服器。

$ FLASK_APP=app.py flask run
用curl調用api
$ curl -H "Content-Type: application/json" --request POST -d ‘{"name":"John Doe","gender":"male"}‘ http://localhost:5000/blockchain/user

你也可以在這裡找到完整代碼。

python用web3.py庫開發以太坊來說非常的方便,有需要學的也可以看看這裡:http://t.cn/RdXcpVD

Python Flask如何開發以太坊智能合約

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.