当前位置: 首页 > 科技观察

通过web3.py用Python访问以太坊

时间:2023-03-21 16:37:26 科技观察

如果想通过Python访问以太坊,可以从以太坊官方Github上看到有两个模块可以实现:web3.py和pyethereum。就我目前的理解,两者的区别在于web3.py主要是作为外部访问以太坊的客户端,也就是说web3.py函数库本身不会成为区块链节点,也不会进行区块链同步,而是连接到区块链上的一个节点,将区块链作为外部数据库;而pyethereum更像是geth,用来让自己成为区块链节点,正常进行Block同步,也可以作为矿工开始挖矿。在这篇文章中,因为我想要一个轻量级的客户端与区块链进行交互,不想准备一个庞大的存储空间来存储区块链的数据,所以我将重点放在web3.py上。配置web3.py执行环境通过web3.py连接以太坊节点访问区块链上的ERC20合约签名并发送交易配置web3.py执行环境web3.py可以直接通过pip安装。需要注意的是,pipinstallweb3在Windows上安装时需要提前安装VisualC++Builder,否则会在安装的最后阶段因为无法编译而失败。通过web3.py链接以太坊节点web3.py本身并不作为区块链节点存在,因此需要一个节点来访问区块链上的数据。一般来说,最安全的方式是使用geth或者parity自己搭建节点,但是如果不想自己搭建节点,可以考虑infura提供的HTTP节点服务。以infura目前的API为例,如果要连接Ropsten测试链,链接地址为https://ropsten.infura.io/v3/api_key,其中的api_key需要注册一个账号才能获取。下面程序模仿web3.py中内置auto.infura的方法,从环境变量中读取INFURA_API_KEY参数,形成infura.io的HTTP地址,用于与Ropsten测试链建立连接。访问区块链上的ERC20合约importosfromweb3import(HTTPProvider,Web3,)INFURA_ROPSTEN_BASE_URL='https://ropsten.infura.io/v3'defload_infura_url():key=os.environ.get('INFURA_API_KEY','')return"%s/%s"%(INFURA_ROPSTEN_BASE_URL,key)w3=Web3(HTTPProvider(load_infura_url()))在开始访问合约之前,我们需要先说一下什么是ABI。在以太坊中,由于合约是以编译好的二进制代码的形式存在,函数库没有办法直接知道合约转账的内容是什么,因为合约的返回值都是二进制的。因此,在操作合约之前,需要提供一个ABI文件,告诉库如何使用合约。#假设我们要调用的合约是一个标准的ERC20合约。withopen("erc20.abi.json")asf:erc20_abi=json.load(f)#Web3只接受校验和地址。因此,在访问相应的合约之前,我们应该确保给定的地址是一个#checksum地址。contract_addr=w3.toChecksumAddress('0x4e470dc7321e84ca96fcaedd0c8abcebbaeb68c6');erc20_contract=w3.eth.contract(address=contract_addr,abi=erc20_abi)forfuncinerc20_contract.all_functions():logger.debug('contractfunctions:%s',func)logger.debug("代币名称:%s",erc20_contract.functions.name().call())这里假设我们要访问Ropsten测试链上的地址是0x4e470dc7321e84ca96fcaedd0c8abcebbaeb68c6的智能合约。该合约是通过etherscan随机发现的ERC20合约,因此可以通过标准的ERC20ABI访问。当我们创建这个合约的实例时,我们首先运行一个循环来打印出合约中的所有函数(这一步实际上是列出ABI上的信息),然后尝试调用合约中的name()来获取合约声明代币的名称。最后输出的内容如下:2018-09-0715:02:53,815|__主要__|调试|合约函数:2018-09-0715:02:53,816|__主要__|调试|合约函数:2018-09-0715:02:53,824|__主要__|调试|合约函数:2018-09-0715:02:53,824|__主要__|调试|合约函数:2018-09-0715:02:53,824|__主要__|调试|合约函数:2018-09-0715:02:53,824|__主要__|调试|合约函数:2018-09-0715:02:53,824|__主要__|调试|合约函数:2018-09-0715:02:53,825|__主要__|调试|合约函数:2018-09-0715:02:53,825|__主要__|调试|合同功能:<功能津贴(地址,地址)>2018-09-0715:02:54,359|__主要__|调试|代币名称:KyberNetworksignalandsendthetransaction在上面的例子中,在调用智能合约时,直接调用了合约中的函数,但是这个只能用于读取区块,如果要向区块链写入数据,可以通过调用智能合约,必须用另一种方式调用合约,即先签署交易,然后支付gas执行交易。假设我们还想调用一个ERC20合约并在合约上执行transferFrom()函数。transferFrom()需要三个参数_from、_to、_value,表示从_from账户转账到_to账户,转账金额为_value。#设置进行交易的账户。account=w3.toChecksumAddress(os.environ.get('ETHEREUM_ACCOUNT',''))w3.eth.defaultAccount=account#Web3只接受校验和地址。因此,在访问相应的合约之前,我们应该确保给定的地址是一个#checksum地址。contract_address=w3.toChecksumAddress('0x4e470dc7321e84ca96fcaedd0c8abcebbaeb68c6')contract=w3.eth.contract(address=contract_address,abi=contract_abi)#准备在区块链上进行交易所需的参数。estimate_gas=contract.functions.transferFrom(account,account,w3.toWei('1','eth')).estimateGas()nonce=w3.eth.getTransactionCount(account)#构建交易。txn=contract.functions.transferFrom(account,account,w3.toWei('1','eth')).buildTransaction({'chainId':3,'gas':estimate_gas,'gasPrice':w3.toWei('1','gwei'),'nonce':随机数})logger.debug('Transaction:%s',txn)#签署交易。private_key=bytes.fromhex(os.environ.get('ETHEREUM_ACCOUNT_PKEY',''))signed_txn=w3.eth.account.signTransaction(txn,private_key=private_key)tx_hash=w3.eth.sendRawTransaction(signed_txn.rawTransaction)记录器。debug('Txhash:0x%s',bytes.hex(tx_hash))上面程序中,前2~3行是从环境变量中读取我们要使用的账号。该帐号将用于发送交易。当然,在支付gas的时候,第10行到第20行会从这个账户中扣除,创建一个原始交易(rawtransaction)。在这个交易中,我们需要指定包含的Gas,nonce等参数,所以你需要在前11到12行确认你要设置多少个参数。然后最重要的25-26行读取私钥并使用私钥对交易进行签名。这里假设私钥将由十六进制编码的文本组成,因此使用bytes.fromhex将十六进制编码转换回字节格式。签名后,发送交易。发送交易时,API会以字节格式返回交易的交易哈希,可以编码打印出来,然后可以在etherscan上搜索交易。