
最近一段时间区块链挺火的,不管是涨是跌,牵动人心。
每当涨的时候,总有人后悔为啥没有早买,每当跌时,也能听见”背后有没有机构做空“疑问。
当看到有人在买卖中受打击而顿挫时,当看到有媒体在别人赔了很多还要去嘲讽时,我就想是不是应该做点什么,这世界本就不完美,为什么要禁止大家向往富有的心呢。
区块链的网络是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_
主要是几个函数
AddEvent添加事件DelEvent删除事件Process事件循环
当有新连接建立时调用AddEvent

有了新事件模型做保障,接下来我重写了连接线程CConnman::ThreadOpenConnectionsWithEvent,去掉了连接限制。详见btch_net_
关系型存储bitcoin内建的存储用的是leveldb,其独有的keyvalue本地存储性能很高,但不利于关系型的查询,因为我们之后需要对数据做分析,所以我选择了mysql。
为了能更好的获取某个连接地址,我重载了CService::GetSockAddr
boolCService::GetSockAddr(inttype,char*ip,int_port)const{_port=port;if(IsIPv4()){type=4;size_taddrlen=sizeof(structsockaddr_in);structsockaddr_in_addr;structsockaddr_in*paddrin=(structsockaddr_in*)_addr;memset(paddrin,0,addrlen);if(!GetInAddr(paddrin-sin_addr))returnfalse;inet_ntop(AF_INET,__addr,ip,INET_ADDRSTRLEN);returntrue;}if(IsIPv6()){type=6;size_taddrlen=sizeof(structsockaddr_in6);structsockaddr_in6_addr;structsockaddr_in6*paddrin6=(structsockaddr_in6*)_addr;memset(paddrin6,0,addrlen);if(!GetIn6Addr(paddrin6-sin6_addr))returnfalse;inet_ntop(AF_INET6,__addr,ip,INET6_ADDRSTRLEN);returntrue;}returnfalse;}当有新地址收入时,会记载的数据库。

节点动态展示

可以看到节点主要分布在欧洲,美国,中国沿海与日韩也有一定的分布。
为了得到上面的展示,我选择了googlemapmarker,并使用headless浏览器puppeteer.
交易收集“你有多少比特币”,这个数字在比特币的网络并没有一个这样的数字记录。
要算出持有量需要对以往的交易推算,用以往总获得减去总支出。因此要算出排行榜,比须对以往所有数据做一次运算。
为了更方便分析,我仍然将原来的leveldb存储到mysql中。
我设计了一系列的表,以对实时网络传播的交易入库,并将被写进块的数据加上标记,这样便有了一个结构型的交易数据。感兴趣的同志戳这里
表架构

交易分2种,一种给矿工奖励的coinbase,一种是普通的交易,包括tx_in(钱来自哪里),tx_out(发多少钱,给谁)
产生交易数据
我在比特币代码接受交易并处理时,插入一条转存的指令:
staticvoidCheckInputsAndUpdateCoins(){InsertCoinDB(tx);}逻辑实现

产生区块数据
在区块存储时,注入一条指令SaveBlock2DB以保存区块信息。
staticCDiskBlockPosSaveBlockToDisk(constCBlockblock,intnHeight){SaveBlock2DB(block,nHeight);}调用实现
intSaveBlock2DB(constCBlockblock,intheight){BtchTxDB::GetInstance()-AddBlock(bbk,height);}这里的BtchTxDB是我新增的类,用来处理比特币里的交易数据。

经过改造过后的比特币的程序跑起来,一天后就有了所有的线上交易数据。
我们就可以分析这些数据了。
最近最大一笔交易发送了多少币mysqlselectid,tx_id,out_value,out_addressfromtx_outorderbyout_valuedesclimit1;+-------+-------+--------------+------------------------------------+|id|tx_id|out_value|out_address|+-------+-------+--------------+------------------------------------+|14847|5141|331824606426|1LJWwgDdWMYZGUYRhszGqL1FkrsftEeckP|
bitcoin最小单位是聪,10^8个聪为一比特币,因此最近最大一笔交易为比特币发送了3318个比特币,(我的天哪),接收地址为1LJWwgDdWMYZGUYRhszGqL1FkrsftEeckP,这是哪个土豪还是交易所。
当前最小一笔交易mysqlselectid,tx_id,out_value,out_addressfromtx_outwhereout_value0orderbyout_valueasclimit1;+-------+-------+-----------+------------------------------------+|id|tx_id|out_value|out_address|+-------+-------+-----------+------------------------------------+|10462|3457|540|3JU1MWabud2iENRtydJXd9LMrUrSFxXbFy|+-------+-------+-----------+------------------------------------+1rowinset(0.03sec)
统计到的最小交易数目为540聪。
谁接收的次数最多mysqlselectcount(*),out_addressfromtx_outwhereout_address''groupbyout_addressorderbycount(*)desclimit2;+----------+------------------------------------+|count(*)|out_address|+----------+------------------------------------+|343|392t5a1Sy5wy4hghGCBzdTht7rsjECAwPN||203|1JwgCVCnw8ziAnXA1c2VqUaMVkV4jtfDmw|+----------+------------------------------------+2rowsinset(0.05sec)谁接收的钱最多
mysqlselectsum(out_value)ov,out_addressfromtx_outwhereout_address''andstatus3groupbyout_addressorderbyovdesclimit5;+---------------+------------------------------------+|ov|out_address|+---------------+------------------------------------+|3053342387036|1MN37fphKuQepWqvM7UuKFaSxW5bX8nhoR||39|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即可,因此我们拉出一个排行榜:
mysqlselectsum(out_value)ov,out_addressfromtx_outwhereout_address''andstatus=2groupbyout_addressorderbyovdesclimit10;+---------------+------------------------------------+|ov|out_address|+---------------+------------------------------------+|3053342387036|1MN37fphKuQepWqvM7UuKFaSxW5bX8nhoR||920423595045|17A16QmavnUfCW11DAApiJxp7ARnxN5pGX||331824606426|1LJWwgDdWMYZGUYRhszGqL1FkrsftEeckP||323000000000|373BRdPtfMycB1yYhzZ2XNPDjvBbYQBsU7||312453740813|1Kr6QSydW9bFQG1mXiPNNu6WpJGmUa9i1g||1|1N52wHoVR79PMDishab2XmRHsbekCdGquK||109540316825|3PA3gFEfTCXU7EAJDeaKpVLUX7EBF5xX4m||102507542525|1DcKsGnjpD38bfj6RMxz945YwohZUTVLby||85003959092|3EC6GCRBCbLGBTHL3xJNUeDeQMELmZsUcD||81224138674|1EEqRvnS7XqMoXDcaGL7bLS3hzZi1qUZm1|+---------------+------------------------------------+10rowsinset(0.05sec)结语
谨希望此文能帮助那些在黑暗中研究和人们,同样给那些对币世界充满好奇的人们。