1 项目简介
Node Open Mining Portal(简称NOMP)是一个由Node.js编写的高效、可扩展的加密货币挖矿池软件,专为履历丰富的系统管理员和开辟者计划。它包含了Stratum挖矿池服务器、奖励处理与支付功能以及一个相应式前端网站,提供实时统计和管理中心。NOMP基于node-stratum-pool模块,支持动态难度调解(vardiff)、工作证实(POW)和权益证实(POS)。它的安全特性包括DDoS攻击防护、IP禁止列表,并采用了Redis举行内存中的数据存储以优化性能。此外,其多币种挖掘和负载均衡能力使得管理多个币种的矿池变得简单。
该项目的安装配置不再举行详细介绍,感兴趣的请参考之前写的文章:https://www.cnblogs.com/zhaoweiwei/p/nomp.html
2 源码详解
2.1 目次
coins目次里是各个币种的名称及算法配置,libs目次中是各大功能模块的源码,node_modules目次中是各个nodejs模块,pool_configs目次中是矿池支持币种的配置,scripts目次中是两个关键性脚本文件,website目次中是前段相关代码及资源;
config.json用于用于设置矿池全局配置,如监听端口、连接超时、任务更新隔断等,init.js是nodejs入口文件,package.json用于记载依赖包及版本等相关信息。
2.2 入口函数
执行node init.js将会根据配置启动主步伐,首先会解析当前目次下的config.json配置文件,并将结果存储在portalConfig变量中,还会创建PoolLogger对象(源码对应libs\logUtil.js文件)统一管理log信息
之后,会里用cluster模块,来判断当进步程是主历程(通常称为“master”),还是工作历程(“workers”),对于工作历程,按照类型创建不同的实例- 1 if (cluster.isWorker){
- 2
- 3 switch(process.env.workerType){
- 4 case 'pool':
- 5 new PoolWorker(logger);
- 6 break;
- 7 case 'paymentProcessor':
- 8 new PaymentProcessor(logger);
- 9 break;
- 10 case 'website':
- 11 new Website(logger);
- 12 break;
- 13 case 'profitSwitch':
- 14 new ProfitSwitch(logger);
- 15 break;
- 16 }
- 17
- 18 return;
- 19 }
复制代码 假如是主历程则会调用以下功能模块函数,创建不同的工作历程- 1 (function init(){
- 2
- 3 poolConfigs = buildPoolConfigs();
- 4
- 5 spawnPoolWorkers();
- 6
- 7 startPaymentProcessor();
- 8
- 9 startWebsite();
- 10
- 11 startProfitSwitch();
- 12
- 13 startCliListener();
- 14
- 15 })();
复制代码 buildPoolConfigs函数会对相关配置文件举行解析整合
spawnPoolWorkers函数会创建PoolWorker历程,功能函数对应libs\poolWorker.js
startPaymentProcessor函数会创建paymentProcessor历程,功能函数对应libs\paymentProcessor.js
startWebsite函数会创建Website历程,功能函数对应libs\website.js
startProfitSwitch函数会创建ProfitSwitch历程,功能函数对应libs\profitSwitch.js
startCliListener函数会创建CliListener对象在cliPort端口举行监听并处理收到的消息,功能函数对应libs\cliListener.js
2.3 buildPoolConfigs
- 1 var buildPoolConfigs = function(){
- 2 var configs = {};
- 3 var configDir = 'pool_configs/';
- 4
- 5 var poolConfigFiles = [];
- 6
- 7
- 8 /* Get filenames of pool config json files that are enabled */
- 9 fs.readdirSync(configDir).forEach(function(file){
- 10 if (!fs.existsSync(configDir + file) || path.extname(configDir + file) !== '.json') return;
- 11 var poolOptions = JSON.parse(JSON.minify(fs.readFileSync(configDir + file, {encoding: 'utf8'})));
- 12 if (!poolOptions.enabled) return;
- 13 poolOptions.fileName = file;
- 14 poolConfigFiles.push(poolOptions);
- 15 });
- 16
- 17
- 18 /* Ensure no pool uses any of the same ports as another pool */
- 19 for (var i = 0; i < poolConfigFiles.length; i++){
- 20 var ports = Object.keys(poolConfigFiles[i].ports);
- 21 for (var f = 0; f < poolConfigFiles.length; f++){
- 22 if (f === i) continue;
- 23 var portsF = Object.keys(poolConfigFiles[f].ports);
- 24 for (var g = 0; g < portsF.length; g++){
- 25 if (ports.indexOf(portsF[g]) !== -1){
- 26 logger.error('Master', poolConfigFiles[f].fileName, 'Has same configured port of ' + portsF[g] + ' as ' + poolConfigFiles[i].fileName);
- 27 process.exit(1);
- 28 return;
- 29 }
- 30 }
- 31
- 32 if (poolConfigFiles[f].coin === poolConfigFiles[i].coin){
- 33 logger.error('Master', poolConfigFiles[f].fileName, 'Pool has same configured coin file coins/' + poolConfigFiles[f].coin + ' as ' + poolConfigFiles[i].fileName + ' pool');
- 34 process.exit(1);
- 35 return;
- 36 }
- 37
- 38 }
- 39 }
- 40
- 41
- 42 poolConfigFiles.forEach(function(poolOptions){
- 43
- 44 poolOptions.coinFileName = poolOptions.coin;
- 45
- 46 var coinFilePath = 'coins/' + poolOptions.coinFileName;
- 47 if (!fs.existsSync(coinFilePath)){
- 48 logger.error('Master', poolOptions.coinFileName, 'could not find file: ' + coinFilePath);
- 49 return;
- 50 }
- 51
- 52 var coinProfile = JSON.parse(JSON.minify(fs.readFileSync(coinFilePath, {encoding: 'utf8'})));
- 53 poolOptions.coin = coinProfile;
- 54 poolOptions.coin.name = poolOptions.coin.name.toLowerCase();
- 55
- 56 if (poolOptions.coin.name in configs){
- 57
- 58 logger.error('Master', poolOptions.fileName, 'coins/' + poolOptions.coinFileName
- 59 + ' has same configured coin name ' + poolOptions.coin.name + ' as coins/'
- 60 + configs[poolOptions.coin.name].coinFileName + ' used by pool config '
- 61 + configs[poolOptions.coin.name].fileName);
- 62
- 63 process.exit(1);
- 64 return;
- 65 }
- 66
- 67 for (var option in portalConfig.defaultPoolConfigs){
- 68 if (!(option in poolOptions)){
- 69 var toCloneOption = portalConfig.defaultPoolConfigs[option];
- 70 var clonedOption = {};
- 71 if (toCloneOption.constructor === Object)
- 72 extend(true, clonedOption, toCloneOption);
- 73 else
- 74 clonedOption = toCloneOption;
- 75 poolOptions[option] = clonedOption;
- 76 }
- 77 }
- 78
- 79
- 80 configs[poolOptions.coin.name] = poolOptions;
- 81
- 82 if (!(coinProfile.algorithm in algos)){
- 83 logger.error('Master', coinProfile.name, 'Cannot run a pool for unsupported algorithm "' + coinProfile.algorithm + '"');
- 84 delete configs[poolOptions.coin.name];
- 85 }
- 86
- 87 });
- 88 return configs;
- 89 };
复制代码 buildPoolConfigs9~15行会依次解析pool_configs中不同币种的配置文件,并将配置中使能状态为true的币种配置存储在poolConfigFiles边量。
19~39行检查每个币种会唯一的对应于coins目次的算法配置文件,且每个币种在矿池中使用不同的监听端口。
42~87行根据config.json中的全局配置,更新每个币种对应的配置(假如相应的配置项不存在),此外相应算法要在node_modules\stratum-pool\lib\algoProperties.js已实现,否则会删除对应算法的配置,即矿池不支持该算法。
88行将全局配置返回,并赋值给全局边量poolConfigs。
2.4 spawnPoolWorkers
- 1 var spawnPoolWorkers = function(){
- 2
- 3 Object.keys(poolConfigs).forEach(function(coin){
- 4 var p = poolConfigs[coin];
- 5
- 6 if (!Array.isArray(p.daemons) || p.daemons.length < 1){
- 7 logger.error('Master', coin, 'No daemons configured so a pool cannot be started for this coin.');
- 8 delete poolConfigs[coin];
- 9 }
- 10 });
- 11
- 12 if (Object.keys(poolConfigs).length === 0){
- 13 logger.warning('Master', 'PoolSpawner', 'No pool configs exists or are enabled in pool_configs folder. No pools spawned.');
- 14 return;
- 15 }
- 16
- 17
- 18 var serializedConfigs = JSON.stringify(poolConfigs);
- 19
- 20 var numForks = (function(){
- 21 if (!portalConfig.clustering || !portalConfig.clustering.enabled)
- 22 return 1;
- 23 if (portalConfig.clustering.forks === 'auto')
- 24 return os.cpus().length;
- 25 if (!portalConfig.clustering.forks || isNaN(portalConfig.clustering.forks))
- 26 return 1;
- 27 return portalConfig.clustering.forks;
- 28 })();
- 29
- 30 var poolWorkers = {};
- 31
- 32 var createPoolWorker = function(forkId){
- 33 var worker = cluster.fork({
- 34 workerType: 'pool',
- 35 forkId: forkId,
- 36 pools: serializedConfigs,
- 37 portalConfig: JSON.stringify(portalConfig)
- 38 });
- 39 worker.forkId = forkId;
- 40 worker.type = 'pool';
- 41 poolWorkers[forkId] = worker;
- 42 worker.on('exit', function(code, signal){
- 43 logger.error('Master', 'PoolSpawner', 'Fork ' + forkId + ' died, spawning replacement worker...');
- 44 setTimeout(function(){
- 45 createPoolWorker(forkId);
- 46 }, 2000);
- 47 }).on('message', function(msg){
- 48 switch(msg.type){
- 49 case 'banIP':
- 50 Object.keys(cluster.workers).forEach(function(id) {
- 51 if (cluster.workers[id].type === 'pool'){
- 52 cluster.workers[id].send({type: 'banIP', ip: msg.ip});
- 53 }
- 54 });
- 55 break;
- 56 }
- 57 });
- 58 };
- 59
- 60 var i = 0;
- 61 var spawnInterval = setInterval(function(){
- 62 createPoolWorker(i);
- 63 i++;
- 64 if (i === numForks){
- 65 clearInterval(spawnInterval);
- 66 logger.debug('Master', 'PoolSpawner', 'Spawned ' + Object.keys(poolConfigs).length + ' pool(s) on ' + numForks + ' thread(s)');
- 67 }
- 68 }, 250);
- 69
- 70 };
复制代码 spawnPoolWorkers 33行会创建pool类型的worker历程,这又会对应在2.2节介绍的内容,根据worker历程类型,创建PoolWorker实例。在PoolWorker中,首先会使用process来处理其他模块发送的IPC消息;之后创建ShareProcessor对象,用于管理客户端的share提交;本地handlers对象中不同函数处理与矿池stratum交互消息,如auth、share、diff等;最后通过Stratum.createPool创建矿池对象pool,并通过pool.start启动矿池,该部分详细内容请参考node_modules\stratum-pool\lib\pool.js文件内容。- 1 this.start = function(){
- 2 SetupVarDiff();
- 3 SetupApi();
- 4 SetupDaemonInterface(function(){
- 5 DetectCoinData(function(){
- 6 SetupRecipients();
- 7 SetupJobManager();
- 8 OnBlockchainSynced(function(){
- 9 GetFirstJob(function(){
- 10 SetupBlockPolling();
- 11 SetupPeer();
- 12 StartStratumServer(function(){
- 13 OutputPoolInfo();
- 14 _this.emit('started');
- 15 });
- 16 });
- 17 });
- 18 });
- 19 });
- 20 };
复制代码 第2行用于设置可变难度,即会根据客户端share的提交修改下发任务的难度。
第4行SetupDaemonInterface根据配置文件中钱包配置(钱包地点host的IP地址及监听端口,rpc用户名及密码),创建与钱包rpc通信的保卫线程daemon(参看node_modules\stratum-pool\lib\daemon.js)
之后在DetectCoinData函数中,通过validateaddress、getdifficulty、getmininginfo等rpc调用来对全局配置中类似hasSubmitMethod的关键项举行初始化,在OnBlockchainSynced函数中会等候钱包数据同步,同步完成后,调用GetFirstJob函数获取第一个job,在该函数中通过调用GetBlockTemplate从钱包获取block信息,然后通过jobManager.processTemplate来处理返回值,生成blockTemplate(node_modules\stratum-pool\lib\blockTemplate.js),在通过newBlock消息关照jobManager,jobManager再通过StartStratumServer将job广播出去,这里的jobParams对应于stratum协议的mining.notify中的params内容,如下图:
至于其他内容如任务提交、难度修改等处理都可以看node_modules\stratum-pool\lib相关内容。
2.5 startPaymentProcessor
- 1 var startPaymentProcessor = function(){
- 2
- 3 var enabledForAny = false;
- 4 for (var pool in poolConfigs){
- 5 var p = poolConfigs[pool];
- 6 var enabled = p.enabled && p.paymentProcessing && p.paymentProcessing.enabled;
- 7 if (enabled){
- 8 enabledForAny = true;
- 9 break;
- 10 }
- 11 }
- 12
- 13 if (!enabledForAny)
- 14 return;
- 15
- 16 var worker = cluster.fork({
- 17 workerType: 'paymentProcessor',
- 18 pools: JSON.stringify(poolConfigs)
- 19 });
- 20 worker.on('exit', function(code, signal){
- 21 logger.error('Master', 'Payment Processor', 'Payment processor died, spawning replacement...');
- 22 setTimeout(function(){
- 23 startPaymentProcessor(poolConfigs);
- 24 }, 2000);
- 25 });
- 26 };
复制代码 startPaymentProcessor 这部分内容是关于挖矿付款的处理,由于本人对这部分内容也没有深入了解,所以不再举行详细介绍。
2.6 startWebsite
- 1 var startWebsite = function(){
- 2
- 3 if (!portalConfig.website.enabled) return;
- 4
- 5 var worker = cluster.fork({
- 6 workerType: 'website',
- 7 pools: JSON.stringify(poolConfigs),
- 8 portalConfig: JSON.stringify(portalConfig)
- 9 });
- 10 worker.on('exit', function(code, signal){
- 11 logger.error('Master', 'Website', 'Website process died, spawning replacement...');
- 12 setTimeout(function(){
- 13 startWebsite(portalConfig, poolConfigs);
- 14 }, 2000);
- 15 });
- 16 };
复制代码 startWebsite 该部分利用express模块生成web前端,这部分内容相对比力独立,不再举行详细介绍,相关功能请直接参考源码。
2.7 startProfitSwitch
- 1 var startProfitSwitch = function(){
- 2
- 3 if (!portalConfig.profitSwitch || !portalConfig.profitSwitch.enabled){
- 4 //logger.error('Master', 'Profit', 'Profit auto switching disabled');
- 5 return;
- 6 }
- 7
- 8 var worker = cluster.fork({
- 9 workerType: 'profitSwitch',
- 10 pools: JSON.stringify(poolConfigs),
- 11 portalConfig: JSON.stringify(portalConfig)
- 12 });
- 13 worker.on('exit', function(code, signal){
- 14 logger.error('Master', 'Profit', 'Profit switching process died, spawning replacement...');
- 15 setTimeout(function(){
- 16 startWebsite(portalConfig, poolConfigs);
- 17 }, 2000);
- 18 });
- 19 };
复制代码 startProfitSwitch 该部分用于获取各大交易网站的实时价格信息,这部分代码已经不再更新,这里也不再详细介绍,有兴趣的请直接查看源码。
2.8 startCliListener
- 1 var startCliListener = function(){
- 2
- 3 var cliPort = portalConfig.cliPort;
- 4
- 5 var listener = new CliListener(cliPort);
- 6 listener.on('log', function(text){
- 7 logger.debug('Master', 'CLI', text);
- 8 }).on('command', function(command, params, options, reply){
- 9
- 10 switch(command){
- 11 case 'blocknotify':
- 12 Object.keys(cluster.workers).forEach(function(id) {
- 13 cluster.workers[id].send({type: 'blocknotify', coin: params[0], hash: params[1]});
- 14 });
- 15 reply('Pool workers notified');
- 16 break;
- 17 case 'coinswitch':
- 18 processCoinSwitchCommand(params, options, reply);
- 19 break;
- 20 case 'reloadpool':
- 21 Object.keys(cluster.workers).forEach(function(id) {
- 22 cluster.workers[id].send({type: 'reloadpool', coin: params[0] });
- 23 });
- 24 reply('reloaded pool ' + params[0]);
- 25 break;
- 26 default:
- 27 reply('unrecognized command "' + command + '"');
- 28 break;
- 29 }
- 30 }).start();
- 31 };
复制代码 startCliListener 第3行根据配置中的cliPort端口创建监听,在10~25行依次处理矿池详细业务相关的blocknotfy、coinswitch、reloadpool命令。
3 总结
NOMP以stratum-pool高性能Stratum池服务器为核心,该部分主要对象可以用下图举行表示:
在stratum-pool基础上,nomp增长网站前端、数据库层、多币种/池支持以及主动切换矿工在不同币种/池之间的操作等功能,如想单纯的查看stratum服务器核心功能,请直接参考该项目
https://github.com/zone117x/node-stratum-pool
也即NOMP项目下node_modules\stratum-pool内容。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |