Python Web3.0应用开发2022

Posted 新缸中之脑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python Web3.0应用开发2022相关的知识,希望对你有一定的参考价值。

在本文中,我们将讨论如何使用 Python 编写 Web 3 dapp。我们使用web3.py库,它支持使用 Python 与以太坊区块链进行交互。

用熟悉的语言学习 Web3.0开发Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart

使用 javascript 开发web dapp 时,可以方便地将应用程序与 MetaMask 集成,MetaMask 包含用户在以太坊区块链上拥有的各种帐户。当需要执行交易时,dapp 将依赖 MetaMask 对交易进行签名。在幕后,MetaMask 连接到一个名为Infura的节点。Infura 是一个连接到以太坊区块链的完整节点。它为应用程序连接到以太坊区块链提供了一种简单的方法,而无需开发人员设置自己的节点,这可能非常昂贵并且需要大量的努力。下图展示了 dapp、MetaMask、Infura 和以太坊区块链之间的流程:

Web3.py 受到 web3.js 的启发,因此可以找到许多类似web3.js中看到的功能。要安装web3.py,请在 Jupyter Notebook 中键入以下命令:

!pip install web3

如果你正在开发 Python dapp,则无法连接到 MetaMask 来访问你的帐户并使用它来签署的交易。相反,你需要在自己的帐户中导入,签署自己的交易,然后自己将其连接到 Infura,如下图所示:

1、注册 Infura

现在我们已经了解了Python dapp 将如何工作,让我们首先在https://infura.io. 注册一个免费帐户:

验证电子邮件后,你将能够登录Infura。创建第一个项目(确保在 PRODUCT 下选择Ethereum)并为项目命名:

现在,你将获得项目 ID、项目密码以及应用程序要连接的端点。对于本文,选择ROPSTEN作为端点:

特别注意的是,复制端点 URL:

https://ropsten.infura.io/v3/<Project_ID>

2、连接到 Web3 提供程序 (Infura)

获得 Infura 端点 URL 后,让我们尝试看看是否能够使用web3.py库连接它:

from web3 import Web3
w3 = Web3(Web3.HTTPProvider(
    'https://ropsten.infura.io/v3/<Project_ID>'))
w3.isConnected()

请务必将Project_ID替换为自己的。

如果看到True输出,则表明已成功连接到Infura。

如果收到有关bitarray版本的错误,请执行以下安装:

!pip install bitarray==1.2.1

3、获取以太坊区块

让我们尝试从 Ropsten 测试网络中获取特定的块:

w3.eth.get_block(12345)

你将看到以下内容:

AttributeDict('difficulty': 39828207,
 'extraData': HexBytes('0xd883010502846765746887676f312e372e338664617277696e'),
 'gasLimit': 4712388,
 'gasUsed': 0,
 'hash': HexBytes('0x8856ffd33791223a229e69910b1157cda0029da204fd2eddbc7f4293ff2ec3c6'),
 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'),
 'miner': '0x0E032D12cBcf5F078b855ea4B4Cd44D357A6B96C',
 'mixHash': HexBytes('0x716db118261307e8ae4e77b70fbb0e9e3f8f39e59eee59996689aad7fdbe469a'),
 'nonce': HexBytes('0x4026c896934436f6'),
 'number': 12345,
 'parentHash': HexBytes('0x77d612c3b20ff8fd7ad919103ab3341e3b959c935f70857e37203af1a0fd8ea5'),
 'receiptsRoot': HexBytes('0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'),
 'sha3Uncles': HexBytes('0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347'),
 'size': 539,
 'stateRoot': HexBytes('0x90032c7b5bb5611c55065a040561930d33d7be74513558cc78f1962278678b54'),
 'timestamp': 1479743393,
 'totalDifficulty': 121127119632,
 'transactions': [],
 'transactionsRoot': HexBytes('0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'),
 'uncles': [])

如果要查看最新块的内容,请使用:

w3.eth.get_block('latest')

4、设置以太坊帐户

现在让我们设置我们的以太坊账户。我将使用之前在 MetaMask 中创建的两个帐户。由于需要在 Python 程序中加载私钥,因此建议将私钥存储在环境文件中,这样就不会在 Python 代码中公开它们。为此,我将安装python-dotenv模块:

!pip install python-dotenv

安装python-dotenv模块后,创建一个名为.env的文件并将其保存在与 Jupyter 笔记本相同的目录中。使用以下内容填充它:

account1_private_key = '<private_key_of_account1>'

要获取账户 1 的私钥,请转到 MetaMask 并按照下图中列出的步骤进行操作:

获得私钥后,可以将其粘贴到.env文件中。

接下来,使用以下代码段设置帐户 1 和 2 的详细信息:

from dotenv import load_dotenv
load_dotenv()
import os
# Account 1
account1_address = '0xB35b89eE8AAc5C3ea6cd5C9080E8c66Cb17ca2CC'
account1_private_key = os.environ.get('account1_private_key')
# Account 2
account2_address = '0x1cc025d9A1741b51FD5dE6003884dc264F149AdC'

由于我稍后只使用账户 1 签署我的交易,因此只需要加载账户 1 的私钥。

请务必将0xB35b89eE8AAc5C3ea6cd5C9080E8c66Cb17ca2CC0x1cc025d9A1741b51FD5dE6003884dc264F149AdC
分别替换为你的Account 1和Account 2的地址。

我还将假设你的账户 1 和账户 2 在 Ropsten 测试网络中已经有一些以太币。如果没有,请使用 MetaMask 从Faucet 获取一些测试币。

5、获取账户余额

现在让我们检查账户 1 的余额:

w3.eth.get_balance(account1_address)

目前我有 6.2468 ETH,所以我得到如下输出(以 Wei 为单位):

6246772509923908581

6、在账户之间转移以太币

现在让我们将 1 ETH 从账户 1 转移到账户 2。这是我们学习如何使用 web3.py 执行交易的好机会。

为此:

  • 首先使用eth.get_transaction_count()函数获取从指定账户发送的交易数量。这将用作交易的随机数。
  • 然后,创建一个包含交易详情的字典:
nonce = w3.eth.get_transaction_count(account1_address)
tx = 
    'nonce': nonce,                      # transaction count
    'to': account2_address,              # who to send the ETH to
    'value': w3.toWei(1, 'ether'),       # the amount to transfer
    'gasPrice': w3.eth.gas_price,        # get the price of gas
  

在上面,我将 1 ETH 从账户 1 转移到账户 2。我使用eth.gas_price属性来获取当前的 gas 价格。

接下来,使用eth.estimate_gas()函数估计此交易需要多少gas,然后将金额插入交易字典:

gas = w3.eth.estimate_gas(tx) 
tx['gas'] = gas 
print(tx)

你应该看到如下交易:

 
  'nonce':371,
  'to':'0x1cc025d9A1741b51FD5dE6003884dc264F149AdC',
  'value':1000000000000000000,
  'gasPrice':2159166649,
  'gas':21000 

现在可以使用以下eth.account.sign_transaction()函数签署交易:

signed_tx = w3.eth.account.sign_transaction(tx,account1_private_key)

要将交易发送到 Infura,请使用以下eth.send_raw_transaction()函数:

tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
print(w3.toHex(tx_hash))

该函数将返回一个哈希值,如下所示:

0x369e102bfa26006e6035db942b6d8bc7b361624e9b323ccce4b16f78f58ecfc0

交易需要一些时间来确认。如果要等待事务完成,请使用eth.wait_for_transaction_receipt()函数(这是一个阻塞调用):

receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

最后,为了验证转账确实正确执行,检查账户 1 和 2 的余额:

print(w3.eth.get_balance(account1_address))
print(w3.eth.get_balance(account2_address))

将 1 ETH 从账户 1 转移到账户 2 的整个代码片段如下所示:

nonce = w3.eth.get_transaction_count(account1_address)
tx = 
    'nonce': nonce,
    'to': account2_address,
    'value': w3.toWei(1, 'ether'),
    'gasPrice': w3.eth.gas_price,

gas = w3.eth.estimate_gas(tx)
tx['gas'] = gas
signed_tx = w3.eth.account.sign_transaction(tx,account1_private_key)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
print(w3.toHex(tx_hash))
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

7、与智能合约交互

我们想要对 web3.py 库做的更重要的事情是与智能合约进行交互。为此,我将参考在上一篇文章中已经部署到 Ropsten 测试网上的两个智能合约。为了方便起见,我将在这里复制两个智能合约。

7.1 第一个合约

这是第一个智能合约的代码:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8;contract ProofOfExistence 
  //---store the hash of the strings---
  mapping (bytes32 => bool) private proofs;
  //--------------------------------------------------
  // Store a proof of existence in the contract state
  //--------------------------------------------------
  function storeProof(bytes32 proof) private 
    // use the hash as the key
    proofs[proof] = true;
  
  
  //----------------------------------------------
  // Calculate and store the proof for a document
  //----------------------------------------------
  function notarize(string memory document) public 
    // call storeProof() with the hash of the string
    storeProof(proofFor(document));
  
  
  //--------------------------------------------
  // Helper function to get a document's sha256
  //--------------------------------------------
  // Takes in a string and returns the hash of the string
  function proofFor(string memory document) private pure 
  returns (bytes32) 
    // converts the string into bytes array and then hash it
    return sha256(bytes(document));
  
  
  //----------------------------------------
  // Check if a document has been notarized
  //----------------------------------------
  function checkDocument(string memory document) public view 
  returns (bool)
    // use the hash of the string and check the proofs mapping
    // object
    return proofs[proofFor(document)];
  

这个合约:

  • 允许你使用notarize()函数对字符串进行公证
  • 允许你检查字符串之前是否使用checkDocument()函数进行了公证。

由于合约已经部署,让我们通过将合约的地址和 ABI 传递给eth.contract()函数来创建对它的引用:

address = '0x5AE4fCa41f2DCA381E6a5211e368d0181A465acf'
abi = '[     "inputs": [         "internalType": "string",     "name": "document",     "type": "string"       ],   "name": "checkDocument",   "outputs": [         "internalType": "bool",     "name": "",     "type": "bool"       ],   "stateMutability": "view",   "type": "function"  ,     "inputs": [         "internalType": "string",     "name": "document",     "type": "string"       ],   "name": "notarize",   "outputs": [],   "stateMutability": "nonpayable",   "type": "function"   ]'
notarizer = w3.eth.contract(address = address, abi = abi)

现在notarizer包含对合约的引用。

要对字符串进行公证,请执行以下操作:

string_to_notarise = "Ofenbach — You Don't Know Me"
nonce = w3.eth.get_transaction_count(account1_address)
# estimate the gas fee
estimated_gas = \\
    notarizer.functions.notarize(string_to_notarise).estimateGas()
# build the transaction
transaction = \\
  notarizer.functions.notarize(string_to_notarise).buildTransaction(
    
        'gas': estimated_gas,
        'gasPrice': w3.eth.gas_price,
        'from': account1_address,
        'nonce': nonce
    )
# sign the transaction
signed_txn = w3.eth.account.sign_transaction(transaction, 
             private_key = account1_private_key)
# send the transaction
tx_hash = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
print(w3.toHex(tx_hash))
# wait for the transaction to confirm
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

在上面代码中:

  • 要从合约中调用notarize()函数,请使用notarizer.functions.notarize()
  • 使用estimateGas()函数来估计调用notarize()合约函数所需的gas
  • 使用buildTransaction()函数来构建交易以对字符串进行公证。
  • 使用eth.sign_transaction()函数签署交易
  • 使用eth.send_raw_transaction()函数发送交易
  • 使用eth.wait_for_transaction_receipt()函数等待交易确认

确认交易后,可以像这样调用checkDocument()函数:

# check if string is notarized correctly
notarizer.functions.checkDocument(string_to_notarise).call()

它应该返回一个True值。

7.2 第二个合约

第二个合约稍微复杂一些:

  • 只有合约的所有者才能对字符串进行公证
  • 调用checkDocument()函数时,调用者必须支付 100 wei 除了 gas 费。
  • checkDocument()函数的结果通过一个名为Document 的事件返回。

完整的合约如下图:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8;contract ProofOfExistence 
  address owner = msg.sender;
  //---define an event---
  event Document(address from, string text, bool valid);
  //---store the hash of the strings---  
  mapping (bytes32 => bool) private proofs;
  //--------------------------------------------------
  // Store a proof of existence in the contract state
  //--------------------------------------------------
  function storeProof(bytes32 proof) private 
    // use the hash as the key
    proofs[proof] = true;
  
  
  //----------------------------------------------
  // Calculate and store the proof for a document
  //----------------------------------------------
  function notarize(string memory document) public 
    require(msg.sender == owner, 
      'Only the owner of this contract can notarize a string');
    // call storeProof() with the hash of the string
    storeProof(proofFor(document));
  
  
  //--------------------------------------------
  // Helper function to get a document's sha256
  //--------------------------------------------
  // Takes in a string and returns the hash of the string
  function proofFor(string memory document) private pure 
  returns (bytes32) 
    // converts the string into bytes array and then hash it
    return sha256(bytes(document));
  
  
  //----------------------------------------
  // Check if a document has been notarized
  //----------------------------------------
  function checkDocument(string memory document) public payable 
    require(msg.value == 100 wei, 
      'This service requires a fee of 100 wei');
    // transfer the money received to the owner
    payable(owner).transfer(msg.value);
    // fire the Document event to return the result
    emit Document(msg.sender, document, proofs[proofFor(document)]);
  

7.3 加载合约

让我们使用其地址和 ABI 加载合约:

address = '0xF620e7eFb991498d72b95a3e66D912f91B4D6Ba7'
abi = '[  "anonymous": false, "inputs": [  "indexed": false, "internalType": "address", "name": "from", "type": "address" ,  "indexed": false, "internalType": "string", "name": "text", "type": "string" ,  "indexed": false, "internalType": "bool", "name": "valid", "type": "bool"  ], "name": "Document", "type": "event" ,  "inputs": [  "internalType": "string", "name": "document", "type": "string"  ], "name": "checkDocument", "outputs": [], "stateMutability": "payable", "type": "function" ,  "inputs": [  "internalType": "string", "name": "document", "type": "string"  ], "name": "notarize", "outputs": [], "stateMutability": "nonpayable", "type": "function"  ]'
notarizer = w3.eth.contract(address=address, abi=abi)

7.4 公正字符串

使用第二个合约对字符串进行公证与第一个合约类似。唯一需要记住的是,只有合约的所有者才能调用notarize()函数。

在第二个合约中调用checkDocument()函数更有趣。

要对字符串进行公证,需要执行以下其他操作:

  • 估计调用checkDocument()函数需要多少gas费用,加上100wei的值(函数需要这个数量)
  • 在交易中包含要发送到checkDocument()函数的金额(100 wei)
  • 创建Document事件的实例
  • 要监听Document事件,需要实现自己的循环机制。在这里,我首先使用w3.eth.filter()函数来监听合约中的特定事件。 然后,我使用了一个无限循环,使用事件过滤器的get_new_entries()函数继续监听事件。当接收到一个事件时,可以通过它的transactionHash属性 获取事件的详细信息。使用此事务哈希,可以调用事件的processReceipt()函数来获取事件的详细信息。就我而言,一旦获取Document事件,我将停止 监听未来的事件。
string_to_check = "Ofenbach — You Don't Know Me"
nonce = w3.eth.getTransactionCount(account1_address)
# estimate the gas fee
estimated_gas = notarizer.functions.checkDocument(
                    string_to_check).estimateGas(
                    'value':100)  # 100 is the wei to send
# build the transaction
transaction = notarizer.functions.checkDocument(
    string_to_check).buildTransaction(
    
        'gas': estimated_gas,
        'gasPrice': w3.eth.gas_price,
        'from': account1_address,
        'nonce': nonce,
        'value': w3.toWei(100, 'wei'),    # amount to send to the 
    )                                    # function
# sign the transaction
signed_txn = w3.eth.account.sign_transaction(transaction, 
             private_key = account1_private_key)
# send the transaction
tx_hash = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
print(w3.toHex(tx_hash))
import time
# create an instance of the event
document_event = notarizer.events.Document()
def handle_event(event):
    receipt = \\
       w3.eth.wait_for_transaction_receipt(event['transactionHash'])
    result = document_event.processReceipt(receipt)
    # print the content of the Document event
    print(result[0]['args'])
    if result[0]['args']['from'] == account1_address:        
        return True
    return False
def log_loop(event_filter, poll_interval):
    while True:
        for event in event_filter.get_new_entries():
            result = handle_event(event)
            if result == True: 
                return
            time.sleep(poll_interval)
block_filter = w3.eth.filter(
    
        'fromBlock':'latest', 
        'address':address               # address of contract
    )
log_loop(block_filter, 2)

当运行上述代码时,将在Document事件触发时看到如下输出:

AttributeDict('from': '0xB35b89eE8AAc5C3ea6cd5C9080E8c66Cb17ca2CC', 'text': "Ofenbach — You Don't Know Me", 'valid': True)

8、结束语

总的来说,使用 Python 和 web3.py 构建一个 web 3 dapp 类似于使用 web3.js 构建一个。关键区别在于,对于 Python dapp,需要自己熟悉交易—— 签署交易、估算所需的 gas 费用、设置 gas 价格,然后等待交易确认并处理触发的事件。


原文链接:Python Web3.0应用开发 — 汇智网

以上是关于Python Web3.0应用开发2022的主要内容,如果未能解决你的问题,请参考以下文章

Web3.0 博客DApp开发实战2022

2022年创造被动收入:Web 3.0的顶级节点

浙大链协Web3.0系列Workshop启动!

Web3.0 区块链 互联网架构演进 AI防范电信诈骗 QCon 大会2022

Web3.0 区块链 互联网架构演进 AI防范电信诈骗 QCon 大会2022

Web3.0 区块链 互联网架构演进 AI防范电信诈骗 QCon 大会2022