文章目录
摘要
步入币圈大门后,除了钱包应用之外,用户最先接触的应该还有区块链浏览器。区块链浏览器不同于电脑和手机上浏览网页用的浏览器软件,而是指一个网站可以查询区块链上的具体信息。比如,给定区块高度,可以查询该高度区块的创建时间,包含了多少交易;给定一个地址,可以查询余额,该地址的所有交易记录等。当前以太坊上的数据量级已达亿级,如何进行数据持久化和查询呢?本文以以太坊为例,对区块链浏览器原理及存储细节进行分析总结。
以太坊上的交易类型
以太坊上 Native Token 交易,就是 ETH 交易,不过以太坊支持智能合约,开发者和机构可以在以太坊上创建合约并发行自己的通证,通过调用合约实现自己发行通证的转账,查询余额等。不同开发者可以分别为自己的合约编写不同的函数名来实现通证转移,那么问题来了,如果钱包类应用要兼容多家的通证,要分别知道他们是用了什么函数名,这么一来通用性较差。为了解决这个问题,以太坊社区制定了 ERC20 标准,该标准规范了通证合约的接口,比如,大家都要把合约转账的函数名编写为 transfer,如果转账成功要提交 transfer event,这么一来,钱包类应用只实现 ERC20 标准里规定的内容,就可以接入符合 ERC20 协议的通证了。
从 Token 的角度来看,以太坊上有 ETH Token,以及基于合约实现的 ERC20 和其他标准的 Token。交易为 ETH 转账交易,ERC20等 Token 转账交易。从合约的角度来看,交易有创建合约和调用合约两种交易类型,其中 ERC20 转账交易是通过调用合约的转账函数实现的。
数据源
如果要解析区块数据,那么问题来了,我们从哪里获取区块数据呢?
常见的方法有三种:
- RPC 接口:搭建全节点,通过调用全节点 RPC 接口获取区块和交易数据
- 优点:开发复杂度低,区块解析器和全节点可以在不同机器上运行
- 缺点:需要等待全节点更新区块完成后才可以获取数据,RPC 短链接密集 IO 操作时效率较低
- 实现:搭建全节点,使用 RPC 接口获取数据
- P2P 协议:ETH 全节点之间通过规定好的 P2P 协议进行数据交互,因此可以实现 P2P 协议,直接从对等节点获取区块和交易数据
- 优点:无需等待全节点同步数据,直接获取区块和交易信息
- 缺点:获取数据相对复杂,因为要实现 P2P 协议
- 实现:为了简化实现,可以在开源全节点的基础上增加数据解析入库逻辑
- 本地数据:ETH golang 实现的全节点中,本地数据通过 leveldb 存储,可以直接解析 leveldb 数据
- 优点:解析速度块,直接读取磁盘
- 缺点:需要了解 ETH 具体存储规则,以及需要维护最长链
- 实现:使用 leveldb 解析本地数据
交易数据解析
分析完交易类型和数据源之后,我们在这里使用 RPC 解析数据的方式,作为 MVP 实现,我们只解析以下交易类型:
- 解析 ETH 交易
- 解析 ERC20 Transfer 交易
- ERC20 合约直接调用
- ERC20 合约间接调用
- 解析创建合约交易
当全节点同步完成后,我们从第一个区块依次向后获取每个区块的信息,以及获取区块中的所有交易,对交易进行解析,解析完成的数据存入数据库中,用作查询使用。
最长链选择
区块链本身是个多叉树结构,之所以称为链是因为大家只认可从根节点到子节点最长的路径,以最长链上的块为主块,在区块链产生区块的过程中,会产生很多叔块,因此在解析的过程中要一直沿着最长链的方向进行解析,那么如何保证最长链呢?
因为这里数据源依赖的是全节点 RPC 接口,全节点本身已经维护了最长链,因此,我们无需复杂的回溯逻辑,只需要对比已经解析高度为 h 的区块和全节点中高度为 h 的区块 hash 是否一致即可,如果一致证明本地解析的链为最长链,具体算法可描述为:
- 区块分叉以全节点为准,同步时检测本地最新块高度 h 区块的 hash 和全节点高度为 h 的块 hash 是否一致,如果一致证明没有发生分叉,继续向后同步;如果不一致证明发生分叉,首先寻找分叉高度,然后进行回退。
- 寻找分叉点过程:从本地区块当前高度前一个高度 h 和全节点 h 区块的 hash 进行比对,如果一致,证明分叉点为 fork height = h + 1,如果不一致,继续向前 h = h - 1,直到找到分叉点。
- 回