为了提升工作体验,我入手了一台M1版的MacBook Pro。暂别了使用25年的Windows。经过三个月的不断磨合,终于把细节调到符合预期了,在此记录一下。

外接键盘

一开始为了快速上手,我想直接把Mac改成Windows的手感,把Windows键和Ctrl键换了个位置。但很快发现这个方式并不好:因为白天在办公室我用外接键盘,晚上回家用笔记本的自带键盘,这两个键位不一致导致的混乱更让人难受。所以,

最终还是决定适应Mac的键位,把外接108键盘的手感改成和笔记本键盘一样:

  1. 先把外接键盘的Windows和Alt的键帽互换位置,左右两边都换了。
  2. 在系统偏好设置——键盘——修饰键里,把Option和Command按键的位置换一下。

用着用着发现我很依赖Home和End键,而这两个按键的默认效果又不对,这里用 Karabiner-Elements 修改了Home和End的行为,感觉好多了:

  • Home → Command Left
  • End → Command Right
  • Shift Home → Shift Command Left
  • Shift End → Shift Command Right

外接鼠标

鼠标遇到的最大问题就是滚轮的方向和之前的习惯是反的。这里用了Mos,开箱即用,默认配置就是翻转了鼠标滚轮的方向。它还有一些附加功能,

  • 平滑滚动(而不是一大格一大格的,看网页的时候很流畅)
  • 按住Option让滚轮变成左右滚动

外接显示器

我的外接显示器是一个2K屏,直接接上之后感觉很模糊。这里用 BetterDummy 让外接屏幕显示正常,大致参考这个链接

快捷键

Windows下还习惯了Ctrl+滚轮缩放网页,缩放Word,以及各种能缩放的东西。到了Mac系统,Ctrl+滚轮会触发整个系统的缩放,Cmd+滚轮似乎没有相应的支持。

好在有 BetterTouchTool 这个强大的工具, 简单配置可以让Cmd+滚轮生效:

BetterTouchTool的配置

其它

Parallel Desktop 虚拟机跑 Windows 程序。很给力,文件和剪贴板都是互通的,甚至还有个融合模式,让某个Windows程序的窗口像Mac原生程序一样显示在屏幕上。

十二、斗转星移

狗池历险最有意思的部分就在第一个月;迈入第二个月之后,事情就变得平淡起来。这一章就简略介绍一下后来发生的小故事。

强制升级

大概1月中旬的时候,XPM的难度从9提升到了10。xolo矿机出了个新版,专门为新的难度做优化。我们测试之后发现新矿机的 块/share 比例更高,就强行推荐用xolo矿机。

简单说一下这里的技术细节。质数币的挖矿算法并不像比特币那么均匀。举个例子,如果挖矿算法的目标是找长度为 9的链,那么每秒可以找到 20 个 share,但是几乎找不到长度为 10 的链。而如果把目标定为 10,每秒就只能找到 10 个share了,也有不小的概率可以找到10。这种设计对于只追求 share 的矿工来说,是有动力用 share 更多的矿机的。旧版的 jh 和 xolo 矿机目标是找 9,而新版 xolo 的矿机目标是找 10。所以用户默认更喜欢用旧版矿机。

然而只出 share 不出块是矿池最痛恨的事情。我们根据 块和 share 的比例 给 jh 矿机算了个折扣,凡是用 jh 矿机挖矿的人,share 数量都要打折。新政一出,大量用户的流失。一开始我们以为流失的只是那些想薅羊毛的人,直到几天后,一位大户联系我们说,在他的 CPU 上 xolo 矿机的效率极其低,这才让我们意识到犯了大错。

jh 矿机的原作者已经不维护了,好在我们发现有另一个作者改造了目标为 10 的版本。联系上作者,和他商定好分成策略,请他为我们专门编译了一个版本(限制了矿池的 IP 只能是我们的矿池)。终于可以用上新版的 jh 矿机了。

大多数矿工都很配合升级了新版矿机,还有少数还在用旧版矿机薅羊毛。为了不被薅羊毛,群神也是费了不少精力。先是限制版本号,没想到道高一尺魔高一丈,居然有人自己编译矿机,把旧版的矿机用新的版本号连接上来。

这段攻防大约持续了大半个月。最后 D 神找到了一个终极方法,这是一个数学方法,可以根据 share 的数值检测出他的计算目标是几。这一筛不要紧,我们查出了居然有用户用着非常古老的矿机在薅羊毛,目标为6。

教程页插图

广告

1月8日那天,狗池接入了 Google 广告。广告主要放在了两个地方,banner位和土豪榜的中间。特别是土豪榜中间插入的广告,要多丑有多丑,当时我是十万个反对。结果 D 神一句话说服了我:丑没关系,赚钱就行,丑也能挖矿。现在想起来这种应该属于信息流广告吧 🙂

广告的收入其实不少,特别是土豪榜的丑广告。巅峰时期差不多每个月有60刀,大概能抵一半的服务器费用。有意思的是,矿工似乎是“高价值”人群,狗池广告的 CPM 比我博客的高多了(于是后来博客的广告就去掉了)。

黑池

群神提出过好多种黑池的方案,但因为大家都太正直了,最后完全没有尝试。这些方法包括:

  • 额外划出去一些钱给我们的钱包(狗池2%的手续费已经是全网最低,黑到4%几乎看不出来)。
  • 51%攻击,狗池 DTC 的占有率有60%多,完全可以不承认矿池外的块,直接变成100%收益。
  • 把 BEK、CDN 的算力导到 DTC 或者 XPM 里。毕竟我们已经占了 BEK 100%的市场,实际上有多少算力挖矿根本不重要了。

其实我们也在多个矿池做过公平比较,实验结果看,我们的收入一直是最高的,甚至比某些矿池的收入多一个数量级。也不知道它们是技术问题还是太黑。

群神一直都坚信走正道才能赚大钱,只可惜市场大环境一直在下行,还没等我们占领更多市场,币就快没了。

跑路

2014年2月7日,当时最大的比特币交易平台 Mt. Gox 宣布自己客户的比特币被盗,自己要倒闭了。之后的很长一段时间比特币一蹶不振,山寨币也跟着加速地跌。

随着市场慢慢变冷,我们的收入也越来越少,到最后,连服务器的成本也支撑不了了。7 月的分红日,群神不但没有收入,还一起凑了 800 元交服务器费;到了 8 月,感觉盼不到下一个春天了,就发了公告准备关机。

最后根据老黄历,我们把关机时间定在了2014年9月8日。截图留念。

关机当天网站首页截图

质数

作为一个数据完整性强迫症的患者,狗池获取的所有质数都被我保留下来了。总计大约20亿,或许能给相关研究人员提供一些素材。(附wiki链接:第一类坎宁安链,第二类坎宁安链,和双坎宁安链

质数链长度 质数数量
61846635074
7192006179
817747003
91067364
1054265
111578
1259
131
狗池搜集到的质数数量,按质数链长度统计

这些质数存放到百度网盘上了,总共118G。

链接:https://pan.baidu.com/s/1p5v8_btFYcouxMl8QOn60A

提取码:gpol 

再回首

再回首 恍然如梦

再回首 我心依旧

只有那无尽的长路伴着我

很好听的老歌《再回首》。

从开始做矿池至今已经有接近 8 年的时间了,从写第一篇回忆录至今也已经 5 年了。翻着聊天记录和日记可以慢慢复原出当时的故事,但已经很难还原出当时的精气神。也许是自己水平有限吧。

回看狗池的经历,年轻时的激情让正在创业的我也略显惭愧。五年前挖的坑在此填上,草草收尾,更是为了向前看。

十一、抢币行动

2014-01-15 14:42,金牌客服X传来急讯:大户Q的钱包被盗。

矿池“错误”页面插图(404等)

虽然事情很紧急,但还是有必要先强行科普一下,虚拟币的钱包被盗和现实中的钱包被盗有什么区别。

简单的理解,现实中的钱包被盗之后,钱包就在小偷手里了,里面的钱一般也回不来了;但是虚拟币钱包被盗之后,钱包同时在自己手里和小偷手里,这就有挽回余地了。这有点类似网游账号密码被黑客知道了,自己和黑客同时拥有了这个账号。在以前网游安全机制不完善的时候,黑客拿到账号会先改了密码和密保问题,然后把道具,甚至账号都卖了。不过,虚拟币的特性从数学上保证了这个“密码”(私钥)是不可修改的。既然这时候的钱包同时被自己和黑客控制,那就要跟黑客拼手速了,看谁先把钱拿出来。

说得技术一些,我们通常看到的虚拟币钱包地址,是类似这样的一个字符串:1icstarNBYj6F9D8eqGXJuf2JpxTuyT9c(欢迎土豪打赏这个比特币钱包)。给别人转账的时候,就是要填别人的这个钱包地址。那么,怎么证明自己是这个钱包地址的主人呢?所有的证明信息都在wallet.dat这个本地文件中。事实上,wallet.dat存储的是私钥,而钱包地址就是对应公钥的hash。每一笔从自己钱包出去的转账,都需要自己的私钥签名才会被虚拟币网络认可。所谓钱包被盗,其实就是wallet.dat被黑客拷走了,于是黑客就有权限用里面的钱了。

到这里,其实还有个问题没解决。从直觉上想,黑客应该会在拿到虚拟币之后,马上把里面的钱都转走。一旦转走了,由于虚拟币的匿名特性,就再也不用想找回来了。如果黑客悄悄地拷走了钱包,然后把钱转走,消失,等我们反应过来的时候,钱已经没了。那这还有什么可以挽回的吗?还真有。因为里面有一部分钱,还没“成熟”,要等几个小时才能转。

这次大户被盗的是他挖矿的收款钱包,由于是在狗池挖的,所有的币都在挖矿时发放。虚拟币有个很有趣的设计,为了防止因为51%攻击,所有虚拟币都要求:挖到的币,要被N个后续的块承认后才能使用。对于质数币来说,N=3200,平均每块一分钟。也就是说,刚挖到质数币需要在两天之后才能使用。对于虚拟币而言,这种保守的设计可以大幅提高虚拟币的稳定性,也让这次抢救成为了可能。

回头看一下大户,在钱包里面还存有价值数万的未成熟的币,所以现在的最佳办法,就是一旦一个币成熟了,就马上转到自己的新钱包里。当然,黑客也知道这点,也会掐着成熟的时候,马上转到黑客自己的钱包里。

简单了解了事情的经过,大户从群里下载了一个嵌入了木马的挖矿程序,估计这个木马扫描了整个硬盘上所有的wallet.dat,并传给了黑客。现在大户所有虚拟币都在危险之中。

10分钟后,狗池提供了紧急抢救方案:提供程序化转账服务,为大户尽可能降低损失。

之后的一小时,金牌客服处于失踪状态。原来她已经完全得到了大户的信任,拿到了大户的钱包,一起帮大户手工抢币。大户和金牌客服两人应该是和一个黑客在比手速,成功率基本和参与人数成正比:70%。

在金牌客服的建议之下,大户采纳了我们提出的程序化抢救的方案。程序化抢救的要点就是在一个新币(块)刚刚成熟的时候,马上提交转账申请。提交过早会被网络拒绝,提交慢了就被黑客转走了。到这里,我们还天真的以为,程序的速度肯定比手工要快。实际上真正的瓶颈在于P2P网络的同步时间。一般虚拟币P2P网络的同步时间在两秒之内,也就是说,当有人挖到一个新块(同时意味着此前的第3200个块成熟了),通过P2P网络广播出去,大约两秒之后,全世界都知道了这条信息。对于矿工而言,知道了这条消息,就可以马上放下手中的活,开始挖下一个块了;对于此时的我们,就可以马上发出转账请求了。虽然这个延时只有短短的一两秒,但这足以让黑客手工完成转账操作。为了更快地得到这条宝贵的新币成熟的信息,我们在中美两地多个机房部署了钱包抢救程序,其中一个节点就在我们狗池的同一个机房,占尽先机。经过这一番改造,我们基本保证获取信息的时间在0.5秒之内,就算黑客比我们早0.5秒得到消息,手速也比不过程序。

大约一个半小时之后,分布式程序化抢救方案上线。抢救成功率上升至100%。最终共为大户挽回损失N万元。

p.s. 几天后,金牌客服收到大户赠送的 iPhone 5S。

十、转型——从公池到私池

私池,我们早有耳闻,但一直看不上眼。曾经觉得只有公池才能做大,才能多赚钱。但是,大户Q的出现让我们改变了这一想法,原来头部用户这么重要。

我们很快发现了私池对我们还有更多的好处。首先服务器可以开的很小,毕竟不用考虑分钱的事了(share的计算是大头)。其次,如果我们给每个大户分配一个IP,那么无论哪个IP被DDoS,吃亏的也只是泄露IP的人。最后甚至连网站界面都不需要做了。

经过简单地讨论,我们很快达成了一致:我们少赚点,看看他愿不愿意,手续费降到 1%。很快,金牌客服X带来了出乎意料的好消息:“嗯,费率你改5%,不对外就行了。反正有钱一起赚。”

“感动死了”群里开始刷屏。很快又开始了“金牌客服 X 神”的第二波刷屏。

其实从概率的角度看,私池并不会比公池有优势。但是大户似乎不相信这一点,而坚持认为私池的话钱就不会被其他人分走了,所以私池更好。好吧,我们也解释不清楚,做就是了。

“土豪榜”页面插图

云币(CDN币)

大户 Q 想让我们做云币的私池,云币是一个纯忽悠的币,从名字就能看出来。它号称用云计算预测未来天气,它的英文名 CDN 说明了作者心目中 CDN 就是云计算的代名词。云币无论官网还是论坛的信息,都显露出一丝不着边际的高大上。论坛的发布贴甚至还用上了当时的最高规格,四国语言同步发布(不是机翻的那种)。

一开始我还很有兴致地去看云币的代码,看看他怎么预测的天气。结果发现它只是从 XPM 的代码 fork 出来改了个名字,根本没有什么天气预报。

这么山寨的币,要是我们自己调研下来,肯定是不会碰的,感觉第二天就会挂掉一样。但现在不一样,这是大户需求,他有办法盘活韭菜,我们跟着喝点韭菜汤也是很好的。

D 神花了半天时间改好了云币的矿池,金牌客服 X 很快让大户开始测试。5%,30%,70%,直逼80%。短短一小时,大户仅仅是切了一些测试机过来,就占据了整个币80%的算力。金牌客服 X 透露说,大户那天晚上正在开心的数钱。

可惜好景不长,第二天,云币作者宣布做出来了官方矿池,吸引大家去挖。但是这个矿池 bug 无数,根本不出块,群里哀声怨道。作者一气之下居然把云币矿工群的韭菜都踢了……这可了得,这个币真的要活不过一天么,我们还没卖呢。

大户 Q 看到这个趋势,马上提出需要我们开公池,保住云币。几小时后,云币公池上线,缓和了韭菜们的心情。也保住了我们那些微薄的收入。

“大户”们

大户们似乎有个自己的圈子,定制了第一个私池后不久,开始源源不断地有“大户”来联系我们。他们的需求千奇百怪:

  • 最主流的需求就是和大户 Q 一样,希望独立出块,钱不会被别人分去。
  • 有个大户似乎是机房管理员,他会希望我们帮他做不同币种不同矿机的收益测试,挑到最后选了 ripple。
  • 还有个大户似乎在用公司的电脑挖矿,说不能连接矿池公开的IP地址,需要我们提供一个专用的IP给他。
  • 也有个外国友人问我们的底线在哪,接不接受他朋友控制的肉鸡来我们这里挖矿。
  • 最坑的是一个口气很大的顾客,要求做一个很奇怪的定制,类似于nicehash的自动分配挖矿,可以切换钱包地址的。因为他那里的启动脚本只能用同一个,而他是通过代理技术切换不同的服务器的。我们信以为真,吭哧吭哧开发了一天,后来发现他的算力连1%都不到。

这些奇葩需求做起来很没有成就感,后来我们接需求之前会请大户先证明一下自己的身份,用自己的收款钱包签个名。我们看到有足够的收益才做。

不过倒是有个意外的收获。后来狗池 DTC 真的被 DDoS 攻击了,而当时私池的算力差不多有一半,攻击者发现攻击之后矿池还在出块,就以为我们已经可以防 DDoS了,很快就停止了攻击。

九、内存币——残酷的现实

  内存币矿池从想做,做出来,上线,一直到关门,总共不到 10 天时间,但回想起来比狗池其它所有的经历加起来更复杂。那十天无论是技术的踩坑、市场的折磨、还是团队内部的争执,都让人感到心累。我试着尽量说清楚那几天的事。

惊现大户

  故事的开头是充满惊奇的。12月24日下午5点,X 神突然给我们带来了内幕消息“大户问我们能不能做 MMC 的池”,因为这个币马上要上交易所了。“大户”?所有人同时冒出了黑人问号。虽说当时大家都忙着修矿池爆内存的问题,但是这条劲爆的消息还是吸引了所有人的注意力:

  • 大户是谁,在我们矿池挖吗?
  • X 神是怎么接触到大户的?
  • 大户是怎么知道 X 神在做矿池的?
  • 低调了两周最终还是没能隐藏身份么……

  一团团疑云笼罩在我们头上。

  在众神的催促下,X 神贴出了他与大户之间的聊天记录。原来 X 神在多天之前买币的时候第一次接触了大户。她凭借社交名媛的气质,吸引了大户的注意,而后又“不小心”透露了狗池是“学长”带着她一起做的,当然她自己完全就是打杂,什么都不懂……在获得了大户的信任之后,X 神晋升为金牌客服 X,我们也因此获得了和大户沟通的直接渠道。

  说起这个大户(大户 Q),在矿池的收入排名是第一名,外加第二名、第三名和第四名,一人承包了矿池几乎一半的收入。而他分四个账户来我们这里挖矿,并不是为了低调,而只是为了方便他的管理。因为他的算力来自于不同地区的网吧。

  大户 Q 和网吧老板合作,如果他在闲时挖矿,就给老板固定的分成。对于网吧老板而言,多花一些电费,就可以得到比电费高很多的合作费用。与此同时网吧的顾客几乎对此没有感知,因为用的是闲时CPU。

  我们看来,大户 Q 凭借那几万台弱机器,可以完虐那些管几十台E5 n核服务器的机房管理员。我们之前遇到的那次“DDoS”也是他的杰作。当然,后来这些弱机器也狠狠地坑了我们一把,暂且按下不表。

内存币

  回到正题上。虽说这还是我们第一次听说“MMC”这个币,但是上交易所这样的内幕消息,对我们来说确是求之不得。更重要的是,哪怕单凭大户 Q 的算力,也足够养活我们了。可惜的是,我们仅仅是意识到这是一个机会,却没有重视起来。技术宅们还沉迷于优化矿池的内存消耗问题。

  D 神用了一些碎片时间初步探索了一下 MMC,这个币全称是 Memory Coin,中文叫内存币,也是一个 CPU 币。内存币之所以只能用 CPU 挖,是因为它需要 1G 内存。计算前,它会先根据当前状态,创建一个 1G 内存的缓存,并且根据缓存里的数值,反复跳转几十次,最终挖到矿。如果用 GPU,给每个计算核心分配 1G 的显存几乎是不可想象的。当时主流的挖矿显卡,只有部分 A 卡的显存是上 G 的,但也只能调动起其中的一两个核心来挖矿,显卡的优势荡然无存。

  截至内幕消息那天,MMC 才发布 7 天,市面上还没出现矿池,留给我们巨大的想象空间。

  痛苦也就此开始。

  先吐槽一下 MMC,它就是一朵奇葩。比如它每出 20 个块,就会有一笔钱直接打给币的作者。于是我们必须要去适配这种特殊的策略,要不然挖到的块就没法被网络承认。当然这只是其中一个点,还有数不清的特殊实现等着我们一个个去适配……

  而我们遇到最大的技术障碍,是矿机的问题。我们刚开始尝试的第 1 天,由于市面上还没 MMC 矿池,所以也没有 MMC 的矿机,我们需要根据钱包代码自制一个矿机。结果到了第 2 天,当我们刚折腾出一些眉目的时候,第一个 MMC 矿池出现了,矿机也有了。我们又转去适配这个矿机……这个矿机各种不靠谱,稍微跑一会就会 core,还得配一个自动重启的脚本。吐槽归吐槽,最麻烦的是这个矿机的协议和之前质数币的完全不一样,甚至不是用长连接的,而是不停地发 HTTP 请求去轮询……经过半天的折腾之后我们放弃了适配,回来自己写矿机。

  多说一句这个第一个 MMC 矿池,一定是快糙猛的杰作,因为矿池上线几小时之后就关掉了。主页上写的是:用户数超过了 1000 人,遇到了我们“早就预料到的”性能问题,所以只能关了……看到后我们心中窃喜,看来我们双核机器支撑十万连接,还是挺有技术门槛的。

  到了第 3 天的上午,第二个 MMC 矿池:mmcpool 出现了(这个矿池的名字就叫 mmcpool)。短短 5 小时,这个矿池占领了 90% 的全网算力,速度惊人。我们估算了一下他的现金流速度,心态崩了。终于把所有精力都集中到赶制 MMC 矿池中来,誓要今晚发布。可惜的是,这个矿池的矿机我们也没法很快适配,再加上这个矿机也有很多设计不合理的地方,比如在切换块的时候会浪费很多算力(大约10秒)。最后我们还是继续开发自己的矿机。

  第 4 天,D 神全力开发矿机。mmcpool 已经占了99%的市场。在这样的环境下,我们的战略不得不定位成:必须要一次成功。

  第 5 天傍晚,我们的矿机经过一天的测试,可以发布了。凭借狗池在市场里口碑,发布之后很快获得了一些算力,不过也就只有这一些了。估算了一下,理论上需要 4 小时可以出块。然而……4小时过去了,什么都没发生。80% 的矿工失去了信心,去了别的矿池,这下出块更难了。

  团队内部也开始起了争执,分为了“一定有bug派”和“一定是人品不好派”。在数小时中,有bug派持续输出,一边审查代码,一边对人品不好派提供的各种证据提出尖锐的质疑。大家看了代码,推了公式,写了模拟代码,争论了两小时,最终达成了一致,真的就是人品不好。

  终于在快到 24 小时的时候,出块了!其实这时,按照期望应该出了两个块,如果用泊松分布估算,我们的运气属于全宇宙最差的15%……看着钱包里这份微薄的收入,大家泪流满面,五味杂陈,只能感叹一下代码没有bug。

  心累。

大户加入

  说回大户,当初我们开 MMC 矿池,就是因为是大户 Q 的建议。然而为什么我们发布了一天多,还不见大户来捧场呢?

  其实在做内存币之前,我们就意识到了,大户 Q 的主要机器都是低端机器,而且是网吧的空闲计算资源,留给挖矿的内存就不多了。但我们没想到的是,他那几万台机器绝大多数只能保证有 256M 内存,如果需要1G内存,可能就只能拿得出几十台……

  内存不够怎么办?群神都是做算法出身的,大家不约而同地想到了用时间换空间的方法,经过 D 神一天的实现和优化,最后在 256M 内存挖矿,大约也可以达到 1G 内存的 ⅓ 性能。

  定制没有白费。第 7 天的傍晚,金牌客服 X 带来了大户的消息:“大户先开几十台 1G 内存的 E3,然后几千台 512M 一线程,最后几万台都连上,把其他池都干掉。。。”这些豪言壮语听着开心,但是我们心里都在打鼓,扣除了 ⅔ 的性能之后,对大户来说挖 MMC 还划算么,性价比不高的话,或许最后只能“帮个忙”。

  大户的几千台机器进来,立竿见影,没几分钟,矿池就出了一个块。恰好在此时,mmcpool 挂了,借着“双喜临门”的势头,狗池一下子吸引到了大量的矿工,我们的双核机器也赶紧升级到了8核,确保服务质量。可惜的是,mmcpool 一恢复,好多矿工又跑了回去。

  说实话,mmcpool 的技术肯定是比我们差很多的,比如前面提到的,他们的矿机会浪费10几秒的算力,而且 reject 率很高,也就是矿工的无效计算非常多。所以比起来,在我们这挖矿,要比他们的收益高30%以上。照理说,矿工是非常趋利的,实际上呢,mmcpool 的头部效应太明显了,矿工们普遍会倾向于去出块多的矿池挖矿,毕竟跟着大多数人走,比较保险。R 神叹息道:“印证了互联网产品重要的不是技术”。

  大户最后确实没有拿他的主力机器来挖 MMC,不过在他象征性的帮助之下,狗池的算力稳定在了全网算力的 20%到30%之间,保持了两天。这两天我们也没闲着,比如把矿池做成了动态 share 难度的,这点对于矿池的稳定性非常重要。因为 mmcpool 经常会挂掉,我们时不时会因此得到大量的短时算力,可能在半小时内,我们的算力就会翻倍,这也意味着矿池的 CPU 消耗也要翻倍。这套策略,会在算力增大的时候,以合适的速度提高 share 难度,反之亦然。我们设计的这套策略在当时似乎非常先进,几乎没有其它竞品在用,当然现在的矿池基已经标配这个了。不过这种锦上添花的东西矿工不太有感知,更多的只是降低我们的运维成本。

  除了矿池的优化,我们还对矿机做了很多优化,技术上,适配了各种古老的操作系统,做了各种编译优化。用户体验上,我们把运行脚本一起打包进了压缩包里。因为我们发现很多人会改 bat 脚本,却不会自己创建一个新的 bat 脚本,所以光写在文档里还不够……

  其实大户加入后的第二天,还发生了一件事,有个新的 MMC 矿池出现了,号称支持用 GPU 挖矿。我不想把它的名字写出来,就叫他“GPU 池”吧。GPU 池也有大户加持,刚上线就占了半壁江山。GPU 池似乎有点黑,我们实测下来收益不到狗池的一半,仔细分析来看,不像是他技术不行,而更像是算力被他黑走了。

DDoS

  经过一周的折腾,MMC 矿池总算走上了正轨,正当我们想歇一口气的时候,又一个杯具发生了。早上 7 点,金牌客服 X 突然在群里着急地说,矿池连不上了!

矿池在线算力为0

  口碑不能砸,D 神赶紧到电脑前排查,一看,DigitalOcean 发了一封站内信,说我们的 IP 被人 DDoS 了,所以他们就把那个 IP 封了。申请解封之后不到 10 秒,系统又自动把这个 IP 封了。我们装了抓包软件,看看到底是什么样的攻击,又申请了一次解封,在那上线的几秒钟,我们看到了 1Gbits/s 的巨大流量(也不知道是不是网卡被打满了)。之后分析发现,是一种叫做 UDP DNS Query Flood 的攻击。虽然我们的服务器配置完全可以抵挡这样的攻击,但对于机房来说,这么大的流量会影响他的正常服务,就选择把我们关掉了。我们后来怎么申请,DigitalOcean 都不肯给我们解封了,只祝福我们“I hope you have better luck in the future”。

DDoS 当时用 nload 命令看到的效果

  这个攻击者一看就是圈内人士,他只攻击了我们用来挖矿的 IP,而完全没去动我们 web 服务的 IP,一点资源都没有浪费。与此同时,mmcpool 也正在被攻击,只有那个 GPU 池还活着。把一个币交给一个黑池真是让人心痛啊。

  我们调研了几小时,发现要防 DDoS 对我们这种做小本生意的人来说是无解的。如果套一层 CDN,矿机的长连接就很难保持。如果选那些防 DDoS 的机房,成本又远超我们的收益。当时我们能找到的防 DDoS 产品,每月要付大几千,而我们那两天从 MMC 中得到的收入只有每天 300 元,况且这个币还不知道能活多久……

  最后我们把矿池公告改成了“矿池正在被 DDoS 攻击,欢迎选择竞争对手的产品”,直接放弃了这个市场。虽然心有不甘,但更多的是如释重负。至少我们还有 DTC 矿池,给我们带来稳定的收入。大家也开始自嘲,被竞争对手认可了,好开心;狗池让我见识到了 DDoS,这辈子值了。

  金牌客服 X 的一句话打断了群里的自嘲:不如先弄个私池,让大户 solo。

八、性能危机——CPU、内存、磁盘,成本啊……

CPU危机

其实这个 CPU 危机,就是上一章里挖的坑。矿池经过几天的发展,四核的机器也几乎已经满了,特别到了周末,挖矿的人也开始变多。必须要想办法优化了。

优化的过程充满了戏剧性,H 神抱着试一试地心理,搜了一下,发现 Go 语言可以调用 C 语言写的 so 模块。然后 H 神又抱着试一试的心理,试了一下两个 Go 和 C 版本 share 检查程序的速度,发现居然 C 比 Go 快 10 倍!于是这个问题就这么解决了,此时距离矿池上线刚刚一周……(要是一开始就知道可以 Go 调用 C 就好了,也免去了 H 神花了好几天把 C 的代码翻译成 Go 的,现在又要亲手把它废掉……)

那些年 Go 语言刚出来,宣传里面有提到说它是编译语言,性能非常好,堪比 C。大家听后都觉得顺理成章,都没想着怀疑一下,结果在我们这个计算密集的程序里,居然有 10 倍的性能差距。

新版上线之后,CPU 使用率有了非常显著的降低,从此我们再也没担心过 CPU 的成本。有趣的是,就在我们更新新版之后的第二天,矿池的用户又开始猛增。要是没有 H 神的及时优化,CPU 占用应该已经到了红圈的位置了。


cpu
优化前后 CPU 使用率

内存危机

内存问题其实早在矿池上线第一天就出现了,一直在优化,从来没解决。CPU 的瓶颈解决之后,内存更是直接站在了瓶颈的位置上。DigitalOcean 上,CPU 和内存是搭配出售的,如果我选双核,那就只能搭配 2G 或者 4G 的内存;如果选四核,那就只能搭配 8G 的内存。现在 CPU 用四核绰绰有余,但是 8G 内存还是非常吃紧。还是直接看图吧。


memory
优化前内存使用率

可以看出内存的增长速度非常可怕,大概半天时间就会内存溢出,导致矿池挂掉。图里面的每次内存直线下降,都是因为重启了矿池,可能是因为挂掉重启或者升级策略主动重启。

照理来说,重启前后的内存消耗应该是相当的,可能少数用户会因为重启矿池而放弃重连,大部分用户还是会连回来的。但是图片里的这个直线下降的效果可不是这么说的,重启前后的内存差异非常的大。这只有一种可能,内存泄露了。连接矿池的矿机可能处于各种不同的网络环境下,断线是常有的事情,所以矿机一般都有断线自动重连的功能。再加上有些用户只在开机的时候顺便挖个矿,“翻台率”其实非常高。要是之前的内存不能及时释放,那最后肯定就是图中的效果了。

其实Go语言是自带内存回收机制的,当时的版本是 Go 1.2,传说这个版本的内存回收还不太成熟。我们自己体验到的就和传说中的一样。还有传说 Go 1.3 会改好 GC 问题(据R神介绍,实际上到了 1.5 版才算改好了),不过估计还得等大半年吧,可能等不到那一天矿池就倒闭了。那剩下的路子似乎只能是少申请内存:申请次数要少,能公用的内存就公用;申请的量也要少,这样至少可以减缓增速,多活几天。

优化的过程当然是非常的苦逼,自己挖的坑就得自己填,当初快糙猛写的代码,内存用的很随意,现在就一个个改吧。大多数还是非常好改的。除了直接改内存,我们还尽可能缩减“go”关键词的使用,每一次“go”都会有一些开销,积少成多。最早我们为了效率,把逻辑上可以并发的所有操作全都 go 了,经过这次精简,每个矿机只保留两个协程,一个等新块(与主程序通信),一个等 share(与矿机通信)。

经过两天的改进,内存终于稳定下来了,基本可以持续运行了。

硬盘危机

硬盘危机一开始还真的没想到,像我这种有数据完整性强迫症的人,特别不喜欢删数据,终于在矿池运行三周之后,硬盘容量告警了。实际上最占硬盘的只有两部分,一个是日志,一个是数据库。日志其实就是存储了每个用户的 IP,通信交互等情况,用于紧急的时候排查问题;数据库存的是所有的账单信息,每个人为什么这么发钱,根据什么算的。

随着时间的积累,这两部分数据都越来越大。当然我们也一拖再拖,一直拖到 1月12日,如果这天再不备份数据库,第二天连备份数据库的空间也没了……

其实硬盘危机相比前面两个还是很好解决的,日志就打包之后就传到另一台备份机上。数据库每天凌晨定期热备,然后只保留一周的流水,和当前的状态数据,剩下的数据全都直接删掉。备份文件同样也是压缩传到备份机上。这个备份机其实也是 VPS,只不过是内存少硬盘大而已。我们选了一台 512G 硬盘的备份机,可以用很久了。相比之下矿池的 Web 节点和矿池服务节点都只有 30G 硬盘。

当然,做这三个优化其实都是因为成本问题。我们在做优化的时候,土豪 F 神一边围观我们优化,一边幽幽地说“升级机器”。土豪的视角还是很先进的,我们其他人只关注了硬件成本,而没有关注人力或者时间成本。特别当我们在做内存优化的时候,我们就已经错过了内存币矿池的先手机会。

七、幸福的烦恼

  还没睡到 3 小时,就被 D 神的电话叫醒:矿池挂了……两人一通排查,认定是使用人数太多导致了内存溢出。不用多说,既然内存不足,就升级机器吧。当初我们选择 DigitalOcean 就是因为它支持快速升级,万一配置不够,点点鼠标,几秒钟就可以升级到更高的配置。重新打开矿池,一切指标都正常,不过我也已经进入了兴奋状态,睡不着了。

  我们一边庆幸狗池选在周六发布,这样周日不管出现什么事情都可以及时修复。另一方面也在考虑,有没有什么方法可以及时找出异常,最好在矿池挂掉之前就能发现苗头,及时解决问题。这时,沉默已久的M神突然出现,秒杀了这个需求。M 神有着丰富的运维经验,他手上的服务器多得需要用两位字母加两位数字编号来命名。M 神为几台矿池服务器安装配置了 Munin,这是一个可以在 Web 端一目了然看到 CPU、内存、硬盘、网络等重要系统指标的监视工具。这款工具还可以方便地配置告警条件,比如硬盘快满的时候会提醒我们。这给我们之后及时发现“幸福的烦恼”提供了大量的便利。

分钱 Bug


矿工账单页面插图
矿工账单页面插图

  看着 X 神画的 gopher 开心分钱的图,心情也是非常的舒畅,但在矿池发布的第二天下午,我们的心情却是非常紧张的。凌晨我们已经见识到了狗池在连续出两个块的时候分钱会有问题,现在随着用户越来越多,出块也越来越快,这个 bug 就发生地更频繁了。这还好,一开始多分了钱,后面就少分点,还是可以慢慢纠正回来的,但是另一个 bug 就更要命了:我们发现,当两个用户同时出块的时候,比特币钱包会告诉我这两个块都成功了……这下就麻烦了,我们以为发出去的钱,其实并没有发出去,后面算的钱就全都错了。一旦有人发现收到的金额不对,就会怀疑狗池在黑他们的钱,这可了得,口碑坏了就什么都没了。

  保证分钱的正确性是狗池的首要目标,我们这帮技术宅什么都没,只能靠着目前看来还不错的口碑让狗池继续壮大。第一个 bug 经过 R 神周日加班终于搞定了;第二个 bug 在我看来是比特币钱包留下的坑,既然钱包里不加锁,只能在矿池里加了,结果我加了一堆锁,总算是把这个问题绕过去了,这时的代码已经丑得我不想再改了……几天后,D 神提出了一个完美的无锁方案,彻底解决了这个问题,当然这是后话了。

新版上线

  上线一天,狗池的代码还充满了 bug,修修补补免不了要重新发布新版本。作为一个网络服务,发布新版本有着与生俱来的优势:只要在服务器上更新一下就可以了。但是作为一个矿池,上线新版本却又不像网站更新这么容易。首先,我们得保证分钱不能错,share 数据都存在内存里,需要及时备份到硬盘,已经分配的钱需要确认写到数据库里了。其次,重启时间要尽可能快,时间就是金钱,停 1 分钟可能会少赚几毛钱,但是如果停了 10 分钟,可能有些矿工就会跑去别的矿池了,所以这里一定要争分夺秒。

  我们最终采用了一种低成本的土办法来上线新版矿池。首先 touch exitpool 发出退出指令,当矿池发现存在“exitpool”这个文件时,就会开始备份各种数据。与此同时,我们人肉观察什么时候矿池结束运行,一旦发现停了,就马上运行新版。新版跑起来之后就要观察指标,万一指标异常,要马上回滚运行之前的版本。回滚命令和运行新版的命令在操作之前都会提前写好,到时候只要直接粘贴就可以了。其实这套方案可以用更自动化的脚本来完成,不过起初我们担心会出错,就在人工监视下一步步执行,后来 bug 少了,矿池也很少更新了,一直没人去写这个脚本,所以直到最后,我们还是这么纯手工地重启矿池。

“DDoS”

  那是一个风和日丽的周一下午,狗池经过昨天的调教,已经稳定运行了大半天了。开完了组会,我回到电脑前,看到群里已经有了 N 条未读消息,就冒了个泡“长求总”,紧接着群里就开始刷屏“报告包工头,我们被 DDoS 了”。

  其实对于 DDoS,我们早有耳闻,论坛上经常看人提到说某某矿池今天被人 DDoS 了,也有一些矿池主打就是抗 DDoS,用着省心。在做矿池的时候我们也考虑过防 DDoS,不过想着这怎么也得两个月之后才需要考虑吧,怎么这不到两天就……

  说回这次 DDoS,其实我们之前都没见过,为什么这么快就认定这是被攻击了呢?回想起下午 1 点多的时候,矿池在线的矿工还只有 1000 人,没想到在之后短短的一小时里,矿工数猛增了 10000 多人,直接导致 CPU 满了。奇怪的是,虽然矿工数多了十倍,但是每分钟 share 数却不见怎么增加。D 神查看日志发现,新进矿池的这 10000 多个“矿工”行为非常异常,一方面大量反复重连,另一方面又几乎不参与计算,因此认定这绝对是被攻击了。

  虽说 DDoS 是分布式攻击,但是经过统计发现,这次攻击的 IP 比较集中,D 神、R 神、S 神经过讨论决定,先封杀连接数最多的 100 个
IP。当然,封杀 IP 这么高大上的功能,是不会存在于我们这么快糙猛做出来的矿池里了。好在 Linux 下,可以简单地配置 iptables 直接封杀来自某些 IP 的所有连接。就这样,经过 D 神的一番操作,矿工数降到了 7000。攻击者似乎也意识到了 D
神的封杀,放慢了攻击脚步。此时是下午 4 点,距离发现攻击刚过一小时。

  但是这种手工封杀的方式毕竟不是长久之策,最好矿池就有识别恶意连接的功能,直接封杀那批伪矿工。于是我开始仔细分析这些伪矿工的行为。又一番统计之后,我惊奇地发现,那些伪矿工其实也在计算,只不过计算性能非常非常的低。之前见到的矿工,大多是职业矿工,他们用 8 核或是 32 核的服务器来挖矿,也有少量矿工可能是用自己的双核、四核电脑在挖。但这批新矿工,不仅在用单核挖矿,而且挖矿效率低的惊人,基本只有普通单核的四分之一……

  那么问题来了,我们要怎么对待这些低效的矿工呢?封杀似乎不一定合理,说不定人家真的就是拿着大量的烂机器,想好好挖矿呢……而且对于我们矿池来说,再弱的矿工也是能给我们带来收益的,我们更应该好好对待他们啊。

  既然暂时选择了要好好对待每一个矿工,那只能在自己身上想办法解决问题了。现在的瓶颈在 CPU 上,那么就只有两条出路了,要不优化程序,要不升级机器。回想起昨天才刚升级了机器,现在就要升级了,这还没怎么赚钱,成本就越来越高了,会不会倒闭啊……

  我们还是先优化一些已知的问题吧。比如之前为了调试,打印了大量日志,现在是时候砍掉大部分了。经过一番简单地优化,上线一看,确实 CPU 使用率下降了,但是下降地非常微弱(图中箭头处)。


CPU占用率
服务器 CPU 使用率变化曲线图(可以看到 CPU 占用在 2 点多开始突然上升)

  实际上最方便的找瓶颈的方法是使用性能分析工具。Go 语言也有(pprof),但是似乎不太给力,我们在线上环境测试了一次,分析报告等了一小时还没出来。但是如果线下测试,由于计算量太小,又找不到瓶颈在哪。所以最后还是只能回到人肉分析。

  于是我们又一个个模块地分析,在分析到 share 验证模块的时候,我突然想起 H 神写完这个模块的时候提了一句,“每次计算需要 70ms”。现在的 share 数量大约是每分钟 2000 个,也就是说,每秒的计算量是 0.07*2000/60 = 2.3 CPU 秒,一秒钟有两秒多的计算需求,这已经超出了双核 CPU 的极限。看来瓶颈就是在这了,但是这部分计算是必不可少的,而且验证 share 的算法也是非常成熟的,我们也不可能一时半会把它优化下来。哎,还是花钱求平安吧,继续升级服务器。

  晚上 8 点,在大家十万个不情愿之下,终于还是把机器升级到了四核,CPU 安静地飘在 75% 的位置上,连接数居然到了接近 2 万;挖矿速度也有了巨大的提升,矿池的算力已经接近数据币全网算力的 50%。看起来之前矿池在双核机器上跑是被严重地压抑了性能。

  矿池终于回到了正常状态,可以暂时歇一口气了。接下来的主要任务是优化程序的性能,避免成本一直上涨。但是在此之前,我们还是很想八卦一下这接近两万台机器,到底是谁的。可以肯定的是,这些机器挖到的矿都是给同一个钱包的,说明这些机器都是一个人的。但是谁会有这么多机器呢?我们想来想去觉得只有黑客手中的肉鸡,才会有这么大的规模。但是从 IP 分布来看,这些机器都集中在几个城市,网段也很接近,像是几个网吧的。不过网吧一般都有还原卡,不太容易中木马……真是越想越绕,不知道到底是何方神圣。还是随他去吧,能让我们赚钱就行。

六、今晚上线——人品、人品和水军

  开发过程看似持续了两周,其实最后几天都在集中测试。最难测试的就是挖到区块之后的提交功能。当时我们所有参与的人都开着机器跑挖矿程序,就算这样,也要一整天时间才能挖到一个块,获得一次珍贵的提交机会。在修复了若干 bug 之后,终于迎来了“今晚上线”的这一天。

  其实这里“上线”更确切地说应该是公开矿池的地址,吸引矿工来挖矿。毕竟要真的说上线的话,矿池已经在线跑了好几天了,还在我们这么多“矿工”的努力下,正常出了两个块。

  对于这次“上线”,大家心里都没底,毕竟我们这帮技术宅完全没有搞营销的经验,在矿工圈里也没有什么熟人。看来只能硬着头皮小心翼翼地贴小广告了。

  首先拟了个发布稿,贴在当时最“官方”的 bitcointalk 论坛(山寨币的发布,都在这个论坛),开头是“我做了个新的 DTC 矿池,欢迎大家来测试……”。其实第一版稿件里用的全是“我们”,后来一方面为了装 B,另一方面为了给矿工一种好说话的感觉,把稿子里所有的“我们”都改成了“我”。这个规矩一直严格执行,直到矿池倒闭。

  等论坛帖子发布 10 多分钟后,我那个潜伏在数据币矿工群的小号终于发挥用处了,发出了这条关键信息。

小白水晶 2013-12-14 22:13:18
好像有新的矿池了?不知道靠谱不,论坛里贴的 https://bitcointalk.org/index.php?topic=370920.0

  这种弱弱的语气也是我们一群人讨论了很久之后的产物。这条消息之后,原本活跃的群突然就安静了……诡异的安静让人有些不安。要知道那时候的矿池,还没网页,只有帖子里的这几句话。要是就这么被认作黑池而没人上钩,我们就要白干一场了。五分钟后,群又恢复到之前热闹的状态了,好几个矿工表示准备试试。

  看后台数据,连上矿池的人在缓慢地增长,发布之初,还是我们自己人的 10 台机器,慢慢就变成了 20、50……大概经过了一小时,已经有 100 台机器连上了矿池。突然,出块了!一阵兴奋之余,我们赶紧分头检查发放的工资、数据库、日志,看看有没有异常现象。一切都好,这才舒了一口气。

  矿工收到了工资,就开始在群里帮我们宣传“一小时挖两个币可以哦,大家快上”。我们其实每个人都已经加了群,虽然看着很激动,但也不敢乱说话,要是被认出来就不好了。这时候就只能靠群里“自干五”们的支持了。分析后台数据,排名第一的矿工用了 14 台 8 核电脑在挖矿,比起他来,我们自己的算力加起来也只是他的零头啊。

  又出块了!这次不是我们看后台数据发现的,而是矿工率先在群里说的。这下群里炸了锅“趁池子还没黑,快去挖”。看了真是哭笑不得啊。连接数 200、300 涨的飞快,远超我们的预期。又一个块!这短短的一个小时,出了三个块,小心脏受不了了……心跳正常之后,仔细算了算,按照期望,这时应该能出一到两个块,出了三个块纯属人品爆发。


blocks

挖矿出块页面插图

  接下来的一小时,我们感受到了什么是人品守恒。出来混,总是要还的。矿工的数量还在增长,但是却一直不出块了。有矿工开始破口大骂,我们只能仔细地排查错误,但是最终的结论是,运气太差。连接数开始缓慢下降,难道矿池才开业两小时就要准备关门了?

  为了缓和紧张的氛围,我们手动统计了所有矿工的欠款,跟帖贴在了发布稿的后面。这一招似乎非常有效,矿工看到池主说还欠自己钱,就暂时相信矿池还没跑路,愿意继续挖着试试了。群里有个矿工看到了这个帖子,猜测是不是池主在手动发钱才这么慢的。潜伏在矿工群里的众神瞬间化身为水军,顺势把舆论导向了这个有趣的中性话题上。

  终于在这个难熬的一小时快要结束的时候,一下子出了两个块,终于可以安心了。这时,R 神也写完了一个显示欠款页面,放出来大家一起检查。所有人同时发现了一个奇怪的现象,怎么有些人的欠款是负数,也就是说,给这些人多发了钱……这是要把自己弄倒闭的节奏啊……

  由于打印了大量的日志,排查起来非常方便。20 分钟之后,终于发现是数据同步的问题:我们最初设计的时候,为了图方便,把“不需要这么实时”的 share 统计数据,通过数据库来传递,反正最后也要入库。没想到在连续出块的时候,数据库还没来得及写入新数据,就接到了读取请求,这就导致矿池用旧的分钱方案又多分了一次钱。还好这个错误只会在连续出块的时候遇到,应该不严重,这真是个幸福的烦恼啊。此时已是凌晨两点,然而大家都非常兴奋,丝毫没有睡意,于是继续讨论怎么修复这个 bug。

  凌晨三点,方案落定,最佳解决方法应该是把关键数据都放到内存里,但是这得明天才能修完了。鉴于矿池运行稳定,大家都准备洗洗睡了。突然,矿池挂了……日志说,是同时打开文件数太多导致的。原来 Ubuntu 默认只能同时打开 1024 个文件(网络连接也算在内),现在已经有 400 多个矿工,再加上一些没释放的资源,已经达到了这个上限。问题倒是很好解决,改一下系统参数就好了。能在醒着的时候遇到这个错误真是幸运。

  到凌晨4点,终于能真的平静下来洗洗睡了。

五、包工头

  到 12 月 1 日,我们已经大致搞明白了比特币的原理,也知道矿池要做的事情,我手痒先设计了一个能用的矿池架构。这个架构主要有四个部分:钱包通信、矿机通信、主逻辑和 Web 界面。这些部分的功能是这样的(请跳过以下无聊的四段):

  钱包通信,顾名思义就是和钱包客户端通信了。比特币是在 P2P 网络上建立的,而其钱包客户端就是其中的一个普通节点,也就是说,这个客户端有着完整的功能。比如转账,比如宣布挖矿成功。我们也能从钱包客户端里面读取到当前虚拟币网络的任何信息。

  矿机通信,顾名思义就是和挖矿程序通信了。功能很单纯,有新的挖矿任务时,就给矿机发放任务;矿机挖到 share 时(下面会介绍什么是 share),就解析数据,并拉去检查提交。

  主逻辑,顾名思义就是主逻辑了。这个模块最初设计的时候是为了打通各模块之间的数据流,确保有新任务的时候可以及时通知到所有矿工。比如矿工算出新块了,要第一时间告诉钱包,及时同步到虚拟币网络。主逻辑的设计思想是尽可能抽象和简洁,随着后续的优化,主循环里面一共只有 20 多行核心代码。

  Web 界面,顾名思义就是个网页了。为了让矿工相信我们不是黑池,得把所有挖到的矿以及每个矿工的收入公开透明地展示出来。这个模块非常独立,上线之后才开始做。


architecture
最早规划的模块结构图(后期经过优化已经有所改变)

  我给自己封了个包工头的职位(史上亲自干活最多的包工头,本章标题就是为了纪念这个唯一出场机会的),由于整个设计思路数我最清晰,所以各个模块之间的协调由我来定。具体在开发过程中,每部分又可以继续拆分出一些独立模块,最后分出了八个独立模块大家一起来写。我写主逻辑以及矿机交互;D 神写分钱机制以及钱包通信;H 神写 share 验证;R 神写数据库接口、share 统计、Web。四个人,两周的业余时间基本都在开发矿池了。

  其实嘛,对于码农来说,编写矿池这几天完全就是“日常”,最没什么可以回忆了。基本都是,提出方案,讨论优化,然后敲代码,测试。不过,有些细节设计还是值得分享一下的。

狗池

  都写到第五章了,还是没说狗池是什么,现在终于要揭秘了:狗池就是我们做的矿池的名字。“池”肯定就是矿池的意思了,那为什么叫“狗”呢?我们在选择编程语言的时候,R 神和 F 神同时建议使用 Go 语言,可以应对“百万级”矿工的连接。D 神:“于是就叫 GoPool,简称狗池。”R 神:“贱名好养活。”于是名字就这么定了……不过后来因为 gopool.net 被注册了,我们只好抢了个 gpool.net,中文名还是不变。

  顺便插播一段漫画调节一下枯燥的氛围。Go 语言的吉祥物其实是 gopher,就是下面左边这个很萌的形象,后来 X 神为 Web 界面上每个功能都设计了一个对应的 gopher 风格的插图,后面不定期插播这套插图,先放一个首页的插图。


gopher首页插图
gopher以及矿池首页插图

share

  前面提到了 share,这是一个在矿池挖矿特有的概念。之前提过,矿池的设计目的就是让小算力的矿工能按照他们出的力分钱,从而得到持续稳定的小额收入。那怎么评估每个矿工出的力呢?其实思路很简单,就是让矿工做一些低难度的“挖矿”任务。比如比特币需要矿工找到一些前面有 67 个 0 的 hash 值,那么矿池可以降低难度到 50 个 0,如果矿工找到一个至少有 50 个 0 的方案,就可以提交,这个难度就小很多了。矿池如果在里面发现有 67 个 0 的,就可以提交给比特币网络了。质数币也类似,只是不以 hash 前面的 0 为难度衡量指标了,而以对应质数序列的长度为目标。质数币网络需要我们找到长度为 10 的序列,矿池就把难度降低到 6。这种矿工提交的低难度方案,就是 share。share 里面可能会有难度符合虚拟币网络要求的方案,就可以变成区块。

  这里还有个有趣的地方。矿工会不会在算到低难度的 share 时,就提交到矿池蹭分红,而在算到难度满足区块的要求时,直接擅自提交独占成果呢?其实这是不行的。还记得最早提过的挖矿其实在计算 hash(“基准字符串”+“随机字符串”)么,里面的基准字符串已经编码了:这个矿挖到之后是分给谁的。所以对于矿工而言,要么参加分红,要么自己挖矿,没法两者兼得。

分钱

  虚拟币矿池的分钱策略有两种现成的方案,PPLNS 和 PPS,具体可以看这个介绍。我们最后考虑到编程实现上的便利,改造了 PPLNS 策略,按照最近一小时的 share 数进行分配。对于矿工而言,PPLNS 手续费更低,只要收入稳定,一般都会选手续费低的。

  对于矿池而言,分钱还需要考虑另一个问题,那就是在哪分钱。这是什么意思呢?在虚拟币世界里有两种方式可以得到钱,一种是挖矿获得,另一种是别人转账过来。前面提到“基准字符串”里面编码了挖矿成功之后钱分给谁。如果是第一种方式,可以直接按照之前矿工的算力投入编写分钱方案,挖到矿时直接分给矿工。如果是第二种方式,则挖矿的收入全给矿池,之后通过转账的方式,把矿池里的钱转给矿工。

  这两种方案其实各有优点,前一种矿工可以及时看到收益;后一种按需取款,避免钱包都是碎银子……可能有人会觉得虚拟币钱包里的钱不就是个数字么,多笔小额的收入有什么关系。其实还真有关系,虚拟币用钱(给别人转账)的时候,需要说清楚,这笔支出的钱,分别是从哪几笔收入里来的。当然这些工作都是钱包客户端自动完成的。不过大量的碎银子会拖慢钱包的速度,甚至有可能导致转账数据包太大,而需要缴纳额外的手续费。

  我们最后采用的是前一种方案,因为该方案挖到的矿都直接分给矿工了,而矿池就不会存有大量虚拟币,也就不会成为黑客的攻击目标了。(后一种方案钱全在矿池,可以携款潜逃 23333)

  简单总结一下整个开发过程中的一些想法,产品设计上,我们第一目标是降低自己的风险,第二目标是为矿工提供稳定的收入。技术设计上,目标就是低成本(代码量和服务器成本),高性能。虽然这个目标不一定能泛化,但是在当时的情形下应该还是比较适合的。

p.s. 作为一个包工头,每天的 push 是必不可少的,那几天我的口头禅是“今晚上线”。然而这并没什么卵用。

四、说干就干——YY 变成现实的重要一步

  经过 D 神连续 4 天的洗脑,大家都已经相信了做矿池是一个发家致富的好路子。但是为什么没人动手做呢?当时没人能搞明白矿池是怎么回事,甚至连比特币的原理也不清楚。这种情况下,从零开始做矿池,技术门槛实在太高。既然我已经率先被洗脑成功,就一定要把这件事干成。

  第一步,拉人。土豪 F 神建了个群,把我们有意向参与开发的人加进了 QQ 群。这居然是个 2000 人的群,大家幻想着事成之后,直接把这个群当作用户群。

  第二步,分工。在所有人都很迷茫的时候,唯一能做的就是学习。先弄明白比特币、矿池的原理再说。

  当时可供学习的素材主要有代码和文档。比特币自然是开源的,也有一些矿池是开源的,比如 P2Pool。文档在当时反而很少,基本只能看比特币最早那篇简短的论文。中文的资料把比特币说得很神,英文也很难找到详细介绍比特币技术的资料,更不用说矿池了。

  配合代码和文档,还有一种很重要的学习素材,那就是数据。矿机(挖矿程序)和矿池的通信数据,比特币客户端存储的数据。于是,大家就开始分头行动,看比特币代码、看矿池代码、看开源矿机代码、分析不开源矿机的通信数据、编译开源矿池测试。这些工作相辅相成,大家有条不紊地进行着。

  和大神们一起讨论与探索,进度还是非常快的:

  第一天,大家就弄明白了比特币的原理(最有效的是看比特币的区块数据),什么是难度,比特币是怎么保证全网 10 分钟只出一个区块的。

  第二天,大概弄明白了质数币的原理,那些质数到底是怎么存的,怎么检验的。

  第三天,矿机与主流矿池的通信已经基本被翻译出来。至此我们终于发现,原来矿池比我们想象中的简单得多,倒是 P2Pool 有很多我们不需要的功能,代码过于复杂了。于是我们停止了为期两天的争论,放弃了修改 P2Pool 的方案,决定自己重写。

 p.s. 这几天所有人的口头禅都是:太 NB 了,怎么设计得这么精妙!