Python中的以太坊智慧合約開發指南
在以太坊上獲得一個基本的智慧合約是一個很簡單的事,只需google查詢“ERC20代幣教程”,你會發現有關如何做到這一點的大量資訊。以程式設計方式與合約互動完全是另一回事,如果你是一個Python程式員,那麼教程就很少。所以寫這個Python中的以太坊智慧合約開發指南。
按我的統計對我們來說幸運的是,2017年Web3.py的第4版釋出,這意味著現在比以往更容易執行python指令碼並觀察區塊鏈上發生的神奇事情。像幽靈般的。
Piper Merriam,Jason Carver以及其他所有在Web3.py上努力工作以使我們其他人生活更輕鬆的人大聲呼喊,在Sempo,我們正在使用以太坊來使災難般的響應更加透明,而且它是隻有 Web3.py
才能真正實現。
設定
首先我們進行設定,確保我們安裝了相關的python庫。
Python庫無處不在,但它們的用途是什麼?
有很多與以太坊相關的python庫,但是當人們談論以太坊時,有兩個會出現很多:Web3.py和Pyethereum。乍一看,你應該使用哪一個並不明顯。
Pyethereum
以太坊虛擬機器(EVM)的Python實現。反過來,EVM是以太坊協議的一部分,它實際執行智慧合約中的程式碼並確定其輸出。因此,如果你想在Python中執行以太坊節點,那麼Pyethereum是一個很好的起點。
即使你非常高興在不執行自己的節點的情況下執行智慧合約,Pyethereum仍然是一個很好的庫,它包含許多功能,可以執行有用的功能,例如從私鑰計算使用者的地址等等。
Web3.py
用於實際與以太坊區塊鏈互動的庫。我們談論的事情包括在賬戶之間轉移乙太網,釋出智慧合約以及觸發附加現有智慧合約的功能。它受到流行的JavaScript/">JavaScript庫Web3.js的啟發,它將成為我們在本教程中使用的主庫。
好的,少說多做!
起初我嘗試使用Python3.5版本,但在執行時我遇到了問題,顯然是由Python的型別提示造成的。基於Python3.6建立虛擬環境解決了這個問題,所以我建議你做同樣的事情。
繼續並pip-install ofollow,noindex">web3 (確保你獲得版本4)。
除非你喜歡花錢,否則你需要在以太坊測試網上使用錢包,例如Ropsten和其他大量以太玩法。一個簡單的方法是下載Chrome的 Metamask 擴充套件,並從那裡建立一個新帳戶。
確保你還選擇左側的’Ropsten Test Net’。
即使你的現有錢包中包含真正的以太幣,我也強烈建議你為開發目的建立一個新的錢包。我們將使用私鑰做一些相對無法預測的事,所以如果它們不小心變成公共主網路的話就不會有問題(公私鑰?)
為新建立的錢包獲取測試Ether非常簡單:只需訪問 faucet.metamask.io 並點選“請求來自faucet的1個 以太”。對於我們將要做的事情,這應該是充足的。
最後,因為我們將在沒有託管我們自己的節點的情況下使用 Ropsten TestNet
,我們需要一個可以連線Blockchain的供應商。 Infura.io 適用於此,所以去那裡建立一個免費帳戶。記下Ropsten TestNet的提供者URL(看起來像https://ropsten.infura.io/FE2Gfedcm3tfed3)。
部署智慧合約
使用Python來部署智慧合約而不執行自己的節點是非常困難的,所以我們將在這一步上做點兒手腳。對於許多智慧合約用例,你只需要執行一次。
正如我之前提到的,有關如何部署ERC20合約的百萬條指南,因此我們將部署一些不同的(並且更方便地更短)。
問:誰喜歡在網際網路上分享他們的意見?
大家都喜歡?
好答案。以下我稱之為“Soap Box”肥皂盒的智慧合約允許任何人向區塊鏈廣播他們想要的任何意見,在永恆的剩餘時間(給予或接受)可以看到它。
但是有一個問題:只有支付了必要的0.02乙太網費用的地址才能播出他們的意見。聽起來不太公平,但就這樣。
Remix ,以太坊的線上程式碼編輯器非常出色,因此在那裡建立一個新檔案並貼上以下程式碼。它是用Solidity(Smart Contracts的程式語言)編寫的。如果程式碼沒有太多意義並不重要,我們將在稍後詳細介紹相關部分,但最終這是一個Python教程。
pragma solidity ^0.4.0; contract SoapBox { // Our 'dict' of addresses that are approved to share opinions //我們批准分享意見的地址的“字典” mapping (address => bool) approvedSoapboxer; string opinion; // Our event to announce an opinion on the blockchain //我們的事件釋出對區塊鏈的意見 event OpinionBroadcast(address _soapboxer, string _opinion); // This is a constructor function, so its name has to match the contract //這是一個建構函式,所以它的名字必須與合約相匹配 function SoapBox() public { } // Because this function is 'payable' it will be called when ether is sent to the contract address. //因為這個函式是“支付”,所以當乙太網被髮送到合約地址時將被呼叫。 function() public payable{ // msg is a special variable that contains information about the transaction // msg是一個特殊變數,包含有關交易的資訊 if (msg.value > 20000000000000000) { //if the value sent greater than 0.02 ether (in Wei) //如果傳送的值大於0.02 ether(在Wei中) // then add the sender's address to approvedSoapboxer //然後將發件人的地址新增到approvedSoapboxer approvedSoapboxer[msg.sender] =true; } } // Our read-only function that checks whether the specified address is approved to post opinions. //我們的只讀函式,用於檢查指定地址是否被批准釋出意見。 function isApproved(address _soapboxer) public view returns (bool approved) { return approvedSoapboxer[_soapboxer]; } // Read-only function that returns the current opinion //返回當前意見的只讀函式 function getCurrentOpinion() public view returns(string) { return opinion; } //Our function that modifies the state on the blockchain //我們的函式修改了區塊鏈上的狀態 function broadcastOpinion(string _opinion) public returns (bool success) { // Looking up the address of the sender will return false if the sender isn't approved //如果發件人未獲批准,查詢發件人的地址將返回false if (approvedSoapboxer[msg.sender]) { opinion = _opinion; emit OpinionBroadcast(msg.sender, opinion); return true; } else { return false; } } }
以下是Metamask變得非常有用的地方:如果你點選重新混音視窗右上角的“run”執行標籤並在“Environment”環境下拉列表中選擇“Injected Web3”注入的Web3,則“Account”帳戶下拉列表中應填充你的帳戶地址早在MetaMask中建立。如果沒有,只需重新整理瀏覽器即可。
然後單擊“create”建立。Metamask應該彈出一個彈出視窗,要求你確認交易。如果沒有,只需開啟Metamask擴充套件並在那裡執行:
你將在Remix控制檯底部收到一條訊息,告知你合約的建立正在等待處理。單擊連結以在Etherscan上檢視其狀態。如果重新整理並且“To”收件人欄位填充了合約地址,則合約已成功部署。
一旦你記下了合約地址,我們就該開始通過Web3.py與合約進行互動了。
在我看來,有四種(半)方式可以與以太坊智慧合約進行互動。最後兩個(一半)經常混在一起,但差異很重要。我們已經看到了第一個:在區塊鏈上部署智慧合約。現在我們將介紹其餘的python:
- 向合約傳送以太:真正自我解釋,將以太幣從錢包傳送到智慧合約的地址。希望換取有用的東西。
- 呼叫函式:執行智慧合約的只讀功能以獲取某些資訊(例如地址的餘額)。
- 與功能進行交易:執行智慧合約的功能,該功能可以更改區塊鏈的狀態。
- 檢視事件:檢視由於先前的功能交易而釋出到區塊鏈的資訊。
將以太幣傳送給合約
一些(但不是全部)智慧合約包括“payable”應付功能。如果你將Ether傳送到合約的地址,則會觸發這些功能。一個典型的用例就是ICO:將以太送到合約中,然後返回給你的是代幣。
首先,我們將從匯入開始,建立一個新的web3物件,通過Infura.io連線到Ropsten TestNet。
import time from web3 import Web3, HTTPProvider contract_address= [YOUR CONTRACT ADDRESS] wallet_private_key= [YOUR TEST WALLET PRIVATE KEY] wallet_address= [YOUR WALLET ADDRESS] w3 = Web3(HTTPProvider([YOUR INFURA URL])) w3.eth.enable_unaudited_features()
你可以在Metamask中的帳戶名稱旁邊的選單中找到你的錢包私鑰。因為我們使用的Web3.py的某些功能尚未經過完全稽核以確保安全性,所以我們需要呼叫 w3.eth.enable_unaudited_features()
來確認我們知道可能會發生問題的情況。我告訴過你我們用私鑰做了一些危險的事情!
現在我們將編寫一個函式,將以太幣從我們的錢包傳送到合約:
def send_ether_to_contract(amount_in_ether): amount_in_wei = w3.toWei(amount_in_ether,'ether'); nonce = w3.eth.getTransactionCount(wallet_address) txn_dict = { 'to': contract_address, 'value': amount_in_wei, 'gas': 2000000, 'gasPrice': w3.toWei('40', 'gwei'), 'nonce': nonce, 'chainId': 3 } signed_txn = w3.eth.account.signTransaction(txn_dict, wallet_private_key) txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction) txn_receipt = None count = 0 while txn_receipt is None and (count < 30): txn_receipt = w3.eth.getTransactionReceipt(txn_hash) print(txn_receipt) time.sleep(10) if txn_receipt is None: return {'status': 'failed', 'error': 'timeout'} return {'status': 'added', 'txn_receipt': txn_receipt}
首先讓我們回顧一下交易字典 txn_dict
:它包含了定義我們傳送給智慧合約的交易所需的大部分資訊。
to Vaule gas gasPrice Nonce Chain ID
關於gas限制的快速說明:有一些功能可以讓你估算交易將使用多少gas。但是,我發現選擇限制的最佳方法是計算出你願意支付多少錢,然後再讓交易失敗,然後再去做。
一旦我們定義了交易的重要部分,我們就會使用我們錢包的私鑰對其進行簽名。然後它就可以傳送到網路了,我們將使用 sendRawTransaction
方法。
在礦工決定將其包含在一個區塊中之前,我們的交易實際上不會完成。一般而言,你為每個單位支付的費用Gas(記住我們的天然氣價格引數)決定了一個節點決定將你的交易包含在一個區塊中的速度(如果有的話)。
https://ethgasstation.info/是一個很好的地方,可以確定你將等待你的交易包含在一個區塊中的時間。
此時間延遲意味著交易是非同步的。當我們呼叫sendRawTransaction時,我們會立即獲得交易的唯一雜湊值。你可以隨時使用此雜湊來查詢你的交易是否已包含在塊中。我們知道,當且僅當我們能夠獲得交易收據時才將交易新增到區塊鏈中(因為所有好的購買都帶有收據嗎?)。這就是為什麼我們建立迴圈來定期檢查我們是否有收據:
txn_receipt = None count = 0 while txn_receipt is None and (count < 30): txn_receipt = w3.eth.getTransactionReceipt(txn_hash) print(txn_receipt) time.sleep(10)
值得注意的是,交易可以新增到區塊鏈中,但仍然因各種原因而失敗,例如沒有足夠的gas。
這就是將以太符號傳送給合約的Python程式碼。讓我們快速回顧一下我們在Solidity中寫的應付函式:
function() public payable{ if (msg.value >= 20000000000000000) { approvedSoapboxer[msg.sender] =true; } }
Msg是智慧合約中的一個特殊變數,其中包含有關傳送到智慧合約的交易的資訊。在這種情況下,我們使用 msg.value
,它給出了交易中傳送的Ether數量(在Wei而不是raw Ether中)。同樣, msg.sender
給出了進行交易的錢包的地址:如果已經發送了足夠的以太幣,我們會將其新增到已批准帳戶的字典中。
繼續執行 send_ether_to_contract
函式。希望你能收到回執。你還可以通過在Etherscan的Ropsten Network部分查詢你的錢包地址來檢查交易是否完成。我們將在下一節中獲得Python中的更多資訊。
呼叫一個函式
我們剛剛向我們的智慧合約傳送了一些以太幣,因此我們想檢查我們的錢包地址是否已被批准分享意見是有意義的。為此,我們在智慧合約中定義了以下功能:
function isApproved(address _soapboxer) public view returns (bool approved) { return approvedSoapboxer[_soapboxer]; }
與python相比,這個函式附帶了很多額外的東西,比如宣告型別(地址和bool)。儘管如此,這個函式只需要一個地址(_soapboxer引數),在有效(但不完全)的雜湊表/python dict中查詢相應的批准布林值並返回該值。
你呼叫的時候一個智慧合約函式,以太坊節點將計算結果,並將其返回給你。這裡的事情變得有點複雜:呼叫是隻讀的,這意味著它們不會對區塊鏈進行任何更改。如果上述函式包含一行程式碼來記錄數字時間,則檢查地址是否已批准:
approvedCheckedCount[_soapboxer] = approvedCheckedCount[_soapboxer] + 1
然後,當呼叫該函式時,該節點將計算 approvedCheckedCount
的新值,但一旦返回結果就丟棄它。
作為只讀的交換,函式呼叫不會花費你執行任何以太,因此你可以愉快地檢查帳戶是否已被批准而不必擔心成本。
讓我們跳回到我們的python檔案的頂部並新增一些更多的設定程式碼。
import contract_abi contract = w3.eth.contract(address = contract_address, abi = contract_abi.abi)
你需要建立另一個名為 contract_abi
的python檔案。這將包含一個大的JSON資訊字串,Python需要與我們在智慧合約中定義的函式進行互動,稱為應用程式二進位制介面(ABI)。你可以在Remix中找到智慧合約的ABI的JSON字串:
- 單擊編譯“Compile”選項卡。
- 單擊詳細資訊“Details”——應顯示包含大量資訊的模式。
- 向下滾動到ABI部分,然後單擊複製到剪貼簿“Copy to clipboard”圖示。
將複製的字串貼上到 contract_abi.py
檔案中,該檔案應如下所示:
abi = """[ { A BIG LIST OF ABI INFO SPREAD ACROSS MULTIPLE DICTS } ]""
我們新增到主python檔案的另一行現在使用此ABI JSON字串並使用它來設定合約物件。如果你瀏覽合約,你會注意到它包含一個函式屬性,其中包含我們在智慧合約中建立的三個函式。
現在我們將建立一個python函式,該函式呼叫 Smart Contract
智慧合約的 isApproved
函式來檢查指定的地址是否被批准分享意見。
def check_whether_address_is_approved(address): return contract.functions.isApproved(address).call()
那很短暫。
你現在可以使用它來檢查你的錢包地址是否已獲批准。如果你之前運行了 send_ether_to_contract
函式併發送了足夠數量的以太,那麼希望你能回到 true
。
與函式交易
我們正在與智慧合約進行最後的主要互動:廣播意見。再一次,我們來看看我們的Solidity Code:
function broadcastOpinion(string _opinion) public returns (bool success) { if (approvedSoapboxer[msg.sender]) { opinion = _opinion; emit OpinionBroadcast(msg.sender, opinion); return true; } else { return false; } }
這裡沒有什麼新東西:我們採用傳入的 _opinion
引數並使用它來設定全域性變數意見。(如果你願意,可以通過getter函式查詢實習生)。有一條線有點不同:
emit OpinionBroadcast(msg.sender, opinion)
我們很快就會介紹。
當你通過交易與智慧合約的功能進行互動時,功能對智慧合約狀態所做的任何更改都會在區塊鏈上釋出。為了換取這種特權,你必須向礦工支付一些(希望很小)的以太量。Python時間:
def broadcast_an_opinion(covfefe): nonce = w3.eth.getTransactionCount(wallet_address) txn_dict = contract.functions.broadcastOpinion(covfefe).buildTransaction({ 'chainId': 3, 'gas': 140000, 'gasPrice': w3.toWei('40', 'gwei'), 'nonce': nonce, }) signed_txn = w3.eth.account.signTransaction(txn_dict, private_key=wallet_private_key) result = w3.eth.sendRawTransaction(signed_txn.rawTransaction) tx_receipt = w3.eth.getTransactionReceipt(result) count = 0 while tx_receipt is None and (count < 30): time.sleep(10) tx_receipt = w3.eth.getTransactionReceipt(result) print(tx_receipt) if tx_receipt is None: return {'status': 'failed', 'error': 'timeout'} processed_receipt = contract.events.OpinionBroadcast().processReceipt(tx_receipt) print(processed_receipt) output = "Address {} broadcasted the opinion: {}"\ .format(processed_receipt[0].args._soapboxer, processed_receipt[0].args._opinion) print(output) return {'status': 'added', 'processed_receipt': processed_receipt}
這實際上與將Ether傳送到智慧合約時使用的過程相同。我們將建立並簽署一個交易,然後將其傳送到網路。再一次,交易是非同步的,這意味著無論函式被告知在Solidity程式碼中返回什麼,你實際得到的東西總是交易的雜湊。
鑑於交易本身並沒有返回任何有用的資訊,我們需要其他東西。這導致我們採用最後(半)方式與智慧合約進行互動。
事件events
我將事件稱為與智慧合約互動的“一半”方式,因為從技術上講,它們是由交易發出的。 事件是智慧合約以易於閱讀的形式在區塊鏈上記錄事物的方式,它們基本上只是一組可以使用特定交易的收據查詢的值。我們在智慧合約的最頂層定義了一個:
event OpinionBroadcast(address _soapboxer, string _opinion);
然後,當我們使用broadcastOpinion函式時,我們使用它向區塊鏈發出資訊。
將交易新增到塊後,你可以使用交易雜湊查詢區塊鏈以查詢 OpinionBroadcast
事件發出的特定值。這是我們在函式 broadcast_an_opinion
中的最後一點python程式碼。你會注意到我們要求事件發出的資訊儲存在’args’屬性中。
這個事件非常公開。實際上,任何人都可以輕鬆使用Etherscan或類似工具來檢視智慧合約發出的所有事件的日誌。
Etherscan會自動檢測“Transfer”轉移事件並列出所有事件。Nifty
如果你已經做到這一點,你就有權發表意見。繼續用你選擇的意見執行 broadcast_an_opinion
。
如果一切順利進行,你應該很快就會收到已處理的收據,以及已放入區塊鏈的 OpinionBroadcast
事件的列印輸出。
Nice。
這是完整的python程式碼:
import time from web3 import Web3, HTTPProvider contract_address= [YOUR CONTRACT ADDRESS] wallet_private_key= [YOUR TEST WALLET PRIVATE KEY] wallet_address= [YOUR WALLET ADDRESS] w3 = Web3(HTTPProvider([YOUR INFURA URL])) w3.eth.enable_unaudited_features() contract = w3.eth.contract(address = contract_address, abi = contract_abi.abi) def send_ether_to_contract(amount_in_ether): amount_in_wei = w3.toWei(amount_in_ether,'ether'); nonce = w3.eth.getTransactionCount(wallet_address) txn_dict = { 'to': contract_address, 'value': amount_in_wei, 'gas': 2000000, 'gasPrice': w3.toWei('40', 'gwei'), 'nonce': nonce, 'chainId': 3 } signed_txn = w3.eth.account.signTransaction(txn_dict, wallet_private_key) txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction) txn_receipt = None count = 0 while txn_receipt is None and (count < 30): txn_receipt = w3.eth.getTransactionReceipt(txn_hash) print(txn_receipt) time.sleep(10) if txn_receipt is None: return {'status': 'failed', 'error': 'timeout'} return {'status': 'added', 'txn_receipt': txn_receipt} def check_whether_address_is_approved(address): return contract.functions.isApproved(address).call() def broadcast_an_opinion(covfefe): nonce = w3.eth.getTransactionCount(wallet_address) txn_dict = contract.functions.broadcastOpinion(covfefe).buildTransaction({ 'chainId': 3, 'gas': 140000, 'gasPrice': w3.toWei('40', 'gwei'), 'nonce': nonce, }) signed_txn = w3.eth.account.signTransaction(txn_dict, private_key=wallet_private_key) result = w3.eth.sendRawTransaction(signed_txn.rawTransaction) tx_receipt = w3.eth.getTransactionReceipt(result) count = 0 while tx_receipt is None and (count < 30): time.sleep(10) tx_receipt = w3.eth.getTransactionReceipt(result) print(tx_receipt) if tx_receipt is None: return {'status': 'failed', 'error': 'timeout'} processed_receipt = contract.events.OpinionBroadcast().processReceipt(tx_receipt) print(processed_receipt) output = "Address {} broadcasted the opinion: {}"\ .format(processed_receipt[0].args._soapboxer, processed_receipt[0].args._opinion) print(output) return {'status': 'added', 'processed_receipt': processed_receipt} if __name__ == "__main__": send_ether_to_contract(0.03) is_approved = check_whether_address_is_approved(wallet_address) print(is_approved) broadcast_an_opinion('Despite the Constant Negative Press')
打包封裝
所以關於它。正如我所提到的,我們還沒有達到使用python實際部署智慧合約很容易的地步,但其他一切都在那裡。在Sempo,我們正在使用上面提到的所有技術來使問題響應更加透明。
感謝Sebastian Dirman指出w3.toWei(value, ‘ether’)是一種更好的方式在Ether和Wei之間進行轉換——只需將以太量乘以1000000000000000000即可導致型別錯誤!
======================================================================
分享一些以太坊、EOS、比特幣等區塊鏈相關的互動式線上程式設計實戰教程:
- java以太坊開發教程,主要是針對java和android程式設計師進行區塊鏈以太坊開發的web3j詳解。
- python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
- php以太坊,主要是介紹使用php進行智慧合約開發互動,進行賬號建立、交易、轉賬、代幣開發以及過濾器和交易等內容。
- 以太坊入門教程,主要介紹智慧合約與dapp應用開發,適合入門。
- 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
- C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括賬戶管理、狀態與交易、智慧合約開發與互動、過濾器和交易等。
- EOS教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、賬戶與錢包、發行代幣、智慧合約開發與部署、使用程式碼與智慧合約互動等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
- java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Java程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
- php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Php程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
- tendermint區塊鏈開發詳解 ,本課程適合希望使用tendermint進行區塊鏈開發的工程師,課程內容即包括tendermint應用開發模型中的核心概念,例如ABCI介面、默克爾樹、多版本狀態庫等,也包括代幣發行等豐富的實操程式碼,是go語言工程師快速入門區塊鏈開發的最佳選擇。
匯智網原創翻譯,轉載請標明出處。這裡是原文 Python中的以太坊智慧合約開發指南