简介一份面向零基础用户的以太坊中转节点搭建指南聚焦在云服务器上配置属于自己的ETH中转服务并附带抽水功能。内容以Windows系统为操作环境从阿里云服务器选购、minerProxy程序下载与解压运行讲起逐步覆盖配置文件自动生成、后台Web端口与token登录、新建端口设置中转矿池、抽水比例配置及抽取算力指向鱼池等环节。文档还专门提醒SSL协议开启后矿机需使用对应协议接入并针对NBminer等常见内核给出连接格式示例。同时说明多个矿池与多个端口如何共存、本地端口不可重复等细节最后介绍云服务器防火墙/安全组放行端口以及替换自定义后台端口的安全建议。资源包共1个docx文档约265KB便于对照查阅目前已有1214人学习下载适合刚接触ETH挖矿中转、想低成本自建节点并掌握抽水机制的新手。1. ETH中转节点到底在转什么给矿机当跳板给矿池当代理一台显卡矿机要挖矿得先连上某个矿池的stratum端口当你手里的矿机不止一台分散在几个厂房甚至几个城市一个个去改矿池地址就不现实了。常见的做法是在一台服务器上起一个ETH中转节点让所有矿机先连到这台服务器再由服务器统一转发到上游矿池。所谓抽水功能不是把矿机的网络流量截走一部分而是在stratum协议层对收益归属做一次改写。这个点新手最容易看走眼以为抽水是「转发时扣下几个数据包」真正落地时才发现它改的是授权消息里的钱包地址牵一发动全身。这篇文章会把协议链路、最小实现、账务验证和踩坑串起来适合刚拿到第一台服务器、想自己收拢矿机统一接入矿池、又不想被现成代理工具黑盒坑一把的人。2. 从TCP链路到抽水原理四种消息、两条连接与一次钱包替换2.1 一条stratum连接上的四类消息流subscribe、authorize、notify、submit矿机和矿池之间跑的是stratum协议底层是TCP消息体是JSON每条消息以换行符结束。矿机连上来之后生命周期里只做四件事订阅、授权、收任务、交结果。第一条消息是mining.subscribe矿机告诉矿池「我要开始挖了」矿池回一个订阅ID和extranonce参数。紧接着矿机发mining.authorize参数里带着钱包地址和矿工名格式通常是0x你的钱包地址.矿工编号。矿池收到后回true或false这条授权决定了之后所有share的收益归属。再往后是矿池主动推mining.notify下发一个新任务矿机算完后发mining.submit把nonce和结果交回去。矿池验算通过回true这个share就被记进矿池账本。{id:1,method:mining.subscribe,params:[nanominer/1.0]} {id:1,result:[sub1,8f39c2,2],error:null} {id:2,method:mining.authorize,params:[0xabc...worker01,x]} {id:2,result:true,error:null}这四行就是一条正常挖矿会话的起点。中转节点要做的是在矿机和上游矿池之间再插一层矿机以为中转就是矿池上游矿池以为中转就是矿机。TCP层做透传只能搬运字节流但要实现抽水就必须能识别出上面每一行JSON尤其是mining.authorize和mining.submit这两条。中转能看懂消息才有资格谈改包、记账、抽水。2.2 抽水的两种实现路线改authorize 与 share级截留第一种路线最简单粗暴只改一处当矿机向中转发mining.authorize时中转把参数里的钱包地址替换成运营方自己的钱包地址再转发给上游矿池。上游矿池从这一刻起把这条连接上所有share的收益全部记到运营钱包名下。你原本以为抽水是「从10个share里抽1个」实际上这种路线是「100%先进运营钱包再靠线下或后台按比例给矿工结算」。它看起来不像抽水但它正是目前大量小规模中转站在用的实现因为只要改一个字符串就能跑起来。第二种路线是share级截留更接近字面意义的「抽水」。矿工连接保留他自己的钱包地址正常share照常进矿工钱包中转型维护一个计数器每累计N个submit就挑其中一个把worker名替换成运营钱包的矿工名通过一条独立的抽水连接提交到上游矿池把这个share的收益记到运营钱包。这种方式抽的是「比例」不是「全部」但需要同时维护两条上游连接还要处理job_id对应关系复杂度高一个量级。对比项改authorize抽水share级截留实现难度低只改一条JSON高需要双连接与改包抽水粒度100%先进运营钱包按计数器精确截留对矿工透明度依赖线下结算说明矿池后台能看到部分share归他人账务复杂度需要完整台账依然需要台账且要做回执配对适合阶段刚开始做中转规模稳定、需要精细控制我的建议是零基础先走第一种跑通链路、把账务模型想清楚再考虑升级到第二种。很多人一上来就照着复杂方案抄最后连粘包问题都还没处理明白反而是浪费一晚上。2.3 选型为什么我推荐Node.js/Go做转发层而不是Python脚本中转节点的本质是一个高并发的长连接转发服务矿机数量上去之后同时保持几千条TCP连接是常态。这时候选型的核心指标不是写起来多快而是单进程能不能撑住大量空闲连接、IO事件能不能不阻塞。Node.js的异步非阻塞模型天然适合这个场景事件循环里每个连接只占很小的内存代码写起来也短几十行就能出一个能跑的网关。Go的优势是编译成单二进制、部署方便、并发模型更硬适合要长期运维的节点但同样的逻辑写出来会多一些样板代码。Python用asyncio也能做只是asyncio的坑比较隐蔽比如事件循环里不小心写了同步阻塞调用整个节点都会卡住。我不是说Python不行而是对零基础的人来说Node.js是最快能看到效果的选择。语言选完真正决定成败的反而是另一个细节TCP是流协议不是消息协议。矿机发过来的数据可能一次带了三条JSON也可能一条JSON被拆成两段处理不好这个后面所有逻辑都会翻车。这一章的结论是先理解消息流向再决定改哪个字段最后才谈选型和编码。3. 零基础跑通第一个带抽水的中转节点Node.js最小实现3.1 先让流量转起来一个只做TCP转发的骨架第一步不追求抽水先让矿机能经过你的服务器连上矿池。下面这段代码是一个纯TCP转发骨架监听本地端口每个矿机连接进来时再向上游矿池发起一条连接双方的数据原样互传。const net require(net); const config { listen_port: 8888, upstream_host: etc.2miners.com, // 上游矿池域名或IP upstream_port: 1010 }; const server net.createServer((clientSocket) { const upSocket net.connect(config.upstream_port, config.upstream_host); // 矿机 - 上游 clientSocket.on(data, (chunk) { upSocket.write(chunk); }); // 上游 - 矿机 upSocket.on(data, (chunk) { clientSocket.write(chunk); }); clientSocket.on(error, () upSocket.destroy()); upSocket.on(error, () clientSocket.destroy()); }); server.listen(config.listen_port, 0.0.0.0, () { console.log(transit node listening on, config.listen_port); });这段代码的逻辑很直白net.createServer接收矿机连接每个连接实例里再net.connect一条到上游矿池的TCP连接。两条连接之间的两个data事件就是双向管道。把这段存成app.js用node app.js跑起来矿机地址填服务器IP:8888正常情况下就能正常挖矿。这里参数只需要关注三个listen_port是矿机要填的中转入口upstream_host和upstream_port是你选定的矿池地址。注意矿池的端口不是HTTP端口是stratum专用端口填错了一连就断。3.2 按行拆JSON把粘包半包处理交给一个累加器纯转发能通但没有任何业务能力因为你看到的还是一堆连续的字节。要识别mining.authorize必须先把字节流切成一整行一整行的JSON。这一步处理粘包与半包TCP在传输过程中会因为缓冲区和网络分片出现多条消息粘在一起、或者一条消息被拆成两半的情况。function attachLineSplitter(socket, onLine) { let buffer ; socket.on(data, (chunk) { buffer chunk.toString(utf8); let idx; while ((idx buffer.indexOf(\n)) 0) { const line buffer.slice(0, idx).trim(); buffer buffer.slice(idx 1); if (line) onLine(line); } }); socket.on(error, () {}); }这段累加器的思路是先把新到的chunk拼进缓冲区然后在缓冲区里找换行符找到一个就切一行出来切完再继续找直到没有换行符为止剩下的内容留到下次数据到达时再拼。trim()是为了去掉行尾的\r因为有些矿池协议会带回车换行两个字符。用了这个累加器之后onLine拿到的参数就是一条完整的JSON字符串可以安全地JSON.parse了。不要试图在data事件里直接parse那是新手最容易低估的坑后面避坑章节会细说。3.3 在authorize上做替换抽水逻辑的15行核心代码有了行拆分抽水逻辑就变得很集中拦截mining.authorize改掉钱包字段再转发。下面是在骨架基础上整合的完整版代码也是这个中转节点最核心的部分。const net require(net); const config { listen_port: 8888, upstream_host: etc.2miners.com, upstream_port: 1010, pump_enable: true, // 是否开启抽水 pump_wallet: 0x你的运营钱包地址, pump_worker: proxy01 // 抽水账号的矿工ID后缀 }; function attachLineSplitter(socket, onLine) { let buffer ; socket.on(data, (chunk) { buffer chunk.toString(utf8); let idx; while ((idx buffer.indexOf(\n)) 0) { const line buffer.slice(0, idx).trim(); buffer buffer.slice(idx 1); if (line) onLine(line); } }); socket.on(error, () {}); } const server net.createServer((clientSocket) { const upSocket net.connect(config.upstream_port, config.upstream_host); // 矿机 - 上游识别并替换authorize attachLineSplitter(clientSocket, (line) { let msg; try { msg JSON.parse(line); } catch (e) { upSocket.write(line \n); return; } if (msg.method mining.authorize config.pump_enable) { const originalWorker Array.isArray(msg.params) ? msg.params[0] : ; msg.params[0] config.pump_wallet . config.pump_worker; console.log([pump], originalWorker, -, msg.params[0]); } upSocket.write(JSON.stringify(msg) \n); }); // 上游 - 矿机原样回传 attachLineSplitter(upSocket, (line) { clientSocket.write(line \n); }); upSocket.on(error, () clientSocket.destroy()); }); server.listen(config.listen_port, 0.0.0.0, () { console.log(transit node with pump running on, config.listen_port); });代码的逻辑是矿机消息先经过累加器切行然后JSON.parse。如果解析出来是mining.authorize且抽水开关打开就把msg.params[0]从矿工的钱包地址改写成config.pump_wallet . config.pump_worker然后序列化后发给上游。其他消息完全不碰原样转发。上游返回给矿机的内容也原样回传矿机不会感觉到任何异常。参数说明里最需要注意的是pump_worker它不参与收益归属但矿池要求worker名不为空所以这里固定给一个后缀。另外pump_enable在调试阶段先设成false让节点先以纯转发模式跑几个小时确认稳定后再开启替换这是最小化排错范围的习惯。3.4 用systemd守护中转进程断电、崩溃后自动拉起直接跑node app.js有致命问题进程一崩就没人管了矿机全部断线。生产环境要用systemd把节点变成系统服务。[Unit] Descriptioneth transit node Afternetwork.target [Service] ExecStart/usr/bin/node /opt/transit-node/app.js Restartalways RestartSec3 Userroot [Install] WantedBymulti-user.target先把文件存到/etc/systemd/system/eth-transit.service然后执行systemctl daemon-reload systemctl enable eth-transit --now。Restartalways表示进程异常退出时三秒后自动拉起Userroot是图省事的写法正式环境建议换一个专用系统用户。看日志用journalctl -u eth-transit -f这条命令在你排查所有抽水问题时都会用到矿机上发生一次连接、提交一个share这里都会留下痕迹。4. 抽水账务与验证比例怎么设、台账怎么记、如何三方对账4.1 为什么「抽5%」不是调一个参数而是一笔账很多人在代码里找「抽水比例」这个参数翻遍配置发现根本没有。原因很简单改authorize方案下上游矿池把收益100%结算到运营钱包所谓抽水比例是你和矿工之间的约定不是矿池知道的事。你收了矿池的全额收益再按约定比例把剩余部分结算给矿工这中间的差额才是中转服务费。所以「抽5%」的正确落地方式是先在本地记录每个矿工钱包贡献了多少算力再在结算周期结束时用总量减去抽水部分给矿工打款。算力占比不能只看submit次数不同矿机的难度可能不同严谨的算法是把每个share的难度累加起来worker_share Σ(该worker每个被接受share的difficulty)worker_ratio worker_share / 全部worker_share总和worker_payout 矿池结算收益 × worker_ratio这是PPLNS结算方式的简化版。真实矿池还会回溯最近N个块的share窗口但中转节点内部按这个公式做估算足够用了。明确一点最终结算以矿池后台实际payout为准本地台账只用来算比例和排查异常。4.2 本地记账按worker记录submit数与在线时长账要从第一条消息开始记不能在月底拍脑袋。记账逻辑可以挂在中转的submit转发路径上每当上游矿池对某条mining.submit返回result: true就给对应worker累加一次计数。下面是一段极简的台账实现。const ledger new Map(); function recordShare(worker, difficulty 1) { const wallet worker.split(.)[0]; const rec ledger.get(wallet) || { shares: 0, accepted: 0, updatedAt: Date.now() }; rec.shares difficulty; rec.accepted 1; rec.updatedAt Date.now(); ledger.set(wallet, rec); }调用时机放在上游连接的回执逻辑里只有当矿池返回true才调用recordShare这样记的是「被矿池接受的有效share」而不是矿机单方面提交的次数。worker.split(.)[0]是为了把钱包.矿工ID归并成钱包维度方便按钱包结算。生产环境要把这个Map定期落盘比如每分钟把整个台账append到一份JSON日志文件里进程重启后从文件恢复。否则中转进程一崩所有账目归零月底对账就变成互相扯皮。落盘可以用setInterval定时执行简单可靠。4.3 三方对账矿机端、中转日志、矿池后台缺一不可上线后不要只看矿机软件里显示的Accepted要养成三方对账的习惯。矿机端显示的是矿机本地提交并被中转返回true的数量中转日志或台账记录的是中转到上游后被矿池接受的量矿池后台显示的是运营钱包名下所有worker的shares。三方数据应该满足以下关系。正常纯转发模式下矿池后台shares等于中转台账shares也接近所有矿机端shares之和。开启抽水后矿池后台只有运营钱包一个账户矿机端的Accepted数量会明显大于矿池后台该矿机名义上的记录因为部分share被记到运营钱包名下了。这时候你要核对的不是「总量是否一致」而是「抽掉的share数是否等于约定比例」。出现偏差时先查journalctl -u eth-transit -f看有没有[pump]日志替换了预期外的worker再拉矿池后台的worker列表对比。通常对不上账的原因是reject被计入了本地台账或者矿机端显示Accepted但上游其实reject了。遇到这种先调整记账口径不要急着怀疑矿池。4.4 边界参数对照表0%、100%与误抽场景场景参数配置现象与注意点纯转发调试pump_enable: false所有收益归矿工钱包适合先验证链路稳定性抽水开启pump_enable: true注意上游矿池只认运营钱包矿工端账需本地结算抽水100%改所有authorize为运营钱包等同于算力承包矿工完全依赖你的结算信誉更换运营钱包改pump_wallet只对之后新建的连接生效存量矿机要重连批量矿机重连无上游矿池可能限制单IP连接数要分批重启矿机最后一个场景经常被忽略改了服务器配置或重启中转后所有矿机同时重连瞬间可能拉出几千条到上游矿池的连接不少矿池按IP限制连接数后到的连接直接被拒。解决方法是矿机端分批次切换比如按矿机组的IP段每小时重启一组而不是一键全切。5. 避坑手册粘包、stale share、断线与对账偏差5.1 现象矿机频繁掉线、High ping日志里JSON解析报错这是中转节点最常见的翻车点。矿机发过来的data事件里经常一次包含多条JSON比如mining.subscribe的响应和mining.notify挤在同一个chunk里也可能一条mining.submit被拆成两个TCP包。直接在data回调里做JSON.parse的人第一晚就会看到一堆Unexpected token报错连上的矿机全部不断重连。原因就是TCP是流协议没有消息边界。解决方法是3.2节那个按行累加器把拆包逻辑统一收口到一个函数里。这里还要提醒一句累加器的buffer必须挂在每个socket实例上不能做成全局变量否则两条矿机连接的数据会互相污染。5.2 现象矿池后台大量stale share算力显示正常但收益低stale share的意思是矿工提交的job_id已经过期。矿池每隔一段时间就推送新的mining.notify矿机算完之前的任务再提交矿池会拒绝或标记stale。中转介入后这个现象会被放大如果上游连接因为网络抖动比矿机慢了一步矿机还在拼命提交旧job而你把所有submit原样转发上游矿池自然大量判旧。解决思路是在中转层做一次job_id过滤矿机提交时检查该job_id是否已经过时过时的直接吞掉并回一个伪造的true避免矿机认为掉线同时减少上游无效请求但要注意伪造回执只能用于吞掉明显过时的share不能在抽水场景下滥用否则账目会失真。更简单的做法是确保上游连接稳定让矿机侧和上游侧拿到同一代job通常net层抖动恢复后自动解决。5.3 现象替换authorize后上游矿池直接断开连接或回result: false最常见的原因是钱包地址不合法比如运营钱包填成了交易所热钱包地址、填错前缀上游矿池校验失败后断开。还有一种是worker后缀太长或带了非法字符矿池协议对worker名长度有隐式限制超长直接拒。解决方法是先拿运营钱包地址在矿池官网页面或直接用普通矿机软件手测一次确认这个地址能正常连上矿池。然后把pump_worker改成一串简短字母数字避免使用中文、空格、特殊符号。改完配置不要只重载进程所有已连接的矿机必须重连因为旧连接仍然用的是老的authorize结果。5.4 现象上游矿池一断所有矿机全线掉线没有任何兜底中转节点把上游矿池地址写死成单个域名或IP时这个上游一旦维护或故障所有矿机都会跟着断。我在生产环境第一次遇到这个问题时凌晨两点被电话叫醒全矿场的人都在等恢复。解决方法是至少配两个上游地址做故障转移检测到上游连接连续重试失败时自动切到备用矿池。代码里可以用一个简单的数组加索引优先连第一个失败则连下一个。更省事的做法是在DNS层面给同一个域名配多个A记录但这不是所有矿池都支持。务必在部署时就做一次断网演练把上游矿池屏蔽掉几分钟看中转会不会自动切走。5.5 现象抽水抽完月底账对不上矿工说他少算了三个点对不上账的根因通常不是「抽错了」而是记账口径不一致。中转本地台账把reject也算进去了或者矿机端统计的Accepted包含了你伪造回执的吞包又或者你没有把难度差异转换成算力再做比例计算。解决方法是把本地台账完全以「上游矿池返回result: true」为唯一口径不要用矿机端的Accepted也不要自己估。每月对账时先把矿池后台的shares总量拉出来和中转台账比对偏差超过1%就开始逐条查journalctl。只要你坚持这个习惯抽水就不会从技术问题变成信任问题。6. 上线前最后一步用真实矿机做端到端验证与流量回放中转节点跑起来不等于能收钱上线前至少做一遍下面的验证每一步都有明确结果才算通过。先测端口连通在服务器上用nc -vz 127.0.0.1 8888确认中转入口在监听。然后拿一台真实矿机把矿池地址改成你的服务器IP:8888观察journalctl -u eth-transit -f正常会依次出现mining.subscribe和mining.authorize的解析日志。接着等矿机算出一个share日志里出现mining.submit上游返回true这一步通过说明链路全通。最后登录矿池后台确认运营钱包名下出现该矿机的worker记录同时把矿机端显示的提交数量与矿池后台的accepted数量做一次减法差值就是你抽走的比例。如果还想看得更细用lsof -i:8888查看当前连接数再对比矿机实际台数能发现有没有矿机处在假连接状态。进阶一点的做法是把整个收包逻辑做一次流量回放把一段真实的矿机抓包文件存下来用脚本按原来的时序喂给中转节点观察输出行为是否和线上一致。这能帮你在不改动线上任何配置的情况下验证新版本的粘包处理、抽水替换和对账逻辑。再往后值得投入的方向是加一个Web管理面板实时展示各钱包的share计数、抽水比例和上游连接状态或者写一个定时对账任务每小时拉一次矿池API跟本地台账自动比对。这些在做大之后都是必须的但起步阶段先别碰把基础链路跑稳比什么都重要。我第一次上线中转节点时只验证了「能连上、能出币」就宣布完成结果上游矿池夜里维护整个节点断了四个小时全矿场的矿机像多米诺骨牌一样掉线。从那以后我养成了一个习惯任何节点上线前先主动把上游矿池断掉十分钟看系统会不会自动恢复再决定要不要交给别人用。这个习惯帮我避开了后面好几次事故希望帮到你。本文还有配套的精品资源点击获取