Dragonfly Capital:如何构建一个以太坊矿池
矿池是以太坊生态系统中的主要力量。随着矿工可提取价值(MEV)呈指数级增长,EIP-1559 升级以及即将到来的合并,矿池已成为生态系统中更重要、更有发言权的参与者。
给萌新解释一下:矿池是软件提供商,使许多矿机能够汇聚其挖矿能力并分享奖励。从两个层面上看,矿池在基于工作量证明(PoW)共识机制的挖矿中是必不可少的:
首先,因为个体矿工的收入波动会很大;
其次,因为围绕挖矿构建软件基础设施的难度越来越高。通过资源的集中,个体矿工可以降低收入变动,因此拥有可预测性更强的业务运营。
但是这种权力也伴随着巨大的责任,矿池拥有很大的权力,这是因为矿池最终决定了其矿机处理哪些区块,以及这些区块中可以包含哪些交易。矿池决定提取哪些 MEV 以及谁可以提取它,他们对 Gas limit 进行投票,并参与重大的政治斗争。因此对于以太坊文化来说,矿池的入场门槛尽可能低、以最大限度地去中心化是至关重要的。
所以当我决定构建一个矿池时,我惊讶地发现它是一项非常有挑战性的工作!关于如何运行具有高响应性、低叔块率的竞争性矿池,很少有公开的知识分享。
所以我想:好吧,让我们解决这个问题。
构建一个矿池包含两部分工作:
设置一个具有良好对等网络和快速处理速度的全节点客户端;
将全节点连接到矿池软件,该软件管理哈希率并在所有矿工之间分配工作负载。
在本文中,这两者都会谈到。
这篇「以太坊矿池构建指南」来自我们构建 MiningDAO.io 矿池的第一手经验,并概述了我们如何将叔块率从 10%-14% 降低到大约 4%-5%,与前 10 名矿池中的部分组织保持持平,甚至更佳。
设置以太坊全节点客户端
运行矿池需要运行以太坊全节点客户端。该客户端将负责接收新块和待处理的交易,以及生成自己的区块并将它们广播到其他节点。本节介绍如何正确设置一个全节点客户端。
服务器硬件要求
运行一个完全同步的节点需要相当好的硬件。我们建议至少 32GB 内存(RAM)和至少 2TB SSD 存储(永远需要将以太坊链与 HDD 同步)。
带宽也很重要。最好尽可能靠近其他节点,以尽快接收新块。我们建议采用其他矿池普遍采用的云服务上使用云托管专用机器:在欧洲是 OVH 和 Hetzner,亚洲则是阿里云和亚马逊云 AWS。
Geth 还是 OpenEthereum?Geth!
下一个决定是使用哪个以太坊客户端。最受欢迎和经过充分测试的选择是 Geth 和 OpenEthereum (原名 Parity)。 Geth 在协议开发方面处于领先地位,并且始终保持最新状态。
为了进行比较,我们利用 Parity-2.7.2 (OpenEthereum 重构之前的最新稳定分支)和 OpenEthereum 进行了一些小规模实验,但两者在区块导入时间和区块生产时间方面的测试结果都很糟糕,导致叔块率高到令人无法接受。
我们欢迎任何人进行更彻底的 A/B 测试,并与我们联系,为我们提供更多数据,但在当前阶段,我们只推荐 Geth。
以下是我们使用的命令行:
geth --datadir=/ssd/gethdata --syncmode=fast --cache=21000
--maxpeers=250 --txpool.globalslots=1000
--http --http.api=eth
--miner.etherbase='0xADDRESS' --mine --miner.threads=0
--miner.extradata='MiningDAO'
--miner.notify='http://127.0.0.1:8107' &>> ~/geth-log.txt
这里 --cache=21000 表示分配 21GB 用于内存状态存储(这是 Geth 可以处理的峰值),命令行其余部分将在下文予以解释。
更重要的是,我们下文描述的对 Geth 代码的修改可以在 这里 找到,作为可供下载的存储库,或者在 这里,作为要应用的补丁。
将空块产生频率降至最低
有两件事会破坏矿工所获取的价值回报:挖出叔块和挖出空块。
这两者几乎同样糟糕:叔块奖励为 1.75 ETH,空块奖励为 2 ETH,两种情况下都没有交易费盈余。相比之下,带有交易费用的完整区块的总奖励通常达到 3-4 ETH,有时甚至更多。
为什么矿池有时会 产生空块,以及如何将其产出频率降至最低?
当另一个矿池挖出一个新区块(比如高度 N)时,高度 N 的任何其他区块都可能成为叔块。 因此每当发现新区块时,Geth 会立即切换矿工的工作,在 N+1 高度挖出一个空区块。这个空块中不包含交易费用,但这还是比挖出注定要成为叔块的区块要好。
随后,Geth 在高度 N+1 处构建了一个「真正的」区块,并再次切换矿工的工作。构建这样一个「真实」的区块需要时间(0.1-0.3 秒),因此需要两步过程。但在这段 0.1 到 0.3 秒的过渡期间,其他矿工们则正在挖一个空块。
收集所有待处理的交易,以实现费用收益最大化,可能很诱人,但像 txpool.globalslots 这样贪婪,会大大增加 Geth 构建「真实」区块所需的处理量(要耗时多达 1 秒或更长时间)。我们的推荐值不大于 1000 或 2000。
要了解这方面的更多详细信息,请查看 https://github.com/ethereum/go-ethereum/issues/21899
将叔块产生频率降至最低
解决了空块,我们就可以开始困难的部分了。要尽量降低叔块率,有两件事是关键:
当其他矿池生产出新区块时,尽快掌握消息;
当您的矿池产生一个新区块时,尽可能广泛地传播它(这样其他人开始在它后面开始挖矿 )。
如前所述,实现良好对等网络(p2p)的第一步是在临近其他节点的云服务器上运行您的全节点,并配备良好的带宽。
其次,良好的带宽允许节点处理更多的直接对等点,从而减少接收新信息所需的 p2p 中继段(hop)数量。 Geth 命令行中对等点数的标志是 --maxpeers。
下面我们将解释一些更细微和强大的技巧,以最大限度地提高区块导入速度和区块广播速度。
使用 bloXroute
bloXroute 是一项致力于改善矿工之间连接并降低其叔块率的服务。大多数矿池都连接到 bloXroute,甚至主要的成熟矿池都 报告,使用 bloXroute 会带来显著改进。KeeperDAO 进行的 指标衡量 进一步证实了 bloXroute 相比同类服务具有巨大优势。
我们的实验也体现出显著的改进。在具有默认对等设置的新同步节点上,大约 90% 的所有新区块首先来自 bloXroute (只有 10% 来自所有其他对等节点)。
即使在我们的节点经过微调以连接到顶级对等点之后,仍有 40%-60% 的新区块首先来自 bloXroute。
按照 bloXroute 设置教程进行操作后,不要忘记 将 bloXroute 节点添加到为您的 Geth 设置的「受信任的对等点」中,稍后会用到它。受信任的对等点 是 Geth 将始终连接到的预设节点,无论对等点如何随机初始化。受信任的对等点也不计入连接数限制。将 bloXroute 网关添加到受信任的对等点,可确保 Geth 的这一连接不会意外断开。
我们进一步建议连接到太极网络。太极网络项目是星火矿池开发的区块广播网络。可以通过将太极网络 各个端点 添加到同一个受信任的对等文件中,来保持与太极网络的连接。
积极广播您的区块
每当 Geth 成功挖出一个新区块时,它就会发送该区块以在网络上广播。默认情况下,Geth 只将其广播到大小为 sqrt(n_peers) 的随机子集,后者将区块广播到它们的部分对等点,依此类推。
即使所有对等点都同样有效,这种机制也不是很理想,但是当某些对等点比其他对等点更强大,而这些对等点最终没有被包含在子集中时,这种机制的缺点尤其明显。
特别是,在挖出新区块时要做的第一件事是将其发送到 bloXroute,以便将其转发到所有参与的矿池。如果 bloXroute 网关最终没有出现在那个随机的 sqrt(n_peers) 子集中,那么您获得叔块的机率就会大大增加!
接下来,您会希望将该区块发送给质量最高的对等点,然后再发送给所有其余的对等点。
我们已将 以下 Geth 补丁 开源,建议将其应用于您的客户端。它将所有新挖出的区块广播到所有受信任的节点(包括 bloXroute),然后广播到所有剩余的对等点。
培养人脉最广的对等点
Vanilla Geth 旨在实现最大程度的去中心化和网络结构扁平化。这种选择非常适合爱好者,并支持由 成千上万 个节点组成的强大生态系统。然而,正如我们在上一节中看到的那样,对于执行关键职责、或一旦故障会造成高昂成本的节点而言,这些默认参数的效果不佳。
实际上,并非所有对等点都同样有效。有些节点连接速度较慢,既不会提供新区块,也不会帮助您的区块进行广播。 其他人,尤其是其他矿池的节点,则会产生源源不断的新区块数据。
根据星火矿池的建议,我们 调整了 Geth 参数,以记录哪些节点最先向我们发送新区块。经过几个月收集这些数据,使我们能够找出始终保持连接的最佳对等点(通过 Geth 中的「静态」/「受信任」节点设置)。 这里 是一个 Python 脚本,我们用来处理上述数据,并将其转换为 Geth 可以摄取的 trusted_nodes.json 列表。
由于 MiningDAO 矿池存在于每个地理区域(北美、欧洲、亚洲)中,我们对每个地理区域的顶级对等点列表进行了数据挖掘。不幸的是,我们不能公开分享这些节点的 IP,避免这些节点遭遇 DDoS 攻击。对于有充分理由的严肃询问者,我们可以私下分享。也很乐于分享我们自己在每个地理区域的节点,以供其他矿池连接!
设置矿池软件
正确设置全节点后,下一步是设置矿池软件本身。该软件将负责处理来自所有单个矿机的连接、跟踪工人的份额并管理支出。
挑选矿池软件
我们简要分析了以下 4 个选项:Miningcore、open-ethereum-pool、NOMP (node open mining portal) 和 MPOS (mining portal open source)。 我们后来了解到 Flexpool Solo,但未对其进行实验。
出于两个原因,我们对于 Miningcore 获得了丰富的经验。首先,它将所有过去的数据保存在 SQL 数据库的磁盘上,这与 open-ethereum-pool 不同,后者通过 Redis 仅将数据保存在 RAM 中。磁盘存储提供了抗重启的稳定性和分析历史数据的能力。其次,我们喜欢 Miningcore 高度可读、面向对象的代码。
最终,MiningDAO 采用了我们自己的矿池软件,以 Miningcore 为模型,用 Go 语言编写以提高速度。我们希望尽快开源我们的软件,但同时我们推荐使用 Miningcore。
修补矿池软件的延迟问题
我们在使用 Miningcore 时遇到的一个主要问题是它处理工作更新的方式。默认情况下,Miningcore 每 0.5 秒 ping 全节点远程过程调用(RPC),以查看最新作业是否已更改。这一设置可能适用于其他具有区块生产时间较长的加密货币(对于区块生产时间为 10 分钟的比特币来说,这是一个可以忽略不计的问题),但对于以太坊而言,这种设置会导致无法接受的高叔块率。
做个背景知识介绍,有一种简单的方法可以计算任何处理延迟所带来的叔块率增加数值。出块时间是 泊松分布 的,意味着无论距离上一个挖出的区块已经过去多久,在下一秒(或毫秒或其他时间单位)内找到下一个区块的概率总是相同的。例如,以太坊的目标是 13 秒出块时间,意味着下一秒出块的概率始终为 1 秒 / 13 秒 ≈ 7.7%。 因此,如果您的矿池因任何原因在管道中的任何地方有 0.1 秒的延迟,则由于该延迟,将有 0.1 秒 /13 秒 ≈ 0.77% 的额外叔块。叔块将来自您的矿机处理过时工作的 0.1 秒时间段。
回到 Miningcore。使用上面的公式,矿工作业更新如果出现 0.5 秒延迟,将导致 0.5 秒 / 13 秒 ≈ 4% 额外的叔块(绝对比例,而不是相对百分比)。 当然,如此高的非受迫性失误率是不可接受的。我们已经广泛尝试将更新频率从 0.5 秒降低到 50 毫秒及以下,但发现这一设置相当不可靠:工作更新仍然明显延迟。
更好的解决方案是利用 Geth 的 notifyWork 功能,以便 Geth 在作业更新出现时主动将其发送到矿池软件。我们对 Miningcore 打了补丁,以支持这一选项,并发布了 修改版。过渡到 notifyWork 后,我们发现 Geth 和 Miningcore 之间的通信延迟几乎可以忽略不计,因此我们的叔块率显著降低。
总结
希望这篇文章有用,并引导更多人运营以太坊矿池;自制矿池的文化对于保持以太坊的开放性和去中心化至关重要。
简单总结一下,我们最初采用 vanilla Miningcore 的默认参数和 vanilla Miningcore 软件。这种默认设置的叔块率约为 10%-14%。我们在逐步采用了本文概述的种种修改后,使我们的叔块率降至 4%-5%,与当前现有的前 10 名矿池的水准持平甚至更好(Etherscan 中显示的叔块率略高,因为我们有时会在生产中进行实验)。
我们的 Geth 修改方案可以在这里作为 repo 找到,也可以在这里作为 补丁 找到。可以在 这里 找到我们的 Miningcore 修改方案,且可以在 这里 找到相应的矿池配置文件。
如果您对如何改进这一参数设置有进一步的想法,请向我们发送 pull 请求或 电子邮件!
微信扫描关注公众号,及时掌握新动向
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场