mirror of https://github.com/BTCPrivate/z-nomp.git
Compare commits
157 Commits
Author | SHA1 | Date |
---|---|---|
sennevb | d49e9b4635 | |
sennevb | 0ebab3196c | |
Konstantin Crutched Anikin | ed76b45f4b | |
OleksandrBlack | cd9a1a2807 | |
sennevb | 53e9fdb28a | |
sennevb | 621181ce69 | |
hellcatz | 0dd13bec9d | |
kalmare | 8481e3df5f | |
sennevb | e1eba6d88f | |
sennevb | af74e65f05 | |
sennevb | 1bccbebbf2 | |
sennevb | 289600de24 | |
Larry Ludlow | cdce1a36a0 | |
Larry Ludlow | 677b367004 | |
Larry Ludlow | a8cd7ba9e0 | |
sennevb | ff0e1f6f32 | |
hellcatz | 3f30f01a9f | |
hellcatz | f9cdef7a72 | |
hellcatz | 9a4497323b | |
hellcatz | f192dd39c4 | |
hellcatz | 7233c9b726 | |
hellcatz | 8d463f5c37 | |
hellcatz | 81bba13224 | |
hellcatz | c826e4d8f3 | |
hellcatz | 5891af3a2e | |
hellcatz | d8a9ec59a4 | |
hellcatz | eb5caf93fa | |
hellcatz | 9911037d1b | |
sennevb | 589ec86400 | |
hellcatz | e2af3372dd | |
sennevb | 5e1301406e | |
sennevb | 4a454bbbd9 | |
Larry Ludlow | f7499029e4 | |
Larry Ludlow | 8a852b82ab | |
hellcatz | dfad325b54 | |
hellcatz | c0e0cea629 | |
hellcatz | 81b7e2e6ee | |
sennevb | cb2951e638 | |
sennevb | 030ff2931b | |
sennevb | 2c68351b4e | |
sennevb | e8e52e3eb3 | |
sennevb | 0c341dffc9 | |
sennevb | 2d7f86fa9f | |
sennevb | 4c982a73ac | |
hellcatz | 129e11e8e3 | |
hellcatz | f6fe32b7e5 | |
madbuda | 82d765f560 | |
madbuda | 86245effe8 | |
hellcatz | f2786e7a5b | |
sennevb | 15f4912b4a | |
Larry | abf70fcfc0 | |
hellcatz | d8dd19ff8b | |
hellcatz | b16990e18a | |
hellcatz | 24a84c6380 | |
hellcatz | 7a3bf72d41 | |
Larry | 3f2bbe8182 | |
Larry | 53183e108c | |
Larry | 3c39b549e0 | |
Procrastinator | e1675cb5fe | |
Procrastinator | 6b36c755d4 | |
Larry | 975e82dedd | |
Larry | 902664c904 | |
Larry | 514f2fa869 | |
Larry | bc92044354 | |
Larry | 0403a7d763 | |
Larry | c74724c19a | |
Larry | f6966534ff | |
Larry | bdbe9a7896 | |
Larry | d96fa1e06b | |
Larry | 6bf70dfb20 | |
Larry | 1785519db3 | |
Larry | a30a9d0ba6 | |
Joshua McKenzie | 04ef1a4cc0 | |
Joshua Yabut | 350a6eb44c | |
Joshua Yabut | 8814ce2b08 | |
hellcatz | f6f6f0c712 | |
hellcatz | 8fd1d7eceb | |
Procrastinator | 6e638e7bf6 | |
hellcatz | aa25a81fe1 | |
Procrastinator | ec70c56327 | |
Procrastinator | 5eed7fc001 | |
Procrastinator | 606bc1f0a8 | |
Procrastinator | 291c26360d | |
Procrastinator | 1988e8186d | |
Aayan L | be2e0d6bfe | |
Procrastinator | f1a9c80feb | |
hellcatz | c5bdda780c | |
hellcatz | 9f0a8f5c32 | |
hellcatz | ee27470e7a | |
hellcatz | 6edd190842 | |
Procrastinator | ec794f34f5 | |
hellcatz | 51d468adb3 | |
hellcatz | fe72b6e533 | |
Procrastinator | e40f2913b1 | |
hellcatz | 9c237291dd | |
hellcatz | fe78759bf8 | |
hellcatz | edf10ef2c9 | |
hellcatz | 8c94daec98 | |
hellcatz | d233ffcb0d | |
hellcatz | d53e6db63a | |
hellcatz | 3062f5ae9e | |
hellcatz | 6f62e0a7c6 | |
hellcatz | e9d4c0bc92 | |
hellcatz | 7e34563af4 | |
hellcatz | 6ea1df2cd5 | |
hellcatz | 7865d6818e | |
hellcatz | 3bc190ada0 | |
hellcatz | 8f68e3e36a | |
hellcatz | d5d4f9c67a | |
hellcatz | 133ce797b7 | |
hellcatz | 8d5a13697f | |
hellcatz | c0f66c0b29 | |
hellcatz | 241bdea7ee | |
hellcatz | 774e2b0088 | |
Aayan L | e6c4bf4c10 | |
Procrastinator | d35a770faa | |
hellcatz | aae3018f55 | |
hellcatz | ea82816ce4 | |
hellcatz | 4d2c35312d | |
hellcatz | cbe82662a8 | |
Procrastinator | fbc868721c | |
Aayan L | 5fafea636c | |
hellcatz | 0f6192e3aa | |
Aayan L | a0548f53eb | |
hellcatz | 4e23d7cace | |
hellcatz | fb5bcf4af5 | |
hellcatz | 7f66c85362 | |
hellcatz | 4ac324c796 | |
hellcatz | b224c82971 | |
hellcatz | 2d85b9f34a | |
Procrastinator | 28f3c476c2 | |
Procrastinator | b8e60519ae | |
hellcatz | 8f942b7c45 | |
Procrastinator | cbad5466da | |
Procrastinator | 8b33ddd6f1 | |
Procrastinator | 834f453cf0 | |
Aayan L | 7839b606a1 | |
Procrastinator | dab2bf427f | |
Procrastinator | 55aa90ed8b | |
Procrastinator | 354d88257d | |
Procrastinator | ed06afc205 | |
hellcatz | 03e141d0b6 | |
Procrastinator | 6937e7b6e2 | |
hellcatz | b0cc4cd560 | |
hellcatz | ccdbc5c1e5 | |
hellcatz | efd0a9be09 | |
hellcatz | 2173628e5e | |
hellcatz | 9b8fbd6885 | |
hellcatz | ca297bc666 | |
hellcatz | 8ef812e1ac | |
hellcatz | 9d113b9f49 | |
hellcatz | a907dfb709 | |
hellcatz | fcac8370b3 | |
sennevb | 0c4cd514b7 | |
hellcatz | b3c05a9d02 | |
hellcatz | 16aff2d0de | |
Procrastinator | 53852d6f0f |
|
@ -17,12 +17,11 @@ Usage of this software requires abilities with sysadmin, database admin, coin da
|
||||||
|
|
||||||
### Community / Support
|
### Community / Support
|
||||||
IRC
|
IRC
|
||||||
* Support / general discussion join: https://rocketchat.zdeveloper.org
|
* Support / general discussion join: https://gitter.im/zclassicorg/z-nomp
|
||||||
|
|
||||||
If your pool uses Z-NOMP let us know and we will list your website here.
|
If your pool uses Z-NOMP let us know and we will list your website here.
|
||||||
|
|
||||||
### Some pools using Z-NOMP or node-stratum-module:
|
### Some pools using Z-NOMP or node-stratum-module:
|
||||||
https://zpool.minegate.eu/ 1% with the whole fee going to the Zclassic donation fund!
|
|
||||||
|
|
||||||
https://pool.cryptobroker.io/zcl Running MPOS and 0.5% of the fee goes to the Zclassic donation fund! 200+ blocks have been found as well!
|
https://pool.cryptobroker.io/zcl Running MPOS and 0.5% of the fee goes to the Zclassic donation fund! 200+ blocks have been found as well!
|
||||||
|
|
||||||
|
@ -32,6 +31,12 @@ http://zclmine.com/ Custom frontend
|
||||||
|
|
||||||
http://zclassic.miningspeed.com Custom frontend and 0% fee
|
http://zclassic.miningspeed.com Custom frontend and 0% fee
|
||||||
|
|
||||||
|
https://zpool.it 0.5% fee
|
||||||
|
|
||||||
|
http://miningpool.io/
|
||||||
|
|
||||||
|
https://lucky-mining.com.ua/ Running MPOS and no fee, [vot][zcl][zen][hush][btg].lucky-mining.com.ua <-- Ukraine
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
{
|
||||||
|
"name": "zen",
|
||||||
|
"symbol": "zen",
|
||||||
|
"algorithm": "equihash",
|
||||||
|
"requireShielding": true,
|
||||||
|
"payFoundersReward": true,
|
||||||
|
"percentFoundersReward": 8.5,
|
||||||
|
"maxFoundersRewardBlockHeight": 839999,
|
||||||
|
"foundersRewardAddressChangeInterval": 17500,
|
||||||
|
"vFoundersRewardAddress": [
|
||||||
|
"zssEdGnZCQ9G86LZFtbynMn1hYTVhn6eYCL",
|
||||||
|
"zsrCsXXmUf8k59NLasEKfxA7us3iNvaPATz",
|
||||||
|
"zsnLPsWMXW2s4w9EmFSwtSLRxL2LhPcfdby",
|
||||||
|
"zshdovbcPfUAfkPeEE2qLbKnoue9RsbVokU",
|
||||||
|
"zsqmq97JAKCCBFRGvxgv6FiJgQLCZBDp62S",
|
||||||
|
"zskyFVFA7VRYX8EGdXmYN75WaBB25FmiL3g",
|
||||||
|
"zsmncLmwEUdVmAGPUrUnNKmPGXyej7mbmdM",
|
||||||
|
"zsfa9VVJCEdjfPbku4XrFcRR8kTDm2T64rz",
|
||||||
|
"zsjdMnfWuFi46VeN2HSXVQWEGsnGHgVxayY",
|
||||||
|
"zseb8wRQ8rZ722oLX5B8rx7qwZiBRb9mdig",
|
||||||
|
"zsjxkovhqiMVggoW7jvSRi3NTSD3a6b6qfd",
|
||||||
|
"zsokCCSU3wvZrS2G6mEDpJ5cH49E7sDyNr1",
|
||||||
|
"zt12EsFgkABHLMRXA7JNnpMqLrxsgCLnVEV",
|
||||||
|
"zt39mvuG9gDTHX8A8Qk45rbk3dSdQoJ8ZAv",
|
||||||
|
"zssTQZs5YxDGijKC86dvcDxzWogWcK7n5AK",
|
||||||
|
"zsywuMoQK7Bved2nrXs56AEtWBhpb88rMzS",
|
||||||
|
"zsxVS2w7h1fHFX2nQtGm4372pd4DSHzq9ee",
|
||||||
|
"zsupGi7ro3uC8CEVwm9r7vrdVUZaXQnHF6T",
|
||||||
|
"zshVZvW47dA5AB3Sqk1h7ytwWJeUJUJxxaE",
|
||||||
|
"zsubBCjvDx252MKFsL4Dcf5rJU9Z9Upqr1N",
|
||||||
|
"zsweaST3NcU4hfgkVULfCsfEq41pjgMDgcW",
|
||||||
|
"zswz6Rxb1S33fUpftETZwtGiVSeYxNKq2xc",
|
||||||
|
"zswnpHtiBbrvYDzbhPQshkgvLSfYhDMRJ4S",
|
||||||
|
"zsjSYAWaEYj35Ht7aXrRJUGY6Dc8qCmgYqu",
|
||||||
|
"zsvMv8fGroWR8epbSiGDCJHmfe6ec2uFQrt",
|
||||||
|
"zsujxCT56BExQDAwKwktBjtnopYnw8BiKbg",
|
||||||
|
"zsxeXc2FTAzmUmeZmqVsKVdwTMSvzyns4rT",
|
||||||
|
"zsuLqgABNudD8bVPbVGeUjGqapuoXp68i7F",
|
||||||
|
"zsoc39J1dCFK1U8kckZznvQkv8As7sajYLz",
|
||||||
|
"zt21NFdu1KRPJ7VRKtrWugM2Jqe5ePNmU4T",
|
||||||
|
"zsp15qbVcbx9ifcjKe6XZEJTvzsFUZ2BHLT",
|
||||||
|
"zso2KvqH6yxLQEYggHdmfL3Tcd5V6E9tqhp",
|
||||||
|
"zsnFG2W5ZHRYh3QucNze4mp31tBkemtfxdj",
|
||||||
|
"zsex2CGJtxHyHbpLXm7kESBmp3vWRqUkJMy",
|
||||||
|
"zsvtFv96nrgrXKUbtNe2BpCt8aQEp5oJ7F8",
|
||||||
|
"zsk5KitThmhK9KBa1KDybPgEmGSFTHzhMVA",
|
||||||
|
"zsuy4n48c4NsJyaCZEzwdAKULM1FqbB6Y4z",
|
||||||
|
"zsgtQVMpX2zNMLvHHG2NDwfqKoaebvVectJ",
|
||||||
|
"zszQqXRSPGdqsWw4iaMTNN6aJz4JjEzSdCF",
|
||||||
|
"zst6dBLrTtaMQBX7BLMNjKLTGcP11PBmgTV",
|
||||||
|
"zshD9r6Eb6dZGdzYW2HCb9CzkMokCT1NGJR",
|
||||||
|
"zswUaj1TboEGmvSfF7fdoxWyH3RMx7MBHHo",
|
||||||
|
"zsv8s4Poi5GxCsbBrRJ97Vsvazp84nrz5AN",
|
||||||
|
"zsmmxrKU6dqWFwUKow1iyovg3gxrgXpEivr",
|
||||||
|
"zskh1221aRC9WEfb5a59WxffeW34McmZZsw",
|
||||||
|
"zssAhuj57NnVm4yNFT6o8muRctABkUaBu3L",
|
||||||
|
"zsi5Yr4Z8HwBvdBqQE8gk7ahExDu95J4oqZ",
|
||||||
|
"zsy6ryEaxfk8emJ8bGVB7tmwRwBL8cfSqBW"
|
||||||
|
],
|
||||||
|
"percentTreasuryReward": 12.0,
|
||||||
|
"treasuryRewardStartBlockHeight": 139200,
|
||||||
|
"treasuryRewardAddressChangeInterval": 50000,
|
||||||
|
"vTreasuryRewardAddress": [
|
||||||
|
"zsyF68hcYYNLPj5i4PfQJ1kUY6nsFnZkc82",
|
||||||
|
"zsfULrmbX7xbhqhAFRffVqCw9RyGv2hqNNG",
|
||||||
|
"zsoemTfqjicem2QVU8cgBHquKb1o9JR5p4Z",
|
||||||
|
"zt339oiGL6tTgc9Q71f5g1sFTZf6QiXrRUr"
|
||||||
|
],
|
||||||
|
"peerMagic": "63617368",
|
||||||
|
"txfee": 0.0004
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "zen_testnet",
|
||||||
|
"symbol": "znt",
|
||||||
|
"algorithm": "equihash",
|
||||||
|
"requireShielding": true,
|
||||||
|
"payFoundersReward": true,
|
||||||
|
"percentFoundersReward": 8.5,
|
||||||
|
"maxFoundersRewardBlockHeight": 839999,
|
||||||
|
"foundersRewardAddressChangeInterval": 17500,
|
||||||
|
"vFoundersRewardAddress": [
|
||||||
|
"zrH8KT8KUcpKKNBu3fjH4hA84jZBCawErqn", "zrGsMC4ou1r5Vxy7Dnxg4PfKpansx83BM8g", "zr6sB2Az36D8CqzeZNavB11WbovsGtJSAZG", "zrBAG3pXCTDq14nivNK9mW8SfwMNcdmMQpb",
|
||||||
|
"zrRLwpYRYky4wsvwLVrDp8fs89EBTRhNMB1", "zrLozMfptTmE3zLP5SrTLyB8TXqH84Agjrr", "zrMckkaLtVTEUvxj4ouU7BPDGa8xmdTZSVE", "zrFc897wJXmF7BcEdbvi2mS1bLrcuWYK6hm",
|
||||||
|
"zrHEnni486u9SNcLWacroSgcdyMA33tfM92", "zrJ3ymPV3R8Xk4N3BdNb898xvZvARm5K7mq", "zrDj3P6trx73291krxU51U9QnfkbGxkyJ6E", "zrJs3vMGFJi9pQCireeSLckJonamBnwTSrY",
|
||||||
|
"zrKFdXQoAkkycy52EFoWARyzZWx6Kat2Som", "zrEXbSe79FXA9KRMnJTZUWZdZhNnjcnAdrq", "zr7iAwfNgJsMpSCmijK3TuVDuNvSmLr1rUz", "zrDEK7K6cftqSjeyVUH1WqJtBUkXN7GidxH",
|
||||||
|
"zrRennuq75hyBVU4JymtZk8UcQ1vRPKpmpj", "zr9HRTL79pKmn5R8fvkC9kucZ4u1bQruLTD", "zrML8KXpJsa1NVnbJuawX86ZvAn543tWdTT", "zrLBAkQoxpEtnztSUEcdxnEvuwtgxyAMGX7",
|
||||||
|
"zr6kPnVzFBYmcBDsWoTrPHRuBxLq21o4zvT", "zrMY3vdvqs9KSvx9TawvcyuVurt1Jj6GPVo", "zr9WB1qBpM4nwi1mudUFfjtMNmqzaBQDsXn", "zrAHbtHDPAqmzWJMQqSYzGyFnDWN3oELZRs",
|
||||||
|
"zrH1f5K3z7EQ6RWWZ7StCDWHTZwFChBVA2W", "zrNTacAid9LS4kAqzM4sw1YcF7gLFrzVM7U", "zrFyZpMVKMeDqbn6A2uUiL9mZmgxuR1pUBg", "zrD1cqGFGzBcPogFHJvnN4XegvvmbTjA43t",
|
||||||
|
"zr5A1D7czWkB4pAWfGC5Pux5Ek7anYybdPK", "zr8yTAxCy6jAdsc6qPvmVEQHbYo25AJKhy9", "zrFW2YjQw4cABim5kEDwapbSqTz3wW7cWkk", "zr9nJvNbsrvUTZD41fhqAQeUcgMfqZmAweN",
|
||||||
|
"zrCx4dXZd5b2tD483Ds4diHpo1QxBMJ76Jr", "zr6eVeRwU6Puob3K1RfWtva1R458oj8pzkL", "zr7B92iHtQcobZjGCXo3DAqMQjsn7ka31wE", "zr8bcemLWAjYuphXSVqtqZWEnJipCB9F5oC",
|
||||||
|
"zrFzsuPXb7HsFd3srBqtVqnC9GQ94DQubV2", "zr4yiBobiHjHnCYi75NmYtyoqCV4A3kpHDL", "zrGVdR4K4F8MfmWxhUiTypK7PTsvHi8uTAh", "zr7WiCDqCMvUdH1xmMu8YrBMFb2x2E6BX3z",
|
||||||
|
"zrEFrGWLX4hPHuHRUD3TPbMAJyeSpMSctUc", "zr5c3f8PTnW8qBFX1GvK2LhyLBBCb1WDdGG", "zrGkAZkZLqC9QKJR3XomgxNizCpNuAupTeg", "zrM7muDowiun9tCHhu5K9vcDGfUptuYorfZ",
|
||||||
|
"zrCsWfwKotWnQmFviqAHAPAJ2jXqZYW966P", "zrLLB3JB3jozUoMGFEGhjqyVXTpngVQ8c4T", "zrAEa8YjJ2f3m2VsM1Xa9EwibZxEnRoSLUx", "zrAdJgp7Cx35xTvB7ABWP8YLTNDArMjP1s3"
|
||||||
|
],
|
||||||
|
"percentTreasuryReward": 12.0,
|
||||||
|
"treasuryRewardStartBlockHeight": 85500,
|
||||||
|
"treasuryRewardAddressChangeInterval": 10000,
|
||||||
|
"vTreasuryRewardAddress": [
|
||||||
|
"zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1", "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1", "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1", "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1"
|
||||||
|
],
|
||||||
|
"txfee": 0.0004
|
||||||
|
}
|
|
@ -25,7 +25,8 @@
|
||||||
},
|
},
|
||||||
"redis": {
|
"redis": {
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"port": 6379
|
"port": 6379,
|
||||||
|
"password": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -42,6 +43,11 @@
|
||||||
"adminCenter": {
|
"adminCenter": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"password": "password"
|
"password": "password"
|
||||||
|
},
|
||||||
|
"tlsOptions" : {
|
||||||
|
"enabled": false,
|
||||||
|
"cert": "",
|
||||||
|
"key": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
88
init.js
88
init.js
|
@ -6,6 +6,8 @@ var cluster = require('cluster');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var extend = require('extend');
|
var extend = require('extend');
|
||||||
|
|
||||||
|
var redis = require('redis');
|
||||||
|
|
||||||
var PoolLogger = require('./libs/logUtil.js');
|
var PoolLogger = require('./libs/logUtil.js');
|
||||||
var CliListener = require('./libs/cliListener.js');
|
var CliListener = require('./libs/cliListener.js');
|
||||||
var PoolWorker = require('./libs/poolWorker.js');
|
var PoolWorker = require('./libs/poolWorker.js');
|
||||||
|
@ -31,9 +33,6 @@ var logger = new PoolLogger({
|
||||||
logColors: portalConfig.logColors
|
logColors: portalConfig.logColors
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
require('newrelic');
|
require('newrelic');
|
||||||
if (cluster.isMaster)
|
if (cluster.isMaster)
|
||||||
|
@ -66,7 +65,6 @@ catch(e){
|
||||||
logger.debug('POSIX', 'Connection Limit', '(Safe to ignore) POSIX module not installed and resource (connection) limit was not raised');
|
logger.debug('POSIX', 'Connection Limit', '(Safe to ignore) POSIX module not installed and resource (connection) limit was not raised');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (cluster.isWorker){
|
if (cluster.isWorker){
|
||||||
|
|
||||||
switch(process.env.workerType){
|
switch(process.env.workerType){
|
||||||
|
@ -179,16 +177,36 @@ var buildPoolConfigs = function(){
|
||||||
return configs;
|
return configs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function roundTo(n, digits) {
|
||||||
|
if (digits === undefined) {
|
||||||
|
digits = 0;
|
||||||
|
}
|
||||||
|
var multiplicator = Math.pow(10, digits);
|
||||||
|
n = parseFloat((n * multiplicator).toFixed(11));
|
||||||
|
var test =(Math.round(n) / multiplicator);
|
||||||
|
return +(test.toFixed(digits));
|
||||||
|
}
|
||||||
|
|
||||||
|
var _lastStartTimes = [];
|
||||||
|
var _lastShareTimes = [];
|
||||||
|
|
||||||
var spawnPoolWorkers = function(){
|
var spawnPoolWorkers = function(){
|
||||||
|
|
||||||
Object.keys(poolConfigs).forEach(function(coin){
|
var redisConfig;
|
||||||
var p = poolConfigs[coin];
|
var connection;
|
||||||
|
|
||||||
if (!Array.isArray(p.daemons) || p.daemons.length < 1){
|
Object.keys(poolConfigs).forEach(function(coin){
|
||||||
|
var pcfg = poolConfigs[coin];
|
||||||
|
if (!Array.isArray(pcfg.daemons) || pcfg.daemons.length < 1){
|
||||||
logger.error('Master', coin, 'No daemons configured so a pool cannot be started for this coin.');
|
logger.error('Master', coin, 'No daemons configured so a pool cannot be started for this coin.');
|
||||||
delete poolConfigs[coin];
|
delete poolConfigs[coin];
|
||||||
|
} else if (!connection) {
|
||||||
|
redisConfig = pcfg.redis;
|
||||||
|
connection = redis.createClient(redisConfig.port, redisConfig.host);
|
||||||
|
connection.on('ready', function(){
|
||||||
|
logger.debug('PPLNT', coin, 'TimeShare processing setup with redis (' + redisConfig.host +
|
||||||
|
':' + redisConfig.port + ')');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -236,6 +254,62 @@ var spawnPoolWorkers = function(){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case 'shareTrack':
|
||||||
|
// pplnt time share tracking of workers
|
||||||
|
if (msg.isValidShare && !msg.isValidBlock) {
|
||||||
|
var now = Date.now();
|
||||||
|
var lastShareTime = now;
|
||||||
|
var lastStartTime = now;
|
||||||
|
var workerAddress = msg.data.worker.split('.')[0];
|
||||||
|
|
||||||
|
// if needed, initialize PPLNT objects for coin
|
||||||
|
if (!_lastShareTimes[msg.coin]) {
|
||||||
|
_lastShareTimes[msg.coin] = {};
|
||||||
|
}
|
||||||
|
if (!_lastStartTimes[msg.coin]) {
|
||||||
|
_lastStartTimes[msg.coin] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// did they just join in this round?
|
||||||
|
if (!_lastShareTimes[msg.coin][workerAddress] || !_lastStartTimes[msg.coin][workerAddress]) {
|
||||||
|
_lastShareTimes[msg.coin][workerAddress] = now;
|
||||||
|
_lastStartTimes[msg.coin][workerAddress] = now;
|
||||||
|
logger.debug('PPLNT', msg.coin, 'Thread '+msg.thread, workerAddress+' joined.');
|
||||||
|
}
|
||||||
|
// grab last times from memory objects
|
||||||
|
if (_lastShareTimes[msg.coin][workerAddress] != null && _lastShareTimes[msg.coin][workerAddress] > 0) {
|
||||||
|
lastShareTime = _lastShareTimes[msg.coin][workerAddress];
|
||||||
|
lastStartTime = _lastStartTimes[msg.coin][workerAddress];
|
||||||
|
}
|
||||||
|
|
||||||
|
var redisCommands = [];
|
||||||
|
|
||||||
|
// if its been less than 15 minutes since last share was submitted
|
||||||
|
var timeChangeSec = roundTo(Math.max(now - lastShareTime, 0) / 1000, 4);
|
||||||
|
//var timeChangeTotal = roundTo(Math.max(now - lastStartTime, 0) / 1000, 4);
|
||||||
|
if (timeChangeSec < 900) {
|
||||||
|
// loyal miner keeps mining :)
|
||||||
|
redisCommands.push(['hincrbyfloat', msg.coin + ':shares:timesCurrent', workerAddress + "." + poolConfigs[msg.coin].poolId, timeChangeSec]);
|
||||||
|
//logger.debug('PPLNT', msg.coin, 'Thread '+msg.thread, workerAddress+':{totalTimeSec:'+timeChangeTotal+', timeChangeSec:'+timeChangeSec+'}');
|
||||||
|
connection.multi(redisCommands).exec(function(err, replies){
|
||||||
|
if (err)
|
||||||
|
logger.error('PPLNT', msg.coin, 'Thread '+msg.thread, 'Error with time share processor call to redis ' + JSON.stringify(err));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// they just re-joined the pool
|
||||||
|
_lastStartTimes[workerAddress] = now;
|
||||||
|
logger.debug('PPLNT', msg.coin, 'Thread '+msg.thread, workerAddress+' re-joined.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// track last time share
|
||||||
|
_lastShareTimes[msg.coin][workerAddress] = now;
|
||||||
|
}
|
||||||
|
if (msg.isValidBlock) {
|
||||||
|
// reset pplnt share times for next round
|
||||||
|
_lastShareTimes[msg.coin] = {};
|
||||||
|
_lastStartTimes[msg.coin] = {};
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
18
libs/api.js
18
libs/api.js
|
@ -15,19 +15,30 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
|
|
||||||
switch(req.params.method){
|
switch(req.params.method){
|
||||||
case 'stats':
|
case 'stats':
|
||||||
|
res.header('Content-Type', 'application/json');
|
||||||
res.end(portalStats.statsString);
|
res.end(portalStats.statsString);
|
||||||
return;
|
return;
|
||||||
case 'pool_stats':
|
case 'pool_stats':
|
||||||
|
res.header('Content-Type', 'application/json');
|
||||||
res.end(JSON.stringify(portalStats.statPoolHistory));
|
res.end(JSON.stringify(portalStats.statPoolHistory));
|
||||||
return;
|
return;
|
||||||
|
case 'blocks':
|
||||||
|
case 'getblocksstats':
|
||||||
|
portalStats.getBlocks(function(data){
|
||||||
|
res.header('Content-Type', 'application/json');
|
||||||
|
res.end(JSON.stringify(data));
|
||||||
|
});
|
||||||
|
break;
|
||||||
case 'payments':
|
case 'payments':
|
||||||
var poolBlocks = [];
|
var poolBlocks = [];
|
||||||
for(var pool in portalStats.stats.pools) {
|
for(var pool in portalStats.stats.pools) {
|
||||||
poolBlocks.push({name: pool, pending: portalStats.stats.pools[pool].pending, payments: portalStats.stats.pools[pool].payments});
|
poolBlocks.push({name: pool, pending: portalStats.stats.pools[pool].pending, payments: portalStats.stats.pools[pool].payments});
|
||||||
}
|
}
|
||||||
|
res.header('Content-Type', 'application/json');
|
||||||
res.end(JSON.stringify(poolBlocks));
|
res.end(JSON.stringify(poolBlocks));
|
||||||
return;
|
return;
|
||||||
case 'worker_stats':
|
case 'worker_stats':
|
||||||
|
res.header('Content-Type', 'application/json');
|
||||||
if (req.url.indexOf("?")>0) {
|
if (req.url.indexOf("?")>0) {
|
||||||
var url_parms = req.url.split("?");
|
var url_parms = req.url.split("?");
|
||||||
if (url_parms.length > 0) {
|
if (url_parms.length > 0) {
|
||||||
|
@ -35,7 +46,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
var workers = {};
|
var workers = {};
|
||||||
var address = url_parms[1] || null;
|
var address = url_parms[1] || null;
|
||||||
//res.end(portalStats.getWorkerStats(address));
|
//res.end(portalStats.getWorkerStats(address));
|
||||||
if (address != null && address.length > 0 && address.startsWith('t'),('R')) {
|
if (address != null && address.length > 0) {
|
||||||
// make sure it is just the miners address
|
// make sure it is just the miners address
|
||||||
address = address.split(".")[0];
|
address = address.split(".")[0];
|
||||||
// get miners balance along with worker balances
|
// get miners balance along with worker balances
|
||||||
|
@ -43,8 +54,6 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
// get current round share total
|
// get current round share total
|
||||||
portalStats.getTotalSharesByAddress(address, function(shares) {
|
portalStats.getTotalSharesByAddress(address, function(shares) {
|
||||||
var totalHash = parseFloat(0.0);
|
var totalHash = parseFloat(0.0);
|
||||||
var totalHeld = parseFloat(0.0);
|
|
||||||
var totalPaid = parseFloat(0.0);
|
|
||||||
var totalShares = shares;
|
var totalShares = shares;
|
||||||
var networkSols = 0;
|
var networkSols = 0;
|
||||||
for (var h in portalStats.statHistory) {
|
for (var h in portalStats.statHistory) {
|
||||||
|
@ -80,7 +89,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.end(JSON.stringify({miner: address, totalHash: totalHash, totalShares: totalShares, networkSols: networkSols, balance: balances.totalHeld, paid: balances.totalPaid, workers: workers, history: history}));
|
res.end(JSON.stringify({miner: address, totalHash: totalHash, totalShares: totalShares, networkSols: networkSols, immature: balances.totalImmature, balance: balances.totalHeld, paid: balances.totalPaid, workers: workers, history: history}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,6 +111,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
res.write('\n');
|
res.write('\n');
|
||||||
var uid = Math.random().toString();
|
var uid = Math.random().toString();
|
||||||
_this.liveStatConnections[uid] = res;
|
_this.liveStatConnections[uid] = res;
|
||||||
|
res.flush();
|
||||||
req.on("close", function() {
|
req.on("close", function() {
|
||||||
delete _this.liveStatConnections[uid];
|
delete _this.liveStatConnections[uid];
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,7 +19,9 @@ module.exports = function(logger){
|
||||||
var proxySwitch = {};
|
var proxySwitch = {};
|
||||||
|
|
||||||
var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
|
var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
|
||||||
|
if (portalConfig.redis.password) {
|
||||||
|
redisClient.auth(portalConfig.redis.password);
|
||||||
|
}
|
||||||
//Handle messages from master process sent via IPC
|
//Handle messages from master process sent via IPC
|
||||||
process.on('message', function(message) {
|
process.on('message', function(message) {
|
||||||
switch(message.type){
|
switch(message.type){
|
||||||
|
@ -133,16 +135,6 @@ module.exports = function(logger){
|
||||||
handlers.auth = function(port, workerName, password, authCallback){
|
handlers.auth = function(port, workerName, password, authCallback){
|
||||||
if (poolOptions.validateWorkerUsername !== true)
|
if (poolOptions.validateWorkerUsername !== true)
|
||||||
authCallback(true);
|
authCallback(true);
|
||||||
else {
|
|
||||||
if (workerName.length === 40) {
|
|
||||||
try {
|
|
||||||
new Buffer(workerName, 'hex');
|
|
||||||
authCallback(true);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
authCallback(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
pool.daemon.cmd('validateaddress', [String(workerName).split(".")[0]], function (results) {
|
pool.daemon.cmd('validateaddress', [String(workerName).split(".")[0]], function (results) {
|
||||||
var isValid = results.filter(function (r) {
|
var isValid = results.filter(function (r) {
|
||||||
|
@ -151,8 +143,6 @@ module.exports = function(logger){
|
||||||
authCallback(isValid);
|
authCallback(isValid);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handlers.share = function(isValidShare, isValidBlock, data){
|
handlers.share = function(isValidShare, isValidBlock, data){
|
||||||
|
@ -187,18 +177,21 @@ module.exports = function(logger){
|
||||||
logger.debug(logSystem, logComponent, logSubCat, 'Block found: ' + data.blockHash + ' by ' + data.worker);
|
logger.debug(logSystem, logComponent, logSubCat, 'Block found: ' + data.blockHash + ' by ' + data.worker);
|
||||||
|
|
||||||
if (isValidShare) {
|
if (isValidShare) {
|
||||||
if(data.shareDiff > 1000000000)
|
if(data.shareDiff > 1000000000) {
|
||||||
logger.debug(logSystem, logComponent, logSubCat, 'Share was found with diff higher than 1.000.000.000!');
|
logger.debug(logSystem, logComponent, logSubCat, 'Share was found with diff higher than 1.000.000.000!');
|
||||||
else if(data.shareDiff > 1000000)
|
} else if(data.shareDiff > 1000000) {
|
||||||
logger.debug(logSystem, logComponent, logSubCat, 'Share was found with diff higher than 1.000.000!');
|
logger.debug(logSystem, logComponent, logSubCat, 'Share was found with diff higher than 1.000.000!');
|
||||||
|
}
|
||||||
//logger.debug(logSystem, logComponent, logSubCat, 'Share accepted at diff ' + data.difficulty + '/' + data.shareDiff + ' by ' + data.worker + ' [' + data.ip + ']' );
|
//logger.debug(logSystem, logComponent, logSubCat, 'Share accepted at diff ' + data.difficulty + '/' + data.shareDiff + ' by ' + data.worker + ' [' + data.ip + ']' );
|
||||||
|
} else if (!isValidShare) {
|
||||||
} else if (!isValidShare)
|
|
||||||
logger.debug(logSystem, logComponent, logSubCat, 'Share rejected: ' + shareData);
|
logger.debug(logSystem, logComponent, logSubCat, 'Share rejected: ' + shareData);
|
||||||
|
}
|
||||||
|
|
||||||
handlers.share(isValidShare, isValidBlock, data)
|
// handle the share
|
||||||
|
handlers.share(isValidShare, isValidBlock, data);
|
||||||
|
|
||||||
|
// send to master for pplnt time tracking
|
||||||
|
process.send({type: 'shareTrack', thread:(parseInt(forkId)+1), coin:poolOptions.coin.name, isValidShare:isValidShare, isValidBlock:isValidBlock, data:data});
|
||||||
|
|
||||||
}).on('difficultyUpdate', function(workerName, diff){
|
}).on('difficultyUpdate', function(workerName, diff){
|
||||||
logger.debug(logSystem, logComponent, logSubCat, 'Difficulty update to diff ' + diff + ' workerName=' + JSON.stringify(workerName));
|
logger.debug(logSystem, logComponent, logSubCat, 'Difficulty update to diff ' + diff + ' workerName=' + JSON.stringify(workerName));
|
||||||
|
|
|
@ -27,7 +27,9 @@ module.exports = function(logger, poolConfig){
|
||||||
var logSubCat = 'Thread ' + (parseInt(forkId) + 1);
|
var logSubCat = 'Thread ' + (parseInt(forkId) + 1);
|
||||||
|
|
||||||
var connection = redis.createClient(redisConfig.port, redisConfig.host);
|
var connection = redis.createClient(redisConfig.port, redisConfig.host);
|
||||||
|
if (redisConfig.password) {
|
||||||
|
connection.auth(redisConfig.password);
|
||||||
|
}
|
||||||
connection.on('ready', function(){
|
connection.on('ready', function(){
|
||||||
logger.debug(logSystem, logComponent, logSubCat, 'Share processing setup with redis (' + redisConfig.host +
|
logger.debug(logSystem, logComponent, logSubCat, 'Share processing setup with redis (' + redisConfig.host +
|
||||||
':' + redisConfig.port + ')');
|
':' + redisConfig.port + ')');
|
||||||
|
@ -38,7 +40,6 @@ module.exports = function(logger, poolConfig){
|
||||||
connection.on('end', function(){
|
connection.on('end', function(){
|
||||||
logger.error(logSystem, logComponent, logSubCat, 'Connection to redis database has been ended');
|
logger.error(logSystem, logComponent, logSubCat, 'Connection to redis database has been ended');
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.info(function(error, response){
|
connection.info(function(error, response){
|
||||||
if (error){
|
if (error){
|
||||||
logger.error(logSystem, logComponent, logSubCat, 'Redis version check failed');
|
logger.error(logSystem, logComponent, logSubCat, 'Redis version check failed');
|
||||||
|
@ -65,18 +66,17 @@ module.exports = function(logger, poolConfig){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.handleShare = function(isValidShare, isValidBlock, shareData) {
|
||||||
this.handleShare = function(isValidShare, isValidBlock, shareData){
|
|
||||||
|
|
||||||
var redisCommands = [];
|
var redisCommands = [];
|
||||||
|
|
||||||
if (isValidShare){
|
if (isValidShare) {
|
||||||
redisCommands.push(['hincrbyfloat', coin + ':shares:roundCurrent', shareData.worker, shareData.difficulty]);
|
redisCommands.push(['hincrbyfloat', coin + ':shares:roundCurrent', shareData.worker, shareData.difficulty]);
|
||||||
redisCommands.push(['hincrby', coin + ':stats', 'validShares', 1]);
|
redisCommands.push(['hincrby', coin + ':stats', 'validShares', 1]);
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
redisCommands.push(['hincrby', coin + ':stats', 'invalidShares', 1]);
|
redisCommands.push(['hincrby', coin + ':stats', 'invalidShares', 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stores share diff, worker, and unique value with a score that is the timestamp. Unique value ensures it
|
/* Stores share diff, worker, and unique value with a score that is the timestamp. Unique value ensures it
|
||||||
doesn't overwrite an existing entry, and timestamp as score lets us query shares from last X minutes to
|
doesn't overwrite an existing entry, and timestamp as score lets us query shares from last X minutes to
|
||||||
generate hashrate for each worker and pool. */
|
generate hashrate for each worker and pool. */
|
||||||
|
@ -86,6 +86,7 @@ module.exports = function(logger, poolConfig){
|
||||||
|
|
||||||
if (isValidBlock){
|
if (isValidBlock){
|
||||||
redisCommands.push(['rename', coin + ':shares:roundCurrent', coin + ':shares:round' + shareData.height]);
|
redisCommands.push(['rename', coin + ':shares:roundCurrent', coin + ':shares:round' + shareData.height]);
|
||||||
|
redisCommands.push(['rename', coin + ':shares:timesCurrent', coin + ':shares:times' + shareData.height]);
|
||||||
redisCommands.push(['sadd', coin + ':blocksPending', [shareData.blockHash, shareData.txHash, shareData.height, shareData.worker, dateNow].join(':')]);
|
redisCommands.push(['sadd', coin + ':blocksPending', [shareData.blockHash, shareData.txHash, shareData.height, shareData.worker, dateNow].join(':')]);
|
||||||
redisCommands.push(['hincrby', coin + ':stats', 'validBlocks', 1]);
|
redisCommands.push(['hincrby', coin + ':stats', 'validBlocks', 1]);
|
||||||
}
|
}
|
||||||
|
@ -97,8 +98,6 @@ module.exports = function(logger, poolConfig){
|
||||||
if (err)
|
if (err)
|
||||||
logger.error(logSystem, logComponent, logSubCat, 'Error with share processor multi ' + JSON.stringify(err));
|
logger.error(logSystem, logComponent, logSubCat, 'Error with share processor multi ' + JSON.stringify(err));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
201
libs/stats.js
201
libs/stats.js
|
@ -7,10 +7,16 @@ var os = require('os');
|
||||||
|
|
||||||
var algos = require('stratum-pool/lib/algoProperties.js');
|
var algos = require('stratum-pool/lib/algoProperties.js');
|
||||||
|
|
||||||
function balanceRound(number) {
|
// redis callback Ready check failed bypass trick
|
||||||
return parseFloat((Math.round(number * 100000000) / 100000000).toFixed(8));
|
function rediscreateClient(port, host, pass) {
|
||||||
|
var client = redis.createClient(port, host);
|
||||||
|
if (pass) {
|
||||||
|
client.auth(pass);
|
||||||
|
}
|
||||||
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort object properties (only own properties will be sorted).
|
* Sort object properties (only own properties will be sorted).
|
||||||
* @param {object} obj object to sort properties
|
* @param {object} obj object to sort properties
|
||||||
|
@ -80,17 +86,35 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
}
|
}
|
||||||
redisClients.push({
|
redisClients.push({
|
||||||
coins: [coin],
|
coins: [coin],
|
||||||
client: redis.createClient(redisConfig.port, redisConfig.host)
|
client: rediscreateClient(redisConfig.port, redisConfig.host, redisConfig.password)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupStatsRedis(){
|
function setupStatsRedis(){
|
||||||
redisStats = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
|
redisStats = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
|
||||||
redisStats.on('error', function(err){
|
redisStats.on('error', function(err){
|
||||||
logger.error(logSystem, 'Historics', 'Redis for stats had an error ' + JSON.stringify(err));
|
redisStats.auth(portalConfig.redis.password);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.getBlocks = function (cback) {
|
||||||
|
var allBlocks = {};
|
||||||
|
async.each(_this.stats.pools, function(pool, pcb) {
|
||||||
|
|
||||||
|
if (_this.stats.pools[pool.name].pending && _this.stats.pools[pool.name].pending.blocks)
|
||||||
|
for (var i=0; i<_this.stats.pools[pool.name].pending.blocks.length; i++)
|
||||||
|
allBlocks[pool.name+"-"+_this.stats.pools[pool.name].pending.blocks[i].split(':')[2]] = _this.stats.pools[pool.name].pending.blocks[i];
|
||||||
|
|
||||||
|
if (_this.stats.pools[pool.name].confirmed && _this.stats.pools[pool.name].confirmed.blocks)
|
||||||
|
for (var i=0; i<_this.stats.pools[pool.name].confirmed.blocks.length; i++)
|
||||||
|
allBlocks[pool.name+"-"+_this.stats.pools[pool.name].confirmed.blocks[i].split(':')[2]] = _this.stats.pools[pool.name].confirmed.blocks[i];
|
||||||
|
|
||||||
|
pcb();
|
||||||
|
}, function(err) {
|
||||||
|
cback(allBlocks);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function gatherStatHistory(){
|
function gatherStatHistory(){
|
||||||
var retentionTime = (((Date.now() / 1000) - portalConfig.website.stats.historicalRetention) | 0).toString();
|
var retentionTime = (((Date.now() / 1000) - portalConfig.website.stats.historicalRetention) | 0).toString();
|
||||||
redisStats.zrangebyscore(['statHistory', retentionTime, '+inf'], function(err, replies){
|
redisStats.zrangebyscore(['statHistory', retentionTime, '+inf'], function(err, replies){
|
||||||
|
@ -153,6 +177,45 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
_this.statPoolHistory.push(data);
|
_this.statPoolHistory.push(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var magnitude = 100000000;
|
||||||
|
var coinPrecision = magnitude.toString().length - 1;
|
||||||
|
|
||||||
|
function roundTo(n, digits) {
|
||||||
|
if (digits === undefined) {
|
||||||
|
digits = 0;
|
||||||
|
}
|
||||||
|
var multiplicator = Math.pow(10, digits);
|
||||||
|
n = parseFloat((n * multiplicator).toFixed(11));
|
||||||
|
var test =(Math.round(n) / multiplicator);
|
||||||
|
return +(test.toFixed(digits));
|
||||||
|
}
|
||||||
|
|
||||||
|
var satoshisToCoins = function(satoshis){
|
||||||
|
return roundTo((satoshis / magnitude), coinPrecision);
|
||||||
|
};
|
||||||
|
|
||||||
|
var coinsToSatoshies = function(coins){
|
||||||
|
return Math.round(coins * magnitude);
|
||||||
|
};
|
||||||
|
|
||||||
|
function coinsRound(number) {
|
||||||
|
return roundTo(number, coinPrecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readableSeconds(t) {
|
||||||
|
var seconds = Math.round(t);
|
||||||
|
var minutes = Math.floor(seconds/60);
|
||||||
|
var hours = Math.floor(minutes/60);
|
||||||
|
var days = Math.floor(hours/24);
|
||||||
|
hours = hours-(days*24);
|
||||||
|
minutes = minutes-(days*24*60)-(hours*60);
|
||||||
|
seconds = seconds-(days*24*60*60)-(hours*60*60)-(minutes*60);
|
||||||
|
if (days > 0) { return (days + "d " + hours + "h " + minutes + "m " + seconds + "s"); }
|
||||||
|
if (hours > 0) { return (hours + "h " + minutes + "m " + seconds + "s"); }
|
||||||
|
if (minutes > 0) {return (minutes + "m " + seconds + "s"); }
|
||||||
|
return (seconds + "s");
|
||||||
|
}
|
||||||
|
|
||||||
this.getCoins = function(cback){
|
this.getCoins = function(cback){
|
||||||
_this.stats.coins = redisClients[0].coins;
|
_this.stats.coins = redisClients[0].coins;
|
||||||
cback();
|
cback();
|
||||||
|
@ -166,7 +229,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
], function(err, total){
|
], function(err, total){
|
||||||
cback(balanceRound(total).toFixed(8));
|
cback(coinsRound(total).toFixed(8));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,7 +244,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
async.each(_this.stats.pools, function(pool, pcb) {
|
async.each(_this.stats.pools, function(pool, pcb) {
|
||||||
pindex++;
|
pindex++;
|
||||||
var coin = String(_this.stats.pools[pool.name].name);
|
var coin = String(_this.stats.pools[pool.name].name);
|
||||||
client.hscan(coin + ':shares:roundCurrent', 0, "match", a+"*", function(error, result) {
|
client.hscan(coin + ':shares:roundCurrent', 0, "match", a+"*", "count", 1000, function(error, result) {
|
||||||
if (error) {
|
if (error) {
|
||||||
pcb(error);
|
pcb(error);
|
||||||
return;
|
return;
|
||||||
|
@ -222,46 +285,68 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
|
|
||||||
var totalHeld = parseFloat(0);
|
var totalHeld = parseFloat(0);
|
||||||
var totalPaid = parseFloat(0);
|
var totalPaid = parseFloat(0);
|
||||||
|
var totalImmature = parseFloat(0);
|
||||||
|
|
||||||
async.each(_this.stats.pools, function(pool, pcb) {
|
async.each(_this.stats.pools, function(pool, pcb) {
|
||||||
var coin = String(_this.stats.pools[pool.name].name);
|
var coin = String(_this.stats.pools[pool.name].name);
|
||||||
|
// get all immature balances from address
|
||||||
|
client.hscan(coin + ':immature', 0, "match", a+"*", "count", 10000, function(error, pends) {
|
||||||
// get all balances from address
|
// get all balances from address
|
||||||
client.hscan(coin + ':balances', 0, "match", a+"*", function(error, bals) {
|
client.hscan(coin + ':balances', 0, "match", a+"*", "count", 10000, function(error, bals) {
|
||||||
// get all payouts from address
|
// get all payouts from address
|
||||||
client.hscan(coin + ':payouts', 0, "match", a+"*", function(error, pays) {
|
client.hscan(coin + ':payouts', 0, "match", a+"*", "count", 10000, function(error, pays) {
|
||||||
|
|
||||||
var addressFound = false;
|
|
||||||
var workerName = "";
|
var workerName = "";
|
||||||
var balName = "";
|
|
||||||
var balAmount = 0;
|
var balAmount = 0;
|
||||||
var paidAmount = 0;
|
var paidAmount = 0;
|
||||||
|
var pendingAmount = 0;
|
||||||
|
|
||||||
|
var workers = {};
|
||||||
|
|
||||||
for (var i in pays[1]) {
|
for (var i in pays[1]) {
|
||||||
if (Math.abs(i % 2) != 1) {
|
if (Math.abs(i % 2) != 1) {
|
||||||
workerName = String(pays[1][i]);
|
workerName = String(pays[1][i]);
|
||||||
|
workers[workerName] = (workers[workerName] || {});
|
||||||
} else {
|
} else {
|
||||||
balAmount = 0;
|
paidAmount = parseFloat(pays[1][i]);
|
||||||
|
workers[workerName].paid = coinsRound(paidAmount);
|
||||||
|
totalPaid += paidAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (var b in bals[1]) {
|
for (var b in bals[1]) {
|
||||||
if (Math.abs(b % 2) != 1) {
|
if (Math.abs(b % 2) != 1) {
|
||||||
balName = String(bals[1][b]);
|
workerName = String(bals[1][b]);
|
||||||
} else if (balName == workerName) {
|
workers[workerName] = (workers[workerName] || {});
|
||||||
|
} else {
|
||||||
balAmount = parseFloat(bals[1][b]);
|
balAmount = parseFloat(bals[1][b]);
|
||||||
totalHeld = balanceRound(totalHeld+balAmount);
|
workers[workerName].balance = coinsRound(balAmount);
|
||||||
|
totalHeld += balAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paidAmount = parseFloat(pays[1][i]);
|
for (var b in pends[1]) {
|
||||||
totalPaid = balanceRound(totalPaid+paidAmount);
|
if (Math.abs(b % 2) != 1) {
|
||||||
balances.push({
|
workerName = String(pends[1][b]);
|
||||||
worker:String(workerName),
|
workers[workerName] = (workers[workerName] || {});
|
||||||
balance:balanceRound(balAmount),
|
} else {
|
||||||
paid:balanceRound(paidAmount)
|
pendingAmount = parseFloat(pends[1][b]);
|
||||||
});
|
workers[workerName].immature = coinsRound(pendingAmount);
|
||||||
|
totalImmature += pendingAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var w in workers) {
|
||||||
|
balances.push({
|
||||||
|
worker:String(w),
|
||||||
|
balance:workers[w].balance,
|
||||||
|
paid:workers[w].paid,
|
||||||
|
immature:workers[w].immature
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pcb();
|
pcb();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback("There was an error getting balances");
|
callback("There was an error getting balances");
|
||||||
|
@ -271,7 +356,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
_this.stats.balances = balances;
|
_this.stats.balances = balances;
|
||||||
_this.stats.address = address;
|
_this.stats.address = address;
|
||||||
|
|
||||||
cback({totalHeld:totalHeld, totalPaid:totalPaid, balances});
|
cback({totalHeld:coinsRound(totalHeld), totalPaid:coinsRound(totalPaid), totalImmature:satoshisToCoins(totalImmature), balances});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -296,7 +381,8 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
['smembers', ':blocksConfirmed'],
|
['smembers', ':blocksConfirmed'],
|
||||||
['hgetall', ':shares:roundCurrent'],
|
['hgetall', ':shares:roundCurrent'],
|
||||||
['hgetall', ':blocksPendingConfirms'],
|
['hgetall', ':blocksPendingConfirms'],
|
||||||
['zrange', ':payments', -100, -1]
|
['zrange', ':payments', -100, -1],
|
||||||
|
['hgetall', ':shares:timesCurrent']
|
||||||
];
|
];
|
||||||
|
|
||||||
var commandsPerCoin = redisCommandTemplates.length;
|
var commandsPerCoin = redisCommandTemplates.length;
|
||||||
|
@ -317,6 +403,12 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
else{
|
else{
|
||||||
for(var i = 0; i < replies.length; i += commandsPerCoin){
|
for(var i = 0; i < replies.length; i += commandsPerCoin){
|
||||||
var coinName = client.coins[i / commandsPerCoin | 0];
|
var coinName = client.coins[i / commandsPerCoin | 0];
|
||||||
|
var marketStats = {};
|
||||||
|
if (replies[i + 2]) {
|
||||||
|
if (replies[i + 2].coinmarketcap) {
|
||||||
|
marketStats = replies[i + 2] ? (JSON.parse(replies[i + 2].coinmarketcap)[0] || 0) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
var coinStats = {
|
var coinStats = {
|
||||||
name: coinName,
|
name: coinName,
|
||||||
symbol: poolConfigs[coinName].coin.symbol.toUpperCase(),
|
symbol: poolConfigs[coinName].coin.symbol.toUpperCase(),
|
||||||
|
@ -335,6 +427,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
networkVersion: replies[i + 2] ? (replies[i + 2].networkSubVersion || 0) : 0,
|
networkVersion: replies[i + 2] ? (replies[i + 2].networkSubVersion || 0) : 0,
|
||||||
networkProtocolVersion: replies[i + 2] ? (replies[i + 2].networkProtocolVersion || 0) : 0
|
networkProtocolVersion: replies[i + 2] ? (replies[i + 2].networkProtocolVersion || 0) : 0
|
||||||
},
|
},
|
||||||
|
marketStats: marketStats,
|
||||||
/* block stat counts */
|
/* block stat counts */
|
||||||
blocks: {
|
blocks: {
|
||||||
pending: replies[i + 3],
|
pending: replies[i + 3],
|
||||||
|
@ -344,14 +437,17 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
/* show all pending blocks */
|
/* show all pending blocks */
|
||||||
pending: {
|
pending: {
|
||||||
blocks: replies[i + 6].sort(sortBlocks),
|
blocks: replies[i + 6].sort(sortBlocks),
|
||||||
confirms: replies[i + 9]
|
confirms: (replies[i + 9] || {})
|
||||||
},
|
},
|
||||||
/* show last 5 found blocks */
|
/* show last 50 found blocks */
|
||||||
confirmed: {
|
confirmed: {
|
||||||
blocks: replies[i + 7].sort(sortBlocks).slice(0,5)
|
blocks: replies[i + 7].sort(sortBlocks).slice(0,50)
|
||||||
},
|
},
|
||||||
payments: [],
|
payments: [],
|
||||||
currentRoundShares: replies[i + 8]
|
currentRoundShares: (replies[i + 8] || {}),
|
||||||
|
currentRoundTimes: (replies[i + 11] || {}),
|
||||||
|
maxRoundTime: 0,
|
||||||
|
shareCount: 0
|
||||||
};
|
};
|
||||||
for(var j = replies[i + 10].length; j > 0; j--){
|
for(var j = replies[i + 10].length; j > 0; j--){
|
||||||
var jsonObj;
|
var jsonObj;
|
||||||
|
@ -364,17 +460,10 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
coinStats.payments.push(jsonObj);
|
coinStats.payments.push(jsonObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
for (var b in coinStats.confirmed.blocks) {
|
|
||||||
var parms = coinStats.confirmed.blocks[b].split(':');
|
|
||||||
if (parms[4] != null && parms[4] > 0) {
|
|
||||||
console.log(fancyTimestamp(parseInt(parms[4]), true));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
allCoinStats[coinStats.name] = (coinStats);
|
allCoinStats[coinStats.name] = (coinStats);
|
||||||
}
|
}
|
||||||
|
// sort pools alphabetically
|
||||||
|
allCoinStats = sortPoolsByName(allCoinStats);
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -419,6 +508,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
shares: workerShares,
|
shares: workerShares,
|
||||||
invalidshares: 0,
|
invalidshares: 0,
|
||||||
currRoundShares: 0,
|
currRoundShares: 0,
|
||||||
|
currRoundTime: 0,
|
||||||
hashrate: null,
|
hashrate: null,
|
||||||
hashrateString: null,
|
hashrateString: null,
|
||||||
luckDays: null,
|
luckDays: null,
|
||||||
|
@ -436,6 +526,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
shares: workerShares,
|
shares: workerShares,
|
||||||
invalidshares: 0,
|
invalidshares: 0,
|
||||||
currRoundShares: 0,
|
currRoundShares: 0,
|
||||||
|
currRoundTime: 0,
|
||||||
hashrate: null,
|
hashrate: null,
|
||||||
hashrateString: null,
|
hashrateString: null,
|
||||||
luckDays: null,
|
luckDays: null,
|
||||||
|
@ -455,6 +546,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
shares: 0,
|
shares: 0,
|
||||||
invalidshares: -workerShares,
|
invalidshares: -workerShares,
|
||||||
currRoundShares: 0,
|
currRoundShares: 0,
|
||||||
|
currRoundTime: 0,
|
||||||
hashrate: null,
|
hashrate: null,
|
||||||
hashrateString: null,
|
hashrateString: null,
|
||||||
luckDays: null,
|
luckDays: null,
|
||||||
|
@ -472,6 +564,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
shares: 0,
|
shares: 0,
|
||||||
invalidshares: -workerShares,
|
invalidshares: -workerShares,
|
||||||
currRoundShares: 0,
|
currRoundShares: 0,
|
||||||
|
currRoundTime: 0,
|
||||||
hashrate: null,
|
hashrate: null,
|
||||||
hashrateString: null,
|
hashrateString: null,
|
||||||
luckDays: null,
|
luckDays: null,
|
||||||
|
@ -510,6 +603,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
portalStats.algos[algo].workers += Object.keys(coinStats.workers).length;
|
portalStats.algos[algo].workers += Object.keys(coinStats.workers).length;
|
||||||
|
|
||||||
var _shareTotal = parseFloat(0);
|
var _shareTotal = parseFloat(0);
|
||||||
|
var _maxTimeShare = parseFloat(0);
|
||||||
for (var worker in coinStats.currentRoundShares) {
|
for (var worker in coinStats.currentRoundShares) {
|
||||||
var miner = worker.split(".")[0];
|
var miner = worker.split(".")[0];
|
||||||
if (miner in coinStats.miners) {
|
if (miner in coinStats.miners) {
|
||||||
|
@ -520,7 +614,18 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
}
|
}
|
||||||
_shareTotal += parseFloat(coinStats.currentRoundShares[worker]);
|
_shareTotal += parseFloat(coinStats.currentRoundShares[worker]);
|
||||||
}
|
}
|
||||||
|
for (var worker in coinStats.currentRoundTimes) {
|
||||||
|
var time = parseFloat(coinStats.currentRoundTimes[worker]);
|
||||||
|
if (_maxTimeShare < time) { _maxTimeShare = time; }
|
||||||
|
var miner = worker.split(".")[0]; // split poolId from minerAddress
|
||||||
|
if (miner in coinStats.miners && coinStats.miners[miner].currRoundTime < time) {
|
||||||
|
coinStats.miners[miner].currRoundTime = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
coinStats.shareCount = _shareTotal;
|
coinStats.shareCount = _shareTotal;
|
||||||
|
coinStats.maxRoundTime = _maxTimeShare;
|
||||||
|
coinStats.maxRoundTimeString = readableSeconds(_maxTimeShare);
|
||||||
|
|
||||||
for (var worker in coinStats.workers) {
|
for (var worker in coinStats.workers) {
|
||||||
var _workerRate = shareMultiplier * coinStats.workers[worker].shares / portalConfig.website.stats.hashrateWindow;
|
var _workerRate = shareMultiplier * coinStats.workers[worker].shares / portalConfig.website.stats.hashrateWindow;
|
||||||
|
@ -529,6 +634,10 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
coinStats.workers[worker].luckHours = ((_networkHashRate / _wHashRate * _blocktime) / (60 * 60)).toFixed(3);
|
coinStats.workers[worker].luckHours = ((_networkHashRate / _wHashRate * _blocktime) / (60 * 60)).toFixed(3);
|
||||||
coinStats.workers[worker].hashrate = _workerRate;
|
coinStats.workers[worker].hashrate = _workerRate;
|
||||||
coinStats.workers[worker].hashrateString = _this.getReadableHashRateString(_workerRate);
|
coinStats.workers[worker].hashrateString = _this.getReadableHashRateString(_workerRate);
|
||||||
|
var miner = worker.split('.')[0];
|
||||||
|
if (miner in coinStats.miners) {
|
||||||
|
coinStats.workers[worker].currRoundTime = coinStats.miners[miner].currRoundTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (var miner in coinStats.miners) {
|
for (var miner in coinStats.miners) {
|
||||||
var _workerRate = shareMultiplier * coinStats.miners[miner].shares / portalConfig.website.stats.hashrateWindow;
|
var _workerRate = shareMultiplier * coinStats.miners[miner].shares / portalConfig.website.stats.hashrateWindow;
|
||||||
|
@ -559,6 +668,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
delete saveStats.pools[pool].pending;
|
delete saveStats.pools[pool].pending;
|
||||||
delete saveStats.pools[pool].confirmed;
|
delete saveStats.pools[pool].confirmed;
|
||||||
delete saveStats.pools[pool].currentRoundShares;
|
delete saveStats.pools[pool].currentRoundShares;
|
||||||
|
delete saveStats.pools[pool].currentRoundTimes;
|
||||||
delete saveStats.pools[pool].payments;
|
delete saveStats.pools[pool].payments;
|
||||||
delete saveStats.pools[pool].miners;
|
delete saveStats.pools[pool].miners;
|
||||||
});
|
});
|
||||||
|
@ -591,11 +701,22 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function sortPoolsByName(objects) {
|
||||||
|
var newObject = {};
|
||||||
|
var sortedArray = sortProperties(objects, 'name', false, false);
|
||||||
|
for (var i = 0; i < sortedArray.length; i++) {
|
||||||
|
var key = sortedArray[i][0];
|
||||||
|
var value = sortedArray[i][1];
|
||||||
|
newObject[key] = value;
|
||||||
|
}
|
||||||
|
return newObject;
|
||||||
|
}
|
||||||
|
|
||||||
function sortBlocks(a, b) {
|
function sortBlocks(a, b) {
|
||||||
var as = a.split(":");
|
var as = parseInt(a.split(":")[2]);
|
||||||
var bs = b.split(":");
|
var bs = parseInt(b.split(":")[2]);
|
||||||
if (as[2] > bs[2]) return -1;
|
if (as > bs) return -1;
|
||||||
if (as[2] < bs[2]) return 1;
|
if (as < bs) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
var https = require('https');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
|
@ -42,7 +42,8 @@ module.exports = function(logger){
|
||||||
'api.html': 'api',
|
'api.html': 'api',
|
||||||
'admin.html': 'admin',
|
'admin.html': 'admin',
|
||||||
'mining_key.html': 'mining_key',
|
'mining_key.html': 'mining_key',
|
||||||
'miner_stats.html': 'miner_stats'
|
'miner_stats.html': 'miner_stats',
|
||||||
|
'payments.html': 'payments'
|
||||||
};
|
};
|
||||||
|
|
||||||
var pageTemplates = {};
|
var pageTemplates = {};
|
||||||
|
@ -53,7 +54,6 @@ module.exports = function(logger){
|
||||||
var keyScriptTemplate = '';
|
var keyScriptTemplate = '';
|
||||||
var keyScriptProcessed = '';
|
var keyScriptProcessed = '';
|
||||||
|
|
||||||
|
|
||||||
var processTemplates = function(){
|
var processTemplates = function(){
|
||||||
|
|
||||||
for (var pageName in pageTemplates){
|
for (var pageName in pageTemplates){
|
||||||
|
@ -95,13 +95,19 @@ module.exports = function(logger){
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//If an html file was changed reload it
|
// if an html file was changed reload it
|
||||||
watch('website', function(filename){
|
/* requires node-watch 0.5.0 or newer */
|
||||||
var basename = path.basename(filename);
|
watch(['./website', './website/pages'], function(evt, filename){
|
||||||
|
var basename;
|
||||||
|
// support older versions of node-watch automatically
|
||||||
|
if (!filename && evt)
|
||||||
|
basename = path.basename(evt);
|
||||||
|
else
|
||||||
|
basename = path.basename(filename);
|
||||||
|
|
||||||
if (basename in pageFiles){
|
if (basename in pageFiles){
|
||||||
console.log(filename);
|
|
||||||
readPageFiles([basename]);
|
readPageFiles([basename]);
|
||||||
logger.debug(logSystem, 'Server', 'Reloaded file ' + basename);
|
logger.special(logSystem, 'Server', 'Reloaded file ' + basename);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -128,6 +134,9 @@ module.exports = function(logger){
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(callback){
|
function(callback){
|
||||||
var client = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
|
var client = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
|
||||||
|
if (portalConfig.redis.password) {
|
||||||
|
client.auth(portalConfig.redis.password);
|
||||||
|
}
|
||||||
client.hgetall('coinVersionBytes', function(err, coinBytes){
|
client.hgetall('coinVersionBytes', function(err, coinBytes){
|
||||||
if (err){
|
if (err){
|
||||||
client.quit();
|
client.quit();
|
||||||
|
@ -221,6 +230,7 @@ module.exports = function(logger){
|
||||||
address = address.split(".")[0];
|
address = address.split(".")[0];
|
||||||
portalStats.getBalanceByAddress(address, function(){
|
portalStats.getBalanceByAddress(address, function(){
|
||||||
processTemplates();
|
processTemplates();
|
||||||
|
res.header('Content-Type', 'text/html');
|
||||||
res.end(indexesProcessed['miner_stats']);
|
res.end(indexesProcessed['miner_stats']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -294,8 +304,8 @@ module.exports = function(logger){
|
||||||
//app.get('/stats/shares/:coin', usershares);
|
//app.get('/stats/shares/:coin', usershares);
|
||||||
//app.get('/stats/shares', shares);
|
//app.get('/stats/shares', shares);
|
||||||
//app.get('/payout/:address', payout);
|
//app.get('/payout/:address', payout);
|
||||||
|
app.use(compress());
|
||||||
app.get('/workers/:address', minerpage);
|
app.get('/workers/:address', minerpage);
|
||||||
|
|
||||||
app.get('/:page', route);
|
app.get('/:page', route);
|
||||||
app.get('/', route);
|
app.get('/', route);
|
||||||
|
|
||||||
|
@ -327,11 +337,23 @@ module.exports = function(logger){
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (portalConfig.website.tlsOptions && portalConfig.website.tlsOptions.enabled === true) {
|
||||||
|
var TLSoptions = {
|
||||||
|
key: fs.readFileSync(portalConfig.website.tlsOptions.key),
|
||||||
|
cert: fs.readFileSync(portalConfig.website.tlsOptions.cert)
|
||||||
|
};
|
||||||
|
|
||||||
|
https.createServer(TLSoptions, app).listen(portalConfig.website.port, portalConfig.website.host, function() {
|
||||||
|
logger.debug(logSystem, 'Server', 'TLS Website started on ' + portalConfig.website.host + ':' + portalConfig.website.port);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
app.listen(portalConfig.website.port, portalConfig.website.host, function () {
|
app.listen(portalConfig.website.port, portalConfig.website.host, function () {
|
||||||
logger.debug(logSystem, 'Server', 'Website started on ' + portalConfig.website.host + ':' + portalConfig.website.port);
|
logger.debug(logSystem, 'Server', 'Website started on ' + portalConfig.website.host + ':' + portalConfig.website.port);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch(e){
|
catch(e){
|
||||||
|
console.log(e)
|
||||||
logger.error(logSystem, 'Server', 'Could not start website on ' + portalConfig.website.host + ':' + portalConfig.website.port
|
logger.error(logSystem, 'Server', 'Could not start website on ' + portalConfig.website.host + ':' + portalConfig.website.port
|
||||||
+ ' - its either in use or you do not have permission');
|
+ ' - its either in use or you do not have permission');
|
||||||
}
|
}
|
||||||
|
|
41
package.json
41
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "z-nomp",
|
"name": "z-nomp",
|
||||||
"version": "0.0.4",
|
"version": "0.0.4",
|
||||||
"description": "Zclassic (Equihash) stratum mining pool based on NOMP",
|
"description": "ZenCash (Equihash) stratum mining pool based on NOMP",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"stratum",
|
"stratum",
|
||||||
"mining",
|
"mining",
|
||||||
|
@ -12,9 +12,9 @@
|
||||||
"zcash",
|
"zcash",
|
||||||
"equihash"
|
"equihash"
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/z-classic/z-nomp",
|
"homepage": "https://github.com/zencashio/z-nomp",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/z-classic/z-nomp/issues"
|
"url": "https://github.com/zencashio/z-nomp/issues"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Joshua Yabut",
|
"author": "Joshua Yabut",
|
||||||
|
@ -31,25 +31,25 @@
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/z-classic/z-nomp.git"
|
"url": "https://github.com/zencashio/z-nomp.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"stratum-pool": "git+https://github.com/z-classic/node-stratum-pool.git",
|
"async": "2.3.0",
|
||||||
"dateformat": "*",
|
"bignum": "0.12.5",
|
||||||
"node-json-minify": "*",
|
"body-parser": "1.17.1",
|
||||||
"redis": "*",
|
"colors": "1.1.2",
|
||||||
"mysql": "*",
|
"compression": "1.6.2",
|
||||||
"async": "*",
|
"dateformat": "2.0.0",
|
||||||
"express": "*",
|
"dot": "1.1.1",
|
||||||
"body-parser": "*",
|
"express": "4.15.2",
|
||||||
"compression": "*",
|
"extend": "3.0.0",
|
||||||
"dot": "*",
|
"mysql": "2.13.0",
|
||||||
"colors": "*",
|
"node-json-minify": "1.0.0",
|
||||||
"node-watch": "*",
|
"node-watch": "0.5.2",
|
||||||
"request": "*",
|
"nonce": "1.0.4",
|
||||||
"nonce": "*",
|
"redis": "2.7.1",
|
||||||
"bignum": "*",
|
"request": "2.81.0",
|
||||||
"extend": "*"
|
"stratum-pool": "git+https://github.com/z-classic/node-stratum-pool.git"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
|
@ -58,4 +58,3 @@
|
||||||
"start": "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/node_modules/stratum-pool/node_modules/equihashverify/build/Release/:$PWD/node_modules/equihashverify/build/Release/ node init.js"
|
"start": "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/node_modules/stratum-pool/node_modules/equihashverify/build/Release/:$PWD/node_modules/equihashverify/build/Release/ node init.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,14 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"paymentProcessing": {
|
"paymentProcessing": {
|
||||||
|
"minConf": 10,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
"paymentMode": "prop",
|
||||||
|
"_comment_paymentMode":"prop, pplnt",
|
||||||
"paymentInterval": 57,
|
"paymentInterval": 57,
|
||||||
"_comment_paymentInterval": "Interval in seconds to check and perform payments.",
|
"_comment_paymentInterval": "Interval in seconds to check and perform payments.",
|
||||||
"minimumPayment": 0.1,
|
"minimumPayment": 0.1,
|
||||||
|
"maxBlocksPerPayment": 3,
|
||||||
"daemon": {
|
"daemon": {
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"port": 8232,
|
"port": 8232,
|
||||||
|
|
|
@ -19,9 +19,13 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"paymentProcessing": {
|
"paymentProcessing": {
|
||||||
|
"minConf": 10,
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
"paymentMode": "prop",
|
||||||
|
"_comment_paymentMode":"prop, pplnt",
|
||||||
"paymentInterval": 20,
|
"paymentInterval": 20,
|
||||||
"minimumPayment": 0.1,
|
"minimumPayment": 0.1,
|
||||||
|
"maxBlocksPerPayment": 3,
|
||||||
"daemon": {
|
"daemon": {
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"port": 19332,
|
"port": 19332,
|
||||||
|
|
|
@ -20,9 +20,13 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"paymentProcessing": {
|
"paymentProcessing": {
|
||||||
|
"minConf": 10,
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
"paymentMode": "prop",
|
||||||
|
"_comment_paymentMode":"prop, pplnt",
|
||||||
"paymentInterval": 20,
|
"paymentInterval": 20,
|
||||||
"minimumPayment": 0.1,
|
"minimumPayment": 0.1,
|
||||||
|
"maxBlocksPerPayment": 1,
|
||||||
"daemon": {
|
"daemon": {
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"port": 19332,
|
"port": 19332,
|
||||||
|
|
|
@ -24,9 +24,13 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"paymentProcessing": {
|
"paymentProcessing": {
|
||||||
|
"minConf": 10,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
"paymentMode": "prop",
|
||||||
|
"_comment_paymentMode":"prop, pplnt",
|
||||||
"paymentInterval": 20,
|
"paymentInterval": 20,
|
||||||
"minimumPayment": 0.1,
|
"minimumPayment": 0.1,
|
||||||
|
"maxBlocksPerPayment": 3,
|
||||||
"daemon": {
|
"daemon": {
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"port": 8232,
|
"port": 8232,
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
{
|
||||||
|
"enabled": false,
|
||||||
|
"coin": "zen.json",
|
||||||
|
|
||||||
|
"address": "znZfjUjJSbUBgMsEqKLXTGxKwUerWh5dDT4",
|
||||||
|
"_comment_address": "a transparent address to send coinbase rewards to and to transfer to zAddress.",
|
||||||
|
|
||||||
|
"zAddress": "zcDVvJbyyFLznbs7jJq974pZaHKsHnk8UXof1ief8HSfQMfG75CucWACCYAZN3vnprhUXCPCUD7vMGTzrFjTjy6nNriiDbe",
|
||||||
|
"_comment_zAddress": "a private address used to send coins to tAddress.",
|
||||||
|
|
||||||
|
"tAddress": "znV763BSvdEySe3SCaTgHNiFrawiYzxRysb",
|
||||||
|
"_comment_tAddress": "transparent address used to send payments, make this a different address, otherwise payments will not send",
|
||||||
|
|
||||||
|
"invalidAddress":"znhGeka9zXmixvw6ufzGpcaSXcSACrjx5WZ",
|
||||||
|
"_comment_invalidAddress": "Invalid addresses will be converted to the above",
|
||||||
|
|
||||||
|
"walletInterval": 2.5,
|
||||||
|
|
||||||
|
"rewardRecipients": {
|
||||||
|
},
|
||||||
|
|
||||||
|
"tlsOptions": {
|
||||||
|
"enabled": false,
|
||||||
|
"serverKey":"",
|
||||||
|
"serverCert":"",
|
||||||
|
"ca":""
|
||||||
|
},
|
||||||
|
|
||||||
|
"paymentProcessing": {
|
||||||
|
"enabled": false,
|
||||||
|
"paymentMode": "prop",
|
||||||
|
"_comment_paymentMode":"prop, pplnt",
|
||||||
|
"paymentInterval": 20,
|
||||||
|
"minimumPayment": 0.1,
|
||||||
|
"maxBlocksPerPayment": 3,
|
||||||
|
"daemon": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8231,
|
||||||
|
"user": "rpcuser",
|
||||||
|
"password": "rpcpassword"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ports": {
|
||||||
|
"3032": {
|
||||||
|
"tls":false,
|
||||||
|
"diff": 2.5,
|
||||||
|
"varDiff": {
|
||||||
|
"minDiff": 0.04,
|
||||||
|
"maxDiff": 16,
|
||||||
|
"targetTime": 15,
|
||||||
|
"retargetTime": 60,
|
||||||
|
"variancePercent": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"poolId": "main",
|
||||||
|
"_comment_poolId": "use it for region identification: eu, us, asia or keep default if you have one stratum instance for one coin",
|
||||||
|
|
||||||
|
"daemons": [
|
||||||
|
{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8231,
|
||||||
|
"user": "rpcuser",
|
||||||
|
"password": "rpcpassword"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"p2p": {
|
||||||
|
"enabled": false,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 19333,
|
||||||
|
"disableTransactions": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"mposMode": {
|
||||||
|
"enabled": false,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 3306,
|
||||||
|
"user": "me",
|
||||||
|
"password": "mypass",
|
||||||
|
"database": "zcl",
|
||||||
|
"checkPassword": true,
|
||||||
|
"autoCreateWorker": false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
{
|
||||||
|
"enabled": false,
|
||||||
|
"coin": "zen_testnet.json",
|
||||||
|
|
||||||
|
"address": "ztXoXdBdoQ4WDxp9mtHNRoqZ9N5pCH2JNWe",
|
||||||
|
"_comment_address": "a transparent address to send coinbase rewards to and to transfer to zAddress.",
|
||||||
|
|
||||||
|
"zAddress": "ztZLon4AswWDcftwdN5Q94ADoZWPcCBBBHaHE84VVDd2bG9o8f6xuMFJgGMdYb35rwLi9LyGLCH3VoDBHKddrNnmEKg5N8c",
|
||||||
|
"_comment_zAddress": "a private address used to send coins to tAddress.",
|
||||||
|
|
||||||
|
"tAddress": "ztj7WuC4ASGVw4r2mCUqgHhyQxzWpxYLbNg",
|
||||||
|
"_comment_tAddress": "transparent address used to send payments, make this a different address, otherwise payments will not send",
|
||||||
|
|
||||||
|
"walletInterval": 2.5,
|
||||||
|
|
||||||
|
"rewardRecipients": {
|
||||||
|
},
|
||||||
|
|
||||||
|
"tlsOptions": {
|
||||||
|
"enabled": false,
|
||||||
|
"serverKey":"",
|
||||||
|
"serverCert":"",
|
||||||
|
"ca":""
|
||||||
|
},
|
||||||
|
|
||||||
|
"paymentProcessing": {
|
||||||
|
"enabled": false,
|
||||||
|
"paymentMode": "prop",
|
||||||
|
"_comment_paymentMode":"prop, pplnt",
|
||||||
|
"paymentInterval": 20,
|
||||||
|
"minimumPayment": 0.1,
|
||||||
|
"maxBlocksPerPayment": 3,
|
||||||
|
"daemon": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8232,
|
||||||
|
"user": "testuser",
|
||||||
|
"password": "testpass"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ports": {
|
||||||
|
"3032": {
|
||||||
|
"tls":false,
|
||||||
|
"diff": 2.5,
|
||||||
|
"varDiff": {
|
||||||
|
"minDiff": 0.04,
|
||||||
|
"maxDiff": 16,
|
||||||
|
"targetTime": 15,
|
||||||
|
"retargetTime": 60,
|
||||||
|
"variancePercent": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"daemons": [
|
||||||
|
{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 18231,
|
||||||
|
"user": "rpcuser",
|
||||||
|
"password": "rpcpassword"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"p2p": {
|
||||||
|
"enabled": false,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 19333,
|
||||||
|
"disableTransactions": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"mposMode": {
|
||||||
|
"enabled": false,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 3306,
|
||||||
|
"user": "me",
|
||||||
|
"password": "mypass",
|
||||||
|
"database": "zcl",
|
||||||
|
"checkPassword": true,
|
||||||
|
"autoCreateWorker": false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/api/stats">/stats</a> global pool stats</li>
|
<li><a href="/api/stats">/stats</a> global pool stats</li>
|
||||||
|
<li><a href="/api/blocks">/stats</a> global block stats</li>
|
||||||
<li><a href="/api/pool_stats">/pool_stats</a> - historical stats</li>
|
<li><a href="/api/pool_stats">/pool_stats</a> - historical stats</li>
|
||||||
<li><a href="/api/payments">/payments</a> - payment history</li>
|
<li><a href="/api/payments">/payments</a> - payment history</li>
|
||||||
<li><a href="/api/worker_stats?taddr">/worker_stats?taddr</a> - historical time per pool json </li>
|
<li><a href="/api/worker_stats?taddr">/worker_stats?taddr</a> - historical time per pool json </li>
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
<div class="chartHolder"><svg id="workerHashrate" /></div>
|
<div class="chartHolder"><svg id="workerHashrate" /></div>
|
||||||
<div>
|
<div>
|
||||||
<div style="float:right; padding-top: 9px; padding-right: 18px;"><i class="fa fa-cog"></i> Shares: <span id="statsTotalShares">...</span></div>
|
<div style="float:right; padding-top: 9px; padding-right: 18px;"><i class="fa fa-cog"></i> Shares: <span id="statsTotalShares">...</span></div>
|
||||||
|
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Immature: <span id="statsTotalImmature">...</span> </div>
|
||||||
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Bal: <span id="statsTotalBal">...</span> </div>
|
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Bal: <span id="statsTotalBal">...</span> </div>
|
||||||
<div style="padding-top: 9px; padding-left: 18px;"><i class="fa fa-money"></i> Paid: <span id="statsTotalPaid">...</span> </div>
|
<div style="padding-top: 9px; padding-left: 18px;"><i class="fa fa-money"></i> Paid: <span id="statsTotalPaid">...</span> </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
<style>
|
||||||
|
#bottomNotes {
|
||||||
|
display: block;
|
||||||
|
padding-left: 18px;
|
||||||
|
padding-right: 18px;
|
||||||
|
padding-bottom: 18px;
|
||||||
|
}
|
||||||
|
#topPool {
|
||||||
|
padding-top: 18px;
|
||||||
|
padding-left: 18px;
|
||||||
|
padding-right: 18px;
|
||||||
|
}
|
||||||
|
#topPool > div > div > svg {
|
||||||
|
display: block;
|
||||||
|
height: 280px;
|
||||||
|
}
|
||||||
|
.poolWrapper {
|
||||||
|
border: solid 1px #c7c7c7;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
.poolLabel {
|
||||||
|
font-size: 1.2em;
|
||||||
|
text-align: center;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
.poolMinerTable {
|
||||||
|
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
$(document).tooltip({
|
||||||
|
content: function () {
|
||||||
|
return $(this).prop('title');
|
||||||
|
},
|
||||||
|
show: null,
|
||||||
|
close: function (event, ui) {
|
||||||
|
ui.tooltip.hover(
|
||||||
|
|
||||||
|
function () {
|
||||||
|
$(this).stop(true).fadeTo(400, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
function () {
|
||||||
|
$(this).fadeOut("400", function () {
|
||||||
|
$(this).remove();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{ function readableDate(a){ return new Date(parseInt(a)).toString(); } }}
|
||||||
|
{{ for(var pool in it.stats.pools) { }}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Blocks</th>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Miners</th>
|
||||||
|
<th>Shares</th>
|
||||||
|
<th>Amount</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{{ for(var p in it.stats.pools[pool].payments) { }}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{if (String(it.stats.pools[pool].name).startsWith("zcash")) { }}
|
||||||
|
<a href="https://explorer.zcha.in/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a>
|
||||||
|
{{ } else if (String(it.stats.pools[pool].name).startsWith("zclassic")) { }}
|
||||||
|
<a href="https://classic.zcha.in/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a>
|
||||||
|
{{ } else if (String(it.stats.pools[pool].name).startsWith("hush")) { }}
|
||||||
|
<a href="https://explorer.myhush.org/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a>
|
||||||
|
{{ } else if (String(it.stats.pools[pool].name).startsWith("zen")) { }}
|
||||||
|
<a href="http://node1.zenchain.info:8886/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a>
|
||||||
|
{{ } else { }}
|
||||||
|
{{=it.stats.pools[pool].payments[p].blocks}}
|
||||||
|
{{ } }}
|
||||||
|
</td>
|
||||||
|
<td>{{=readableDate(it.stats.pools[pool].payments[p].time)}}</td>
|
||||||
|
<td>{{=it.stats.pools[pool].payments[p].miners}}</td>
|
||||||
|
<td>{{=Math.round(it.stats.pools[pool].payments[p].shares)}}</td>
|
||||||
|
<td>{{=it.stats.pools[pool].payments[p].paid}} {{=it.stats.pools[pool].symbol}}</td>
|
||||||
|
</tr>
|
||||||
|
{{ } }}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{ } }}
|
|
@ -69,7 +69,24 @@
|
||||||
.boxStatsList > div > div{
|
.boxStatsList > div > div{
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.tooltip {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
width: 60px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 2px;
|
||||||
|
font: 12px sans-serif;
|
||||||
|
background: lightsteelblue;
|
||||||
|
border: 0px;
|
||||||
|
border-radius: 8px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
#tooltip.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="topCharts">
|
<div id="topCharts">
|
||||||
<div class="chartWrapper">
|
<div class="chartWrapper">
|
||||||
<div class="chartLabel">Pool Historical Hashrate</div>
|
<div class="chartLabel">Pool Historical Hashrate</div>
|
||||||
|
@ -109,9 +126,8 @@
|
||||||
</div>
|
</div>
|
||||||
{{ } }}
|
{{ } }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ for(var pool in it.stats.pools) { }}
|
{{ for(var pool in it.stats.pools) { }}
|
||||||
{{ var paidJackpots = parseFloat(it.stats.pools[pool].poolStats.validBlocks) * 1.0; }}
|
{{ var blockscomb = new Array; }}
|
||||||
<div class="pure-g-r" id="boxesLower">
|
<div class="pure-g-r" id="boxesLower">
|
||||||
<div class="pure-u-1-1">
|
<div class="pure-u-1-1">
|
||||||
<div class="boxStats" id="boxStatsRight">
|
<div class="boxStats" id="boxStatsRight">
|
||||||
|
@ -122,6 +138,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="boxStatsList" style="margin-top: 9px;">
|
<div class="boxStatsList" style="margin-top: 9px;">
|
||||||
<!--<div id="{{=it.stats.pools[pool].name}}NewBlocks"></div>-->
|
<!--<div id="{{=it.stats.pools[pool].name}}NewBlocks"></div>-->
|
||||||
|
|
||||||
{{ for(var b in it.stats.pools[pool].pending.blocks) { }}
|
{{ for(var b in it.stats.pools[pool].pending.blocks) { }}
|
||||||
{{ var block = it.stats.pools[pool].pending.blocks[b].split(":"); }}
|
{{ var block = it.stats.pools[pool].pending.blocks[b].split(":"); }}
|
||||||
<div style="margin-bottom: 9px; background-color: #eeeeee; min-width:600px;"><i class="fa fa-bars"></i>
|
<div style="margin-bottom: 9px; background-color: #eeeeee; min-width:600px;"><i class="fa fa-bars"></i>
|
||||||
|
@ -130,19 +147,26 @@
|
||||||
<a href="https://explorer.zcha.in/blocks/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
|
<a href="https://explorer.zcha.in/blocks/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
|
||||||
{{ } else if (String(it.stats.pools[pool].name).startsWith("zclassic")) { }}
|
{{ } else if (String(it.stats.pools[pool].name).startsWith("zclassic")) { }}
|
||||||
<a href="https://classic.zcha.in/blocks/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
|
<a href="https://classic.zcha.in/blocks/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
|
||||||
|
{{ } else if (String(it.stats.pools[pool].name).startsWith("hush")) { }}
|
||||||
|
<a href="https://explorer.myhush.org/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
|
||||||
{{ } else { }}
|
{{ } else { }}
|
||||||
{{=block[2]}}
|
{{=block[2]}}
|
||||||
{{ } }}
|
{{ } }}
|
||||||
{{if (block[4] != null) { }}
|
{{if (block[4] != null) { }}
|
||||||
<span style="padding-left: 18px;"><small>{{=readableDate(block[4])}}</small></span>
|
<span style="padding-left: 18px;"><small>{{=readableDate(block[4])}}</small></span>
|
||||||
{{ } }}
|
{{ } }}
|
||||||
|
{{if (it.stats.pools[pool].pending.confirms) { }}
|
||||||
{{if (it.stats.pools[pool].pending.confirms[block[0]]) { }}
|
{{if (it.stats.pools[pool].pending.confirms[block[0]]) { }}
|
||||||
<span style="float:right; color: red;"><small>{{=it.stats.pools[pool].pending.confirms[block[0]]}} of 100</small></span>
|
<span style="float:right; color: red;"><small>{{=it.stats.pools[pool].pending.confirms[block[0]]}} of 100</small></span>
|
||||||
{{ } else { }}
|
{{ } else { }}
|
||||||
<span style="float:right; color: red;"><small>*PENDING*</small></span>
|
<span style="float:right; color: red;"><small>*PENDING*</small></span>
|
||||||
{{ } }}
|
{{ } }}
|
||||||
|
{{ } else { }}
|
||||||
|
<span style="float:right; color: red;"><small>*PENDING*</small></span>
|
||||||
|
{{ } }}
|
||||||
<div><i class="fa fa-gavel"></i><small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3]}}</a></div>
|
<div><i class="fa fa-gavel"></i><small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3]}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
{{ blockscomb.push(block);}}
|
||||||
{{ } }}
|
{{ } }}
|
||||||
{{ var i=0; for(var b in it.stats.pools[pool].confirmed.blocks) { }}
|
{{ var i=0; for(var b in it.stats.pools[pool].confirmed.blocks) { }}
|
||||||
{{ if (i < 8) { i++; }}
|
{{ if (i < 8) { i++; }}
|
||||||
|
@ -162,19 +186,137 @@
|
||||||
<span style="float:right; padding-left: 18px; color: green;"><small>*PAID*</small></span>
|
<span style="float:right; padding-left: 18px; color: green;"><small>*PAID*</small></span>
|
||||||
<div><i class="fa fa-gavel"></i><small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3]}}</a></div>
|
<div><i class="fa fa-gavel"></i><small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3]}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
{{blockscomb.push(block);}}
|
||||||
{{ } }}
|
{{ } }}
|
||||||
{{ } }}
|
{{ } }}
|
||||||
|
<!--{{=JSON.stringify(blockscomb)}}-->
|
||||||
|
<script>
|
||||||
|
var blockscomb = ({{=JSON.stringify(blockscomb)}})
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<center><div id="bottomCharts{{=pool}}" style="text-align:center;" align="center">
|
||||||
|
<div class="chartWrapper" style="text-align:center;">
|
||||||
|
<div class="chartLabel">Finders of the last {{=blockscomb.length}} blocks</div>
|
||||||
|
<div class="hidden" id="tooltip{{=pool}}"><p><span id="value{{=pool}}"></span> blocks found by <span id="finderr{{=pool}}"></span></p></div>
|
||||||
|
<div class="chartHolder" id="pie{{=pool}}"><svg id="blocksPie{{=pool}}" style="display: block; margin: auto; text-align:center;"/></div>
|
||||||
|
</div>
|
||||||
|
</div></center>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var groupedByFinder = {};
|
||||||
|
var data = [];
|
||||||
|
|
||||||
|
for (var i=0; i < blockscomb.length; i++) {
|
||||||
|
finder=blockscomb[i][3]; // if other doesn 't already have a property for the current letter
|
||||||
|
// create it and assign it to a new empty array
|
||||||
|
if (!(finder in groupedByFinder))
|
||||||
|
groupedByFinder[finder] = [];
|
||||||
|
|
||||||
|
groupedByFinder[finder].push(blockscomb[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(groupedByFinder).forEach(function(i) {
|
||||||
|
var obj = {};
|
||||||
|
obj.label = i
|
||||||
|
obj.value = groupedByFinder[i].length
|
||||||
|
data.push(obj)
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(JSON.stringify(data))
|
||||||
|
|
||||||
|
var w = 400;
|
||||||
|
var h = 400;
|
||||||
|
var r = h/2;
|
||||||
|
var legendRectSize = 18;
|
||||||
|
var legendSpacing = 5;
|
||||||
|
|
||||||
|
var color = d3.scale.category20c();
|
||||||
|
var div = d3.select("#pie{{=pool}}").append("div")
|
||||||
|
.attr("class", "tooltip")
|
||||||
|
.style("opacity", 0);
|
||||||
|
|
||||||
|
var vis = d3.select('#blocksPie{{=pool}}')
|
||||||
|
.data([data])
|
||||||
|
.attr("width", 1000)
|
||||||
|
.attr("height", h)
|
||||||
|
.attr("style", "display: block; margin: auto;")
|
||||||
|
.attr("preserveAspectRatio", "xMidYMin")
|
||||||
|
.append("svg:g")
|
||||||
|
.attr("transform", "translate(" + r + "," + r + ")");
|
||||||
|
|
||||||
|
|
||||||
|
var pie = d3.layout.pie().value(function(d){return d.value;});
|
||||||
|
|
||||||
|
// declare an arc generator function
|
||||||
|
var arc = d3.svg.arc().outerRadius(r);
|
||||||
|
|
||||||
|
// select paths, use arc generator to draw
|
||||||
|
var arcs = vis.selectAll("g.slice{{=pool}}")
|
||||||
|
.data(pie)
|
||||||
|
.enter()
|
||||||
|
.append("svg:g")
|
||||||
|
.attr("class", "slice{{=pool}}")
|
||||||
|
.attr("id", "slice")
|
||||||
|
.on("mouseover", function(d){
|
||||||
|
d3.select("#tooltip{{=pool}}")
|
||||||
|
.style("left", d3.event.pageX + "px")
|
||||||
|
.style("top", d3.event.pageY + "px")
|
||||||
|
.style("opacity", 1)
|
||||||
|
.select("#value{{=pool}}")
|
||||||
|
.text(d.data.value);
|
||||||
|
d3.select("#tooltip{{=pool}}")
|
||||||
|
.select("#finderr{{=pool}}")
|
||||||
|
.text(d.data.label);
|
||||||
|
});
|
||||||
|
|
||||||
|
arcs.append("svg:path")
|
||||||
|
.attr("fill", function(d, i){
|
||||||
|
return color(i);
|
||||||
|
})
|
||||||
|
.attr("d", function (d) {
|
||||||
|
return arc(d);
|
||||||
|
});
|
||||||
|
|
||||||
|
var legend = vis.selectAll('.legend')
|
||||||
|
.data(color.domain())
|
||||||
|
.enter()
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'legend')
|
||||||
|
.attr('id', {{=JSON.stringify(pool)}})
|
||||||
|
.attr('transform', function(d, i) {
|
||||||
|
var height = legendRectSize + legendSpacing;
|
||||||
|
var offset = height * color.domain().length / 2;
|
||||||
|
var horz = 12 * legendRectSize;
|
||||||
|
var vert = i * height;
|
||||||
|
return 'translate(' + horz + ',' + vert + ')';
|
||||||
|
});
|
||||||
|
|
||||||
|
legend.append('rect')
|
||||||
|
.attr('width', legendRectSize)
|
||||||
|
.attr('height', legendRectSize)
|
||||||
|
.style('fill', color)
|
||||||
|
.style('stroke', color);
|
||||||
|
|
||||||
|
legend.append('text')
|
||||||
|
.attr('x', legendRectSize + legendSpacing)
|
||||||
|
.attr('y', legendRectSize - legendSpacing)
|
||||||
|
.text(function(d, i) {
|
||||||
|
return data[i].label;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{{ } }}
|
{{ } }}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/stats.js';
|
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/stats.js';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.statsSource = new EventSource("/api/live_stats");
|
window.statsSource = new EventSource("/api/live_stats");
|
||||||
$(function() {
|
$(function() {
|
||||||
|
@ -187,7 +329,7 @@
|
||||||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool)));
|
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool)));
|
||||||
$('#statsLuckDays' + pool).text(stats.pools[pool].luckDays);
|
$('#statsLuckDays' + pool).text(stats.pools[pool].luckDays);
|
||||||
$('#statsValidBlocks' + pool).text(stats.pools[pool].poolStats.validBlocks);
|
$('#statsValidBlocks' + pool).text(stats.pools[pool].poolStats.validBlocks);
|
||||||
$('#statsTotalPaid' + pool).text((parseFloat(stats.pools[pool].poolStats.totalPaid)+paidJackpots).toFixed(8));
|
$('#statsTotalPaid' + pool).text((parseFloat(stats.pools[pool].poolStats.totalPaid)).toFixed(8));
|
||||||
$('#statsNetworkBlocks' + pool).text(stats.pools[pool].poolStats.networkBlocks);
|
$('#statsNetworkBlocks' + pool).text(stats.pools[pool].poolStats.networkBlocks);
|
||||||
$('#statsNetworkDiff' + pool).text(stats.pools[pool].poolStats.networkDiff);
|
$('#statsNetworkDiff' + pool).text(stats.pools[pool].poolStats.networkDiff);
|
||||||
$('#statsNetworkSols' + pool).text(getReadableNetworkHashRateString(stats.pools[pool].poolStats.networkSols));
|
$('#statsNetworkSols' + pool).text(getReadableNetworkHashRateString(stats.pools[pool].poolStats.networkSols));
|
||||||
|
|
|
@ -33,6 +33,17 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
function searchKeyPress(e)
|
||||||
|
{
|
||||||
|
// look for window.event in case event isn't passed in
|
||||||
|
e = e || window.event;
|
||||||
|
if (e.keyCode == 13)
|
||||||
|
{
|
||||||
|
document.getElementById('btnSearch').click();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
$('.btn-lg').click(function(){
|
$('.btn-lg').click(function(){
|
||||||
window.location = "workers/" + $('.input-lg').val();
|
window.location = "workers/" + $('.input-lg').val();
|
||||||
|
@ -46,7 +57,7 @@
|
||||||
<div class="poolLabel">
|
<div class="poolLabel">
|
||||||
<span style="float:right; margin-bottom: 8px;">
|
<span style="float:right; margin-bottom: 8px;">
|
||||||
<small>Miner Lookup:
|
<small>Miner Lookup:
|
||||||
<input type="text" class="form-control input-lg">
|
<input type="text" class="form-control input-lg" onkeypress="return searchKeyPress(event);">
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-default btn-lg" type="button">Lookup</button>
|
<button class="btn btn-default btn-lg" type="button">Lookup</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -4,6 +4,7 @@ var workerHistoryMax = 160;
|
||||||
|
|
||||||
var statData;
|
var statData;
|
||||||
var totalHash;
|
var totalHash;
|
||||||
|
var totalImmature;
|
||||||
var totalBal;
|
var totalBal;
|
||||||
var totalPaid;
|
var totalPaid;
|
||||||
var totalShares;
|
var totalShares;
|
||||||
|
@ -143,6 +144,7 @@ function updateStats() {
|
||||||
totalHash = statData.totalHash;
|
totalHash = statData.totalHash;
|
||||||
totalPaid = statData.paid;
|
totalPaid = statData.paid;
|
||||||
totalBal = statData.balance;
|
totalBal = statData.balance;
|
||||||
|
totalImmature = statData.immature;
|
||||||
totalShares = statData.totalShares;
|
totalShares = statData.totalShares;
|
||||||
// do some calculations
|
// do some calculations
|
||||||
var _blocktime = 250;
|
var _blocktime = 250;
|
||||||
|
@ -153,6 +155,7 @@ function updateStats() {
|
||||||
$("#statsHashrate").text(getReadableHashRateString(totalHash));
|
$("#statsHashrate").text(getReadableHashRateString(totalHash));
|
||||||
$("#statsHashrateAvg").text(getReadableHashRateString(calculateAverageHashrate(null)));
|
$("#statsHashrateAvg").text(getReadableHashRateString(calculateAverageHashrate(null)));
|
||||||
$("#statsLuckDays").text(luckDays);
|
$("#statsLuckDays").text(luckDays);
|
||||||
|
$("#statsTotalImmature").text(totalImmature);
|
||||||
$("#statsTotalBal").text(totalBal);
|
$("#statsTotalBal").text(totalBal);
|
||||||
$("#statsTotalPaid").text(totalPaid);
|
$("#statsTotalPaid").text(totalPaid);
|
||||||
$("#statsTotalShares").text(totalShares.toFixed(2));
|
$("#statsTotalShares").text(totalShares.toFixed(2));
|
||||||
|
@ -175,7 +178,11 @@ function updateWorkerStats() {
|
||||||
function addWorkerToDisplay(name, htmlSafeName, workerObj) {
|
function addWorkerToDisplay(name, htmlSafeName, workerObj) {
|
||||||
var htmlToAdd = "";
|
var htmlToAdd = "";
|
||||||
htmlToAdd = '<div class="boxStats" id="boxStatsLeft" style="float:left; margin: 9px; min-width: 260px;"><div class="boxStatsList">';
|
htmlToAdd = '<div class="boxStats" id="boxStatsLeft" style="float:left; margin: 9px; min-width: 260px;"><div class="boxStatsList">';
|
||||||
htmlToAdd+='<div class="boxLowerHeader">'+name+'</div><div>';
|
if (htmlSafeName.indexOf("_") >= 0) {
|
||||||
|
htmlToAdd+= '<div class="boxLowerHeader">'+htmlSafeName.substr(htmlSafeName.indexOf("_")+1,htmlSafeName.length)+'</div>';
|
||||||
|
} else {
|
||||||
|
htmlToAdd+= '<div class="boxLowerHeader">noname</div>';
|
||||||
|
}
|
||||||
htmlToAdd+='<div><i class="fa fa-tachometer"></i> <span id="statsHashrate'+htmlSafeName+'">'+getReadableHashRateString(workerObj.hashrate)+'</span> (Now)</div>';
|
htmlToAdd+='<div><i class="fa fa-tachometer"></i> <span id="statsHashrate'+htmlSafeName+'">'+getReadableHashRateString(workerObj.hashrate)+'</span> (Now)</div>';
|
||||||
htmlToAdd+='<div><i class="fa fa-tachometer"></i> <span id="statsHashrateAvg'+htmlSafeName+'">'+getReadableHashRateString(calculateAverageHashrate(name))+'</span> (Avg)</div>';
|
htmlToAdd+='<div><i class="fa fa-tachometer"></i> <span id="statsHashrateAvg'+htmlSafeName+'">'+getReadableHashRateString(calculateAverageHashrate(name))+'</span> (Avg)</div>';
|
||||||
htmlToAdd+='<div><i class="fa fa-shield"></i> <small>Diff:</small> <span id="statsDiff'+htmlSafeName+'">'+workerObj.diff+'</span></div>';
|
htmlToAdd+='<div><i class="fa fa-shield"></i> <small>Diff:</small> <span id="statsDiff'+htmlSafeName+'">'+workerObj.diff+'</span></div>';
|
||||||
|
|
Loading…
Reference in New Issue