python ethereum 代码分析 《3》

python ethereum 代码分析 《3》

python 版本以太坊

pyethereum部分

本文主要介绍以太坊Merkle Patricia Trie以及区块的校验存储

一些关键字解释

sha3: 以太坊所用的一种hash方法

rlp :Recursive Length Prefix,递归长度前缀编码,以太坊用于编码数据。用于编码任意的嵌套结构的二进制数据(在python中就是列表,其实rlp在Merkle Patricia Trie的实现中主要就是编码列表用的),它是以太坊中数据序列化/反序列化的主要方法,区块、交易等数据结构在持久化时会先经过RLP编码后再存储到数据库中。

BlockHeader

先看一下以太坊block区块头的结构

class BlockHeader(rlp.Serializable):

    """A block header.
    If the block with this header exists as an instance of :class:`Block`, the
    connection can be made explicit by setting :attr:`BlockHeader.block`. Then,
    :attr:`BlockHeader.state_root`, :attr:`BlockHeader.tx_list_root` and
    :attr:`BlockHeader.receipts_root` always refer to the up-to-date value in
    the block instance.
    :ivar block: an instance of :class:`Block` or `None`
    :ivar prevhash: the 32 byte hash of the previous block
    :ivar uncles_hash: the 32 byte hash of the RLP encoded list of uncle
                       headers
    :ivar coinbase: the 20 byte coinbase address
    :ivar state_root: the root of the block's state trie
    :ivar tx_list_root: the root of the block's transaction trie
    :ivar receipts_root: the root of the block's receipts trie
    :ivar bloom: TODO
    :ivar difficulty: the block's difficulty
    :ivar number: the number of ancestors of this block (0 for the genesis
                  block)
    :ivar gas_limit: the block's gas limit
    :ivar gas_used: the total amount of gas used by all transactions in this
                    block
    :ivar timestamp: a UNIX timestamp
    :ivar extra_data: up to 1024 bytes of additional data
    :ivar nonce: a 32 byte nonce constituting a proof-of-work, or the empty
                 string as a placeholder
    """

    fields = [
        ('prevhash', hash32),#父区块的hash
        ('uncles_hash', hash32),#矿工引用的uncle区块列表的hash
        ('coinbase', address),#挖出此区块矿工的地址
        ('state_root', trie_root),#state trie树树根
        ('tx_list_root', trie_root),#交易trie树树根
        ('receipts_root', trie_root),#收据trie树树根
        ('bloom', int256),
        ('difficulty', big_endian_int),#当前区块难度
        ('number', big_endian_int),#区块的高度
        ('gas_limit', big_endian_int),#区块gas上限
        ('gas_used', big_endian_int),#区块使用的gas
        ('timestamp', big_endian_int),#时间戳
        ('extra_data', binary),
        ('mixhash', binary),
        ('nonce', Binary(8, allow_empty=True))#工作量证明随机数,通过校验该值来判断矿工的工作量证明是否合法。
    ]

Merkle Patricia Trie

首先要提一点,以太坊用Merkle Patricia Trie 这种数据结构来做区块中交易的快速校验和全网状态state的快速验证。Merkle Patricia Trie是一种树,区块头中的state_root,tx_list_root,receipts_root 分别是State Trie ,Transactions Trie,Receipts Trie的树根,这三个树都是Merkle Patricia Trie。

普通trie树和merkle树看这篇博客

先说下trie树的一个扩展Radix Trie树,这个树也是只存key / value的一棵树
这棵树每个节点看起来是这样的[i0, i1 … in, value] ,现在如果我们的key是用16进制(hex)编码,那么n=15,每个节点列表长度为17,[i0,i1,…i15,value],列表中的0~15的某一位就表示key中的一个16进制字符。
Radix Trie树有个问题,如果我要存储一个长度为20的hex编码的key,那么这个树的高度就有20,每一层就只存了key中一个字符,这样效率就不高,以太坊就改进了一下,增加了一些节点类型来解决这个事情。
Merkle Patricia Trie树也是用来存key / value的一棵树
Merkle Patricia Trie的四种节点类型
1.NULL (represented as the empty string) 空节点
2.branch A 17-item node [ v0 … v15, vt ] 分支节点,0~15每一位表示16进制编码的一个字符
3.leaf A 2-item node [ encodedPath, value ] 这个value是原始key在叶子节点上对应的value值
4.extension A 2-item node [ encodedPath, key ] 这个key是子节点的hash

实际在Merkle Patricia Trie的实现中,还用了HP(hex prefix)编码方式去编码的key,其实就是十六进制编码再加前缀来表示节点类型。同时key是以bytes存储的,而一个hex char只有4位,一个byte有八位,所以不够八位的时候prefix 后面再加0000补全。
HP编码前缀的规则

hex char bits node type partial path length
0 0000 extension even
1 0001 extension odd
2 0010 terminating (leaf) even
3 0011 terminating (leaf) odd

先看一个Merkle Patricia Trie存储的列子
以下是要存储到tire树中的四个键值对
<64 6f> : ‘verb’
<64 6f 67> : ‘puppy’
<64 6f 67 65> : ‘coin’
<68 6f 72 73 65> : ‘stallion’
添加之后,trie树结构:
rootHash: [ <16>, hashA ] // extension node
hashA: [ <>, <>, <>, <>, hashB, <>, <>, <>, hashC, <>, <>, <>, <>, <>, <>, <>, <> ] // branch node
hashC: [ <20 6f 72 73 65>, ‘stallion’ ] //leaf node
hashB: [ <00 6f>, hashD ] // extension node
hashD: [ <>, <>, <>, <>, <>, <>, hashE, <>, <>, <>, <>, <>, <>, <>, <>, <>, verb ] // branch node
hashE: [ <17>, hashF ] // extension node
hashF: [ <>, <>, <>, <>, <>, <>, hashG, <>, <>, <>, <>, <>, <>, <>, <>, <>, puppy ] // branch node
hashG: [ <35>, ‘coin’ ] //leaf node

注意到root node 有公共前缀hex char 6 ,6前面的1表示这是个extension node,并且原来长度是奇数,补了个前缀1后正好凑成一个byte。
存储在root node上的key,value 便是 <16>, hashA,而hashA又是子节点在leveldb中的地址;
其中,hashA = sha3(rlp_encode([ <>, <>, <>, <>, hashB, <>, <>, <>, hashC, <>, <>, <>, <>, <>, <>, <>, <> ]))
这个hashA的计算方式正是体现了Merkle Patricia Trie的‘Merkle’部分,每个节点的hash地址正是节点数据rlp编码过后的hash。如果某个矿工篡改了叶子节点的数据,比如State Trie某个叶子节点一个账户的余额,并生成了State Tire的root hash&#x

以下是使用 Python 的 Web3 模块获取 UniswapV2 价格的示例代码: ```python from web3 import Web3 # Connect to Ethereum network w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/<YOUR_INFURA_PROJECT_ID>')) # The UniswapV2Router02 contract address on Ethereum mainnet uniswap_router_contract_address = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' # ABI for the UniswapV2Router02 contract uniswap_router_abi = [ { "inputs": [], "name": "WETH", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "tokenA", "type": "address" }, { "internalType": "address", "name": "tokenB", "type": "address" }, { "internalType": "uint256", "name": "amountADesired", "type": "uint256" }, { "internalType": "uint256", "name": "amountBDesired", "type": "uint256" }, { "internalType": "uint256", "name": "amountAMin", "type": "uint256" }, { "internalType": "uint256", "name": "amountBMin", "type": "uint256" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "deadline", "type": "uint256" } ], "name": "swapExactTokensForTokens", "outputs": [ { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" } ], "stateMutability": "nonpayable", "type": "function" } ] # Load the UniswapV2Router02 contract uniswap_router = w3.eth.contract(address=uniswap_router_contract_address, abi=uniswap_router_abi) # Get the address of the WETH token weth_address = uniswap_router.functions.WETH().call() # The UniswapV2Pair contract address for the token pair you want to get the price for token_pair_address = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' # ABI for the UniswapV2Pair contract uniswap_pair_abi = [ { "inputs": [ { "internalType": "address", "name": "_token0", "type": "address" }, { "internalType": "address", "name": "_token1", "type": "address" } ], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": False, "inputs": [ { "indexed": True, "internalType": "address", "name": "sender", "type": "address" }, { "indexed": False, "internalType": "uint256", "name": "amount0", "type": "uint256" }, { "indexed": False, "internalType": "uint256", "name": "amount1", "type": "uint256" }, { "indexed": True, "internalType": "address", "name": "to", "type": "address" } ], "name": "Mint", "type": "event" }, { "anonymous": False, "inputs": [ { "indexed": True, "internalType": "address", "name": "sender", "type": "address" }, { "indexed": False, "internalType": "uint256", "name": "amount0", "type": "uint256" }, { "indexed": False, "internalType": "uint256", "name": "amount1", "type": "uint256" } ], "name": "Swap", "type": "event" }, { "anonymous": False, "inputs": [ { "indexed": True, "internalType": "address", "name": "sender", "type": "address" }, { "indexed": False, "internalType": "uint256", "name": "amount0In", "type": "uint256" }, { "indexed": False, "internalType": "uint256", "name": "amount1In", "type": "uint256" }, { "indexed": False, "internalType": "uint256", "name": "amount0Out", "type": "uint256" }, { "indexed": False, "internalType": "uint256", "name": "amount1Out", "type": "uint256" }, { "indexed": True, "internalType": "address", "name": "to", "type": "address" } ], "name": "Sync", "type": "event" }, { "inputs": [], "name": "getReserves", "outputs": [ { "internalType": "uint112", "name": "_reserve0", "type": "uint112" }, { "internalType": "uint112", "name": "_reserve1", "type": "uint112" }, { "internalType": "uint32", "name": "_blockTimestampLast", "type": "uint32" } ], "stateMutability": "view", "type": "function" } ] # Load the UniswapV2Pair contract uniswap_pair = w3.eth.contract(address=token_pair_address, abi=uniswap_pair_abi) # Get the reserves of the token pair (token0_reserve, token1_reserve, timestamp) = uniswap_pair.functions.getReserves().call() # Get the decimals of the tokens token0_decimals = w3.eth.contract(address=uniswap_pair.functions.token0().call(), abi=uniswap_pair_abi).functions.decimals().call() token1_decimals = w3.eth.contract(address=uniswap_pair.functions.token1().call(), abi=uniswap_pair_abi).functions.decimals().call() # Calculate the price of token1 in terms of token0 price = (token1_reserve * 10 ** token0_decimals) / (token0_reserve * 10 ** token1_decimals) # Print the price print(f'The price of token1 in terms of token0 is {price}') ``` 请注意,你需要使用自己的 Infura 项目 ID 替换代码中的 `<YOUR_INFURA_PROJECT_ID>`,并且你需要使用正确的 UniswapV2Pair 地址和对应的 ABI。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值