以太坊实战-交易爬虫
本文从一个区块链跑路项目说起,怎么通过大家可见的区块数据进行自己的业务分析,目的主要是讲述中间涉及到的技术,如果你能从中获益,并因此构建自己更加强大的分析工具,我深感荣幸.
防止区块链项目跑路
首先要说说这个争议颇多的英雄链: 做为首个全球加密数字货币区块链博彩平台的建设者,HeroChain致力打造数字货币一站式博彩娱乐互动平台,是实现在区块链上加密数字货币的娱乐和产品集合服务平台。HeroChain团队目标是落地与合作全球85个博彩合法的国家和地区,或博彩业合法牌照或与当地博彩业紧密合作,未来使得HEC能与线下赌场打通,使得HEC拥有更大的交易场景。团队认为:HEC的应用覆盖和使用领域确实足以支撑这个巨量加密数字货币的流通市值。由于没有税收,使得HeroChain团队每年可以拿出收益的30%,在进行市场回购HEC, 让参与者获利。关键这个博彩业市场不像之前其它项目的预测的预期市场,是庞大而真实的网络娱乐刚需市场。
从这段描述来看,英雄链所针对的应用场景和未来目标都是非常有说服力的。然而目前出现有用户和项目团队因为破发矛盾激化,进而对该项目本身也产生各种质疑。媒体也对其核心人员的资金流向进行了分析:
从结果来看,项目募集的资金都最终流向了某一个地址,确实存在发行者卷款跑路的可能(详细分析可以查阅参考文献两篇文章)。
我这里只是以这件事件做一个引子,由于区块链的数据对大众完全透明公开,所有人的资金流向其实都摆在眼前,只是说现在链上基础工具不完善,普通人很难去分析这庞大而精细的交易记录。如果我们做一个交易爬虫,能够轻松分析任意账户的资金流动,那么不论是对普通小白验证项目的可信度还是金融从业者分析深度数据,都是很有价值的。
下面,我就介绍下,如果要产生ERC20某个代币的资金流向图(类似下图),要注意哪些技术关键点。
服务端控制智能合约
要和智能合约进行交互,显然需要完成通用编程语言对合约的控制,这里我们以golang
代码为例,展示怎么从golang
中调用合约函数。官方go-ethereum已经提供了这样的工具abigen
,直接从合约sol
代码生成go代码:
Command | Description |
---|---|
abigen |
Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain Ethereum contract ABIs with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see our Native DApps wiki page for details. |
那我们要分析erc20的代币,所以定义好一份接口合约即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
然后使用abigen
工具生成go代码
1
|
|
然后在golang
中就可以像这样调用合约函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
ERC20关键参数获取
做交易爬虫,现在最关键的是分析交易参数,比如这是etherscan.io
上一个MCAP
转账交易:
某个地址是否合约
在交易里,如果是合约的调用那么To
字段必然是一个合约地址,那么当我们拿到一个交易时,怎么判断这个交易是否一次合约调用呢,或者怎么判断To
是合约地址而不是用户钱包地址呢?
1
|
|
理解了这个原理,那么在go代码里就很容易判断了:
1 2 3 4 5 |
|
这里附上其他环境判断是否合约的方法
在合约solidity代码里判断:
1 2 3 4 5 |
|
在geth
的console:
1
|
|
From
From
无法直接从交易函数里获取,因为来源地址可以从签名里反解出来,为了拿取到这个字段,用的方法是解析交易的String()
输出来获取,虽然办法效率不高,但为了不改动源码这是最简单的。
[update]from
获取已经更新,不再使用正则解析,详见代码get from field
To
收款地址的获取就比较麻烦一些了,它不像eth的直接转账,交易的to
字段就是收款地址,合约调用的To
是合约地址,真正的收款地址存放在Data
字段里,那么我们来看看Data
字段怎么编码的。
函数签名
Data
的起始4个字节是函数签名的sha3结果的前缀,举个例子,对于下面的合约
1 2 3 4 5 |
|
如果要调用
baz函数,则结果应该是
keccak256(“baz(uint32,bool)”)[0:4]转换为16进制是
0xcdcd77c0`
参数编码
参数编码是依次对函数签名每个参数进行32字节左补齐编码,如baz(69,true)
这次调用,参数69
和true
分别编码结果是:
69
,编码为0x0000000000000000000000000000000000000000000000000000000000000045
true
,编码为0x0000000000000000000000000000000000000000000000000000000000000001
那么整合起来,baz(69,true)
调用时交易的Data
应该为:
1
|
|
transfer
回到我们的需求,我们要分析的20代币的转账,其实就是分析transfer(address _to,unit256 _value)
的合约函数调用,该函数签名编码是0xa9059cbb
,比如我们要对0x54d28e24df3a2381d4c072118da0ef0a51a4fcd9
转账493480000
个MCAP,编码过程为:
1 2 3 4 5 |
|
最终结果Data
是
0xa9059cbb00000000000000000000000054d28e24df3a2381d4c072118da0ef0a51a4fcd9000000000000000000000000000000000000000000000000000000001d69e840
Put it together
把这上面关键点整合起来,就可以构建一个简单爬虫,这个爬虫执行流程应该是:
- 遍历区块交易,取到我们关注的某个合约的所有转账交易
- 解析交易关键字段,包含交易ID,from,to,金额,时间戳
- 入库,提供webAPI给应用层