这将是一个教程,指导Python开发人员学习Web3.py,一个区块链(以太坊)库的基础知识。我们将在Python解释器中做很多这方面的工作。
注意:为了安全起见,我们将通过一个测试网络进行转账。所有这些相同的技术都可以在以太坊主网络上使用。
主要是以下几个步骤:
1. 安装
2. 建立连接
3. 初始化
4. 创建帐户
5. ENS帐户
6. 转账
安装
我们将使用pip从命令行安装web3.py:
$ pip3 install web3
对于同时安装了Python 2和3的用户,您应该检查一下pip命令调用哪个版本。有些默认值为2.7:
$ pip -V
pip 10.0.1 from /Library/Python/2.7/site-packages/pip-10.0.1-py2.7.egg/pip (python 2.7)
$ pip3 -V
pip 20.0.2 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
另外,如果您使用的是virtualenv,则这里有一些有关为Web3.py设置干净环境的文档。
设置连接
要与网络交互,您要么必须托管一个节点,要么使用为您托管一个节点的服务。
因为这是一个基本教程,所以我们将使用一个服务。最受欢迎的是Infura。您可以设置自己的免费帐户(此处的说明),也可以使用下面的产品ID。获得项目ID和API端点至关重要,这将是我们到区块链的API端点,而分析仪表板会有所帮助。
复制并确保在地址前添加https://。
有了这些,就可以使用Python连接到区块链了!
初始化
让我们启动我们的Python解释器。这可能因您的Python安装而异,但通常可以通过运行通常在python文件之前放置的任何关键字来实现。对于安装了Pyton 2和Pyton 3的Mac iTerm来说,它是:
$ python3
Python 3.8.2
[Clang 6.0 (clang-600.0.57)] on darwin
Type “help”, “copyright”, “credits” or “license” for more information.
>>>
要检查我们所有的设置是否正确,请运行您的python解释器和以下命令:
>>> from web3 import Web3, HTTPProvider
>>> import json
上面的命令从web3.py导入了一些主要方法,我们将使用它们来连接到区块链以及本地json库。
接下来,我们将创建一个对象w3,该对象将通过Infura API端点(以https://开头)进行初始化。在本教程的其余部分中,它将成为web3.py与区块链一起工作的主要方式。
>>> w3 = Web3(Web3.HTTPProvider(“https://rinkeby.infura.io/v3/8e4cd4b220fa42d3ac2acca966fd07fa”))
注意:您需要在Infura API地址前面添加HTTPS://,否则会出现错误!
我们还需要添加一些中间件来帮助我们使用Infura和Rinkeby testnet:
>>> from web3.middleware import geth_poa_middleware
>>> w3.middleware_onion.inject(geth_poa_middleware, layer=0)
现在看看一切是否顺利,我们运行:
>>> w3.isConnected()
如果您获得True,那就恭喜!您已连接到区块链!
1. 如果得到False,则可以检查以下几件事:
2. 如果必须重新启动解释器,则必须重新导入库并重新初始化变量
3. 您是否正确复制了Infura API密钥?
4. 您是否已安装web3.py并安装并导入了Web3和HTTPProvider库?
5. 您是否在API密钥前面加上了https://?
创建一个帐户
如果我们想在区块链上汇款,则需要一个以太坊账户。以太坊账户是以太坊区块链上身份的主要单位-账户的地址是在网络上如何识别用户的地址。帐户系统的基础是基于公钥加密的分散身份协议。本质上,区块链网络上的身份通过其公共地址对应方(由整个网络持有)对来自单个私钥(由单个用户秘密持有)的数字签名进行身份验证来确认。虽然它有很大的用户体验障碍,但它确实提供了一个快速的对等身份验证协议。
使用web3.py生成一个在以太坊网络上使用的帐户非常简单。
注意:在接下来的几个步骤中,我将打破一些性规则:
1)我将生成一个熵(随机性)不足的私钥
2)我将在线发布私钥。
在本教程之外,我不会使用此密钥,它只是出于教育目的。您应该始终使用正确的私钥管理,例如Geth或MetaMask,并且永远不要公开共享您的私钥。
>>> my_account = w3.eth.account.create(‘Nobody expects the Spanish Inquisition!’)
>>> my_account._address
‘0x5b580eB23Fca4f0936127335a92f722905286738’
>>> my_account._private_key
HexBytes(‘0x265434629c3d2e652550d62225adcb2813d3ac32c6e07c8c39b5cc1efbca18b3’)
上面的命令使用输入的字符串生成my_account对象,该对象包含一个私钥(my_account._private_key)及其关联的以太坊地址(my_account._address)。但是,由于已公开发布该密钥,因此有人可以生成和使用相同的私钥。
因此,用户通常将私钥创建和管理委托给称为客户端(如Geth)或钱包(如MetaMask)的软件。这些项目提供了一种非常安全的方式来生成和处理用于区块链交互的私钥。
ENS帐户
以太坊地址是长的十六进制数。它们几乎不可能输入或记住,所以以太坊社区创建了以太坊名称系统(ENS)。它的好处与域名系统相同,它将网站服务器号(216.58.194.46)替换为人类可读的名称(谷歌). 大多数ENS名称使用.eth域,而不是.com域。
例如我有一个以太坊帐户,地址为0x4d3dd8471a289E820Aa9E2Dc5f437C1b2E22F598,但是我使用ENS将更易读的名称coogan.eth映射到该地址。如果您在支持ENS的应用或项目中键入这些内容(例如web3.py!),它将替换为以太坊十六进制地址。不幸的是,我们无法在本教程中使用它,因为.eth域名只能在主网上使用,但可能在下一教程中使用!
转账
在最后一节中,我们将从刚刚创建的帐户中向另一个以太坊帐户发送一些钱。全部来自python解释器!
加密货币美元的价格极不稳定。这引起了企业的犹豫——为什么你会接受一种价格不确定的货币?
但是,在以太坊生态系统中建立了一种新颖的解决方法。以太坊被称为“世界计算机”,因为它是一个分布式系统,允许开发人员上传并执行自己的代码。以这种方式上传到以太坊的代码称为智能合约。将其上传到网络后,它将成为一个独立的实体,具有自己的地址,内存存储和网络访问权限。智能合约催生了一个丰富的以太坊开发者社区,该社区既富有创造力又不懈地应对各种挑战,例如区块链固有的价格波动。
以太坊创建稳定的价值来源,一群开发人员编写了名为Dai的代码并将其上载到以太坊。这是一个数字令牌,始终价值约1美元。持有Dai代币后,我们可以与其他用户以与美元相同的汇率兑换它。我们现在就要做!
合约实例化
首先要与开发人员上传到以太坊的代码进行交互,我们需要知道上传的代码公开了哪些方法。Web3.py本机知道如何与核心的以太坊软件对接,但是需要与第三方代码交互的指导。我们通过为Web3.py提供应用程序二进制接口(ABI)提供该指南。与应用程序编程接口(API)相似,ABI允许我们的机器知道对我们可用的功能以及这些功能需要的参数。ABI在区块链上不可用,由开发人员在Github或Etherscan等网站上提供。
下面是我们将要使用的testnet Dai ABI,请单击并复制整个代码片段:
注意:此代码很长,请特别注意完整复制!
abi = ‘[{“constant”:true,”inputs”:[],”name”:”name”,
“outputs”:[{“internalType”:”string”,”name”:””,
“type”:”string”}],”payable”:false,”stateMutability”
:”view”,”type”:”function”},{“constant”:false,”inputs”
:[{“internalType”:”address”,”name”:”spender”,”type”
:”address”},{“internalType”:”uint256″,”name”:”value”
,”type”:”uint256″}],”name”:”approve”,”outputs”:
[{“internalType”:”bool”,”name”:””,”type”:”bool”}],
“payable”:false,”stateMutability”:”nonpayable”,”type”
:”function”},{“constant”:true,”inputs”:[],”name”:
“totalSupply”,”outputs”:[{“internalType”:”uint256″,
“name”:””,”type”:”uint256″}],”payable”:false,”stateMutability”
:”view”,”type”:”function”},{“constant”:false,”inputs”:
[{“internalType”:”address”,”name”:”from”,”type”:”address”},
{“internalType”:”address”,”name”:”to”,”type”:”address”},
{“internalType”:”uint256″,”name”:”value”,”type”:”uint256″}],
“name”:”transferFrom”,”outputs”:[{“internalType”:”bool”,
“name”:””,”type”:”bool”}],”payable”:false,”stateMutability”
:”nonpayable”,”type”:”function”},{“constant”:true,”inputs”:[],
“name”:”decimals”,”outputs”:[{“internalType”:”uint8″,”name”:
“”,”type”:”uint8″}],”payable”:false,”stateMutability”:
“view”,”type”:”function”},{“constant”:false,”inputs”:
[{“internalType”:”address”,”name”:”spender”,”type”:”address”},
{“internalType”:”uint256″,”name”:”addedValue”,”type”:”uint256″}],
“name”:”increaseAllowance”,”outputs”:[{“internalType”:”bool”,
“name”:””,”type”:”bool”}],”payable”:false,”stateMutability”:
“nonpayable”,”type”:”function”},{“constant”:false,”inputs”:
[{“internalType”:”address”,”name”:”to”,”type”:”address”},
{“internalType”:”uint256″,”name”:”value”,”type”:”uint256″}],
“name”:”mint”,”outputs”:[{“internalType”:”bool”,”name”:””,
“type”:”bool”}],”payable”:false,”stateMutability”:”nonpayable”,
“type”:”function”},{“constant”:true,”inputs”:[{“internalType”:
“address”,”name”:”owner”,”type”:”address”}],”name”:”balanceOf”,
“outputs”:[{“internalType”:”uint256″,”name”:””,”type”:”uint256″}],
“payable”:false,”stateMutability”:”view”,”type”:”function”},
{“constant”:true,”inputs”:[],”name”:”symbol”,”outputs”:[{
“internalType”:”string”,”name”:””,”type”:”string”}],”payable”:false,
“stateMutability”:”view”,”type”:”function”},{“constant”:false,
“inputs”:[{“internalType”:”address”,”name”:”spender”,
“type”:”address”},{“internalType”:”uint256″,”name”:”subtractedValue”,
“type”:”uint256″}],”name”:”decreaseAllowance”,”outputs”:[{
“internalType”:”bool”,”name”:””,”type”:”bool”}],”payable”:false,
“stateMutability”:”nonpayable”,”type”:”function”},{
“constant”:false,”inputs”:[{“internalType”:”address”,”name”:”to”,
“type”:”address”},{“internalType”:”uint256″,”name”:”value”,
“type”:”uint256″}],”name”:”transfer”,”outputs”:[{
“internalType”:”bool”,”name”:””,”type”:”bool”}],
“payable”:false,”stateMutability”:”nonpayable”,
“type”:”function”},{“constant”:true,”inputs”:[{
“internalType”:”address”,”name”:”owner”,”type”:”address”},
{“internalType”:”address”,”name”:”spender”,”type”:”address”}],
“name”:”allowance”,”outputs”:[{“internalType”:”uint256″,
“name”:””,”type”:”uint256″}],”payable”:false,”stateMutability”:”view”,
“type”:”function”},{“inputs”:[],”payable”:false,
“stateMutability”:”nonpayable”,”type”:”constructor”},
{“anonymous”:false,”inputs”:[{“indexed”:true,”internalType”:”address”
,”name”:”from”,”type”:”address”},{“indexed”:true,
“internalType”:”address”,”name”:”to”,”type”:”address”},
{“indexed”:false,”internalType”:”uint256″,”name”:”value”,
“type”:”uint256″}],”name”:”Transfer”,”type”:”event”},
{“anonymous”:false,”inputs”:[{“indexed”:true,”internalType”:”address”,
“name”:”owner”,”type”:”address”},{“indexed”:true,
“internalType”:”address”,”name”:”spender”,”type”:”address”},
{“indexed”:false,”internalType”:”uint256″,”name”:”value”,
“type”:”uint256″}],”name”:”Approval”,”type”:”event”}]’
我们需要使用json进行解析:
>>> abi = json.loads(abi)
我们还需要告诉web3.py在以太坊网络的哪里可以找到这些代码。我们使用以下代码:
>>> address = ‘0xc3dbf84Abb494ce5199D5d4D815b10EC29529ff8’
然后,我们使用ABI和地址实例化智能合约对象。这将使我们能够访问代码公开的函数:
>>> dai = w3.eth.contract(address=address, abi=abi)
为了测试我们是否已正确实例化合同,我们将调用一个函数,该函数告诉我们该合约持有多少Dai:
>>> dai.functions.totalSupply().call()
10100000000000101001376883458034812485564519
(运行此程序时,余额可能会有所不同)
建立交易
要从我们的西班牙查询帐户(my_account)转移Dai,我们将使用Dai智能合约的转移功能,如下所示:
我们可以看到我们需要向合约传递两个参数:to,这将是十六进制的以太坊地址和值,即uint256。对于经验丰富的开发人员来说,处理256位无符号整数(uint256)可能是一个挑战。Web3.py提供了一种方法,可以将值从整数转换为此智能合约所需的格式toHex。我们发送10个Dai,并且由于我们要发送的数量少于16个,因此我们只需在其前面放置一个0x。对于地址,请输入您要寄给Dai的地址。
因此,我们的交易当前如下所示:
transaction = dai.functions.transfer(TO_ADDRESS, 0x10)
这些参数对于Dai合约很有用(我们不会在此处出现错误),但是我们需要更多参数以使交易在以太坊网络上运行。这些值是chainId,gas和nonce。
.buildTransaction({‘chainId’: 4,
‘gas’:70000,
‘nonce’: w3.eth.getTransactionCount(my_account._address)})
ChainId帮助web3.py知道将事务发送到哪个网络。不同的网络有不同的怪事(正如我们在一开始为Rinkeby安装中间件时所看到的),这有助于web3.py正确捆绑交易。Rinkeby的网络ID为4,这是网络ID的完整列表。
Gas是您向网络上的矿工支付的用于支付交易费用的小笔款项。
Nonce是每个以太坊账户专用的全局变量。它的作用与账本底部的数字相同:它允许对来自不同帐户的付款进行正确排序。发送每笔交易后,它递增一。Web3.py有一种查找地址当前随机数的方法:w3.eth.getTransactionCount(ETHEREUM_ADDRESS)。
我们将使用web3.py方法.buildTransaction将这三个变量合并到我们的交易中。我还要添加朋友的以太坊地址,并寄给他10Dai:
>>> transaction = dai.functions.transfer(
‘0xafC2F2bBD4173311BE60A7f5d4103b098D2703e8’,
0x10).buildTransaction({‘chainId’: 4, ‘gas’:70000,
‘nonce’: w3.eth.getTransactionCount(
‘0x5b580eB23Fca4f0936127335a92f722905286738’)})
签署和发送交易
现在我们有了交易,我们需要使用私钥对其进行签名。这就是以太坊的点对点协议如何知道要汇款的账户。要进行签名,我们将交易对象和my_account._private_key放入以下函数中:
>>> signed_txn = w3.eth.account.signTransaction(
transaction,
‘0x265434629c3d2e652550d62225adcb2813d3ac32c6e07c8c39b5cc1efbca18b3’)
通过已签名的交易,我们现在要做的就是通过Infura API端点将其发送到网络。我们使用以下命令通过w3对象执行此操作:
>>> txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
如果成功的话,恭喜!您刚刚使用Python进行汇款了!
要查找您的交易,您可以打印txn_hash并将字符串值带到Einscan for Rinkeby。这是我的哈希值(您会有所不同!):
>>> txn_hash
HexBytes(
‘0xc5f98cbe6f1eaef16916b148e6c4ae926b11ab9dde750e188362745da39d560e’)
如您所见,使用web3.py为您的应用程序提供了各种可能性。