到 12 月 1 日,我们已经大致搞明白了比特币的原理,也知道矿池要做的事情,我手痒先设计了一个能用的矿池架构。这个架构主要有四个部分:钱包通信、矿机通信、主逻辑和 Web 界面。这些部分的功能是这样的(请跳过以下无聊的四段):
钱包通信,顾名思义就是和钱包客户端通信了。比特币是在 P2P 网络上建立的,而其钱包客户端就是其中的一个普通节点,也就是说,这个客户端有着完整的功能。比如转账,比如宣布挖矿成功。我们也能从钱包客户端里面读取到当前虚拟币网络的任何信息。
矿机通信,顾名思义就是和挖矿程序通信了。功能很单纯,有新的挖矿任务时,就给矿机发放任务;矿机挖到 share 时(下面会介绍什么是 share),就解析数据,并拉去检查提交。
主逻辑,顾名思义就是主逻辑了。这个模块最初设计的时候是为了打通各模块之间的数据流,确保有新任务的时候可以及时通知到所有矿工。比如矿工算出新块了,要第一时间告诉钱包,及时同步到虚拟币网络。主逻辑的设计思想是尽可能抽象和简洁,随着后续的优化,主循环里面一共只有 20 多行核心代码。
Web 界面,顾名思义就是个网页了。为了让矿工相信我们不是黑池,得把所有挖到的矿以及每个矿工的收入公开透明地展示出来。这个模块非常独立,上线之后才开始做。
我给自己封了个包工头的职位(史上亲自干活最多的包工头,本章标题就是为了纪念这个唯一出场机会的),由于整个设计思路数我最清晰,所以各个模块之间的协调由我来定。具体在开发过程中,每部分又可以继续拆分出一些独立模块,最后分出了八个独立模块大家一起来写。我写主逻辑以及矿机交互;D 神写分钱机制以及钱包通信;H 神写 share 验证;R 神写数据库接口、share 统计、Web。四个人,两周的业余时间基本都在开发矿池了。
其实嘛,对于码农来说,编写矿池这几天完全就是“日常”,最没什么可以回忆了。基本都是,提出方案,讨论优化,然后敲代码,测试。不过,有些细节设计还是值得分享一下的。
都写到第五章了,还是没说狗池是什么,现在终于要揭秘了:狗池就是我们做的矿池的名字。“池”肯定就是矿池的意思了,那为什么叫“狗”呢?我们在选择编程语言的时候,R 神和 F 神同时建议使用 Go 语言,可以应对“百万级”矿工的连接。D 神:“于是就叫 GoPool,简称狗池。”R 神:“贱名好养活。”于是名字就这么定了……不过后来因为 gopool.net 被注册了,我们只好抢了个 gpool.net,中文名还是不变。
顺便插播一段漫画调节一下枯燥的氛围。Go 语言的吉祥物其实是 gopher,就是下面左边这个很萌的形象,后来 X 神为 Web 界面上每个功能都设计了一个对应的 gopher 风格的插图,后面不定期插播这套插图,先放一个首页的插图。
前面提到了 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 是必不可少的,那几天我的口头禅是“今晚上线”。然而这并没什么卵用。