最近一段时间区块链挺火的,不管是涨是跌,牵动人心。
每当涨的时候,总有人后悔为啥没有早买, 每当跌时,也能听见”背后有没有机构做空“疑问。
当看到有人在买卖中受打击而顿挫时,当看到有媒体在别人赔了很多还要去嘲讽时,我就想是不是应该做点什么,这世界本就不完美,为什么要禁止大家向往富有的心呢。
区块链的网络是P2P的,交易来自四面八方, 很难知道交易的双方在什么地方,哪个方位。我打算从监测流量入手,来探知这张网每天的异动。
没有思路,在区块链上研究了2个月,仍然找不到方法去看看到底哪些人在背后“影响”着市场。
直到2天前的从化之行, 泡着温泉, 看到几个泉眼里冒出来的热浪, 感觉发现了一些规律。
大局着手,我们来分析一下这张网的基本数据
谁最有钱
中本聪,李笑来。。。个个都是炒币高手,甚至身边的90后也声称炒币赚了100万, 那么到底谁最有钱,如果不能知道是谁,是不是可以通过技术手段算得一个排行榜。
哪些人在交易
交易数据满天飞,几乎每一秒钟都有很多交易,我想在里边寻找一些规律,看看有没有可能是一部分人的交易优先被网络认可。
区块链的交易从发生到被网络认可,是需要一段过程的,整个时间从分钟到小时不等, 一般认为被认可的条件:
交易被成功写入一个区块。此区块后又产生了5到6个区块。这张网有多大
炒币的人越来越多,矿机也越来越多,网络中的节点到底有多少,是不是可以找到一个通用的方法以收集全网的节点数量与动态。
带着这2个目标,我以比特币为例,来开始这趟旅程。
为什么要这些数据
先问大家一个问题, 都是交易,如何判断现在有没有人在砸盘呢。
一定量的抛空,会让群体散户产生恐具,进而导致新一步的下跌,每天都有无数双眼睛盯着交易大盘
从当前的大盘表现,如何预测下一时刻的表现,是一个很难的事。
一般交易所里交易的是代币, 产生的与发放的并不发生在用户的本地钱包里,而是交易所的远程钱包。出于安全性考虑, 在一定量币的交易完后,用户一般会将币传回到
本地的钱包中, 在下次交易前上传到交易所钱包, 我们可以将这个上传与传回当成一次交易来统计。
另一方面,如果所有的交易都发生在交易所,没有本地的买卖,就没有了网络的数据,这部分每天的交易情况是可以从交易所得到,因此我想了解一下全网每天的非代持交易情况,
并且为算法分析提供接口,为预测未来的价格走势提供可能。
比特币是一把双刃剑,可以做为其他币种的交换者, 也可以成为大量洗钱的暗黑空间,因此对大宗交易的监测成了goverment或者经济体一件很重要又麻烦的事情。
节点收集
连接限制
比特币只能向外最多连接8个点, 连接的数量不能超过125,
既使改变了这个数字,还是会受到并发模型的限制。比特币的网络模型是select:
在linux上 select的fd限制是1024。
重构bitcoin事件模型
如果要让比特币客户端支持更多的连接,可以有2种方式,一种是多次select遍历所有的连接。
如果要让比特币客户端支持更多的连接,可以有2种方式,一种是多次select遍历所有的连接。
nodes[10000];
select(nodes, 0, 1024);
select(nodes, 1024, 2048);
....
select(nodes, 10000-1024, 10000);
另一种是epoll模式,可以支持100万甚至更多的连接,我选择了这种方式btch_net_event.h
主要是几个函数
AddEvent 添加事件
DelEvent 删除事件
Process 事件循环
当有新连接建立时调用AddEvent
有了新事件模型做保障,接下来我重写了连接线程CConnman::ThreadOpenConnectionsWithEvent,去掉了连接限制。详见btch_net_event.cpp
关系型存储
bitcoin内建的存储用的是leveldb, 其独有的key value本地存储性能很高,但不利于关系型的查询,因为我们之后需要对数据做分析,所以我选择了mysql。
为了能更好的获取某个连接地址,我重载了CService::GetSockAddr
bool CService::GetSockAddr(int& type, char* ip, int& _port) const
{
_port = port;
if (IsIPv4()) {
type = 4;
size_t addrlen = sizeof(struct sockaddr_in);
struct sockaddr_in _addr;
struct sockaddr_in *paddrin = (struct sockaddr_in*)&_addr;
memset(paddrin, 0, addrlen);
if (!GetInAddr(&paddrin->sin_addr))
return false;
inet_ntop(AF_INET, &_addr.sin_addr, ip, INET_ADDRSTRLEN);
return true;
}
if (IsIPv6()) {
type = 6;
size_t addrlen = sizeof(struct sockaddr_in6);
struct sockaddr_in6 _addr;
struct sockaddr_in6 *paddrin6 = (struct sockaddr_in6*)&_addr;
memset(paddrin6, 0, addrlen);
if (!GetIn6Addr(&paddrin6->sin6_addr))
return false;
inet_ntop(AF_INET6, &_addr.sin6_addr, ip, INET6_ADDRSTRLEN);
return true;
}
return false;
}
当有新地址收入时,会记载的数据库。
节点动态展示
可以看到节点主要分布在欧洲,美国,中国沿海与日韩也有一定的分布。
为了得到上面的展示,我选择了google map marker,并使用headless浏览器puppeteer.
交易收集
“你有多少比特币”,这个数字在比特币的网络并没有一个这样的数字记录。
要算出持有量需要对以往的交易推算,用以往总获得减去总支出。因此要算出排行榜,比须对以往所有数据做一次运算。
为了更方便分析,我仍然将原来的leveldb存储到mysql中。
我设计了一系列的表,以对实时网络传播的交易入库,并将被写进块的数据加上标记,这样便有了一个结构型的交易数据。感兴趣的同志戳这里
表架构
交易分2种,一种给矿工奖励的coinbase, 一种是普通的交易,包括tx_in(钱来自哪里),tx_out(发多少钱,给谁)
产生交易数据
我在比特币代码接受交易并处理时,插入一条转存的指令:
static void CheckInputsAndUpdateCoins()
{
InsertCoinDB(tx);
...
}
逻辑实现
产生区块数据
在区块存储时,注入一条指令SaveBlock2DB以保存区块信息。
static CDiskBlockPos SaveBlockToDisk(const CBlock& block, int nHeight) {
SaveBlock2DB(block, nHeight);
...
}
调用实现
int SaveBlock2DB(const CBlock& block, int height)
{
...
BtchTxDB::GetInstance()->AddBlock(&bbk, height);
}
这里的BtchTxDB是我新增的类,用来处理比特币里的交易数据。
经过改造过后的比特币的程序跑起来,一天后就有了所有的线上交易数据。
我们就可以分析这些数据了。
最近最大一笔交易发送了多少币
mysql> select id, tx_id, out_value, out_address from tx_out order by out_value desc limit 1;
+-------+-------+--------------+------------------------------------+
| id | tx_id | out_value | out_address |
+-------+-------+--------------+------------------------------------+
| 14847 | 5141 | 331824606426 | 1LJWwgDdWMYZGUYRhszGqL1FkrsftEeckP |
bitcoin最小单位是聪, 10^8个聪为一比特币, 因此最近最大一笔交易为比特币发送了3318个比特币, (我的天哪), 接收地址为1LJWwgDdWMYZGUYRhszGqL1FkrsftEeckP, 这是哪个土豪还是交易所。
当前最小一笔交易
mysql> select id, tx_id, out_value, out_address from tx_out where out_value > 0 order by out_value asc limit 1;
+-------+-------+-----------+------------------------------------+
| id | tx_id | out_value | out_address |
+-------+-------+-----------+------------------------------------+
| 10462 | 3457 | 540 | 3JU1MWabud2iENRtydJXd9LMrUrSFxXbFy |
+-------+-------+-----------+------------------------------------+
1 row in set (0.03 sec)
统计到的最小交易数目为540聪。
谁接收的次数最多
mysql> select count(*), out_address from tx_out where out_address <> '' group by out_address order by count(*) desc limit 2;
+----------+------------------------------------+
| count(*) | out_address |
+----------+------------------------------------+
| 343 | 392t5a1Sy5wy4hghGCBzdTht7rsjECAwPN |
| 203 | 1JwgCVCnw8ziAnXA1c2VqUaMVkV4jtfDmw |
+----------+------------------------------------+
2 rows in set (0.05 sec)
谁接收的钱最多
mysql> select sum(out_value) ov, out_address from tx_out where out_address <> '' and status <> 3 group by out_address order by ov desc limit 5;
+---------------+------------------------------------+
| ov | out_address |
+---------------+------------------------------------+
| 3053342387036 | 1MN37fphKuQepWqvM7UuKFaSxW5bX8nhoR |
| 1860288937639 | 17A16QmavnUfCW11DAApiJxp7ARnxN5pGX |
| 469584006559 | 1Kr6QSydW9bFQG1mXiPNNu6WpJGmUa9i1g |
| 331824606426 | 1LJWwgDdWMYZGUYRhszGqL1FkrsftEeckP |
| 222882357474 | 1DcKsGnjpD38bfj6RMxz945YwohZUTVLby |
(status=3 代表已经消费,<>3 代表还没消费)。
可以看到接收比特币最多的地址是
1MN37fphKuQepWqvM7UuKFaSxW5bX8nhoR
总共接收了30533个比特币, 我猜它是一个交易所。
哪个地址钱最多
在比特币的交易里,输入与输出决定了币的流动,别人和你交易,就要把你的地址放到输出中(out_address),而你要花钱,也就是花费掉上一个交易的out_value.
比特币的币运算采用UTXO, 即只运算没花费出去的,且一笔交易输出最多只能被消费一次,在我的代码里,用户的币放在tx_out表里, 是否被消费用status字段来区分。
status值 | 含义 |
---|---|
1 | 被记录 |
2 | 被确认 |
3 | 被消费 |
在库里选择一下,看看哪个地址最有钱,只要查询status=2即可,因此我们拉出一个排行榜:
mysql> select sum(out_value) ov, out_address from tx_out where out_address <> '' and status = 2 group by out_address order by ov desc limit 10;
+---------------+------------------------------------+
| ov | out_address |
+---------------+------------------------------------+
| 3053342387036 | 1MN37fphKuQepWqvM7UuKFaSxW5bX8nhoR |
| 920423595045 | 17A16QmavnUfCW11DAApiJxp7ARnxN5pGX |
| 331824606426 | 1LJWwgDdWMYZGUYRhszGqL1FkrsftEeckP |
| 323000000000 | 373BRdPtfMycB1yYhzZ2XNPDjvBbYQBsU7 |
| 312453740813 | 1Kr6QSydW9bFQG1mXiPNNu6WpJGmUa9i1g |
| 115881899983 | 1N52wHoVR79PMDishab2XmRHsbekCdGquK |
| 109540316825 | 3PA3gFEfTCXU7EAJDeaKpVLUX7EBF5xX4m |
| 102507542525 | 1DcKsGnjpD38bfj6RMxz945YwohZUTVLby |
| 85003959092 | 3EC6GCRBCb*HL3xJNUeDeQMELmZsUcD |
| 81224138674 | 1EEqRvnS7XqMoXDcaGL7bLS3hzZi1qUZm1 |
+---------------+------------------------------------+
10 rows in set (0.05 sec)
结语
上个月帐上又多了5万,当然我这只是简单玩一下,纯属娱乐。在抄币的同时,做些研究,以反馈给同样爱币的你。
谨希望此文能帮助那些在黑暗中研究和人们,同样给那些对币世界充满好奇的人们。