从Python角度了解以太坊

开发 后端
Web3.py是一个用于与以太坊网络交互的Python库,它封装了很多操作,便于我们进行交易、与智能合约交互、读取区块中的数据等等。

  [[431026]]

区块链基础

以太坊(Ethereum)的底层是区块链技术,而区块链简单而言就是由Hash值串联起来的链表结构,链表中的节点会记录交易信息,如果将节点中的信息以JSON格式描述,看起来是这个样子:

  1.    "number": 1234567, 
  2.    "hash""0xabc123..."
  3.    "parentHash""0xdef456..."
  4.    "miner""0xa1b2c3..."
  5.    ..., 
  6.    "transactions": [...] 
  • hash:当前节点的Hash值
  • parentHash:前一个节点的Hash值
  • miner:矿工地址
  • transactions:当前节点包含的交易数据

矿工接收交易数据后,会将其封装到一个区块里,并将区块信息广播到以太坊网络中,这里会有很多细节,建议找本书看看,这里想强调的是,想让矿工干活,需要花钱,在以太坊上,其货币称为「ether」。

使用Web3.py

Web3.py是一个用于与以太坊网络交互的Python库,它封装了很多操作,便于我们进行交易、与智能合约交互、读取区块中的数据等等。

Web3.py官方文档:https://web3py.readthedocs.io/en/stable/index.html

通过一张图,可以很清晰的知道我们开发的应用、Web3.py以及是以太坊网络的关系。

从上图可知,Web3.py其实就是中间层,它可以通过HTTP、IPC(进程间通信)、WebSocket的方法连接到以太坊节点,从而实现与整个以太坊网络的交互。

使用前,我们需要安装Web3.py:

  1. pip install web3 
  2. pip install 'web3[tester]' 

安装web3[tester]的目的时,使用Web3.py提供的模拟节点进行测试,如果我们要同步真正的节点需要做:

  • 1.下载Geth构建以太坊节点。
  • 2.启动Geth并等待它同步以太坊网络中的数据,Geth默认会启动HTTP服务,端口为8545.
  • 3.使用Web3.py通过HTTP连接到刚刚构建好的节点。
  • 4.使用Web3.py提供的API与节点进行交互

Geth是使用Go实现Ethereum协议的程序,与之类似的还是使用C++或Python实现的,只是Geth势头第一。

同步过程需要拉取数据,可能需要几个小时。

这里只是演示,所以直接使用模拟节点就好了,如下图:

上图中,Web3.py提供了4种接入以太坊节点的方式,其中第4种便是通过TesterProvider接入模拟的以太坊节点,要使用这个功能,你需要安装web3[tester]。

安装好web3后,先通过TesterProvider方法连接到模拟节点中。

  1. In [1]: from web3 import Web3 
  2.  
  3. # 使用EthereumTesterProvider,连接模拟节点 
  4. In [2]: w3 = Web3(Web3.EthereumTesterProvider()) 
  5.  
  6. # 判断连接是否正常 
  7. In [3]: w3.isConnected() 
  8. Out[3]: True 

为了方便我们测试(如测试编写好的智能合约),Web3.py提供的模拟节点中已经为我们提供了一些账户,每个账户中有1000000个ether。

  1. # 获得可以使用的测试账户 
  2. In [4]: w3.eth.accounts 
  3. Out[4]: 
  4. ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf'
  5.  '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF'
  6.  '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69'
  7.  '0x1efF47bc3a10a45D4B230B5d10E37751FE6AA718'
  8.  '0xe1AB8145F7E55DC933d51a18c793F901A3A0b276'
  9.  '0xE57bFE9F44b819898F47BF37E5AF72a0783e1141'
  10.  '0xd41c057fd1c78805AAC12B0A94a405c0461A6FBb'
  11.  '0xF1F6619B38A98d6De0800F1DefC0a6399eB6d30C'
  12.  '0xF7Edc8FA1eCc32967F827C9043FcAe6ba73afA5c'
  13.  '0x4CCeBa2d7D2B4fdcE4304d3e09a1fea9fbEb1528'
  14.  
  15. # 获得第一个账户下的金额  
  16.  In [5]: w3.eth.get_balance(w3.eth.accounts[0]) 
  17. Out[5]: 1000000000000000000000000 

使用w3.eth.get_balance方法获得的金额为1000000000000000000000000,是因为其单位是wei。

因为计算机不太擅长处理浮点数,所以为了解决这个问题,很多程序员会将1.23元以123存到数据库中,即以分为基本单位。以太坊这边也一样,ether类似于元的单位,而wei类似于分,只是分只是增大了100倍,而wei与ether的比例是(18个0):

  1. 1 ether = 100000000000000000 wei  
  2. 1 wei = 0.0000000000000000001 ether 

Web3.py提供了toWei与fromWei方法进行单位的换算,ether与wei单位之间还有多个单位,可以查阅Web3.py文档Converting currency denominations。简单使用toWei与fromWei两个方法:

  1. In [7]: Web3.toWei(1, 'ether'
  2. Out[7]: 1000000000000000000 
  3.  
  4. In [8]: Web3.fromWei(1000000000000000000000000, 'ether'
  5. Out[8]: Decimal('1000000'

模拟交易

有了账户以及钱后,就可以模拟交易行为了,即将你账户中的币转到其他账户中。

先来看看,没有任何转账状态下的区块链:

  1. # 获取区块链中最新一个区块的信息 
  2. In [9]: w3.eth.get_block('latest'
  3. Out[9]: 
  4. AttributeDict({'number': 0, 
  5.  'hash': HexBytes('0x78b6514d115669937c0933824a0c74ff2eab14a25f1b1e799609872bcb18113b'), 
  6.  # 前一个区块Hash为0 
  7.  'parentHash': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), 
  8. ... 
  9.  'gasLimit': 3141592, 
  10.  'gasUsed': 0, 
  11.  'timestamp': 1635092566, 
  12.  # 没有交易 
  13.  'transactions': [], 
  14.  'uncles': []}) 

因为是模拟节点,所以与真实节点不同,它不会在大约15秒内增加一个新区块,而是会一直模拟等待,直到你进行交易。

到目前为止,因为我们没有进行任何交易,所以parentHash(前置区块Hash)为0,transactions(交易数据)为空,这个区块,其实就是创世区块。

现在我们进行一笔交易,如下:

  1. # 发起一笔交易 
  2. In [10]: tx_hash = w3.eth.send_transaction({ 
  3.     ...:     'from': w3.eth.accounts[0], 
  4.     ...:     'to': w3.eth.accounts[1], 
  5.     ...:     'value': w3.toWei(3, 'ether'
  6.     ...: }) 
  • from:发送者账户的地址
  • to:接受者账户的地址
  • value:此次转账金额

我们可以通过get_transaction获得这次交易更详细的信息,如下:

  1. # 获取那笔交易的信息   
  2. In [14]: w3.eth.get_transaction(tx_hash) 
  3. Out[14]: 
  4. AttributeDict({'hash': HexBytes('0x15e9fb95dc39da2d70f4cc41556bd092c68a97a04892426a064e321bfe78662a'), 
  5.  'nonce': 0, 
  6.  'blockHash': HexBytes('0x9f92558e214519a5e4ba7b8b4769a59bdc8c6c13e6fe5b0ec062b806e18f049f'), 
  7. # 交易数据在第一个区块中 
  8.  'blockNumber': 1, 
  9.  # 全网络第一个交易 
  10.  'transactionIndex': 0, 
  11.  'from''0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf'
  12.  'to''0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF'
  13.  'value': 3000000000000000000, 
  14.  'gas': 21000, 
  15.  'gasPrice': 1, 
  16.  'data''0x'
  17.  'v': 28, 
  18.  'r': HexBytes('0x11bebd35f91582f55dc180dcfc1c5ccad48dadc207802727f7ac997df6490b22'), 
  19.  's': HexBytes('0x697db707f5b7cc4d3a3196b434b0d5616300b8afbe8a21ab47ed9335252e4ebd')}) 

完成交易后,我们可以查询对应账户的余额来判断是否转账成功。

  1. In [15]: w3.eth.get_balance(w3.eth.accounts[0]) 
  2. Out[15]: 999996999999999999979000 
  3.  
  4. In [16]: w3.eth.get_balance(w3.eth.accounts[1]) 
  5. Out[16]: 1000003000000000000000000 

可以看到,第二个账户,余额从1,000,000到1,000,003 ether,但第一个账户减少的金额却超过3 ether,这是因为交易需要扣除一笔小额手续费所导致的。

在真实的以太坊网络中,交易手续费用是可以调整的,这取决于你发起交易时的网络需求以及你希望处理交易的速度,就目前而言,交易手续费是比较大的成本。

创建账户

在Web 2.0中,大多数应用在使用前需要创建账户,这个账户会保存到公司服务器中,即你虽然使用账户,但却没有账户的所有权,以微信为例,如果哪天腾讯公司要封掉你的账户是非常轻松的,这个账户下对你非常重要的数据将与你不辞而别。

在Web 3.0中,你同样可以创建账户,创建完后,你会拥有该账户的私钥与公钥,这个账户不会受到应用创建者或公司的影响,只要你不泄露私钥,那么账户的所有权就在你手中,当然也要一些隐患,如果你丢失了私钥,那么就丢失了账户,没有找回密码一说,所以在币圈,非常强调对自己私钥的保护。

通过Web3.py,我们可以轻松的创建一个账户,这个过程是无需连接到区块链网络或任何服务的,也没有注册过程,如下:

  1. # 创建账户 
  2. In [17]: acct = w3.eth.account.create() 
  3.  
  4. # 账户地址 
  5. In [19]: acct.address 
  6. Out[19]: '0x004D8ae69CD02Be5c491F7D095b5585cECE01407' 
  7.  
  8. # 账户私钥(不可泄露给他人) 
  9. In [20]: acct.key 
  10. Out[20]: HexBytes('0x39a579d1302e36fbf8b283eca5e1d52b4c56811921dfcdc2996f59eff7be6258'

再次强调,你不需要联网,不需要提供任何其他信息,便可以创建一个有效的以太坊账户,后续我会写一下账户生成的过程。

账户是一个重要的概念,因为我们影响区块链产生变化的唯一方式便是产生一个交易(调用智能合约也看为产生一个交易),而每个交易必须由一个账户进行签名,避免别人伪冒。

一个账户可以进行交易、进行交易间信息的传输、可以部署智能合约、可以与智能合约进行交互等。

首先,我们实践一下,如何通过账户进行转账。

回顾前面的代码,我们通过EthereumTesterProvider连接的模拟节点并有一些账户,这些账户的转账过程通过send_transaction方法完成,这里隐藏了比较多细节,因为Web3.py知道你在使用EthereumTesterProvider管理的测试账户,而这些账户都处在unlocked状态,即默认情况下,这些账户的交易都会自动完成签名。

这次,我们从自己创建的账户转账看看,因为我们自己的账户没有ether,所以需要先从测试账户转点钱过去。

  1. In [25]: w3.eth.get_balance(acct.address) 
  2. Out[25]: 0 
  3.  
  4. In [26]: w3.eth.get_balance(test_acct) 
  5. Out[26]: 1000000000000000000000000 
  6.  
  7. # 测试账户转10000000000到创建的账户中 
  8. In [27]: tx_hash = w3.eth.send_transaction({ 
  9.     ...:     'from': test_acct, 
  10.     ...:     'to': acct.address, 
  11.     ...:     'value': 10000000000 
  12.     ...: }) 
  13.  
  14. In [28]: tx_hash 
  15. Out[28]: HexBytes('0xa1b8be56bee0421035cbb9afb157218770f692c71b553a82cb52529c5dd12c3d'

创建的账户中有钱了,现在使用创建的账户来完成一笔交易,这个过程,我们需要手动对交易数据进行签名。

  1. # 交易数据 
  2. In [29]: tx_data = { 
  3.     ...:     'to': test_acct, 
  4.     ...:     'value': 500000000, 
  5.     ...:     'gas': 21000, 
  6.     ...:     'gasPrice': 1,  # 这个gas价格只存在于测试网络中 
  7.     ...:     'nonce': 0 
  8.     ...:  } 
  9.  
  10. # 使用acct的私钥对交易数据进行签名 
  11. In [30]: signed = w3.eth.account.sign_transaction(tx_data, acct.key
  12.  
  13. In [31]: signed 
  14. Out[31]: SignedTransaction(rawTransaction=HexBytes('0xf8638001825208946813eb9362372eef6200f3b1dbc3f819671cba69841dcd6500801ca029f2b216949529fbd19841c52e0cc78f218f45dd3531b918224f345f2e381aa9a0266619842d80050ab00bf8e0ea383056d7691a955113764e86927ddf36a478bb'), hash=HexBytes('0x402a89616ea2c37af4a17d8ff527e83141ac39968f3a61ddf92d4a1f5830cd29'), r=18973632901206428005591964593075310485747666570252293259781563419879236180649, s=17368282753934865160480197991111055873845272890414273881219608275127669913787, v=28) 
  15.  
  16. # 进行交易 
  17. In [32]: tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction) 
  18.  
  19. In [33]: tx_hash 
  20. Out[33]: HexBytes('0x402a89616ea2c37af4a17d8ff527e83141ac39968f3a61ddf92d4a1f5830c 

先关注到交易数据,其中gas、gasPrice、nonce是比较细的东西。

gas:表示这次交易需要花费多少gas(燃气)

gasPrice:表示gas的价格,在真实的以太坊网络中,gasPrice是很高的,也是目前以太坊网络的一个问题与痛点

none:在以太坊网络中,none表示当前账户的交易数,Ethereum protocol(以太坊协议)会追踪这个值,避免发生双重支付(也称双花攻击),这里为0表示acct账户第一次产生交易。

需要注意,无论是send_transaction方法还是send_raw_transaction方法,都不代表交易完成了,这些方法只是将交易数据广播到以太坊网络中,只有当以太坊网络中的矿工节点将交易上链了,才能说交易完成了,如果你的gas与gasPrice很小,在真实以太坊网络中,你将很难上链,即难以完成真正的交易。

从开发者角度看,以太坊上的这种账户模式可以让我们在创建应用时,不太需要考虑账户管理等功能,以太坊已经天然解决了这部分问题,你可以将精力集中在具体的业务中。

与智能合约交互

以太坊被提出的一个重要原因是,比特币网络不支持图灵完备的编程语言,导致很多应用无法被开发,以太坊则支持图灵完毕的编程语言,如Solidity语言,它的语法与JS相近,是图灵完备的语言,基于Solidity语言,可以快速开发智能合约。

简单而言,智能合约就是存储在以太坊区块链上的程序,任何人都可以使用它,如果你需要部署一个智能合约,其过程与发起一笔交易类似,只是交易数据里,包含着Solidity编译后的字节码,伪代码如下:

  1. # 代码编译成字节码 
  2. bytecode = "6080604052348015610...36f6c63430006010033" 
  3.  
  4. tx = { 
  5.     # 将字节码作为数据包含在交易数据体中 
  6.    'data': bytecode, 
  7.    'value': 0, 
  8.    'gas': 1500000, 
  9.    'gasPrice': 1, 
  10.    'nonce': 0 

部署智能合约相比于普通交易通常需要更多的gas,且部署智能合约的交易体中没有to字段。

Web3.py将部署以及与智能合约交互的过程进行了简化,伪代码如下:

  1. # 部署一个新的智能合约 
  2. Example = w3.eth.contract(abi=abi, bytecode=bytecode) 
  3. tx_hash = Example.constructor().transact() 
  4.  
  5. # 通过智能合约的地址连接一个智能合约 
  6. myContract = web3.eth.contract(address=address, abi=abi) 
  7. # 传参、使用智能合约 
  8. twentyone = myContract.functions.multiply7(3).call() 

生成签名信息

账户除了可以进行交易等链上(on-chain)操作,还可以进行消息签名等链下(off-chain)操作。

与交易不同,被签名消息不需要上链,也不会被广播到区块链网络中,即不需要花费任何成本,简单而言,签名消息只是用你的私钥对数据进行了一个数学操作,当你将这段数据发送给他人时,他人可以通过数据方法还原出签名私钥对应的公钥,从而确定这个数据是由你签名的。

这有什么用?可以使用到NFT上。

你可以对你的作品(一段数据)进行签名,然后到OpenSea(目前最大的NFT交易市场)进行售卖,当有卖家购买时,才会将购买时产生的交易数据上链,上链的过程需要花费ether,而你签名的过程是不需要任何成本的,上链的操作其实只是表明你签名的这个作品被某个账户购买了,这个购买的交易操作产生的数据会记录到区块链中,是不可更改的。

通过一段伪代码,可以更直观的理解签名消息的整个流程:

  1. # 1. 待签名数据 
  2. msg = "我是二两,给我打钱" 
  3.  
  4. # 2. 使用你账户的私钥进行前面 
  5. pk = b"..." 
  6. signed_message = sign_message(message=msg, private_key=pk) 
  7.  
  8. # 3. 通过网络发送签名后的数据 
  9.  
  10. # 4. 消息接收者解码发送的数据,获得数据的公钥,从而可以确定发送消息者的身份 
  11. sender = decode_message_sender(msg, signed_message.signature) 
  12. print(sender) 

参考

A Developer's Guide to Ethereum, Pt. 1

A Developer's Guide to Ethereum, Pt. 2

责任编辑:武晓燕 来源: 懒编程
相关推荐

2021-07-27 05:21:34

边缘计算数据网络

2022-10-18 08:00:00

2022-03-01 08:10:24

区块链以太坊数据库

2020-12-02 13:24:07

强化学习算法

2022-09-14 10:23:46

以太坊技术

2022-04-15 08:33:51

PythonGraph以太坊数据

2021-05-02 22:19:46

以太坊比特币加密货币

2009-11-05 15:43:02

Visual Stud

2021-05-03 23:32:55

以太坊区块链比特币

2021-05-22 22:57:24

以太坊加密货币比特币

2021-12-08 13:57:29

以太坊加密货币比特币

2021-04-29 16:11:14

以太坊共识链验证者

2018-06-01 09:17:52

区块链以太坊

2021-05-07 09:06:55

GraphQLAPI 以太坊

2021-05-13 16:13:21

区块链以太坊NFT

2010-01-12 17:33:06

C++

2022-09-20 11:34:15

以太坊PoS共识机制攻击

2021-03-04 11:05:24

比特币以太坊加密货币

2022-09-06 14:35:34

区块链以太坊NFT

2021-06-04 11:45:36

病毒软件挖矿恶意软件
点赞
收藏

51CTO技术栈公众号