From 5f3562f0f246d86fcdc9f0064ba237a60e2438c5 Mon Sep 17 00:00:00 2001 From: Jan <34112129+itsmylife44@users.noreply.github.com> Date: Sun, 20 May 2018 20:06:46 +0200 Subject: [PATCH 01/98] Adding srbminer in options --- website_example/pages/getting_started.html | 65 +++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/website_example/pages/getting_started.html b/website_example/pages/getting_started.html index dd4684454..52acb4b1e 100644 --- a/website_example/pages/getting_started.html +++ b/website_example/pages/getting_started.html @@ -247,6 +247,69 @@

Mining Applications

+ + + + SRBMiner Cryptonight AMD GPU Miner + OpenCL (AMD) + XMRIG version for AMD GPU + Download + See more + + + +
+ +

+{
+/* Type can be : normal, lite, heavy  */
+"cryptonight_type" : "heavy",
+
+/* Intensity 0-> auto intensity, or value from 1-200  */
+"intensity" : 0,
+
+/* To use 2 threads per card set double_threads to true  */
+"double_threads" : false,
+
+/* Gpu target temperature, leave it on 0 if you don't want to use this option  */
+"target_temperature" : 0,
+
+/* If you use a SSL/TLS encrypted pool set this to true*/
+"pool_use_tls" : false,
+
+/* Mining pool address WITHOUT the stratum+tcp:// or stratum+tls:// part  */
+"pool" : "POOL_HOST:PORT",
+
+/* Address of you wallet  */
+"wallet" : "YOUR_WALLET_ADDRESS",
+
+/* Password for your wallet, probably just x  */
+"password" : "YOUR_WORKER_NAME",
+
+/* Location for devfee servers, to get better latency  */
+"location" : "europe",
+
+/* If you want to log console output, put a filename here  */
+"log_file" : "",
+
+/* Charity mining, if you want to disable charity mining just delete these lines, or set values to "" (empty) */
+"charity_pool" : "POOL_HOST:PORT",
+"charity_wallet" : "YOUR_WALLET_ADDRESS",
+"charity_password" : "YOUR_WORKER_NAME",
+
+/* Settings for each GPU manually */
+/* Put in devices that you want to use, if you for ex. don't want to use gpu 2, just don't insert it,like in this example */
+/* Id starts from 0 , not from 1 !! */
+/* To get a list of available devices with their id's, use --listdevices parameter */
+/* This is just an example, edit it and remove comment lines (the slash and star) !! */
+/*"gpu_conf" : 
+[ 
+	{ "id" : 0, "intensity" : 0, "double_threads" : true},
+	{ "id" : 1, "intensity" : 60, "double_threads" : false},
+	{ "id" : 3, "intensity" : 50, "double_threads" : true},
+	{ "id" : 4, "intensity" : 0, "double_threads" : true},
+]*/
+}		    
 	    
             
         
@@ -378,4 +441,4 @@ 

Mining Applications

var password = workerName ? workerName : 'x'; updateTextClasses('examplePassword', password); }); - \ No newline at end of file + From 646fe1324af5c7e0d2ae35e744b7643a523c2abd Mon Sep 17 00:00:00 2001 From: Mark Allen Evans Date: Sun, 1 Jul 2018 08:37:37 -0400 Subject: [PATCH 02/98] base --- cert.pem | 35 ++++++ chain.pem | 27 +++++ config.ETNX.json | 305 +++++++++++++++++++++++++++++++++++++++++++++++ fullchain.pem | 62 ++++++++++ package.json | 22 ++-- privkey.pem | 28 +++++ 6 files changed, 468 insertions(+), 11 deletions(-) create mode 100644 cert.pem create mode 100644 chain.pem create mode 100644 config.ETNX.json create mode 100644 fullchain.pem create mode 100644 privkey.pem diff --git a/cert.pem b/cert.pem new file mode 100644 index 000000000..7a797c01b --- /dev/null +++ b/cert.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGFjCCBP6gAwIBAgISA9E1K07JHERe3wiRADaU19f8MA0GCSqGSIb3DQEBCwUA +MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD +ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODA1MjIwODQzNTJaFw0x +ODA4MjAwODQzNTJaMCExHzAdBgNVBAMTFnVzcG9vbC5lbGVjdHJvbmVyby5vcmcw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4O+Gl1W++pOgukZsD0kaG +1WzopfrdOwMbIS+WQKMiWDif3LdgyLjxv4Xzd7sXrx8nISs9otkUM9UlyFdFO/AM +g60mjN6HuU2pR30LVnAZIQiI4gSR6mvbpxRiIb7PByfL55IFaqe64qV7rQNB3tbW +kX5AmNJrXDpogRri7aeBDpqWo/GSi/LJ/sQvHR/OSHH90hJM+Ydhy82nIGOjrih/ +xru/By4PoD2K1fxzVTiZAtdo2nEvCsDafW/BtqdrwOQStAO9FCN2wSJKyAjjwMYT +0QvOmTT0bpApsxo7BjuJv6ce1Smgpb3Wa9c2WvoGfwdzoRycdlhRsEHRxVH5JRjb +AgMBAAGjggMdMIIDGTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH +AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFEbqD+PMh2U1HW9X +Xg0vNYr8W15JMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsG +AQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNl +bmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNl +bmNyeXB0Lm9yZy8wIQYDVR0RBBowGIIWdXNwb29sLmVsZWN0cm9uZXJvLm9yZzCB +/gYDVR0gBIH2MIHzMAgGBmeBDAECATCB5gYLKwYBBAGC3xMBAQEwgdYwJgYIKwYB +BQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIGrBggrBgEFBQcCAjCB +ngyBm1RoaXMgQ2VydGlmaWNhdGUgbWF5IG9ubHkgYmUgcmVsaWVkIHVwb24gYnkg +UmVseWluZyBQYXJ0aWVzIGFuZCBvbmx5IGluIGFjY29yZGFuY2Ugd2l0aCB0aGUg +Q2VydGlmaWNhdGUgUG9saWN5IGZvdW5kIGF0IGh0dHBzOi8vbGV0c2VuY3J5cHQu +b3JnL3JlcG9zaXRvcnkvMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYA23Sv7ssp +7LH+yj5xbSzluaq7NveEcYPHXZ1PN7Yfv2QAAAFjhzvJQQAABAMARzBFAiBL45hx +bUNPBjRbRzGOwgayXUev3PNdzd/fR12+cSxa9gIhAKwn1g3I/0cD33QAvdTM7XKd +Kmq5A9evk4V7Q1uaNXIBAHUAKTxRllTIOWW6qlD8WAfUt2+/WHopctykwwz05UVH +9HgAAAFjhzvJWQAABAMARjBEAiBb+hVVrNHGG3jxaQsyZXU/PaN3i6jr5TP3w6Rc +tX5xhAIgBbYrrga+VOrfb0C0ZI7WXEJMtDBPL0CSYFk6q+J2f/0wDQYJKoZIhvcN +AQELBQADggEBAFJYbkm5LekuN3lt4Qwdz03EsuUOCTzqqTIIeWbrLTWQ7eDDIacZ ++bRnGOzzBOUdb8ztpPCoknM8GFwb+RurhPHxfk1G/Ujm6pnyDf1TKKnCzdgnrtsz +VozxvwwjXfQRUX6kYsylO15GXldXMwAVxxVp5pZDzj8M2F10+eG07o1SIDFkFWED +UG0tthbRZeb9+D3ustrNU5yOGLNtm0dvYl2MII8VJcoKRtGbP61PV6BPvD43WjPt +muKah86XXL6ctDf1RPN1yWIEwALtmdDrvLyWj/fqol1++kGNLMVUjNVeD4r37Fd1 +rx8AFQiQ9xhs8H6847oGNXFmdJGuDEz9emI= +-----END CERTIFICATE----- diff --git a/chain.pem b/chain.pem new file mode 100644 index 000000000..0002462ce --- /dev/null +++ b/chain.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- diff --git a/config.ETNX.json b/config.ETNX.json new file mode 100644 index 000000000..00c193935 --- /dev/null +++ b/config.ETNX.json @@ -0,0 +1,305 @@ +{ + "poolHost": "uspool.electronero.org", + + "coin": "electronero", + "symbol": "ETNX", + "coinUnits": 100, + "block_reward": 4722590, + "coinDecimalPlaces": 2, + "coinDifficultyTarget": 120, + + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "etnkHfFuanNeTe3q9dux4d9cRiLkUR4hDffvhfTp6nbhEJ5R8TY4vdyZjT4BtWxnvSJ5nfD64eCAQfKMJHSym2dj8PQqeiKmBM", + "intAddressPrefix": 19, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "fullchain.pem", + "ports": [ + { + "port": 1122, + "difficulty": 10000, + "desc": "Low end hardware" + }, + { + "port": 2233, + "difficulty": 100000, + "desc": "Mid range hardware" + }, + { + "port": 3344, + "difficulty": 500000, + "desc": "High end hardware" + }, + { + "port": 4455, + "difficulty": 1000000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 6666, + "difficulty": 2500, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 6677, + "difficulty": 1000000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 3000, + "maxDiff": 100000000, + "targetTime": 120, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 2160, + "maxAddresses": 50, + "mixin": 6, + "priority": 0, + "transferFee": 20000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 100000, + "maxTransactionAmount": 50000000, + "denomination": 100 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 30, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 5.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "electronero_admin_dash", + "ssl": true, + "sslPort": 8119, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "fullchain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 20393 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 1030 + }, + + "redis": { + "host": "127.0.0.1", + "port": 3279, + "auth": null, + "db": 0 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected since %LAST_HASH%.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": true, + "botName": "uspool_etnxBot", + "token": "578231235:AAEMc0jDP8hD12yffDOJKxnewkX-MygBcQY", + "channel": "etnxpoolnews", + "channelStats": { + "enabled": true, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } +} diff --git a/fullchain.pem b/fullchain.pem new file mode 100644 index 000000000..d9031520e --- /dev/null +++ b/fullchain.pem @@ -0,0 +1,62 @@ +-----BEGIN CERTIFICATE----- +MIIGFjCCBP6gAwIBAgISA9E1K07JHERe3wiRADaU19f8MA0GCSqGSIb3DQEBCwUA +MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD +ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODA1MjIwODQzNTJaFw0x +ODA4MjAwODQzNTJaMCExHzAdBgNVBAMTFnVzcG9vbC5lbGVjdHJvbmVyby5vcmcw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4O+Gl1W++pOgukZsD0kaG +1WzopfrdOwMbIS+WQKMiWDif3LdgyLjxv4Xzd7sXrx8nISs9otkUM9UlyFdFO/AM +g60mjN6HuU2pR30LVnAZIQiI4gSR6mvbpxRiIb7PByfL55IFaqe64qV7rQNB3tbW +kX5AmNJrXDpogRri7aeBDpqWo/GSi/LJ/sQvHR/OSHH90hJM+Ydhy82nIGOjrih/ +xru/By4PoD2K1fxzVTiZAtdo2nEvCsDafW/BtqdrwOQStAO9FCN2wSJKyAjjwMYT +0QvOmTT0bpApsxo7BjuJv6ce1Smgpb3Wa9c2WvoGfwdzoRycdlhRsEHRxVH5JRjb +AgMBAAGjggMdMIIDGTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH +AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFEbqD+PMh2U1HW9X +Xg0vNYr8W15JMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsG +AQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNl +bmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNl +bmNyeXB0Lm9yZy8wIQYDVR0RBBowGIIWdXNwb29sLmVsZWN0cm9uZXJvLm9yZzCB +/gYDVR0gBIH2MIHzMAgGBmeBDAECATCB5gYLKwYBBAGC3xMBAQEwgdYwJgYIKwYB +BQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIGrBggrBgEFBQcCAjCB +ngyBm1RoaXMgQ2VydGlmaWNhdGUgbWF5IG9ubHkgYmUgcmVsaWVkIHVwb24gYnkg +UmVseWluZyBQYXJ0aWVzIGFuZCBvbmx5IGluIGFjY29yZGFuY2Ugd2l0aCB0aGUg +Q2VydGlmaWNhdGUgUG9saWN5IGZvdW5kIGF0IGh0dHBzOi8vbGV0c2VuY3J5cHQu +b3JnL3JlcG9zaXRvcnkvMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYA23Sv7ssp +7LH+yj5xbSzluaq7NveEcYPHXZ1PN7Yfv2QAAAFjhzvJQQAABAMARzBFAiBL45hx +bUNPBjRbRzGOwgayXUev3PNdzd/fR12+cSxa9gIhAKwn1g3I/0cD33QAvdTM7XKd +Kmq5A9evk4V7Q1uaNXIBAHUAKTxRllTIOWW6qlD8WAfUt2+/WHopctykwwz05UVH +9HgAAAFjhzvJWQAABAMARjBEAiBb+hVVrNHGG3jxaQsyZXU/PaN3i6jr5TP3w6Rc +tX5xhAIgBbYrrga+VOrfb0C0ZI7WXEJMtDBPL0CSYFk6q+J2f/0wDQYJKoZIhvcN +AQELBQADggEBAFJYbkm5LekuN3lt4Qwdz03EsuUOCTzqqTIIeWbrLTWQ7eDDIacZ ++bRnGOzzBOUdb8ztpPCoknM8GFwb+RurhPHxfk1G/Ujm6pnyDf1TKKnCzdgnrtsz +VozxvwwjXfQRUX6kYsylO15GXldXMwAVxxVp5pZDzj8M2F10+eG07o1SIDFkFWED +UG0tthbRZeb9+D3ustrNU5yOGLNtm0dvYl2MII8VJcoKRtGbP61PV6BPvD43WjPt +muKah86XXL6ctDf1RPN1yWIEwALtmdDrvLyWj/fqol1++kGNLMVUjNVeD4r37Fd1 +rx8AFQiQ9xhs8H6847oGNXFmdJGuDEz9emI= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- diff --git a/package.json b/package.json index dc2726a16..a8f25a2f2 100644 --- a/package.json +++ b/package.json @@ -8,20 +8,20 @@ "url": "https://github.com/dvandal/cryptonote-nodejs-pool.git" }, "dependencies": { - "async": "1", - "base58-native": "*", - "bignum": "*", - "cli-color": "*", + "async": "^1.5.2", + "base58-native": "^0.1.4", + "bignum": "^0.13.0", + "cli-color": "^1.2.0", "cryptoforknote-util": "git://github.com/MoneroOcean/node-cryptoforknote-util.git", "cryptonight-hashing": "git://github.com/MoneroOcean/node-cryptonight-hashing.git", - "dateformat": "*", - "mailgun.js": "*", - "node-telegram-bot-api": "*", - "nodemailer": "2.7.2", - "nodemailer-sendmail-transport": "*", - "redis": "*", + "dateformat": "^3.0.3", + "mailgun.js": "^2.0.1", + "node-telegram-bot-api": "^0.30.0", + "nodemailer": "^2.7.2", + "nodemailer-sendmail-transport": "^1.0.2", + "redis": "^2.8.0", "socket.io": "^2.1.1", - "time-ago": "*" + "time-ago": "^0.2.1" }, "engines": { "node": ">=4.0" diff --git a/privkey.pem b/privkey.pem new file mode 100644 index 000000000..9a47c2c45 --- /dev/null +++ b/privkey.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4O+Gl1W++pOgu +kZsD0kaG1WzopfrdOwMbIS+WQKMiWDif3LdgyLjxv4Xzd7sXrx8nISs9otkUM9Ul +yFdFO/AMg60mjN6HuU2pR30LVnAZIQiI4gSR6mvbpxRiIb7PByfL55IFaqe64qV7 +rQNB3tbWkX5AmNJrXDpogRri7aeBDpqWo/GSi/LJ/sQvHR/OSHH90hJM+Ydhy82n +IGOjrih/xru/By4PoD2K1fxzVTiZAtdo2nEvCsDafW/BtqdrwOQStAO9FCN2wSJK +yAjjwMYT0QvOmTT0bpApsxo7BjuJv6ce1Smgpb3Wa9c2WvoGfwdzoRycdlhRsEHR +xVH5JRjbAgMBAAECggEAfw1s0/yZj5QSOutXSA9fa+ULsnWkx6TGw5LjSm9REYJd +qoGARVKnmLBIJFtRv0eZ6FZLh7oikngcam37IVfwd/DnHErRvC/iivcuGP+HVw0c +4aldFXPg5h4XRIWZHRUGMOyGGw4ulgdvsBbkxCx1ZIv50XqSwk9cSG2gqRbUCX9p +KYYqUgs3xVLzY9l4ekuNS3Hoxt3R2p0P4BG6yWGWcRNQ6ifvQg/pGoZlZtgJPnHB +miTyh9IQIhw1A2dtR+iaM0Eyx1vW/x2W1naStuSvQFhPbtJcMJp6IUVMwoxPn0EH +Rktt8FzpDoZF7k6LXNIVDiJrZu8H5ze77alaIWAzwQKBgQDdyLt4/TCpwNzs0pqu ++iz2Hc8/UxX8emcuY4iwLIcykTgHtlLFtkW/et1jYKjgVmkN/fN8R7qNNOK8dSIn +9pJTuYY62EACTSsz3pt2f1Hl79MP/xJBLHBsi4bXCx3j7hXaYH5dlOXNNEXuGhZZ +KUGNCfTDwpwN3MDGrwPQB3/RqQKBgQDUqB7HymkHGeIDRvU9wuNR3/hgx/YGygkw +AyuFXR4I7QeLMq7fD0r6KbZN6b8sELF8URqtsEyN42M9x7e5tlrkM8aYBRPCZQiz +MntSCdq1ygBi8xxnELTIieXyWSjhHXX4HNNXLtRBUVW2yUX0pyZnIWxmksl2n/xP +Q5+um5qw4wKBgB3ePLEsl/MkkBwsuLw74PY8SX7jt6BK875hlTT0qjh7KiqPuVH7 +HCWPbGVHzTZ5LKi/vJzOJCeEKczrkXAEWux/nb4rBWPJ9LmFjzZEK0lmJOyQk2rH +i1Ry7fPkAWCC3dqNCDeeJ9yc35YTWVMkpTlTT+1VNiL/yc54H3FSsToRAoGBAJ/r +8oHjGQrLDynRwfn5RsQ6v2ng1ocShshBiggYF880mZi3u8RplsP8fGmpYCf+Qcsw +nvSBgYCuwOIkAGm71iyJN075enuVSW6hhxYR7RUK3TGMjA6CBMJO2ctf+5sXcbDt +1I1a3SplxSTZxi8PSJUEEKYuuyzncXIWc852nGVrAoGAcJ4k2HUdJsuc63OqI5Bs +uBMNfqoxO53GzhGnFDT0VHGO9fT4R6OCYPVgprw0OuBeOLkD6n+PJJFUDaUCGXO+ +sJDvvHccAS4MHnc1Dw+xbJFuQ9/5BTJYen+9SEv1WbKWp5va8ArC4VRV1bVB+rel +B9Y/IerykEYc8rxJIuKAmjM= +-----END PRIVATE KEY----- From b2a17e26ec323d34f4c5f450bb11582fe89c3c77 Mon Sep 17 00:00:00 2001 From: Mark Allen Evans Date: Sun, 1 Jul 2018 08:38:02 -0400 Subject: [PATCH 03/98] base --- config.ETNX.json | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/config.ETNX.json b/config.ETNX.json index 00c193935..824a342b2 100644 --- a/config.ETNX.json +++ b/config.ETNX.json @@ -4,10 +4,10 @@ "coin": "electronero", "symbol": "ETNX", "coinUnits": 100, - "block_reward": 4722590, "coinDecimalPlaces": 2, "coinDifficultyTarget": 120, + "daemonType": "default", "cnAlgorithm": "cryptonight", "cnVariant": 1, "cnBlobType": 0, @@ -28,7 +28,7 @@ "enabled": true, "clusterForks": "auto", "poolAddress": "etnkHfFuanNeTe3q9dux4d9cRiLkUR4hDffvhfTp6nbhEJ5R8TY4vdyZjT4BtWxnvSJ5nfD64eCAQfKMJHSym2dj8PQqeiKmBM", - "intAddressPrefix": 19, + "intAddressPrefix": 18019, "blockRefreshInterval": 1000, "minerTimeout": 900, "sslCert": "cert.pem", @@ -57,7 +57,7 @@ }, { "port": 6666, - "difficulty": 2500, + "difficulty": 3000, "desc": "Hidden port", "hidden": true }, @@ -69,9 +69,9 @@ } ], "varDiff": { - "minDiff": 3000, - "maxDiff": 100000000, - "targetTime": 120, + "minDiff": 100, + "maxDiff": 1000000000, + "targetTime": 60, "retargetTime": 30, "variancePercent": 30, "maxJump": 100 @@ -108,13 +108,14 @@ "enabled": true, "interval": 2160, "maxAddresses": 50, - "mixin": 6, + "mixin": 2, "priority": 0, "transferFee": 20000, "dynamicTransferFee": true, "minerPayFee" : true, "minPayment": 100000, - "maxTransactionAmount": 50000000, + "maxPayment": 100000000, + "maxTransactionAmount": 500000000, "denomination": 100 }, @@ -135,7 +136,7 @@ "port": 8117, "blocks": 30, "payments": 30, - "password": "electronero_admin_dash", + "password": "vsend_admin_dash", "ssl": true, "sslPort": 8119, "sslCert": "cert.pem", @@ -158,7 +159,8 @@ "host": "127.0.0.1", "port": 3279, "auth": null, - "db": 0 + "db": 0, + "cleanupInterval": 15 }, "notifications": { @@ -184,7 +186,7 @@ "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." }, "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected since %LAST_HASH%.", + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", @@ -225,13 +227,14 @@ "token": "578231235:AAEMc0jDP8hD12yffDOJKxnewkX-MygBcQY", "channel": "etnxpoolnews", "channelStats": { - "enabled": true, + "enabled": false, "interval": 30 }, "botCommands": { "stats": "/stats", - "enable": "/enable", - "disable": "/disable" + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" } }, @@ -300,6 +303,10 @@ "payments": { "enabled": true } + }, + "blocks": { + "enabled": true, + "days": 30 } } } From 11d874bb4f65201eeb2bbde50b6586a2d8c4e977 Mon Sep 17 00:00:00 2001 From: Interchained Date: Sun, 1 Jul 2018 08:42:25 -0400 Subject: [PATCH 04/98] Update config.ETNX.json --- config.ETNX.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config.ETNX.json b/config.ETNX.json index 824a342b2..262cb0a45 100644 --- a/config.ETNX.json +++ b/config.ETNX.json @@ -27,7 +27,7 @@ "poolServer": { "enabled": true, "clusterForks": "auto", - "poolAddress": "etnkHfFuanNeTe3q9dux4d9cRiLkUR4hDffvhfTp6nbhEJ5R8TY4vdyZjT4BtWxnvSJ5nfD64eCAQfKMJHSym2dj8PQqeiKmBM", + "poolAddress": "ETNX_ADDRESS", "intAddressPrefix": 18019, "blockRefreshInterval": 1000, "minerTimeout": 900, @@ -136,7 +136,7 @@ "port": 8117, "blocks": 30, "payments": 30, - "password": "vsend_admin_dash", + "password": "admin_dash_pass", "ssl": true, "sslPort": 8119, "sslCert": "cert.pem", @@ -223,9 +223,9 @@ "telegram": { "enabled": true, - "botName": "uspool_etnxBot", - "token": "578231235:AAEMc0jDP8hD12yffDOJKxnewkX-MygBcQY", - "channel": "etnxpoolnews", + "botName": "BOT_NAME", + "token": "BOT_TOKEN", + "channel": "BOT_CHANNEL", "channelStats": { "enabled": false, "interval": 30 From afbfe717ffd8b1b31053bb8bfd271e2c562eeb14 Mon Sep 17 00:00:00 2001 From: Interchained Date: Sun, 1 Jul 2018 08:42:42 -0400 Subject: [PATCH 05/98] Update config.ETNX.json --- config.ETNX.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.ETNX.json b/config.ETNX.json index 262cb0a45..7e8199a05 100644 --- a/config.ETNX.json +++ b/config.ETNX.json @@ -1,5 +1,5 @@ { - "poolHost": "uspool.electronero.org", + "poolHost": "POOL_HOST", "coin": "electronero", "symbol": "ETNX", From ba738365e9e93ebf5635abe2db6bf26a9adabea3 Mon Sep 17 00:00:00 2001 From: Interchained Date: Sun, 1 Jul 2018 08:43:08 -0400 Subject: [PATCH 06/98] Delete privkey.pem --- privkey.pem | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 privkey.pem diff --git a/privkey.pem b/privkey.pem deleted file mode 100644 index 9a47c2c45..000000000 --- a/privkey.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4O+Gl1W++pOgu -kZsD0kaG1WzopfrdOwMbIS+WQKMiWDif3LdgyLjxv4Xzd7sXrx8nISs9otkUM9Ul -yFdFO/AMg60mjN6HuU2pR30LVnAZIQiI4gSR6mvbpxRiIb7PByfL55IFaqe64qV7 -rQNB3tbWkX5AmNJrXDpogRri7aeBDpqWo/GSi/LJ/sQvHR/OSHH90hJM+Ydhy82n -IGOjrih/xru/By4PoD2K1fxzVTiZAtdo2nEvCsDafW/BtqdrwOQStAO9FCN2wSJK -yAjjwMYT0QvOmTT0bpApsxo7BjuJv6ce1Smgpb3Wa9c2WvoGfwdzoRycdlhRsEHR -xVH5JRjbAgMBAAECggEAfw1s0/yZj5QSOutXSA9fa+ULsnWkx6TGw5LjSm9REYJd -qoGARVKnmLBIJFtRv0eZ6FZLh7oikngcam37IVfwd/DnHErRvC/iivcuGP+HVw0c -4aldFXPg5h4XRIWZHRUGMOyGGw4ulgdvsBbkxCx1ZIv50XqSwk9cSG2gqRbUCX9p -KYYqUgs3xVLzY9l4ekuNS3Hoxt3R2p0P4BG6yWGWcRNQ6ifvQg/pGoZlZtgJPnHB -miTyh9IQIhw1A2dtR+iaM0Eyx1vW/x2W1naStuSvQFhPbtJcMJp6IUVMwoxPn0EH -Rktt8FzpDoZF7k6LXNIVDiJrZu8H5ze77alaIWAzwQKBgQDdyLt4/TCpwNzs0pqu -+iz2Hc8/UxX8emcuY4iwLIcykTgHtlLFtkW/et1jYKjgVmkN/fN8R7qNNOK8dSIn -9pJTuYY62EACTSsz3pt2f1Hl79MP/xJBLHBsi4bXCx3j7hXaYH5dlOXNNEXuGhZZ -KUGNCfTDwpwN3MDGrwPQB3/RqQKBgQDUqB7HymkHGeIDRvU9wuNR3/hgx/YGygkw -AyuFXR4I7QeLMq7fD0r6KbZN6b8sELF8URqtsEyN42M9x7e5tlrkM8aYBRPCZQiz -MntSCdq1ygBi8xxnELTIieXyWSjhHXX4HNNXLtRBUVW2yUX0pyZnIWxmksl2n/xP -Q5+um5qw4wKBgB3ePLEsl/MkkBwsuLw74PY8SX7jt6BK875hlTT0qjh7KiqPuVH7 -HCWPbGVHzTZ5LKi/vJzOJCeEKczrkXAEWux/nb4rBWPJ9LmFjzZEK0lmJOyQk2rH -i1Ry7fPkAWCC3dqNCDeeJ9yc35YTWVMkpTlTT+1VNiL/yc54H3FSsToRAoGBAJ/r -8oHjGQrLDynRwfn5RsQ6v2ng1ocShshBiggYF880mZi3u8RplsP8fGmpYCf+Qcsw -nvSBgYCuwOIkAGm71iyJN075enuVSW6hhxYR7RUK3TGMjA6CBMJO2ctf+5sXcbDt -1I1a3SplxSTZxi8PSJUEEKYuuyzncXIWc852nGVrAoGAcJ4k2HUdJsuc63OqI5Bs -uBMNfqoxO53GzhGnFDT0VHGO9fT4R6OCYPVgprw0OuBeOLkD6n+PJJFUDaUCGXO+ -sJDvvHccAS4MHnc1Dw+xbJFuQ9/5BTJYen+9SEv1WbKWp5va8ArC4VRV1bVB+rel -B9Y/IerykEYc8rxJIuKAmjM= ------END PRIVATE KEY----- From 42d84d3782c15bd135fb8080b5eb9fe04f053629 Mon Sep 17 00:00:00 2001 From: Interchained Date: Sun, 1 Jul 2018 08:43:13 -0400 Subject: [PATCH 07/98] Delete cert.pem --- cert.pem | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 cert.pem diff --git a/cert.pem b/cert.pem deleted file mode 100644 index 7a797c01b..000000000 --- a/cert.pem +++ /dev/null @@ -1,35 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGFjCCBP6gAwIBAgISA9E1K07JHERe3wiRADaU19f8MA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODA1MjIwODQzNTJaFw0x -ODA4MjAwODQzNTJaMCExHzAdBgNVBAMTFnVzcG9vbC5lbGVjdHJvbmVyby5vcmcw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4O+Gl1W++pOgukZsD0kaG -1WzopfrdOwMbIS+WQKMiWDif3LdgyLjxv4Xzd7sXrx8nISs9otkUM9UlyFdFO/AM -g60mjN6HuU2pR30LVnAZIQiI4gSR6mvbpxRiIb7PByfL55IFaqe64qV7rQNB3tbW -kX5AmNJrXDpogRri7aeBDpqWo/GSi/LJ/sQvHR/OSHH90hJM+Ydhy82nIGOjrih/ -xru/By4PoD2K1fxzVTiZAtdo2nEvCsDafW/BtqdrwOQStAO9FCN2wSJKyAjjwMYT -0QvOmTT0bpApsxo7BjuJv6ce1Smgpb3Wa9c2WvoGfwdzoRycdlhRsEHRxVH5JRjb -AgMBAAGjggMdMIIDGTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH -AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFEbqD+PMh2U1HW9X -Xg0vNYr8W15JMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsG -AQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNl -bmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNl -bmNyeXB0Lm9yZy8wIQYDVR0RBBowGIIWdXNwb29sLmVsZWN0cm9uZXJvLm9yZzCB -/gYDVR0gBIH2MIHzMAgGBmeBDAECATCB5gYLKwYBBAGC3xMBAQEwgdYwJgYIKwYB -BQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIGrBggrBgEFBQcCAjCB -ngyBm1RoaXMgQ2VydGlmaWNhdGUgbWF5IG9ubHkgYmUgcmVsaWVkIHVwb24gYnkg -UmVseWluZyBQYXJ0aWVzIGFuZCBvbmx5IGluIGFjY29yZGFuY2Ugd2l0aCB0aGUg -Q2VydGlmaWNhdGUgUG9saWN5IGZvdW5kIGF0IGh0dHBzOi8vbGV0c2VuY3J5cHQu -b3JnL3JlcG9zaXRvcnkvMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYA23Sv7ssp -7LH+yj5xbSzluaq7NveEcYPHXZ1PN7Yfv2QAAAFjhzvJQQAABAMARzBFAiBL45hx -bUNPBjRbRzGOwgayXUev3PNdzd/fR12+cSxa9gIhAKwn1g3I/0cD33QAvdTM7XKd -Kmq5A9evk4V7Q1uaNXIBAHUAKTxRllTIOWW6qlD8WAfUt2+/WHopctykwwz05UVH -9HgAAAFjhzvJWQAABAMARjBEAiBb+hVVrNHGG3jxaQsyZXU/PaN3i6jr5TP3w6Rc -tX5xhAIgBbYrrga+VOrfb0C0ZI7WXEJMtDBPL0CSYFk6q+J2f/0wDQYJKoZIhvcN -AQELBQADggEBAFJYbkm5LekuN3lt4Qwdz03EsuUOCTzqqTIIeWbrLTWQ7eDDIacZ -+bRnGOzzBOUdb8ztpPCoknM8GFwb+RurhPHxfk1G/Ujm6pnyDf1TKKnCzdgnrtsz -VozxvwwjXfQRUX6kYsylO15GXldXMwAVxxVp5pZDzj8M2F10+eG07o1SIDFkFWED -UG0tthbRZeb9+D3ustrNU5yOGLNtm0dvYl2MII8VJcoKRtGbP61PV6BPvD43WjPt -muKah86XXL6ctDf1RPN1yWIEwALtmdDrvLyWj/fqol1++kGNLMVUjNVeD4r37Fd1 -rx8AFQiQ9xhs8H6847oGNXFmdJGuDEz9emI= ------END CERTIFICATE----- From 1974919068ebb1b84456ee62924fd5fd36f097f7 Mon Sep 17 00:00:00 2001 From: Interchained Date: Sun, 1 Jul 2018 08:43:20 -0400 Subject: [PATCH 08/98] Delete chain.pem --- chain.pem | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 chain.pem diff --git a/chain.pem b/chain.pem deleted file mode 100644 index 0002462ce..000000000 --- a/chain.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- From 50a9796142881b8fa37ec289208dee5e956ce31f Mon Sep 17 00:00:00 2001 From: Interchained Date: Sun, 1 Jul 2018 08:43:29 -0400 Subject: [PATCH 09/98] Delete fullchain.pem --- fullchain.pem | 62 --------------------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 fullchain.pem diff --git a/fullchain.pem b/fullchain.pem deleted file mode 100644 index d9031520e..000000000 --- a/fullchain.pem +++ /dev/null @@ -1,62 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGFjCCBP6gAwIBAgISA9E1K07JHERe3wiRADaU19f8MA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODA1MjIwODQzNTJaFw0x -ODA4MjAwODQzNTJaMCExHzAdBgNVBAMTFnVzcG9vbC5lbGVjdHJvbmVyby5vcmcw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4O+Gl1W++pOgukZsD0kaG -1WzopfrdOwMbIS+WQKMiWDif3LdgyLjxv4Xzd7sXrx8nISs9otkUM9UlyFdFO/AM -g60mjN6HuU2pR30LVnAZIQiI4gSR6mvbpxRiIb7PByfL55IFaqe64qV7rQNB3tbW -kX5AmNJrXDpogRri7aeBDpqWo/GSi/LJ/sQvHR/OSHH90hJM+Ydhy82nIGOjrih/ -xru/By4PoD2K1fxzVTiZAtdo2nEvCsDafW/BtqdrwOQStAO9FCN2wSJKyAjjwMYT -0QvOmTT0bpApsxo7BjuJv6ce1Smgpb3Wa9c2WvoGfwdzoRycdlhRsEHRxVH5JRjb -AgMBAAGjggMdMIIDGTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH -AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFEbqD+PMh2U1HW9X -Xg0vNYr8W15JMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsG -AQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNl -bmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNl -bmNyeXB0Lm9yZy8wIQYDVR0RBBowGIIWdXNwb29sLmVsZWN0cm9uZXJvLm9yZzCB -/gYDVR0gBIH2MIHzMAgGBmeBDAECATCB5gYLKwYBBAGC3xMBAQEwgdYwJgYIKwYB -BQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIGrBggrBgEFBQcCAjCB -ngyBm1RoaXMgQ2VydGlmaWNhdGUgbWF5IG9ubHkgYmUgcmVsaWVkIHVwb24gYnkg -UmVseWluZyBQYXJ0aWVzIGFuZCBvbmx5IGluIGFjY29yZGFuY2Ugd2l0aCB0aGUg -Q2VydGlmaWNhdGUgUG9saWN5IGZvdW5kIGF0IGh0dHBzOi8vbGV0c2VuY3J5cHQu -b3JnL3JlcG9zaXRvcnkvMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYA23Sv7ssp -7LH+yj5xbSzluaq7NveEcYPHXZ1PN7Yfv2QAAAFjhzvJQQAABAMARzBFAiBL45hx -bUNPBjRbRzGOwgayXUev3PNdzd/fR12+cSxa9gIhAKwn1g3I/0cD33QAvdTM7XKd -Kmq5A9evk4V7Q1uaNXIBAHUAKTxRllTIOWW6qlD8WAfUt2+/WHopctykwwz05UVH -9HgAAAFjhzvJWQAABAMARjBEAiBb+hVVrNHGG3jxaQsyZXU/PaN3i6jr5TP3w6Rc -tX5xhAIgBbYrrga+VOrfb0C0ZI7WXEJMtDBPL0CSYFk6q+J2f/0wDQYJKoZIhvcN -AQELBQADggEBAFJYbkm5LekuN3lt4Qwdz03EsuUOCTzqqTIIeWbrLTWQ7eDDIacZ -+bRnGOzzBOUdb8ztpPCoknM8GFwb+RurhPHxfk1G/Ujm6pnyDf1TKKnCzdgnrtsz -VozxvwwjXfQRUX6kYsylO15GXldXMwAVxxVp5pZDzj8M2F10+eG07o1SIDFkFWED -UG0tthbRZeb9+D3ustrNU5yOGLNtm0dvYl2MII8VJcoKRtGbP61PV6BPvD43WjPt -muKah86XXL6ctDf1RPN1yWIEwALtmdDrvLyWj/fqol1++kGNLMVUjNVeD4r37Fd1 -rx8AFQiQ9xhs8H6847oGNXFmdJGuDEz9emI= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- From 79f7d13d296e33e67c25c60c2be1c9a6d1ae4c60 Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 10 Jul 2018 03:53:01 -0400 Subject: [PATCH 10/98] add subaddress --- lib/utils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 2d8a37def..5be59c1da 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -26,6 +26,7 @@ exports.instanceId = function() { **/ var addressBase58Prefix = parseInt(cnUtil.address_decode(new Buffer(config.poolServer.poolAddress)).toString()); var integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1; +var subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(config.poolServer.subAddressPrefix) : "N/A"; // Get address prefix function getAddressPrefix(address) { @@ -48,6 +49,7 @@ exports.validateMinerAddress = function(address) { var addressPrefix = getAddressPrefix(address); if (addressPrefix === addressBase58Prefix) return true; else if (addressPrefix === integratedAddressBase58Prefix) return true; + else if (addressPrefix === subAddressBase58Prefix) return true; return false; } @@ -143,4 +145,4 @@ exports.ringBuffer = function(maxSize){ isFull = false; } }; -}; \ No newline at end of file +}; From 9d63c9aeeec4ee4219705dec3cb1adec7150877f Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 10 Jul 2018 05:27:19 -0400 Subject: [PATCH 11/98] Create electronero.json --- config_examples/electronero.json | 312 +++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 config_examples/electronero.json diff --git a/config_examples/electronero.json b/config_examples/electronero.json new file mode 100644 index 000000000..d60e26b20 --- /dev/null +++ b/config_examples/electronero.json @@ -0,0 +1,312 @@ +{ + "poolHost": "POOL_HOST", + + "coin": "electronero", + "symbol": "ETNX", + "coinUnits": 100, + "coinDecimalPlaces": 2, + "coinDifficultyTarget": 120, + + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "ETNX_ADDRESS", + "intAddressPrefix": 18019, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "fullchain.pem", + "ports": [ + { + "port": 1122, + "difficulty": 10000, + "desc": "Low end hardware" + }, + { + "port": 2233, + "difficulty": 100000, + "desc": "Mid range hardware" + }, + { + "port": 3344, + "difficulty": 500000, + "desc": "High end hardware" + }, + { + "port": 4455, + "difficulty": 1000000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 6666, + "difficulty": 3000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 6677, + "difficulty": 1000000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 1000000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 2160, + "maxAddresses": 50, + "mixin": 2, + "priority": 0, + "transferFee": 20000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 100000, + "maxPayment": 100000000, + "maxTransactionAmount": 500000000, + "denomination": 100 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 30, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "admin_dash_pass", + "ssl": true, + "sslPort": 8119, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "fullchain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 20393 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 1030 + }, + + "redis": { + "host": "127.0.0.1", + "port": 3279, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": true, + "botName": "BOT_NAME", + "token": "BOT_TOKEN", + "channel": "BOT_CHANNEL", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From bda28523e198d8e1058486c1403fba9b951e432b Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 10 Jul 2018 05:29:43 -0400 Subject: [PATCH 12/98] Delete electronero.json --- config_examples/electronero.json | 312 ------------------------------- 1 file changed, 312 deletions(-) delete mode 100644 config_examples/electronero.json diff --git a/config_examples/electronero.json b/config_examples/electronero.json deleted file mode 100644 index d60e26b20..000000000 --- a/config_examples/electronero.json +++ /dev/null @@ -1,312 +0,0 @@ -{ - "poolHost": "POOL_HOST", - - "coin": "electronero", - "symbol": "ETNX", - "coinUnits": 100, - "coinDecimalPlaces": 2, - "coinDifficultyTarget": 120, - - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "ETNX_ADDRESS", - "intAddressPrefix": 18019, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "cert.pem", - "sslKey": "privkey.pem", - "sslCA": "fullchain.pem", - "ports": [ - { - "port": 1122, - "difficulty": 10000, - "desc": "Low end hardware" - }, - { - "port": 2233, - "difficulty": 100000, - "desc": "Mid range hardware" - }, - { - "port": 3344, - "difficulty": 500000, - "desc": "High end hardware" - }, - { - "port": 4455, - "difficulty": 1000000, - "desc": "Cloud-mining / NiceHash" - }, - { - "port": 6666, - "difficulty": 3000, - "desc": "Hidden port", - "hidden": true - }, - { - "port": 6677, - "difficulty": 1000000, - "desc": "SSL connection", - "ssl": true - } - ], - "varDiff": { - "minDiff": 100, - "maxDiff": 1000000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, - - "payments": { - "enabled": true, - "interval": 2160, - "maxAddresses": 50, - "mixin": 2, - "priority": 0, - "transferFee": 20000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 100000, - "maxPayment": 100000000, - "maxTransactionAmount": 500000000, - "denomination": 100 - }, - - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 30, - "poolFee": 0.0, - "devDonation": 0.0, - "networkFee": 0.0 - }, - - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "admin_dash_pass", - "ssl": true, - "sslPort": 8119, - "sslCert": "cert.pem", - "sslKey": "privkey.pem", - "sslCA": "fullchain.pem", - "trustProxyIP": true - }, - - "daemon": { - "host": "127.0.0.1", - "port": 20393 - }, - - "wallet": { - "host": "127.0.0.1", - "port": 1030 - }, - - "redis": { - "host": "127.0.0.1", - "port": 3279, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, - - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, - - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, - - "telegram": { - "enabled": true, - "botName": "BOT_NAME", - "token": "BOT_TOKEN", - "channel": "BOT_CHANNEL", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, - - "prices": { - "source": "cryptonator", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } -} From 6960103f8a9350e1ccb92f280148e6b0f3e59017 Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 10 Jul 2018 05:29:53 -0400 Subject: [PATCH 13/98] Add files via upload --- config_examples/etnx.config.json | 313 +++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 config_examples/etnx.config.json diff --git a/config_examples/etnx.config.json b/config_examples/etnx.config.json new file mode 100644 index 000000000..3049e47bb --- /dev/null +++ b/config_examples/etnx.config.json @@ -0,0 +1,313 @@ +{ + "poolHost": "uspool.electronero.org", + + "coin": "electronero", + "symbol": "ETNX", + "coinUnits": 100, + "coinDecimalPlaces": 2, + "coinDifficultyTarget": 120, + + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "etnkHfFuanNeTe3q9dux4d9cRiLkUR4hDffvhfTp6nbhEJ5R8TY4vdyZjT4BtWxnvSJ5nfD64eCAQfKMJHSym2dj8PQqeiKmBM", + "intAddressPrefix": 18019, + "subAddressPrefix": 42, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "fullchain.pem", + "ports": [ + { + "port": 1122, + "difficulty": 10000, + "desc": "Low end hardware" + }, + { + "port": 2233, + "difficulty": 100000, + "desc": "Mid range hardware" + }, + { + "port": 3344, + "difficulty": 500000, + "desc": "High end hardware" + }, + { + "port": 4455, + "difficulty": 1000000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 6666, + "difficulty": 3000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 6677, + "difficulty": 1000000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 1000000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 2160, + "maxAddresses": 10, + "mixin": 2, + "priority": 0, + "transferFee": 20000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 100000, + "maxPayment": 25000000, + "maxTransactionAmount": 50000000, + "denomination": 100 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 30, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 0.5 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "vsend_admin_dash", + "ssl": true, + "sslPort": 8119, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "fullchain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 20393 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 1030 + }, + + "redis": { + "host": "127.0.0.1", + "port": 3279, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": true, + "botName": "uspool_etnxBot", + "token": "578231235:AAEMc0jDP8hD12yffDOJKxnewkX-MygBcQY", + "channel": "etnxpoolnews", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From 9d8b57fe512e044cc8dc31d02b7f19dabbba68dc Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 10 Jul 2018 05:31:34 -0400 Subject: [PATCH 14/98] Update and rename etnx.config.json to electronero.json --- .../{etnx.config.json => electronero.json} | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename config_examples/{etnx.config.json => electronero.json} (95%) diff --git a/config_examples/etnx.config.json b/config_examples/electronero.json similarity index 95% rename from config_examples/etnx.config.json rename to config_examples/electronero.json index 3049e47bb..eae8e748c 100644 --- a/config_examples/etnx.config.json +++ b/config_examples/electronero.json @@ -1,5 +1,5 @@ { - "poolHost": "uspool.electronero.org", + "poolHost": "POOL_URL", "coin": "electronero", "symbol": "ETNX", @@ -27,7 +27,7 @@ "poolServer": { "enabled": true, "clusterForks": "auto", - "poolAddress": "etnkHfFuanNeTe3q9dux4d9cRiLkUR4hDffvhfTp6nbhEJ5R8TY4vdyZjT4BtWxnvSJ5nfD64eCAQfKMJHSym2dj8PQqeiKmBM", + "poolAddress": "POOL_ADDRESS", "intAddressPrefix": 18019, "subAddressPrefix": 42, "blockRefreshInterval": 1000, @@ -126,7 +126,7 @@ "depth": 30, "poolFee": 0.0, "devDonation": 0.0, - "networkFee": 0.5 + "networkFee": 0.0 }, "api": { @@ -137,7 +137,7 @@ "port": 8117, "blocks": 30, "payments": 30, - "password": "vsend_admin_dash", + "password": "admin_pass", "ssl": true, "sslPort": 8119, "sslCert": "cert.pem", @@ -148,12 +148,12 @@ "daemon": { "host": "127.0.0.1", - "port": 20393 + "port": 3000 }, "wallet": { "host": "127.0.0.1", - "port": 1030 + "port": 3000 }, "redis": { @@ -224,9 +224,9 @@ "telegram": { "enabled": true, - "botName": "uspool_etnxBot", - "token": "578231235:AAEMc0jDP8hD12yffDOJKxnewkX-MygBcQY", - "channel": "etnxpoolnews", + "botName": "POOL_BOT", + "token": "POOL_BOT_TOKEN", + "channel": "POOL_BOT_CHANNEL", "channelStats": { "enabled": false, "interval": 30 From 2e0e84d12eff1f5a9dedda74974535d90db80dfd Mon Sep 17 00:00:00 2001 From: David Young Date: Wed, 11 Jul 2018 09:04:02 +1200 Subject: [PATCH 15/98] Remove redundant waitforit.sh chmod --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 31034591f..2401a94f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,7 @@ FROM node:8-slim RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs-legacy npm git libboost1.55-all libssl-dev \ - && rm -rf /var/lib/apt/lists/* && \ - chmod +x /wait-for-it.sh + && rm -rf /var/lib/apt/lists/* ADD . /pool/ WORKDIR /pool/ From b9de2091a3fd606a95d81667952582865983924e Mon Sep 17 00:00:00 2001 From: David Young Date: Sat, 14 Jul 2018 13:53:03 +1200 Subject: [PATCH 16/98] Try to make RPC wallet work with password --- lib/apiInterfaces.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index 6638ed1ab..45bec9875 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -109,7 +109,8 @@ module.exports = function(daemonConfig, walletConfig, poolApiConfig){ rpc(daemonConfig.host, daemonConfig.port, method, params, callback); }, rpcWallet: function(method, params, callback){ - rpc(walletConfig.host, walletConfig.port, method, params, callback); + rpc(walletConfig.host, walletConfig.port, method, params, callback, + walletConfig.password); }, pool: function(path, callback){ var bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; From ca75545fd970349f30231d1dff21d96983349b36 Mon Sep 17 00:00:00 2001 From: Interchained Date: Mon, 16 Jul 2018 09:14:30 -0400 Subject: [PATCH 17/98] Update config.ETNX.json --- config.ETNX.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.ETNX.json b/config.ETNX.json index 7e8199a05..c2dc781c2 100644 --- a/config.ETNX.json +++ b/config.ETNX.json @@ -9,8 +9,8 @@ "daemonType": "default", "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, + "cnVariant": 4, + "cnBlobType": 3, "logging": { "files": { From 05dbe6eb72cb078e8d95ff790c244edbbda42885 Mon Sep 17 00:00:00 2001 From: Interchained Date: Mon, 16 Jul 2018 09:18:16 -0400 Subject: [PATCH 18/98] Update electronero.json --- config_examples/electronero.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config_examples/electronero.json b/config_examples/electronero.json index eae8e748c..d451ffb77 100644 --- a/config_examples/electronero.json +++ b/config_examples/electronero.json @@ -9,8 +9,8 @@ "daemonType": "default", "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, + "cnVariant": 4, + "cnBlobType": 3, "logging": { "files": { From 219fe00150830eaa6dd35c273aeb2572390faac2 Mon Sep 17 00:00:00 2001 From: Interchained Date: Mon, 16 Jul 2018 09:19:00 -0400 Subject: [PATCH 19/98] Update config.ETNX.json --- config.ETNX.json | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/config.ETNX.json b/config.ETNX.json index c2dc781c2..d451ffb77 100644 --- a/config.ETNX.json +++ b/config.ETNX.json @@ -1,5 +1,5 @@ { - "poolHost": "POOL_HOST", + "poolHost": "POOL_URL", "coin": "electronero", "symbol": "ETNX", @@ -27,8 +27,9 @@ "poolServer": { "enabled": true, "clusterForks": "auto", - "poolAddress": "ETNX_ADDRESS", + "poolAddress": "POOL_ADDRESS", "intAddressPrefix": 18019, + "subAddressPrefix": 42, "blockRefreshInterval": 1000, "minerTimeout": 900, "sslCert": "cert.pem", @@ -107,15 +108,15 @@ "payments": { "enabled": true, "interval": 2160, - "maxAddresses": 50, + "maxAddresses": 10, "mixin": 2, "priority": 0, "transferFee": 20000, "dynamicTransferFee": true, "minerPayFee" : true, "minPayment": 100000, - "maxPayment": 100000000, - "maxTransactionAmount": 500000000, + "maxPayment": 25000000, + "maxTransactionAmount": 50000000, "denomination": 100 }, @@ -125,7 +126,7 @@ "depth": 30, "poolFee": 0.0, "devDonation": 0.0, - "networkFee": 5.0 + "networkFee": 0.0 }, "api": { @@ -136,7 +137,7 @@ "port": 8117, "blocks": 30, "payments": 30, - "password": "admin_dash_pass", + "password": "admin_pass", "ssl": true, "sslPort": 8119, "sslCert": "cert.pem", @@ -147,12 +148,12 @@ "daemon": { "host": "127.0.0.1", - "port": 20393 + "port": 3000 }, "wallet": { "host": "127.0.0.1", - "port": 1030 + "port": 3000 }, "redis": { @@ -223,9 +224,9 @@ "telegram": { "enabled": true, - "botName": "BOT_NAME", - "token": "BOT_TOKEN", - "channel": "BOT_CHANNEL", + "botName": "POOL_BOT", + "token": "POOL_BOT_TOKEN", + "channel": "POOL_BOT_CHANNEL", "channelStats": { "enabled": false, "interval": 30 From e7319deddcf6df88ca1220c584ff0b0b13fbd1bc Mon Sep 17 00:00:00 2001 From: Interchained Date: Mon, 16 Jul 2018 09:19:27 -0400 Subject: [PATCH 20/98] Update config.ETNX.json --- config.ETNX.json | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/config.ETNX.json b/config.ETNX.json index 7e8199a05..d451ffb77 100644 --- a/config.ETNX.json +++ b/config.ETNX.json @@ -1,5 +1,5 @@ { - "poolHost": "POOL_HOST", + "poolHost": "POOL_URL", "coin": "electronero", "symbol": "ETNX", @@ -9,8 +9,8 @@ "daemonType": "default", "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, + "cnVariant": 4, + "cnBlobType": 3, "logging": { "files": { @@ -27,8 +27,9 @@ "poolServer": { "enabled": true, "clusterForks": "auto", - "poolAddress": "ETNX_ADDRESS", + "poolAddress": "POOL_ADDRESS", "intAddressPrefix": 18019, + "subAddressPrefix": 42, "blockRefreshInterval": 1000, "minerTimeout": 900, "sslCert": "cert.pem", @@ -107,15 +108,15 @@ "payments": { "enabled": true, "interval": 2160, - "maxAddresses": 50, + "maxAddresses": 10, "mixin": 2, "priority": 0, "transferFee": 20000, "dynamicTransferFee": true, "minerPayFee" : true, "minPayment": 100000, - "maxPayment": 100000000, - "maxTransactionAmount": 500000000, + "maxPayment": 25000000, + "maxTransactionAmount": 50000000, "denomination": 100 }, @@ -125,7 +126,7 @@ "depth": 30, "poolFee": 0.0, "devDonation": 0.0, - "networkFee": 5.0 + "networkFee": 0.0 }, "api": { @@ -136,7 +137,7 @@ "port": 8117, "blocks": 30, "payments": 30, - "password": "admin_dash_pass", + "password": "admin_pass", "ssl": true, "sslPort": 8119, "sslCert": "cert.pem", @@ -147,12 +148,12 @@ "daemon": { "host": "127.0.0.1", - "port": 20393 + "port": 3000 }, "wallet": { "host": "127.0.0.1", - "port": 1030 + "port": 3000 }, "redis": { @@ -223,9 +224,9 @@ "telegram": { "enabled": true, - "botName": "BOT_NAME", - "token": "BOT_TOKEN", - "channel": "BOT_CHANNEL", + "botName": "POOL_BOT", + "token": "POOL_BOT_TOKEN", + "channel": "POOL_BOT_CHANNEL", "channelStats": { "enabled": false, "interval": 30 From 48e5dcfd5f65a9e8e1b5cefceddd45b287a0dc9b Mon Sep 17 00:00:00 2001 From: David Young Date: Sat, 21 Jul 2018 21:27:36 +1200 Subject: [PATCH 21/98] Send password if defined --- lib/apiInterfaces.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index 45bec9875..ca516cf8e 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -57,13 +57,16 @@ function jsonHttpRequest(host, port, data, callback, path){ /** * Send RPC request **/ -function rpc(host, port, method, params, callback){ +function rpc(host, port, method, params, callback, password){ var data = JSON.stringify({ id: "0", jsonrpc: "2.0", method: method, params: params }); + if (password !== undefined) { + request['password'] = password; + } jsonHttpRequest(host, port, data, function(error, replyJson){ if (error){ callback(error, {}); From 350edcec30fa751d9bd0d2257ede4cc620e168e0 Mon Sep 17 00:00:00 2001 From: David Young Date: Sat, 21 Jul 2018 22:18:25 +1200 Subject: [PATCH 22/98] Corrected RPC request variable name --- lib/apiInterfaces.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index ca516cf8e..3a8b8ff95 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -65,7 +65,7 @@ function rpc(host, port, method, params, callback, password){ params: params }); if (password !== undefined) { - request['password'] = password; + data['password'] = password; } jsonHttpRequest(host, port, data, function(error, replyJson){ if (error){ From 9d25715cadcbfe7b7018cc8a56c02cee4f01ec71 Mon Sep 17 00:00:00 2001 From: David Young Date: Sun, 22 Jul 2018 11:45:22 +1200 Subject: [PATCH 23/98] Fix JSONify of passworded request --- lib/apiInterfaces.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index 3a8b8ff95..132dafeea 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -59,14 +59,16 @@ function jsonHttpRequest(host, port, data, callback, path){ **/ function rpc(host, port, method, params, callback, password){ var data = JSON.stringify({ + var request = { id: "0", jsonrpc: "2.0", method: method, params: params - }); + }; if (password !== undefined) { - data['password'] = password; + request['password'] = password; } + var data = JSON.stringify(request); jsonHttpRequest(host, port, data, function(error, replyJson){ if (error){ callback(error, {}); From ed1e14c915889f5d7dc02330ef31a3a70b457d06 Mon Sep 17 00:00:00 2001 From: David Young Date: Sun, 22 Jul 2018 12:04:01 +1200 Subject: [PATCH 24/98] Corrected RPC function --- lib/apiInterfaces.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index 132dafeea..27110bc7f 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -58,7 +58,6 @@ function jsonHttpRequest(host, port, data, callback, path){ * Send RPC request **/ function rpc(host, port, method, params, callback, password){ - var data = JSON.stringify({ var request = { id: "0", jsonrpc: "2.0", From 2245d4be8e0a2cf3f5aaaf54314f8389756d5835 Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 31 Jul 2018 15:39:55 -0400 Subject: [PATCH 25/98] fix difficulty target, 60 seconds --- config.ETNX.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.ETNX.json b/config.ETNX.json index d451ffb77..7051295fd 100644 --- a/config.ETNX.json +++ b/config.ETNX.json @@ -5,7 +5,7 @@ "symbol": "ETNX", "coinUnits": 100, "coinDecimalPlaces": 2, - "coinDifficultyTarget": 120, + "coinDifficultyTarget": 60, "daemonType": "default", "cnAlgorithm": "cryptonight", From 41857a2d3b72f65a6ec4620d7c48146254d41723 Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 31 Jul 2018 16:04:30 -0400 Subject: [PATCH 26/98] decrypt the subaddress base58 with the CN-util, [test] --- lib/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 5be59c1da..9a5f446c7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -26,7 +26,8 @@ exports.instanceId = function() { **/ var addressBase58Prefix = parseInt(cnUtil.address_decode(new Buffer(config.poolServer.poolAddress)).toString()); var integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1; -var subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(config.poolServer.subAddressPrefix) : "N/A"; +var subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(cnUtil.address_decode(new Buffer(config.poolServer.subAddressPrefix)).toString()) : "N/A"; + // Get address prefix function getAddressPrefix(address) { From af583def4217ea2ed12ed7f2eba94c0df05609f4 Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 31 Jul 2018 17:40:14 -0400 Subject: [PATCH 27/98] revert --- lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 9a5f446c7..dc13c270c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -26,7 +26,7 @@ exports.instanceId = function() { **/ var addressBase58Prefix = parseInt(cnUtil.address_decode(new Buffer(config.poolServer.poolAddress)).toString()); var integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1; -var subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(cnUtil.address_decode(new Buffer(config.poolServer.subAddressPrefix)).toString()) : "N/A"; +var subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(config.poolServer.subAddressPrefix) : "N/A"; // Get address prefix From 799e58705101eb38ea8f7ffb1d4ab7c2f8e440cd Mon Sep 17 00:00:00 2001 From: ArqTras <33489188+ArqTras@users.noreply.github.com> Date: Sun, 19 Aug 2018 10:31:04 +0200 Subject: [PATCH 28/98] Update to intucoin cn-lite variant 1 --- config_examples/intucoin.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config_examples/intucoin.json b/config_examples/intucoin.json index 9c65a8a09..b702ccb10 100644 --- a/config_examples/intucoin.json +++ b/config_examples/intucoin.json @@ -8,9 +8,9 @@ "coinDifficultyTarget": 120, "daemonType": "forknote", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 0, + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 2, "logging": { "files": { From 02b034215fb133912e9700e48047d21eb03dd5aa Mon Sep 17 00:00:00 2001 From: ArqTras <33489188+ArqTras@users.noreply.github.com> Date: Wed, 22 Aug 2018 09:46:38 +0200 Subject: [PATCH 29/98] Aeon Config file --- config_examples/aeon.json | 304 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 config_examples/aeon.json diff --git a/config_examples/aeon.json b/config_examples/aeon.json new file mode 100644 index 000000000..66294ea56 --- /dev/null +++ b/config_examples/aeon.json @@ -0,0 +1,304 @@ +{ + "poolHost": "your.pool.host", + + "coin": "aeon", + "symbol": "AEON", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 12, + "coinDifficultyTarget": 240, + + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } +], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 20, + "mixin": 3, + "priority": 0, + "transferFee": 1000000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 10000000000, + "maxTransactionAmount": 0, + "denomination": 10000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 3, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 11181 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 11188 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } +} From a66d250946f492c7edc54aec46c54aebece1418b Mon Sep 17 00:00:00 2001 From: ArqTras <33489188+ArqTras@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:22:11 +0200 Subject: [PATCH 30/98] Decimals --- config_examples/aeon.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config_examples/aeon.json b/config_examples/aeon.json index 66294ea56..a34bcce39 100644 --- a/config_examples/aeon.json +++ b/config_examples/aeon.json @@ -108,10 +108,10 @@ "maxAddresses": 20, "mixin": 3, "priority": 0, - "transferFee": 1000000, + "transferFee": 10000000000, "dynamicTransferFee": true, "minerPayFee" : true, - "minPayment": 10000000000, + "minPayment": 250000000000, "maxTransactionAmount": 0, "denomination": 10000000000 }, From 4d746edf53a58c6f1cddcf68205ec8f0c4a59263 Mon Sep 17 00:00:00 2001 From: ArqTras <33489188+ArqTras@users.noreply.github.com> Date: Tue, 28 Aug 2018 22:30:19 +0200 Subject: [PATCH 31/98] Create xmv.json --- config_examples/xmv.json | 318 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 config_examples/xmv.json diff --git a/config_examples/xmv.json b/config_examples/xmv.json new file mode 100644 index 000000000..cad40bfad --- /dev/null +++ b/config_examples/xmv.json @@ -0,0 +1,318 @@ +{ + "poolHost": "your.pool.host", + + "coin": "moneroV", + "symbol": "XMV", + "coinUnits": 100000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 19, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 1000000000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 100000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 18081 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 18082 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From 724cca13ceabc0a53c0ae9015d8f17b88690d478 Mon Sep 17 00:00:00 2001 From: ArqTras <33489188+ArqTras@users.noreply.github.com> Date: Thu, 30 Aug 2018 02:09:27 +0200 Subject: [PATCH 32/98] Luka Coin --- config_examples/luka.json | 318 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 config_examples/luka.json diff --git a/config_examples/luka.json b/config_examples/luka.json new file mode 100644 index 000000000..19ab4b00c --- /dev/null +++ b/config_examples/luka.json @@ -0,0 +1,318 @@ +{ + "poolHost": "69.30.193.98", + + "coin": "Luka", + "symbol": "LUK", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 60, + + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "**your pool wallet**", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } +], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 1, + "priority": 0, + "transferFee": 10, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 25000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 0.0, + "fixBlockHeightRPC": true + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 52421 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 52424 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 5, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From ca3ec953d04e4f99b52b7c792d8ed8a9b0141e13 Mon Sep 17 00:00:00 2001 From: ArqTras <33489188+ArqTras@users.noreply.github.com> Date: Thu, 30 Aug 2018 02:43:54 +0200 Subject: [PATCH 33/98] ip --- config_examples/luka.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_examples/luka.json b/config_examples/luka.json index 19ab4b00c..6d66fbf29 100644 --- a/config_examples/luka.json +++ b/config_examples/luka.json @@ -1,5 +1,5 @@ { - "poolHost": "69.30.193.98", + "poolHost": "your ip", "coin": "Luka", "symbol": "LUK", From 76682d417a5455d9337ed3d247fa2dde03c59873 Mon Sep 17 00:00:00 2001 From: ArqTras <33489188+ArqTras@users.noreply.github.com> Date: Thu, 30 Aug 2018 23:55:42 +0200 Subject: [PATCH 34/98] Fee for payments Use walletd wit bind ports for daemon and for rpc wallet --- config_examples/luka.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_examples/luka.json b/config_examples/luka.json index 6d66fbf29..a639e43ca 100644 --- a/config_examples/luka.json +++ b/config_examples/luka.json @@ -110,7 +110,7 @@ "maxAddresses": 50, "mixin": 1, "priority": 0, - "transferFee": 10, + "transferFee": 100000, "dynamicTransferFee": true, "minerPayFee" : true, "minPayment": 25000000, From 8d55553cc8d35117ed9af7285dfcb8e8970af49f Mon Sep 17 00:00:00 2001 From: prox443 <34525734+prox443@users.noreply.github.com> Date: Tue, 11 Sep 2018 23:24:39 +0200 Subject: [PATCH 35/98] Update bbscoin.json Now we need BlobType:2 --- config_examples/bbscoin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_examples/bbscoin.json b/config_examples/bbscoin.json index d01f817a8..57adb424f 100644 --- a/config_examples/bbscoin.json +++ b/config_examples/bbscoin.json @@ -10,7 +10,7 @@ "daemonType": "default", "cnAlgorithm": "cryptonight", "cnVariant": 1, - "cnBlobType": 0, + "cnBlobType": 2, "logging": { "files": { From 8ce1b4d161c8402d7f3d04693829c56f010b0827 Mon Sep 17 00:00:00 2001 From: prox443 <34525734+prox443@users.noreply.github.com> Date: Sun, 23 Sep 2018 13:23:24 +0200 Subject: [PATCH 36/98] Update bbscoin.json fork at block 180 000 --- config_examples/bbscoin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_examples/bbscoin.json b/config_examples/bbscoin.json index 57adb424f..e04a5c534 100644 --- a/config_examples/bbscoin.json +++ b/config_examples/bbscoin.json @@ -8,7 +8,7 @@ "coinDifficultyTarget": 120, "daemonType": "default", - "cnAlgorithm": "cryptonight", + "cnAlgorithm": "cryptonight_light", "cnVariant": 1, "cnBlobType": 2, From 98ed692092d66ee2c910465c1642642731c32da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20=C5=A0erejch?= Date: Tue, 16 Oct 2018 13:23:36 +0200 Subject: [PATCH 37/98] Create citicash.json feature: New config example for CitiCash (CCH). --- config_examples/citicash.json | 312 ++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 config_examples/citicash.json diff --git a/config_examples/citicash.json b/config_examples/citicash.json new file mode 100644 index 000000000..10c106277 --- /dev/null +++ b/config_examples/citicash.json @@ -0,0 +1,312 @@ +{ + "poolHost": "your.pool.host", + + "coin": "CitiCash", + "symbol": "CCH", + "coinUnits": 1000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 60, + + "daemonType": "default", + "cnAlgorithm": "cryptonight_heavy", + "cnVariant": null, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **"", + "intAddressPrefix": 31572, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 5, + "mixin": 6, + "priority": 0, + "transferFee": 74000000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 5000000000, + "maxPayment": 50000000000000, + "maxTransactionAmount": 0, + "denomination": 5000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 1.0, + "devDonation": 0.0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": true, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 1234 + }, + + "wallet": { + "host": "localhost", + "port": 2345 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From b76e169a44e5a786e263debec761066bff4efe8e Mon Sep 17 00:00:00 2001 From: parsicoin Date: Wed, 31 Oct 2018 01:28:35 +0330 Subject: [PATCH 38/98] ParsiCoin Config --- config_examples/parsicoin.json | 312 +++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 config_examples/parsicoin.json diff --git a/config_examples/parsicoin.json b/config_examples/parsicoin.json new file mode 100644 index 000000000..00ad269b5 --- /dev/null +++ b/config_examples/parsicoin.json @@ -0,0 +1,312 @@ +{ + "poolHost": "pool.website.com", + + "coin": "parsicoin", + "symbol": "PARS", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "**Your Pool Wallet Address**", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 120, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "." + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "+" + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 300, + "maxAddresses": 50, + "mixin": 1, + "priority": 0, + "transferFee": 10000000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 10000000000, + "maxPayment": 100000000000, + "maxTransactionAmount": 100000000000, + "denomination": 100000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 2.0, + "devDonation": 0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 900, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "CHANGE ME", + "ssl": true, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 18230 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 18231 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} + From 777398db78ccd4576edc1092ea782fa15d408637 Mon Sep 17 00:00:00 2001 From: "ParsiCoin (PARS)" <36328859+ParsiCoin@users.noreply.github.com> Date: Sun, 11 Nov 2018 23:03:47 +0330 Subject: [PATCH 39/98] Update parsicoin.json --- config_examples/parsicoin.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config_examples/parsicoin.json b/config_examples/parsicoin.json index 00ad269b5..57e882a70 100644 --- a/config_examples/parsicoin.json +++ b/config_examples/parsicoin.json @@ -105,7 +105,7 @@ "payments": { "enabled": true, - "interval": 300, + "interval": 900, "maxAddresses": 50, "mixin": 1, "priority": 0, @@ -113,8 +113,8 @@ "dynamicTransferFee": true, "minerPayFee" : true, "minPayment": 10000000000, - "maxPayment": 100000000000, - "maxTransactionAmount": 100000000000, + "maxPayment": 10000000000000, + "maxTransactionAmount": 10000000000000, "denomination": 100000000 }, From fe91ae61d9e27146fef18687a65053f00ef4421a Mon Sep 17 00:00:00 2001 From: UltraNote Date: Sat, 1 Dec 2018 02:36:40 +0100 Subject: [PATCH 40/98] Added UltraNote (XUN) --- config_examples/ultranote.json | 299 +++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 config_examples/ultranote.json diff --git a/config_examples/ultranote.json b/config_examples/ultranote.json new file mode 100644 index 000000000..f33c20c13 --- /dev/null +++ b/config_examples/ultranote.json @@ -0,0 +1,299 @@ +{ + "poolHost": "delta.ultranote.org", + + "coin": "ultranote", + "symbol": "XUN", + "coinUnits": 1000000, + "coinDecimalPlaces": 6, + "coinDifficultyTarget": 120, + + "daemonType": "default", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "Pool Wallet Address", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "ports": [ + { + "port": 5555, + "difficulty": 10000, + "desc": "Low end hardware" + }, + { + "port": 7777, + "difficulty": 50000, + "desc": "Mid range hardware" + }, + { + "port": 8888, + "difficulty": 100000, + "desc": "High end hardware", + "hidden": false + } + ], + "varDiff": { + "minDiff": 1000, + "maxDiff": 200000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 120, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 100000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 5000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 1, + "devDonation": 0.2, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "pass", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 31000 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8082 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From f5c77dda4b52da2eea6dc6c48d2852b290f93abd Mon Sep 17 00:00:00 2001 From: parsicoin Date: Tue, 4 Dec 2018 15:11:00 +0330 Subject: [PATCH 41/98] monero-v8 --- config_examples/monero.json | 2 +- website_example/pages/getting_started.html | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/config_examples/monero.json b/config_examples/monero.json index a69a73bac..9f40ce789 100644 --- a/config_examples/monero.json +++ b/config_examples/monero.json @@ -9,7 +9,7 @@ "daemonType": "default", "cnAlgorithm": "cryptonight", - "cnVariant": 1, + "cnVariant": 8, "cnBlobType": 0, "logging": { diff --git a/website_example/pages/getting_started.html b/website_example/pages/getting_started.html index baf8af8bc..ca5663cce 100644 --- a/website_example/pages/getting_started.html +++ b/website_example/pages/getting_started.html @@ -327,7 +327,10 @@

Mining Applications

} else if (cnVariant === 4) { algorithm = 'Cryptonight Fast (Masari)'; xmrstakAlgo = 'cryptonight_masari'; - } else { + } else if (cnVariant === 8) { + algorithm = 'Cryptonight Variant 2 (Cryptonight v8)'; + xmrstakAlgo = 'cryptonight_v8'; + } else { algorithm = 'Cryptonight (Original)'; xmrstakAlgo = 'cryptonight'; } From f43bdeeb62091bb92c50e62c9f48b6e28a56f583 Mon Sep 17 00:00:00 2001 From: ma7555 <7144929+ma7555@users.noreply.github.com> Date: Mon, 17 Dec 2018 14:13:22 +0200 Subject: [PATCH 42/98] Update monero.json update to CN V8 --- config_examples/monero.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_examples/monero.json b/config_examples/monero.json index a69a73bac..9f40ce789 100644 --- a/config_examples/monero.json +++ b/config_examples/monero.json @@ -9,7 +9,7 @@ "daemonType": "default", "cnAlgorithm": "cryptonight", - "cnVariant": 1, + "cnVariant": 8, "cnBlobType": 0, "logging": { From 31f1b5009d19eb2257d68c20a484038d28a58ba7 Mon Sep 17 00:00:00 2001 From: ma7555 <7144929+ma7555@users.noreply.github.com> Date: Tue, 18 Dec 2018 10:38:41 +0200 Subject: [PATCH 43/98] Update loki.json fix error failed to parse a block and pool crashing --- config_examples/loki.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_examples/loki.json b/config_examples/loki.json index bbc1d5f06..f3203ae70 100644 --- a/config_examples/loki.json +++ b/config_examples/loki.json @@ -10,7 +10,7 @@ "daemonType": "default", "cnAlgorithm": "cryptonight_heavy", "cnVariant": null, - "cnBlobType": 0, + "cnBlobType": 5, "logging": { "files": { From 924764fc954a430a2aea25da80038ea733240b53 Mon Sep 17 00:00:00 2001 From: nthon Date: Sat, 22 Dec 2018 13:32:37 +0700 Subject: [PATCH 44/98] Create arto.json --- config_examples/arto.json | 313 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 config_examples/arto.json diff --git a/config_examples/arto.json b/config_examples/arto.json new file mode 100644 index 000000000..dc53f3360 --- /dev/null +++ b/config_examples/arto.json @@ -0,0 +1,313 @@ +{ + "poolHost": "", + + "coin": "arto", + "symbol": "RTO", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 90, + + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight", + "cnVariant": 7, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 25000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 200000, + "targetTime": 100, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 0, + "priority": 0, + "transferFee": 1000000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 20000000, + "maxPayment": 50000000000, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 1.0, + "devDonation": 0.0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": true, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": false + }, + + "daemon": { + "host": "127.0.0.1", + "port": 19451 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 19452 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From 9a0aabc5c017446da89c9017eb7702aee33da89b Mon Sep 17 00:00:00 2001 From: Interchained Date: Tue, 25 Dec 2018 21:41:40 -0500 Subject: [PATCH 45/98] Update electronero.json --- config_examples/electronero.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config_examples/electronero.json b/config_examples/electronero.json index d451ffb77..ca6db3bc8 100644 --- a/config_examples/electronero.json +++ b/config_examples/electronero.json @@ -4,8 +4,8 @@ "coin": "electronero", "symbol": "ETNX", "coinUnits": 100, - "coinDecimalPlaces": 2, - "coinDifficultyTarget": 120, + "coinDecimalPlaces": 8, + "coinDifficultyTarget": 60, "daemonType": "default", "cnAlgorithm": "cryptonight", From 1e04a3c800c1b13c5b9072eea0bfe76f797dc91c Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sat, 29 Dec 2018 14:13:40 -0400 Subject: [PATCH 46/98] Add ability to use first vout for block reward Loki, in particular, no longer uses a constant fee: most blocks don't include the governance reward, but then there is an occassional very large reward with includes a large governance reward. The proper solution here is to use the first vout to extract the miner reward, which this patch adds support for. --- README.md | 3 ++- config_examples/loki.json | 3 ++- lib/api.js | 26 +++++++++++++++++++++++++- lib/blockUnlocker.js | 20 ++++++++++++++++---- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c3b69bb9d..842dba782 100644 --- a/README.md +++ b/README.md @@ -359,7 +359,8 @@ Explanation for each field: "depth": 60, "poolFee": 0.8, // 0.8% pool fee (1% total fee total including donations) "devDonation": 0.2, // 0.2% donation to send to pool dev - "networkFee": 0.0, // Network/Governance fee (used by some coins like Loki) + "networkFee": 0.0, // Network/Governance fee (used by some coins) + "useFirstVout": false, // Should be true for a coin like Loki that has multiple block reward recipients where first is miner reward /* Some forknote coins have an issue with block height in RPC request, to fix you can enable this option. See: https://github.com/forknote/forknote-pool/issues/48 */ diff --git a/config_examples/loki.json b/config_examples/loki.json index f3203ae70..1ff7267e4 100644 --- a/config_examples/loki.json +++ b/config_examples/loki.json @@ -125,7 +125,8 @@ "depth": 60, "poolFee": 0.8, "devDonation": 0.2, - "networkFee": 5.0 + "networkFee": 0.0, + "useFirstVout": true }, "api": { diff --git a/lib/api.js b/lib/api.js index 48de03f64..cc5c3413b 100644 --- a/lib/api.js +++ b/lib/api.js @@ -480,7 +480,31 @@ function getLastBlockData(callback) { return; } var blockHeader = reply.block_header; - callback(null, { + if (config.blockUnlocker.useFirstVout) { + apiInterfaces.rpcDaemon('getblock', { height: blockHeader.height }, function(error, result) { + if (error) { + log('error', logSystem, 'Error getting last block details: %j', [error]); + callback(true); + return; + } + var vout = JSON.parse(result.json).miner_tx.vout; + if (!vout.length) { + log('error', logSystem, 'Error: tx at height %s has no vouts!', [blockHeader.height]); + callback(true); + return; + } + callback(null, { + difficulty: blockHeader.difficulty, + height: blockHeader.height, + timestamp: blockHeader.timestamp, + reward: vout[0].amount, + hash: blockHeader.hash + }); + }); + return; + } + + callback(null, { difficulty: blockHeader.difficulty, height: blockHeader.height, timestamp: blockHeader.timestamp, diff --git a/lib/blockUnlocker.js b/lib/blockUnlocker.js index f268040a3..b76aed047 100644 --- a/lib/blockUnlocker.js +++ b/lib/blockUnlocker.js @@ -65,15 +65,16 @@ function runInterval(){ async.filter(blocks, function(block, mapCback){ var daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; var blockHeight = (daemonType === "forknote" || daemonType === "bytecoin" || config.blockUnlocker.fixBlockHeightRPC) ? block.height + 1 : block.height; - apiInterfaces.rpcDaemon('getblockheaderbyheight', {height: blockHeight}, function(error, result){ + var rpcmethod = config.blockUnlocker.useFirstVout ? 'getblock' : 'getblockheaderbyheight'; + apiInterfaces.rpcDaemon(rpcmethod, {height: blockHeight}, function(error, result){ if (error){ - log('error', logSystem, 'Error with getblockheaderbyheight RPC request for block %s - %j', [block.serialized, error]); + log('error', logSystem, 'Error with %s RPC request for block %s - %j', [rpcmethod, block.serialized, error]); block.unlocked = false; mapCback(); return; } if (!result.block_header){ - log('error', logSystem, 'Error with getblockheaderbyheight, no details returned for %s - %j', [block.serialized, result]); + log('error', logSystem, 'Error with %s, no details returned for %s - %j', [rpcmethod, block.serialized, result]); block.unlocked = false; mapCback(); return; @@ -81,7 +82,18 @@ function runInterval(){ var blockHeader = result.block_header; block.orphaned = blockHeader.hash === block.hash ? 0 : 1; block.unlocked = blockHeader.depth >= config.blockUnlocker.depth; - block.reward = blockHeader.reward; + if (config.blockUnlocker.useFirstVout) { + var vout = JSON.parse(result.json).miner_tx.vout; + if (!vout.length) { + log('error', logSystem, 'Error: tx at height %s has no vouts!', [blockHeight]); + block.unlocked = false; + mapCback(); + return; + } + block.reward = vout[0].amount; + } else { + block.reward = blockHeader.reward; + } if (config.blockUnlocker.networkFee) { var networkFeePercent = config.blockUnlocker.networkFee / 100; block.reward = block.reward - (block.reward * networkFeePercent); From ad26cd4bc0dffd1bb0e7967717a3d576dc40e28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86JT?= <34797913+aejontargaryen@users.noreply.github.com> Date: Sun, 13 Jan 2019 21:22:46 -0500 Subject: [PATCH 47/98] Create dragonglass.json {Dragonglass} DRGL CryptoNote CNv8 --- config_examples/dragonglass.json | 312 +++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 config_examples/dragonglass.json diff --git a/config_examples/dragonglass.json b/config_examples/dragonglass.json new file mode 100644 index 000000000..2869f1a71 --- /dev/null +++ b/config_examples/dragonglass.json @@ -0,0 +1,312 @@ +{ + "poolHost": "yourpool.host", + + "coin": "Dragonglass", + "symbol": "DRGL", + "coinUnits": 10000000, + "coinDecimalPlaces": 7, + "coinDifficultyTarget": 81, + + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 8, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "dRGL....", + "intAddressPrefix": 251, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 1000, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 60, + "maxAddresses": 50, + "mixin": 1, + "priority": 0, + "transferFee": 800000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 100000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.5, + "devDonation": 0.0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "yourpassword", + "ssl": false, + "sslPort": 8119, + "sslCert": "/etc/ssl/abc.crt", + "sslKey": "/etc/ssl/private/abc.key", + "sslCA": "/etc/ssl/abc-ca_bundle.crt", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 8180 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8181 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From bd67b8c2968f13815d611375ba5cb3924b8d28d0 Mon Sep 17 00:00:00 2001 From: muscleman Date: Sun, 20 Jan 2019 17:46:43 -0600 Subject: [PATCH 48/98] Update getting_started.html updated for Stellite and Monero v8 --- website_example/pages/getting_started.html | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/website_example/pages/getting_started.html b/website_example/pages/getting_started.html index 56e9bb95b..b83a033e6 100644 --- a/website_example/pages/getting_started.html +++ b/website_example/pages/getting_started.html @@ -377,23 +377,31 @@

Mining Applications

} } else if (cnAlgorithm == "cryptonight_heavy") { - algorithm = 'Cryptonight Heavy'; - xmrstakAlgo = 'cryptonight_heavy'; + if (cnVariant === 1) { + algorithm = 'Crytonight Heavy (Haven)'; + xmrstakAlgo = 'haven'; + } else { + algorithm = 'Cryptonight Heavy'; + xmrstakAlgo = 'cryptonight_heavy'; + } } else { if (cnVariant === 1) { algorithm = 'Cryptonight (Monero v7)'; xmrstakAlgo = 'cryptonight_v7'; - } else if (cnVariant === 3) { + } else if (cnVariant === 9) { algorithm = 'Cryptonight (Stellite v7)'; xmrstakAlgo = 'cryptonight_v7_stellite'; } else if (cnVariant === 4) { algorithm = 'Cryptonight Fast (Masari)'; xmrstakAlgo = 'cryptonight_masari'; - } else if (cnVariant === 8) { - algorithm = 'Cryptonight Variant 2 (Cryptonight v8)'; - xmrstakAlgo = 'cryptonight_v8'; - } else { + } else if (cnVariant === 2) { + algorithm = 'Cryptonight (Monero v8)'; + xmrstakAlgo = 'cryptonight_v8'; + } else if (cnVariant === 8) { + algorithm = 'Cryptonight (Monero v8)'; + xmrstakAlgo = 'cryptonight_v8'; + } else { algorithm = 'Cryptonight (Original)'; xmrstakAlgo = 'cryptonight'; } From 0c06a5926713ae854887c9cef1404b41aa94422f Mon Sep 17 00:00:00 2001 From: mosu forge Date: Fri, 8 Feb 2019 02:31:54 +0000 Subject: [PATCH 49/98] Add RPC authorization for wallet-rpc --- config_examples/monero.json | 4 +- lib/apiInterfaces.js | 103 +++++++++++++++++++++--------------- package.json | 3 ++ 3 files changed, 66 insertions(+), 44 deletions(-) diff --git a/config_examples/monero.json b/config_examples/monero.json index 9f40ce789..a8876595a 100644 --- a/config_examples/monero.json +++ b/config_examples/monero.json @@ -152,7 +152,9 @@ "wallet": { "host": "127.0.0.1", - "port": 18082 + "port": 18082, + "user": "", + "pass": "" }, "redis": { diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index 27110bc7f..bf6a4ea69 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -6,11 +6,16 @@ **/ // Load required modules -var http = require('http'); -var https = require('https'); +var request = require("request-promise"); +var queue = require("promise-queue"); +var http = require("http"); +var https = require("https"); + +var request_agent = new http.Agent({keepAlive: true, maxSockets: 1}) +var request_queue = new queue(1, Infinity) /** - * Send API request using JSON HTTP + * Send generic API request (i.e. market price) **/ function jsonHttpRequest(host, port, data, callback, path){ path = path || '/json_rpc'; @@ -55,47 +60,50 @@ function jsonHttpRequest(host, port, data, callback, path){ } /** - * Send RPC request + * Send RPC request to daemon or wallet **/ -function rpc(host, port, method, params, callback, password){ - var request = { - id: "0", - jsonrpc: "2.0", - method: method, - params: params +function rpc(host, port, method, params={}, auth={}, callback=false) { + var options = { + uri: `http://${host}:${port}/json_rpc`, + method: "POST", + agent: request_agent, + json: { + jsonrpc: "2.0", + id: "0", + method: method + } }; - if (password !== undefined) { - request['password'] = password; + if(Object.keys(params).length !== 0) { + options.json.params = params } - var data = JSON.stringify(request); - jsonHttpRequest(host, port, data, function(error, replyJson){ - if (error){ - callback(error, {}); - return; - } - callback(replyJson.error, replyJson.result) - }); -} - -/** - * Send RPC requests in batch mode - **/ -function batchRpc(host, port, array, callback){ - var rpcArray = []; - for (var i = 0; i < array.length; i++){ - rpcArray.push({ - id: i.toString(), - jsonrpc: "2.0", - method: array[i][0], - params: array[i][1] - }); + if(Object.keys(auth).length !== 0) { + options.auth = auth; + options.auth.sendImmediately = false; } - var data = JSON.stringify(rpcArray); - jsonHttpRequest(host, port, data, callback); + + request_queue.add(() => { + return request(options) + .then((response) => { + if(response.hasOwnProperty("error")) { + if(typeof callback === "function") { + callback(response.error, {}); + } + return; + } + if(typeof callback === "function") { + callback(false, response.result) + } + }).catch(error => { + if(typeof callback === "function") { + callback(error, {}); + } + return; + }); + }); } /** - * Send RPC request to pool API + * Send request to pool API for graphs **/ function poolRpc(host, port, path, callback){ jsonHttpRequest(host, port, '', callback, path); @@ -105,16 +113,25 @@ function poolRpc(host, port, path, callback){ * Exports API interfaces functions **/ module.exports = function(daemonConfig, walletConfig, poolApiConfig){ + return { - batchRpcDaemon: function(batchArray, callback){ - batchRpc(daemonConfig.host, daemonConfig.port, batchArray, callback); - }, rpcDaemon: function(method, params, callback){ - rpc(daemonConfig.host, daemonConfig.port, method, params, callback); + var auth = {}; + if(daemonConfig.hasOwnProperty("user") && daemonConfig.user && + daemonConfig.hasOwnProperty("pass") && daemonConfig.pass) { + auth.user = daemonConfig.user; + auth.pass = daemonConfig.pass; + } + rpc(daemonConfig.host, daemonConfig.port, method, params, auth, callback); }, rpcWallet: function(method, params, callback){ - rpc(walletConfig.host, walletConfig.port, method, params, callback, - walletConfig.password); + var auth = {}; + if(walletConfig.hasOwnProperty("user") && walletConfig.user && + walletConfig.hasOwnProperty("pass") && walletConfig.pass) { + auth.user = walletConfig.user; + auth.pass = walletConfig.pass; + } + rpc(walletConfig.host, walletConfig.port, method, params, auth, callback); }, pool: function(path, callback){ var bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; diff --git a/package.json b/package.json index a8f25a2f2..26779d7d8 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,10 @@ "node-telegram-bot-api": "^0.30.0", "nodemailer": "^2.7.2", "nodemailer-sendmail-transport": "^1.0.2", + "promise-queue": "^2.2.5", "redis": "^2.8.0", + "request": "^2.88.0", + "request-promise": "^4.2.2", "socket.io": "^2.1.1", "time-ago": "^0.2.1" }, From e61ebd435183cd9d4bf1d64f73e7e4aaa3dcd3bb Mon Sep 17 00:00:00 2001 From: qwertycoin-org <36213808+qwertycoin-org@users.noreply.github.com> Date: Sat, 9 Feb 2019 22:06:51 +0100 Subject: [PATCH 50/98] Qwertycoin forked to Cryptonight_Classic Qwertycoin forked back to Cryptonight_Classic --- config_examples/qwertycoin.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config_examples/qwertycoin.json b/config_examples/qwertycoin.json index fda67de4c..2a4fed9af 100644 --- a/config_examples/qwertycoin.json +++ b/config_examples/qwertycoin.json @@ -8,9 +8,9 @@ "coinDifficultyTarget": 120, "daemonType": "default", - "cnAlgorithm": "cryptonight_heavy", + "cnAlgorithm": "cryptonight", "cnVariant": 0, - "cnBlobType": 4, + "cnBlobType": 0, "logging": { "files": { From ffa889c67d2c697e4054f124483867e45cf22527 Mon Sep 17 00:00:00 2001 From: muscleman Date: Sun, 10 Feb 2019 15:18:57 -0600 Subject: [PATCH 51/98] Update configReader.js --- lib/configReader.js | 96 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 18 deletions(-) diff --git a/lib/configReader.js b/lib/configReader.js index bb2162751..3c9820b8d 100644 --- a/lib/configReader.js +++ b/lib/configReader.js @@ -6,18 +6,18 @@ **/ // Load required modules -var fs = require('fs'); +let fs = require('fs'); // Set pool software version -global.version = "v1.3.5"; +global.version = "v1.4.0"; /** * Load pool configuration **/ // Get configuration file path -var configFile = (function(){ - for (var i = 0; i < process.argv.length; i++){ +let configFile = (function(){ + for (let i = 0; i < process.argv.length; i++){ if (process.argv[i].indexOf('-config=') === 0) return process.argv[i].split('=')[1]; } @@ -37,25 +37,85 @@ catch(e){ * Developper donation addresses -- thanks for supporting my works! **/ -var donationAddresses = { - BTC: '17XRyHm2gWAj2yfbyQgqxm25JGhvjYmQjm', +let donationAddresses = { + BTC: '34GDVuVbuxyYdR8bPZ7g6r12AhPPCrNfXt', BCH: 'qpl0gr8u3yu7z4nzep955fqy3w8m6w769sec08u3dp', - ETH: '0x83ECF65934690D132663F10a2088a550cA201353', - LTC: 'LS9To9u2C95VPHKauRMEN5BLatC8C1k4F1', - DERO: 'dERojUtWgEsAGjTXEJyezgSqvpEEfRGiWCRxuELx2PXa9gopNwNr7YGPAFNmJzzWgW84wGJ84RS8jAp1GesTeXgY7VDJMBGWYt', - GRFT: 'GBqRuitSoU3PFPBAkXMEnLdBRWXH4iDSD6RDxnQiEFjVJhWUi1UuqfV5EzosmaXgpPGE6JJQjMYhZZgWY8EJQn8jQTsuTit', - ITNS: 'iz4fRGV8XsRepDtnK8XQDpHc3TbtciQWQ5Z9285qihDkCAvB9VX1yKt6qUCY6sp2TCC252SQLHrjmeLuoXsv4aF42YZtnZQ53', - MSR: '5n7mffxVT9USrq7tcG3TM8HL5yAz7MirUWypXXJfHrNfTcjNtDouLAAGex8s8htu4vBpmMXFzay8KG3jYGMFhYPr2aMbN6i', - XMR: '49WyMy9Q351C59dT913ieEgqWjaN12dWM5aYqJxSTZCZZj1La5twZtC3DyfUsmVD3tj2Zud7m6kqTVDauRz53FqA9zphHaj', - SUMO: 'Sumoo4mVXMfYw2PFtPRXzviHfESnx5aW6VrMLoYVeVubYH6snAwr6D9R8j7fuvQvjifDqLLKH1KtMXuv7iHGgCM1fd4spVrvP1T', - XHV: 'hvxy2RAzE7NfXPLE3AmsuRaZztGDYckCJ14XMoWa6BUqGrGYicLCcjDEjhjGAQaAvHYGgPD7cGUwcYP7nEUs8u6w3uaap9UZTf', - XTL: 'Se45GzgpFG3CnvYNwEFnxiRHD2x7YzRnhFLdxjUqXdbv3ysNbfW5U7aUdn87RgMRPM7xwN6CTbXNc7nL5QUgcww11bDeypTe1' + ETH: '0xd4d9a4f22475039f115824b15999a5a8143d424c', + LTC: 'LW169WygGDMBN1PGSr8kNbrFBx94emGWfB', + DERO: 'dERirD3WyQi4udWH7478H66Ryqn3syEU8bywCQEu3k5ULohQRcz4uoXP12NjmN4STmEDbpHZWqa7bPRiHNFPFgTBPmcBmB4yyCF8mZmNUanDb', + GRFT: 'GMPHYf5KRkcAyik7Jw9oHRfJtUdw2Kj5f4VTFJ25AaFVYxofetir8Cnh7S76Q854oMXzwaguL8p5KEz1tm3rn1SA6r6p9dMjuV81yqXCgi', + LTHN: 'NaWe5B5NqvZ3TV2Mj1pxYtTgrnTBwQDMDNtqVzMR6Xa5ejxu6hbi6KULHTqd732ebc5qTHvKXonokghUBd3pjLa8czn8PNg57mR2XqEcvr7w', + MSR: '5t5mEm254JNJ9HqRjY9vCiTE8aZALHX3v8TqhyQ3TTF9VHKZQXkRYjPDweT9kK4rJw7dDLtZXGjav2z9y24vXCdRc3DY4daikoNTeK1v4e', + XMR: '4Cf2TfMKhCgJ2vsM3HeBUnYe52tXrvv8X1ajjuQEMUQ8iU8kvUzCSsCEacxFhEmeb2JgPpQ5chdyw3UiTfUgapJBhHdmH87gYyoDR6NMZj', + SUMO: 'SumipDETyjLYi8rqkmyE9c4SftzYzWPCGA3XvcXbGuBYcqDQJWe8wp8NEwNicFyzZgKTSjCjnpuXTitwn6VdBcFZEFXLcUYThVkF1dR9Q1uxEa', + XHV: 'hvi1aCqoAZF19J8pijvqnrUkeAeP8Rvr4XyfDMGJcarhbL15KgYKM1hN7kiHMu3fer5k8JJ8YRLKCahDKFgLFgJMYAfngJjDmkZAVuiRP15qv', + XTL: 'SEiStP7SMy1bvjkWc9dd1t2v1Et5q2DrmaqLqFTQQ9H7JKdZuATcPHUbUL3bRjxzxTDYitHsAPqF8EeCLw3bW8ARe8rYRNQQwys1JcJAs3qSH', + BLOC: 'abLoc7JNzYXijnKnPf7tSFUNSWBuwKrmUPMevvPkH4jc3b1K9LmS76DKpPamgQ5AYAC2CW9dJfTJ91AnXHYDNXAKRqPx5ZrtR49+9ea139b03f8ec0fce656096ad336a3c7b7041210b56386808dfbe4d1be8186c8', + AEON: 'WzWP347dSczJVmPpw65AsAGi5WhT85z5n66D8vcT3RcxRBBj4tFiDcd2CVFcQ1bBpjNQD5Z5kbXrLjVidvoKFaFK6uj4vyX3yrB1ap6jvPzB', + COAL: 'CoFbPDzEmLDHntwuC4WHkw3hQ4cX7g2JdVSGzqndAcDca4XgKyR4Wca7tkJw56eVX12iAQNGRzNPNXsegXmoJvUDSmkUKhL+3f128d248560b076805004a589de88bf6546bc9a0e0011dd56d6040d99b5d622', + XVG: 'DKSE7UW9Pwssq2ZF7rMvQVBG2EDio1GZHP', + BCN: '24WXh1qTEZzgDG3Ly1MtCJX7fRbDNqbzC4iEzpQBVhLwZ9jBeSs7GFQgu1bYxpHFKyjBQvABXicZ2MJ7si6rVBcQKQBCgLj+f122a0901996579ca5e8a7b5bdfb958a8e8d9fde470faeb7794315c3473b9aaa', + TRTL: 'TRTLv1Hqo3wHdqLRXuCyX3MwvzKyxzwXeBtycnkDy8ceFp4E23bm3P467xLEbUusH6Q1mqQUBiYwJ2yULJbvr5nKe8kcyc4uyps+0f2404e298d6e6c132b300713bcd723d7fa61dd1206ee4b5975c254c67783686', + KRB: 'KdAXXrRCGcENDhuRqEkocjZ5tpPfu17U35mqhgDEJTjTFBfs4hxhiBKK95XEpAuY8V9nomNcMeTz1E6tehWEvU6h8vCBs51+b7480fabf430e71b4702fbcf35e2454acba69be935e289fa84c485c2f07f5e63', + CIV: 'CM2ieMxxePe5z1BUkeNm9p5pmpn3k4juX1', + DASH: 'XhVA4JaYjtqvuSLSNFgxYUQKJaJnRhi1ww', + NAH: 'SWNNDA4wUvTkFVYvvhDP24yiCrR2p7SZbt', + PCN: 'P9GzJEnyYRgA6GHfdwtr7FhYq3s365ASKS', + LUX: 'LgozXD5vaHT3BDkNVRLWCBaNvSBmJxn5NT', + TUBE: 'bi1b95WYJRES7oBrvRo2eQV53ExLzFAzjKVM4wp9H9B6irCR6UuQxHf183XsJwemdoQm5PUHhQVwS67Hf5yUE7qg4SwbJJfAjLE1PH6T9V667', + DOGE: 'DM6FYmmLw4R5uFaSNdtYMrBLYuy1ank39R', + NBR: 'NDysWekoQnxeUquciujUQFPvVXTaU2D23eLbbboNFavCDozx2157EB3KzyKxk3mdyJRU1YfarvN35EfkGFbNEAQ4KHbkwy2+643750a2037d089470b8889d3a13edf673fdb0738e9c9bc4812ce41ded0644a7', + XUN: 'Xun3jQ4dLmfdRCnBuavjukJRm8EMntWqhL27JKJqeEf73rwi8nRoMYaMusv8pD9s5Y7oK8aHYCieB9rcNJ4uDfzZ8HUmzRq8yQ+cb22ae07df149caf85050ff14fd8b82d18fd0d62601a714b969855461046111b', + WAE: 'KfDMEcEpi7HANXK5N6vgAorWDLNqFfLnMk', + IRD: 'ir2btddJ78sicpKntYo3oRMLQh91VktzBfZzWbhwZnQxS815QLiG5WCAH9sgVGC5uwLZuMJCwW5CdFigNbJ3WTxU2CG5GnUDe+fe88be6c5a157ab1c97242ec0c6be699f48c604f752def45259097d20405a035', + D: 'DSHxGR1RFja4dwoqG2VXvFoSK5uS35pBWA', + PLURA: 'Pv8xzGjaY4TBPQUc8mLqcPGXCpWJbAYAjZ1uwZJK9Cfg1qvQBx3zZHqGF4XWnZHeYDKfkQVA8yDSjVdE56nz2Jab2Xu16PGb1+bda64dd03e45d67fd6fa4b99d91b507c02230bfe21cf2e862e436f18143d547a', + BTCP: 'b1B64ofQxHBPYXPYJvkuM9z1nexV8NSXtj6', + PIVX: 'DAE6oWhR1TwD8vFSxQHGaTMwyymHcCsmDH', + BSM: 'Sm4gdR5meAJAets9DwXCtq9tRZofxg6uubw6rYPKqYi7EN1MKADYG6obJczbjCmwfS752ThxYHUr3gBqY8KDWrB91dPcCbhJe', + OMB: 'casiLpKfELY6hyxUqS1zYjjAMTSWrcUm5Cpc1bSdwNyCEmP2ii8EfVWLvvjysm2YXBXM2vGvpkGUs42RD1ihi9uDATCr5crEjvw8AfnGHwkaw', + LOKI: 'LK8CGQ17G9R3ys3Xf33wCeViD2B95jgdpjAhcRsjuheJ784dumXn7g3RPAzedWpFq364jJKYL9dkQ8mY66sZG9BiCvqjvv6LgsyHf6H2gy', + BKC: 'bkc1o8uPqS7YD6Eo4yoaGp9N2MgbUrU7pUChtFFXS3K6fWF5hT9SrP2dGJfsTnn6pWZtQckuPYUSbJy5qv2MnLGo8eFS3t6qJw+29c6c1a056b3bc2598d0d57ab396c2d9c0053642dd9b8cbb7cb5bd63742f1eea', + SOLACE: 'Siz7GSHywyu3RxHDAcuW9iBKVLfgjjftDj5p9AucrNb9YV1jkPZNBdjDiWxcjMZK94Kdo97BzMuSZAU87U7UCzUL4JeRiP2zFz87AgfgBiFUW', + RTO: 'ALJj1xFnU8854yRhkrQKLyKeZYUxTt1oFQ4nEpbNsJoneXMBHyCgCJCW9xq2QZLVnbB4hBwxbEopSdWEmiyXXFiC66VQvaF+b40b160bf2f6efda8e56213c5907e960518633c8152f79c4645dbfda637c5122', + ITA: 'iz4FdJrFNkGNEmLSJAA39bT4cuQBVAgQTa818Gqshzg32PadREJSuWWEHFunmdfS8rjHGSHTxZoja5nWSZ8zWwiW51F2SAPWRN42mdXBvWe6', + INTU: 'intuvSUykQhFXJj22j2KJqC5CRMv3AWar467CtzZFVAVBxLUwb8yQi1LF72LzLRHtvAoNGkCYr3EZHYUJmeSqcZCLhxK73jJZDF+e35f3bdc1581764e8d8d778c2acbfd1acb838a2da61553ef637c980765bc3a26', + WOW: 'So2ifgjqGMZJhCrqpFMotQQAiJAiATuJLNAK2HrPLoNzK8hkqNbf9t8gmx6bzAQrXRMnWnoELoiD6GTv8guPBRwH5yoUvyBK1Ku1YYpQf2x8', + XMV: '4Cd7rzeiqwQJe8dCZbQeQxeNHUV4P4w7hAJ2g8U2ciiEao2AmqN9tXEfngQaPGV6T2Sx9mEtCaEMzaCR53iQRJqEgha9Dv26d83ML2wMiT', + XMC: '4H6kZARSRW9WAfxooKC2hkSpNf3RHo7ERBZWFdHN2BiX5BRxoiP5881EEK7P9wdzCZZmUxGWCZNuMjYdKFL1UuqdNUSZQs3uWGhCd4s4Xe', + WTIP: 'WtiptjGr6oRdZVFqumdfLbVd1XMtCYWKBT6dXppmCWA3TAk4tvesMCsbFLewv4rnmQHKimvwvcsbzC5FgWRrkGmc1DXreHk7sS+b319cf450209a800ea35c7f0d66754d0e243314650fe89ffd5db352733d9b9db', + BBS: 'fyT3HPm3Qrh87dR64wwLCi855A6bgYdF13mqtSHvX3RB67XEBb8aRrtVGsHa8u9juzByX8Mv6CPDjeuJwHhfqrjA19e7t2Fad+38adf2a9aadf58bf65374a683853ccbc169f75c9a38dc89cecb1beba3788c764', + XNV: 'NizKdaicW4bVfYB3AVhnsq9qnvUYSKe568YaNV2KQCYCDrNGzpvxqBo6mxF8cBkiQDU5xkgB2PrUGFKf66wVDVoNbQBhu2JRacy6uhsLoUyBJ', + XRN: 'PiyAfA1u1bNH9XjsPjXc5M64sip7LCj6ziBKEneKPmbVWM6kPMMAQs17h26tnCogW5Q72c5pVGJ4QW3kSKso4MW4h6hQJAVW23z6w2YMYKdHB', + XTRI: 'TixxgPgBkxgC4JM39WZuacjMLnJqm9YbjPq1YAR4BJbLXiCzf345r9SVbNuKMAG1CcjRMsv7kpatt7gStpUE3gGJRb8cbvWQHfk6L4pEbmdF7', + RVN: 'RFQvccQyLF3YMhKQby3bKvNXJczhgzEofu', + ETNXP: 'f4VR74XR616Tw2wAMMfaLV1vmYBSBBbmWXBUtaV8YDb6DHsfKRoYkFaCvhPhsGDDfm1afhzLNuf5XGFmNrvodPoQ6m4qGwXTuNt3gNnTEMYVA', + ZEL: 't1gsjJAhgGDB45ohJnJkbxtvr8WcUSzahyr', + RYO: 'SumipDETyjLYi8rqkmyE9c4SftzYzWPCGA3XvcXbGuBYcqDQJWe8wp8NEwNicFyzZgKTSjCjnpuXTitwn6VdBcFZEFXLcSos83oaS9wV7CJdKY', + INC: 'i9ajAmx77JLPtZL7JEs3ZEVbErmXqvQeGbg7PywpCaRKQGyypTv4TTpDYLMtrdGBGXMJM2mugBf14csz4wmNitZuGdXmy2c4hbG2iiUscAxQ', + QUAN: 'QRm4DREddkpmmC48CqXqkhV6S75fhtciBo', + PURK: 'PK2TdygFzH7X9jZPAdjvgQFHBR5bdNmsr6xefs2yQsDRHkKuoAsqQ7hX73nLgjWpiji4GqJMmNx357eu98TpU9Yr2es6SBBtQ+1246e3b3c4fdba89cbcad2113d0dff839d0f1f0a8a6f648b1c292b9bd8a0f8c2', + ACM: 'PDo5Z3pqN8weHSbfqayJEiSeAkAyr32NB3', + ARQ: 'aRi1cDd6LkAcc1p6W58dkPi8xSfbZ5EuYFrHxwH3py1MQ9rFrzmSaghguD4GGpCfHSMmKXWJrd4e5CkabC3viWJKfHuDLYqHNGs9D83sj6BPX', + NCP: 'cczJxhhLKTg7oNGiy1kmFadqTcKDschTb3LUKGvQdhxrVepPo9KjfxjSeqXBUKWVFwcHZRTGLi3k6USWHF1YP82e2TWRse4KD6+70fa2954af5cfc9fd292ce1643e276c8899b769255108c84caf2dcc406ea0678', + XGS: 'GTqypwunRe5ZkNdmAr26B9mmeMoGwhgoCV', + SUQA: 'Sic7A6F5r8RkjBvHjRwA9jWhHawXNrFxFX', + SHB: 'SVSEb9adxGpWckC2KuwuNbD1ved2x7YWTj', + GPKR: 'GdQ4ewDqJyhMU4BpchEs5uyn8U5VeHii7V', + ETNX: 'f4VR74XR616Tw2wAMMfaLV1vmYBSBBbmWXBUtaV8YDb6DHsfKRoYkFaCvhPhsGDDfm1afhzLNuf5XGFmNrvodPoQ6m4qTKUkc8Y2dL6d97BZw', + LMO: 'darkWdeodDHM5YWWGBHKa821DrL3HSzeaBNaVmXTr3svd75GUVcUBbxdSdFqJFUgTWfxfZJJcGTg58rfKct5hedk3Gz9dFLa6U', + TTNZ: 'Tri1J1prCp9VWj3AyjSTHU5aH1kVt4hsJ9xv7jbMauBzCLcqgKzJB2SATr2aypSFfmBW2dNfDVxMW3sQ4ys147Pz5pRgJvKzVS', + FHV: 'fh3ddFK3JqWNRZFsiL7xB5bGa79ejPfBNVbSdXVzdKiR7vRXHC3osL51vg9PyHKmxvWgz4ymZeHzcRZNRTJ5kwzM2BtrZMiq7', + SAFEX: 'Safex5zXVvH6GYJY2tnL4GcJy4W72GRutjhV1aCaRiPhYnfv4CyDjmGfLYQDd4GaJvHEKrpE7r9ux6UMCv5i1PmvjNwxA4r4Roi3K', + MUTX: 'ZYZZZDAz8vEfwGL3QfuRpMHneGuMbm6uTCtJ4h1kyhYkSi8Yjp62fBQWuHEviGoUdcXmSuYM3mDVa6tR9Rv5zRMra5i1F5UKvSXZc8X7MwHCki', + BTCN: 'MvweZSjjwTTdwrKJcRL2SzRZm9YrEAthNNeyzGjdbbeujoPsayzLM4efBhLZQoV6GreZTNgHv2gBZ9Hzz7SkTYVN2EgTbqh', + ARMS: 'gunsGSAGHJweDXBXGwCQrwUqACk67GkZB991b4HHkze8a7bnif8XwPF1NMdoY6oRhm8qjbPu2Jh7F4egLD3mpFTN1oQNydPdNv' }; global.donations = {}; -var percent = config.blockUnlocker.devDonation; -var wallet = donationAddresses[config.symbol]; +let percent = config.blockUnlocker.devDonation; +let wallet = donationAddresses[config.symbol]; if (percent && wallet) { global.donations[wallet] = percent; } From 78dcbab5867fee07dfb95867fdf3c157660b049f Mon Sep 17 00:00:00 2001 From: Learner Date: Mon, 11 Feb 2019 14:09:59 +0700 Subject: [PATCH 52/98] Add BitcoinMono config Newly launched coin. --- config_examples/bitcoinmono.json | 318 +++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 config_examples/bitcoinmono.json diff --git a/config_examples/bitcoinmono.json b/config_examples/bitcoinmono.json new file mode 100644 index 000000000..dffa5eee0 --- /dev/null +++ b/config_examples/bitcoinmono.json @@ -0,0 +1,318 @@ +{ + "poolHost": "your.pool.host", + + "coin": "bitcoinmono", + "symbol": "BTCM", + "coinUnits": 10000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 50, + + "daemonType": "default", + "cnAlgorithm": "cryptonight_pico", + "cnVariant": 0, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 10000000000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 500000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 21204 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 21205 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From 77b0e15de36f413b1550b5f320f9a04e3e9bc2ec Mon Sep 17 00:00:00 2001 From: Learner Date: Mon, 11 Feb 2019 14:17:59 +0700 Subject: [PATCH 53/98] Update daemon/wallet port 11358 and 11359 --- config_examples/bitcoinmono.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config_examples/bitcoinmono.json b/config_examples/bitcoinmono.json index dffa5eee0..9c3190f59 100644 --- a/config_examples/bitcoinmono.json +++ b/config_examples/bitcoinmono.json @@ -147,12 +147,12 @@ "daemon": { "host": "127.0.0.1", - "port": 21204 + "port": 11358 }, "wallet": { "host": "127.0.0.1", - "port": 21205 + "port": 11359 }, "redis": { From 9463e4b3b50cf51e79b2679e69f092585c841712 Mon Sep 17 00:00:00 2001 From: professorviz Date: Tue, 12 Feb 2019 18:39:42 -0600 Subject: [PATCH 54/98] upgraded to new static Buffer methods --- lib/pool.js | 25 ++++++++++--------------- lib/utils.js | 4 ++-- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 2e0b53d06..5aa701265 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -141,9 +141,9 @@ function BlockTemplate(template){ this.difficulty = template.difficulty; this.height = template.height; this.reserveOffset = template.reserved_offset; - this.buffer = new Buffer(this.blob, 'hex'); + this.buffer = Buffer.from(this.blob, 'hex'); instanceId.copy(this.buffer, this.reserveOffset + 4, 0, 3); - this.previous_hash = new Buffer(32); + this.previous_hash = Buffer.alloc(32); this.buffer.copy(this.previous_hash, 0, 7, 39); this.extraNonce = 0; @@ -199,8 +199,8 @@ function processBlockTemplate(template){ function jobRefresh(loop, callback){ callback = callback || function(){}; getBlockTemplate(function(error, result){ - let buffer = new Buffer(result.blocktemplate_blob, 'hex'); - let new_hash = new Buffer(32); + let buffer = Buffer.from(result.blocktemplate_blob, 'hex'); + let new_hash = Buffer.alloc(32); buffer.copy(new_hash, 0, 7, 39); if (loop) @@ -214,11 +214,6 @@ function jobRefresh(loop, callback){ return; } - //var buffer = new Buffer(result.blocktemplate_blob, 'hex'); - //var previous_hash = new Buffer(32); - //buffer.copy(previous_hash,0,7,39); - //if (!currentBlockTemplate || previous_hash.toString('hex') !== currentBlockTemplate.previous_hash.toString('hex')) { - if (!currentBlockTemplate || result.height > currentBlockTemplate.height) { log('info', logSystem, 'New block to mine at height %d w/ difficulty of %d', [result.height, result.difficulty]); processBlockTemplate(result); @@ -329,7 +324,7 @@ Miner.prototype = { this.pendingDifficulty = null; } - var padded = new Buffer(32); + var padded = Buffer.alloc(32); padded.fill(0); var diffBuff = diff1.div(this.difficulty).toBuffer(); @@ -337,7 +332,7 @@ Miner.prototype = { var buff = padded.slice(0, 4); var buffArray = buff.toByteArray().reverse(); - var buffReversed = new Buffer(buffArray); + var buffReversed = Buffer.from(buffArray); this.target = buffReversed.readUInt32BE(0); var hex = buffReversed.toString('hex'); return hex; @@ -839,7 +834,7 @@ function recordShareData(miner, job, shareDiff, blockCandidate, hashHex, shareTy function processShare(miner, job, blockTemplate, params){ var nonce = params.nonce; var resultHash = params.result; - var template = new Buffer(blockTemplate.buffer.length); + var template = Buffer.alloc(blockTemplate.buffer.length); if (!miner.proxy) { blockTemplate.buffer.copy(template); template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); @@ -849,14 +844,14 @@ function processShare(miner, job, blockTemplate, params){ template.writeUInt32BE(params.poolNonce, job.clientPoolLocation); template.writeUInt32BE(params.workerNonce, job.clientNonceLocation); } - var shareBuffer = utils.cnUtil.construct_block_blob(template, new Buffer(nonce, 'hex'), cnBlobType); + var shareBuffer = utils.cnUtil.construct_block_blob(template, Buffer.from(nonce, 'hex'), cnBlobType); var convertedBlob; var hash; var shareType; if (shareTrustEnabled && miner.trust.threshold <= 0 && miner.trust.penalty <= 0 && Math.random() > miner.trust.probability){ - hash = new Buffer(resultHash, 'hex'); + hash = Buffer.from(resultHash, 'hex'); shareType = 'trusted'; } else { @@ -873,7 +868,7 @@ function processShare(miner, job, blockTemplate, params){ } var hashArray = hash.toByteArray().reverse(); - var hashNum = bignum.fromBuffer(new Buffer(hashArray)); + var hashNum = bignum.fromBuffer(Buffer.from(hashArray)); var hashDiff = diff1.div(hashNum); if (hashDiff.ge(blockTemplate.difficulty)){ diff --git a/lib/utils.js b/lib/utils.js index dc13c270c..651a5e682 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -24,14 +24,14 @@ exports.instanceId = function() { /** * Validate miner address **/ -var addressBase58Prefix = parseInt(cnUtil.address_decode(new Buffer(config.poolServer.poolAddress)).toString()); +var addressBase58Prefix = parseInt(cnUtil.address_decode(Buffer.from(config.poolServer.poolAddress)).toString()); var integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1; var subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(config.poolServer.subAddressPrefix) : "N/A"; // Get address prefix function getAddressPrefix(address) { - var addressBuffer = new Buffer(address); + var addressBuffer = Buffer.from(address); var addressPrefix = cnUtil.address_decode(addressBuffer); if (addressPrefix) addressPrefix = parseInt(addressPrefix.toString()); From b670246c15598d0ebc2d5fa7228de6f306aa94c1 Mon Sep 17 00:00:00 2001 From: muscleman Date: Thu, 21 Feb 2019 21:27:19 -0600 Subject: [PATCH 55/98] Create wownero.json added includeHeight and includeAlgo flags --- config_examples/wownero.json | 309 +++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 config_examples/wownero.json diff --git a/config_examples/wownero.json b/config_examples/wownero.json new file mode 100644 index 000000000..fa8e1a335 --- /dev/null +++ b/config_examples/wownero.json @@ -0,0 +1,309 @@ +{ + "poolHost": "your.pool.host", + + "coin": "wownero", + "symbol": "WOW", + "coinUnits": 100000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 300, + + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 12, + "cnBlobType": 0, + "includeHeight": true, + "includeAlgo": "cn/wow", + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "Your pool wallet address", + "intAddressPrefix": 19, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 4333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 4555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 4777, + "difficulty": 500000, + "hidden": true, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 4888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 1000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 16, + "priority": 0, + "transferFee": 14000000000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 1000000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 2.0, + "devDonation": 0.2, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 34558, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 34568 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 39578 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 10, + "stepInterval": 600, + "maximumPeriod": 3600 + }, + "miners": { + "enabled": true, + "updateInterval": 10, + "stepInterval": 600, + "maximumPeriod": 3600 + }, + "workers": { + "enabled": true, + "updateInterval": 10, + "stepInterval": 6000, + "maximumPeriod": 3600 + }, + "difficulty": { + "enabled": true, + "updateInterval": 10, + "stepInterval": 600, + "maximumPeriod": 3600 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From 614555e10b628b5c010ba7697a97efc00f0f20d7 Mon Sep 17 00:00:00 2001 From: muscleman Date: Thu, 21 Feb 2019 21:29:16 -0600 Subject: [PATCH 56/98] Update pool.js added includeHeight and includeAlgo flags --- lib/pool.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 5aa701265..2526ddb2b 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -400,7 +400,10 @@ Miner.prototype = { job_id: newJob.id, id: this.id }; - + if (typeof config.includeAlgo !== "undefined" && config.includeAlgo) + this.cachedJob['algo'] = config.algo + if (typeof config.includeAlgo !== "undefined" && config.includeHeight) + this.cachedJob['height'] = currentBlockTemplate.height } return this.cachedJob; }, @@ -857,7 +860,10 @@ function processShare(miner, job, blockTemplate, params){ else { convertedBlob = utils.cnUtil.convert_blob(shareBuffer, cnBlobType); var hard_fork_version = convertedBlob[0]; - hash = cryptoNight(convertedBlob, cnVariant); + if (typeof config.includeAlgo !== "undefined" && config.includeHeight) + hash = cryptoNight(convertedBlob, cnVariant, job.height); + else + hash = cryptoNight(convertedBlob, cnVariant); log('info', logSystem, 'Mining pool algorithm: %s variant %d, Hard fork version: %d', [cnAlgorithm, cnVariant, hard_fork_version]); shareType = 'valid'; } From 6402352a28dc747f0f95845448adbed12cdfb003 Mon Sep 17 00:00:00 2001 From: muscleman Date: Thu, 21 Feb 2019 21:32:49 -0600 Subject: [PATCH 57/98] Update monero.json added includeHeight --- config_examples/monero.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config_examples/monero.json b/config_examples/monero.json index a8876595a..46b7fb116 100644 --- a/config_examples/monero.json +++ b/config_examples/monero.json @@ -9,9 +9,10 @@ "daemonType": "default", "cnAlgorithm": "cryptonight", - "cnVariant": 8, + "cnVariant": 13, "cnBlobType": 0, - + "includeHeight": true, + "logging": { "files": { "level": "info", From 9dace7a6e987beaa8804b6b446fb58b6fb3d9901 Mon Sep 17 00:00:00 2001 From: muscleman Date: Thu, 21 Feb 2019 21:40:53 -0600 Subject: [PATCH 58/98] Update README.md changes for includeHeight and includeAlgo --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 842dba782..1a5307dd9 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,8 @@ Explanation for each field: "cnAlgorithm": "cryptonight", "cnVariant": 1, "cnBlobType": 0, +"includeHeight":false, /*true to include block.height in job to miner*/ +"includeAlgo":"cn/wow", /*wownero specific change to include algo in job to miner*/ /* Logging */ "logging": { From 07223b9cedd6653d02c98f552e6d9c7495eac0d4 Mon Sep 17 00:00:00 2001 From: muscleman Date: Fri, 22 Feb 2019 19:19:02 -0600 Subject: [PATCH 59/98] Update getting_started.html wownero update --- website_example/pages/getting_started.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website_example/pages/getting_started.html b/website_example/pages/getting_started.html index b83a033e6..9c1ac9028 100644 --- a/website_example/pages/getting_started.html +++ b/website_example/pages/getting_started.html @@ -401,6 +401,9 @@

Mining Applications

} else if (cnVariant === 8) { algorithm = 'Cryptonight (Monero v8)'; xmrstakAlgo = 'cryptonight_v8'; + } else if (cnVariant === 12) { + algorithm = 'Cryptonight (Wow)'; + xmrstakAlgo = 'cn/wow'; } else { algorithm = 'Cryptonight (Original)'; xmrstakAlgo = 'cryptonight'; From 1ec1d3acb385492ab7f15cfd8cd2328af61b8115 Mon Sep 17 00:00:00 2001 From: muscleman Date: Tue, 26 Feb 2019 21:41:54 -0600 Subject: [PATCH 60/98] Update pool.js --- lib/pool.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 2526ddb2b..b973a058b 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -400,12 +400,12 @@ Miner.prototype = { job_id: newJob.id, id: this.id }; - if (typeof config.includeAlgo !== "undefined" && config.includeAlgo) - this.cachedJob['algo'] = config.algo - if (typeof config.includeAlgo !== "undefined" && config.includeHeight) - this.cachedJob['height'] = currentBlockTemplate.height } - return this.cachedJob; + if (typeof config.includeAlgo !== "undefined" && config.includeAlgo) + this.cachedJob['algo'] = config.algo + if (typeof config.includeAlgo !== "undefined" && config.includeHeight) + this.cachedJob['height'] = currentBlockTemplate.height + return this.cachedJob; }, checkBan: function(validShare){ if (!banningEnabled) return; From 0c0153070da243c0b4ade34529ca4392aea41998 Mon Sep 17 00:00:00 2001 From: Learner Date: Fri, 1 Mar 2019 21:04:38 +0700 Subject: [PATCH 61/98] Update payments config I forgot to change the numbers last time. --- config_examples/bitcoinmono.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config_examples/bitcoinmono.json b/config_examples/bitcoinmono.json index 9c3190f59..70ec273e4 100644 --- a/config_examples/bitcoinmono.json +++ b/config_examples/bitcoinmono.json @@ -69,7 +69,7 @@ } ], "varDiff": { - "minDiff": 100, + "minDiff": 1000, "maxDiff": 100000000, "targetTime": 60, "retargetTime": 30, @@ -108,21 +108,21 @@ "enabled": true, "interval": 1800, "maxAddresses": 50, - "mixin": 5, + "mixin": 0, "priority": 0, - "transferFee": 10000000000, - "dynamicTransferFee": true, + "transferFee": 1000, + "dynamicTransferFee": false, "minerPayFee" : true, - "minPayment": 500000000000, - "maxPayment": null, + "minPayment": 10000, + "maxPayment": 1000000000000, "maxTransactionAmount": 0, - "denomination": 100000000 + "denomination": 10 }, "blockUnlocker": { "enabled": true, "interval": 30, - "depth": 60, + "depth": 50, "poolFee": 0.8, "devDonation": 0.2, "networkFee": 0.0 From bc928cda174b6a1eaf58032765868cbd84a247e9 Mon Sep 17 00:00:00 2001 From: muscleman Date: Sun, 3 Mar 2019 08:45:25 -0600 Subject: [PATCH 62/98] Update package.json revert --- package.json | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 26779d7d8..dd2428712 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,33 @@ { "name": "cryptonote-nodejs-pool", - "version": "1.3.5", + "version": "1.4.0", "license": "GPL-2.0", - "author": "Daniel Vandal", + "Original author": "Daniel Vandal", + "Maintained by": "Musclesonvacation", "repository": { "type": "git", - "url": "https://github.com/dvandal/cryptonote-nodejs-pool.git" + "url": "https://github.com/muscleman/cryptonote-nodejs-pool.git" }, "dependencies": { - "async": "^1.5.2", - "base58-native": "^0.1.4", - "bignum": "^0.13.0", - "cli-color": "^1.2.0", + "async": "1", + "base58-native": "*", + "bignum": "*", + "cli-color": "*", "cryptoforknote-util": "git://github.com/MoneroOcean/node-cryptoforknote-util.git", "cryptonight-hashing": "git://github.com/MoneroOcean/node-cryptonight-hashing.git", - "dateformat": "^3.0.3", - "mailgun.js": "^2.0.1", - "node-telegram-bot-api": "^0.30.0", - "nodemailer": "^2.7.2", - "nodemailer-sendmail-transport": "^1.0.2", - "promise-queue": "^2.2.5", - "redis": "^2.8.0", - "request": "^2.88.0", - "request-promise": "^4.2.2", + "multi-hashing": "git://github.com/muscleman/node-multi-hashing.git#ipbc", + "dateformat": "*", + "mailgun.js": "*", + "node-telegram-bot-api": "*", + "nodemailer": "2.7.2", + "nodemailer-sendmail-transport": "*", + "redis": "*", "socket.io": "^2.1.1", - "time-ago": "^0.2.1" + "time-ago": "*", + "request": "^2.88.0", + "request-promise-native": "^1.0.5" }, "engines": { - "node": ">=4.0" + "node": ">=8.11.3" } } From 5657e20cdf21627cee5e3bcf425e3e3bda4703a3 Mon Sep 17 00:00:00 2001 From: muscleman Date: Sun, 3 Mar 2019 08:46:20 -0600 Subject: [PATCH 63/98] Update apiInterfaces.js revert --- lib/apiInterfaces.js | 178 +++++++++++++++++++------------------------ 1 file changed, 80 insertions(+), 98 deletions(-) diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index bf6a4ea69..0128b88ba 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -6,104 +6,94 @@ **/ // Load required modules -var request = require("request-promise"); -var queue = require("promise-queue"); -var http = require("http"); -var https = require("https"); - -var request_agent = new http.Agent({keepAlive: true, maxSockets: 1}) -var request_queue = new queue(1, Infinity) +let http = require('http'); +let https = require('https'); +let request = require('request-promise-native'); /** - * Send generic API request (i.e. market price) + * Send API request using JSON HTTP **/ function jsonHttpRequest(host, port, data, callback, path){ - path = path || '/json_rpc'; - callback = callback || function(){}; - - var options = { - hostname: host, - port: port, - path: path, - method: data ? 'POST' : 'GET', - headers: { - 'Content-Length': data.length, - 'Content-Type': 'application/json', - 'Accept': 'application/json' + try { + path = path || '/json_rpc'; + callback = callback || function(){}; + let options = { + 'uri': `${(port === 443 ? 'https' : 'http')}://${host}:${port}${path}`, + 'method': data ? 'POST' : 'GET', + 'headers' : { + 'Content-Length': data.length, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } } - }; + options['json'] = data - var req = (port === 443 ? https : http).request(options, function(res){ - var replyData = ''; - res.setEncoding('utf8'); - res.on('data', function(chunk){ - replyData += chunk; - }); - res.on('end', function(){ - var replyJson; - try{ - replyJson = JSON.parse(replyData); - } - catch(e){ - callback(e, {}); - return; - } - callback(null, replyJson); - }); - }); + request(options) + .then(response => { + response = response ? response : {} + if (response instanceof Array || response instanceof Object) { + callback(null, response) + } else { + callback(null, JSON.parse(response)) + } + }) + .catch(error => { + callback(error, {}) + }) + } catch(error){ + console.log('catch ' , error) + callback(error, {}) + } +} - req.on('error', function(e){ - callback(e, {}); +/** + * Send RPC request + **/ +function rpc(host, port, method, params, callback, username, password){ + let payload = { + id: "0", + jsonrpc: "2.0", + method: method, + params: params + }; + if (password !== undefined) { + if (username !== undefined){ + payload['auth'] = {'user': username, 'password': password, 'sendImmediately':false} + payload['agent'] = new http.Agent({'keepAlive': true, 'maxSockets': 1}) + payload['forever'] = true + } else { + payload['password'] = password; + } + } + //let data = JSON.stringify(payload); + jsonHttpRequest(host, port, payload, function(error, replyJson){ + if (error){ + callback(error, {}); + return; + } + callback(replyJson.error, replyJson.result) }); - - req.end(data); } /** - * Send RPC request to daemon or wallet + * Send RPC requests in batch mode **/ -function rpc(host, port, method, params={}, auth={}, callback=false) { - var options = { - uri: `http://${host}:${port}/json_rpc`, - method: "POST", - agent: request_agent, - json: { +function batchRpc(host, port, array, callback){ + let rpcArray = []; + for (let i = 0; i < array.length; i++){ + rpcArray.push({ + id: i.toString(), jsonrpc: "2.0", - id: "0", - method: method - } - }; - if(Object.keys(params).length !== 0) { - options.json.params = params - } - if(Object.keys(auth).length !== 0) { - options.auth = auth; - options.auth.sendImmediately = false; + method: array[i][0], + params: array[i][1] + }); } - - request_queue.add(() => { - return request(options) - .then((response) => { - if(response.hasOwnProperty("error")) { - if(typeof callback === "function") { - callback(response.error, {}); - } - return; - } - if(typeof callback === "function") { - callback(false, response.result) - } - }).catch(error => { - if(typeof callback === "function") { - callback(error, {}); - } - return; - }); - }); + let data = JSON.stringify(rpcArray); + jsonHttpRequest(host, port, data, callback); } /** - * Send request to pool API for graphs + * Send RPC request to pool API **/ function poolRpc(host, port, path, callback){ jsonHttpRequest(host, port, '', callback, path); @@ -113,29 +103,21 @@ function poolRpc(host, port, path, callback){ * Exports API interfaces functions **/ module.exports = function(daemonConfig, walletConfig, poolApiConfig){ - return { + batchRpcDaemon: function(batchArray, callback){ + batchRpc(daemonConfig.host, daemonConfig.port, batchArray, callback); + }, rpcDaemon: function(method, params, callback){ - var auth = {}; - if(daemonConfig.hasOwnProperty("user") && daemonConfig.user && - daemonConfig.hasOwnProperty("pass") && daemonConfig.pass) { - auth.user = daemonConfig.user; - auth.pass = daemonConfig.pass; - } - rpc(daemonConfig.host, daemonConfig.port, method, params, auth, callback); + rpc(daemonConfig.host, daemonConfig.port, method, params, callback, + daemonConfig.user, daemonConfig.pass); }, rpcWallet: function(method, params, callback){ - var auth = {}; - if(walletConfig.hasOwnProperty("user") && walletConfig.user && - walletConfig.hasOwnProperty("pass") && walletConfig.pass) { - auth.user = walletConfig.user; - auth.pass = walletConfig.pass; - } - rpc(walletConfig.host, walletConfig.port, method, params, auth, callback); + rpc(walletConfig.host, walletConfig.port, method, params, callback, + walletConfig.user, walletConfig.pass); }, pool: function(path, callback){ - var bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; - var poolApi = (bindIp !== "0.0.0.0" ? poolApiConfig.bindIp : "127.0.0.1"); + let bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; + let poolApi = (bindIp !== "0.0.0.0" ? poolApiConfig.bindIp : "127.0.0.1"); poolRpc(poolApi, poolApiConfig.port, path, callback); }, jsonHttpRequest: jsonHttpRequest From 6a283264451f20ea7f9ef28349bda72045e683f4 Mon Sep 17 00:00:00 2001 From: muscleman Date: Sun, 3 Mar 2019 15:34:35 -0600 Subject: [PATCH 64/98] Update api.js bug fix --- lib/api.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/api.js b/lib/api.js index cc5c3413b..0df245f62 100644 --- a/lib/api.js +++ b/lib/api.js @@ -834,15 +834,15 @@ function handleGetPayments(urlParts, response){ 0, config.api.payments, function(err, result){ - var reply; + var data; if (err) { - var data = {error: 'Query failed'}; + data = {error: 'Query failed'}; } else { - var data = result; + data = result ? result : ''; } - reply = JSON.stringify(data); + var reply = JSON.stringify(data); response.writeHead("200", { 'Access-Control-Allow-Origin': '*', @@ -869,13 +869,13 @@ function handleGetBlocks(urlParts, response){ config.api.blocks, function(err, result){ - var reply; + var data; if (err) - var data = {error: 'Query failed'}; + data = {error: 'Query failed'}; else - var data = result - reply = JSON.stringify(result); + data = result ? result : '' + var reply = JSON.stringify(data); response.writeHead("200", { 'Access-Control-Allow-Origin': '*', From 3e51baa5b0a73c124352ba73cc589fe12dbdf914 Mon Sep 17 00:00:00 2001 From: EDDragonWolf Date: Mon, 4 Mar 2019 07:19:01 -0800 Subject: [PATCH 65/98] Added correct handling of CryptoNight v8 ReverseWaltz --- config_examples/graft.json | 2 +- website_example/pages/getting_started.html | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/config_examples/graft.json b/config_examples/graft.json index 63929bb6a..8776e5cfe 100644 --- a/config_examples/graft.json +++ b/config_examples/graft.json @@ -9,7 +9,7 @@ "daemonType": "default", "cnAlgorithm": "cryptonight", - "cnVariant": 1, + "cnVariant": 14, "cnBlobType": 0, "logging": { diff --git a/website_example/pages/getting_started.html b/website_example/pages/getting_started.html index 9c1ac9028..2a40f0e71 100644 --- a/website_example/pages/getting_started.html +++ b/website_example/pages/getting_started.html @@ -404,6 +404,9 @@

Mining Applications

} else if (cnVariant === 12) { algorithm = 'Cryptonight (Wow)'; xmrstakAlgo = 'cn/wow'; + } else if (cnVariant === 14) { + algorithm = 'Cryptonight v8 ReverseWaltz'; + xmrstakAlgo = 'cryptonight_v8_reversewaltz'; } else { algorithm = 'Cryptonight (Original)'; xmrstakAlgo = 'cryptonight'; From 488a5347dde049f7169311a592b81e6139ecc9dd Mon Sep 17 00:00:00 2001 From: muscleman Date: Sat, 9 Mar 2019 15:28:09 -0600 Subject: [PATCH 66/98] Update pool.js bug fix --- lib/pool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pool.js b/lib/pool.js index b973a058b..0368543cf 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -860,7 +860,7 @@ function processShare(miner, job, blockTemplate, params){ else { convertedBlob = utils.cnUtil.convert_blob(shareBuffer, cnBlobType); var hard_fork_version = convertedBlob[0]; - if (typeof config.includeAlgo !== "undefined" && config.includeHeight) + if (typeof config.includeHeight !== "undefined" && config.includeHeight) hash = cryptoNight(convertedBlob, cnVariant, job.height); else hash = cryptoNight(convertedBlob, cnVariant); From 2ba52c569c477e888b796b30e4922896d6e8520d Mon Sep 17 00:00:00 2001 From: muscleman Date: Sun, 10 Mar 2019 08:21:02 -0500 Subject: [PATCH 67/98] Update pool.js bug --- lib/pool.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 0368543cf..07a54f2e1 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -402,8 +402,8 @@ Miner.prototype = { }; } if (typeof config.includeAlgo !== "undefined" && config.includeAlgo) - this.cachedJob['algo'] = config.algo - if (typeof config.includeAlgo !== "undefined" && config.includeHeight) + this.cachedJob['algo'] = config.includeAlgo + if (typeof config.includeHeight !== "undefined" && config.includeHeight) this.cachedJob['height'] = currentBlockTemplate.height return this.cachedJob; }, From 46ee4c608c3066ce124bc20cc1b5a8096b40ffa7 Mon Sep 17 00:00:00 2001 From: muscleman Date: Thu, 14 Mar 2019 21:12:28 -0500 Subject: [PATCH 68/98] Update pool.js unuseable code --- lib/pool.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 07a54f2e1..e8bde5160 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -199,20 +199,17 @@ function processBlockTemplate(template){ function jobRefresh(loop, callback){ callback = callback || function(){}; getBlockTemplate(function(error, result){ - let buffer = Buffer.from(result.blocktemplate_blob, 'hex'); - let new_hash = Buffer.alloc(32); - buffer.copy(new_hash, 0, 7, 39); - - if (loop) - setTimeout(function(){ - jobRefresh(true); - }, config.poolServer.blockRefreshInterval); if (error){ log('error', logSystem, 'Error polling getblocktemplate %j', [error]); if (!poolStarted) log('error', logSystem, 'Could not start pool'); callback(false); return; } + if (loop) + setTimeout(function(){ + jobRefresh(true); + }, config.poolServer.blockRefreshInterval); + if (!currentBlockTemplate || result.height > currentBlockTemplate.height) { log('info', logSystem, 'New block to mine at height %d w/ difficulty of %d', [result.height, result.difficulty]); From c98983940cd64cd94ce61a6f41f169314acbc2ef Mon Sep 17 00:00:00 2001 From: pcca-matrix Date: Sun, 17 Mar 2019 16:41:39 +0100 Subject: [PATCH 69/98] clean old codes --- lib/pool.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index e8bde5160..86ea3c2ca 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -143,8 +143,6 @@ function BlockTemplate(template){ this.reserveOffset = template.reserved_offset; this.buffer = Buffer.from(this.blob, 'hex'); instanceId.copy(this.buffer, this.reserveOffset + 4, 0, 3); - this.previous_hash = Buffer.alloc(32); - this.buffer.copy(this.previous_hash, 0, 7, 39); this.extraNonce = 0; // The clientNonceLocation is the location at which the client pools should set the nonces for each of their clients. From 0e30314bf2bdf73d3da9c6479f8108cdb329f6a6 Mon Sep 17 00:00:00 2001 From: pcca-matrix Date: Sun, 17 Mar 2019 16:52:19 +0100 Subject: [PATCH 70/98] JobRefresh correction if daemon not available --- lib/pool.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 86ea3c2ca..3b48f0ffb 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -197,17 +197,17 @@ function processBlockTemplate(template){ function jobRefresh(loop, callback){ callback = callback || function(){}; getBlockTemplate(function(error, result){ + if (loop) + setTimeout(function(){ + jobRefresh(true); + }, config.poolServer.blockRefreshInterval); + if (error){ log('error', logSystem, 'Error polling getblocktemplate %j', [error]); if (!poolStarted) log('error', logSystem, 'Could not start pool'); callback(false); return; } - if (loop) - setTimeout(function(){ - jobRefresh(true); - }, config.poolServer.blockRefreshInterval); - if (!currentBlockTemplate || result.height > currentBlockTemplate.height) { log('info', logSystem, 'New block to mine at height %d w/ difficulty of %d', [result.height, result.difficulty]); From cf55317cd512800c4444252c978ba1792e090529 Mon Sep 17 00:00:00 2001 From: ParsiCoin Date: Mon, 18 Mar 2019 02:46:00 +0330 Subject: [PATCH 71/98] New ParsiCoin Config for V3+ --- config_examples/parsicoin.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/config_examples/parsicoin.json b/config_examples/parsicoin.json index 57e882a70..d0e691c52 100644 --- a/config_examples/parsicoin.json +++ b/config_examples/parsicoin.json @@ -3,7 +3,7 @@ "coin": "parsicoin", "symbol": "PARS", - "coinUnits": 100000000, + "coinUnits": 1000000000000, "coinDecimalPlaces": 4, "coinDifficultyTarget": 120, @@ -105,24 +105,24 @@ "payments": { "enabled": true, - "interval": 900, + "interval": 1800, "maxAddresses": 50, - "mixin": 1, + "mixin": 0, "priority": 0, - "transferFee": 10000000, + "transferFee": 10000000000, "dynamicTransferFee": true, "minerPayFee" : true, - "minPayment": 10000000000, - "maxPayment": 10000000000000, - "maxTransactionAmount": 10000000000000, - "denomination": 100000000 + "minPayment": 1000000000000, + "maxPayment": null, + "maxTransactionAmount": 200000000000000, + "denomination": 1000000000000 }, "blockUnlocker": { "enabled": true, "interval": 30, "depth": 10, - "poolFee": 2.0, + "poolFee": 1.0, "devDonation": 0, "networkFee": 0.0 }, @@ -135,7 +135,7 @@ "port": 8117, "blocks": 30, "payments": 30, - "password": "CHANGE ME", + "password": "your_password", "ssl": true, "sslPort": 8119, "sslCert": "./cert.pem", @@ -146,12 +146,12 @@ "daemon": { "host": "127.0.0.1", - "port": 18230 + "port": 18240 }, "wallet": { "host": "127.0.0.1", - "port": 18231 + "port": 18242 }, "redis": { From fed2d99abdf390e472152d2d966b0616bfbd4995 Mon Sep 17 00:00:00 2001 From: "ParsiCoin (PARS)" <36328859+ParsiCoin@users.noreply.github.com> Date: Wed, 20 Mar 2019 18:27:49 +0330 Subject: [PATCH 72/98] correct payments --- config_examples/parsicoin.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config_examples/parsicoin.json b/config_examples/parsicoin.json index d0e691c52..1b0f876eb 100644 --- a/config_examples/parsicoin.json +++ b/config_examples/parsicoin.json @@ -105,16 +105,16 @@ "payments": { "enabled": true, - "interval": 1800, + "interval": 120, "maxAddresses": 50, "mixin": 0, "priority": 0, - "transferFee": 10000000000, + "transferFee": 100000000000, "dynamicTransferFee": true, "minerPayFee" : true, "minPayment": 1000000000000, "maxPayment": null, - "maxTransactionAmount": 200000000000000, + "maxTransactionAmount": 100000000000000, "denomination": 1000000000000 }, From 21109dce485c99abec0cd3fbe1c35d0615db1eb0 Mon Sep 17 00:00:00 2001 From: muscleman Date: Wed, 20 Mar 2019 18:40:23 -0500 Subject: [PATCH 73/98] Create turtle.json for use with monero ocean hashing package --- config_examples/turtle.json | 315 ++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 config_examples/turtle.json diff --git a/config_examples/turtle.json b/config_examples/turtle.json new file mode 100644 index 000000000..b8c863a82 --- /dev/null +++ b/config_examples/turtle.json @@ -0,0 +1,315 @@ +{ + "poolHost": "your.pool.host", + "coin": "turtlecoin", + "symbol": "TRTL", + "coinUnits": 100, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 30, + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_pico", + "cnVariant": 2, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": 6, + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 30, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "separators": [{"value":"+","desc":"plus"}, {"value":".","desc":"dot"}], + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 200, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 100000, + "maxTransactionAmount": 100000000, + "denomination": 100 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 120, + "poolFee": 0.8, + "devDonation": 0.0, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "192.168.0.116", + "port": 3107, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": true, + "sslPort": 3109, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 11898 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8070 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From 5644f2d396d78b197ea5ea160e24327fdae32736 Mon Sep 17 00:00:00 2001 From: muscleman Date: Fri, 22 Mar 2019 06:56:41 -0500 Subject: [PATCH 74/98] Update turtle.json --- config_examples/turtle.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_examples/turtle.json b/config_examples/turtle.json index b8c863a82..ae61816bf 100644 --- a/config_examples/turtle.json +++ b/config_examples/turtle.json @@ -122,7 +122,7 @@ "interval": 30, "depth": 120, "poolFee": 0.8, - "devDonation": 0.0, + "devDonation": 0.2, "networkFee": 0.0, "fixBlockHeightRPC": false }, From 7c36265a2ae880cb157db00a5995657df3785c73 Mon Sep 17 00:00:00 2001 From: ArqTras <33489188+ArqTras@users.noreply.github.com> Date: Wed, 3 Apr 2019 00:10:49 +0200 Subject: [PATCH 75/98] Coin diff target 120 --- config_examples/arqma.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_examples/arqma.conf b/config_examples/arqma.conf index ba1fe15cb..453b54e9b 100644 --- a/config_examples/arqma.conf +++ b/config_examples/arqma.conf @@ -5,7 +5,7 @@ "symbol": "ARQ", "coinUnits": 1000000000, "coinDecimalPlaces": 9, - "coinDifficultyTarget": 240, + "coinDifficultyTarget": 120, "cnAlgorithm": "cryptonight_light", "cnVariant": 1, From 66a7095583a643e41158f15b784bef7b3c563e34 Mon Sep 17 00:00:00 2001 From: professorviz Date: Sun, 7 Apr 2019 14:57:01 -0500 Subject: [PATCH 76/98] updated Readme --- README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 24acbbcca..5e9b8524c 100644 --- a/README.md +++ b/README.md @@ -94,13 +94,13 @@ Features Community / Support === -* [GitHub Wiki](https://github.com/mcnproject/mm-cryptonote-nodejs-pool/wiki) -* [GitHub Issues](https://github.com/mcnproject/mm-cryptonote-nodejs-pool/issues) -* [Discord Server](https://discord.gg/RGqymUj) +* [GitHub Wiki](https://github.com/dvandal/cryptonote-nodejs-pool/wiki) +* [GitHub Issues](https://github.com/dvandal/cryptonote-nodejs-pool/issues) +* [Telegram Group](http://t.me/CryptonotePool)j) #### Pools Using This Software -* https://minercountry.com/ +* https://smartcoinpool.com/ Usage @@ -150,7 +150,7 @@ sudo su - your-user Clone the repository and run `npm update` for all the dependencies to be installed: ```bash -git clone https://github.com/mcnproject/mm-cryptonote-nodejs-pool.git pool +git clone https://github.com/dvandal/cryptonote-nodejs-pool.git pool cd pool npm update @@ -622,7 +622,7 @@ sudo systemctl start cryptonote-nodejs-pool.service To enable merged mining you will need to use at leas 2 nodejs processes. One for the pool process + main coin config and another process for the child coin payments, unlocker, etc. First, you will need both coins node daemons running. -Take a look at the [config_inf8-mcn.json](https://github.com/campurro/cryptonote-nodejs-pool/blob/master/config_inf8-mcn.json) for a main (pool) config example. +Take a look at the [config_inf8-mcn.json](https://github.com/dvandal/cryptonote-nodejs-pool/blob/dvandal-with-mergemining/config_inf8-mcn.json) for a main (pool) config example. The child coin config file is a normal config with your child coin data but with the poolServer.enabled set to false. ``` @@ -774,11 +774,15 @@ Donations Thanks for supporting my works on this project! If you want to make a donation to [Campurro](https://github.com/campurro/), the developper of the merged mining support for this project, you can send any amount of your choice to one of theses addresses: -* Bitcoin (BTC): `35kAuFPUS1REXQnGM2TxqMbwkKZ9hA4ZfW` -* Ethereum (ETH): `0x1f6177295A6630858BFA25fD60effA048B307674` -* Litecoin (LTC): `MPoYzJjr7FhRqwgSNNYi6Ai9uQ9MeiR5Yy` -* Monero (XMR): `49nCLA3KtAx5MTWidB3opHif7cwnXXHcn6occpXB8DTe6B4QjcAqG4SH7TrGPVuE4n1ygE6is5nER4ms1Yb1hnmYMkBwN4L` -* Moneta Verde (MCN): `VduTsfyVGBAA2CqdqEh4Vya7B1im7sf1PDbntv1GomLxQpXNxwucsww4ArmR6uLK7PQCY4kVnPGXr8evyXDFNmkk2aBv178S6` +* Bitcoin (BTC): `34GDVuVbuxyYdR8bPZ7g6r12AhPPCrNfXt` +* Bitcoin Cash (BCH): `qpl0gr8u3yu7z4nzep955fqy3w8m6w769sec08u3dp` +* Ethereum (ETH): `0xd4d9a4f22475039f115824b15999a5a8143d424c` +* Litecoin (LTC): `LW169WygGDMBN1PGSr8kNbrFBx94emGWfB` +* Monero (XMR): `4Cf2TfMKhCgJ2vsM3HeBUnYe52tXrvv8X1ajjuQEMUQ8iU8kvUzCSsCEacxFhEmeb2JgPpQ5chdyw3UiTfUgapJBhHdmH87gYyoDR6NMZj` +* Graft (GRFT): `GMPHYf5KRkcAyik7Jw9oHRfJtUdw2Kj5f4VTFJ25AaFVYxofetir8Cnh7S76Q854oMXzwaguL8p5KEz1tm3rn1SA6r6p9dMjuV81yqXCgi` +* Haven (XHV): `hvi1aCqoAZF19J8pijvqnrUkeAeP8Rvr4XyfDMGJcarhbL15KgYKM1hN7kiHMu3fer5k8JJ8YRLKCahDKFgLFgJMYAfngJjDmkZAVuiRP15qv` +* Masari (MSR): `5t5mEm254JNJ9HqRjY9vCiTE8aZALHX3v8TqhyQ3TTF9VHKZQXkRYjPDweT9kK4rJw7dDLtZXGjav2z9y24vXCdRc3DY4daikoNTeK1v4e` +* Stellite (XTL): `SEiStP7SMy1bvjkWc9dd1t2v1Et5q2DrmaqLqFTQQ9H7JKdZuATcPHUbUL3bRjxzxTDYitHsAPqF8EeCLw3bW8ARe8rYRNQQwys1JcJAs3qSH` Credits From 3bce63189dc61221554026a07812e494ca3c96c5 Mon Sep 17 00:00:00 2001 From: muscleman Date: Sun, 7 Apr 2019 15:01:08 -0500 Subject: [PATCH 77/98] Update README.md Updated the credits --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5e9b8524c..f8502d074 100644 --- a/README.md +++ b/README.md @@ -789,6 +789,7 @@ Credits --------- * [Daniel Vandal](https://github.com/dvandal/) - Developer of this project when this project was created. * [fancoder](//github.com/fancoder) - Developer on cryptonote-universal-pool project from which current project is forked. +* [Campurro](//github.com/campurro) - Developer of merge mining support. License ------- From 6adc3ef66c39477cdb3545142e0c187f68b23330 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 9 Apr 2019 20:04:38 +0200 Subject: [PATCH 78/98] Update apiInterfaces.js --- lib/apiInterfaces.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index 6638ed1ab..18f010058 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -105,8 +105,12 @@ module.exports = function(daemonConfig, walletConfig, poolApiConfig){ batchRpcDaemon: function(batchArray, callback){ batchRpc(daemonConfig.host, daemonConfig.port, batchArray, callback); }, - rpcDaemon: function(method, params, callback){ - rpc(daemonConfig.host, daemonConfig.port, method, params, callback); + rpcDaemon: function(method, params, callback, serverConfig){ + if (serverConfig) { + rpc(serverConfig.host, serverConfig.port, method, params, callback); + } else { + rpc(daemonConfig.host, daemonConfig.port, method, params, callback); + } }, rpcWallet: function(method, params, callback){ rpc(walletConfig.host, walletConfig.port, method, params, callback); From 11058f002a28d8dacbc5a4d71c169d14c17714da Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 9 Apr 2019 20:05:07 +0200 Subject: [PATCH 79/98] Update pool.js --- lib/pool.js | 223 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 184 insertions(+), 39 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 2e0b53d06..125e699f1 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -51,6 +51,11 @@ var instanceId = utils.instanceId(); // Pool variables var poolStarted = false; var connectedMiners = {}; +// Get merged mining tag reseved space size +var MERGE_MINING_TAG_RESERVED_SIZE = utils.cnUtil.get_merge_mining_tag_reserved_size(); +var POOL_NONCE_SIZE = 19; // 17 + 2 + +console.log(MERGE_MINING_TAG_RESERVED_SIZE + ', ' + POOL_NONCE_SIZE); // Pool settings var shareTrustEnabled = config.poolServer.shareTrust && config.poolServer.shareTrust.enabled; @@ -71,6 +76,11 @@ if (!config.poolServer.paymentId.addressSeparator) config.poolServer.paymentId.a var validBlockTemplates = []; var currentBlockTemplate; +// Child Block templates +var validChildBlockTemplates = []; +var currentChildBlockTemplate; + + // Difficulty buffer var diff1 = bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16); @@ -140,7 +150,8 @@ function BlockTemplate(template){ this.blob = template.blocktemplate_blob; this.difficulty = template.difficulty; this.height = template.height; - this.reserveOffset = template.reserved_offset; + this.reserveOffset = template.reserved_offset + MERGE_MINING_TAG_RESERVED_SIZE - (POOL_NONCE_SIZE - 2); + console.log(template.reserved_offset + '; ' + this.reserveOffset); this.buffer = new Buffer(this.blob, 'hex'); instanceId.copy(this.buffer, this.reserveOffset + 4, 0, 3); this.previous_hash = new Buffer(32); @@ -156,6 +167,10 @@ BlockTemplate.prototype = { nextBlob: function(){ this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); + if (config.poolServer.mergedMining) { +console.log("convert_blob merged block"); + return utils.cnUtil.convert_blob(this.buffer, cnBlobType, currentChildBlockTemplate.buffer).toString('hex'); + } return utils.cnUtil.convert_blob(this.buffer, cnBlobType).toString('hex'); }, nextBlobWithChildNonce: function(){ @@ -171,10 +186,19 @@ BlockTemplate.prototype = { **/ function getBlockTemplate(callback){ apiInterfaces.rpcDaemon('getblocktemplate', - {reserve_size: 17, wallet_address: config.poolServer.poolAddress}, + {reserve_size: MERGE_MINING_TAG_RESERVED_SIZE, wallet_address: config.poolServer.poolAddress}, callback) } +/** + * Get child block template + **/ +function getChildBlockTemplate(callback){ + apiInterfaces.rpcDaemon('getblocktemplate', + {reserve_size: MERGE_MINING_TAG_RESERVED_SIZE, wallet_address: config.poolServer.poolChildAddress}, + callback, config.childDaemon) +} + /** * Process block template **/ @@ -193,12 +217,39 @@ function processBlockTemplate(template){ } } + +/** + * Process child block template + **/ +function processChildBlockTemplate(template){ + if (currentChildBlockTemplate) + validChildBlockTemplates.push(currentChildBlockTemplate); + + if (validChildBlockTemplates.length > 12) + validChildBlockTemplates.shift(); + + currentChildBlockTemplate = new BlockTemplate(template); + + for (var minerId in connectedMiners){ + var miner = connectedMiners[minerId]; + miner.pushMessage('job', miner.getJob()); + } +} + + /** * Job refresh **/ function jobRefresh(loop, callback){ callback = callback || function(){}; getBlockTemplate(function(error, result){ + if (error){ + log('error', logSystem, 'Error polling getblocktemplate %j', [error]); + if (!poolStarted) log('error', logSystem, 'Could not start pool'); + callback(false); + return; + } + let buffer = new Buffer(result.blocktemplate_blob, 'hex'); let new_hash = new Buffer(32); buffer.copy(new_hash, 0, 7, 39); @@ -207,12 +258,6 @@ function jobRefresh(loop, callback){ setTimeout(function(){ jobRefresh(true); }, config.poolServer.blockRefreshInterval); - if (error){ - log('error', logSystem, 'Error polling getblocktemplate %j', [error]); - if (!poolStarted) log('error', logSystem, 'Could not start pool'); - callback(false); - return; - } //var buffer = new Buffer(result.blocktemplate_blob, 'hex'); //var previous_hash = new Buffer(32); @@ -227,7 +272,28 @@ function jobRefresh(loop, callback){ startPoolServerTcp(function(successful){ poolStarted = true }); } callback(true); - }) + }); + + if (config.poolServer.mergedMining) { + // Get child block template + getChildBlockTemplate(function(cerror, cresult) { + if (cerror){ + log('error', logSystem, 'Error polling getblocktemplate on child daemon %j', [cerror]); + return; + } + + var buffer = new Buffer(cresult.blocktemplate_blob, 'hex'); + var new_hash = new Buffer(32); + buffer.copy(new_hash, 0, 7, 39); + + if (!currentChildBlockTemplate || cresult.height > currentChildBlockTemplate.height) { + log('info', logSystem, 'New child block to mine at height %d w/ difficulty of %d', [cresult.height, cresult.difficulty]); + processChildBlockTemplate(cresult); + } + + }); + } + } /** @@ -247,7 +313,7 @@ var VarDiff = (function(){ /** * Miner **/ -function Miner(id, login, pass, ip, port, agent, workerName, startingDiff, noRetarget, pushMessage){ +function Miner(id, login, pass, ip, port, agent, workerName, startingDiff, noRetarget, pushMessage, childWalletAddress){ this.id = id; this.login = login; this.pass = pass; @@ -259,6 +325,7 @@ function Miner(id, login, pass, ip, port, agent, workerName, startingDiff, noRet } this.workerName = workerName; this.pushMessage = pushMessage; + this.childWalletAddress = childWalletAddress; this.heartbeat(); this.noRetarget = noRetarget; this.difficulty = startingDiff; @@ -343,18 +410,20 @@ Miner.prototype = { return hex; }, getJob: function(){ - if (this.lastBlockHeight === currentBlockTemplate.height && !this.pendingDifficulty && this.cachedJob !== null) { + if (this.lastBlockHeight === currentBlockTemplate.height && this.lastChildBlockHeight === currentChildBlockTemplate.height && !this.pendingDifficulty && this.cachedJob !== null) { return this.cachedJob; } if (!this.proxy) { var blob = currentBlockTemplate.nextBlob(); this.lastBlockHeight = currentBlockTemplate.height; + this.lastChildBlockHeight = currentChildBlockTemplate.height; var target = this.getTargetHex(); var newJob = { id: utils.uid(), extraNonce: currentBlockTemplate.extraNonce, height: currentBlockTemplate.height, + childHeight: currentChildBlockTemplate.height, difficulty: this.difficulty, diffHex: this.diffHex, submissions: [] @@ -375,12 +444,14 @@ Miner.prototype = { var blob = currentBlockTemplate.nextBlobWithChildNonce(); this.lastBlockHeight = currentBlockTemplate.height; + this.lastChildBlockHeight = currentChildBlockTemplate.height; var target = this.getTargetHex(); let newJob = { id: utils.uid(), extraNonce: currentBlockTemplate.extraNonce, height: currentBlockTemplate.height, + childHeight: currentChildBlockTemplate.height, difficulty: this.difficulty, diffHex: this.diffHex, clientPoolLocation: currentBlockTemplate.clientPoolLocation, @@ -397,6 +468,7 @@ Miner.prototype = { blocktemplate_blob: blob, difficulty: currentBlockTemplate.difficulty, height: currentBlockTemplate.height, + childHeight: currentChildBlockTemplate.height, reserved_offset: currentBlockTemplate.reserveOffset, client_nonce_offset: currentBlockTemplate.clientNonceLocation, client_pool_offset: currentBlockTemplate.clientPoolLocation, @@ -460,12 +532,33 @@ function handleMinerMethod(method, params, ip, portData, sendReply, pushMessage) var port = portData.port; var pass = params.pass; + if (!pass) { pass = ""; } var workerName = ''; + var childWalletAddress = ""; + + if (config.poolServer.mergedMining && pass.length > 10) { + if (pass.indexOf('@') >= 0) { + passDelimiterPos = pass.lastIndexOf('@'); + workerName = pass.substr(passDelimiterPos).trim(); + pass = pass.substr(0, passDelimiterPos).trim(); + } + // wallet+paymentId@workerName + var addr = pass.split(config.poolServer.paymentId.addressSeparator); + var address = addr[0] || null; + + if (utils.cnUtil.address_decode(new Buffer(config.poolServer.poolChildAddress)) === utils.cnUtil.address_decode(new Buffer(address))) { + childWalletAddress = pass; + log('info', logSystem, 'Merged mining address in password field: %s', [pass]); + pass = 'x'; + } + + } + if (params.rigid) { workerName = params.rigid.trim(); } else if (pass) { - workerName = pass.trim(); + workerName = (workerName.length > 0)? workerName:pass.trim(); if (pass.indexOf(':') >= 0 && pass.indexOf('@') >= 0) { passDelimiterPos = pass.lastIndexOf(':'); workerName = pass.substr(0, passDelimiterPos).trim(); @@ -520,7 +613,7 @@ function handleMinerMethod(method, params, ip, portData, sendReply, pushMessage) } var minerId = utils.uid(); - miner = new Miner(minerId, login, pass, ip, port, params.agent, workerName, difficulty, noRetarget, pushMessage); + miner = new Miner(minerId, login, pass, ip, port, params.agent, workerName, difficulty, noRetarget, pushMessage, childWalletAddress); connectedMiners[minerId] = miner; sendReply(null, { @@ -616,7 +709,16 @@ function handleMinerMethod(method, params, ip, portData, sendReply, pushMessage) return; } - var shareAccepted = processShare(miner, job, blockTemplate, params); + var childBlockTemplate = currentChildBlockTemplate.height === job.childHeight ? currentChildBlockTemplate : validChildBlockTemplates.filter(function(t){ + return t.height === job.childHeight; + })[0]; + + if (config.poolServer.mergedMining && !childBlockTemplate){ + sendReply('Child Block expired'); + return; + } + + var shareAccepted = processShare(miner, job, blockTemplate, childBlockTemplate, params); miner.checkBan(shareAccepted); if (shareTrustEnabled){ @@ -736,9 +838,15 @@ function IsBannedIp(ip){ /** * Record miner share data **/ -function recordShareData(miner, job, shareDiff, blockCandidate, hashHex, shareType, blockTemplate){ +function recordShareData(miner, job, shareDiff, blockCandidate, hashHex, shareType, blockTemplate, isChildCoin){ var dateNow = Date.now(); var dateNowSeconds = dateNow / 1000 | 0; + var coin = isChildCoin? config.childCoin:config.coin; + var wallet_address = isChildCoin? miner.childWalletAddress:miner.login; + log('info', logSystem, 'En recordShareData %s' + (isChildCoin? ' (child)':''), [wallet_address]); + if (!wallet_address || !coin) { + return; + } var updateScore; // Weighting older shares lower than newer ones to prevent pool hopping @@ -753,37 +861,37 @@ function recordShareData(miner, job, shareDiff, blockCandidate, hashHex, shareTy redis.call('hincrbyfloat', KEYS[1], ARGV[1], score) return {score, tostring(age)} `, - 2 /*keys*/, config.coin + ':scores:roundCurrent', config.coin + ':stats', - /* args */ miner.login, job.difficulty, Date.now(), config.poolServer.slushMining.weight]; + 2 /*keys*/, coin + ':scores:roundCurrent', coin + ':stats', + /* args */ wallet_address, job.difficulty, Date.now(), config.poolServer.slushMining.weight]; } else { job.score = job.difficulty; - updateScore = ['hincrbyfloat', config.coin + ':scores:roundCurrent', miner.login, job.score] + updateScore = ['hincrbyfloat', coin + ':scores:roundCurrent', wallet_address, job.score] } var redisCommands = [ updateScore, - ['hincrby', config.coin + ':shares_actual:roundCurrent', miner.login, job.difficulty], - ['zadd', config.coin + ':hashrate', dateNowSeconds, [job.difficulty, miner.login, dateNow].join(':')], - ['hincrby', config.coin + ':workers:' + miner.login, 'hashes', job.difficulty], - ['hset', config.coin + ':workers:' + miner.login, 'lastShare', dateNowSeconds], - ['expire', config.coin + ':workers:' + miner.login, (86400 * cleanupInterval)], - ['expire', config.coin + ':payments:' + miner.login, (86400 * cleanupInterval)] + ['hincrby', coin + ':shares_actual:roundCurrent', wallet_address, job.difficulty], + ['zadd', coin + ':hashrate', dateNowSeconds, [job.difficulty, wallet_address, dateNow].join(':')], + ['hincrby', coin + ':workers:' + wallet_address, 'hashes', job.difficulty], + ['hset', coin + ':workers:' + wallet_address, 'lastShare', dateNowSeconds], + ['expire', coin + ':workers:' + wallet_address, (86400 * cleanupInterval)], + ['expire', coin + ':payments:' + wallet_address, (86400 * cleanupInterval)] ]; if (miner.workerName) { - redisCommands.push(['zadd', config.coin + ':hashrate', dateNowSeconds, [job.difficulty, miner.login + '~' + miner.workerName, dateNow].join(':')]); - redisCommands.push(['hincrby', config.coin + ':unique_workers:' + miner.login + '~' + miner.workerName, 'hashes', job.difficulty]); - redisCommands.push(['hset', config.coin + ':unique_workers:' + miner.login + '~' + miner.workerName, 'lastShare', dateNowSeconds]); - redisCommands.push(['expire', config.coin + ':unique_workers:' + miner.login + '~' + miner.workerName, (86400 * cleanupInterval)]); + redisCommands.push(['zadd', coin + ':hashrate', dateNowSeconds, [job.difficulty, wallet_address + '~' + miner.workerName, dateNow].join(':')]); + redisCommands.push(['hincrby', coin + ':unique_workers:' + wallet_address + '~' + miner.workerName, 'hashes', job.difficulty]); + redisCommands.push(['hset', coin + ':unique_workers:' + wallet_address + '~' + miner.workerName, 'lastShare', dateNowSeconds]); + redisCommands.push(['expire', coin + ':unique_workers:' + wallet_address + '~' + miner.workerName, (86400 * cleanupInterval)]); } if (blockCandidate){ - redisCommands.push(['hset', config.coin + ':stats', 'lastBlockFound', Date.now()]); - redisCommands.push(['rename', config.coin + ':scores:roundCurrent', config.coin + ':scores:round' + job.height]); - redisCommands.push(['rename', config.coin + ':shares_actual:roundCurrent', config.coin + ':shares_actual:round' + job.height]); - redisCommands.push(['hgetall', config.coin + ':scores:round' + job.height]); - redisCommands.push(['hgetall', config.coin + ':shares_actual:round' + job.height]); + redisCommands.push(['hset', coin + ':stats', 'lastBlockFound', Date.now()]); + redisCommands.push(['rename', coin + ':scores:roundCurrent', coin + ':scores:round' + job.height]); + redisCommands.push(['rename', coin + ':shares_actual:roundCurrent', coin + ':shares_actual:round' + job.height]); + redisCommands.push(['hgetall', coin + ':scores:round' + job.height]); + redisCommands.push(['hgetall', coin + ':shares_actual:round' + job.height]); } redisClient.multi(redisCommands).exec(function(err, replies){ @@ -807,7 +915,7 @@ function recordShareData(miner, job, shareDiff, blockCandidate, hashHex, shareTy var totalShares = Object.keys(workerShares).reduce(function(p, c){ return p + parseInt(workerShares[c]) }, 0); - redisClient.zadd(config.coin + ':blocks:candidates', job.height, [ + redisClient.zadd(coin + ':blocks:candidates', job.height, [ hashHex, Date.now() / 1000 | 0, blockTemplate.difficulty, @@ -824,19 +932,20 @@ function recordShareData(miner, job, shareDiff, blockCandidate, hashHex, shareTy 'HASH': hashHex, 'DIFFICULTY': blockTemplate.difficulty, 'SHARES': totalShares, - 'MINER': miner.login.substring(0,7)+'...'+miner.login.substring(miner.login.length-7) + 'MINER': wallet_address.substring(0,7)+'...'+wallet_address.substring(miner.login.length-7) }); } }); - - log('info', logSystem, 'Accepted %s share at difficulty %d/%d from %s@%s', [shareType, job.difficulty, shareDiff, miner.login, miner.ip]); + if (!isChildCoin) { + log('info', logSystem, 'Accepted %s share at difficulty %d/%d from %s@%s', [shareType, job.difficulty, shareDiff, miner.login, miner.ip]); + } } /** * Process miner share data **/ -function processShare(miner, job, blockTemplate, params){ +function processShare(miner, job, blockTemplate, childBlockTemplate, params){ var nonce = params.nonce; var resultHash = params.result; var template = new Buffer(blockTemplate.buffer.length); @@ -849,7 +958,12 @@ function processShare(miner, job, blockTemplate, params){ template.writeUInt32BE(params.poolNonce, job.clientPoolLocation); template.writeUInt32BE(params.workerNonce, job.clientNonceLocation); } - var shareBuffer = utils.cnUtil.construct_block_blob(template, new Buffer(nonce, 'hex'), cnBlobType); + var shareBuffer = null; + if (config.poolServer.mergedMining) { + shareBuffer = utils.cnUtil.construct_block_blob(template, new Buffer(nonce, 'hex'), cnBlobType, childBlockTemplate.buffer); + } else { + shareBuffer = utils.cnUtil.construct_block_blob(template, new Buffer(nonce, 'hex'), cnBlobType); + } var convertedBlob; var hash; @@ -861,6 +975,7 @@ function processShare(miner, job, blockTemplate, params){ } else { convertedBlob = utils.cnUtil.convert_blob(shareBuffer, cnBlobType); + var hard_fork_version = convertedBlob[0]; hash = cryptoNight(convertedBlob, cnVariant); log('info', logSystem, 'Mining pool algorithm: %s variant %d, Hard fork version: %d', [cnAlgorithm, cnVariant, hard_fork_version]); @@ -903,6 +1018,36 @@ function processShare(miner, job, blockTemplate, params){ recordShareData(miner, job, hashDiff.toString(), false, null, shareType); } + if (config.poolServer.mergedMining){ + if (hashDiff.ge(childBlockTemplate.difficulty)){ + + var mergedBuffer = utils.cnUtil.merge_blocks(shareBuffer, childBlockTemplate.buffer); + apiInterfaces.rpcDaemon('submitblock', [mergedBuffer.toString('hex')], function(error, result){ + if (error){ + log('error', logSystem, 'Error submitting child block at height %d from %s@%s, share type: "%s" - %j', [job.height, miner.login, miner.ip, shareType, error]); + if (miner.childWalletAddress) { + recordShareData(miner, job, hashDiff.toString(), false, null, shareType, null, true); + } + } + else{ + var blockFastHash = utils.cnUtil.get_block_id(mergedBuffer, 2).toString('hex'); + log('info', logSystem, + 'Child Block %s found at height %d by miner %s@%s - submit result: %j', + [blockFastHash.substr(0, 6), job.height, miner.login, miner.ip, result] + ); + if (miner.childWalletAddress) { + recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate, true); + } + jobRefresh(); + } + }, config.childDaemon); + } else { + if (miner.childWalletAddress) { + recordShareData(miner, job, hashDiff.toString(), false, null, shareType, null, true); + } + } + } + return true; } From f9582c860ff8cedc7d799cfc8b0ac028b771f511 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 9 Apr 2019 20:06:35 +0200 Subject: [PATCH 80/98] Create config.js --- config.js | 315 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 config.js diff --git a/config.js b/config.js new file mode 100644 index 000000000..1eefc45c8 --- /dev/null +++ b/config.js @@ -0,0 +1,315 @@ +{ + "poolHost": "infinium8.minercountry.com", + + "coin": "infinium8", + "symbol": "INF8", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 90, + + "childCoin": "monetaverde", + + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "mergedMining": true, + "clusterForks": 1, + "poolAddress": "**** YOUR INF8 WALLET ADDRESS *******", + "poolChildAddress": "**** YOUR MCN WALLET ADDRESS ********", + "intAddressPrefix": "", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + } + ], + "varDiff": { + "minDiff": 500, + "maxDiff": 100000000, + "targetTime": 45, + "retargetTime": 40, + "variancePercent": 30, + "maxJump": 60 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 30, + "maxAddresses": 5, + "mixin": 3, + "priority": 0, + "transferFee": 10000000000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 10000000000000, + "maxPayment": null, + "maxTransactionAmount": 1000000000000000, + "denomination": 10000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.5, + "devDonation": 0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8999, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 27855 + }, + + "childDaemon": { + "host": "192.168.1.39", + "port": 26081 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 27856 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} From fae10911ae7ddc982af3f15b1aa496c5ae90e6ad Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 9 Apr 2019 20:07:56 +0200 Subject: [PATCH 81/98] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6e8fca89..51546bdfc 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "base58-native": "*", "bignum": "*", "cli-color": "*", - "cryptoforknote-util": "git://github.com/campurro/node-cryptoforknote-util.git", + "cryptoforknote-util": "https://github.com/Cideg/node-cryptoforknote-util.git", "cryptonight-hashing": "git://github.com/MoneroOcean/node-cryptonight-hashing.git", "dateformat": "*", "mailgun.js": "*", From dd038f8faafff38d3bbcb24e45a457fcaaa35906 Mon Sep 17 00:00:00 2001 From: muscleman Date: Sat, 4 May 2019 20:26:23 -0500 Subject: [PATCH 82/98] Update pool.js --- lib/pool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pool.js b/lib/pool.js index e3549d433..b076e895f 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -1037,7 +1037,7 @@ function processShare(miner, job, blockTemplate, childBlockTemplate, params){ [blockFastHash.substr(0, 6), job.childHeight, miner.login, miner.ip, result] ); if (miner.childWalletAddress) { - recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate, true); + recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, childBlockTemplate, true); } jobRefresh(); } From b8014cf1de93058bfe8c73a962bc2b96d7afe1fd Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 7 May 2019 09:30:38 +0200 Subject: [PATCH 83/98] Create campurro-merged-website_tamplate --- campurro-merged-website_tamplate | 1 + 1 file changed, 1 insertion(+) create mode 100644 campurro-merged-website_tamplate diff --git a/campurro-merged-website_tamplate b/campurro-merged-website_tamplate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/campurro-merged-website_tamplate @@ -0,0 +1 @@ + From b7f4aa1e700ac92dbf6f0f106e855f45ddde387e Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 7 May 2019 09:32:57 +0200 Subject: [PATCH 84/98] Delete campurro-merged-website_tamplate --- campurro-merged-website_tamplate | 1 - 1 file changed, 1 deletion(-) delete mode 100644 campurro-merged-website_tamplate diff --git a/campurro-merged-website_tamplate b/campurro-merged-website_tamplate deleted file mode 100644 index 8b1378917..000000000 --- a/campurro-merged-website_tamplate +++ /dev/null @@ -1 +0,0 @@ - From ac7e2fbad5c29ae6ed35d77aa770aebe1080ee64 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 7 May 2019 09:33:35 +0200 Subject: [PATCH 85/98] Create index.html --- campurro-merged-website-tamplate/index.html | 215 ++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 campurro-merged-website-tamplate/index.html diff --git a/campurro-merged-website-tamplate/index.html b/campurro-merged-website-tamplate/index.html new file mode 100644 index 000000000..b539f4bef --- /dev/null +++ b/campurro-merged-website-tamplate/index.html @@ -0,0 +1,215 @@ + + + + + + + + (42) + (ZLS) Merged Mining Pool - MinerCountry + + + + + + + + + + + + + + + + + +
+ + + + + +
+
Network: N/A
+
Pool: N/A
+
You: N/A
+
Stats Updated  
+
+
+ + +
+
+

+
+ +
+ + + + + + + + + + + + From b302cf36bd494fd575b8de74f5c91dd53e947055 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 7 May 2019 09:34:34 +0200 Subject: [PATCH 86/98] Create config.js --- campurro-merged-website-tamplate/config.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 campurro-merged-website-tamplate/config.js diff --git a/campurro-merged-website-tamplate/config.js b/campurro-merged-website-tamplate/config.js new file mode 100644 index 000000000..69fda3c9a --- /dev/null +++ b/campurro-merged-website-tamplate/config.js @@ -0,0 +1,20 @@ +var api = "http://207.180.227.127:8111"; +var apiMerged = "http://207.180.227.127:8117"; + +var email = ""; +var telegram = ""; +var discord = ""; + +var marketCurrencies = ["{symbol}-BTC", "{symbol}-USD", "{symbol}-EUR"]; + +var blockchainExplorer = "http://173.249.48.79:8081/block/{id}"; +var transactionExplorer = "http://173.249.48.79:8081/tx/{id}"; + +var blockchainExplorerMerged = "http://explorer.coin42.co/search?value={id}"; +var transactionExplorerMerged = "http://explorer.coin42.co/search?value={id}"; + + + + +var themeCss = "themes/default.css"; +var defaultLang = 'en'; From d2cdc9ba94ffd53155e654a41187a70befd7cf3b Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 7 May 2019 09:34:58 +0200 Subject: [PATCH 87/98] Create admin.html --- campurro-merged-website-tamplate/admin.html | 114 ++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 campurro-merged-website-tamplate/admin.html diff --git a/campurro-merged-website-tamplate/admin.html b/campurro-merged-website-tamplate/admin.html new file mode 100644 index 000000000..c2356aac0 --- /dev/null +++ b/campurro-merged-website-tamplate/admin.html @@ -0,0 +1,114 @@ + + + + + + + Mining Pool Admin Panel + + + + + + + + + + + + + + + + + + + + + + From a0df2249d41f4aae7af22aa973cf263cebc924df Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 7 May 2019 09:37:39 +0200 Subject: [PATCH 88/98] Add files via upload --- .../pages/admin/faq.html | 78 ++ .../pages/admin/getting_started.html | 447 ++++++++++ .../pages/admin/home.html | 420 ++++++++++ .../pages/admin/market.html | 339 ++++++++ .../pages/admin/monitoring.html | 146 ++++ .../pages/admin/payments.html | 200 +++++ .../pages/admin/pool_blocks.html | 314 +++++++ .../pages/admin/ports.html | 85 ++ .../pages/admin/settings.html | 254 ++++++ .../pages/admin/statistics.html | 170 ++++ .../pages/admin/tools.html | 124 +++ .../pages/admin/top10miners.html | 68 ++ .../pages/admin/userslist.html | 96 +++ .../pages/admin/worker_stats.html | 624 ++++++++++++++ .../pages/faq.html | 78 ++ .../pages/getting_started.html | 385 +++++++++ .../pages/home.html | 403 +++++++++ .../pages/market.html | 339 ++++++++ .../pages/monetaverde.html | 766 ++++++++++++++++++ .../pages/payments.html | 312 +++++++ .../pages/plura.html | 357 ++++++++ .../pages/pool_blocks.html | 357 ++++++++ .../pages/settings.html | 254 ++++++ .../pages/top10miners.html | 68 ++ .../pages/worker_stats.html | 766 ++++++++++++++++++ 25 files changed, 7450 insertions(+) create mode 100644 campurro-merged-website-tamplate/pages/admin/faq.html create mode 100644 campurro-merged-website-tamplate/pages/admin/getting_started.html create mode 100644 campurro-merged-website-tamplate/pages/admin/home.html create mode 100644 campurro-merged-website-tamplate/pages/admin/market.html create mode 100644 campurro-merged-website-tamplate/pages/admin/monitoring.html create mode 100644 campurro-merged-website-tamplate/pages/admin/payments.html create mode 100644 campurro-merged-website-tamplate/pages/admin/pool_blocks.html create mode 100644 campurro-merged-website-tamplate/pages/admin/ports.html create mode 100644 campurro-merged-website-tamplate/pages/admin/settings.html create mode 100644 campurro-merged-website-tamplate/pages/admin/statistics.html create mode 100644 campurro-merged-website-tamplate/pages/admin/tools.html create mode 100644 campurro-merged-website-tamplate/pages/admin/top10miners.html create mode 100644 campurro-merged-website-tamplate/pages/admin/userslist.html create mode 100644 campurro-merged-website-tamplate/pages/admin/worker_stats.html create mode 100644 campurro-merged-website-tamplate/pages/faq.html create mode 100644 campurro-merged-website-tamplate/pages/getting_started.html create mode 100644 campurro-merged-website-tamplate/pages/home.html create mode 100644 campurro-merged-website-tamplate/pages/market.html create mode 100644 campurro-merged-website-tamplate/pages/monetaverde.html create mode 100644 campurro-merged-website-tamplate/pages/payments.html create mode 100644 campurro-merged-website-tamplate/pages/plura.html create mode 100644 campurro-merged-website-tamplate/pages/pool_blocks.html create mode 100644 campurro-merged-website-tamplate/pages/settings.html create mode 100644 campurro-merged-website-tamplate/pages/top10miners.html create mode 100644 campurro-merged-website-tamplate/pages/worker_stats.html diff --git a/campurro-merged-website-tamplate/pages/admin/faq.html b/campurro-merged-website-tamplate/pages/admin/faq.html new file mode 100644 index 000000000..cd3100db4 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/faq.html @@ -0,0 +1,78 @@ +

Frequently Asked Questions (FAQ)

+ +

What is difficulty?

+
Difficulty is a measure of how difficult it is to find a hash below a given target.
+ +

What is luck?

+
Mining is probabilistic in nature: if you find a block earlier than you statistically should on average you are lucky if it takes longer, you are unlucky. In a perfect World pool would find a block on 100% luck value. Less then 100% means the pool was lucky. More then 100% means the pool was unlucky.
+ +

What is share?

+
Share is a possible valid hash for the block. Shares are beings sent by your rigs to the pool to prove their work.
+ +

What is block?

+
Transaction data is recorded in blocks. New transactions are being processes by miners into new blocks which are added to the end of the blockchain.
+ +

How long does it take to find a block?

+
It depends on amount of active miners. The more miners work on pool → the more hashrate pool has → the more blocks are found by the pool. However the more miners are active → the less reward you get from each block found.
+ +

Which payouts scheme is used?

+
Proportional (Share-based): Every time a block is found, its reward is split between miners according to the number of shares they submitted.
+ +

How current payout estimate is calculated?

+
The estimated payout is a calculated using your percentage of valid shares on the total for current round. This percentage is then applied to the reward of the last block found by the network.
+ +

I have been mining on this pool for 1 hour but still have not received any payouts. WTF?

+
As soon as the block is found you will get your reward. Please wait a little bit more time.
+ +

My hashrate is wrong! Why?

+
Since you start to mine your hashrate grows gradually. Please wait. The pool determines your hashrate based on the amount of shares sent by your mining rigs (workers). This value could be a little bit different from reported hasrate (in your mining software).
+ +
+

How to use the Telegram Bot?

+
+
Telegram bot will report via Telegram, when your worker is connected, disconnected or banned, and payments have been made to your wallet. Also you can check pool stats directly from him.
+
+ + Bot name: @Pool_bot
+
+ + Commands:
+ /stats - Pool statistics
+ /blocks - Blocks notifications (enable or disable)
+ /report address - Miner statistics
+ /notify address - Miner notifications (enable or disable)
+
+ + Multiple addresses monitoring available.
+
+
+ + + \ No newline at end of file diff --git a/campurro-merged-website-tamplate/pages/admin/getting_started.html b/campurro-merged-website-tamplate/pages/admin/getting_started.html new file mode 100644 index 000000000..1c858eec6 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/getting_started.html @@ -0,0 +1,447 @@ + +
+
+

Connection Details

+
+
+
Mining Pool Address:
+
Algorithm:
+
+
+ +

Username

+
+
+
This is your wallet address
+
Exchange Payment ID: address.paymentID
+
Difficulty locking: address+diff
+
+
+ +

Password

+
+
+
This is your worker name
+
+
+
+ + +
+

Mining Ports

+
+
+ + + + + + + + + + + + + + + +
PortStarting DifficultyDescription
+
+
+
+ +
+ + +

Mining Applications

+ + +
+

Generate your custom configuration to mine on our pool

+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
App Name Architecture Features Download Configuration
XMR StakCPU & GPU (AMD/NVIDIA)Easy to use CPU + GPU Mining AppDownloadSee more
+
+ +
"pool_list": [
+    {
+        "pool_address": "POOL_HOST:PORT",
+        "wallet_address": "YOUR_WALLET_ADDRESS",
+        "rig_id": "YOUR_WORKER_NAME",
+        "pool_password": "x",
+        "use_nicehash": false,
+        "use_tls": false, /* Set to true if you are using an SSL port */
+        "tls_fingerprint": "",
+        "pool_weight": 1
+    },
+],
+"currency": "",
+ +
+
XMRigCPULigthweight but powerful CPU Mining AppDownloadSee more
+
+ +
"pools": [
+    {
+        "url": "POOL_HOST:PORT",
+        "user": "YOUR_WALLET_ADDRESS",
+        "pass": "YOUR_WORKER_NAME",
+        "keepalive": true,
+        "nicehash": false,
+        "variant": 1
+    }
+],
+ +
+
XMRig-AMDOpenCL (AMD)XMRIG version for AMD GPUDownloadSee more
+
+ +
"pools": [
+    {
+        "url": "POOL_HOST:PORT",
+        "user": "YOUR_WALLET_ADDRESS",
+        "pass": "YOUR_WORKER_NAME"
+        "keepalive": true,
+        "nicehash": false,
+        "variant": 1
+    }
+],
+ +
+
XMRig-NVIDIACuda (Nvidia)XMRIG version for Nvidia GPUDownloadSee more
+
+ +
"pools": [
+    {
+        "url": "POOL_HOST:PORT",
+        "user": "YOUR_WALLET_ADDRESS",
+        "pass": "YOUR_WORKER_NAME"
+        "keepalive": true,
+        "nicehash": false,
+        "variant": 1
+    }
+],
+ +
+
XMRigCCCPUXMRIG Fork, optimized with remote controlDownloadSee more
+
+ +
"pools": [
+    {
+        "url": "POOL_HOST:PORT",
+        "user": "YOUR_WALLET_ADDRESS",
+        "pass": "YOUR_WORKER_NAME",
+        "keepalive": true,
+        "nicehash": false
+    },
+],
+ +
+
SRBMiner Cryptonight AMD GPU MinerOpenCL (AMD)XMRIG version for AMD GPUDownloadSee more
+
+ +

+{
+/* Type can be : normal, lite, heavy  */
+"cryptonight_type" : "heavy",
+
+/* Intensity 0-> auto intensity, or value from 1-200  */
+"intensity" : 0,
+
+/* To use 2 threads per card set double_threads to true  */
+"double_threads" : false,
+
+/* Gpu target temperature, leave it on 0 if you don't want to use this option  */
+"target_temperature" : 0,
+
+/* If you use a SSL/TLS encrypted pool set this to true*/
+"pool_use_tls" : false,
+
+/* Mining pool address WITHOUT the stratum+tcp:// or stratum+tls:// part  */
+"pool" : "POOL_HOST:PORT",
+
+/* Address of you wallet  */
+"wallet" : "YOUR_WALLET_ADDRESS",
+
+/* Password for your wallet, probably just x  */
+"password" : "YOUR_WORKER_NAME",
+
+/* Location for devfee servers, to get better latency  */
+"location" : "europe",
+
+/* If you want to log console output, put a filename here  */
+"log_file" : "",
+
+/* Charity mining, if you want to disable charity mining just delete these lines, or set values to "" (empty) */
+"charity_pool" : "POOL_HOST:PORT",
+"charity_wallet" : "YOUR_WALLET_ADDRESS",
+"charity_password" : "YOUR_WORKER_NAME",
+
+/* Settings for each GPU manually */
+/* Put in devices that you want to use, if you for ex. don't want to use gpu 2, just don't insert it,like in this example */
+/* Id starts from 0 , not from 1 !! */
+/* To get a list of available devices with their id's, use --listdevices parameter */
+/* This is just an example, edit it and remove comment lines (the slash and star) !! */
+/*"gpu_conf" : 
+[ 
+	{ "id" : 0, "intensity" : 0, "double_threads" : true},
+	{ "id" : 1, "intensity" : 60, "double_threads" : false},
+	{ "id" : 3, "intensity" : 50, "double_threads" : true},
+	{ "id" : 4, "intensity" : 0, "double_threads" : true},
+]*/
+}		    
+	    
+            
+
+
+ + + diff --git a/campurro-merged-website-tamplate/pages/admin/home.html b/campurro-merged-website-tamplate/pages/admin/home.html new file mode 100644 index 000000000..aab1238e4 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/home.html @@ -0,0 +1,420 @@ +
+ + +
+ +
+
+
Pool Information
+
+
+
Why Should You Choose This Pool?
+ - Decentralization - It is important for the health of the network and the coin.
+ - Fairness - This pool make use of the RBPPS (Prop.) Payment Model to ensure every miners receive a fair payout.
+ - Convenient: Can mine directly to Exchanges, Variable and Fixed Difficulty and custom payout threshold.
+ - Stay informed - Worker hash-rate charts, Email & Telegram notifications for your workers for when blocks are found.
+ - Community & Support - Dedicated Telegram channel with a wonderful community always willing to assist.

+ This is only a few reasons, we trust that you will have great results and please feel free to contact us should you need any assistance. +
+
+
+
+ +
+
+
Coin Information
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+
Pool Hash Rate
+
N/A (0%)
+
+
+
+ + +
+
+
+ +
+
+
Blocks Found
+
N/A (Never)
+
+
+
+ + +
+
+
+ +
+
+
Blocks Found Every
+
N/A (estimated)
+
+
+
+ + +
+
+
+ +
+
+
Current Effort
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Network Hash Rate
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Difficulty
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Blockchain Height
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Last Reward
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Connected Miners
+
N/A (N/A workers)
+
+
+
+ + +
+
+
+ +
+
+
Pool Fee
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Minimum Payout
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Payment Interval
+
N/A
+
+
+
+ + +
+
+
Last Hash
+
+ +
(Never)
+
+
+
+ +
+ + +
+
+
+

Hash Rate

+
+ + +
+
+
+
+
+

Difficulty

+
+ + +
+
+
+
+
+

Miners

+
+ + +
+
+
+
+
+

Workers

+
+ + +
+
+
+
+ + + diff --git a/campurro-merged-website-tamplate/pages/admin/market.html b/campurro-merged-website-tamplate/pages/admin/market.html new file mode 100644 index 000000000..c4bdb3bdd --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/market.html @@ -0,0 +1,339 @@ + +
+
Loading market prices ...
+ +
+
+

Price in USD

+
+ + +
+
+
+
+
+

Hash/USD

+
+ + +
+
+
+
+ + +
+

Estimate Mining Profits

+
+
+ +
+ + +
+ = +

per day
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/campurro-merged-website-tamplate/pages/admin/monitoring.html b/campurro-merged-website-tamplate/pages/admin/monitoring.html new file mode 100644 index 000000000..5e0707d87 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/monitoring.html @@ -0,0 +1,146 @@ + + + + +
+
+
+
+
+ + + diff --git a/campurro-merged-website-tamplate/pages/admin/payments.html b/campurro-merged-website-tamplate/pages/admin/payments.html new file mode 100644 index 000000000..f890e8eaf --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/payments.html @@ -0,0 +1,200 @@ + +
+ + +
+
+
+ +
+
+
Total Payments
+
N/A (N/A miners)
+
+
+
+ + +
+
+
+ +
+
+
Minimum Payout
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Payment Interval
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Denomination Unit
+
N/A
+
+
+
+ + +
+
+
Payment system
+
+

This pool uses the "Score" payment system, under which later + shares submitted by miners receive more weight in determining the miner's reward than earlier shares. + +

For more details (including the specific mathematics involved) see the slushpool reward + description.

+
+
+
+
+ +
+ + +
+
+ + + + + + + + + + + + + + +
Time SentTransaction HashAmountFeeMixinPayees
+
+
+ +

+ +

+ + + diff --git a/campurro-merged-website-tamplate/pages/admin/pool_blocks.html b/campurro-merged-website-tamplate/pages/admin/pool_blocks.html new file mode 100644 index 000000000..0862d0013 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/pool_blocks.html @@ -0,0 +1,314 @@ + +
+ + +
+
+
+ +
+
+
Total Blocks Mined
+
N/A (Never)
+
+
+
+ + +
+
+
+ +
+
+
Maturity Requirement
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Average Luck
+
N/A
+
+
+
+ +
+ + +
+

Blocks found

+
+ + +
+
+ + +
+
+ + + + + + + + + + + + + + +
Time FoundRewardHeightDifficultyBlock HashEffortStatus
+
+
+ +

+ +

+ + + diff --git a/campurro-merged-website-tamplate/pages/admin/ports.html b/campurro-merged-website-tamplate/pages/admin/ports.html new file mode 100644 index 000000000..04b7a7f25 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/ports.html @@ -0,0 +1,85 @@ +

Current Ports Usage (0 users)

+ + + + + +
+
+ + + + + + + + + + +
PortConnected Users 
+
+
+ + + + diff --git a/campurro-merged-website-tamplate/pages/admin/settings.html b/campurro-merged-website-tamplate/pages/admin/settings.html new file mode 100644 index 000000000..2acdf0b54 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/settings.html @@ -0,0 +1,254 @@ + +

Verification fields

+ +
+

In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.

+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +

Set your minimal payout level

+
+

If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.

+
+ +
+ +
+
+ +
+
+
+ + +
+

Enable email notifications

+
+

This pool will send out email notification when a block is found and whenever a payout happens.

+
+ +
+ +
+
+ + +
+
+
+
+ + diff --git a/campurro-merged-website-tamplate/pages/admin/statistics.html b/campurro-merged-website-tamplate/pages/admin/statistics.html new file mode 100644 index 000000000..e1fdc9d73 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/statistics.html @@ -0,0 +1,170 @@ +

Pool Statistics

+ + +
+ + +
+
+
+ +
+
+
Total Mined
+
...
+
+
+
+ + +
+
+
+ +
+
+
Total Paid
+
...
+
+
+
+ + +
+
+
+ +
+
+
Total Owed
+
...
+
+
+
+ + +
+
+
+ +
+
+
Profit (before tx fees)
+
...
+
+
+
+ + +
+
+
+ +
+
+
Registered Addresses
+
...
+
+
+
+ + +
+
+
+ +
+
+
Blocks Unlocked
+
...
+
+
+
+ + +
+
+
+ +
+
+
Blocks Orphaned
+
... (0%)
+
+
+
+ + +
+
+
+ +
+
+
Average Luck (shares/diff)
+
...
+
+
+
+ +
+ + + diff --git a/campurro-merged-website-tamplate/pages/admin/tools.html b/campurro-merged-website-tamplate/pages/admin/tools.html new file mode 100644 index 000000000..ae54c36a8 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/tools.html @@ -0,0 +1,124 @@ + +

Test E-Mail notifications

+ +
+
+ +
+
+
+ +
+
+ +
+
+
+ + +

Test Telegram Channel notifications

+ +
+
+ +
+
+
+ +
+
+
+ + diff --git a/campurro-merged-website-tamplate/pages/admin/top10miners.html b/campurro-merged-website-tamplate/pages/admin/top10miners.html new file mode 100644 index 000000000..f8f365145 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/top10miners.html @@ -0,0 +1,68 @@ + +

Top 10 miners

+
+
+ + + + + + + + + + + + +
#MinerHash RateLast Share SubmittedTotal Hashes Submitted
+
+
+ + + \ No newline at end of file diff --git a/campurro-merged-website-tamplate/pages/admin/userslist.html b/campurro-merged-website-tamplate/pages/admin/userslist.html new file mode 100644 index 000000000..7bb6ce5a1 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/userslist.html @@ -0,0 +1,96 @@ +

Users List (0 users)

+ + + + + +
+
+ + + + + + + + + + + + + + +
WalletHashrate Hashes Pending Paid Last share
+
+
+ + + diff --git a/campurro-merged-website-tamplate/pages/admin/worker_stats.html b/campurro-merged-website-tamplate/pages/admin/worker_stats.html new file mode 100644 index 000000000..b9a3f3566 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/admin/worker_stats.html @@ -0,0 +1,624 @@ + +

Your Stats & Payment History

+ +
+
+ + +
+
+ + +
+

  Hash Rate

+
+
+
Current Hash Rate:
+
Average 1/6/24-hour Hash Rate: + / /
+
Last Share Submitted:
+
Total Hashes Submitted:
+
+
+
+ + +
+
+
+
+ + +
+

  Payments

+
+
+
Pending Balance:
+
Total Paid:
+
Round contribution: % + (shares), % (score)
+
Current Payout Estimate:
+
+
+
+ + +
+
+
+
+ + +

  Workers Statistics

+
+
+ + + + + + + + + + + + + + + + +
Status Worker Name Hash Rate HR (1h) HR (6h) HR (24h) Last Share Submitted Total Hashes Submitted
+
+
+ + +

  Payments History

+
+
+ + + + + + + + + + + + +
Time SentTransaction HashAmountMixin
+
+
+ +

+ +

+ +
+ + + diff --git a/campurro-merged-website-tamplate/pages/faq.html b/campurro-merged-website-tamplate/pages/faq.html new file mode 100644 index 000000000..cd3100db4 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/faq.html @@ -0,0 +1,78 @@ +

Frequently Asked Questions (FAQ)

+ +

What is difficulty?

+
Difficulty is a measure of how difficult it is to find a hash below a given target.
+ +

What is luck?

+
Mining is probabilistic in nature: if you find a block earlier than you statistically should on average you are lucky if it takes longer, you are unlucky. In a perfect World pool would find a block on 100% luck value. Less then 100% means the pool was lucky. More then 100% means the pool was unlucky.
+ +

What is share?

+
Share is a possible valid hash for the block. Shares are beings sent by your rigs to the pool to prove their work.
+ +

What is block?

+
Transaction data is recorded in blocks. New transactions are being processes by miners into new blocks which are added to the end of the blockchain.
+ +

How long does it take to find a block?

+
It depends on amount of active miners. The more miners work on pool → the more hashrate pool has → the more blocks are found by the pool. However the more miners are active → the less reward you get from each block found.
+ +

Which payouts scheme is used?

+
Proportional (Share-based): Every time a block is found, its reward is split between miners according to the number of shares they submitted.
+ +

How current payout estimate is calculated?

+
The estimated payout is a calculated using your percentage of valid shares on the total for current round. This percentage is then applied to the reward of the last block found by the network.
+ +

I have been mining on this pool for 1 hour but still have not received any payouts. WTF?

+
As soon as the block is found you will get your reward. Please wait a little bit more time.
+ +

My hashrate is wrong! Why?

+
Since you start to mine your hashrate grows gradually. Please wait. The pool determines your hashrate based on the amount of shares sent by your mining rigs (workers). This value could be a little bit different from reported hasrate (in your mining software).
+ +
+

How to use the Telegram Bot?

+
+
Telegram bot will report via Telegram, when your worker is connected, disconnected or banned, and payments have been made to your wallet. Also you can check pool stats directly from him.
+
+ + Bot name: @Pool_bot
+
+ + Commands:
+ /stats - Pool statistics
+ /blocks - Blocks notifications (enable or disable)
+ /report address - Miner statistics
+ /notify address - Miner notifications (enable or disable)
+
+ + Multiple addresses monitoring available.
+
+ + + + \ No newline at end of file diff --git a/campurro-merged-website-tamplate/pages/getting_started.html b/campurro-merged-website-tamplate/pages/getting_started.html new file mode 100644 index 000000000..08aacaf2a --- /dev/null +++ b/campurro-merged-website-tamplate/pages/getting_started.html @@ -0,0 +1,385 @@ + + +
+
+

Connection Details

+
+
+
Mining Pool Address:
+
Algorithm:
+
+
+ +

Username

+
+
+
This is your wallet address
+
Exchange Payment ID: address.paymentID
+
Difficulty locking: address+diff
+
+
+ +

Password

+
+
+
Use your Zelerius ZLS wallet address as password for merged mining.
+
Exchange Payment ID: address+paymentID
+
Worker Name: address@workerName
+
+
+ +
+ + +
+

Mining Ports

+
+
+ + + + + + + + + + + + + + + +
PortStarting DifficultyDescription
+
+
+
+ +
+ + +

Mining Applications

+ + +
+

Generate your custom configuration to mine on our pool

+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
App Name Architecture Features Download Configuration
XMR StakCPU & GPU (AMD/NVIDIA)Easy to use CPU + GPU Mining AppDownloadSee more
+
+ +
"pool_list": [
+    {
+        "pool_address": "POOL_HOST:PORT",
+        "wallet_address": "YOUR_WALLET_ADDRESS",
+        "rig_id": "YOUR_WORKER_NAME",
+        "pool_password": "YOUR_FRTY_WALLET_ADDRESS",
+        "use_nicehash": false,
+        "use_tls": false, /* Set to true if you are using an SSL port */
+        "tls_fingerprint": "",
+        "pool_weight": 1
+    },
+],
+"currency": "",
+ +
+
XMRigCPULigthweight but powerful CPU Mining AppDownloadSee more
+
+ +
"pools": [
+    {
+        "url": "POOL_HOST:PORT",
+        "user": "YOUR_WALLET_ADDRESS",
+        "pass": "YOUR_BSM_WALLET_ADDRESS"
+        "keepalive": true,
+        "nicehash": false,
+        "variant": 1 
+    }
+],
+ +
+
XMRig-AMDOpenCL (AMD)XMRIG version for AMD GPUDownloadSee more
+
+ +
"pools": [
+    {
+        "url": "POOL_HOST:PORT",
+        "user": "YOUR_WALLET_ADDRESS",
+        "pass": "YOUR_BSM_WALLET_ADDRESS"
+        "keepalive": true,
+        "nicehash": false,
+        "variant": 1
+    }
+],
+ +
+
XMRig-NVIDIACuda (Nvidia)XMRIG version for Nvidia GPUDownloadSee more
+
+ +
"pools": [
+    {
+        "url": "POOL_HOST:PORT",
+        "user": "YOUR_WALLET_ADDRESS",
+        "pass": "YOUR_BSM_WALLET_ADDRESS"
+        "keepalive": true,
+        "nicehash": false,
+        "variant": 1
+    }
+],
+ +
+
XMRigCCCPUXMRIG Fork, optimized with remote controlDownloadSee more
+
+ +
"pools": [
+    {
+        "url": "POOL_HOST:PORT",
+        "user": "YOUR_WALLET_ADDRESS",
+        "pass": "YOUR_BSM_WALLET_ADDRESS",
+        "keepalive": true,
+        "nicehash": false
+    },
+],
+ +
+
+
+
+ + + diff --git a/campurro-merged-website-tamplate/pages/home.html b/campurro-merged-website-tamplate/pages/home.html new file mode 100644 index 000000000..c9457a637 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/home.html @@ -0,0 +1,403 @@ + +
+ + +
+ + +
+
+
+ +
+
+
Pool Hash Rate
+
N/A (0%)
+
+
+
+ + +
+
+
+ +
+
+
Blocks Found
+
+ N/A42 + / N/AZLS + + (Never) +
+
+
+
+ + +
+
+
+ +
+
+
Blocks Found Every
+
N/A (estimated)
+
+
+
+ + +
+
+
+ +
+
+
Current Effort
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Network Hash Rate
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Difficulty
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Blockchain Height
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Last Reward
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Network Hash Rate (ZLS)
+
N/A
+
+
+
+
+
+
+ +
+
+
Difficulty (ZLS)
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Blockchain Height (ZLS)
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Last Reward (ZLS)
+
N/A
+
+
+
+ + + + + +
+
+
+ +
+
+
Connected Miners
+
N/A (N/A workers)
+
+
+
+ + +
+
+
+ +
+
+
Pool Fee
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Minimum Payout
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Payment Interval
+
N/A
+
+
+
+ + +
+
+
Last Hash
+
+ +
(Never)
+
+
+
+ +
+ + +
+
+

Hash Rate

+
+
+
+
+
+

Difficulty

+
+
+
+
+
+

Miners

+
+
+
+
+
+

Workers

+
+
+
+
+
+ + + diff --git a/campurro-merged-website-tamplate/pages/market.html b/campurro-merged-website-tamplate/pages/market.html new file mode 100644 index 000000000..c4bdb3bdd --- /dev/null +++ b/campurro-merged-website-tamplate/pages/market.html @@ -0,0 +1,339 @@ + +
+
Loading market prices ...
+ +
+
+

Price in USD

+
+ + +
+
+
+
+
+

Hash/USD

+
+ + +
+
+
+
+ + +
+

Estimate Mining Profits

+
+
+ +
+ + +
+ = +

per day
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/campurro-merged-website-tamplate/pages/monetaverde.html b/campurro-merged-website-tamplate/pages/monetaverde.html new file mode 100644 index 000000000..76d0aa4dd --- /dev/null +++ b/campurro-merged-website-tamplate/pages/monetaverde.html @@ -0,0 +1,766 @@ + + +

Your Stats & Payment History

+ + + +
+
+ +
+
+ + +
+
+ + +
+

  Hash Rate

+
+
+
Current Hash Rate:
+
Last Share Submitted:
+
Total Hashes Submitted:
+
+
+
+
+ +
+
+
+
+
+ + +
+

  Payments

+
+
+
Pending Balance:
+
Total Paid:
+
Current Payout Estimate:
+
+
+
+
+ +
+
+
+
+
+ + +

  Workers Statistics

+
+
+ + + + + + + + + + + + + +
Status Worker Name Hash Rate Last Share Submitted Total Hashes Submitted
+
+
+ + +

  Payments History

+
+
+ + + + + + + + + + + + +
Time SentTransaction HashAmountMixin
+
+
+ +

+ +

+ +
+
+ + + + + + +
+ +
+
+ + +
+
+ + +
+

  Hash Rate

+
+
+
Current Hash Rate:
+
Last Share Submitted:
+
Total Hashes Submitted:
+
+
+
+
+ +
+
+
+
+
+ + +
+

  Payments

+
+
+
Pending Balance:
+
Total Paid:
+
Current Payout Estimate:
+
+
+
+
+ +
+
+
+
+
+ + +

  Workers Statistics

+
+
+ + + + + + + + + + + + + +
Status Worker Name Hash Rate Last Share Submitted Total Hashes Submitted
+
+
+ + +

  Payments History

+
+
+ + + + + + + + + + + + +
Time SentTransaction HashAmountMixin
+
+
+ +

+ +

+ +
+ +
+
+ + + diff --git a/campurro-merged-website-tamplate/pages/payments.html b/campurro-merged-website-tamplate/pages/payments.html new file mode 100644 index 000000000..8c61f4856 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/payments.html @@ -0,0 +1,312 @@ + + + +
+
+ + +
+ + +
+
+
+ +
+
+
Total Payments
+
N/A (N/A miners)
+
+
+
+ + +
+
+
+ +
+
+
Minimum Payout
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Payment Interval
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Denomination Unit
+
N/A
+
+
+
+ +
+ + +
+
+ + + + + + + + + + + + + + +
Time SentTransaction HashAmountFeeMixinPayees
+
+
+ +

+ +

+
+ + +
+ +
+ + +
+
+
+ +
+
+
Total Payments
+
N/A (N/A miners)
+
+
+
+ + +
+
+
+ +
+
+
Minimum Payout
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Payment Interval
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Denomination Unit
+
N/A
+
+
+
+ +
+ + +
+
+ + + + + + + + + + + + + + +
Time SentTransaction HashAmountFeeMixinPayees
+
+
+ +

+ +

+
+
+ + + + diff --git a/campurro-merged-website-tamplate/pages/plura.html b/campurro-merged-website-tamplate/pages/plura.html new file mode 100644 index 000000000..96b92cd1d --- /dev/null +++ b/campurro-merged-website-tamplate/pages/plura.html @@ -0,0 +1,357 @@ + + + +
+
+ +
+ + +
+
+
+ +
+
+
Total Blocks Mined
+
N/A (Never)
+
+
+
+ + +
+
+
+ +
+
+
Maturity Requirement
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Average Luck
+
N/A
+
+
+
+ +
+ + +
+
+ + + + + + + + + + + + + + +
Time FoundRewardHeightDifficultyBlock HashEffortStatus
+
+
+ +

+ +

+ + +
+ + +
+ +
+ + +
+
+
+ +
+
+
Total Blocks Mined
+
N/A (Never)
+
+
+
+ + +
+
+
+ +
+
+
Maturity Requirement
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Average Luck
+
N/A
+
+
+
+ +
+ + +
+
+ + + + + + + + + + + + + + +
Time FoundRewardHeightDifficultyBlock HashEffortStatus
+
+
+ +

+ +

+ +
+
+ + + + diff --git a/campurro-merged-website-tamplate/pages/pool_blocks.html b/campurro-merged-website-tamplate/pages/pool_blocks.html new file mode 100644 index 000000000..f0fe9f682 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/pool_blocks.html @@ -0,0 +1,357 @@ + + + +
+
+ +
+ + +
+
+
+ +
+
+
Total Blocks Mined
+
N/A (Never)
+
+
+
+ + +
+
+
+ +
+
+
Maturity Requirement
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Average Luck
+
N/A
+
+
+
+ +
+ + +
+
+ + + + + + + + + + + + + + +
Time FoundRewardHeightDifficultyBlock HashEffortStatus
+
+
+ +

+ +

+ + +
+ + +
+ +
+ + +
+
+
+ +
+
+
Total Blocks Mined
+
N/A (Never)
+
+
+
+ + +
+
+
+ +
+
+
Maturity Requirement
+
N/A
+
+
+
+ + +
+
+
+ +
+
+
Average Luck
+
N/A
+
+
+
+ +
+ + +
+
+ + + + + + + + + + + + + + +
Time FoundRewardHeightDifficultyBlock HashEffortStatus
+
+
+ +

+ +

+ +
+
+ + + + diff --git a/campurro-merged-website-tamplate/pages/settings.html b/campurro-merged-website-tamplate/pages/settings.html new file mode 100644 index 000000000..2acdf0b54 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/settings.html @@ -0,0 +1,254 @@ + +

Verification fields

+ +
+

In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.

+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + +

Set your minimal payout level

+
+

If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.

+
+ +
+ +
+
+ +
+
+
+ + +
+

Enable email notifications

+
+

This pool will send out email notification when a block is found and whenever a payout happens.

+
+ +
+ +
+
+ + +
+
+
+
+ + diff --git a/campurro-merged-website-tamplate/pages/top10miners.html b/campurro-merged-website-tamplate/pages/top10miners.html new file mode 100644 index 000000000..f8f365145 --- /dev/null +++ b/campurro-merged-website-tamplate/pages/top10miners.html @@ -0,0 +1,68 @@ + +

Top 10 miners

+
+
+ + + + + + + + + + + + +
#MinerHash RateLast Share SubmittedTotal Hashes Submitted
+
+
+ + + \ No newline at end of file diff --git a/campurro-merged-website-tamplate/pages/worker_stats.html b/campurro-merged-website-tamplate/pages/worker_stats.html new file mode 100644 index 000000000..6583fd68c --- /dev/null +++ b/campurro-merged-website-tamplate/pages/worker_stats.html @@ -0,0 +1,766 @@ + + +

Your Stats & Payment History

+ + + +
+
+ +
+
+ + +
+
+ + +
+

  Hash Rate

+
+
+
Current Hash Rate:
+
Last Share Submitted:
+
Total Hashes Submitted:
+
+
+
+
+ +
+
+
+
+
+ + +
+

  Payments

+
+
+
Pending Balance:
+
Total Paid:
+
Current Payout Estimate:
+
+
+
+
+ +
+
+
+
+
+ + +

  Workers Statistics

+
+
+ + + + + + + + + + + + + +
Status Worker Name Hash Rate Last Share Submitted Total Hashes Submitted
+
+
+ + +

  Payments History

+
+
+ + + + + + + + + + + + +
Time SentTransaction HashAmountMixin
+
+
+ +

+ +

+ +
+
+ + + + + + +
+ +
+
+ + +
+
+ + +
+

  Hash Rate

+
+
+
Current Hash Rate:
+
Last Share Submitted:
+
Total Hashes Submitted:
+
+
+
+
+ +
+
+
+
+
+ + +
+

  Payments

+
+
+
Pending Balance:
+
Total Paid:
+
Current Payout Estimate:
+
+
+
+
+ +
+
+
+
+
+ + +

  Workers Statistics

+
+
+ + + + + + + + + + + + + +
Status Worker Name Hash Rate Last Share Submitted Total Hashes Submitted
+
+
+ + +

  Payments History

+
+
+ + + + + + + + + + + + +
Time SentTransaction HashAmountMixin
+
+
+ +

+ +

+ +
+ +
+
+ + + From db0e4a2637d32da3c49e3e3d7b86b608a0af1648 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 7 May 2019 09:39:44 +0200 Subject: [PATCH 89/98] Add files via upload --- campurro-merged-website-tamplate/js/common.js | 455 ++++++++++++++++++ campurro-merged-website-tamplate/js/custom.js | 1 + campurro-merged-website-tamplate/lang/ca.json | 166 +++++++ campurro-merged-website-tamplate/lang/en.json | 168 +++++++ campurro-merged-website-tamplate/lang/es.json | 168 +++++++ campurro-merged-website-tamplate/lang/fr.json | 168 +++++++ campurro-merged-website-tamplate/lang/it.json | 168 +++++++ campurro-merged-website-tamplate/lang/ko.json | 167 +++++++ .../lang/languages.js | 1 + campurro-merged-website-tamplate/lang/ru.json | 181 +++++++ .../lang/timeago/jquery.timeago.af.js | 30 ++ .../lang/timeago/jquery.timeago.am.js | 30 ++ .../lang/timeago/jquery.timeago.ar.js | 104 ++++ .../lang/timeago/jquery.timeago.az.js | 30 ++ .../lang/timeago/jquery.timeago.bg.js | 28 ++ .../lang/timeago/jquery.timeago.bs.js | 55 +++ .../lang/timeago/jquery.timeago.ca.js | 30 ++ .../lang/timeago/jquery.timeago.cs.js | 34 ++ .../lang/timeago/jquery.timeago.cy.js | 30 ++ .../lang/timeago/jquery.timeago.da.js | 28 ++ .../lang/timeago/jquery.timeago.de.js | 28 ++ .../lang/timeago/jquery.timeago.dv.js | 32 ++ .../lang/timeago/jquery.timeago.el.js | 28 ++ .../lang/timeago/jquery.timeago.en.js | 30 ++ .../lang/timeago/jquery.timeago.es.js | 29 ++ .../lang/timeago/jquery.timeago.et.js | 28 ++ .../lang/timeago/jquery.timeago.eu.js | 28 ++ .../lang/timeago/jquery.timeago.fa.js | 32 ++ .../lang/timeago/jquery.timeago.fi.js | 38 ++ .../lang/timeago/jquery.timeago.fr.js | 27 ++ .../lang/timeago/jquery.timeago.gl.js | 28 ++ .../lang/timeago/jquery.timeago.he.js | 26 + .../lang/timeago/jquery.timeago.hr.js | 54 +++ .../lang/timeago/jquery.timeago.hu.js | 28 ++ .../lang/timeago/jquery.timeago.hy.js | 28 ++ .../lang/timeago/jquery.timeago.id.js | 29 ++ .../lang/timeago/jquery.timeago.is.js | 29 ++ .../lang/timeago/jquery.timeago.it.js | 26 + .../lang/timeago/jquery.timeago.ja.js | 29 ++ .../lang/timeago/jquery.timeago.jv.js | 28 ++ .../lang/timeago/jquery.timeago.ko.js | 31 ++ .../lang/timeago/jquery.timeago.ky.js | 42 ++ .../lang/timeago/jquery.timeago.lt.js | 30 ++ .../lang/timeago/jquery.timeago.lv.js | 30 ++ .../lang/timeago/jquery.timeago.mk.js | 30 ++ .../lang/timeago/jquery.timeago.nl.js | 30 ++ .../lang/timeago/jquery.timeago.no.js | 28 ++ .../lang/timeago/jquery.timeago.pl.js | 39 ++ .../lang/timeago/jquery.timeago.pt-br.js | 28 ++ .../lang/timeago/jquery.timeago.pt.js | 26 + .../lang/timeago/jquery.timeago.ro.js | 29 ++ .../lang/timeago/jquery.timeago.rs.js | 54 +++ .../lang/timeago/jquery.timeago.ru.js | 43 ++ .../lang/timeago/jquery.timeago.rw.js | 30 ++ .../lang/timeago/jquery.timeago.si.js | 28 ++ .../lang/timeago/jquery.timeago.sk.js | 34 ++ .../lang/timeago/jquery.timeago.sl.js | 46 ++ .../lang/timeago/jquery.timeago.sq.js | 26 + .../lang/timeago/jquery.timeago.sr.js | 54 +++ .../lang/timeago/jquery.timeago.sv.js | 28 ++ .../lang/timeago/jquery.timeago.th.js | 30 ++ .../lang/timeago/jquery.timeago.tr.js | 26 + .../lang/timeago/jquery.timeago.uk.js | 42 ++ .../lang/timeago/jquery.timeago.ur.js | 30 ++ .../lang/timeago/jquery.timeago.uz.js | 29 ++ .../lang/timeago/jquery.timeago.vi.js | 30 ++ .../lang/timeago/jquery.timeago.zh-CN.js | 31 ++ .../lang/timeago/jquery.timeago.zh-TW.js | 30 ++ .../lang/zh-CN.json | 168 +++++++ 69 files changed, 3749 insertions(+) create mode 100644 campurro-merged-website-tamplate/js/common.js create mode 100644 campurro-merged-website-tamplate/js/custom.js create mode 100644 campurro-merged-website-tamplate/lang/ca.json create mode 100644 campurro-merged-website-tamplate/lang/en.json create mode 100644 campurro-merged-website-tamplate/lang/es.json create mode 100644 campurro-merged-website-tamplate/lang/fr.json create mode 100644 campurro-merged-website-tamplate/lang/it.json create mode 100644 campurro-merged-website-tamplate/lang/ko.json create mode 100644 campurro-merged-website-tamplate/lang/languages.js create mode 100644 campurro-merged-website-tamplate/lang/ru.json create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.af.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.am.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ar.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.az.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.bg.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.bs.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ca.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.cs.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.cy.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.da.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.de.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.dv.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.el.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.en.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.es.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.et.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.eu.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fa.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fi.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fr.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.gl.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.he.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hr.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hu.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hy.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.id.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.is.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.it.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ja.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.jv.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ko.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ky.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.lt.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.lv.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.mk.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.nl.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.no.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pl.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pt-br.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pt.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ro.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.rs.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ru.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.rw.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.si.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sk.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sl.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sq.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sr.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sv.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.th.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.tr.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.uk.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ur.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.uz.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.vi.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.zh-CN.js create mode 100644 campurro-merged-website-tamplate/lang/timeago/jquery.timeago.zh-TW.js create mode 100644 campurro-merged-website-tamplate/lang/zh-CN.json diff --git a/campurro-merged-website-tamplate/js/common.js b/campurro-merged-website-tamplate/js/common.js new file mode 100644 index 000000000..ed2a9ca89 --- /dev/null +++ b/campurro-merged-website-tamplate/js/common.js @@ -0,0 +1,455 @@ + +/** + * Common javascript code for cryptonote-nodejs-pool + * Author: Daniel Vandal + * GitHub: https://github.com/dvandal/cryptonote-nodejs-pool + **/ + +/** + * Layout + **/ + +// Collapse menu on load for mobile devices +$('#menu-content').collapse('hide'); + +/** + * Cookies handler + **/ + +var docCookies = { + getItem: function (sKey) { + return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; + }, + setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) { + if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } + var sExpires = ""; + if (vEnd) { + switch (vEnd.constructor) { + case Number: + sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; + break; + case String: + sExpires = "; expires=" + vEnd; + break; + case Date: + sExpires = "; expires=" + vEnd.toUTCString(); + break; + } + } + document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); + return true; + }, + removeItem: function (sKey, sPath, sDomain) { + if (!sKey || !this.hasItem(sKey)) { return false; } + document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : ""); + return true; + }, + hasItem: function (sKey) { + return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); + } +}; + +/** + * Pages routing + **/ + +// Current page +var currentPage; + +// Handle hash change +window.onhashchange = function(){ + routePage(); +}; + +// Route to page +var xhrPageLoading; +function routePage(loadedCallback) { + if (currentPage) currentPage.destroy(); + $('#page').html(''); + $('#loading').show(); + + if (xhrPageLoading) { + xhrPageLoading.abort(); + } + + $('.hot_link').parent().removeClass('active'); + var $link = $('a.hot_link[href="' + (window.location.hash || '#') + '"]'); + + $link.parent().addClass('active'); + var page = $link.data('page'); + + loadTranslations(); + + xhrPageLoading = $.ajax({ + url: 'pages/' + page, + cache: false, + success: function (data) { + $('#menu-content').collapse('hide'); + $('#loading').hide(); + $('#page').show().html(data); + loadTranslations(); + if (currentPage) currentPage.update(); + if (loadedCallback) loadedCallback(); + } + }); +} + +/** + * Strings + **/ + +// Add .update() custom jQuery function to update text content +$.fn.update = function(txt){ + var el = this[0]; + if (el.textContent !== txt) + el.textContent = txt; + return this; +}; + +// Update Text classes +function updateTextClasses(className, text){ + var els = document.getElementsByClassName(className); + if (els) { + for (var i = 0; i < els.length; i++){ + var el = els[i]; + if (el && el.textContent !== text) + el.textContent = text; + } + } +} + +// Update Text content +function updateText(elementId, text){ + var el = document.getElementById(elementId); + if (el && el.textContent !== text){ + el.textContent = text; + } + return el; +} + +// Convert float to string +function floatToString(float) { + return float.toFixed(6).replace(/[0\.]+$/, ''); +} + +// Format number +function formatNumber(number, delimiter){ + if(number != '') { + number = number.split(delimiter).join(''); + + var formatted = ''; + var sign = ''; + + if(number < 0){ + number = -number; + sign = '-'; + } + + while(number >= 1000){ + var mod = number % 1000; + + if(formatted != '') formatted = delimiter + formatted; + if(mod == 0) formatted = '000' + formatted; + else if(mod < 10) formatted = '00' + mod + formatted; + else if(mod < 100) formatted = '0' + mod + formatted; + else formatted = mod + formatted; + + number = parseInt(number / 1000); + } + + if(formatted != '') formatted = sign + number + delimiter + formatted; + else formatted = sign + number; + return formatted; + } + return ''; +} + +// Format date +function formatDate(time){ + if (!time) return ''; + return new Date(parseInt(time) * 1000).toLocaleString(); +} + +// Format percentage +function formatPercent(percent) { + if (!percent && percent !== 0) return ''; + return percent + '%'; +} + +// Get readable time +function getReadableTime(seconds){ + var units = [ [60, 'second'], [60, 'minute'], [24, 'hour'], + [7, 'day'], [4, 'week'], [12, 'month'], [1, 'year'] ]; + + function formatAmounts(amount, unit){ + var rounded = Math.round(amount); + var unit = unit + (rounded > 1 ? 's' : ''); + if (getTranslation(unit)) unit = getTranslation(unit); + return '' + rounded + ' ' + unit; + } + + var amount = seconds; + for (var i = 0; i < units.length; i++){ + if (amount < units[i][0]) { + return formatAmounts(amount, units[i][1]); + } + amount = amount / units[i][0]; + } + return formatAmounts(amount, units[units.length - 1][1]); +} + +// Get readable hashrate +function getReadableHashRateString(hashrate){ + var i = 0; + var byteUnits = [' H', ' KH', ' MH', ' GH', ' TH', ' PH' ]; + while (hashrate > 1000){ + hashrate = hashrate / 1000; + i++; + } + return hashrate.toFixed(2) + byteUnits[i]; +} + +// Get coin decimal places +function getCoinDecimalPlaces() { + if (typeof coinDecimalPlaces != "undefined") return coinDecimalPlaces; + else if (lastStats.config.coinDecimalPlaces) return lastStats.config.coinDecimalPlaces; + else lastStats.config.coinUnits.toString().length - 1; +} +function getCoinDecimalPlacesMerged() { + if (typeof coinDecimalPlacesMerged != "undefined") return coinDecimalPlacesMerged; + else if (mergedStats.config.coinDecimalPlaces) return mergedStats.config.coinDecimalPlaces; + else mergedStats.config.coinUnits.toString().length - 1; +} + +// Get readable coins +function getReadableCoins(coins, digits, withoutSymbol){ + var coinDecimalPlaces = getCoinDecimalPlaces(); + var amount = parseFloat((parseInt(coins || 0) / lastStats.config.coinUnits).toFixed(digits || coinDecimalPlaces)); + return amount.toString() + (withoutSymbol ? '' : (' ' + lastStats.config.symbol)); +} +function getReadableCoinsMerged(coins, digits, withoutSymbol){ + var coinDecimalPlacesMerged = getCoinDecimalPlacesMerged(); + var amount = parseFloat((parseInt(coins || 0) / mergedStats.config.coinUnits).toFixed(digits || coinDecimalPlacesMerged)); + return amount.toString() + (withoutSymbol ? '' : (' ' + mergedStats.config.symbol)); +} + +// Format payment link +function formatPaymentLink(hash, merged){ + return '' + hash + ''; +} + +// Format difficulty +function formatDifficulty(x) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " "); +} + +// Format luck / current effort +function formatLuck(difficulty, shares) { + // Only an approximation to reverse the calculations done in pool.js, because the shares with their respective times are not recorded in redis + // Approximation assumes equal pool hashrate for the whole round + // Could potentially be replaced by storing the sum of all job.difficulty in the redis db. + if (lastStats.config.slushMiningEnabled) { + // Uses integral calculus to calculate the average of a dynamic function + var accurateShares = 1/lastStats.config.blockTime * ( // 1/blockTime to get the average + shares * lastStats.config.weight * ( // Basically calculates the 'area below the graph' between 0 and blockTime + 1 - Math.pow( + Math.E, + ((- lastStats.config.blockTime) / lastStats.config.weight) // blockTime is equal to the highest possible result of (dateNowSeconds - scoreTime) + ) + ) + ); + } + else { + var accurateShares = shares; + } + + var percent = Math.round(accurateShares / difficulty * 100); + if(!percent){ + return '?'; + } + else if(percent <= 100){ + return '' + percent + '%'; + } + else if(percent >= 101 && percent <= 150){ + return '' + percent + '%'; + } + else{ + return '' + percent + '%'; + } +} + +/** + * URLs + **/ + +// Return pool host +function getPoolHost() { + if (typeof poolHost != "undefined") return poolHost; + if (lastStats.config.poolHost) return lastStats.config.poolHost; + else return window.location.hostname; +} + +// Return transaction URL +function getTransactionUrl(id, merged) { + if (merged && transactionExplorerMerged){ + return transactionExplorerMerged.replace('{symbol}', mergedStats.config.symbol.toLowerCase()).replace('{id}', id); + } else { + return transactionExplorer.replace('{symbol}', lastStats.config.symbol.toLowerCase()).replace('{id}', id); + } +} + +// Return blockchain explorer URL +function getBlockchainUrl(id, merged) { + if (merged && blockchainExplorerMerged) { + return blockchainExplorerMerged.replace('{symbol}', mergedStats.config.symbol.toLowerCase()).replace('{id}', id); + } else { + return blockchainExplorer.replace('{symbol}', lastStats.config.symbol.toLowerCase()).replace('{id}', id); + } +} + +/** + * Tables + **/ + +// Sort table cells +function sortTable() { + var table = $(this).parents('table').eq(0), + rows = table.find('tr:gt(0)').toArray().sort(compareTableRows($(this).index())); + this.asc = !this.asc; + if(!this.asc) { + rows = rows.reverse() + } + for(var i = 0; i < rows.length; i++) { + table.append(rows[i]) + } +} + +// Compare table rows +function compareTableRows(index) { + return function(a, b) { + var valA = getCellValue(a, index), valB = getCellValue(b, index); + if (!valA) { valA = 0; } + if (!valB) { valB = 0; } + return $.isNumeric(valA) && $.isNumeric(valB) ? valA - valB : valA.toString().localeCompare(valB.toString()) + } +} + +// Get table cell value +function getCellValue(row, index) { + return $(row).children('td').eq(index).data("sort") +} + +/** + * Translations + **/ + +if (typeof langs == "undefined") { + var langs = { en: 'English' }; +} + +if (typeof defaultLang == "undefined") { + var defaultLang = 'en'; +} + +var langCode = defaultLang; +var langData = null; + +function getTranslation(key) { + if (!langData || !langData[key]) return null; + return langData[key]; +} + +var translate = function(data) { + langData = data; + + $("[tkey]").each(function(index) { + var strTr = data[$(this).attr('tkey')]; + $(this).html(strTr); + }); + + $("[tplaceholder]").each(function(index) { + var strTr = data[$(this).attr('tplaceholder')]; + $(this).attr('placeholder', strTr) + }); + + $("[tvalue]").each(function(index) { + var strTr = data[$(this).attr('tvalue')]; + $(this).attr('value', strTr) + }); +} + +// Get language code from URL +const $_GET = {}; +const args = location.search.substr(1).split(/&/); +for (var i=0; i' + langs[lang] + ''; + numLangs ++; + } + html += ''; + } + if (html && numLangs > 1) { + $('#langSelector').html(html); + $('#newLang').each(function(){ + $(this).change(function() { + var newLang = $(this).val(); + var url = '?lang=' + newLang; + if (window.location.hash) url += window.location.hash; + window.location.href = url; + }); + }); + } + + // Mobile + var html = ''; + var numLangs = 0; + if (langs) { + html += ''; + } + if (html && numLangs > 1) { + $('#mLangSelector').html(html); + $('#mNewLang').each(function(){ + $(this).change(function() { + var newLang = $(this).val(); + var url = '?lang=' + newLang; + if (window.location.hash) url += window.location.hash; + window.location.href = url; + }); + }); + } +} diff --git a/campurro-merged-website-tamplate/js/custom.js b/campurro-merged-website-tamplate/js/custom.js new file mode 100644 index 000000000..4bdcd2b11 --- /dev/null +++ b/campurro-merged-website-tamplate/js/custom.js @@ -0,0 +1 @@ +/* Insert your pool's unique Javascript here */ \ No newline at end of file diff --git a/campurro-merged-website-tamplate/lang/ca.json b/campurro-merged-website-tamplate/lang/ca.json new file mode 100644 index 000000000..aa7f85532 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/ca.json @@ -0,0 +1,166 @@ +{ + "miningPool": "Mining Pool", + "dashboard": "Tauler", + "gettingStarted": "Començar", + "yourStats": "Estadístiques personals", + "poolBlocks": "Blocs de la Pool", + "settings": "Ajustaments", + "faq": "FAQ", + "telegram": "Grup de Telegram", + "discord": "Discord", + "contactUs": "Contactar", + "network": "Xarxa", + "pool": "Pool", + "you": "La teva taxa", + "statsUpdated": "Estadístiques actualitzades", + + "poolHashrate": "Taxa de la Pool (hash)", + "currentEffort": "Esforç actual", + "networkHashrate": "Taxa de la Xarxa (hash)", + "networkDifficulty": "Dificultat", + "blockchainHeight": "Altura cadena de blocs", + "networkLastReward": "Última recompensa", + "poolMiners": "Miners connectats", + "poolFee": "Tarifa de la Pool", + + "minerStats": "Les teves estadístiques i historial de pagaments", + "workerStats": "Estadístiques personals", + "miner": "Miner", + "miners": "Miners", + "minersCount": "miners", + "workers": "Treballadors", + "workersCount": "treballadors", + "workerName": "Nom Treballador", + "lastHash": "Últim hash", + "hashRate": "Taxa (hash)", + "currentHashRate": "Taxa actual (hash)", + "lastShare": "Última acció enviada", + "totalHashes": "Total d'accions enviades", + "top10miners": "Top 10 Miners", + + "blocksTotal": "Blocs trobats", + "blockSolvedTime": "Bloc trobat cada", + "blocksMaturityCount": "Maduresa requerida", + "efficiency": "Eficiència", + "averageLuck": "Promitg de sort", + "timeFound": "Data i hora", + "reward": "Recompensa", + "height": "Altura", + "difficulty": "Dificultat", + "blockHash": "Bloc Hash", + "effort": "Esforç", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + + "payments": "Pagaments", + "paymentsHistory": "Historial de pagaments", + "paymentsTotal": "Total de pagaments", + "paymentsMinimum": "Pagament mínim", + "paymentsInterval": "Interval de pagaments", + "paymentsDenomination": "Unitat de denominació", + "timeSent": "Data", + "transactionHash": "Hash transacció", + "amount": "Import", + "fee": "Tarifa", + "mixin": "Mixin", + "payees": "Beneficiàris", + "pendingBalance": "Balanç pendent", + "totalPaid": "Total pagat", + "payoutEstimate": "Pagament estimat", + "paymentSummarySingle": "El %DATE% has rebut %AMOUNT%", + "paymentSummaryMulti": "El %DATE% has rebut %AMOUNT% en %COUNT% pagaments", + + "connectionDetails": "Detalls de connexió", + "cnAlgorithm": "Algoritme", + "miningPoolHost": "Adreça de la Pool", + "username": "Usuari", + "usernameDesc": "La teva adreça del wallet", + "paymentId": "ID de pagament (Exchanges)", + "fixedDiff": "Dificultat fixa", + "address": "adreça", + "addrPaymentId": "IDPagament", + "addrDiff": "diff", + "password": "Contrassenya", + "passwordDesc": "Nom del seu treballador", + "emailNotifications": "Notificacions per E-Mail", + "miningPorts": "Ports de mineria", + "port": "Port", + "portDiff": "Dificultat inicial", + "description": "Descripció", + "miningApps": "Aplicacions de mineria", + "configGeneratorDesc": "Genera la teva configuració personalitzada per minar a la nostra Pool", + "addressField": "Adreça Wallet", + "paymentIdField": "ID de Pagament per a Exchanges (opcional)", + "fixedDiffField": "Dificultat fixa (opcional)", + "workerNameField": "Nom_Treballador", + "emailNotificationsField": "Notificacions per E-Mail (opcional))", + "generateConfig": "Generar configuració", + "appName": "Aplicació", + "appArch": "Arquitectura", + "appDesc": "Característiques", + "download": "Descarregar", + "showConfig": "Veure més", + + "market": "Mercat / Calculadora", + "loadingMarket": "Carregant preus del mercat", + "priceIn": "Preu en", + "hashPer": "Hash/", + "estimateProfit": "Beneficis estimats", + "enterYourHashrate": "Entra la teva taxa (hash)", + "perDay": "per dia", + + "verificationFields": "Camps de verificació", + "minerVerification": "Per tenir una mica més de seguretat que l'adreça de la cartera és vostra, us demanem que proporcioneu una de les adreces IP que utilitza el vostre miner.", + "minerAddress": "Adreça del moneder", + "minerIP": "Adreça IP del Miner", + "setMinimumPayout": "Estableix el nivell mínim de pagament", + "minerMinPayout": "Si preferiu un nivell de pagament més alt que el predeterminat del grup, aquí podeu canviar-lo per als vostres miners. L'import que indiqueu aquí es convertirà en l'import mínim dels pagaments de la vostra adreça.", + "minimumPayout": "Pagament mínim", + "enableEmailNotifications": "Activar notificacions per de correu electrònic", + "minerEmailNotify": "Aquesta Pool enviarà una notificació per correu electrònic quan es trobi un bloc i quan es produeixi un pagament.", + "emailAddress": "Adreça de correu electrònic", + "noMinerAddress": "No s'ha especificat cap adreça del moneder", + "noMinerIP": "No s'especificat cap adreça IP", + "noPayoutLevel": "No s'ha especificat cap nivell de pagament", + "noEmail": "No s'ha especificat cap adreça de correu electrònic", + "invalidEmail": "S'ha especificat una adreça de correu electrònic no vàlida", + "minerPayoutSet": "Fet! S'ha establert el nivell mínim de pagament", + "notificationEnabled": "Fet! S'han activat les notificacions per correu electrònic", + "notificationDisabled": "Fet! S'han desactivat les notificacions per correu electrònic", + + "enterYourAddress": "Introduïu la vostra adreça", + "enterYourMinerIP": "Una adreça IP que utilitzin els miners (qualsevol)", + "enterYourEmail": "Introduïu la vostra adreça de correu electrònic (opcional)", + "lookup": "Cercar", + "searching": "Cercant...", + "loadMore": "Carregar més", + "set": "Establir", + "enable": "Activar", + "disable": "Desactivar", + "status": "Estat", + "updated": "Actualitzat:", + "source": "Origen:", + "error": "Error:", + + "na": "N/A", + "estimated": "estimat", + "never": "Mai", + "second": "segon", + "seconds": "segons", + "minute": "minut", + "minutes": "minuts", + "hour": "hora", + "hours": "hores", + "day": "dia", + "days": "dies", + "week": "setmana", + "weeks": "setmanes", + "month": "mes", + "months": "mesos", + "year": "any", + "years": "anys", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" + +} diff --git a/campurro-merged-website-tamplate/lang/en.json b/campurro-merged-website-tamplate/lang/en.json new file mode 100644 index 000000000..ff5f7422e --- /dev/null +++ b/campurro-merged-website-tamplate/lang/en.json @@ -0,0 +1,168 @@ +{ + "miningPool": "Mining Pool", + "dashboard": "Dashboard", + "gettingStarted": "Getting Started", + "yourStats": "Worker Statistics", + "poolBlocks": "Pool Blocks", + "settings": "Settings", + "faq": "FAQ", + "telegram": "Telegram group", + "discord": "Discord", + "contactUs": "Contact Us", + + "network": "Network", + "pool": "Pool", + "you": "You", + "statsUpdated": "Stats Updated", + + "poolHashrate": "Pool Hash Rate", + "currentEffort": "Current Effort", + "networkHashrate": "Network Hash Rate", + "networkDifficulty": "Difficulty", + "blockchainHeight": "Blockchain Height", + "networkLastReward": "Last Reward", + "poolMiners": "Connected Miners", + "poolFee": "Pool Fee", + + "minerStats": "Your Stats & Payment History", + "workerStats": "Workers Statistics", + "miner": "Miner", + "miners": "Miners", + "minersCount": "miners", + "workers": "Workers", + "workersCount": "workers", + "workerName": "Worker Name", + "lastHash": "Last Hash", + "hashRate": "Hash Rate", + "currentHashRate": "Current Hash Rate", + "lastShare": "Last Share Submitted", + "totalHashes": "Total Hashes Submitted", + "top10miners": "Top 10 miners", + + "blocksTotal": "Blocks Found", + "blockSolvedTime": "Blocks Found Every", + "blocksMaturityCount": "Maturity Requirement", + "efficiency": "Efficiency", + "averageLuck": "Average Luck", + "timeFound": "Time Found", + "reward": "Reward", + "height": "Height", + "difficulty": "Difficulty", + "blockHash": "Block Hash", + "effort": "Effort", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + + "payments": "Payments", + "paymentsHistory": "Payments History", + "paymentsTotal": "Total Payments", + "paymentsMinimum": "Minimum Payout", + "paymentsInterval": "Payment Interval", + "paymentsDenomination": "Denomination Unit", + "timeSent": "Time Sent", + "transactionHash": "Transaction Hash", + "amount": "Amount", + "fee": "Fee", + "mixin": "Mixin", + "payees": "Payees", + "pendingBalance": "Pending Balance", + "totalPaid": "Total Paid", + "payoutEstimate": "Current Payout Estimate", + "paymentSummarySingle": "On %DATE% you have received %AMOUNT%", + "paymentSummaryMulti": "On %DATE% you have received %AMOUNT% in %COUNT% payments", + + "connectionDetails": "Connection Details", + "miningPoolHost": "Mining Pool Address", + "cnAlgorithm": "Algorithm", + "username": "Username", + "usernameDesc": "This is your wallet address", + "paymentId": "Exchange Payment ID", + "fixedDiff": "Difficulty locking", + "address": "address", + "addrPaymentId": "paymentID", + "addrDiff": "diff", + "password": "Password", + "passwordDesc": "This is your worker name", + "emailNotifications": "Email Notifications", + "miningPorts": "Mining Ports", + "port": "Port", + "portDiff": "Starting Difficulty", + "description": "Description", + "miningApps": "Mining Applications", + "configGeneratorDesc": "Generate your custom configuration to mine on our pool", + "addressField": "Wallet Address", + "paymentIdField": "Payment ID for exchanges (optional)", + "fixedDiffField": "Fixed difficulty (optional)", + "workerNameField": "Worker_Name", + "emailNotificationsField": "Email Notifications (optional)", + "generateConfig": "Generate configuration", + "appName": "App Name", + "appArch": "Architecture", + "appDesc": "Features", + "download": "Download", + "showConfig": "See more", + + "market": "Market / Calculator", + "loadingMarket": "Loading market prices", + "priceIn": "Price in", + "hashPer": "Hash/", + "estimateProfit": "Estimate Mining Profits", + "enterYourHashrate": "Enter Your Hash Rate", + "perDay": "per day", + + "verificationFields": "Verification fields", + "minerVerification": "In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.", + "minerAddress": "Miner Address", + "minerIP": "Miner IP address", + "setMinimumPayout": "Set your minimal payout level", + "minerMinPayout": "If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.", + "minimumPayout": "Minimum payout", + "enableEmailNotifications": "Enable email notifications", + "minerEmailNotify": "This pool will send out email notification when a block is found and whenever a payout happens.", + "emailAddress": "Email address", + "noMinerAddress": "No miner address specified", + "noMinerIP": "No miner IP address specified", + "noPayoutLevel": "No payout level specified", + "noEmail": "No email address specified", + "invalidEmail": "Invalid email address specified", + "minerPayoutSet": "Done! Your minimum payout level was set", + "notificationEnabled": "Done! Email notifications have been enabled", + "notificationDisabled": "Done! Email notifications have been disabled", + + "enterYourAddress": "Enter Your Address", + "enterYourMinerIP": "An IP address your miners use (any)", + "enterYourEmail": "Enter Your E-Mail Address (optional)", + + "lookup": "Lookup", + "searching": "Searching...", + "loadMore": "Load more", + "set": "Set", + "enable": "Enable", + "disable": "Disable", + + "status": "Status", + "updated": "Updated:", + "source": "Source:", + "error": "Error:", + + "na": "N/A", + "estimated": "estimated", + "never": "Never", + "second": "second", + "seconds": "seconds", + "minute": "minute", + "minutes": "minutes", + "hour": "hour", + "hours": "hours", + "day": "day", + "days": "days", + "week": "week", + "weeks": "weeks", + "month": "month", + "months": "months", + "year": "year", + "years": "years", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" +} diff --git a/campurro-merged-website-tamplate/lang/es.json b/campurro-merged-website-tamplate/lang/es.json new file mode 100644 index 000000000..b667f6f90 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/es.json @@ -0,0 +1,168 @@ +{ + "miningPool": "Mining Pool", + "dashboard": "Panel", + "gettingStarted": "Empezar", + "yourStats": "Estadísticas personales", + "poolBlocks": "Bloques de la Pool", + "settings": "Ajustes", + "faq": "FAQ", + "telegram": "Grupo de Telegram", + "discord": "Discord", + "contactUs": "Contacto", + + "network": "Red", + "pool": "Pool", + "you": "Tú Tasa", + "statsUpdated": "Estadísticas actualizadas", + + "poolHashrate": "Tasa Pool (hash)", + "currentEffort": "Ronda actual", + "networkHashrate": "Tasa Red (hash)", + "networkDifficulty": "Dificultad", + "blockchainHeight": "Altura", + "networkLastReward": "Última recompensa", + "poolMiners": "Mineros conectados", + "poolFee": "Tarifa Pool", + + "minerStats": "Tus estadísticas e historial de pagos", + "workerStats": "Estadísticas personales", + "miner": "Minero", + "miners": "Mineros", + "minersCount": "mineros", + "workers": "Trabajadores", + "workersCount": "trabajadores", + "workerName": "Nombre Trabajador", + "lastHash": "Último hash", + "hashRate": "Tasa (hash)", + "currentHashRate": "Tasa actual (hash)", + "lastShare": "Última acción enviada", + "totalHashes": "Total de acciones enviadas", + "top10miners": "Top 10 Mineros", + + "blocksTotal": "Bloques encontrados", + "blockSolvedTime": "Bloque encontrado cada", + "blocksMaturityCount": "Madurez requerida", + "efficiency": "Eficiencia", + "averageLuck": "Promedio de suerte", + "timeFound": "Fecha y hora", + "reward": "Recompensa", + "height": "Altura", + "difficulty": "Dificultad", + "blockHash": "Bloque Hash", + "effort": "Esfuerzo", + "blocksFoundLast24": "Bloques encontrados en las últimas 24 horas", + "blocksFoundLastDays": "Bloques encontrados en los últimos {DAYS} días", + + "payments": "Pagos", + "paymentsHistory": "Historial de pagos", + "paymentsTotal": "Total de pagos", + "paymentsMinimum": "Pago mínimo", + "paymentsInterval": "Intervalo de pagos", + "paymentsDenomination": "Unidad de denominación", + "timeSent": "Fecha y hora", + "transactionHash": "Hash transacción", + "amount": "Importe", + "fee": "Tarifa", + "mixin": "Mixin", + "payees": "Beneficiarios", + "pendingBalance": "Balance pendiente", + "totalPaid": "Total pagado", + "payoutEstimate": "Pago estimado", + "paymentSummarySingle": "El %DATE% has recibido %AMOUNT%", + "paymentSummaryMulti": "El %DATE% has recibido %AMOUNT% en %COUNT% pagos", + + "connectionDetails": "Detalles de conexión", + "miningPoolHost": "Dirección de la Pool", + "cnAlgorithm": "Algoritmo", + "username": "Usuario", + "usernameDesc": "Tu dirección del wallet", + "paymentId": "ID de pago (Exchanges)", + "fixedDiff": "Dificultad fija", + "address": "dirección", + "addrPaymentId": "IDPago", + "addrDiff": "diff", + "password": "Contraseña", + "passwordDesc": "Nombre de su trabajador", + "emailNotifications": "Notificaciones por E-Mail", + "miningPorts": "Puertos de minería", + "port": "Puerto", + "portDiff": "Dificultad inicial", + "description": "Descripción", + "miningApps": "Aplicaciones de minería", + "configGeneratorDesc": "Genera tu configuración personalizada para minar a nuestra Pool", + "addressField": "Dirección Wallet", + "paymentIdField": "ID de Pago para Exchanges (opcional)", + "fixedDiffField": "Dificultad fija (opcional)", + "workerNameField": "Nombre_Trabajador", + "emailNotificationsField": "Notificaciones por E-Mail (opcional)", + "generateConfig": "Generar configuración", + "appName": "Aplicación", + "appArch": "Arquitectura", + "appDesc": "Características", + "download": "Descargar", + "showConfig": "Ver más", + + "market": "Mercado / Calculadora", + "loadingMarket": "Cargando precios del mercado", + "priceIn": "Precio en", + "hashPer": "Hash/", + "estimateProfit": "Beneficios estimados", + "enterYourHashrate": "Entra tu tasa (hash)", + "perDay": "por día", + + "verificationFields": "Campos de verificación", + "minerVerification": "Para tener un poco más de seguridad que la dirección de la cartera es vuestra, le pedimos que proporcione una de las direcciones IP que utiliza su minero.", + "minerAddress": "Dirección del monedero", + "minerIP": "Dirección IP del Minero", + "setMinimumPayout": "Establece el nivel mínimo de pago", + "minerMinPayout": "Si prefiere un nivel de pago más alto que el predeterminado del grupo, aquí puede cambiarlo para sus mineros. El importe que indique aquí se convertirá en el importe mínimo de los pagos de su dirección.", + "minimumPayout": "Pago mínimo", + "enableEmailNotifications": "Activar notificaciones por correo electrónico", + "minerEmailNotify": "Esta Pool enviará una notificación por correo electrónico cuando se encuentre un bloque y cuando se produzca un pago.", + "emailAddress": "Dirección de correo electrónico", + "noMinerAddress": "No se ha especificado ninguna dirección del monedero", + "noMinerIP": "No se especificado ninguna dirección IP", + "noPayoutLevel": "No se ha especificado ningún nivel de pago", + "noEmail": "No se ha especificado ninguna dirección de correo electrónico", + "invalidEmail": "Se ha especificado una dirección de correo electrónico no válida", + "minerPayoutSet": "Hecho! Se ha establecido el nivel mínimo de pago", + "notificationEnabled": "Hecho! Se han activado las notificaciones por correo electrónico", + "notificationDisabled": "Hecho! Se han desactivado las notificaciones por correo electrónico", + + "enterYourAddress": "Introduzca su dirección", + "enterYourMinerIP": "Una dirección IP que utilicen los mineros (cualquiera)", + "enterYourEmail": "Introduzca su dirección de correo electrónico (opcional)", + + "lookup": "Buscar", + "searching": "Buscando...", + "loadMore": "Cargar más", + "set": "Establecer", + "enable": "Activar", + "disable": "Desactivar", + + "status": "Estado", + "updated": "Actualizado:", + "source": "Origen:", + "error": "Error:", + + "na": "N/A", + "estimated": "estimado", + "never": "Nunca", + "second": "segundo", + "seconds": "segundos", + "minute": "minuto", + "minutes": "minutos", + "hour": "hora", + "hours": "horas", + "day": "día", + "days": "dias", + "week": "semana", + "weeks": "semanas", + "month": "mes", + "months": "meses", + "year": "año", + "years": "años", + + "poweredBy": "Powered by", + "openSource": "open sourced bajo " +} diff --git a/campurro-merged-website-tamplate/lang/fr.json b/campurro-merged-website-tamplate/lang/fr.json new file mode 100644 index 000000000..84ce29157 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/fr.json @@ -0,0 +1,168 @@ +{ + "miningPool": "Mining Pool", + "dashboard": "Tableau de bord", + "gettingStarted": "Comment démarrer", + "yourStats": "Vos statistiques", + "poolBlocks": "Blocs trouvés", + "settings": "Paramètres", + "faq": "FAQ", + "telegram": "Telegram", + "discord": "Discord", + "contactUs": "Nous contacter", + + "network": "Réseau", + "pool": "Pool", + "you": "Vous", + "statsUpdated": "Statistiques mises à jour", + + "poolHashrate": "Taux de Hash du Pool", + "currentEffort": "Effort actuel", + "networkHashrate": "Taux de Hash du réseau", + "networkDifficulty": "Difficulté", + "blockchainHeight": "Hauteur de la BlockChain", + "networkLastReward": "Dernière récompense", + "poolMiners": "Mineurs connectés", + "poolFee": "Frais du pool", + + "minerStats": "Vos statistiques et Historique des paiements", + "workerStats": "Statistiques des travailleurs", + "miner": "Mineur", + "miners": "Mineurs", + "minersCount": "mineurs", + "workers": "Travailleurs", + "workersCount": "travailleurs", + "workerName": "Nom du travailleur", + "lastHash": "Dernier Hash", + "hashRate": "Taux de Hash", + "currentHashRate": "Taux de Hash actuel", + "lastShare": "Dernière transmission", + "totalHashes": "Hashes transmis", + "top10miners": "Top 10 mineurs", + + "blocksTotal": "Blocs trouvés", + "blockSolvedTime": "Bloc trouvé chaque", + "blocksMaturityCount": "Maturité requise", + "efficiency": "Efficacité", + "averageLuck": "Chance moyenne", + "timeFound": "Trouvé le", + "reward": "Récompense", + "height": "Hauteur", + "difficulty": "Difficulté", + "blockHash": "Hash du bloc", + "effort": "Effort", + "blocksFoundLast24": "Blocs trouvés dans les 24 dernières heures", + "blocksFoundLastDays": "Blocs trouvés dans les derniers {DAYS} jours", + + "payments": "Paiements", + "paymentsHistory": "Historique des paiements", + "paymentsTotal": "Nombre de paiements", + "paymentsMinimum": "Minimum avant paiement", + "paymentsInterval": "Intervale de paiement", + "paymentsDenomination": "Unité de dénomination", + "timeSent": "Envoyé le", + "transactionHash": "Hash de transaction", + "amount": "Montant", + "fee": "Frais", + "mixin": "Mixin", + "payees": "Payés", + "pendingBalance": "Balance en attente", + "totalPaid": "Total payé", + "payoutEstimate": "Estimation de paiement", + "paymentSummarySingle": "Le %DATE% vous avez reçu %AMOUNT%", + "paymentSummaryMulti": "Le %DATE% vous avez reçu %AMOUNT% en %COUNT% paiements", + + "connectionDetails": "Détails de connexion", + "miningPoolHost": "Adresse du pool", + "cnAlgorithm": "Algorithme", + "username": "Nom d'utilisateur", + "usernameDesc": "C'est l'adresse de votre wallet", + "paymentId": "ID de paiement de l'exchange", + "fixedDiff": "Difficulté fixe", + "address": "adresse", + "addrPaymentId": "idPaiement", + "addrDiff": "diff", + "password": "Mot de passe", + "passwordDesc": "C'est l'identifiant de votre travailleur", + "emailNotifications": "Notifications par email", + "miningPorts": "Ports de minage", + "port": "Port", + "portDiff": "Difficulté de départ", + "description": "Description", + "miningApps": "Applications de minage", + "configGeneratorDesc": "Générer votre configuration personalisée pour miner sur notre pool", + "addressField": "Adresse de votre wallet", + "paymentIdField": "ID de paiement pour l'exchange (optionnel)", + "fixedDiffField": "Difficulté fixe (optionnel)", + "workerNameField": "Nom_du_Travailleur", + "emailNotificationsField": "Notifications par Email (optionnel)", + "generateConfig": "Générer la configuration", + "appName": "Nom de l'App", + "appArch": "Architecture", + "appDesc": "Fonctionalités", + "download": "Télécharger", + "showConfig": "Afficher", + + "market": "Marché et calculateur", + "loadingMarket": "Chargement des prix du marché", + "priceIn": "Prix en", + "hashPer": "Hash/", + "estimateProfit": "Estimation des profits de minage", + "enterYourHashrate": "Entrez votre taux de Hash", + "perDay": "par jour", + + "verificationFields": "Champs de vérification", + "minerVerification": "Afin de nous assurer que l'adresse du mineur est bien la vôtre, nous vous demandons d'entrer une adresse IP utilisée par votre mineur.", + "minerAddress": "Adresse du mineur", + "minerIP": "Adresse IP du mineur", + "setMinimumPayout": "Configurer votre niveau de paiement minimum", + "minerMinPayout": "Si vous préférez un montant de paiement minimum plus élevé que celui du pool c'est ici que vous pouvez le changer pour vos mineurs. Le montant que vous indiquerez ici deviendra le montant minimum pour les paiements à votre adresse.", + "minimumPayout": "Paiement minimum", + "enableEmailNotifications": "Activer les notifications par email", + "minerEmailNotify": "Ce pool peut vous envoyer une notification par email lorsqu'un bloc est trouvé ou bien lorsqu'un paiement vous est transmis.", + "emailAddress": "Adresse email", + "noMinerAddress": "Aucune adresse de mineur spécifiée", + "noMinerIP": "Aucune adresse IP pour votre mineur spécifiée", + "noPayoutLevel": "Aucun niveau de paiement spécifié", + "noEmail": "Aucune adresse email spécifiée", + "invalidEmail": "L'adresse email spécifiée est invalide", + "minerPayoutSet": "Fait! Votre niveau de paiement minimum a été configuré", + "notificationEnabled": "Fait! Les notifications par email ont été activées", + "notificationDisabled": "Fait! Les notifications par email ont été désactivées", + + "enterYourAddress": "Entrez votre adresse", + "enterYourMinerIP": "Une adresse IP utilisée par votre mineur (peu importe)", + "enterYourEmail": "Votre adresse email", + + "lookup": "Chercher", + "searching": "Recherche...", + "loadMore": "Charger plus", + "set": "Configurer", + "enable": "Activer", + "disable": "Désactiver", + + "status": "Statut", + "updated": "Mis à jour:", + "source": "Source:", + "error": "Erreur:", + + "na": "N/D", + "estimated": "estimé", + "never": "Jamais", + "second": "seconde", + "seconds": "secondes", + "minute": "minute", + "minutes": "minutes", + "hour": "heure", + "hours": "heures", + "day": "jour", + "days": "jours", + "week": "semaine", + "weeks": "weeks", + "month": "mois", + "months": "mois", + "year": "année", + "years": "années", + + "poweredBy": "Propulsé par", + "openSource": "et libre de droits sous licence" +} \ No newline at end of file diff --git a/campurro-merged-website-tamplate/lang/it.json b/campurro-merged-website-tamplate/lang/it.json new file mode 100644 index 000000000..cbe084fc1 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/it.json @@ -0,0 +1,168 @@ +{ + "miningPool": "Mining Pool", + "dashboard": "Dashboard", + "gettingStarted": "Come Iniziare", + "yourStats": "Statistiche del Worker", + "poolBlocks": "Pool Blocks", + "settings": "Impostazioni", + "faq": "FAQ", + "telegram": "Gruppo Telegram", + "discord": "Discord", + "contactUs": "Contattaci", + + "network": "Rete", + "pool": "Pool", + "you": "Tu", + "statsUpdated": "Stats Aggiornati", + + "poolHashrate": "Pool Hash Rate", + "currentEffort": "Current Effort", + "networkHashrate": "Network Hash Rate", + "networkDifficulty": "Difficoltà", + "blockchainHeight": "Blockchain Height", + "networkLastReward": "Ultimo Reward", + "poolMiners": "Miners Connessi", + "poolFee": "Pool Fee", + + "minerStats": "Stats Personali & Storia dei tuoi pagamenti", + "workerStats": "Statistiche del Worker", + "miner": "Miner", + "miners": "Miners", + "minersCount": "miners", + "workers": "Workers", + "workersCount": "workers", + "workerName": "Worker Name", + "lastHash": "Ultimo Hash", + "hashRate": "Hash Rate", + "currentHashRate": "Attuale Hash Rate", + "lastShare": "Ultimo Share Trasmesso", + "totalHashes": "Totale Hashes Trasmessi", + "top10miners": "Top 10 miners", + + "blocksTotal": "Blocchi trovati", + "blockSolvedTime": "Blocco trovato ogni", + "blocksMaturityCount": "Maturità richiesta", + "efficiency": "Efficenza", + "averageLuck": "Fortuna", + "timeFound": "Orario trovato", + "reward": "Ricompensa", + "height": "Height", + "difficulty": "Difficoltà", + "blockHash": "Block Hash", + "effort": "Effort", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + + "payments": "Pagamenti", + "paymentsHistory": "Storia Pagamenti", + "paymentsTotal": "Pagamenti totali", + "paymentsMinimum": "Minimo pagamento", + "paymentsInterval": "intervallo pagamento", + "paymentsDenomination": "Unità di denominazione", + "timeSent": "Orario Inviato", + "transactionHash": "Transazione Hash", + "amount": "Quantità", + "fee": "Fee", + "mixin": "Mixin", + "payees": "Pagati", + "pendingBalance": "In sospeso", + "totalPaid": "Totale pagato", + "payoutEstimate": "Stima del pagamento corrente", + "paymentSummarySingle": "In %DATE% hai ricevuto %AMOUNT%", + "paymentSummaryMulti": "In %DATE% hai ricevuto %AMOUNT% in %COUNT% pagamenti", + + "connectionDetails": "Detagli Connessione", + "miningPoolHost": "Mining Pool Address", + "cnAlgorithm": "Algorithmo", + "username": "Username", + "usernameDesc": "questo è L'indrizzo del wallet", + "paymentId": "Exchange Payment ID", + "fixedDiff": "Difficoltà di blocco", + "address": "indrizzo", + "addrPaymentId": "paymentID", + "addrDiff": "diff", + "password": "Password", + "passwordDesc": "Questo è il nome del Worker", + "emailNotifications": "Notifiche Email", + "miningPorts": "Mining Ports", + "port": "Port", + "portDiff": "Difficoltà avvio", + "description": "Descrizione", + "miningApps": "Applicazioni Mining", + "configGeneratorDesc": "Genera La tua configurazione per minare sulla nostra pool", + "addressField": "Indrizzo wallet", + "paymentIdField": "Pagamento ID per exchanges (optionale)", + "fixedDiffField": "Fixed difficulty (optionale)", + "workerNameField": "Worker_Name", + "emailNotificationsField": "Notifiche email (optionale)", + "generateConfig": "Genera configuratione", + "appName": "App Name", + "appArch": "Architettura", + "appDesc": "Features", + "download": "Download", + "showConfig": "Vedi", + + "market": "Market / Calculatoe", + "loadingMarket": "Loading market prices", + "priceIn": "Price in", + "hashPer": "Hash/", + "estimateProfit": "Stima dei profitti", + "enterYourHashrate": "Inserisci il tuo Hashrate", + "perDay": "al giorno", + + "verificationFields": "Campi di verifica", + "minerVerification": "Per avere un po 'più di fiducia che l'indirizzo del tuo wallet è tuo ti chiediamo di dare uno degli indirizzi IP che viene utilizzato dal tuo miners.", + "minerAddress": "Miner Address", + "minerIP": "Miner IP address", + "setMinimumPayout": "Inserisci il pagamento minimo ", + "minerMinPayout": "Se preferisci un livello di pagamento più alto rispetto al valore predefinito del pool, è qui che puoi cambiarlo per i tuoi miner. L'importo indicato qui diventerà l'importo minimo per i pagamenti del pool al tuo indirizzo.", + "minimumPayout": "Payout Minimo", + "enableEmailNotifications": "Abilitare notifiche email", + "minerEmailNotify": "Questa pool invierà una notifica via email quando viene trovato un blocco e ogni volta che si verifica un pagamento.", + "emailAddress": "Email address", + "noMinerAddress": "Nessun indrizzo wallet indicato", + "noMinerIP": "Nessun indrizzo ip indicato del miner", + "noPayoutLevel": "Nessun livello specificato", + "noEmail": "Nessun indirizzo email specificato", + "invalidEmail": "Indrizzo email invalido", + "minerPayoutSet": "Fatto! Il tuo livello di pagamento minimo è stato impostato", + "notificationEnabled": "Fatto! Le notifiche email sono state abilitate", + "notificationDisabled": "Fatto! Le notifiche email sono state disabilitate", + + "enterYourAddress": "Inserisci il tuo indrizzo", + "enterYourMinerIP": "Un indrizzo ip di qualsiasi tuo miner", + "enterYourEmail": "Inserisci il tuo indrizzo email (optionale)", + + "lookup": "Consulto..", + "searching": "Cerco...", + "loadMore": "Carica Di più", + "set": "imposta", + "enable": "abilita", + "disable": "Disabilita", + + "status": "Stato", + "updated": "Aggiornato:", + "source": "Fonte:", + "error": "Errore:", + + "na": "N/A", + "estimated": "stimato", + "never": "mai", + "second": "secondo", + "seconds": "secondi", + "minute": "minuto", + "minutes": "minuti", + "hour": "ora", + "hours": "ore", + "day": "giorno", + "days": "giorni", + "week": "settimana", + "weeks": "settimane", + "month": "mese", + "months": "mesi", + "year": "anno", + "years": "anni", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" +} diff --git a/campurro-merged-website-tamplate/lang/ko.json b/campurro-merged-website-tamplate/lang/ko.json new file mode 100644 index 000000000..229d66d51 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/ko.json @@ -0,0 +1,167 @@ +{ + "miningPool": "Mining Pool", + "dashboard": "풀상황", + "gettingStarted": "도움말:시작", + "yourStats": "마이너(워커) 상황", + "poolBlocks": "풀블럭상태", + "settings": "설정", + "telegram": "텔레그램연결", + "discord": "Discord", + "contactUs": "문의하기", + + "network": "네트워크", + "pool": "풀", + "you": "you", + "statsUpdated": "업데이트", + + "poolHashrate": "풀의 해시레이트", + "currentEffort": "Current Effort", + "networkHashrate": "네크워크 해시레이트", + "networkDifficulty": "난이도", + "blockchainHeight": "블록체인 높이", + "networkLastReward": "최근 보상량", + "poolMiners": "연결된 마이너수", + "poolFee": "풀 수수료", + + "minerStats": "마이너 상태와 지급상황", + "workerStats": "워커 상태", + "miner": "Miner", + "miners": "Miners", + "minersCount": "miners", + "workers": "Workers", + "workersCount": "workers", + "workerName": "Worker Name", + "lastHash": "Last Hash", + "hashRate": "Hash Rate", + "currentHashRate": "Current Hash Rate", + "lastShare": "Last Share Submitted", + "totalHashes": "Total Hashes Submitted", + "top10miners": "상위 10 채굴자", + + "blocksTotal": "블록 발견", + "blockSolvedTime": "Blocks Found Every", + "blocksMaturityCount": "적립되기전 블럭수량", + "efficiency": "Efficiency", + "averageLuck": "Average Luck", + "timeFound": "블럭 발견시작", + "reward": "보상", + "height": "블럭번호", + "difficulty": "난이도", + "blockHash": "블럭 해시", + "effort": "풀의노력", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + + "payments": "출금상황", + "paymentsHistory": "출금 내역", + "paymentsTotal": "전체 출금 수", + "paymentsMinimum": "최소 출금 수량", + "paymentsInterval": "출금 주기", + "paymentsDenomination": "최소 출금 단위", + "timeSent": "출금 시작", + "transactionHash": "트랜잭션 아이디", + "amount": "수량", + "fee": "수수료", + "mixin": "Mixin", + "payees": "출금대상수", + "pendingBalance": "적립 대기", + "totalPaid": "전체 출금", + "payoutEstimate": "Current Payout Estimate", + "paymentSummarySingle": "On %DATE% you have received %AMOUNT%", + "paymentSummaryMulti": "On %DATE% you have received %AMOUNT% in %COUNT% payments", + + "connectionDetails": "연결정보", + "miningPoolHost": "풀 접속주소", + "cnAlgorithm": "채굴 알고리즘", + "username": "Username", + "usernameDesc": "This is your wallet address", + "paymentId": "거래소 Payment ID", + "fixedDiff": "고정 난이도 설정", + "address": "지갑주소", + "addrPaymentId": "paymentID", + "addrDiff": "난이도", + "password": "암호", + "passwordDesc": "워커 이름 설정", + "emailNotifications": "Email Notifications", + "miningPorts": "채굴 포트", + "port": "접속포트", + "portDiff": "시작 난이도", + "description": "설명", + "miningApps": "마이닝 프로그램", + "configGeneratorDesc": "Generate your custom configuration to mine on our pool", + "addressField": "지갑 주소", + "paymentIdField": "거래소용 Payment ID (optional)", + "fixedDiffField": "고정난이도 (optional)", + "workerNameField": "워커 이름", + "emailNotificationsField": "Email Notifications (optional)", + "generateConfig": "설정 예제 생성", + "appName": "프로그램 이름", + "appArch": "Architecture", + "appDesc": "Features", + "download": "Download", + "showConfig": "See more", + + "market": "시장가격 / 채굴량계산기", + "loadingMarket": "Loading market prices", + "priceIn": "Price in", + "hashPer": "Hash/", + "estimateProfit": "Estimate Mining Profits", + "enterYourHashrate": "Enter Your Hash Rate", + "perDay": "per day", + + "verificationFields": "Verification fields", + "minerVerification": "In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.", + "minerAddress": "Miner Address", + "minerIP": "Miner IP address", + "setMinimumPayout": "Set your minimal payout level", + "minerMinPayout": "If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.", + "minimumPayout": "Minimum payout", + "enableEmailNotifications": "Enable email notifications", + "minerEmailNotify": "This pool will send out email notification when a block is found and whenever a payout happens.", + "emailAddress": "Email address", + "noMinerAddress": "No miner address specified", + "noMinerIP": "No miner IP address specified", + "noPayoutLevel": "No payout level specified", + "noEmail": "No email address specified", + "invalidEmail": "Invalid email address specified", + "minerPayoutSet": "Done! Your minimum payout level was set", + "notificationEnabled": "Done! Email notifications have been enabled", + "notificationDisabled": "Done! Email notifications have been disabled", + + "enterYourAddress": "Enter Your Address", + "enterYourMinerIP": "An IP address your miners use (any)", + "enterYourEmail": "Enter Your E-Mail Address (optional)", + + "lookup": "Lookup", + "searching": "Searching...", + "loadMore": "Load more", + "set": "Set", + "enable": "Enable", + "disable": "Disable", + + "status": "Status", + "updated": "Updated:", + "source": "Source:", + "error": "Error:", + + "na": "N/A", + "estimated": "estimated", + "never": "Never", + "second": "second", + "seconds": "seconds", + "minute": "minute", + "minutes": "minutes", + "hour": "hour", + "hours": "hours", + "day": "day", + "days": "days", + "week": "week", + "weeks": "weeks", + "month": "month", + "months": "months", + "year": "year", + "years": "years", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" +} diff --git a/campurro-merged-website-tamplate/lang/languages.js b/campurro-merged-website-tamplate/lang/languages.js new file mode 100644 index 000000000..b73a776a2 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/languages.js @@ -0,0 +1 @@ +var langs = { 'en': 'English', 'es': 'Español', 'fr': 'Français', 'it': 'Italiano', 'ru': 'Русский', 'ca': 'Català', 'ko': '한국어', 'zh-CN': '简体中文' }; \ No newline at end of file diff --git a/campurro-merged-website-tamplate/lang/ru.json b/campurro-merged-website-tamplate/lang/ru.json new file mode 100644 index 000000000..a3ce4b1a0 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/ru.json @@ -0,0 +1,181 @@ +{ + "miningPool": "Майнинг пул", + "dashboard": "Главная", + "gettingStarted": "Присоединиться", + "yourStats": "Статистика", + "poolBlocks": "Блоки пула", + "settings": "Настройки", + "faq": "FAQ", + "telegram": "Группа Telegram", + "discord": "Discord", + "contactUs": "Почта", + + "network": "Сеть", + "pool": "Пул", + "you": "Вы", + "statsUpdated": "Статистика обновлена", + + "poolHashrate": "Скорость пула", + "currentEffort": "Сложность раунда", + "networkHashrate": "Скорость сети", + "networkDifficulty": "Сложность", + "blockchainHeight": "№ последнего блока", + "networkLastReward": "Последнее вознаграждение", + "poolMiners": "Пользователи пула", + "poolFee": "Комиссия пула", + + "minerStats": "Ваша статистика и история платежей", + "workerStats": "Статистика ферм", + "miner": "Miner", + "miners": "Пользователи", + "minersCount": "miners", + "workers": "Фермы", + "workersCount": "фермы", + "workerName": "Имя фермы", + "lastHash": "Последний хеш", + "hashRate": "Скорость", + "currentHashRate": "Текущая скорость", + "lastShare": "Последняя шара принята", + "totalHashes": "Всего принято хешей", + "top10miners": "Лучшие 10 майнеров", + "blocksTotal": "Найдено блоков", + "blockSolvedTime": "Время нахождения блока", + "blocksMaturityCount": "Требование подтверждения", + "efficiency": "Эффективность", + "averageLuck": "Средняя удача", + "timeFound": "Время нахождения", + "reward": "Выплата", + "height": "№ блока", + "difficulty": "Сложность", + "blockHash": "Хеш блока", + "effort": "Усилие", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + + "payments": "Платежи", + "paymentsHistory": "История платежей", + "paymentsTotal": "Всего платежей", + "paymentsMinimum": "Минимальный платёж", + "paymentsInterval": "Интервал платежей", + "paymentsDenomination": "Единица измерения", + "timeSent": "Время отправления", + "transactionHash": "Хеш транзакции", + "amount": "Сумма", + "fee": "Комиссия", + "mixin": "Mixin", + "payees": "Получателей", + "pendingBalance": "Ожидающий баланс", + "totalPaid": "Всего выплачено", + "payoutEstimate": "Текущая оценка выплат", + + "connectionDetails": "Детали подключения", + "miningPoolHost": "Адрес майнинг пула", + "cnAlgorithm": "Алгоритм", + "username": "Имя пользователя", + "usernameDesc": "Это адрес вашего кошелька", + "paymentId": "Биржевой Payment ID", + "fixedDiff": "Фиксированная сложность", + "address": "address", + "addrPaymentId": "paymentID", + "addrDiff": "diff", + "password": "Пароль", + "passwordDesc": "Это имя вашей фермы в статистике", + "emailNotifications": "Уведомление по почте", + "miningPorts": "Порты для майнинга", + "port": "Порт", + "portDiff": "Стартовая сложность", + "description": "Описание", + "miningApps": "Программы для майнинга", + "configGeneratorDesc": "Создайте свою собственную конфигурацию для этого пула", + "addressField": "Адрес вашего кошелька", + "paymentIdField": "Payment ID для биржи (опция)", + "fixedDiffField": "Фиксированная сложность (опция)", + "workerNameField": "Имя_Фермы", + "emailNotificationsField": "Уведомление по почте (опция)", + "generateConfig": "Создать конфигурацию", + "appName": "Программа", + "appArch": "Архитектура", + "appDesc": "Особенности", + "download": "Скачать", + "showConfig": "Посмотреть", + + "market": "Рынок / Калькулятор", + "loadingMarket": "Загрузка стоимости", + "priceIn": "Стоимость в", + "hashPer": "Стоимость хеша/", + "estimateProfit": "Рассчёт прибыли", + "enterYourHashrate": "Введите вашу скорость", + "perDay": "/в день", + + "verificationFields": "Проверочные данные", + "minerVerification": "Чтобы быть уверенным в том, что адрес кошелька принадлежит вам, мы просим вас указать один из IP-адресов, который используется вашими фермами.", + "minerAddress": "Ваш кошелёк", + "minerIP": "IP адрес фермы", + "setMinimumPayout": "Установите минимальный уровень выплат", + "minerMinPayout": "Вы можете установить минимальный порог оплаты, если предпочитаете более высокий уровень выплат, чем значение по умолчанию. Сумма, которую вы здесь укажете, станет минимальной суммой для платежей пула на ваш адрес.", + "minimumPayout": "Минимальная выплата", + "enableEmailNotifications": "Включить уведомление по почте", + "minerEmailNotify": "Пул отправит уведомление по электронной почте, когда будет найден блок или когда произведёт выплату.", + "emailAddress": "E-mail адрес", + "noMinerAddress": "Вы не ввели свой кошелёк", + "noMinerIP": "Вы не ввели IP адрес фермы", + "noPayoutLevel": "Вы не ввели уровень оплаты", + "noEmail": "Вы не ввели e-mail адрес", + "invalidEmail": "Не правильный e-mail адрес", + "minerPayoutSet": "Минимальный уровень оплаты успешно установлен !", + "notificationEnabled": "Уведомления по электронной почте успешно включены !", + "notificationDisabled": "Уведомления по электронной почте успешно выключены !", + + "enterYourAddress": "Введите адрес своего кошелька", + "enterYourMinerIP": "IP-адрес, который используют ваши фермы", + "enterYourEmail": "Введите свой E-Mail адрес (опция)", + + "lookup": "Посмотреть", + "searching": "Поиск ...", + "loadMore": "Загрузить ещё", + "set": "Установить", + "enable": "Включить", + "disable": "Выключить", + + "status": "Статус", + "updated": "Обновлено:", + "source": "Source:", + "error": "Ошибка:", + + "na": "N/A", + "estimated": "примерно", + "never": "Не найден", + "second": "секунда", + "seconds": "секунд", + "minute": "минута", + "minutes": "минут", + "hour": "час", + "hours": "часов", + "day": "день", + "days": "дней", + "week": "неделя", + "weeks": "недель", + "month": "месяц", + "months": "месяцев", + "year": "год", + "years": "года", + + "timeagoPrefixAgo": null, + "timeagoPrefixFromNow": null, + "timeagoSuffixAgo": "назад", + "timeagoSuffixFromNow": "from now", + "timeagoSeconds": "меньше минуты", + "timeagoMinute": "около минуты", + "timeagoMinutes": "%d минут", + "timeagoHour": "about an hour", + "timeagoHours": "about %d hours", + "timeagoDay": " день", + "timeagoDays": "%d дней", + "timeagoMonth": "about месяц", + "timeagoMonths": "%d месяцев", + "timeagoYear": "about год", + "timeagoYears": "%d лет", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" +} \ No newline at end of file diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.af.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.af.js new file mode 100644 index 000000000..817a7fa59 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.af.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Afrikaans + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "gelede", + suffixFromNow: "van nou af", + seconds: "%d sekondes", + minute: "1 minuut", + minutes: "%d minute", + hour: "1 uur", + hours: "%d ure", + day: "1 dag", + days: "%d dae", + month: "1 maand", + months: "%d maande", + year: "1 jaar", + years: "%d jaar", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.am.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.am.js new file mode 100644 index 000000000..65502c39d --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.am.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Amharic + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "በፊት", + suffixFromNow: "በኋላ", + seconds: "ከአንድ ደቂቃ በታች", + minute: "ከአንድ ደቂቃ ገደማ", + minutes: "ከ%d ደቂቃ", + hour: "ከአንድ ሰዓት ገደማ", + hours: "ከ%d ሰዓት ገደማ", + day: "ከአንድ ቀን", + days: "ከ%d ቀን", + month: "ከአንድ ወር ገደማ", + months: "ከ%d ወር", + year: "ከአንድ ዓመት ገደማ", + years: "ከ%d ዓመት", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ar.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ar.js new file mode 100644 index 000000000..14cd18f23 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ar.js @@ -0,0 +1,104 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + function numpf(n, a) { + return a[plural=n===0 ? 0 : n===1 ? 1 : n===2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: "منذ", + prefixFromNow: "بعد", + suffixAgo: null, + suffixFromNow: null, // null OR "من الآن" + second: function(value) { return numpf(value, [ + 'أقل من ثانية', + 'ثانية واحدة', + 'ثانيتين', + '%d ثوانٍ', + '%d ثانية', + '%d ثانية']); }, + seconds: function(value) { return numpf(value, [ + 'أقل من ثانية', + 'ثانية واحدة', + 'ثانيتين', + '%d ثوانٍ', + '%d ثانية', + '%d ثانية']); }, + minute: function(value) { return numpf(value, [ + 'أقل من دقيقة', + 'دقيقة واحدة', + 'دقيقتين', + '%d دقائق', + '%d دقيقة', + 'دقيقة']); }, + minutes: function(value) { return numpf(value, [ + 'أقل من دقيقة', + 'دقيقة واحدة', + 'دقيقتين', + '%d دقائق', + '%d دقيقة', + 'دقيقة']); }, + hour: function(value) { return numpf(value, [ + 'أقل من ساعة', + 'ساعة واحدة', + 'ساعتين', + '%d ساعات', + '%d ساعة', + '%d ساعة']); }, + hours: function(value) { return numpf(value, [ + 'أقل من ساعة', + 'ساعة واحدة', + 'ساعتين', + '%d ساعات', + '%d ساعة', + '%d ساعة']); }, + day: function(value) { return numpf(value, [ + 'أقل من يوم', + 'يوم واحد', + 'يومين', + '%d أيام', + '%d يومًا', + '%d يوم']); }, + days: function(value) { return numpf(value, [ + 'أقل من يوم', + 'يوم واحد', + 'يومين', + '%d أيام', + '%d يومًا', + '%d يوم']); }, + month: function(value) { return numpf(value, [ + 'أقل من شهر', + 'شهر واحد', + 'شهرين', + '%d أشهر', + '%d شهرًا', + '%d شهر']); }, + months: function(value) { return numpf(value, [ + 'أقل من شهر', + 'شهر واحد', + 'شهرين', + '%d أشهر', + '%d شهرًا', + '%d شهر']); }, + year: function(value) { return numpf(value, [ + 'أقل من عام', + 'عام واحد', + '%d عامين', + '%d أعوام', + '%d عامًا']); + }, + years: function(value) { return numpf(value, [ + 'أقل من عام', + 'عام واحد', + 'عامين', + '%d أعوام', + '%d عامًا', + '%d عام']);} + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.az.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.az.js new file mode 100644 index 000000000..8332c41cf --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.az.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Azerbaijani + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: 'əvvəl', + suffixFromNow: 'sonra', + seconds: 'saniyələr', + minute: '1 dəqiqə', + minutes: '%d dəqiqə', + hour: '1 saat', + hours: '%d saat', + day: '1 gün', + days: '%d gün', + month: '1 ay', + months: '%d ay', + year: '1 il', + years: '%d il', + wordSeparator: '', + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.bg.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.bg.js new file mode 100644 index 000000000..a3bd343af --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.bg.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Bulgarian + jQuery.timeago.settings.strings = { + prefixAgo: "преди", + prefixFromNow: "след", + suffixAgo: null, + suffixFromNow: null, + seconds: "по-малко от минута", + minute: "една минута", + minutes: "%d минути", + hour: "един час", + hours: "%d часа", + day: "един ден", + days: "%d дни", + month: "един месец", + months: "%d месеца", + year: "една година", + years: "%d години" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.bs.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.bs.js new file mode 100644 index 000000000..cbb178069 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.bs.js @@ -0,0 +1,55 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Bosnian + var numpf = function(n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "prije", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekund", + seconds: function(value) { + return numpf(value, "%d sekund", "%d sekunde", "%d sekundi"); + }, + minute: "oko minut", + minutes: function(value) { + return numpf(value, "%d minut", "%d minute", "%d minuta"); + }, + hour: "oko sat", + hours: function(value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "oko jednog dana", + days: function(value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mjesec dana", + months: function(value) { + return numpf(value, "%d mjesec", "%d mjeseca", "%d mjeseci"); + }, + year: "prije godinu dana ", + years: function(value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; + +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ca.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ca.js new file mode 100644 index 000000000..e4cb5cab7 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ca.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Catalan + jQuery.timeago.settings.strings = { + prefixAgo: "fa", + prefixFromNow: "d'aquí", + suffixAgo: null, + suffixFromNow: null, + seconds: "menys d'un minut", + minute: "un minut", + minutes: "%d minuts", + hour: "una hora", + hours: "%d hores", + day: "un dia", + days: "%d dies", + month: "un mes", + months: "%d mesos", + year: "un any", + years: "%d anys", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.cs.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.cs.js new file mode 100644 index 000000000..b940f6949 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.cs.js @@ -0,0 +1,34 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Czech + (function() { + function f(n, d, a) { + return a[d>=0 ? 0 : a.length===2 || n<5 ? 1 : 2]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: 'před', + prefixFromNow: 'za', + suffixAgo: null, + suffixFromNow: null, + seconds: function(n, d) {return f(n, d, ['méně než minutou', 'méně než minutu']);}, + minute: function(n, d) {return f(n, d, ['minutou', 'minutu']);}, + minutes: function(n, d) {return f(n, d, ['%d minutami', '%d minuty', '%d minut']);}, + hour: function(n, d) {return f(n, d, ['hodinou', 'hodinu']);}, + hours: function(n, d) {return f(n, d, ['%d hodinami', '%d hodiny', '%d hodin']);}, + day: function(n, d) {return f(n, d, ['%d dnem', '%d den']);}, + days: function(n, d) {return f(n, d, ['%d dny', '%d dny', '%d dní']);}, + month: function(n, d) {return f(n, d, ['%d měsícem', '%d měsíc']);}, + months: function(n, d) {return f(n, d, ['%d měsíci', '%d měsíce', '%d měsíců']);}, + year: function(n, d) {return f(n, d, ['%d rokem', '%d rok']);}, + years: function(n, d) {return f(n, d, ['%d lety', '%d roky', '%d let']);} + }; + })(); +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.cy.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.cy.js new file mode 100644 index 000000000..4c514a8df --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.cy.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Welsh + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "yn ôl", + suffixFromNow: "o hyn", + seconds: "llai na munud", + minute: "am funud", + minutes: "%d munud", + hour: "tua awr", + hours: "am %d awr", + day: "y dydd", + days: "%d diwrnod", + month: "tua mis", + months: "%d mis", + year: "am y flwyddyn", + years: "%d blynedd", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.da.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.da.js new file mode 100644 index 000000000..236c34c44 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.da.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Danish + jQuery.timeago.settings.strings = { + prefixAgo: "for", + prefixFromNow: "om", + suffixAgo: "siden", + suffixFromNow: "", + seconds: "mindre end et minut", + minute: "ca. et minut", + minutes: "%d minutter", + hour: "ca. en time", + hours: "ca. %d timer", + day: "en dag", + days: "%d dage", + month: "ca. en måned", + months: "%d måneder", + year: "ca. et år", + years: "%d år" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.de.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.de.js new file mode 100644 index 000000000..6a877a231 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.de.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // German + jQuery.timeago.settings.strings = { + prefixAgo: "vor", + prefixFromNow: "in", + suffixAgo: "", + suffixFromNow: "", + seconds: "wenigen Sekunden", + minute: "etwa einer Minute", + minutes: "%d Minuten", + hour: "etwa einer Stunde", + hours: "%d Stunden", + day: "etwa einem Tag", + days: "%d Tagen", + month: "etwa einem Monat", + months: "%d Monaten", + year: "etwa einem Jahr", + years: "%d Jahren" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.dv.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.dv.js new file mode 100644 index 000000000..0d70a493c --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.dv.js @@ -0,0 +1,32 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + /** + * Dhivehi time in Thaana for timeago.js + **/ + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ކުރިން", + suffixFromNow: "ފަހުން", + seconds: "ސިކުންތުކޮޅެއް", + minute: "މިނިޓެއްވަރު", + minutes: "%d މިނިޓު", + hour: "ގަޑިއެއްވަރު", + hours: "ގާތްގަނޑަކަށް %d ގަޑިއިރު", + day: "އެއް ދުވަސް", + days: "މީގެ %d ދުވަސް", + month: "މަހެއްވަރު", + months: "މީގެ %d މަސް", + year: "އަހަރެއްވަރު", + years: "މީގެ %d އަހަރު", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.el.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.el.js new file mode 100644 index 000000000..2db9ebea8 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.el.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Greek + jQuery.timeago.settings.strings = { + prefixAgo: "πριν", + prefixFromNow: "σε", + suffixAgo: "", + suffixFromNow: "", + seconds: "λιγότερο από ένα λεπτό", + minute: "περίπου ένα λεπτό", + minutes: "%d λεπτά", + hour: "περίπου μία ώρα", + hours: "περίπου %d ώρες", + day: "μία μέρα", + days: "%d μέρες", + month: "περίπου ένα μήνα", + months: "%d μήνες", + year: "περίπου ένα χρόνο", + years: "%d χρόνια" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.en.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.en.js new file mode 100644 index 000000000..8ca50afff --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.en.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // English (Template) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ago", + suffixFromNow: "from now", + seconds: "less than a minute", + minute: "about a minute", + minutes: "%d minutes", + hour: "about an hour", + hours: "about %d hours", + day: "a day", + days: "%d days", + month: "about a month", + months: "%d months", + year: "about a year", + years: "%d years", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.es.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.es.js new file mode 100644 index 000000000..0785b3f42 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.es.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Spanish + jQuery.timeago.settings.strings = { + prefixAgo: "hace", + prefixFromNow: "dentro de", + suffixAgo: "", + suffixFromNow: "", + seconds: "menos de un minuto", + minute: "un minuto", + minutes: "unos %d minutos", + hour: "una hora", + hours: "%d horas", + day: "un día", + days: "%d días", + month: "un mes", + months: "%d meses", + year: "un año", + years: "%d años" + }; +})); + diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.et.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.et.js new file mode 100644 index 000000000..ac2461ec7 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.et.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Estonian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "tagasi", + suffixFromNow: "pärast", + seconds: function(n, d) { return d < 0 ? "vähem kui minuti aja" : "vähem kui minut aega"; }, + minute: function(n, d) { return d < 0 ? "umbes minuti aja" : "umbes minut aega"; }, + minutes: function(n, d) { return d < 0 ? "%d minuti" : "%d minutit"; }, + hour: function(n, d) { return d < 0 ? "umbes tunni aja" : "umbes tund aega"; }, + hours: function(n, d) { return d < 0 ? "%d tunni" : "%d tundi"; }, + day: function(n, d) { return d < 0 ? "umbes päeva" : "umbes päev"; }, + days: "%d päeva", + month: function(n, d) { return d < 0 ? "umbes kuu aja" : "umbes kuu aega"; }, + months: function(n, d) { return d < 0 ? "%d kuu" : "%d kuud"; }, + year: function(n, d) { return d < 0 ? "umbes aasta aja" : "umbes aasta aega"; }, + years: function(n, d) { return d < 0 ? "%d aasta" : "%d aastat"; } + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.eu.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.eu.js new file mode 100644 index 000000000..5c2c32c7e --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.eu.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + jQuery.timeago.settings.strings = { + prefixAgo: "duela", + prefixFromNow: "hemendik", + suffixAgo: "", + suffixFromNow: "barru", + seconds: "minutu bat bainu gutxiago", + minute: "minutu bat", + minutes: "%d minutu inguru", + hour: "ordu bat", + hours: "%d ordu", + day: "egun bat", + days: "%d egun", + month: "hilabete bat", + months: "%d hilabete", + year: "urte bat", + years: "%d urte" + }; +})); + diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fa.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fa.js new file mode 100644 index 000000000..ec8ccb952 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fa.js @@ -0,0 +1,32 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Persian + // Use DIR attribute for RTL text in Persian Language for ABBR tag . + // By MB.seifollahi@gmail.com + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "پیش", + suffixFromNow: "از حال", + seconds: "کمتر از یک دقیقه", + minute: "حدود یک دقیقه", + minutes: "%d دقیقه", + hour: "حدود یک ساعت", + hours: "حدود %d ساعت", + day: "یک روز", + days: "%d روز", + month: "حدود یک ماه", + months: "%d ماه", + year: "حدود یک سال", + years: "%d سال", + wordSeparator: " ", + numbers: ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fi.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fi.js new file mode 100644 index 000000000..b5f7e696d --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fi.js @@ -0,0 +1,38 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Finnish + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "sitten", + suffixFromNow: "tulevaisuudessa", + seconds: "alle minuutti", + minute: "minuutti", + minutes: "%d minuuttia", + hour: "tunti", + hours: "%d tuntia", + day: "päivä", + days: "%d päivää", + month: "kuukausi", + months: "%d kuukautta", + year: "vuosi", + years: "%d vuotta" + }; + + // The above is not a great localization because one would usually + // write "2 days ago" in Finnish as "2 päivää sitten", however + // one would write "2 days into the future" as "2:n päivän päästä" + // which cannot be achieved with localization support this simple. + // This is because Finnish has word suffixes (attached directly + // to the end of the word). The word "day" is "päivä" in Finnish. + // As workaround, the above localizations will say + // "2 päivää tulevaisuudessa" which is understandable but + // not as fluent. +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fr.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fr.js new file mode 100644 index 000000000..1bb052aa1 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.fr.js @@ -0,0 +1,27 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // French + jQuery.timeago.settings.strings = { + // environ ~= about, it's optional + prefixAgo: "il y a", + prefixFromNow: "d'ici", + seconds: "moins d'une minute", + minute: "environ une minute", + minutes: "environ %d minutes", + hour: "environ une heure", + hours: "environ %d heures", + day: "environ un jour", + days: "environ %d jours", + month: "environ un mois", + months: "environ %d mois", + year: "un an", + years: "%d ans" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.gl.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.gl.js new file mode 100644 index 000000000..277bbf70f --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.gl.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Galician + jQuery.timeago.settings.strings = { + prefixAgo: "hai", + prefixFromNow: "dentro de", + suffixAgo: "", + suffixFromNow: "", + seconds: "menos dun minuto", + minute: "un minuto", + minutes: "uns %d minutos", + hour: "unha hora", + hours: "%d horas", + day: "un día", + days: "%d días", + month: "un mes", + months: "%d meses", + year: "un ano", + years: "%d anos" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.he.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.he.js new file mode 100644 index 000000000..2cd31ab60 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.he.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Hebrew + jQuery.timeago.settings.strings = { + prefixAgo: "לפני", + prefixFromNow: "עוד", + seconds: "פחות מדקה", + minute: "דקה", + minutes: "%d דקות", + hour: "שעה", + hours: function(number){return (number===2) ? "שעתיים" : "%d שעות";}, + day: "יום", + days: function(number){return (number===2) ? "יומיים" : "%d ימים";}, + month: "חודש", + months: function(number){return (number===2) ? "חודשיים" : "%d חודשים";}, + year: "שנה", + years: function(number){return (number===2) ? "שנתיים" : "%d שנים";} + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hr.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hr.js new file mode 100644 index 000000000..bd142979a --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hr.js @@ -0,0 +1,54 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Croatian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "prije", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekundu", + seconds: function (value) { + return numpf(value, "%d sekundu", "%d sekunde", "%d sekundi"); + }, + minute: "oko minutu", + minutes: function (value) { + return numpf(value, "%d minutu", "%d minute", "%d minuta"); + }, + hour: "oko jedan sat", + hours: function (value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "jedan dan", + days: function (value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mjesec dana", + months: function (value) { + return numpf(value, "%d mjesec", "%d mjeseca", "%d mjeseci"); + }, + year: "prije godinu dana", + years: function (value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hu.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hu.js new file mode 100644 index 000000000..0009de9e2 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hu.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Hungarian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: null, + seconds: "kevesebb mint egy perce", + minute: "körülbelül egy perce", + minutes: "%d perce", + hour: "körülbelül egy órája", + hours: "körülbelül %d órája", + day: "körülbelül egy napja", + days: "%d napja", + month: "körülbelül egy hónapja", + months: "%d hónapja", + year: "körülbelül egy éve", + years: "%d éve" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hy.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hy.js new file mode 100644 index 000000000..3f0de6e7d --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.hy.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Armenian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "առաջ", + suffixFromNow: "հետո", + seconds: "վայրկյաններ", + minute: "մեկ րոպե", + minutes: "%d րոպե", + hour: "մեկ ժամ", + hours: "%d ժամ", + day: "մեկ օր", + days: "%d օր", + month: "մեկ ամիս", + months: "%d ամիս", + year: "մեկ տարի", + years: "%d տարի" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.id.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.id.js new file mode 100644 index 000000000..ca530ccf8 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.id.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Indonesian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "yang lalu", + suffixFromNow: "dari sekarang", + seconds: "kurang dari semenit", + minute: "sekitar satu menit", + minutes: "%d menit", + hour: "sekitar sejam", + hours: "sekitar %d jam", + day: "sehari", + days: "%d hari", + month: "sekitar sebulan", + months: "%d bulan", + year: "sekitar setahun", + years: "%d tahun" + }; +})); + diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.is.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.is.js new file mode 100644 index 000000000..e3d4b1fd1 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.is.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + jQuery.timeago.settings.strings = { + prefixAgo: "fyrir", + prefixFromNow: "eftir", + suffixAgo: "síðan", + suffixFromNow: null, + seconds: "minna en mínútu", + minute: "mínútu", + minutes: "%d mínútum", + hour: "klukkutíma", + hours: "um %d klukkutímum", + day: "degi", + days: "%d dögum", + month: "mánuði", + months: "%d mánuðum", + year: "ári", + years: "%d árum", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.it.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.it.js new file mode 100644 index 000000000..e1cac8431 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.it.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Italian + jQuery.timeago.settings.strings = { + suffixAgo: "fa", + suffixFromNow: "da ora", + seconds: "meno di un minuto", + minute: "circa un minuto", + minutes: "%d minuti", + hour: "circa un'ora", + hours: "circa %d ore", + day: "un giorno", + days: "%d giorni", + month: "circa un mese", + months: "%d mesi", + year: "circa un anno", + years: "%d anni" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ja.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ja.js new file mode 100644 index 000000000..30f3308c3 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ja.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Japanese + jQuery.timeago.settings.strings = { + prefixAgo: "", + prefixFromNow: "今から", + suffixAgo: "前", + suffixFromNow: "後", + seconds: "1 分未満", + minute: "約 1 分", + minutes: "%d 分", + hour: "約 1 時間", + hours: "約 %d 時間", + day: "約 1 日", + days: "約 %d 日", + month: "約 1 ヶ月", + months: "約 %d ヶ月", + year: "約 1 年", + years: "約 %d 年", + wordSeparator: "" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.jv.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.jv.js new file mode 100644 index 000000000..0344053d7 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.jv.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Javanesse (Boso Jowo) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "kepungkur", + suffixFromNow: "seko saiki", + seconds: "kurang seko sakmenit", + minute: "kurang luwih sakmenit", + minutes: "%d menit", + hour: "kurang luwih sakjam", + hours: "kurang luwih %d jam", + day: "sedina", + days: "%d dina", + month: "kurang luwih sewulan", + months: "%d wulan", + year: "kurang luwih setahun", + years: "%d tahun" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ko.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ko.js new file mode 100644 index 000000000..23d1d3780 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ko.js @@ -0,0 +1,31 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Korean + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "전", + suffixFromNow: "후", + seconds: "1분", + minute: "약 1분", + minutes: "%d분", + hour: "약 1시간", + hours: "약 %d시간", + day: "하루", + days: "%d일", + month: "약 1개월", + months: "%d개월", + year: "약 1년", + years: "%d년", + wordSeparator: " ", + numbers: [] + }; +})); + diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ky.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ky.js new file mode 100644 index 000000000..58dba293a --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ky.js @@ -0,0 +1,42 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Russian + function numpf(n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + var n10 = n % 10; + if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { + return f; + } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { + return s; + } else { + return t; + } + } + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "мурун", + suffixFromNow: null, + seconds: "1 минуттан аз", + minute: "минута", + minutes: function(value) { return numpf(value, "%d минута", "%d минута", "%d минут"); }, + hour: "саат", + hours: function(value) { return numpf(value, "%d саат", "%d саат", "%d саат"); }, + day: "күн", + days: function(value) { return numpf(value, "%d күн", "%d күн", "%d күн"); }, + month: "ай", + months: function(value) { return numpf(value, "%d ай", "%d ай", "%d ай"); }, + year: "жыл", + years: function(value) { return numpf(value, "%d жыл", "%d жыл", "%d жыл"); } + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.lt.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.lt.js new file mode 100644 index 000000000..2079fccd3 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.lt.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + //Lithuanian + jQuery.timeago.settings.strings = { + prefixAgo: "prieš", + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "nuo dabar", + seconds: "%d sek.", + minute: "min.", + minutes: "%d min.", + hour: "val.", + hours: "%d val.", + day: "1 d.", + days: "%d d.", + month: "mėn.", + months: "%d mėn.", + year: "metus", + years: "%d metus", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.lv.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.lv.js new file mode 100644 index 000000000..855d1a4dd --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.lv.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + //Latvian + jQuery.timeago.settings.strings = { + prefixAgo: "pirms", + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "no šī brīža", + seconds: "%d sek.", + minute: "min.", + minutes: "%d min.", + hour: "st.", + hours: "%d st.", + day: "1 d.", + days: "%d d.", + month: "mēnesis.", + months: "%d mēnesis.", + year: "gads", + years: "%d gads", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.mk.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.mk.js new file mode 100644 index 000000000..301a5da83 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.mk.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Macedonian + (function() { + jQuery.timeago.settings.strings={ + prefixAgo: "пред", + prefixFromNow: "за", + suffixAgo: null, + suffixFromNow: null, + seconds: "%d секунди", + minute: "%d минута", + minutes: "%d минути", + hour: "%d час", + hours: "%d часа", + day: "%d ден", + days: "%d денови" , + month: "%d месец", + months: "%d месеци", + year: "%d година", + years: "%d години" + }; + })(); +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.nl.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.nl.js new file mode 100644 index 000000000..2c5de89c0 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.nl.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Dutch + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "over", + suffixAgo: "geleden", + suffixFromNow: null, + seconds: "minder dan een minuut", + minute: "ongeveer een minuut", + minutes: "%d minuten", + hour: "ongeveer een uur", + hours: "ongeveer %d uur", + day: "een dag", + days: "%d dagen", + month: "ongeveer een maand", + months: "%d maanden", + year: "ongeveer een jaar", + years: "%d jaar", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.no.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.no.js new file mode 100644 index 000000000..c896337c7 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.no.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Norwegian + jQuery.timeago.settings.strings = { + prefixAgo: "for", + prefixFromNow: "om", + suffixAgo: "siden", + suffixFromNow: "", + seconds: "mindre enn et minutt", + minute: "ca. et minutt", + minutes: "%d minutter", + hour: "ca. en time", + hours: "ca. %d timer", + day: "en dag", + days: "%d dager", + month: "ca. en måned", + months: "%d måneder", + year: "ca. et år", + years: "%d år" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pl.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pl.js new file mode 100644 index 000000000..48427846a --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pl.js @@ -0,0 +1,39 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Polish + function numpf(n, s, t) { + // s - 2-4, 22-24, 32-34 ... + // t - 5-21, 25-31, ... + var n10 = n % 10; + if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { + return s; + } else { + return t; + } + } + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "za", + suffixAgo: "temu", + suffixFromNow: null, + seconds: "mniej niż minutę", + minute: "minutę", + minutes: function(value) { return numpf(value, "%d minuty", "%d minut"); }, + hour: "godzinę", + hours: function(value) { return numpf(value, "%d godziny", "%d godzin"); }, + day: "dzień", + days: "%d dni", + month: "miesiąc", + months: function(value) { return numpf(value, "%d miesiące", "%d miesięcy"); }, + year: "rok", + years: function(value) { return numpf(value, "%d lata", "%d lat"); } + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pt-br.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pt-br.js new file mode 100644 index 000000000..a8701a880 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pt-br.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Brazilian Portuguese + jQuery.timeago.settings.strings = { + prefixAgo: "há", + prefixFromNow: "em", + suffixAgo: null, + suffixFromNow: null, + seconds: "alguns segundos", + minute: "um minuto", + minutes: "%d minutos", + hour: "uma hora", + hours: "%d horas", + day: "um dia", + days: "%d dias", + month: "um mês", + months: "%d meses", + year: "um ano", + years: "%d anos" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pt.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pt.js new file mode 100644 index 000000000..13791a037 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.pt.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Portuguese + jQuery.timeago.settings.strings = { + suffixAgo: "atrás", + suffixFromNow: "a partir de agora", + seconds: "menos de um minuto", + minute: "cerca de um minuto", + minutes: "%d minutos", + hour: "cerca de uma hora", + hours: "cerca de %d horas", + day: "um dia", + days: "%d dias", + month: "cerca de um mês", + months: "%d meses", + year: "cerca de um ano", + years: "%d anos" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ro.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ro.js new file mode 100644 index 000000000..fe59db900 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ro.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Romanian + jQuery.timeago.settings.strings = { + prefixAgo: "acum", + prefixFromNow: "in timp de", + suffixAgo: "", + suffixFromNow: "", + seconds: "mai putin de un minut", + minute: "un minut", + minutes: "%d minute", + hour: "o ora", + hours: "%d ore", + day: "o zi", + days: "%d zile", + month: "o luna", + months: "%d luni", + year: "un an", + years: "%d ani" + }; +})); + diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.rs.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.rs.js new file mode 100644 index 000000000..b9e518825 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.rs.js @@ -0,0 +1,54 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Serbian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "pre", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekund", + seconds: function (value) { + return numpf(value, "%d sekund", "%d sekunde", "%d sekundi"); + }, + minute: "oko minut", + minutes: function (value) { + return numpf(value, "%d minut", "%d minuta", "%d minuta"); + }, + hour: "oko jedan sat", + hours: function (value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "jedan dan", + days: function (value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mesec dana", + months: function (value) { + return numpf(value, "%d mesec", "%d meseca", "%d meseci"); + }, + year: "godinu dana", + years: function (value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ru.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ru.js new file mode 100644 index 000000000..4ff3f8d30 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ru.js @@ -0,0 +1,43 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Russian + function numpf(n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + n = n % 100; + var n10 = n % 10; + if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { + return f; + } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { + return s; + } else { + return t; + } + } + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "назад", + suffixFromNow: null, + seconds: "меньше минуты", + minute: "минуту", + minutes: function(value) { return numpf(value, "%d минуту", "%d минуты", "%d минут"); }, + hour: "час", + hours: function(value) { return numpf(value, "%d час", "%d часа", "%d часов"); }, + day: "день", + days: function(value) { return numpf(value, "%d день", "%d дня", "%d дней"); }, + month: "месяц", + months: function(value) { return numpf(value, "%d месяц", "%d месяца", "%d месяцев"); }, + year: "год", + years: function(value) { return numpf(value, "%d год", "%d года", "%d лет"); } + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.rw.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.rw.js new file mode 100644 index 000000000..50119e1e8 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.rw.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Kinyarwanda + jQuery.timeago.settings.strings = { + prefixAgo: "hashize", + prefixFromNow: "mu", + suffixAgo: null, + suffixFromNow: null, + seconds: "amasegonda macye", + minute: "umunota", + minutes: "iminota %d", + hour: "isaha", + hours: "amasaha %d", + day: "umunsi", + days: "iminsi %d", + month: "ukwezi", + months: "amezi %d", + year: "umwaka", + years: "imyaka %d", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.si.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.si.js new file mode 100644 index 000000000..6fa215e9a --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.si.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Sinhalese (SI) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "පෙර", + suffixFromNow: "පසුව", + seconds: "තත්පර කිහිපයකට", + minute: "මිනිත්තුවකට පමණ", + minutes: "මිනිත්තු %d කට", + hour: "පැයක් පමණ ", + hours: "පැය %d කට පමණ", + day: "දවසක ට", + days: "දවස් %d කට ", + month: "මාසයක් පමණ", + months: "මාස %d කට", + year: "වසරක් පමණ", + years: "වසරක් %d කට පමණ" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sk.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sk.js new file mode 100644 index 000000000..e28ab7c9b --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sk.js @@ -0,0 +1,34 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Slovak + (function() { + function f(n, d, a) { + return a[d>=0 ? 0 : a.length===2 || n<5 ? 1 : 2]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: 'pred', + prefixFromNow: 'o', + suffixAgo: null, + suffixFromNow: null, + seconds: function(n, d) {return f(n, d, ['menej ako minútou', 'menej ako minútu']);}, + minute: function(n, d) {return f(n, d, ['minútou', 'minútu']);}, + minutes: function(n, d) {return f(n, d, ['%d minútami', '%d minúty', '%d minút']);}, + hour: function(n, d) {return f(n, d, ['hodinou', 'hodinu']);}, + hours: function(n, d) {return f(n, d, ['%d hodinami', '%d hodiny', '%d hodín']);}, + day: function(n, d) {return f(n, d, ['%d dňom', '%d deň']);}, + days: function(n, d) {return f(n, d, ['%d dňami', '%d dni', '%d dní']);}, + month: function(n, d) {return f(n, d, ['%d mesiacom', '%d mesiac']);}, + months: function(n, d) {return f(n, d, ['%d mesiacmi', '%d mesiace', '%d mesiacov']);}, + year: function(n, d) {return f(n, d, ['%d rokom', '%d rok']);}, + years: function(n, d) {return f(n, d, ['%d rokmi', '%d roky', '%d rokov']);} + }; + })(); +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sl.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sl.js new file mode 100644 index 000000000..9f0329ac6 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sl.js @@ -0,0 +1,46 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Slovenian with support for dual + var numpf = function (n, a) { + return a[n%100===1 ? 1 : n%100===2 ? 2 : n%100===3 || n%100===4 ? 3 : 0]; + }; + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "čez", + suffixAgo: "nazaj", + suffixFromNow: null, + second: "sekundo", + seconds: function (value) { + return numpf(value, ["%d sekund", "%d sekundo", "%d sekundi", "%d sekunde"]); + }, + minute: "minuto", + minutes: function (value) { + return numpf(value, ["%d minut", "%d minuto", "%d minuti", "%d minute"]); + }, + hour: "eno uro", + hours: function (value) { + return numpf(value, ["%d ur", "%d uro", "%d uri", "%d ure"]); + }, + day: "en dan", + days: function (value) { + return numpf(value, ["%d dni", "%d dan", "%d dneva", "%d dni"]); + }, + month: "en mesec", + months: function (value) { + return numpf(value, ["%d mesecev", "%d mesec", "%d meseca", "%d mesece"]); + }, + year: "eno leto", + years: function (value) { + return numpf(value, ["%d let", "%d leto", "%d leti", "%d leta"]); + }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sq.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sq.js new file mode 100644 index 000000000..cb8ae703a --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sq.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Albanian SQ + jQuery.timeago.settings.strings = { + suffixAgo: "më parë", + suffixFromNow: "tani", + seconds: "më pak se një minutë", + minute: "rreth një minutë", + minutes: "%d minuta", + hour: "rreth një orë", + hours: "rreth %d orë", + day: "një ditë", + days: "%d ditë", + month: "rreth një muaj", + months: "%d muaj", + year: "rreth një vit", + years: "%d vjet" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sr.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sr.js new file mode 100644 index 000000000..bd1efe79a --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sr.js @@ -0,0 +1,54 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Serbian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "пре", + prefixFromNow: "за", + suffixAgo: null, + suffixFromNow: null, + second: "секунд", + seconds: function (value) { + return numpf(value, "%d секунд", "%d секунде", "%d секунди"); + }, + minute: "један минут", + minutes: function (value) { + return numpf(value, "%d минут", "%d минута", "%d минута"); + }, + hour: "један сат", + hours: function (value) { + return numpf(value, "%d сат", "%d сата", "%d сати"); + }, + day: "један дан", + days: function (value) { + return numpf(value, "%d дан", "%d дана", "%d дана"); + }, + month: "месец дана", + months: function (value) { + return numpf(value, "%d месец", "%d месеца", "%d месеци"); + }, + year: "годину дана", + years: function (value) { + return numpf(value, "%d годину", "%d године", "%d година"); + }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sv.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sv.js new file mode 100644 index 000000000..caf09dbb0 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.sv.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Swedish + jQuery.timeago.settings.strings = { + prefixAgo: "för", + prefixFromNow: "om", + suffixAgo: "sedan", + suffixFromNow: "", + seconds: "mindre än en minut", + minute: "ungefär en minut", + minutes: "%d minuter", + hour: "ungefär en timme", + hours: "ungefär %d timmar", + day: "en dag", + days: "%d dagar", + month: "ungefär en månad", + months: "%d månader", + year: "ungefär ett år", + years: "%d år" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.th.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.th.js new file mode 100644 index 000000000..23d59d48e --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.th.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Thai + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ที่แล้ว", + suffixFromNow: "จากตอนนี้", + seconds: "น้อยกว่าหนึ่งนาที", + minute: "ประมาณหนึ่งนาที", + minutes: "%d นาที", + hour: "ประมาณหนึ่งชั่วโมง", + hours: "ประมาณ %d ชั่วโมง", + day: "หนึ่งวัน", + days: "%d วัน", + month: "ประมาณหนึ่งเดือน", + months: "%d เดือน", + year: "ประมาณหนึ่งปี", + years: "%d ปี", + wordSeparator: "", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.tr.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.tr.js new file mode 100644 index 000000000..8e0d2d4e9 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.tr.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Turkish + jQuery.timeago.settings.strings = { + suffixAgo: 'önce', + suffixFromNow: null, + seconds: 'birkaç saniye', + minute: '1 dakika', + minutes: '%d dakika', + hour: '1 saat', + hours: '%d saat', + day: '1 gün', + days: '%d gün', + month: '1 ay', + months: '%d ay', + year: '1 yıl', + years: '%d yıl' + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.uk.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.uk.js new file mode 100644 index 000000000..489963b5d --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.uk.js @@ -0,0 +1,42 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Ukrainian + function numpf(n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + var n10 = n % 10; + if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { + return f; + } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { + return s; + } else { + return t; + } + } + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "тому", + suffixFromNow: null, + seconds: "менше хвилини", + minute: "хвилина", + minutes: function(value) { return numpf(value, "%d хвилина", "%d хвилини", "%d хвилин"); }, + hour: "година", + hours: function(value) { return numpf(value, "%d година", "%d години", "%d годин"); }, + day: "день", + days: function(value) { return numpf(value, "%d день", "%d дні", "%d днів"); }, + month: "місяць", + months: function(value) { return numpf(value, "%d місяць", "%d місяці", "%d місяців"); }, + year: "рік", + years: function(value) { return numpf(value, "%d рік", "%d роки", "%d років"); } + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ur.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ur.js new file mode 100644 index 000000000..9d0cd402d --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.ur.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Urdu + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "پہلے", + suffixFromNow: "اب سے", + seconds: "کچھ سیکنڈز", + minute: "تقریباً ایک منٹ", + minutes: "%d منٹ", + hour: "تقریباً ایک گھنٹہ", + hours: "تقریباً %d گھنٹے", + day: "ایک دن", + days: "%d دن", + month: "تقریباً ایک مہینہ", + months: "%d مہینے", + year: "تقریباً ایک سال", + years: "%d سال", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.uz.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.uz.js new file mode 100644 index 000000000..f4ce8b33b --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.uz.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + //Uzbek + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "keyin", + suffixAgo: "avval", + suffixFromNow: null, + seconds: "bir necha soniya", + minute: "1 daqiqa", + minutes: function(value) { return "%d daqiqa"; }, + hour: "1 soat", + hours: function(value) { return "%d soat"; }, + day: "1 kun", + days: function(value) { return "%d kun"; }, + month: "1 oy", + months: function(value) { return "%d oy"; }, + year: "1 yil", + years: function(value) { return "%d yil"; }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.vi.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.vi.js new file mode 100644 index 000000000..30f592ac3 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.vi.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Vietnamese + jQuery.timeago.settings.strings = { + prefixAgo: 'cách đây', + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "trước", + seconds: "chưa đến một phút", + minute: "khoảng một phút", + minutes: "%d phút", + hour: "khoảng một tiếng", + hours: "khoảng %d tiếng", + day: "một ngày", + days: "%d ngày", + month: "khoảng một tháng", + months: "%d tháng", + year: "khoảng một năm", + years: "%d năm", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.zh-CN.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.zh-CN.js new file mode 100644 index 000000000..c21a2874a --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.zh-CN.js @@ -0,0 +1,31 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Simplified Chinese + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "之前", + suffixFromNow: "之后", + seconds: "不到1分钟", + minute: "大约1分钟", + minutes: "%d分钟", + hour: "大约1小时", + hours: "大约%d小时", + day: "1天", + days: "%d天", + month: "大约1个月", + months: "%d月", + year: "大约1年", + years: "%d年", + numbers: [], + wordSeparator: "" + }; +})); + diff --git a/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.zh-TW.js b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.zh-TW.js new file mode 100644 index 000000000..15f562699 --- /dev/null +++ b/campurro-merged-website-tamplate/lang/timeago/jquery.timeago.zh-TW.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Traditional Chinese, zh-tw + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "之前", + suffixFromNow: "之後", + seconds: "不到1分鐘", + minute: "大約1分鐘", + minutes: "%d分鐘", + hour: "大約1小時", + hours: "%d小時", + day: "大約1天", + days: "%d天", + month: "大約1個月", + months: "%d個月", + year: "大約1年", + years: "%d年", + numbers: [], + wordSeparator: "" + }; +})); diff --git a/campurro-merged-website-tamplate/lang/zh-CN.json b/campurro-merged-website-tamplate/lang/zh-CN.json new file mode 100644 index 000000000..10ad0ba7c --- /dev/null +++ b/campurro-merged-website-tamplate/lang/zh-CN.json @@ -0,0 +1,168 @@ +{ + "miningPool": "矿池", + "dashboard": "仪表盘", + "gettingStarted": "挖矿指导", + "yourStats": "挖矿数据", + "poolBlocks": "池中区块", + "settings": "设置", + "faq": "常见问题", + "telegram": "Telegram群组", + "discord": "Discord", + "contactUs": "联系方式", + + "network": "全网", + "pool": "矿池", + "you": "用户", + "statsUpdated": "数据更新", + + "poolHashrate": "矿池算力", + "currentEffort": "当前效益", + "networkHashrate": "全网算力", + "networkDifficulty": "难度", + "blockchainHeight": "区块高度", + "networkLastReward": "上次收益", + "poolMiners": "在线矿工", + "poolFee": "矿池税", + + "minerStats": "用户数据及支付历史", + "workerStats": "设备数据", + "miner": "矿工", + "miners": "矿工", + "minersCount": "矿工数", + "workers": "挖矿设备", + "workersCount": "设备数", + "workerName": "设备名", + "lastHash": "上次提交", + "hashRate": "算力", + "currentHashRate": "当前算力", + "lastShare": "上次提交", + "totalHashes": "总提交", + "top10miners": "前十矿工", + + "blocksTotal": "已发现区块", + "blockSolvedTime": "区块发现用时", + "blocksMaturityCount": "成熟要求", + "efficiency": "效率", + "averageLuck": "平均幸运值", + "timeFound": "发现时间", + "reward": "收益", + "height": "高度", + "difficulty": "难度", + "blockHash": "区块Hash", + "effort": "Effort", + "blocksFoundLast24": "过去24小时内发现区块", + "blocksFoundLastDays": "过去{DAYS}天中发现区块", + + "payments": "支付", + "paymentsHistory": "支付历史", + "paymentsTotal": "总支付", + "paymentsMinimum": "最小支付额", + "paymentsInterval": "支付周期", + "paymentsDenomination": "面额单位", + "timeSent": "Time Sent", + "transactionHash": "交易Hash", + "amount": "数额", + "fee": "费用", + "mixin": "Mixin", + "payees": "收款人", + "pendingBalance": "处理中余额", + "totalPaid": "总计支付", + "payoutEstimate": "当前支付估计", + "paymentSummarySingle": "在%DATE%您收到%AMOUNT%", + "paymentSummaryMulti": "在%DATE%,通过%COUNT%笔支付,您收到%AMOUNT%", + + "connectionDetails": "连接信息", + "miningPoolHost": "矿池地址", + "cnAlgorithm": "算法", + "username": "用户名", + "usernameDesc": "这是你的钱包地址", + "paymentId": "交易所支付ID", + "fixedDiff": "固定难度", + "address": "地址", + "addrPaymentId": "交易ID", + "addrDiff": "难度", + "password": "密码", + "passwordDesc": "这是你的设备名", + "emailNotifications": "邮件提醒", + "miningPorts": "挖矿端口", + "port": "端口", + "portDiff": "初始难度", + "description": "介绍", + "miningApps": "挖矿应用", + "configGeneratorDesc": "生成您定制的配置", + "addressField": "钱包地址", + "paymentIdField": "为交易所的交易ID (可选)", + "fixedDiffField": "固定难度 (可选)", + "workerNameField": "设备名", + "emailNotificationsField": "邮件提醒 (可选)", + "generateConfig": "生成配置", + "appName": "应用名", + "appArch": "架构平台", + "appDesc": "特征", + "download": "下载", + "showConfig": "了解更多", + + "market": "市场计算器", + "loadingMarket": "加载市场价格", + "priceIn": "价格", + "hashPer": "Hash/", + "estimateProfit": "预计挖矿收益", + "enterYourHashrate": "确认您的算力", + "perDay": "每天", + + "verificationFields": "验证字段", + "minerVerification": "为了确保钱包地址是您的,我们要求您提供您的矿工使用的IP地址之一.", + "minerAddress": "矿工地址", + "minerIP": "矿工IP地址", + "setMinimumPayout": "设置您的最低支付水平", + "minerMinPayout": "如果你喜欢比池的默认值更高的支付水平,那么这是你可以为你的矿工改变它。您在此处显示的金额将成为您的地址的最低付款金额.", + "minimumPayout": "最低付款金额", + "enableEmailNotifications": "启用电子邮件通知", + "minerEmailNotify": "当发现一个区块并且每当支付发生时,该池将发送电子邮件通知.", + "emailAddress": "邮箱地址", + "noMinerAddress": "没有指定矿工地址", + "noMinerIP": "没有指定矿工IP地址", + "noPayoutLevel": "没有指定最小付款金额", + "noEmail": "没有指定邮箱地址", + "invalidEmail": "指定的电子邮件地址无效", + "minerPayoutSet": "完成! 您的最低支出水平已设定", + "notificationEnabled": "完成! 电子邮件通知已启用", + "notificationDisabled": "完成! 电子邮件通知已被禁用", + + "enterYourAddress": "输入您的地址", + "enterYourMinerIP": "您的矿工使用的IP地址 (任一)", + "enterYourEmail": "输入你的电子邮箱地址 (可选)", + + "lookup": "查找", + "searching": "搜索中...", + "loadMore": "加载更多", + "set": "设置", + "enable": "允许", + "disable": "禁止", + + "status": "状态", + "updated": "已更新:", + "source": "源:", + "error": "错误:", + + "na": "无", + "estimated": "预计", + "never": "从不", + "second": "秒", + "seconds": "秒", + "minute": "分", + "minutes": "分", + "hour": "小时", + "hours": "小时", + "day": "天", + "days": "天", + "week": "周", + "weeks": "周", + "month": "月", + "months": "月", + "year": "年", + "years": "年", + + "poweredBy": "驱动源于", + "openSource": "开源于" +} From 3941872f92651e06b5c254270bc8b9d711e81cf0 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Tue, 7 May 2019 09:41:28 +0200 Subject: [PATCH 90/98] Add files via upload --- .../themes/admin.css | 104 ++ .../themes/custom.css | 60 + .../themes/default.css | 1010 +++++++++++++++++ .../timeago/jquery.timeago.af.js | 30 + .../timeago/jquery.timeago.am.js | 30 + .../timeago/jquery.timeago.ar.js | 104 ++ .../timeago/jquery.timeago.az.js | 30 + .../timeago/jquery.timeago.bg.js | 28 + .../timeago/jquery.timeago.bs.js | 55 + .../timeago/jquery.timeago.ca.js | 30 + .../timeago/jquery.timeago.cs.js | 34 + .../timeago/jquery.timeago.cy.js | 30 + .../timeago/jquery.timeago.da.js | 28 + .../timeago/jquery.timeago.de.js | 28 + .../timeago/jquery.timeago.dv.js | 32 + .../timeago/jquery.timeago.el.js | 28 + .../timeago/jquery.timeago.en.js | 30 + .../timeago/jquery.timeago.es.js | 29 + .../timeago/jquery.timeago.et.js | 28 + .../timeago/jquery.timeago.eu.js | 28 + .../timeago/jquery.timeago.fa.js | 32 + .../timeago/jquery.timeago.fi.js | 38 + .../timeago/jquery.timeago.fr.js | 27 + .../timeago/jquery.timeago.gl.js | 28 + .../timeago/jquery.timeago.he.js | 26 + .../timeago/jquery.timeago.hr.js | 54 + .../timeago/jquery.timeago.hu.js | 28 + .../timeago/jquery.timeago.hy.js | 28 + .../timeago/jquery.timeago.id.js | 29 + .../timeago/jquery.timeago.is.js | 29 + .../timeago/jquery.timeago.it.js | 26 + .../timeago/jquery.timeago.ja.js | 29 + .../timeago/jquery.timeago.jv.js | 28 + .../timeago/jquery.timeago.ko.js | 31 + .../timeago/jquery.timeago.ky.js | 42 + .../timeago/jquery.timeago.lt.js | 30 + .../timeago/jquery.timeago.lv.js | 30 + .../timeago/jquery.timeago.mk.js | 30 + .../timeago/jquery.timeago.nl.js | 30 + .../timeago/jquery.timeago.no.js | 28 + .../timeago/jquery.timeago.pl.js | 39 + .../timeago/jquery.timeago.pt-br.js | 28 + .../timeago/jquery.timeago.pt.js | 26 + .../timeago/jquery.timeago.ro.js | 29 + .../timeago/jquery.timeago.rs.js | 54 + .../timeago/jquery.timeago.ru.js | 43 + .../timeago/jquery.timeago.rw.js | 30 + .../timeago/jquery.timeago.si.js | 28 + .../timeago/jquery.timeago.sk.js | 34 + .../timeago/jquery.timeago.sl.js | 46 + .../timeago/jquery.timeago.sq.js | 26 + .../timeago/jquery.timeago.sr.js | 54 + .../timeago/jquery.timeago.sv.js | 28 + .../timeago/jquery.timeago.th.js | 30 + .../timeago/jquery.timeago.tr.js | 26 + .../timeago/jquery.timeago.uk.js | 42 + .../timeago/jquery.timeago.ur.js | 30 + .../timeago/jquery.timeago.uz.js | 29 + .../timeago/jquery.timeago.vi.js | 30 + .../timeago/jquery.timeago.zh-CN.js | 31 + .../timeago/jquery.timeago.zh-TW.js | 30 + 61 files changed, 3112 insertions(+) create mode 100644 campurro-merged-website-tamplate/themes/admin.css create mode 100644 campurro-merged-website-tamplate/themes/custom.css create mode 100644 campurro-merged-website-tamplate/themes/default.css create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.af.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.am.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.ar.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.az.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.bg.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.bs.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.ca.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.cs.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.cy.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.da.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.de.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.dv.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.el.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.en.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.es.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.et.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.eu.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.fa.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.fi.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.fr.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.gl.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.he.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.hr.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.hu.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.hy.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.id.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.is.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.it.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.ja.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.jv.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.ko.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.ky.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.lt.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.lv.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.mk.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.nl.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.no.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.pl.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.pt-br.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.pt.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.ro.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.rs.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.ru.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.rw.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.si.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.sk.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.sl.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.sq.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.sr.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.sv.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.th.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.tr.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.uk.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.ur.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.uz.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.vi.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.zh-CN.js create mode 100644 campurro-merged-website-tamplate/timeago/jquery.timeago.zh-TW.js diff --git a/campurro-merged-website-tamplate/themes/admin.css b/campurro-merged-website-tamplate/themes/admin.css new file mode 100644 index 000000000..74db8cc53 --- /dev/null +++ b/campurro-merged-website-tamplate/themes/admin.css @@ -0,0 +1,104 @@ + +#page-wrapper { + padding: 0 15px 0; + min-height: 568px; +} +@media (min-width: 992px) { + #page-wrapper { + padding: 0 30px; + } +} + +.nav-side-menu li.sign-out { + border-left: 3px solid darkred; + background-color: rgba(255,0,0,.2); +} + +.nav-side-menu li.sign-out:hover { + border-left: 3px solid darkred; + background-color: darkred; +} + +/* Pool Statistics */ +#poolStats .luckGood { + color: #5eff5e; +} +#poolStats .luckBad { + color: red; +} +#poolStats .luckMid { + color: #FFF500; +} + +/* Users list */ +.usersList { + word-wrap: break-word; +} +.usersList .tooltip-inner { + max-width: 100%; +} +@media (min-width: 768px) { + .usersList { + table-layout: fixed; + } + .usersList tr > th, + .usersList tr > td { + text-align: center; + white-space: nowrap; + } + .usersList .col1 { + white-space: normal; + } + .usersList .col2 { + width: 100px; + } + .usersList .col3 { + width: 100px; + } + .usersList .col4 { + width: 100px; + } + .usersList .col5 { + width: 100px; + } + .usersList .col6 { + width: 120px; + white-space: normal; + } +} + +/* Monitoring */ +.adminMonitor code { + white-space: normal; +} +.adminMonitor .tab-pane li { + margin-bottom: 10px; +} +.adminMonitor .infos { + margin-bottom: 20px; +} +.adminMonitor #logTable th { + white-space: nowrap; +} + +/* Ports Usage */ +#portsUsage tr > td { + vertical-align: middle; + font-size: 0.95em; +} +#portsUsage table .col1, +#portsUsage table .col2 { + text-align: center; +} +@media (min-width: 768px) { + #portsUsage table th { + white-space: nowrap; + } + #portsUsage table .col1 { + width:60px; + } + #portsUsage table .col2 { + width:160px; + text-align: center; + } +} diff --git a/campurro-merged-website-tamplate/themes/custom.css b/campurro-merged-website-tamplate/themes/custom.css new file mode 100644 index 000000000..650f639d5 --- /dev/null +++ b/campurro-merged-website-tamplate/themes/custom.css @@ -0,0 +1,60 @@ + +/* Insert your pool's unique CSS here */ + +.nav-side-menu { + background: #164450; +} + +.nav-side-menu .brand { + background: #164450; + color: #fff; + padding: 10px; +} +.nav-side-menu .brand a { + color: #fff; + font-size: 16px; +} + +.brand { padding-top: 5px } +.brand img.img-responsive { + margin: 0 auto; +} + +.nav-side-menu ul .active, .nav-side-menu li .active { + border-left: 3px solid #5fbcd3; + background-color: #2c89a0; +} + +.infoBox { + /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#2c89a0+0,5fbcd3+100 */ +background: #2c89a0; /* Old browsers */ +background: -moz-linear-gradient(45deg, #2c89a0 0%, #5fbcd3 100%); /* FF3.6-15 */ +background: -webkit-linear-gradient(45deg, #2c89a0 0%,#5fbcd3 100%); /* Chrome10-25,Safari5.1-6 */ +background: linear-gradient(45deg, #2c89a0 0%,#5fbcd3 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ +filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#2c89a0', endColorstr='#5fbcd3',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ +} + +.mergedBg { + background: #3b5ca3; +} + +#top-bar { + background-color: #164450; +} + +.infoBox .content .value sup { + top: -.9em; + text-decoration: none; + text-shadow: none; + font-size: 45%; + right: -3px; + font-weight: normal; +} + +#infoBlocksTotal .content .value .smallText { + font-size: 10px; + font-weight: normal; + display: block; + text-shadow: none; + margin-top: -3px; +} \ No newline at end of file diff --git a/campurro-merged-website-tamplate/themes/default.css b/campurro-merged-website-tamplate/themes/default.css new file mode 100644 index 000000000..e6a6683da --- /dev/null +++ b/campurro-merged-website-tamplate/themes/default.css @@ -0,0 +1,1010 @@ + +@import url(//fonts.googleapis.com/css?family=Roboto+Condensed:400,700); +@import url(//fonts.googleapis.com/css?family=Roboto:400,300,500,700); +@import url(//fonts.googleapis.com/css?family=Inconsolata:400,700); + +body { + background-color: #f5f5f5; + font-family: 'Roboto', Helvetica, Arial, sans-serif; + line-height: 1.428571429; + color: #262626; + margin: 0; + padding: 0; + margin-bottom: 85px; + font-size: 16px; + overflow-y: scroll; +} +a { + color: #014e71; +} +a:hover { + color: #0274a8; +} +hr { + border-top-color: #0274a8; +} +strong, b { + font-weight: 500; +} +h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { + font-weight: 400; + -webkit-font-smoothing: antialiased; + font-family: 'Roboto Condensed', Arial, sans-serif; +} +h3,.h3 { + font-size: 32px; + margin-bottom: 14px; +} +code { + color: #0274a8; + background-color: #efefef; + padding: 2px 10px; + border-radius: 0; +} + +.padding-5 { padding: 5px !important; } +.padding-10 { padding: 10px !important; } +.padding-15 { padding: 15px !important; } +.padding-20 { padding: 20px !important; } +.padding-25 { padding: 25px !important; } +.padding-30 { padding: 30px !important; } + +.padding-t-5 { padding-top: 5px !important; } +.padding-t-10 { padding-top: 10px !important; } +.padding-t-15 { padding-top: 15px !important; } +.padding-t-20 { padding-top: 20px !important; } +.padding-t-25 { padding-top: 25px !important; } +.padding-t-30 { padding-top: 30px !important; } + +.padding-b-5 { padding-bottom: 5px !important; } +.padding-b-10 { padding-bottom: 10px !important; } +.padding-b-15 { padding-bottom: 15px !important; } +.padding-b-20 { padding-bottom: 20px !important; } +.padding-b-25 { padding-bottom: 25px !important; } +.padding-b-30 { padding-bottom: 30px !important; } + +.padding-l-5 { padding-left: 5px !important; } +.padding-l-10 { padding-left: 10px !important; } +.padding-l-15 { padding-left: 15px !important; } +.padding-l-20 { padding-left: 20px !important; } +.padding-l-25 { padding-left: 25px !important; } +.padding-l-30 { padding-left: 30px !important; } + +.padding-r-5 { padding-right: 5px !important; } +.padding-r-10 { padding-right: 10px !important; } +.padding-r-15 { padding-right: 15px !important; } +.padding-r-20 { padding-right: 20px !important; } +.padding-r-25 { padding-right: 25px !important; } +.padding-r-30 { padding-right: 30px !important; } + +.push-up-5 { margin-top: 5px !important; } +.push-up-10 { margin-top: 10px !important; } +.push-up-15 { margin-top: 15px !important; } +.push-up-20 { margin-top: 20px !important; } +.push-up-25 { margin-top: 25px !important; } +.push-up-30 { margin-top: 30px !important; } + +.push-down-5 { margin-bottom: 5px !important; } +.push-down-10 { margin-bottom: 10px !important; } +.push-down-15 { margin-bottom: 15px !important; } +.push-down-20 { margin-bottom: 20px !important; } +.push-down-25 { margin-bottom: 25px !important; } +.push-down-30 { margin-bottom: 30px !important; } + +/* Footer */ +footer{ + position: fixed; + bottom: 0; + width: 100%; + background-color: #262626; + font-size: 14px; + color: #efefef; + z-index: 9999; +} +footer a { + color: #6ED5EE; +} +footer > div{ + color: #fff; + margin: 10px auto; + text-align: center; +} + +/* Wrappers */ +#wrapper { + width: 100%; +} +#page-wrapper { + padding: 0 15px; + min-height: 568px; +} +@media (min-width: 992px) { + #page-wrapper { + position: inherit; + margin: 0 0 0 225px; + padding: 60px 30px 0 30px; + } +} +#loading{ + font-size: 2em; +} + +/* Sidebar */ +.nav-side-menu { + overflow: auto; + font-size: 16px; + font-weight: 200; + background-color: rgba(0, 0, 0, .8); + position: fixed; + top: 0; + width: 225px; + height: 100%; + color: #fff; + z-index: 1000; +} +.nav-side-menu .brand { + background-color: #014e71; + line-height: 50px; + display: block; + text-align: center; + font-size: 20px; + color: #fff; + font-weight: 400; +} +.nav-side-menu .brand a { + color: #fff; + text-decoration: none; +} + +.nav-side-menu .toggle-btn { + display: none; +} +.nav-side-menu ul, +.nav-side-menu li { + list-style: none; + padding: 0; + margin: 0; + line-height: 35px; + cursor: pointer; +} +.nav-side-menu ul :not(collapsed) .arrow:before, +.nav-side-menu li :not(collapsed) .arrow:before { + font-family: FontAwesome; + content: "\f078"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; + float: right; +} +.nav-side-menu ul .active, +.nav-side-menu li .active { + border-left: 3px solid #03a9f4; + background-color: #0274a8; +} +.nav-side-menu ul .active a, +.nav-side-menu li .active a { + color: #fff; +} +.nav-side-menu ul .sub-menu li.active, +.nav-side-menu li .sub-menu li.active { + color: #03a9f4; +} +.nav-side-menu ul .sub-menu li a, +.nav-side-menu li .sub-menu li a { + display: inline-block; + width: 85%; +} +.nav-side-menu ul .sub-menu li.active a, +.nav-side-menu li .sub-menu li.active a { + color: #03a9f4; +} +.nav-side-menu ul .sub-menu li, +.nav-side-menu li .sub-menu li { + background-color: #181c20; + border: none; + line-height: 28px; + border-bottom: 1px solid #23282e; + margin-left: 0; +} +.nav-side-menu ul .sub-menu li:hover, +.nav-side-menu li .sub-menu li:hover { + background-color: #020203; +} +.nav-side-menu ul .sub-menu li:before, +.nav-side-menu li .sub-menu li:before { + font-family: FontAwesome; + content: "\f105"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; +} +.nav-side-menu li { + padding-left: 0; + border-left: 3px solid #2e353d; + border-bottom: 1px solid #23282e; +} +.nav-side-menu li a { + text-decoration: none; + color: #fff; + display: block; + width: 100%; + padding: 2px 0; +} +.nav-side-menu li a i { + padding-left: 10px; + width: 20px; + padding-right: 20px; +} +.nav-side-menu li:hover { + border-left: 3px solid #03a9f4; + background-color: #0274a8; + -webkit-transition: all 1s ease; + -moz-transition: all 1s ease; + -o-transition: all 1s ease; + -ms-transition: all 1s ease; + transition: all 1s ease; +} +.nav-side-menu li:hover a { + color: #fff; +} +@media (max-width: 991px) { + .nav-side-menu { + position: relative; + width: 100%; + } + .nav-side-menu .toggle-btn { + display: block; + cursor: pointer; + position: absolute; + right: 10px; + top: 10px; + z-index: 10 !important; + padding: 3px; + background-color: #ffffff; + color: #000; + width: 40px; + text-align: center; + border-radius: 3px; + } + .brand { + text-align: left !important; + font-size: 22px; + padding-left: 20px; + line-height: 58px !important; + } +} +@media (min-width: 992px) { + .nav-side-menu .menu-list .menu-content { + display: block; + } +} + +/* Top Bar */ +#top-bar { + background-color: #0274a8; + line-height: 50px; + display: block; + font-size: 17px; + color: #fff; + padding: 0 15px; + z-index: 1000; +} +#top-bar div { + display: inline-block; + padding: 0 15px; +} +#top-bar div strong { + font-weight: 700; +} +#top-bar #statsUpdated { + font-weight: normal; +} +@media (max-width: 1199px) { + #top-bar #statsUpdated .text { + display: none; + } +} +@media (min-width: 992px) { + #top-bar { + overflow: auto; + position: fixed; + top: 0; + left: 225px; + right: 0; + padding: 0 15px; + } +} +@media (max-width: 767px) { + #top-bar { + display: none; + } +} + +/* Bootstrap Buttons */ +.btn-default { + color: #ffffff; + background-color: #014e71; + border-color: #014e71; +} +.btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { + color: #ffffff; + background-color: #013d58; + border-color: #013d58; +} +.btn-default.disabled,.btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { + background-color: #014e71; + border-color: #014e71; +} +.btn-default .badge { + color: #014e71; + background-color: #ffffff; +} +.btn-primary { + color: #ffffff; + background-color: #03a678; + border-color: #03a678; +} +.btn-primary:hover,.btn-primary:focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #027454; + border-color: #026a4d; +} +.btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { + background-color: #03a678; + border-color: #03a678; +} +.btn-primary .badge { + color: #03a678; + background-color: #ffffff; +} + +/* Bootstrap Form Controls */ +.input-group-addon { + color: #014e71; +} + +/* Bootstrap Tables */ +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #000000; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #000000; +} +.table > thead > tr > th { + color: #014e71; + border-bottom-color: #014e71; + font-weight: 400; +} +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: rgba(151, 222, 255, 0.3); +} +.table > tbody > tr:hover td, +.table > tbody > tr:hover th { + background-color: rgba(3, 169, 244, 0.3); +} +.table > tbody > tr > td { + border-top-color: #c9e0e9; +} +table th.sort:hover{ + cursor: pointer; +} + +/* jQuery sparkline tooltip */ +.jqstooltip { + border: none !important; + background: rgba(0, 0, 0, 0.8) !important; + border-radius: 2px !important; + margin-top: -20px !important; + /* Override Bootstrap defaults to fix jQuery.Sparkline tooltips appearance */ + -webkit-box-sizing: content-box !important; + -moz-box-sizing: content-box; + box-sizing: content-box !important; +} +.jqstooltip .jqsfield { + color: #ccc; + font-size: 13px !important; +} +.jqstooltip .jqsfield b { + color: #fff; +} + +/* Content Card */ +.card { + display: inline-block; + position: relative; + width: 100%; + margin: 0; + padding: 0; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + border-radius: 3px; + color: rgba(0, 0, 0, 0.87); + background: #fff; +} + +.card table, .card .table { + margin-bottom: 0; +} + +/* Info box */ +.infoBox { + background-color: #03a9f4; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + height: 80px; + cursor: default; + position: relative; + overflow: hidden; + margin-bottom: 30px; + border-radius: 3px; +} +@media (max-width: 767px){ + .infoBox{ + margin-bottom: 15px; + } +} +.infoBox .icon { + position: absolute; + right: 10px; + top: 10px; + text-align: center; + font-size: 60px; + line-height: 60px; + width: 75px; + text-align: center; + color: rgba(255, 255, 255, 0.3); +} +.infoBox .content { + display: inline-block; + padding: 7px 16px; +} +.infoBox .content .text { + font-size: 13px; + margin-top: 11px; + color: #fff; + text-transform: uppercase; +} +.infoBox .content .value { + font-size: 26px; + font-weight: 800; + margin-top: -4px; + color: #fff; + text-shadow: 1px 1px 1px #000; +} +.infoBox .content .value .smallText { + font-size: 16px; + font-weight: normal; +} +#marketInfos .infoBox { + height: 100px; +} +#marketInfos .infoBox .source { + font-size: 13px; + font-style: italic; + color: #fff; +} +#marketInfos .infoBox .source a, #marketInfos .infoBox .source a:hover { + color: #fff; +} + + +/* Hash info box */ +.hashInfo { + background-color: #014e71; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + cursor: default; + padding: 15px; + border-radius: 3px; + color: #fff; +} +.hashInfo .text { + font-size: 13px; + color: #fff; + text-transform: uppercase; +} +.hashInfo .content { + overflow: hidden; +} +.hashInfo .content .value { + float: left; + font-family: 'Inconsolata', monospace; + font-size: 16px; + font-weight: 800; + color: #fff; + padding-right: 8px; +} +.hashInfo .content .value a { + color: #fff; +} +.hashInfo .content .time { + float: left; + font-size: 15px; + font-weight: normal; +} + +/* Pool Statistics */ +.stats > div:not(#addressError) { + color: #262626; + padding: 10px 0px; +} +.stats > div:not(#addressError).marketFooter { + padding: 5px 0; +} +.stats > div:not(#addressError) > i.fa { + color: #0274a8; + font-size: 21px; + width: 30px; + text-align: center; +} +.stats > div:not(#addressError) > span:not(.input-group-btn):first-of-type { + font-weight: 500; + padding: 0 2px; + color: #0274a8; +} + +/* Pool and Worker Charts */ +.poolChart, .marketChart, .userChart, .userChartMerged { + display: none; +} +.poolChart .chart, .marketChart .chart { + padding: 10px; +} +.userChart .chart, .userChartMerged .chart { + padding: 10px 0; +} +.poolChart h4, +.marketChart h4, +.userChart h4, +.userChartMerged h4 { + text-align: center; + font-size: 21px; +} + +/* Market Rates */ +#marketStats { + margin-top: 20px; +} +#marketStats .infoBox { + margin-bottom: 15px; +} +#marketUpdate { + display: none; +} + +/* Mining Profit Calculator */ +#miningProfitCalc{ + margin: 35px 0; +} +#miningProfitCalc .input-group-addon{ + padding: 6px; +} +#miningProfitCalc .input-group-addon, +#miningProfitCalc .input-group-btn .btn, +#miningProfitCalc .input-group .form-control{ + height:45px; +} +#calcHashDropdown{ + border-radius: 0; + border-left: 0; + border-right: 0; +} +#calcHashHolder{ + width: 590px; + max-width: 100%; +} +#calcHashRate{ + z-index: inherit; + font-family: 'Inconsolata', monospace; +} +#calcHashAmount{ + font-family: 'Inconsolata', monospace; + display: inline-block; + text-align: center; + vertical-align: middle; + overflow: hidden; +} +#calcHashPeriod{ + font-family: 'Inconsolata', monospace; + display: inline-block; + width: 60px; + vertical-align: middle; +} +#calcHashResultsHolder{ + min-width: 170px; + max-width: 170px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +@media (min-width: 768px) { + #calcHashResultsHolder{ + min-width: 225px; + max-width: 225px; + } +} + +/* Mining Ports */ +#miningPorts tr > td { + vertical-align: middle; + font-size: 0.95em; +} +#miningPorts table .col1, +#miningPorts table .col2 { + text-align: center; +} +@media (min-width: 768px) { + #miningPorts table th { + white-space: nowrap; + } + #miningPorts table .col1 { + width:60px; + } + #miningPorts table .col2 { + width:160px; + text-align: center; + } +} + +/* Mining Apps */ +#miningApps .table > thead > tr > th { + white-space: nowrap; +} +#miningApps_rows > tr:first-child > td { + padding-top: 10px; +} +#miningApps_rows > tr > td { + border-top: none; +} +#miningApps_rows .appInfo td { + padding: 10px; + margin: 0; +} +#miningApps_rows .appConfig td { + padding: 0 10px; +} +#miningApps_rows tr:hover, +#miningApps_rows tr:hover > td, +#miningApps_rows tr:hover > th { + background-color: transparent; +} +#miningApps .btn { + width: 100%; +} +#miningApps .miningAppTitle { + font-weight: bold; +} +#miningApps .exampleAddress, +#miningApps .exampleWorkerName { + font-style: italic; +} + +/* Worker Statistics */ +#yourStatsInput, #yourStatsInputMerged { + z-index: inherit; + font-family: 'Inconsolata', monospace; +} +#yourAddressDisplay { + word-wrap: break-word; +} +#yourAddressDisplay > span { + font-family: 'Inconsolata', monospace; +} +#lookUp > span:nth-child(2), #lookUpMerged > span:nth-child(2) { + display: none; +} +.yourStats, +.yourWorkers, +.yourStatsMerged, +.yourWorkersMerged { + display: none; +} +.tab-pane #workerStats, .tab-pane #workerStatsMerged { + padding-top: 1em; +} + +#yourAddressDisplay { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.9em; +} +#addressError { + color: red; +} +#workersReport td { + vertical-align: middle; +} +#workersReport .status-ok{ + color: #17a600; +} +#workersReport .status-error{ + color: #a60000; +} +#workersReport table .col1, +#workersReport table .col3, +#workersReport table .col4, +#workersReport table .col5 { + text-align: center; +} +@media (min-width: 768px) { + #workersReport table th { + white-space: nowrap; + } + #workersReport table .col1 { + width:80px; + } + #workersReport table .col3 { + width:150px; + } + #workersReport table .col4 { + width: 210px; + } + #workersReport table .col5 { + width: 210px; + } +} +#workerPayments td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} +#workerPayments table .col3, +#workerPayments table .col4 { + text-align: center; +} +#workerPayments table .summary { + font-weight: 700; +} +@media (min-width: 768px) { + #workerPayments table { + table-layout:fixed; + } + #workerPayments table th { + white-space: nowrap; + } + #workerPayments table .col1 { + width:190px; + } + #workerPayments table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + #workerPayments table .col3 { + width:130px; + } + #workerPayments table .col4 { + width: 70px; + } +} + +/* Pool Blocks */ +.blocksStats { + margin-top: 20px; +} +.blocksStats .infoBox { + margin-bottom: 15px; +} +.blocksStats .luckGood { + color: #5eff5e; +} +.blocksStats .luckBad { + color: red; +} +.blocksStats .luckMid { + color: #FFF500; +} +.blocksReport tr > td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} +.blocksReport table .col2, +.blocksReport table .col3, +.blocksReport table .col4, +.blocksReport table .col6, +.blocksReport table .col7 { + text-align: center; +} +@media (min-width: 768px) { + .blocksReport table { + table-layout:fixed; + } + .blocksReport table th { + white-space: nowrap; + } + .blocksReport table .col1 { + width:190px; + } + .blocksReport table .col2 { + width:80px; + } + .blocksReport table .col3 { + width:90px; + } + .blocksReport table .col4 { + width:120px; + } + .blocksReport table .col5 { + overflow: hidden; + text-overflow: ellipsis; + } + .blocksReport table .col6 { + width: 55px; + } + .blocksReport table .col7 { + width: 80px; + } +} +.blocksReport .pending td{ + background-color:rgba(255, 255, 255, .3); +} +.blocksReport .unlocked td{ + background-color:rgba(0, 255, 0, .1); +} +.blocksReport .orphaned td{ + background-color:rgba(255, 0, 0, .1); +} +.blocksReport .luckGood, +.blocksReport .unlocked .col7 { + color: #17a600; +} +.blocksReport .luckBad, +.blocksReport .orphaned .col7 { + color: #a60000; +} +.blocksReport .luckMid { + color: #df9d00; +} + +/* Payments */ +.paymentsStats { + margin-top: 20px; +} +.paymentsStats .infoBox { + margin-bottom: 15px; +} +#paymentsReport td,#paymentsReportMerged td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} +#paymentsReport table .col3, +#paymentsReport table .col4, +#paymentsReport table .col5, +#paymentsReport table .col6, +#paymentsReportMerged table .col3, +#paymentsReportMerged table .col4, +#paymentsReportMerged table .col5, +#paymentsReportMerged table .col6 { + text-align: center; +} +@media (min-width: 768px) { + #paymentsReport table { + table-layout:fixed; + } + #paymentsReport table th { + white-space: nowrap; + } + #paymentsReport table .col1 { + width:190px; + } + #paymentsReport table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + #paymentsReport table .col3 { + width:130px; + } + #paymentsReport table .col4 { + width:110px; + } + #paymentsReport table .col5 { + width: 70px; + } + #paymentsReport table .col6 { + width: 70px; + } + + #paymentsReportMerged table { + table-layout:fixed; + } + #paymentsReportMerged table th { + white-space: nowrap; + } + #paymentsReportMerged table .col1 { + width:190px; + } + #paymentsReportMerged table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + #paymentsReportMerged table .col3 { + width:130px; + } + #paymentsReportMerged table .col4 { + width:110px; + } + #paymentsReportMerged table .col5 { + width: 70px; + } + #paymentsReportMerged table .col6 { + width: 70px; + } +} + +/* Top 10 miners */ +#top10miners td { + vertical-align: middle; +} +#top10miners table .col1, +#top10miners table .col3, +#top10miners table .col4, +#top10miners table .col5 { + text-align: center; +} +@media (min-width: 768px) { + #top10miners table th { + white-space: nowrap; + } + #top10miners table .col1 { + width:80px; + } + #top10miners table .col3 { + width:150px; + } + #top10miners table .col4 { + width: 210px; + } + #top10miners table .col5 { + width: 210px; + } +} + +/* Language selector */ +#langSelector { + float: right; + padding-right: 0 !important; +} + +#langSelector select { + display: inline-block; + width: auto; + height: 32px; + padding: 6px 10px; +} + +#mLangSelector { + display: none; + padding: 10px; +} +@media (max-width: 767px) { + #mLangSelector { + display: block; + } +} + +.dropdown-menu { + background-color: #014e71; +} diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.af.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.af.js new file mode 100644 index 000000000..817a7fa59 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.af.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Afrikaans + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "gelede", + suffixFromNow: "van nou af", + seconds: "%d sekondes", + minute: "1 minuut", + minutes: "%d minute", + hour: "1 uur", + hours: "%d ure", + day: "1 dag", + days: "%d dae", + month: "1 maand", + months: "%d maande", + year: "1 jaar", + years: "%d jaar", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.am.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.am.js new file mode 100644 index 000000000..65502c39d --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.am.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Amharic + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "በፊት", + suffixFromNow: "በኋላ", + seconds: "ከአንድ ደቂቃ በታች", + minute: "ከአንድ ደቂቃ ገደማ", + minutes: "ከ%d ደቂቃ", + hour: "ከአንድ ሰዓት ገደማ", + hours: "ከ%d ሰዓት ገደማ", + day: "ከአንድ ቀን", + days: "ከ%d ቀን", + month: "ከአንድ ወር ገደማ", + months: "ከ%d ወር", + year: "ከአንድ ዓመት ገደማ", + years: "ከ%d ዓመት", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.ar.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.ar.js new file mode 100644 index 000000000..14cd18f23 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.ar.js @@ -0,0 +1,104 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + function numpf(n, a) { + return a[plural=n===0 ? 0 : n===1 ? 1 : n===2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: "منذ", + prefixFromNow: "بعد", + suffixAgo: null, + suffixFromNow: null, // null OR "من الآن" + second: function(value) { return numpf(value, [ + 'أقل من ثانية', + 'ثانية واحدة', + 'ثانيتين', + '%d ثوانٍ', + '%d ثانية', + '%d ثانية']); }, + seconds: function(value) { return numpf(value, [ + 'أقل من ثانية', + 'ثانية واحدة', + 'ثانيتين', + '%d ثوانٍ', + '%d ثانية', + '%d ثانية']); }, + minute: function(value) { return numpf(value, [ + 'أقل من دقيقة', + 'دقيقة واحدة', + 'دقيقتين', + '%d دقائق', + '%d دقيقة', + 'دقيقة']); }, + minutes: function(value) { return numpf(value, [ + 'أقل من دقيقة', + 'دقيقة واحدة', + 'دقيقتين', + '%d دقائق', + '%d دقيقة', + 'دقيقة']); }, + hour: function(value) { return numpf(value, [ + 'أقل من ساعة', + 'ساعة واحدة', + 'ساعتين', + '%d ساعات', + '%d ساعة', + '%d ساعة']); }, + hours: function(value) { return numpf(value, [ + 'أقل من ساعة', + 'ساعة واحدة', + 'ساعتين', + '%d ساعات', + '%d ساعة', + '%d ساعة']); }, + day: function(value) { return numpf(value, [ + 'أقل من يوم', + 'يوم واحد', + 'يومين', + '%d أيام', + '%d يومًا', + '%d يوم']); }, + days: function(value) { return numpf(value, [ + 'أقل من يوم', + 'يوم واحد', + 'يومين', + '%d أيام', + '%d يومًا', + '%d يوم']); }, + month: function(value) { return numpf(value, [ + 'أقل من شهر', + 'شهر واحد', + 'شهرين', + '%d أشهر', + '%d شهرًا', + '%d شهر']); }, + months: function(value) { return numpf(value, [ + 'أقل من شهر', + 'شهر واحد', + 'شهرين', + '%d أشهر', + '%d شهرًا', + '%d شهر']); }, + year: function(value) { return numpf(value, [ + 'أقل من عام', + 'عام واحد', + '%d عامين', + '%d أعوام', + '%d عامًا']); + }, + years: function(value) { return numpf(value, [ + 'أقل من عام', + 'عام واحد', + 'عامين', + '%d أعوام', + '%d عامًا', + '%d عام']);} + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.az.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.az.js new file mode 100644 index 000000000..8332c41cf --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.az.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Azerbaijani + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: 'əvvəl', + suffixFromNow: 'sonra', + seconds: 'saniyələr', + minute: '1 dəqiqə', + minutes: '%d dəqiqə', + hour: '1 saat', + hours: '%d saat', + day: '1 gün', + days: '%d gün', + month: '1 ay', + months: '%d ay', + year: '1 il', + years: '%d il', + wordSeparator: '', + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.bg.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.bg.js new file mode 100644 index 000000000..a3bd343af --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.bg.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Bulgarian + jQuery.timeago.settings.strings = { + prefixAgo: "преди", + prefixFromNow: "след", + suffixAgo: null, + suffixFromNow: null, + seconds: "по-малко от минута", + minute: "една минута", + minutes: "%d минути", + hour: "един час", + hours: "%d часа", + day: "един ден", + days: "%d дни", + month: "един месец", + months: "%d месеца", + year: "една година", + years: "%d години" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.bs.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.bs.js new file mode 100644 index 000000000..cbb178069 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.bs.js @@ -0,0 +1,55 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Bosnian + var numpf = function(n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "prije", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekund", + seconds: function(value) { + return numpf(value, "%d sekund", "%d sekunde", "%d sekundi"); + }, + minute: "oko minut", + minutes: function(value) { + return numpf(value, "%d minut", "%d minute", "%d minuta"); + }, + hour: "oko sat", + hours: function(value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "oko jednog dana", + days: function(value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mjesec dana", + months: function(value) { + return numpf(value, "%d mjesec", "%d mjeseca", "%d mjeseci"); + }, + year: "prije godinu dana ", + years: function(value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; + +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.ca.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.ca.js new file mode 100644 index 000000000..e4cb5cab7 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.ca.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Catalan + jQuery.timeago.settings.strings = { + prefixAgo: "fa", + prefixFromNow: "d'aquí", + suffixAgo: null, + suffixFromNow: null, + seconds: "menys d'un minut", + minute: "un minut", + minutes: "%d minuts", + hour: "una hora", + hours: "%d hores", + day: "un dia", + days: "%d dies", + month: "un mes", + months: "%d mesos", + year: "un any", + years: "%d anys", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.cs.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.cs.js new file mode 100644 index 000000000..b940f6949 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.cs.js @@ -0,0 +1,34 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Czech + (function() { + function f(n, d, a) { + return a[d>=0 ? 0 : a.length===2 || n<5 ? 1 : 2]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: 'před', + prefixFromNow: 'za', + suffixAgo: null, + suffixFromNow: null, + seconds: function(n, d) {return f(n, d, ['méně než minutou', 'méně než minutu']);}, + minute: function(n, d) {return f(n, d, ['minutou', 'minutu']);}, + minutes: function(n, d) {return f(n, d, ['%d minutami', '%d minuty', '%d minut']);}, + hour: function(n, d) {return f(n, d, ['hodinou', 'hodinu']);}, + hours: function(n, d) {return f(n, d, ['%d hodinami', '%d hodiny', '%d hodin']);}, + day: function(n, d) {return f(n, d, ['%d dnem', '%d den']);}, + days: function(n, d) {return f(n, d, ['%d dny', '%d dny', '%d dní']);}, + month: function(n, d) {return f(n, d, ['%d měsícem', '%d měsíc']);}, + months: function(n, d) {return f(n, d, ['%d měsíci', '%d měsíce', '%d měsíců']);}, + year: function(n, d) {return f(n, d, ['%d rokem', '%d rok']);}, + years: function(n, d) {return f(n, d, ['%d lety', '%d roky', '%d let']);} + }; + })(); +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.cy.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.cy.js new file mode 100644 index 000000000..4c514a8df --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.cy.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Welsh + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "yn ôl", + suffixFromNow: "o hyn", + seconds: "llai na munud", + minute: "am funud", + minutes: "%d munud", + hour: "tua awr", + hours: "am %d awr", + day: "y dydd", + days: "%d diwrnod", + month: "tua mis", + months: "%d mis", + year: "am y flwyddyn", + years: "%d blynedd", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.da.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.da.js new file mode 100644 index 000000000..236c34c44 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.da.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Danish + jQuery.timeago.settings.strings = { + prefixAgo: "for", + prefixFromNow: "om", + suffixAgo: "siden", + suffixFromNow: "", + seconds: "mindre end et minut", + minute: "ca. et minut", + minutes: "%d minutter", + hour: "ca. en time", + hours: "ca. %d timer", + day: "en dag", + days: "%d dage", + month: "ca. en måned", + months: "%d måneder", + year: "ca. et år", + years: "%d år" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.de.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.de.js new file mode 100644 index 000000000..6a877a231 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.de.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // German + jQuery.timeago.settings.strings = { + prefixAgo: "vor", + prefixFromNow: "in", + suffixAgo: "", + suffixFromNow: "", + seconds: "wenigen Sekunden", + minute: "etwa einer Minute", + minutes: "%d Minuten", + hour: "etwa einer Stunde", + hours: "%d Stunden", + day: "etwa einem Tag", + days: "%d Tagen", + month: "etwa einem Monat", + months: "%d Monaten", + year: "etwa einem Jahr", + years: "%d Jahren" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.dv.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.dv.js new file mode 100644 index 000000000..0d70a493c --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.dv.js @@ -0,0 +1,32 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + /** + * Dhivehi time in Thaana for timeago.js + **/ + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ކުރިން", + suffixFromNow: "ފަހުން", + seconds: "ސިކުންތުކޮޅެއް", + minute: "މިނިޓެއްވަރު", + minutes: "%d މިނިޓު", + hour: "ގަޑިއެއްވަރު", + hours: "ގާތްގަނޑަކަށް %d ގަޑިއިރު", + day: "އެއް ދުވަސް", + days: "މީގެ %d ދުވަސް", + month: "މަހެއްވަރު", + months: "މީގެ %d މަސް", + year: "އަހަރެއްވަރު", + years: "މީގެ %d އަހަރު", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.el.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.el.js new file mode 100644 index 000000000..2db9ebea8 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.el.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Greek + jQuery.timeago.settings.strings = { + prefixAgo: "πριν", + prefixFromNow: "σε", + suffixAgo: "", + suffixFromNow: "", + seconds: "λιγότερο από ένα λεπτό", + minute: "περίπου ένα λεπτό", + minutes: "%d λεπτά", + hour: "περίπου μία ώρα", + hours: "περίπου %d ώρες", + day: "μία μέρα", + days: "%d μέρες", + month: "περίπου ένα μήνα", + months: "%d μήνες", + year: "περίπου ένα χρόνο", + years: "%d χρόνια" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.en.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.en.js new file mode 100644 index 000000000..8ca50afff --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.en.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // English (Template) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ago", + suffixFromNow: "from now", + seconds: "less than a minute", + minute: "about a minute", + minutes: "%d minutes", + hour: "about an hour", + hours: "about %d hours", + day: "a day", + days: "%d days", + month: "about a month", + months: "%d months", + year: "about a year", + years: "%d years", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.es.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.es.js new file mode 100644 index 000000000..0785b3f42 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.es.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Spanish + jQuery.timeago.settings.strings = { + prefixAgo: "hace", + prefixFromNow: "dentro de", + suffixAgo: "", + suffixFromNow: "", + seconds: "menos de un minuto", + minute: "un minuto", + minutes: "unos %d minutos", + hour: "una hora", + hours: "%d horas", + day: "un día", + days: "%d días", + month: "un mes", + months: "%d meses", + year: "un año", + years: "%d años" + }; +})); + diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.et.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.et.js new file mode 100644 index 000000000..ac2461ec7 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.et.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Estonian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "tagasi", + suffixFromNow: "pärast", + seconds: function(n, d) { return d < 0 ? "vähem kui minuti aja" : "vähem kui minut aega"; }, + minute: function(n, d) { return d < 0 ? "umbes minuti aja" : "umbes minut aega"; }, + minutes: function(n, d) { return d < 0 ? "%d minuti" : "%d minutit"; }, + hour: function(n, d) { return d < 0 ? "umbes tunni aja" : "umbes tund aega"; }, + hours: function(n, d) { return d < 0 ? "%d tunni" : "%d tundi"; }, + day: function(n, d) { return d < 0 ? "umbes päeva" : "umbes päev"; }, + days: "%d päeva", + month: function(n, d) { return d < 0 ? "umbes kuu aja" : "umbes kuu aega"; }, + months: function(n, d) { return d < 0 ? "%d kuu" : "%d kuud"; }, + year: function(n, d) { return d < 0 ? "umbes aasta aja" : "umbes aasta aega"; }, + years: function(n, d) { return d < 0 ? "%d aasta" : "%d aastat"; } + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.eu.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.eu.js new file mode 100644 index 000000000..5c2c32c7e --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.eu.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + jQuery.timeago.settings.strings = { + prefixAgo: "duela", + prefixFromNow: "hemendik", + suffixAgo: "", + suffixFromNow: "barru", + seconds: "minutu bat bainu gutxiago", + minute: "minutu bat", + minutes: "%d minutu inguru", + hour: "ordu bat", + hours: "%d ordu", + day: "egun bat", + days: "%d egun", + month: "hilabete bat", + months: "%d hilabete", + year: "urte bat", + years: "%d urte" + }; +})); + diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.fa.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.fa.js new file mode 100644 index 000000000..ec8ccb952 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.fa.js @@ -0,0 +1,32 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Persian + // Use DIR attribute for RTL text in Persian Language for ABBR tag . + // By MB.seifollahi@gmail.com + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "پیش", + suffixFromNow: "از حال", + seconds: "کمتر از یک دقیقه", + minute: "حدود یک دقیقه", + minutes: "%d دقیقه", + hour: "حدود یک ساعت", + hours: "حدود %d ساعت", + day: "یک روز", + days: "%d روز", + month: "حدود یک ماه", + months: "%d ماه", + year: "حدود یک سال", + years: "%d سال", + wordSeparator: " ", + numbers: ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.fi.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.fi.js new file mode 100644 index 000000000..b5f7e696d --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.fi.js @@ -0,0 +1,38 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Finnish + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "sitten", + suffixFromNow: "tulevaisuudessa", + seconds: "alle minuutti", + minute: "minuutti", + minutes: "%d minuuttia", + hour: "tunti", + hours: "%d tuntia", + day: "päivä", + days: "%d päivää", + month: "kuukausi", + months: "%d kuukautta", + year: "vuosi", + years: "%d vuotta" + }; + + // The above is not a great localization because one would usually + // write "2 days ago" in Finnish as "2 päivää sitten", however + // one would write "2 days into the future" as "2:n päivän päästä" + // which cannot be achieved with localization support this simple. + // This is because Finnish has word suffixes (attached directly + // to the end of the word). The word "day" is "päivä" in Finnish. + // As workaround, the above localizations will say + // "2 päivää tulevaisuudessa" which is understandable but + // not as fluent. +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.fr.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.fr.js new file mode 100644 index 000000000..1bb052aa1 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.fr.js @@ -0,0 +1,27 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // French + jQuery.timeago.settings.strings = { + // environ ~= about, it's optional + prefixAgo: "il y a", + prefixFromNow: "d'ici", + seconds: "moins d'une minute", + minute: "environ une minute", + minutes: "environ %d minutes", + hour: "environ une heure", + hours: "environ %d heures", + day: "environ un jour", + days: "environ %d jours", + month: "environ un mois", + months: "environ %d mois", + year: "un an", + years: "%d ans" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.gl.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.gl.js new file mode 100644 index 000000000..277bbf70f --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.gl.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Galician + jQuery.timeago.settings.strings = { + prefixAgo: "hai", + prefixFromNow: "dentro de", + suffixAgo: "", + suffixFromNow: "", + seconds: "menos dun minuto", + minute: "un minuto", + minutes: "uns %d minutos", + hour: "unha hora", + hours: "%d horas", + day: "un día", + days: "%d días", + month: "un mes", + months: "%d meses", + year: "un ano", + years: "%d anos" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.he.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.he.js new file mode 100644 index 000000000..2cd31ab60 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.he.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Hebrew + jQuery.timeago.settings.strings = { + prefixAgo: "לפני", + prefixFromNow: "עוד", + seconds: "פחות מדקה", + minute: "דקה", + minutes: "%d דקות", + hour: "שעה", + hours: function(number){return (number===2) ? "שעתיים" : "%d שעות";}, + day: "יום", + days: function(number){return (number===2) ? "יומיים" : "%d ימים";}, + month: "חודש", + months: function(number){return (number===2) ? "חודשיים" : "%d חודשים";}, + year: "שנה", + years: function(number){return (number===2) ? "שנתיים" : "%d שנים";} + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.hr.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.hr.js new file mode 100644 index 000000000..bd142979a --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.hr.js @@ -0,0 +1,54 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Croatian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "prije", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekundu", + seconds: function (value) { + return numpf(value, "%d sekundu", "%d sekunde", "%d sekundi"); + }, + minute: "oko minutu", + minutes: function (value) { + return numpf(value, "%d minutu", "%d minute", "%d minuta"); + }, + hour: "oko jedan sat", + hours: function (value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "jedan dan", + days: function (value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mjesec dana", + months: function (value) { + return numpf(value, "%d mjesec", "%d mjeseca", "%d mjeseci"); + }, + year: "prije godinu dana", + years: function (value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.hu.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.hu.js new file mode 100644 index 000000000..0009de9e2 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.hu.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Hungarian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: null, + seconds: "kevesebb mint egy perce", + minute: "körülbelül egy perce", + minutes: "%d perce", + hour: "körülbelül egy órája", + hours: "körülbelül %d órája", + day: "körülbelül egy napja", + days: "%d napja", + month: "körülbelül egy hónapja", + months: "%d hónapja", + year: "körülbelül egy éve", + years: "%d éve" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.hy.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.hy.js new file mode 100644 index 000000000..3f0de6e7d --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.hy.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Armenian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "առաջ", + suffixFromNow: "հետո", + seconds: "վայրկյաններ", + minute: "մեկ րոպե", + minutes: "%d րոպե", + hour: "մեկ ժամ", + hours: "%d ժամ", + day: "մեկ օր", + days: "%d օր", + month: "մեկ ամիս", + months: "%d ամիս", + year: "մեկ տարի", + years: "%d տարի" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.id.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.id.js new file mode 100644 index 000000000..ca530ccf8 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.id.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Indonesian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "yang lalu", + suffixFromNow: "dari sekarang", + seconds: "kurang dari semenit", + minute: "sekitar satu menit", + minutes: "%d menit", + hour: "sekitar sejam", + hours: "sekitar %d jam", + day: "sehari", + days: "%d hari", + month: "sekitar sebulan", + months: "%d bulan", + year: "sekitar setahun", + years: "%d tahun" + }; +})); + diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.is.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.is.js new file mode 100644 index 000000000..e3d4b1fd1 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.is.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + jQuery.timeago.settings.strings = { + prefixAgo: "fyrir", + prefixFromNow: "eftir", + suffixAgo: "síðan", + suffixFromNow: null, + seconds: "minna en mínútu", + minute: "mínútu", + minutes: "%d mínútum", + hour: "klukkutíma", + hours: "um %d klukkutímum", + day: "degi", + days: "%d dögum", + month: "mánuði", + months: "%d mánuðum", + year: "ári", + years: "%d árum", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.it.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.it.js new file mode 100644 index 000000000..e1cac8431 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.it.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Italian + jQuery.timeago.settings.strings = { + suffixAgo: "fa", + suffixFromNow: "da ora", + seconds: "meno di un minuto", + minute: "circa un minuto", + minutes: "%d minuti", + hour: "circa un'ora", + hours: "circa %d ore", + day: "un giorno", + days: "%d giorni", + month: "circa un mese", + months: "%d mesi", + year: "circa un anno", + years: "%d anni" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.ja.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.ja.js new file mode 100644 index 000000000..30f3308c3 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.ja.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Japanese + jQuery.timeago.settings.strings = { + prefixAgo: "", + prefixFromNow: "今から", + suffixAgo: "前", + suffixFromNow: "後", + seconds: "1 分未満", + minute: "約 1 分", + minutes: "%d 分", + hour: "約 1 時間", + hours: "約 %d 時間", + day: "約 1 日", + days: "約 %d 日", + month: "約 1 ヶ月", + months: "約 %d ヶ月", + year: "約 1 年", + years: "約 %d 年", + wordSeparator: "" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.jv.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.jv.js new file mode 100644 index 000000000..0344053d7 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.jv.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Javanesse (Boso Jowo) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "kepungkur", + suffixFromNow: "seko saiki", + seconds: "kurang seko sakmenit", + minute: "kurang luwih sakmenit", + minutes: "%d menit", + hour: "kurang luwih sakjam", + hours: "kurang luwih %d jam", + day: "sedina", + days: "%d dina", + month: "kurang luwih sewulan", + months: "%d wulan", + year: "kurang luwih setahun", + years: "%d tahun" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.ko.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.ko.js new file mode 100644 index 000000000..23d1d3780 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.ko.js @@ -0,0 +1,31 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Korean + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "전", + suffixFromNow: "후", + seconds: "1분", + minute: "약 1분", + minutes: "%d분", + hour: "약 1시간", + hours: "약 %d시간", + day: "하루", + days: "%d일", + month: "약 1개월", + months: "%d개월", + year: "약 1년", + years: "%d년", + wordSeparator: " ", + numbers: [] + }; +})); + diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.ky.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.ky.js new file mode 100644 index 000000000..58dba293a --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.ky.js @@ -0,0 +1,42 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Russian + function numpf(n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + var n10 = n % 10; + if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { + return f; + } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { + return s; + } else { + return t; + } + } + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "мурун", + suffixFromNow: null, + seconds: "1 минуттан аз", + minute: "минута", + minutes: function(value) { return numpf(value, "%d минута", "%d минута", "%d минут"); }, + hour: "саат", + hours: function(value) { return numpf(value, "%d саат", "%d саат", "%d саат"); }, + day: "күн", + days: function(value) { return numpf(value, "%d күн", "%d күн", "%d күн"); }, + month: "ай", + months: function(value) { return numpf(value, "%d ай", "%d ай", "%d ай"); }, + year: "жыл", + years: function(value) { return numpf(value, "%d жыл", "%d жыл", "%d жыл"); } + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.lt.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.lt.js new file mode 100644 index 000000000..2079fccd3 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.lt.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + //Lithuanian + jQuery.timeago.settings.strings = { + prefixAgo: "prieš", + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "nuo dabar", + seconds: "%d sek.", + minute: "min.", + minutes: "%d min.", + hour: "val.", + hours: "%d val.", + day: "1 d.", + days: "%d d.", + month: "mėn.", + months: "%d mėn.", + year: "metus", + years: "%d metus", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.lv.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.lv.js new file mode 100644 index 000000000..855d1a4dd --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.lv.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + //Latvian + jQuery.timeago.settings.strings = { + prefixAgo: "pirms", + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "no šī brīža", + seconds: "%d sek.", + minute: "min.", + minutes: "%d min.", + hour: "st.", + hours: "%d st.", + day: "1 d.", + days: "%d d.", + month: "mēnesis.", + months: "%d mēnesis.", + year: "gads", + years: "%d gads", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.mk.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.mk.js new file mode 100644 index 000000000..301a5da83 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.mk.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Macedonian + (function() { + jQuery.timeago.settings.strings={ + prefixAgo: "пред", + prefixFromNow: "за", + suffixAgo: null, + suffixFromNow: null, + seconds: "%d секунди", + minute: "%d минута", + minutes: "%d минути", + hour: "%d час", + hours: "%d часа", + day: "%d ден", + days: "%d денови" , + month: "%d месец", + months: "%d месеци", + year: "%d година", + years: "%d години" + }; + })(); +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.nl.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.nl.js new file mode 100644 index 000000000..2c5de89c0 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.nl.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Dutch + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "over", + suffixAgo: "geleden", + suffixFromNow: null, + seconds: "minder dan een minuut", + minute: "ongeveer een minuut", + minutes: "%d minuten", + hour: "ongeveer een uur", + hours: "ongeveer %d uur", + day: "een dag", + days: "%d dagen", + month: "ongeveer een maand", + months: "%d maanden", + year: "ongeveer een jaar", + years: "%d jaar", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.no.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.no.js new file mode 100644 index 000000000..c896337c7 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.no.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Norwegian + jQuery.timeago.settings.strings = { + prefixAgo: "for", + prefixFromNow: "om", + suffixAgo: "siden", + suffixFromNow: "", + seconds: "mindre enn et minutt", + minute: "ca. et minutt", + minutes: "%d minutter", + hour: "ca. en time", + hours: "ca. %d timer", + day: "en dag", + days: "%d dager", + month: "ca. en måned", + months: "%d måneder", + year: "ca. et år", + years: "%d år" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.pl.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.pl.js new file mode 100644 index 000000000..48427846a --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.pl.js @@ -0,0 +1,39 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Polish + function numpf(n, s, t) { + // s - 2-4, 22-24, 32-34 ... + // t - 5-21, 25-31, ... + var n10 = n % 10; + if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { + return s; + } else { + return t; + } + } + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "za", + suffixAgo: "temu", + suffixFromNow: null, + seconds: "mniej niż minutę", + minute: "minutę", + minutes: function(value) { return numpf(value, "%d minuty", "%d minut"); }, + hour: "godzinę", + hours: function(value) { return numpf(value, "%d godziny", "%d godzin"); }, + day: "dzień", + days: "%d dni", + month: "miesiąc", + months: function(value) { return numpf(value, "%d miesiące", "%d miesięcy"); }, + year: "rok", + years: function(value) { return numpf(value, "%d lata", "%d lat"); } + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.pt-br.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.pt-br.js new file mode 100644 index 000000000..a8701a880 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.pt-br.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Brazilian Portuguese + jQuery.timeago.settings.strings = { + prefixAgo: "há", + prefixFromNow: "em", + suffixAgo: null, + suffixFromNow: null, + seconds: "alguns segundos", + minute: "um minuto", + minutes: "%d minutos", + hour: "uma hora", + hours: "%d horas", + day: "um dia", + days: "%d dias", + month: "um mês", + months: "%d meses", + year: "um ano", + years: "%d anos" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.pt.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.pt.js new file mode 100644 index 000000000..13791a037 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.pt.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Portuguese + jQuery.timeago.settings.strings = { + suffixAgo: "atrás", + suffixFromNow: "a partir de agora", + seconds: "menos de um minuto", + minute: "cerca de um minuto", + minutes: "%d minutos", + hour: "cerca de uma hora", + hours: "cerca de %d horas", + day: "um dia", + days: "%d dias", + month: "cerca de um mês", + months: "%d meses", + year: "cerca de um ano", + years: "%d anos" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.ro.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.ro.js new file mode 100644 index 000000000..fe59db900 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.ro.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Romanian + jQuery.timeago.settings.strings = { + prefixAgo: "acum", + prefixFromNow: "in timp de", + suffixAgo: "", + suffixFromNow: "", + seconds: "mai putin de un minut", + minute: "un minut", + minutes: "%d minute", + hour: "o ora", + hours: "%d ore", + day: "o zi", + days: "%d zile", + month: "o luna", + months: "%d luni", + year: "un an", + years: "%d ani" + }; +})); + diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.rs.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.rs.js new file mode 100644 index 000000000..b9e518825 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.rs.js @@ -0,0 +1,54 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Serbian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "pre", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekund", + seconds: function (value) { + return numpf(value, "%d sekund", "%d sekunde", "%d sekundi"); + }, + minute: "oko minut", + minutes: function (value) { + return numpf(value, "%d minut", "%d minuta", "%d minuta"); + }, + hour: "oko jedan sat", + hours: function (value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "jedan dan", + days: function (value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mesec dana", + months: function (value) { + return numpf(value, "%d mesec", "%d meseca", "%d meseci"); + }, + year: "godinu dana", + years: function (value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.ru.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.ru.js new file mode 100644 index 000000000..4ff3f8d30 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.ru.js @@ -0,0 +1,43 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Russian + function numpf(n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + n = n % 100; + var n10 = n % 10; + if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { + return f; + } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { + return s; + } else { + return t; + } + } + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "назад", + suffixFromNow: null, + seconds: "меньше минуты", + minute: "минуту", + minutes: function(value) { return numpf(value, "%d минуту", "%d минуты", "%d минут"); }, + hour: "час", + hours: function(value) { return numpf(value, "%d час", "%d часа", "%d часов"); }, + day: "день", + days: function(value) { return numpf(value, "%d день", "%d дня", "%d дней"); }, + month: "месяц", + months: function(value) { return numpf(value, "%d месяц", "%d месяца", "%d месяцев"); }, + year: "год", + years: function(value) { return numpf(value, "%d год", "%d года", "%d лет"); } + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.rw.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.rw.js new file mode 100644 index 000000000..50119e1e8 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.rw.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Kinyarwanda + jQuery.timeago.settings.strings = { + prefixAgo: "hashize", + prefixFromNow: "mu", + suffixAgo: null, + suffixFromNow: null, + seconds: "amasegonda macye", + minute: "umunota", + minutes: "iminota %d", + hour: "isaha", + hours: "amasaha %d", + day: "umunsi", + days: "iminsi %d", + month: "ukwezi", + months: "amezi %d", + year: "umwaka", + years: "imyaka %d", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.si.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.si.js new file mode 100644 index 000000000..6fa215e9a --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.si.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Sinhalese (SI) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "පෙර", + suffixFromNow: "පසුව", + seconds: "තත්පර කිහිපයකට", + minute: "මිනිත්තුවකට පමණ", + minutes: "මිනිත්තු %d කට", + hour: "පැයක් පමණ ", + hours: "පැය %d කට පමණ", + day: "දවසක ට", + days: "දවස් %d කට ", + month: "මාසයක් පමණ", + months: "මාස %d කට", + year: "වසරක් පමණ", + years: "වසරක් %d කට පමණ" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.sk.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.sk.js new file mode 100644 index 000000000..e28ab7c9b --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.sk.js @@ -0,0 +1,34 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Slovak + (function() { + function f(n, d, a) { + return a[d>=0 ? 0 : a.length===2 || n<5 ? 1 : 2]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: 'pred', + prefixFromNow: 'o', + suffixAgo: null, + suffixFromNow: null, + seconds: function(n, d) {return f(n, d, ['menej ako minútou', 'menej ako minútu']);}, + minute: function(n, d) {return f(n, d, ['minútou', 'minútu']);}, + minutes: function(n, d) {return f(n, d, ['%d minútami', '%d minúty', '%d minút']);}, + hour: function(n, d) {return f(n, d, ['hodinou', 'hodinu']);}, + hours: function(n, d) {return f(n, d, ['%d hodinami', '%d hodiny', '%d hodín']);}, + day: function(n, d) {return f(n, d, ['%d dňom', '%d deň']);}, + days: function(n, d) {return f(n, d, ['%d dňami', '%d dni', '%d dní']);}, + month: function(n, d) {return f(n, d, ['%d mesiacom', '%d mesiac']);}, + months: function(n, d) {return f(n, d, ['%d mesiacmi', '%d mesiace', '%d mesiacov']);}, + year: function(n, d) {return f(n, d, ['%d rokom', '%d rok']);}, + years: function(n, d) {return f(n, d, ['%d rokmi', '%d roky', '%d rokov']);} + }; + })(); +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.sl.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.sl.js new file mode 100644 index 000000000..9f0329ac6 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.sl.js @@ -0,0 +1,46 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Slovenian with support for dual + var numpf = function (n, a) { + return a[n%100===1 ? 1 : n%100===2 ? 2 : n%100===3 || n%100===4 ? 3 : 0]; + }; + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "čez", + suffixAgo: "nazaj", + suffixFromNow: null, + second: "sekundo", + seconds: function (value) { + return numpf(value, ["%d sekund", "%d sekundo", "%d sekundi", "%d sekunde"]); + }, + minute: "minuto", + minutes: function (value) { + return numpf(value, ["%d minut", "%d minuto", "%d minuti", "%d minute"]); + }, + hour: "eno uro", + hours: function (value) { + return numpf(value, ["%d ur", "%d uro", "%d uri", "%d ure"]); + }, + day: "en dan", + days: function (value) { + return numpf(value, ["%d dni", "%d dan", "%d dneva", "%d dni"]); + }, + month: "en mesec", + months: function (value) { + return numpf(value, ["%d mesecev", "%d mesec", "%d meseca", "%d mesece"]); + }, + year: "eno leto", + years: function (value) { + return numpf(value, ["%d let", "%d leto", "%d leti", "%d leta"]); + }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.sq.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.sq.js new file mode 100644 index 000000000..cb8ae703a --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.sq.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Albanian SQ + jQuery.timeago.settings.strings = { + suffixAgo: "më parë", + suffixFromNow: "tani", + seconds: "më pak se një minutë", + minute: "rreth një minutë", + minutes: "%d minuta", + hour: "rreth një orë", + hours: "rreth %d orë", + day: "një ditë", + days: "%d ditë", + month: "rreth një muaj", + months: "%d muaj", + year: "rreth një vit", + years: "%d vjet" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.sr.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.sr.js new file mode 100644 index 000000000..bd1efe79a --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.sr.js @@ -0,0 +1,54 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Serbian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "пре", + prefixFromNow: "за", + suffixAgo: null, + suffixFromNow: null, + second: "секунд", + seconds: function (value) { + return numpf(value, "%d секунд", "%d секунде", "%d секунди"); + }, + minute: "један минут", + minutes: function (value) { + return numpf(value, "%d минут", "%d минута", "%d минута"); + }, + hour: "један сат", + hours: function (value) { + return numpf(value, "%d сат", "%d сата", "%d сати"); + }, + day: "један дан", + days: function (value) { + return numpf(value, "%d дан", "%d дана", "%d дана"); + }, + month: "месец дана", + months: function (value) { + return numpf(value, "%d месец", "%d месеца", "%d месеци"); + }, + year: "годину дана", + years: function (value) { + return numpf(value, "%d годину", "%d године", "%d година"); + }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.sv.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.sv.js new file mode 100644 index 000000000..caf09dbb0 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.sv.js @@ -0,0 +1,28 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Swedish + jQuery.timeago.settings.strings = { + prefixAgo: "för", + prefixFromNow: "om", + suffixAgo: "sedan", + suffixFromNow: "", + seconds: "mindre än en minut", + minute: "ungefär en minut", + minutes: "%d minuter", + hour: "ungefär en timme", + hours: "ungefär %d timmar", + day: "en dag", + days: "%d dagar", + month: "ungefär en månad", + months: "%d månader", + year: "ungefär ett år", + years: "%d år" + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.th.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.th.js new file mode 100644 index 000000000..23d59d48e --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.th.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Thai + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ที่แล้ว", + suffixFromNow: "จากตอนนี้", + seconds: "น้อยกว่าหนึ่งนาที", + minute: "ประมาณหนึ่งนาที", + minutes: "%d นาที", + hour: "ประมาณหนึ่งชั่วโมง", + hours: "ประมาณ %d ชั่วโมง", + day: "หนึ่งวัน", + days: "%d วัน", + month: "ประมาณหนึ่งเดือน", + months: "%d เดือน", + year: "ประมาณหนึ่งปี", + years: "%d ปี", + wordSeparator: "", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.tr.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.tr.js new file mode 100644 index 000000000..8e0d2d4e9 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.tr.js @@ -0,0 +1,26 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Turkish + jQuery.timeago.settings.strings = { + suffixAgo: 'önce', + suffixFromNow: null, + seconds: 'birkaç saniye', + minute: '1 dakika', + minutes: '%d dakika', + hour: '1 saat', + hours: '%d saat', + day: '1 gün', + days: '%d gün', + month: '1 ay', + months: '%d ay', + year: '1 yıl', + years: '%d yıl' + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.uk.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.uk.js new file mode 100644 index 000000000..489963b5d --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.uk.js @@ -0,0 +1,42 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Ukrainian + function numpf(n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + var n10 = n % 10; + if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { + return f; + } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { + return s; + } else { + return t; + } + } + + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "тому", + suffixFromNow: null, + seconds: "менше хвилини", + minute: "хвилина", + minutes: function(value) { return numpf(value, "%d хвилина", "%d хвилини", "%d хвилин"); }, + hour: "година", + hours: function(value) { return numpf(value, "%d година", "%d години", "%d годин"); }, + day: "день", + days: function(value) { return numpf(value, "%d день", "%d дні", "%d днів"); }, + month: "місяць", + months: function(value) { return numpf(value, "%d місяць", "%d місяці", "%d місяців"); }, + year: "рік", + years: function(value) { return numpf(value, "%d рік", "%d роки", "%d років"); } + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.ur.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.ur.js new file mode 100644 index 000000000..9d0cd402d --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.ur.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Urdu + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "پہلے", + suffixFromNow: "اب سے", + seconds: "کچھ سیکنڈز", + minute: "تقریباً ایک منٹ", + minutes: "%d منٹ", + hour: "تقریباً ایک گھنٹہ", + hours: "تقریباً %d گھنٹے", + day: "ایک دن", + days: "%d دن", + month: "تقریباً ایک مہینہ", + months: "%d مہینے", + year: "تقریباً ایک سال", + years: "%d سال", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.uz.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.uz.js new file mode 100644 index 000000000..f4ce8b33b --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.uz.js @@ -0,0 +1,29 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + //Uzbek + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "keyin", + suffixAgo: "avval", + suffixFromNow: null, + seconds: "bir necha soniya", + minute: "1 daqiqa", + minutes: function(value) { return "%d daqiqa"; }, + hour: "1 soat", + hours: function(value) { return "%d soat"; }, + day: "1 kun", + days: function(value) { return "%d kun"; }, + month: "1 oy", + months: function(value) { return "%d oy"; }, + year: "1 yil", + years: function(value) { return "%d yil"; }, + wordSeparator: " " + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.vi.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.vi.js new file mode 100644 index 000000000..30f592ac3 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.vi.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Vietnamese + jQuery.timeago.settings.strings = { + prefixAgo: 'cách đây', + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "trước", + seconds: "chưa đến một phút", + minute: "khoảng một phút", + minutes: "%d phút", + hour: "khoảng một tiếng", + hours: "khoảng %d tiếng", + day: "một ngày", + days: "%d ngày", + month: "khoảng một tháng", + months: "%d tháng", + year: "khoảng một năm", + years: "%d năm", + wordSeparator: " ", + numbers: [] + }; +})); diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.zh-CN.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.zh-CN.js new file mode 100644 index 000000000..c21a2874a --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.zh-CN.js @@ -0,0 +1,31 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Simplified Chinese + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "之前", + suffixFromNow: "之后", + seconds: "不到1分钟", + minute: "大约1分钟", + minutes: "%d分钟", + hour: "大约1小时", + hours: "大约%d小时", + day: "1天", + days: "%d天", + month: "大约1个月", + months: "%d月", + year: "大约1年", + years: "%d年", + numbers: [], + wordSeparator: "" + }; +})); + diff --git a/campurro-merged-website-tamplate/timeago/jquery.timeago.zh-TW.js b/campurro-merged-website-tamplate/timeago/jquery.timeago.zh-TW.js new file mode 100644 index 000000000..15f562699 --- /dev/null +++ b/campurro-merged-website-tamplate/timeago/jquery.timeago.zh-TW.js @@ -0,0 +1,30 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } +}(function (jQuery) { + // Traditional Chinese, zh-tw + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "之前", + suffixFromNow: "之後", + seconds: "不到1分鐘", + minute: "大約1分鐘", + minutes: "%d分鐘", + hour: "大約1小時", + hours: "%d小時", + day: "大約1天", + days: "%d天", + month: "大約1個月", + months: "%d個月", + year: "大約1年", + years: "%d年", + numbers: [], + wordSeparator: "" + }; +})); From 26baebf257d2ff4e73bed0489f97bfa539c5bac3 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Sun, 25 Oct 2020 19:46:23 +0100 Subject: [PATCH 91/98] Delete config.ETNX.json --- config.ETNX.json | 313 ----------------------------------------------- 1 file changed, 313 deletions(-) delete mode 100644 config.ETNX.json diff --git a/config.ETNX.json b/config.ETNX.json deleted file mode 100644 index 7051295fd..000000000 --- a/config.ETNX.json +++ /dev/null @@ -1,313 +0,0 @@ -{ - "poolHost": "POOL_URL", - - "coin": "electronero", - "symbol": "ETNX", - "coinUnits": 100, - "coinDecimalPlaces": 2, - "coinDifficultyTarget": 60, - - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 4, - "cnBlobType": 3, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "POOL_ADDRESS", - "intAddressPrefix": 18019, - "subAddressPrefix": 42, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "cert.pem", - "sslKey": "privkey.pem", - "sslCA": "fullchain.pem", - "ports": [ - { - "port": 1122, - "difficulty": 10000, - "desc": "Low end hardware" - }, - { - "port": 2233, - "difficulty": 100000, - "desc": "Mid range hardware" - }, - { - "port": 3344, - "difficulty": 500000, - "desc": "High end hardware" - }, - { - "port": 4455, - "difficulty": 1000000, - "desc": "Cloud-mining / NiceHash" - }, - { - "port": 6666, - "difficulty": 3000, - "desc": "Hidden port", - "hidden": true - }, - { - "port": 6677, - "difficulty": 1000000, - "desc": "SSL connection", - "ssl": true - } - ], - "varDiff": { - "minDiff": 100, - "maxDiff": 1000000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, - - "payments": { - "enabled": true, - "interval": 2160, - "maxAddresses": 10, - "mixin": 2, - "priority": 0, - "transferFee": 20000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 100000, - "maxPayment": 25000000, - "maxTransactionAmount": 50000000, - "denomination": 100 - }, - - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 30, - "poolFee": 0.0, - "devDonation": 0.0, - "networkFee": 0.0 - }, - - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "admin_pass", - "ssl": true, - "sslPort": 8119, - "sslCert": "cert.pem", - "sslKey": "privkey.pem", - "sslCA": "fullchain.pem", - "trustProxyIP": true - }, - - "daemon": { - "host": "127.0.0.1", - "port": 3000 - }, - - "wallet": { - "host": "127.0.0.1", - "port": 3000 - }, - - "redis": { - "host": "127.0.0.1", - "port": 3279, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, - - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, - - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, - - "telegram": { - "enabled": true, - "botName": "POOL_BOT", - "token": "POOL_BOT_TOKEN", - "channel": "POOL_BOT_CHANNEL", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, - - "prices": { - "source": "cryptonator", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } -} From f4f4eb8ce2d96f33e1a293a86ad9a907d175e715 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Sun, 25 Oct 2020 19:47:07 +0100 Subject: [PATCH 92/98] Add files via upload --- Dockerfile | 3 +- Example_MergeMining.json | 402 ++++++++++++++++++++++ README.md | 153 +++++---- TODO.md | 32 ++ init.js | 698 +++++++++++++++++++++++---------------- package.json | 60 ++-- 6 files changed, 962 insertions(+), 386 deletions(-) create mode 100644 Example_MergeMining.json create mode 100644 TODO.md diff --git a/Dockerfile b/Dockerfile index 2401a94f1..31034591f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,8 @@ FROM node:8-slim RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs-legacy npm git libboost1.55-all libssl-dev \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* && \ + chmod +x /wait-for-it.sh ADD . /pool/ WORKDIR /pool/ diff --git a/Example_MergeMining.json b/Example_MergeMining.json new file mode 100644 index 000000000..91daafd47 --- /dev/null +++ b/Example_MergeMining.json @@ -0,0 +1,402 @@ +{ + "poolHost": "your.pool.host", + + "coin": "Arqma", + "symbol": "ARQ", + "coinUnits": 1000000000, + "coinDecimalPlaces": 9, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + + "daemonType": "default", + "cnAlgorithm": "cryptonight_pico", + "cnVariant": 2, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": [{ + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "coin": "CyprusCoin", + "childDaemon": { + "host": "1.2.3.4", + "port": 11975 + }, + "pattern": "^XCY", + "blockchainExplorer": "http://explorer.mycypruscoin.com/?hash={id}#blockchain_block", + "transactionExplorer": "http://explorer.mycypruscoin.com/?hash={id}#blockchain_transaction", + "api": "https://your.pool.host/apiMerged1", + "enabled": true + }, + { + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "coin": "Turtlecoin", + "childDaemon": { + "host": "1.2.3.4", + "port": 11898 + }, + "pattern": "^TRTL", + "blockchainExplorer": "https://explorer.turtlecoin.lol/block.html?hash={id}", + "transactionExplorer": "https://explorer.turtlecoin.lol/block.html?hash={id}", + "api": "https://your.pool.host/apiMerged2", + "enabled": true + }, + { + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "coin": "Plenteum", + "childDaemon": { + "host": "1.2.3.4", + "port": 44016 + }, + "pattern": "^Pl", + "blockchainExplorer": "http://block-explorer.plenteum.com/?hash={id}#blockchain_block", + "transactionExplorer": "http://block-explorer.plenteum.com/?hash={id}#blockchain_transaction", + "api": "https://your.pool.host/apiMerged3", + "enabled": true + }, + { + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "coin": "Iridium", + "childDaemon": { + "host": "1.2.3.4", + "port": 13007 + }, + "pattern": "^ir", + "blockchainExplorer": "http://explorer.ird.cash/?hash={id}#block", + "transactionExplorer": "http://explorer.ird.cash/?hash={id}#transaction", + "api": "https://your.pool.host/apiMerged4", + "enabled": true + }, + { + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "coin": "Tritanium", + "childDaemon": { + "host": "1.2.3.4", + "port": 16123 + }, + "pattern": "^Tri", + "blockchainExplorer": "http://www.tritaniumcoin.com/?hash={id}#blockchain_block", + "transactionExplorer": "http://www.tritaniumcoin.com/?hash={id}#blockchain_transaction", + "api": "https://your.pool.host/apiMerged5", + "enabled": true + }, + { + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "coin": "WrkzCoin", + "childDaemon": { + "host": "1.2.3.4", + "port": 17856 + }, + "pattern": "^Wrkz", + "blockchainExplorer": "http://myexplorer.wrkz.work/?hash={id}#blockchain_block", + "transactionExplorer": "http://myexplorer.wrkz.work/?hash={id}#blockchain_transaction", + "api": "https://your.pool.host/apiMerged6", + "enabled": true + }, + { + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "coin": "Elphyrecoin2", + "childDaemon": { + "host": "1.2.3.4", + "port": 45501 + }, + "pattern": "^Phy", + "blockchainExplorer": "http://explorer2.elphyrecoin.xyz/?hash={id}#blockchain_block", + "transactionExplorer": "http://explorer2.elphyrecoin.xyz/?hash={id}#blockchain_transaction", + "api": "https://your.pool.host/apiMerged7", + "enabled": true + } + ], + "poolServer": { + "enabled": true, + "mergedMining": true, + "clusterForks": 5, + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 1141703, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "chain.pem", + "ports": [ + { + "port": 1444, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 2444, + "difficulty": 50000, + "desc": "Mid range hardware" + }, + { + "port": 3444, + "difficulty": 500000, + "desc": "High end hardware" + } + ], + "varDiff": { + "minDiff": 500, + "maxDiff": 100000000, + "targetTime": 45, + "retargetTime": 40, + "variancePercent": 30, + "maxJump": 60 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 900, + "maxAddresses": 5, + "mixin": 6, + "priority": 0, + "transferFee": 10000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000, + "maxPayment": null, + "maxTransactionAmount": 100000000000, + "denomination": 1000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 18, + "poolFee": 0.5, + "devDonation": 0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 60, + "bindIp": "127.0.0.1", + "port": 8217, + "blocks": 30, + "payments": 30, + "password": "password", + "ssl": false, + "sslPort": 8119, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 19994 + }, + + "wallet": { + "host": "1.2.3.4", + "port": 19996 + }, + + "redis": { + "host": "1.2.3.4", + "port": 6379, + "auth": null, + "db": 10, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/README.md b/README.md index f8502d074..36d61f4a1 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -cryptonote-nodejs-pool for merged mining -========================================= +cryptonote-merged-pool +====================== -High performance Node.js (with native C addons) mining pool for CryptoNote based coins. Comes with lightweight example front-end script which uses the pool's AJAX API. Support for Cryptonight (Original, Monero v7, Stellite v7), Cryptonight Light (Original, Aeon v7, IPBC), Cryptonight Pico (Trtl) and Cryptonight Heavy (Sumokoin) algorithms. With Merged mining support. +High performance Node.js (with native C addons) mining pool for CryptoNote based coins. Comes with lightweight example front-end script which uses the pool's AJAX API. Support for Cryptonight (Original, Monero v7, Stellite v7), Cryptonight Light (Original, Aeon v7, IPBC) and Cryptonight Heavy (Sumokoin) algorithms. +**For original cryptonote-nodejs-pool use [orig](https://github.com/blockinator/cryptonote-merged-pool/tree/orig) branch.** #### Table of Contents * [Features](#features) @@ -96,12 +97,17 @@ Community / Support * [GitHub Wiki](https://github.com/dvandal/cryptonote-nodejs-pool/wiki) * [GitHub Issues](https://github.com/dvandal/cryptonote-nodejs-pool/issues) -* [Telegram Group](http://t.me/CryptonotePool)j) +* [Telegram Group](http://t.me/CryptonotePool) #### Pools Using This Software -* https://smartcoinpool.com/ - +* https://imaginary.stream/ +* https://graft.anypool.net/ +* https://www.dark-mine.su/ +* http://itns.proxpool.com/ +* https://bytecoin.pt/ +* https://pool.leviar.io/ +* https://pool.croatpirineus.cat/ Usage === @@ -109,12 +115,15 @@ Usage #### Requirements * Coin daemon(s) (find the coin's repo and build latest version from source) * [List of Cryptonote coins](https://github.com/dvandal/cryptonote-nodejs-pool/wiki/Cryptonote-Coins) -* [Node.js](http://nodejs.org/) v4.0+ +* [Node.js](http://nodejs.org/) v11.0+ * For Ubuntu: ``` - curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash + curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash sudo apt-get install -y nodejs -``` + ``` + * Or use NVM(https://github.com/creationix/nvm) for debian/ubuntu. + + * [Redis](http://redis.io/) key-value store v2.6+ * For Ubuntu: ``` @@ -122,6 +131,16 @@ sudo add-apt-repository ppa:chris-lea/redis-server sudo apt-get update sudo apt-get install redis-server ``` + Dont forget to tune redis-server: + ``` +echo never > /sys/kernel/mm/transparent_hugepage/enabled +echo 1024 > /proc/sys/net/core/somaxconn + ``` + Add this lines to your /etc/rc.local and make it executable + ``` + chmod +x /etc/rc.local + ``` + * libssl required for the node-multi-hashing module * For Ubuntu: `sudo apt-get install libssl-dev` @@ -150,7 +169,7 @@ sudo su - your-user Clone the repository and run `npm update` for all the dependencies to be installed: ```bash -git clone https://github.com/dvandal/cryptonote-nodejs-pool.git pool +git clone https://github.com/muscleman/cryptonote-nodejs-pool.git pool cd pool npm update @@ -166,7 +185,7 @@ Explanation for each field: "poolHost": "your.pool.host", /* Used for storage in redis so multiple coins can share the same redis instance. */ -"coin": "graft", +"coin": "graft", // Must match the parentCoin variable in config.js /* Used for front-end display */ "symbol": "GRFT", @@ -180,8 +199,8 @@ Explanation for each field: /* Coin network time to mine one block, see DIFFICULTY_TARGET constant in DAEMON_CODE/src/cryptonote_config.h */ "coinDifficultyTarget": 120, -/* Used for storage in redis so multiple coins can share the same redis instance. */ -"childCoin": "monetaverde", +"blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", //used on blocks page to generate hyperlinks. +"transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", //used on the payments page to generate hyperlinks /* Set daemon type. Supported values: default, forknote (Fix block height + 1), bytecoin (ByteCoin Wallet RPC API) */ "daemonType": "default", @@ -195,8 +214,8 @@ Explanation for each field: "cnVariant": 1, "cnBlobType": 0, "includeHeight":false, /*true to include block.height in job to miner*/ -"includeAlgo":"cn/wow", /*wownero specific change to include algo in job to miner*/ - +"includeAlgo":"cn/wow", /*wownero specific change to include algo in job to miner*/ "includeAlgo":"cn/wow", /*wownero specific change to include algo in job to miner*/ +"isRandomX": true, /* Logging */ "logging": { @@ -220,13 +239,24 @@ Explanation for each field: "colors": true } }, - +"childPools":[ {"poolAddress":"your wallet", + "intAddressPrefix": null, + "coin": "MCN", //must match COIN name in the child pools config.json + "childDaemon": { + "host": "127.0.0.1", + "port": 26081 + }, + "pattern": "^Vdu", //regex to identify which childcoin the miner specified in password. eg) Vdu is first 3 chars of a MCN wallet address. + "blockchainExplorer": "https://explorer.mcn.green/?hash={id}#blockchain_block", + "transactionExplorer": "https://explorer.mcn.green/?hash={id}#blockchain_transaction", + "api": "https://multi-miner.smartcoinpool.net/apiMerged1", + "enabled": true + } +] /* Modular Pool Server */ "poolServer": { "enabled": true, - - "mergedMining": true, - + "mergedMining":false, /* Set to "auto" by default which will spawn one process/fork/worker for each CPU core in your system. Each of these workers will run a separate instance of your pool(s), and the kernel will load balance miners using these forks. Optionally, @@ -234,14 +264,14 @@ Explanation for each field: "clusterForks": "auto", /* Address where block rewards go, and miner payments come from. */ - "poolAddress": "GBqRuitSoU3PFPBAkXMEnLdBRWXH4iDSD6RDxnQiEFjVJhWUi1UuqfV5EzosmaXgpPGE6JJQjMYhZZgWY8EJQn8jQTsuTit", - - /* Address where child coin block rewards go, and miner payments come from. */ - "poolChildAddress": "**** YOUR MCN WALLET ADDRESS ********", + "poolAddress": "your wallet", /* This is the integrated address prefix used for miner login validation. */ "intAddressPrefix": 91, - + + /* This is the Subaddress prefix used for miner login validation. */ + "subAddressPrefix": 252, + /* Poll RPC daemons for new blocks every this many milliseconds. */ "blockRefreshInterval": 1000, @@ -307,7 +337,10 @@ Explanation for each field: /* Set payment ID on miner client side by passing
. */ "paymentId": { - "addressSeparator": "." // Character separator between
and + "addressSeparator": ".", // Character separator between
and + "validation": true // Refuse login if non alphanumeric characters in + "validations": ["1,16", "64"], //regex quantity. range 1-16 characters OR exactly 64 character + "ban": true // ban the miner for invalid paymentid }, /* Feature to trust share difficulties from miners which can @@ -364,8 +397,7 @@ Explanation for each field: "depth": 60, "poolFee": 0.8, // 0.8% pool fee (1% total fee total including donations) "devDonation": 0.2, // 0.2% donation to send to pool dev - "networkFee": 0.0, // Network/Governance fee (used by some coins) - "useFirstVout": false, // Should be true for a coin like Loki that has multiple block reward recipients where first is miner reward + "networkFee": 0.0, // Network/Governance fee (used by some coins like Loki) /* Some forknote coins have an issue with block height in RPC request, to fix you can enable this option. See: https://github.com/forknote/forknote-pool/issues/48 */ @@ -396,16 +428,11 @@ Explanation for each field: "port": 18981 }, -/* Child coin daemon connection details (default port is MCN's 26081) */ -"childDaemon": { - "host": "127.0.0.1", - "port": 26081 -}, - /* Wallet daemon connection details (default port is 18980) */ "wallet": { "host": "127.0.0.1", - "port": 18982 + "port": 18982, + "password": "--rpc-password" }, /* Redis connection info (default port is 6379) */ @@ -414,7 +441,7 @@ Explanation for each field: "port": 6379, "auth": null, // If set, client will run redis auth command on connect. Use for remote db "db": 0, // Set the REDIS database to use (default to 0) - "cleanupInterval": 20 // Set the REDIS database cleanup interval (in days) + "cleanupInterval": 15 // Set the REDIS database cleanup interval (in days) } /* Pool Notifications */ @@ -517,7 +544,7 @@ Explanation for each field: /* Prices settings for market and price charts */ "prices": { - "source": "cryptonator", // Exchange (supported values: cryptonator, altex, crex24, cryptopia, stocks.exchange, tradeogre) + "source": "cryptonator", // Exchange (supported values: cryptonator, altex, crex24, cryptopia, stocks.exchange, tradeogre, maplechange) "currency": "USD" // Default currency }, @@ -569,6 +596,12 @@ Explanation for each field: "stepInterval": 1800, "maximumPeriod": 86400 }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, "payments": { // Payment chart uses all user payments data stored in DB "enabled": true } @@ -597,6 +630,8 @@ This software contains four distinct modules: * `api` - Used by the website to display network, pool and miners' data * `unlocker` - Processes block candidates and increases miners' balances when blocks are unlocked * `payments` - Sends out payments to miners according to their balances stored in redis +* `chartsDataCollector` - Processes miners and workers hashrate stats and charts +* `telegramBot` - Processes telegram bot commands By default, running the `init.js` script will start up all four modules. You can optionally have the script start @@ -609,7 +644,7 @@ node init.js -module=api [Example screenshot](http://i.imgur.com/SEgrI3b.png) of running the pool in single module mode with tmux. To keep your pool up, on operating system with systemd, you can create add your pool software as a service. -Use this [example](https://github.com/dvandal/cryptonote-nodejs-pool/blob/master/deployment/cryptonote-nodejs-pool.service) to create the systemd service `/lib/systemd/system/cryptonote-nodejs-pool.service` +Use this [example](https://github.com/muscleman/cryptonote-nodejs-pool/blob/master/deployment/cryptonote-nodejs-pool.service) to create the systemd service `/lib/systemd/system/cryptonote-nodejs-pool.service` Then enable and start the service with the following commands : ``` @@ -617,21 +652,6 @@ sudo systemctl enable cryptonote-nodejs-pool.service sudo systemctl start cryptonote-nodejs-pool.service ``` - -#### 3) Merged mining support - -To enable merged mining you will need to use at leas 2 nodejs processes. One for the pool process + main coin config and another process for the child coin payments, unlocker, etc. -First, you will need both coins node daemons running. -Take a look at the [config_inf8-mcn.json](https://github.com/dvandal/cryptonote-nodejs-pool/blob/dvandal-with-mergemining/config_inf8-mcn.json) for a main (pool) config example. -The child coin config file is a normal config with your child coin data but with the poolServer.enabled set to false. - -``` -nodejs init.js -config=config_inf8-mcn.json -nodejs init.js -config=config_child_coin.json -``` -You can use [forever](https://github.com/nodejitsu/forever) or [PM2](https://github.com/Unitech/pm2) to start both as daemons. - - #### 4) Host the front-end Simply host the contents of the `website_example` directory on file server capable of serving simple static files. @@ -660,6 +680,9 @@ var telegram = "https://t.me/YourPool"; /* Pool Discord URL */ var discord = "https://discordapp.com/invite/YourPool"; +/*Pool Facebook URL */ +var facebook = "https://www.facebook.com/ 0) ? config.redis.db : 0; -global.redisClient = redis.createClient(config.redis.port, config.redis.host, { db: redisDB, auth_pass: config.redis.auth }); - -// Load pool modules -if (cluster.isWorker){ - switch(process.env.workerType){ - case 'pool': - require('./lib/pool.js'); - break; - case 'blockUnlocker': - require('./lib/blockUnlocker.js'); - break; - case 'paymentProcessor': - require('./lib/paymentProcessor.js'); - break; - case 'api': - require('./lib/api.js'); - break; - case 'chartsDataCollector': - require('./lib/chartsDataCollector.js'); - break; - case 'telegramBot': - require('./lib/telegramBot.js'); - break; - } - return; -} - -// Initialize log system -var logSystem = 'master'; -require('./lib/exceptionWriter.js')(logSystem); - -// Pool informations -log('info', logSystem, 'Starting Cryptonote Node.JS pool version %s', [version]); - -// Run a single module ? -var singleModule = (function(){ - var validModules = ['pool', 'api', 'unlocker', 'payments', 'chartsDataCollector', 'telegramBot']; - - for (var i = 0; i < process.argv.length; i++){ - if (process.argv[i].indexOf('-module=') === 0){ - var moduleName = process.argv[i].split('=')[1]; - if (validModules.indexOf(moduleName) > -1) - return moduleName; - - log('error', logSystem, 'Invalid module "%s", valid modules: %s', [moduleName, validModules.join(', ')]); - process.exit(); - } - } -})(); - -/** - * Start modules - **/ -(function init(){ - checkRedisVersion(function(){ - if (singleModule){ - log('info', logSystem, 'Running in single module mode: %s', [singleModule]); - - switch(singleModule){ - case 'pool': - spawnPoolWorkers(); - break; - case 'unlocker': - spawnBlockUnlocker(); - break; - case 'payments': - spawnPaymentProcessor(); - break; - case 'api': - spawnApi(); - break; - case 'chartsDataCollector': - spawnChartsDataCollector(); - break; - case 'telegramBot': - spawnTelegramBot(); - break; - } - } - else{ - spawnPoolWorkers(); - spawnBlockUnlocker(); - spawnPaymentProcessor(); - spawnApi(); - spawnChartsDataCollector(); - spawnTelegramBot(); - } - }); -})(); - -/** - * Check redis database version - **/ -function checkRedisVersion(callback){ - redisClient.info(function(error, response){ - if (error){ - log('error', logSystem, 'Redis version check failed'); - return; - } - var parts = response.split('\r\n'); - var version; - var versionString; - for (var i = 0; i < parts.length; i++){ - if (parts[i].indexOf(':') !== -1){ - var valParts = parts[i].split(':'); - if (valParts[0] === 'redis_version'){ - versionString = valParts[1]; - version = parseFloat(versionString); - break; - } - } - } - if (!version){ - log('error', logSystem, 'Could not detect redis version - must be super old or broken'); - return; - } - else if (version < 2.6){ - log('error', logSystem, "You're using redis version %s the minimum required version is 2.6. Follow the damn usage instructions...", [versionString]); - return; - } - callback(); - }); -} - -/** - * Spawn pool workers module - **/ -function spawnPoolWorkers(){ - if (!config.poolServer || !config.poolServer.enabled || !config.poolServer.ports || config.poolServer.ports.length === 0) return; - - if (config.poolServer.ports.length === 0){ - log('error', logSystem, 'Pool server enabled but no ports specified'); - return; - } - - var numForks = (function(){ - if (!config.poolServer.clusterForks) - return 1; - if (config.poolServer.clusterForks === 'auto') - return os.cpus().length; - if (isNaN(config.poolServer.clusterForks)) - return 1; - return config.poolServer.clusterForks; - })(); - - var poolWorkers = {}; - - var createPoolWorker = function(forkId){ - var worker = cluster.fork({ - workerType: 'pool', - forkId: forkId - }); - worker.forkId = forkId; - worker.type = 'pool'; - poolWorkers[forkId] = worker; - worker.on('exit', function(code, signal){ - log('error', logSystem, 'Pool fork %s died, spawning replacement worker...', [forkId]); - setTimeout(function(){ - createPoolWorker(forkId); - }, 2000); - }).on('message', function(msg){ - switch(msg.type){ - case 'banIP': - Object.keys(cluster.workers).forEach(function(id) { - if (cluster.workers[id].type === 'pool'){ - cluster.workers[id].send({type: 'banIP', ip: msg.ip}); - } - }); - break; - } - }); - }; - - var i = 1; - var spawnInterval = setInterval(function(){ - createPoolWorker(i.toString()); - i++; - if (i - 1 === numForks){ - clearInterval(spawnInterval); - log('info', logSystem, 'Pool spawned on %d thread(s)', [numForks]); - } - }, 10); -} - -/** - * Spawn block unlocker module - **/ -function spawnBlockUnlocker(){ - if (!config.blockUnlocker || !config.blockUnlocker.enabled) return; - - var worker = cluster.fork({ - workerType: 'blockUnlocker' - }); - worker.on('exit', function(code, signal){ - log('error', logSystem, 'Block unlocker died, spawning replacement...'); - setTimeout(function(){ - spawnBlockUnlocker(); - }, 2000); - }); -} - -/** - * Spawn payment processor module - **/ -function spawnPaymentProcessor(){ - if (!config.payments || !config.payments.enabled) return; - - var worker = cluster.fork({ - workerType: 'paymentProcessor' - }); - worker.on('exit', function(code, signal){ - log('error', logSystem, 'Payment processor died, spawning replacement...'); - setTimeout(function(){ - spawnPaymentProcessor(); - }, 2000); - }); -} - -/** - * Spawn API module - **/ -function spawnApi(){ - if (!config.api || !config.api.enabled) return; - - var worker = cluster.fork({ - workerType: 'api' - }); - worker.on('exit', function(code, signal){ - log('error', logSystem, 'API died, spawning replacement...'); - setTimeout(function(){ - spawnApi(); - }, 2000); - }); -} - -/** - * Spawn charts data collector module - **/ -function spawnChartsDataCollector(){ - if (!config.charts) return; - - var worker = cluster.fork({ - workerType: 'chartsDataCollector' - }); - worker.on('exit', function(code, signal){ - log('error', logSystem, 'chartsDataCollector died, spawning replacement...'); - setTimeout(function(){ - spawnChartsDataCollector(); - }, 2000); - }); -} - -/** - * Spawn telegram bot module - **/ -function spawnTelegramBot(){ - if (!config.telegram || !config.telegram.enabled || !config.telegram.token) return; - - var worker = cluster.fork({ - workerType: 'telegramBot' - }); - worker.on('exit', function(code, signal){ - log('error', logSystem, 'telegramBot died, spawning replacement...'); - setTimeout(function(){ - spawnTelegramBot(); - }, 2000); - }); -} + /** + * Cryptonite Node.JS Pool + * https://github.com/dvandal/cryptonote-nodejs-pool + * + * Pool initialization script + **/ + + // Load needed modules + var fs = require('fs'); + var cluster = require('cluster'); + var os = require('os'); + + // Load configuration + require('./lib/configReader.js'); + + // Load log system + require('./lib/logger.js'); + + // Initialize redis database client + var redis = require('redis'); + + var redisDB = (config.redis.db && config.redis.db > 0) ? config.redis.db : 0; + global.redisClient = redis.createClient(config.redis.port, config.redis.host, { + db: redisDB, + auth_pass: config.redis.auth + }); + + if ((typeof config.poolServer.mergedMining !== 'undefined' && config.poolServer.mergedMining) && typeof config.childPools !== 'undefined') + config.childPools = config.childPools.filter(pool => pool.enabled); + else + config.childPools = []; + + // Load pool modules + if (cluster.isWorker) { + switch (process.env.workerType) { + case 'pool': + require('./lib/pool.js'); + break; + case 'daemon': + require('./lib/daemon.js') + break + case 'childDaemon': + require('./lib/childDaemon.js') + break + case 'blockUnlocker': + require('./lib/blockUnlocker.js'); + break; + case 'paymentProcessor': + require('./lib/paymentProcessor.js'); + break; + case 'api': + require('./lib/api.js'); + break; + case 'chartsDataCollector': + require('./lib/chartsDataCollector.js'); + break; + case 'telegramBot': + require('./lib/telegramBot.js'); + break; + } + return; + } + + // Initialize log system + var logSystem = 'master'; + require('./lib/exceptionWriter.js')(logSystem); + + // Pool informations + log('info', logSystem, 'Starting Cryptonote Node.JS pool version %s', [version]); + + // Developer donations + if (devFee < 0.2) + log('info', logSystem, 'Developer donation \(devDonation\) is set to %d\%, Please consider raising it to 0.2\% or higher !!!', [devFee]); + + // Run a single module ? + var singleModule = (function () { + var validModules = ['pool', 'api', 'unlocker', 'payments', 'chartsDataCollector', 'telegramBot']; + + for (var i = 0; i < process.argv.length; i++) { + if (process.argv[i].indexOf('-module=') === 0) { + var moduleName = process.argv[i].split('=')[1]; + if (validModules.indexOf(moduleName) > -1) + return moduleName; + + log('error', logSystem, 'Invalid module "%s", valid modules: %s', [moduleName, validModules.join(', ')]); + process.exit(); + } + } + })(); + + /** + * Start modules + **/ + (function init () { + checkRedisVersion(function () { + if (singleModule) { + log('info', logSystem, 'Running in single module mode: %s', [singleModule]); + + switch (singleModule) { + case 'daemon': + spawnDaemon() + break + case 'pool': + spawnPoolWorkers(); + break; + case 'unlocker': + spawnBlockUnlocker(); + break; + case 'payments': + spawnPaymentProcessor(); + break; + case 'api': + spawnApi(); + break; + case 'chartsDataCollector': + spawnChartsDataCollector(); + break; + case 'telegramBot': + spawnTelegramBot(); + break; + } + } else { + spawnPoolWorkers(); + spawnDaemon(); + if (config.poolServer.mergedMining) + spawnChildDaemons(); + spawnBlockUnlocker(); + spawnPaymentProcessor(); + spawnApi(); + spawnChartsDataCollector(); + spawnTelegramBot(); + } + }); + })(); + + /** + * Check redis database version + **/ + function checkRedisVersion (callback) { + redisClient.info(function (error, response) { + if (error) { + log('error', logSystem, 'Redis version check failed'); + return; + } + var parts = response.split('\r\n'); + var version; + var versionString; + for (var i = 0; i < parts.length; i++) { + if (parts[i].indexOf(':') !== -1) { + var valParts = parts[i].split(':'); + if (valParts[0] === 'redis_version') { + versionString = valParts[1]; + version = parseFloat(versionString); + break; + } + } + } + if (!version) { + log('error', logSystem, 'Could not detect redis version - must be super old or broken'); + return; + } else if (version < 2.6) { + log('error', logSystem, "You're using redis version %s the minimum required version is 2.6. Follow the damn usage instructions...", [versionString]); + return; + } + callback(); + }); + } + + /** + * Spawn pool workers module + **/ + function spawnPoolWorkers () { + if (!config.poolServer || !config.poolServer.enabled || !config.poolServer.ports || config.poolServer.ports.length === 0) return; + + if (config.poolServer.ports.length === 0) { + log('error', logSystem, 'Pool server enabled but no ports specified'); + return; + } + var numForks = (function () { + if (!config.poolServer.clusterForks) + return 1; + if (config.poolServer.clusterForks === 'auto') + return os.cpus() + .length; + if (isNaN(config.poolServer.clusterForks)) + return 1; + return config.poolServer.clusterForks; + })(); + + var poolWorkers = {}; + + var createPoolWorker = function (forkId) { + var worker = cluster.fork({ + workerType: 'pool', + forkId: forkId + }); + worker.forkId = forkId; + worker.type = 'pool'; + poolWorkers[forkId] = worker; + worker.on('exit', function (code, signal) { + log('error', logSystem, 'Pool fork %s died, spawning replacement worker...', [forkId]); + setTimeout(function () { + createPoolWorker(forkId); + }, 2000); + }) + .on('message', function (msg) { + switch (msg.type) { + case 'banIP': + Object.keys(cluster.workers) + .forEach(function (id) { + if (cluster.workers[id].type === 'pool') { + cluster.workers[id].send({ + type: 'banIP', + ip: msg.ip + }); + } + }); + break; + } + }); + }; + + var i = 1; + var spawnInterval = setInterval(function () { + createPoolWorker(i.toString()); + i++; + if (i - 1 === numForks) { + clearInterval(spawnInterval); + log('info', logSystem, 'Pool spawned on %d thread(s)', [numForks]); + } + }, 10); + } + + /** + * Spawn pool workers module + **/ + function spawnChildDaemons () { + if (!config.poolServer || !config.poolServer.enabled || !config.poolServer.ports || config.poolServer.ports.length === 0) return; + + if (config.poolServer.ports.length === 0) { + log('error', logSystem, 'Pool server enabled but no ports specified'); + return; + } + + let numForks = config.childPools.length; + if (numForks === 0) return; + var daemonWorkers = {}; + + var createDaemonWorker = function (poolId) { + var worker = cluster.fork({ + workerType: 'childDaemon', + poolId: poolId + }); + worker.poolId = poolId; + worker.type = 'childDaemon'; + daemonWorkers[poolId] = worker; + worker.on('exit', function (code, signal) { + log('error', logSystem, 'Child Daemon fork %s died, spawning replacement worker...', [poolId]); + setTimeout(function () { + createDaemonWorker(poolId); + }, 2000); + }) + .on('message', function (msg) { + switch (msg.type) { + case 'ChildBlockTemplate': + Object.keys(cluster.workers) + .forEach(function (id) { + if (cluster.workers[id].type === 'pool') { + cluster.workers[id].send({ + type: 'ChildBlockTemplate', + block: msg.block, + poolIndex: msg.poolIndex + }); + } + }); + break; + } + }); + }; + + var i = 0; + var spawnInterval = setInterval(function () { + createDaemonWorker(i.toString()) + i++ + if (i === numForks) { + clearInterval(spawnInterval); + log('info', logSystem, 'Child Daemon spawned on %d thread(s)', [numForks]); + } + }, 10); + } + + + /** + * Spawn daemon module + **/ + function spawnDaemon () { + if (!config.poolServer || !config.poolServer.enabled || !config.poolServer.ports || config.poolServer.ports.length === 0) return; + + var worker = cluster.fork({ + workerType: 'daemon' + }); + worker.on('exit', function (code, signal) { + log('error', logSystem, 'Daemon died, spawning replacement...'); + setTimeout(function () { + spawnDaemon(); + }, 10); + }) + .on('message', function (msg) { + switch (msg.type) { + case 'BlockTemplate': + Object.keys(cluster.workers) + .forEach(function (id) { + if (cluster.workers[id].type === 'pool') { + cluster.workers[id].send({ + type: 'BlockTemplate', + block: msg.block + }); + } + }); + break; + } + }); + } + + /** + * Spawn block unlocker module + **/ + function spawnBlockUnlocker () { + if (!config.blockUnlocker || !config.blockUnlocker.enabled) return; + + var worker = cluster.fork({ + workerType: 'blockUnlocker' + }); + worker.on('exit', function (code, signal) { + log('error', logSystem, 'Block unlocker died, spawning replacement...'); + setTimeout(function () { + spawnBlockUnlocker(); + }, 2000); + }); + } + + /** + * Spawn payment processor module + **/ + function spawnPaymentProcessor () { + if (!config.payments || !config.payments.enabled) return; + + var worker = cluster.fork({ + workerType: 'paymentProcessor' + }); + worker.on('exit', function (code, signal) { + log('error', logSystem, 'Payment processor died, spawning replacement...'); + setTimeout(function () { + spawnPaymentProcessor(); + }, 2000); + }); + } + + /** + * Spawn API module + **/ + function spawnApi () { + if (!config.api || !config.api.enabled) return; + + var worker = cluster.fork({ + workerType: 'api' + }); + worker.on('exit', function (code, signal) { + log('error', logSystem, 'API died, spawning replacement...'); + setTimeout(function () { + spawnApi(); + }, 2000); + }); + } + + /** + * Spawn charts data collector module + **/ + function spawnChartsDataCollector () { + if (!config.charts) return; + + var worker = cluster.fork({ + workerType: 'chartsDataCollector' + }); + worker.on('exit', function (code, signal) { + log('error', logSystem, 'chartsDataCollector died, spawning replacement...'); + setTimeout(function () { + spawnChartsDataCollector(); + }, 2000); + }); + } + + /** + * Spawn telegram bot module + **/ + function spawnTelegramBot () { + if (!config.telegram || !config.telegram.enabled || !config.telegram.token) return; + + var worker = cluster.fork({ + workerType: 'telegramBot' + }); + worker.on('exit', function (code, signal) { + log('error', logSystem, 'telegramBot died, spawning replacement...'); + setTimeout(function () { + spawnTelegramBot(); + }, 2000); + }); + } diff --git a/package.json b/package.json index d24e146de..0a2a0f314 100644 --- a/package.json +++ b/package.json @@ -1,33 +1,31 @@ { - "name": "cryptonote-nodejs-pool", - "version": "1.4.0", - "license": "GPL-2.0", - "Original author": "Daniel Vandal", - "Maintained by": "Musclesonvacation", - "repository": { - "type": "git", - "url": "https://github.com/muscleman/cryptonote-nodejs-pool.git" - }, - "dependencies": { - "async": "1", - "base58-native": "*", - "bignum": "*", - "cli-color": "*", - "cryptoforknote-util": "https://github.com/Cideg/node-cryptoforknote-util.git", - "cryptonight-hashing": "git://github.com/MoneroOcean/node-cryptonight-hashing.git", - "multi-hashing": "git://github.com/muscleman/node-multi-hashing.git#ipbc", - "dateformat": "*", - "mailgun.js": "*", - "node-telegram-bot-api": "*", - "nodemailer": "2.7.2", - "nodemailer-sendmail-transport": "*", - "redis": "*", - "socket.io": "^2.1.1", - "time-ago": "*", - "request": "^2.88.0", - "request-promise-native": "^1.0.5" - }, - "engines": { - "node": ">=8.11.3" - } + "name": "cryptonote-nodejs-pool", + "version": "1.4.0", + "license": "GPL-2.0", + "Original author": "Daniel Vandal", + "Maintained by": "Musclesonvacation", + "repository": { + "type": "git", + "url": "https://github.com/muscleman/cryptonote-nodejs-pool.git" + }, + "dependencies": { + "async": "1", + "base58-native": "*", + "bignum": "*", + "cli-color": "*", + "cryptoforknote-util": "git://github.com/MoneroOcean/node-cryptoforknote-util.git", + "cryptonight-hashing": "git://github.com/MoneroOcean/node-cryptonight-hashing.git", + "dateformat": "*", + "mailgun.js": "*", + "node-telegram-bot-api": "*", + "nodemailer": "2.7.2", + "nodemailer-sendmail-transport": "*", + "redis": "*", + "socket.io": "^2.1.1", + "time-ago": "*", + "turtlecoin-multi-hashing": "https://github.com/turtlecoin/node8-multi-hashing.git" + }, + "engines": { + "node": ">=8.11.3" + } } From ea210b3aa4c0e851eb6ae947249fdf8f666ef087 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Sun, 25 Oct 2020 19:47:28 +0100 Subject: [PATCH 93/98] Delete config.js --- config.js | 315 ------------------------------------------------------ 1 file changed, 315 deletions(-) delete mode 100644 config.js diff --git a/config.js b/config.js deleted file mode 100644 index 1eefc45c8..000000000 --- a/config.js +++ /dev/null @@ -1,315 +0,0 @@ -{ - "poolHost": "infinium8.minercountry.com", - - "coin": "infinium8", - "symbol": "INF8", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 90, - - "childCoin": "monetaverde", - - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "mergedMining": true, - "clusterForks": 1, - "poolAddress": "**** YOUR INF8 WALLET ADDRESS *******", - "poolChildAddress": "**** YOUR MCN WALLET ADDRESS ********", - "intAddressPrefix": "", - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" - }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" - }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" - }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" - }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true - } - ], - "varDiff": { - "minDiff": 500, - "maxDiff": 100000000, - "targetTime": 45, - "retargetTime": 40, - "variancePercent": 30, - "maxJump": 60 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, - - "payments": { - "enabled": true, - "interval": 30, - "maxAddresses": 5, - "mixin": 3, - "priority": 0, - "transferFee": 10000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 10000000000000, - "maxPayment": null, - "maxTransactionAmount": 1000000000000000, - "denomination": 10000000000 - }, - - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.5, - "devDonation": 0, - "networkFee": 0.0 - }, - - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8999, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, - - "daemon": { - "host": "127.0.0.1", - "port": 27855 - }, - - "childDaemon": { - "host": "192.168.1.39", - "port": 26081 - }, - - "wallet": { - "host": "127.0.0.1", - "port": 27856 - }, - - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, - - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, - - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, - - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, - - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } -} From 695795e865c7354532507f5df39f899b8178cdae Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Sun, 25 Oct 2020 19:47:36 +0100 Subject: [PATCH 94/98] Delete config_inf8-mcn.json --- config_inf8-mcn.json | 315 ------------------------------------------- 1 file changed, 315 deletions(-) delete mode 100644 config_inf8-mcn.json diff --git a/config_inf8-mcn.json b/config_inf8-mcn.json deleted file mode 100644 index 1eefc45c8..000000000 --- a/config_inf8-mcn.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "poolHost": "infinium8.minercountry.com", - - "coin": "infinium8", - "symbol": "INF8", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 90, - - "childCoin": "monetaverde", - - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "mergedMining": true, - "clusterForks": 1, - "poolAddress": "**** YOUR INF8 WALLET ADDRESS *******", - "poolChildAddress": "**** YOUR MCN WALLET ADDRESS ********", - "intAddressPrefix": "", - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" - }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" - }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" - }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" - }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true - } - ], - "varDiff": { - "minDiff": 500, - "maxDiff": 100000000, - "targetTime": 45, - "retargetTime": 40, - "variancePercent": 30, - "maxJump": 60 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, - - "payments": { - "enabled": true, - "interval": 30, - "maxAddresses": 5, - "mixin": 3, - "priority": 0, - "transferFee": 10000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 10000000000000, - "maxPayment": null, - "maxTransactionAmount": 1000000000000000, - "denomination": 10000000000 - }, - - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.5, - "devDonation": 0, - "networkFee": 0.0 - }, - - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8999, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, - - "daemon": { - "host": "127.0.0.1", - "port": 27855 - }, - - "childDaemon": { - "host": "192.168.1.39", - "port": 26081 - }, - - "wallet": { - "host": "127.0.0.1", - "port": 27856 - }, - - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, - - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, - - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, - - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, - - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } -} From 56b4a9340b16bba498d23e7969ad537e2a1b5510 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Sun, 25 Oct 2020 19:48:30 +0100 Subject: [PATCH 95/98] Add files via upload --- lib/api.js | 3716 ++++++++++++++++++++---------------- lib/apiInterfaces.js | 186 +- lib/blockUnlocker.js | 489 ++--- lib/charts.js | 504 ++--- lib/chartsDataCollector.js | 12 +- lib/childDaemon.js | 84 + lib/configReader.js | 121 +- lib/daemon.js | 92 + lib/email.js | 98 +- lib/exceptionWriter.js | 26 +- lib/logger.js | 111 +- lib/market.js | 1064 +++++++---- lib/notifications.js | 416 ++-- lib/paymentProcessor.js | 519 ++--- lib/pool.js | 2421 ++++++++++++----------- lib/telegram.js | 96 +- lib/telegramBot.js | 499 ++--- lib/utils.js | 220 ++- 18 files changed, 5954 insertions(+), 4720 deletions(-) create mode 100644 lib/childDaemon.js create mode 100644 lib/daemon.js diff --git a/lib/api.js b/lib/api.js index 0df245f62..8f8cae5fc 100644 --- a/lib/api.js +++ b/lib/api.js @@ -6,1079 +6,1359 @@ **/ // Load required modules -var fs = require('fs'); -var http = require('http'); -var https = require('https'); -var url = require("url"); -var async = require('async'); +let fs = require('fs'); +let http = require('http'); +let https = require('https'); +let url = require("url"); +let async = require('async'); -var apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet); -var authSid = Math.round(Math.random() * 10000000000) + '' + Math.round(Math.random() * 10000000000); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet); +let authSid = Math.round(Math.random() * 10000000000) + '' + Math.round(Math.random() * 10000000000); -var charts = require('./charts.js'); -var notifications = require('./notifications.js'); -var market = require('./market.js'); -var utils = require('./utils.js'); +let charts = require('./charts.js'); +let notifications = require('./notifications.js'); +let market = require('./market.js'); +let utils = require('./utils.js'); // Initialize log system -var logSystem = 'api'; +let logSystem = 'api'; require('./exceptionWriter.js')(logSystem); // Data storage variables used for live statistics -var currentStats = {}; -var minerStats = {}; -var minersHashrate = {}; +let currentStats = {}; +let minerStats = {}; +let minersHashrate = {}; -var liveConnections = {}; -var addressConnections = {}; +let liveConnections = {}; +let addressConnections = {}; /** * Handle server requests **/ -function handleServerRequest(request, response) { - var urlParts = url.parse(request.url, true); - - switch(urlParts.pathname){ - // Pool statistics - case '/stats': - handleStats(urlParts, request, response); - break; - case '/live_stats': - response.writeHead(200, { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - 'Connection': 'keep-alive' - }); - - var address = urlParts.query.address ? urlParts.query.address : 'undefined'; - var uid = Math.random().toString(); - var key = address + ':' + uid; - - response.on("finish", function() { - delete liveConnections[key]; - }); - response.on("close", function() { - delete liveConnections[key]; - }); - - liveConnections[key] = response; - break; - - // Worker statistics - case '/stats_address': - handleMinerStats(urlParts, response); - break; - - // Payments - case '/get_payments': - handleGetPayments(urlParts, response); - break; - - // Blocks - case '/get_blocks': - handleGetBlocks(urlParts, response); - break; - - // Get market prices - case '/get_market': - handleGetMarket(urlParts, response); - break; - - // Top 10 miners - case '/get_top10miners': - handleTopMiners(response); - break; - - // Miner settings - case '/get_miner_payout_level': - handleGetMinerPayoutLevel(urlParts, response); - break; - case '/set_miner_payout_level': - handleSetMinerPayoutLevel(urlParts, response); - break; - case '/get_email_notifications': - handleGetMinerNotifications(urlParts, response); - break; - case '/set_email_notifications': - handleSetMinerNotifications(urlParts, response); - break; - case '/get_telegram_notifications': - handleGetTelegramNotifications(urlParts, response); - break; - case '/set_telegram_notifications': - handleSetTelegramNotifications(urlParts, response); - break; - - // Miners/workers hashrate (used for charts) - case '/miners_hashrate': - if (!authorize(request, response)) { - return; - } - handleGetMinersHashrate(response); - break; - case '/workers_hashrate': - if (!authorize(request, response)) { - return; - } - handleGetWorkersHashrate(response); - break; - - // Pool Administration - case '/admin_stats': - if (!authorize(request, response)) - return; - handleAdminStats(response); - break; - case '/admin_monitoring': - if (!authorize(request, response)) { - return; - } - handleAdminMonitoring(response); - break; - case '/admin_log': - if (!authorize(request, response)) { - return; - } - handleAdminLog(urlParts, response); - break; - case '/admin_users': - if (!authorize(request, response)) { - return; - } - handleAdminUsers(response); - break; - case '/admin_ports': - if (!authorize(request, response)) { - return; - } - handleAdminPorts(response); - break; - - // Test notifications +function handleServerRequest (request, response) { + let urlParts = url.parse(request.url, true); + + switch (urlParts.pathname) { + // Pool statistics + case '/stats': + handleStats(urlParts, request, response); + break; + case '/live_stats': + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Connection': 'keep-alive' + }); + + let address = urlParts.query.address ? urlParts.query.address : 'undefined'; + let uid = Math.random() + .toString(); + let key = address + ':' + uid; + + response.on("finish", function () { + delete liveConnections[key]; + }); + response.on("close", function () { + delete liveConnections[key]; + }); + + liveConnections[key] = response; + break; + + // Worker statistics + case '/stats_address': + handleMinerStats(urlParts, response); + break; + + // Payments + case '/get_payments': + handleGetPayments(urlParts, response); + break; + + // Blocks + case '/get_blocks': + handleGetBlocks(urlParts, response); + break; + + // Get market prices + case '/get_market': + handleGetMarket(urlParts, response); + break; + + // Top 10 miners + case '/get_top10miners': + handleTopMiners(response); + break; + + // Miner settings + case '/get_miner_payout_level': + handleGetMinerPayoutLevel(urlParts, response); + break; + case '/set_miner_payout_level': + handleSetMinerPayoutLevel(urlParts, response); + break; + case '/get_email_notifications': + handleGetMinerNotifications(urlParts, response); + break; + case '/set_email_notifications': + handleSetMinerNotifications(urlParts, response); + break; + case '/get_telegram_notifications': + handleGetTelegramNotifications(urlParts, response); + break; + case '/set_telegram_notifications': + handleSetTelegramNotifications(urlParts, response); + break; + case '/block_explorers': + handleBlockExplorers(response) + break + case '/get_apis': + handleGetApis(response) + break + // Miners/workers hashrate (used for charts) + case '/miners_hashrate': + if (!authorize(request, response)) { + return; + } + handleGetMinersHashrate(response); + break; + case '/workers_hashrate': + if (!authorize(request, response)) { + return; + } + handleGetWorkersHashrate(response); + break; + + // Pool Administration + case '/admin_stats': + if (!authorize(request, response)) + return; + handleAdminStats(response); + break; + case '/admin_monitoring': + if (!authorize(request, response)) { + return; + } + handleAdminMonitoring(response); + break; + case '/admin_log': + if (!authorize(request, response)) { + return; + } + handleAdminLog(urlParts, response); + break; + case '/admin_users': + if (!authorize(request, response)) { + return; + } + handleAdminUsers(request, response); + break; + case '/admin_ports': + if (!authorize(request, response)) { + return; + } + handleAdminPorts(request, response); + break; + + // Test notifications case '/test_email_notification': - if (!authorize(request, response)) { - return; - } - handleTestEmailNotification(urlParts, response); - break; - case '/test_telegram_notification': - if (!authorize(request, response)) { - return; - } - handleTestTelegramNotification(urlParts, response); - break; - - // Default response - default: - response.writeHead(404, { - 'Access-Control-Allow-Origin': '*' - }); - response.end('Invalid API call'); - break; - } + if (!authorize(request, response)) { + return; + } + handleTestEmailNotification(urlParts, response); + break; + case '/test_telegram_notification': + if (!authorize(request, response)) { + return; + } + handleTestTelegramNotification(urlParts, response); + break; + + // Default response + default: + response.writeHead(404, { + 'Access-Control-Allow-Origin': '*' + }); + response.end('Invalid API call'); + break; + } } /** * Collect statistics data **/ -function collectStats(){ - var startTime = Date.now(); - var redisFinished; - var daemonFinished; - - var redisCommands = [ - ['zremrangebyscore', config.coin + ':hashrate', '-inf', ''], - ['zrange', config.coin + ':hashrate', 0, -1], - ['hgetall', config.coin + ':stats'], - ['zrange', config.coin + ':blocks:candidates', 0, -1, 'WITHSCORES'], - ['zrevrange', config.coin + ':blocks:matured', 0, config.api.blocks - 1, 'WITHSCORES'], - ['hgetall', config.coin + ':scores:roundCurrent'], - ['hgetall', config.coin + ':stats'], - ['zcard', config.coin + ':blocks:matured'], - ['zrevrange', config.coin + ':payments:all', 0, config.api.payments - 1, 'WITHSCORES'], - ['zcard', config.coin + ':payments:all'], - ['keys', config.coin + ':payments:*'], - ['hgetall', config.coin + ':shares_actual:roundCurrent'], +function collectStats () { + let startTime = Date.now(); + let redisFinished; + let daemonFinished; + + let redisCommands = [ + ['zremrangebyscore', `${config.coin}:hashrate`, '-inf', ''], + ['zrange', `${config.coin}:hashrate`, 0, -1], + ['hgetall', `${config.coin}:stats`], + ['zrange', `${config.coin}:blocks:candidates`, 0, -1, 'WITHSCORES'], + ['zrevrange', `${config.coin}:blocks:matured`, 0, config.api.blocks - 1, 'WITHSCORES'], + ['hgetall', `${config.coin}:scores:prop:roundCurrent`], + ['hgetall', `${config.coin}:stats`], + // ['zcard', `${config.coin}:blocks:matured`], + ['zrevrange', `${config.coin}:payments:all`, 0, config.api.payments - 1, 'WITHSCORES'], + ['zcard', `${config.coin}:payments:all`], + ['keys', `${config.coin}:payments:*`], + ['hgetall', `${config.coin}:shares_actual:prop:roundCurrent`], + ['zrange', `${config.coin}:blocks:matured`, 0, -1, 'WITHSCORES'] ]; - var windowTime = (((Date.now() / 1000) - config.api.hashrateWindow) | 0).toString(); - redisCommands[0][3] = '(' + windowTime; - - async.parallel({ - pool: function(callback){ - redisClient.multi(redisCommands).exec(function(error, replies){ - redisFinished = Date.now(); - var dateNowSeconds = Date.now() / 1000 | 0; - - if (error){ - log('error', logSystem, 'Error getting redis data %j', [error]); - callback(true); - return; - } - - var data = { - stats: replies[2], - blocks: replies[3].concat(replies[4]), - totalBlocks: parseInt(replies[7]) + (replies[3].length / 2), - totalDiff: 0, - totalShares: 0, - payments: replies[8], - totalPayments: parseInt(replies[9]), - totalMinersPaid: replies[10] && replies[10].length > 0 ? replies[10].length - 1 : 0, - miners: 0, - workers: 0, - hashrate: 0, - roundScore: 0, - roundHashes: 0 - }; - - for (var i = 0; i < data.blocks.length; i++){ - var block = data.blocks[i].split(':'); - if (block[5]) { - var blockShares = parseInt(block[3]); - var blockDiff = parseInt(block[2]); - data.totalDiff += blockDiff; - data.totalShares += blockShares; - } - } - - minerStats = {}; - minersHashrate = {}; - - var hashrates = replies[1]; - for (var i = 0; i < hashrates.length; i++){ - var hashParts = hashrates[i].split(':'); - minersHashrate[hashParts[1]] = (minersHashrate[hashParts[1]] || 0) + parseInt(hashParts[0]); - } - - var totalShares = 0; - - for (var miner in minersHashrate){ - if (miner.indexOf('~') !== -1) { - data.workers ++; - } else { - totalShares += minersHashrate[miner]; - data.miners ++; - } - - minersHashrate[miner] = Math.round(minersHashrate[miner] / config.api.hashrateWindow); - - if (!minerStats[miner]) { minerStats[miner] = {}; } - minerStats[miner]['hashrate'] = minersHashrate[miner]; - } - - data.hashrate = Math.round(totalShares / config.api.hashrateWindow); - - data.roundScore = 0; - - if (replies[5]){ - for (var miner in replies[5]){ - var roundScore = parseFloat(replies[5][miner]); - - data.roundScore += roundScore; - - if (!minerStats[miner]) { minerStats[miner] = {}; } - minerStats[miner]['roundScore'] = roundScore; - } - } - - data.roundHashes = 0; - - if (replies[11]){ - for (var miner in replies[11]){ - var roundHashes = parseInt(replies[11][miner]) - data.roundHashes += roundHashes; - - if (!minerStats[miner]) { minerStats[miner] = {}; } - minerStats[miner]['roundHashes'] = roundHashes; - } - } - - if (replies[6]) { - data.lastBlockFound = replies[6].lastBlockFound; - } - - callback(null, data); - }); - }, - lastblock: function(callback){ - getLastBlockData(function(error, data) { - daemonFinished = Date.now(); - callback(error, data); - }); - }, - network: function(callback){ - getNetworkData(function(error, data) { - daemonFinished = Date.now(); - callback(error, data); - }); - }, - config: function(callback){ - callback(null, { - poolHost: config.poolHost || '', - ports: getPublicPorts(config.poolServer.ports), - cnAlgorithm: config.cnAlgorithm || 'cryptonight', - cnVariant: config.cnVariant || 0, - cnBlobType: config.cnBlobType || 0, - hashrateWindow: config.api.hashrateWindow, - fee: config.blockUnlocker.poolFee, - networkFee: config.blockUnlocker.networkFee || 0, - coin: config.coin, - coinUnits: config.coinUnits, - coinDecimalPlaces: config.coinDecimalPlaces || 4, // config.coinUnits.toString().length - 1, - coinDifficultyTarget: config.coinDifficultyTarget, - symbol: config.symbol, - depth: config.blockUnlocker.depth, - donation: donations, - version: version, - paymentsInterval: config.payments.interval, - minPaymentThreshold: config.payments.minPayment, - maxPaymentThreshold: config.payments.maxPayment || null, - transferFee: config.payments.transferFee, - denominationUnit: config.payments.denomination, - slushMiningEnabled: config.poolServer.slushMining.enabled, - weight: config.poolServer.slushMining.weight, - priceSource: config.prices ? config.prices.source : 'cryptonator', - priceCurrency: config.prices ? config.prices.currency : 'USD', - paymentIdSeparator: config.poolServer.paymentId && config.poolServer.paymentId.addressSeparator ? config.poolServer.paymentId.addressSeparator : ".", - fixedDiffEnabled: config.poolServer.fixedDiff.enabled, - fixedDiffSeparator: config.poolServer.fixedDiff.addressSeparator, - sendEmails: config.email ? config.email.enabled : false, - blocksChartEnabled: (config.charts.blocks && config.charts.blocks.enabled), - blocksChartDays: config.charts.blocks && config.charts.blocks.days ? config.charts.blocks.days : null, - telegramBotName: config.telegram && config.telegram.botName ? config.telegram.botName : null, - telegramBotStats: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.stats : "/stats", - telegramBotReport: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.report : "/report", - telegramBotNotify: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.notify : "/notify", - telegramBotBlocks: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.blocks : "/blocks" - }); - }, - charts: function (callback) { - // Get enabled charts data - charts.getPoolChartsData(function(error, data) { - if (error) { - callback(error, data); - return; - } - - // Blocks chart - if (!config.charts.blocks || !config.charts.blocks.enabled || !config.charts.blocks.days) { - callback(error, data); - return; - } - - var chartDays = config.charts.blocks.days; - - var beginAtTimestamp = (Date.now() / 1000) - (chartDays * 86400); - var beginAtDate = new Date(beginAtTimestamp * 1000); - if (chartDays > 1) { - beginAtDate = new Date(beginAtDate.getFullYear(), beginAtDate.getMonth(), beginAtDate.getDate(), 0, 0, 0, 0); - beginAtTimestamp = beginAtDate / 1000 | 0; - } - - var blocksCount = {}; - if (chartDays === 1) { - for (var h = 0; h <= 24; h++) { - var date = utils.dateFormat(new Date((beginAtTimestamp + (h * 60 * 60)) * 1000), 'yyyy-mm-dd HH:00'); - blocksCount[date] = 0; - } - } else { - for (var d = 0; d <= chartDays; d++) { - var date = utils.dateFormat(new Date((beginAtTimestamp + (d * 86400)) * 1000), 'yyyy-mm-dd'); - blocksCount[date] = 0; - } - } - - redisClient.zrevrange(config.coin + ':blocks:matured', 0, -1, 'WITHSCORES', function(err, result) { - for (var i = 0; i < result.length; i++){ - var block = result[i].split(':'); - if (block[5]) { - var blockTimestamp = block[1]; - if (blockTimestamp < beginAtTimestamp) { - continue; - } - var date = utils.dateFormat(new Date(blockTimestamp * 1000), 'yyyy-mm-dd'); - if (chartDays === 1) utils.dateFormat(new Date(blockTimestamp * 1000), 'yyyy-mm-dd HH:00'); - if (!blocksCount[date]) blocksCount[date] = 0; - blocksCount[date] ++; - } - } - data.blocks = blocksCount; - callback(error, data); - }); - }); - } - }, function(error, results){ - log('info', logSystem, 'Stat collection finished: %d ms redis, %d ms daemon', [redisFinished - startTime, daemonFinished - startTime]); + let windowTime = (((Date.now() / 1000) - config.api.hashrateWindow) | 0) + .toString(); + redisCommands[0][3] = '(' + windowTime; + + async.parallel({ + health: function (callback) { + let keys = [] + let data = {} + keys.push(config.coin) + let healthCommands = [] + healthCommands.push(['hget', `${config.coin}:status:daemon`, 'lastStatus']) + healthCommands.push(['hget', `${config.coin}:status:wallet`, 'lastStatus']) + healthCommands.push(['hget', `${config.coin}:status:price`, 'lastReponse']) + /* + config.childPools.forEach(pool => { + healthCommands.push(['hmget', `${pool.coin}:status:daemon`, 'lastStatus']) + healthCommands.push(['hmget', `${pool.coin}:status:wallet`, 'lastStatus']) + keys.push(pool.coin) + + }) + */ + redisClient.multi(healthCommands) + .exec(function (error, replies) { + + if (error) { + data = { + daemon: 'fail', + wallet: 'fail', + price: 'fail' + } + callback(null, data) + } + for (var i = 0, index = 0; i < keys.length; index += 2, i++) { + data[keys[i]] = { + daemon: replies[index], + wallet: replies[index + 1], + price: replies[index + 2] + } + + } + callback(null, data) + }) + }, + pool: function (callback) { + redisClient.multi(redisCommands) + .exec(function (error, replies) { + redisFinished = Date.now(); + let dateNowSeconds = Date.now() / 1000 | 0; + + if (error) { + log('error', logSystem, 'Error getting redis data %j', [error]); + callback(true); + return; + } + + let data = { + stats: replies[2], + blocks: truncateMinerAddress(replies[3].concat(replies[4])), + totalBlocks: 0, + totalBlocksSolo: 0, + totalDiff: 0, + totalDiffSolo: 0, + totalShares: 0, + totalSharesSolo: 0, + payments: replies[7], + totalPayments: parseInt(replies[8]), + totalMinersPaid: replies[9] && replies[9].length > 0 ? replies[9].length - 1 : 0, + miners: 0, + minersSolo: 0, + workers: 0, + workersSolo: 0, + hashrate: 0, + hashrateSolo: 0, + roundScore: 0, + roundHashes: 0 + }; + + calculateBlockData(data, replies[3].concat(replies[11])) + + minerStats = {}; + + minersHashrate = {}; + minersHashrateSolo = {}; + + minersHashrate = {}; + minersRewardType = {} + + let hashrates = replies[1]; + for (let i = 0; i < hashrates.length; i++) { + let hashParts = hashrates[i].split(':'); + minersHashrate[hashParts[1]] = (minersHashrate[hashParts[1]] || 0) + parseInt(hashParts[0]); + minersRewardType[hashParts[1]] = hashParts[3] + } + + let totalShares = 0 + let totalSharesSolo = 0 + + for (let miner in minersHashrate) { + if (minersRewardType[miner] === 'prop') { + if (miner.indexOf('~') !== -1) { + data.workers++; + totalShares += minersHashrate[miner]; + } else { + data.miners++; + } + } else if (minersRewardType[miner] === 'solo') { + if (miner.indexOf('~') !== -1) { + data.workersSolo++; + totalSharesSolo += minersHashrate[miner]; + } else { + data.minersSolo++; + } + } + minersHashrate[miner] = Math.round(minersHashrate[miner] / config.api.hashrateWindow); + if (!minerStats[miner]) { + minerStats[miner] = {}; + } + minerStats[miner]['hashrate'] = minersHashrate[miner]; + } + + + data.hashrate = Math.round(totalShares / config.api.hashrateWindow); + data.hashrateSolo = Math.round(totalSharesSolo / config.api.hashrateWindow); + + data.roundScore = 0; + + if (replies[5]) { + for (let miner in replies[5]) { + let roundScore = parseFloat(replies[5][miner]); + + data.roundScore += roundScore; + + if (!minerStats[miner]) { + minerStats[miner] = {}; + } + minerStats[miner]['roundScore'] = roundScore; + } + } + + data.roundHashes = 0; + + if (replies[10]) { + for (let miner in replies[10]) { + let roundHashes = parseInt(replies[10][miner]) + data.roundHashes += roundHashes; + + if (!minerStats[miner]) { + minerStats[miner] = {}; + } + minerStats[miner]['roundHashes'] = roundHashes; + } + } + + if (replies[6]) { + if (!replies[6].lastBlockFound || parseInt(replies[6].lastBlockFound) < parseInt(replies[6].lastBlockFoundprop)) + data.lastBlockFound = replies[6].lastBlockFoundprop; + else + data.lastBlockFound = replies[6].lastBlockFound + + if (replies[6].lastBlockFoundsolo) + data.lastBlockFoundSolo = replies[6].lastBlockFoundsolo; + } + + callback(null, data); + }); + }, + lastblock: function (callback) { + getLastBlockData(function (error, data) { + daemonFinished = Date.now(); + callback(error, data); + }); + }, + network: function (callback) { + getNetworkData(function (error, data) { + daemonFinished = Date.now(); + callback(error, data); + }); + }, + config: function (callback) { + callback(null, { + poolHost: config.poolHost || '', + ports: getPublicPorts(config.poolServer.ports), + cnAlgorithm: config.cnAlgorithm || 'cryptonight', + cnVariant: config.cnVariant || 0, + cnBlobType: config.cnBlobType || 0, + hashrateWindow: config.api.hashrateWindow, + fee: config.blockUnlocker.poolFee, + networkFee: config.blockUnlocker.networkFee || 0, + coin: config.coin, + coinUnits: config.coinUnits, + coinDecimalPlaces: config.coinDecimalPlaces || 4, // config.coinUnits.toString().length - 1, + coinDifficultyTarget: config.coinDifficultyTarget, + symbol: config.symbol, + depth: config.blockUnlocker.depth, + donation: donations, + version: version, + paymentsInterval: config.payments.interval, + minPaymentThreshold: config.payments.minPayment, + maxPaymentThreshold: config.payments.maxPayment || null, + transferFee: config.payments.transferFee, + denominationUnit: config.payments.denomination, + slushMiningEnabled: config.poolServer.slushMining.enabled, + weight: config.poolServer.slushMining.weight, + priceSource: config.prices ? config.prices.source : 'cryptonator', + priceCurrency: config.prices ? config.prices.currency : 'USD', + paymentIdSeparator: config.poolServer.paymentId && config.poolServer.paymentId.addressSeparator ? config.poolServer.paymentId.addressSeparator : ".", + fixedDiffEnabled: config.poolServer.fixedDiff.enabled, + fixedDiffSeparator: config.poolServer.fixedDiff.addressSeparator, + sendEmails: config.email ? config.email.enabled : false, + blocksChartEnabled: (config.charts.blocks && config.charts.blocks.enabled), + blocksChartDays: config.charts.blocks && config.charts.blocks.days ? config.charts.blocks.days : null, + telegramBotName: config.telegram && config.telegram.botName ? config.telegram.botName : null, + telegramBotStats: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.stats : "/stats", + telegramBotReport: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.report : "/report", + telegramBotNotify: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.notify : "/notify", + telegramBotBlocks: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.blocks : "/blocks" + }); + }, + charts: function (callback) { + // Get enabled charts data + charts.getPoolChartsData(function (error, data) { + if (error) { + callback(error, data); + return; + } + + // Blocks chart + if (!config.charts.blocks || !config.charts.blocks.enabled || !config.charts.blocks.days) { + callback(error, data); + return; + } + + let chartDays = config.charts.blocks.days; + + let beginAtTimestamp = (Date.now() / 1000) - (chartDays * 86400); + let beginAtDate = new Date(beginAtTimestamp * 1000); + if (chartDays > 1) { + beginAtDate = new Date(beginAtDate.getFullYear(), beginAtDate.getMonth(), beginAtDate.getDate(), 0, 0, 0, 0); + beginAtTimestamp = beginAtDate / 1000 | 0; + } + + let blocksCount = {}; + let blocksCountSolo = {}; + if (chartDays === 1) { + for (let h = 0; h <= 24; h++) { + let date = utils.dateFormat(new Date((beginAtTimestamp + (h * 60 * 60)) * 1000), 'yyyy-mm-dd HH:00'); + blocksCount[date] = 0; + blocksCountSolo[date] = 0 + } + } else { + for (let d = 0; d <= chartDays; d++) { + let date = utils.dateFormat(new Date((beginAtTimestamp + (d * 86400)) * 1000), 'yyyy-mm-dd'); + blocksCount[date] = 0; + blocksCountSolo[date] = 0 + } + } + + redisClient.zrevrange(config.coin + ':blocks:matured', 0, -1, 'WITHSCORES', function (err, result) { + for (let i = 0; i < result.length; i++) { + let block = result[i].split(':'); + if (block[0] === 'prop' || block[0] === 'solo') { + let blockTimestamp = block[3]; + if (blockTimestamp < beginAtTimestamp) { + continue; + } + let date = utils.dateFormat(new Date(blockTimestamp * 1000), 'yyyy-mm-dd'); + if (chartDays === 1) utils.dateFormat(new Date(blockTimestamp * 1000), 'yyyy-mm-dd HH:00'); + if (block[0] === 'prop') { + if (!blocksCount[date]) blocksCount[date] = 0; + blocksCount[date]++; + continue + } + if (block[0] === 'solo') { + if (!blocksCountSolo[date]) blocksCountSolo[date] = 0; + blocksCountSolo[date]++; + } + } else { + if (block[5]) { + let blockTimestamp = block[1]; + if (blockTimestamp < beginAtTimestamp) { + continue; + } + let date = utils.dateFormat(new Date(blockTimestamp * 1000), 'yyyy-mm-dd'); + if (chartDays === 1) utils.dateFormat(new Date(blockTimestamp * 1000), 'yyyy-mm-dd HH:00'); + if (!blocksCount[date]) blocksCount[date] = 0; + blocksCount[date]++; + } + } + } + data.blocks = blocksCount; + data.blocksSolo = blocksCountSolo; + callback(error, data); + }); + }); + } + }, function (error, results) { + log('info', logSystem, 'Stat collection finished: %d ms redis, %d ms daemon', [redisFinished - startTime, daemonFinished - startTime]); + + if (error) { + log('error', logSystem, 'Error collecting all stats'); + } else { + currentStats = results; + broadcastLiveStats(); + broadcastFinished = Date.now(); + log('info', logSystem, 'Stat collection broadcastLiveStats: %d ms', [broadcastFinished - startTime]); + } + + setTimeout(collectStats, config.api.updateInterval * 1000); + }); - if (error){ - log('error', logSystem, 'Error collecting all stats'); - } - else{ - currentStats = results; - broadcastLiveStats(); - } +} - setTimeout(collectStats, config.api.updateInterval * 1000); - }); +function truncateMinerAddress (blocks) { + for (let i = 0; i < blocks.length; i++) { + let block = blocks[i].split(':'); + if (block[0] === 'solo' || block[0] === 'prop') { + block[1] = `${block[1].substring(0,7)}...${block[1].substring(block[1].length-7)}` + blocks[i] = block.join(':') + } + } + return blocks +} +/** + * Calculate the Diff, shares and totalblocks + **/ +function calculateBlockData (data, blocks) { + for (let i = 0; i < blocks.length; i++) { + let block = blocks[i].split(':'); + if (block[0] === 'solo') { + data.totalDiffSolo += parseInt(block[4]) + data.totalSharesSolo += parseInt(block[5]) + data.totalBlocksSolo += 1 + } else if (block[0] === 'prop') { + data.totalDiff += parseInt(block[4]) + data.totalShares += parseInt(block[5]) + data.totalBlocks += 1 + } else { + if (block[5]) { + data.totalDiff += parseInt(block[2]) + data.totalShares += parseInt(block[3]) + data.totalBlocks += 1 + } + } + } } /** * Get Network data **/ -var networkDataRpcMode = 'get_info'; -function getNetworkData(callback, rpcMode) { - if (!rpcMode) rpcMode = networkDataRpcMode; - - // Try get_info RPC method first if available (not all coins support it) - if (rpcMode === 'get_info') { - apiInterfaces.rpcDaemon('get_info', {}, function(error, reply){ - if (error) { - getNetworkData(callback, 'getlastblockheader'); - return; - } else { - networkDataRpcMode = 'get_info'; - - callback(null, { - difficulty: reply.difficulty, - height: reply.height - }); - } - }); - } - - // Else fallback to getlastblockheader - else { - apiInterfaces.rpcDaemon('getlastblockheader', {}, function(error, reply){ - if (error){ - log('error', logSystem, 'Error getting network data %j', [error]); - callback(true); - return; - } else { - networkDataRpcMode = 'getlastblockheader'; - - var blockHeader = reply.block_header; - callback(null, { - difficulty: blockHeader.difficulty, - height: blockHeader.height + 1 - }); - } - }); - } +let networkDataRpcMode = 'get_info'; + +function getNetworkData (callback, rpcMode) { + if (!rpcMode) rpcMode = networkDataRpcMode; + + // Try get_info RPC method first if available (not all coins support it) + if (rpcMode === 'get_info') { + apiInterfaces.rpcDaemon('get_info', {}, function (error, reply) { + if (error) { + getNetworkData(callback, 'getlastblockheader'); + return; + } else { + networkDataRpcMode = 'get_info'; + + callback(null, { + difficulty: reply.difficulty, + height: reply.height + }); + } + }); + } + + // Else fallback to getlastblockheader + else { + apiInterfaces.rpcDaemon('getlastblockheader', {}, function (error, reply) { + if (error) { + log('error', logSystem, 'Error getting network data %j', [error]); + callback(true); + return; + } else { + networkDataRpcMode = 'getlastblockheader'; + + let blockHeader = reply.block_header; + callback(null, { + difficulty: blockHeader.difficulty, + height: blockHeader.height + 1 + }); + } + }); + } } /** * Get Last Block data **/ -function getLastBlockData(callback) { - apiInterfaces.rpcDaemon('getlastblockheader', {}, function(error, reply){ - if (error){ - log('error', logSystem, 'Error getting last block data %j', [error]); - callback(true); - return; - } - var blockHeader = reply.block_header; - if (config.blockUnlocker.useFirstVout) { - apiInterfaces.rpcDaemon('getblock', { height: blockHeader.height }, function(error, result) { - if (error) { - log('error', logSystem, 'Error getting last block details: %j', [error]); - callback(true); - return; - } - var vout = JSON.parse(result.json).miner_tx.vout; - if (!vout.length) { - log('error', logSystem, 'Error: tx at height %s has no vouts!', [blockHeader.height]); - callback(true); - return; - } - callback(null, { - difficulty: blockHeader.difficulty, - height: blockHeader.height, - timestamp: blockHeader.timestamp, - reward: vout[0].amount, - hash: blockHeader.hash - }); - }); - return; - } +function getLastBlockData (callback) { + apiInterfaces.rpcDaemon('getlastblockheader', {}, function (error, reply) { + if (error) { + log('error', logSystem, 'Error getting last block data %j', [error]); + callback(true); + return; + } + let blockHeader = reply.block_header; + if (config.blockUnlocker.useFirstVout) { + apiInterfaces.rpcDaemon('getblock', { + height: blockHeader.height + }, function (error, result) { + if (error) { + log('error', logSystem, 'Error getting last block details: %j', [error]); + callback(true); + return; + } + let vout = JSON.parse(result.json) + .miner_tx.vout; + if (!vout.length) { + log('error', logSystem, 'Error: tx at height %s has no vouts!', [blockHeader.height]); + callback(true); + return; + } + callback(null, { + difficulty: blockHeader.difficulty, + height: blockHeader.height, + timestamp: blockHeader.timestamp, + reward: vout[0].amount, + hash: blockHeader.hash + }); + }); + return; + } + callback(null, { + difficulty: blockHeader.difficulty, + height: blockHeader.height, + timestamp: blockHeader.timestamp, + reward: blockHeader.reward, + hash: blockHeader.hash + }); + }); +} - callback(null, { - difficulty: blockHeader.difficulty, - height: blockHeader.height, - timestamp: blockHeader.timestamp, - reward: blockHeader.reward, - hash: blockHeader.hash - }); - }); +function handleGetApis (callback) { + let apis = {} + config.childPools.forEach(pool => { + if (pool.enabled) + apis[pool.coin] = { + api: pool.api + } + }) + callback(apis) } /** * Broadcast live statistics **/ -function broadcastLiveStats(){ - log('info', logSystem, 'Broadcasting to %d visitors and %d address lookups', [Object.keys(liveConnections).length, Object.keys(addressConnections).length]); - - // Live statistics - var processAddresses = {}; - for (var key in liveConnections){ - var addrOffset = key.indexOf(':'); - var address = key.substr(0, addrOffset); - if (!processAddresses[address]) processAddresses[address] = []; - processAddresses[address].push(liveConnections[key]); - } - - for (var address in processAddresses) { - var data = currentStats; - - data.miner = {}; - if (address && minerStats[address]){ - data.miner = minerStats[address]; - } - - var destinations = processAddresses[address]; - sendLiveStats(data, destinations); - } - - // Workers Statistics - var processAddresses = {}; - for (var key in addressConnections){ - var addrOffset = key.indexOf(':'); - var address = key.substr(0, addrOffset); - if (!processAddresses[address]) processAddresses[address] = []; - processAddresses[address].push(addressConnections[key]); - } - - for (var address in processAddresses) { - broadcastWorkerStats(address, processAddresses[address]); - } +function broadcastLiveStats () { + log('info', logSystem, 'Broadcasting to %d visitors and %d address lookups', [Object.keys(liveConnections) + .length, Object.keys(addressConnections) + .length]); + + // Live statistics + let processAddresses = {}; + for (let key in liveConnections) { + let addrOffset = key.indexOf(':'); + let address = key.substr(0, addrOffset); + if (!processAddresses[address]) processAddresses[address] = []; + processAddresses[address].push(liveConnections[key]); + } + + for (let address in processAddresses) { + let data = currentStats; + + data.miner = {}; + if (address && minerStats[address]) { + data.miner = minerStats[address]; + } + + let destinations = processAddresses[address]; + sendLiveStats(data, destinations); + } + + // Workers Statistics + processAddresses = {}; + for (let key in addressConnections) { + let addrOffset = key.indexOf(':'); + let address = key.substr(0, addrOffset); + if (!processAddresses[address]) processAddresses[address] = []; + processAddresses[address].push(addressConnections[key]); + } + + for (let address in processAddresses) { + broadcastWorkerStats(address, processAddresses[address]); + } } /** * Takes a chart data JSON string and uses it to compute the average over the past hour, 6 hours, * and 24 hours. Returns [AVG1, AVG6, AVG24]. **/ -function extractAverageHashrates(chartdata) { - var now = new Date() / 1000 | 0; - - var sums = [0, 0, 0]; // 1h, 6h, 24h - var counts = [0, 0, 0]; - - var sets = JSON.parse(chartdata); // [time, avgValue, updateCount] - for (var j in sets) { - var hr = sets[j][1]; - if (now - sets[j][0] <= 1*60*60) { sums[0] += hr; counts[0]++; } - if (now - sets[j][0] <= 6*60*60) { sums[1] += hr; counts[1]++; } - if (now - sets[j][0] <= 24*60*60) { sums[2] += hr; counts[2]++; } - } - - return [sums[0] * 1.0 / (counts[0] || 1), sums[1] * 1.0 / (counts[1] || 1), sums[2] * 1.0 / (counts[2] || 1)]; +function extractAverageHashrates (chartdata) { + let now = new Date() / 1000 | 0; + + let sums = [0, 0, 0]; // 1h, 6h, 24h + let counts = [0, 0, 0]; + + let sets = chartdata ? JSON.parse(chartdata) : []; // [time, avgValue, updateCount] + for (let j in sets) { + let hr = sets[j][1]; + if (now - sets[j][0] <= 1 * 60 * 60) { + sums[0] += hr; + counts[0]++; + } + if (now - sets[j][0] <= 6 * 60 * 60) { + sums[1] += hr; + counts[1]++; + } + if (now - sets[j][0] <= 24 * 60 * 60) { + sums[2] += hr; + counts[2]++; + } + } + + return [sums[0] * 1.0 / (counts[0] || 1), sums[1] * 1.0 / (counts[1] || 1), sums[2] * 1.0 / (counts[2] || 1)]; } /** * Broadcast worker statistics **/ -function broadcastWorkerStats(address, destinations) { - var redisCommands = [ - ['hgetall', config.coin + ':workers:' + address], - ['zrevrange', config.coin + ':payments:' + address, 0, config.api.payments - 1, 'WITHSCORES'], - ['keys', config.coin + ':unique_workers:' + address + '~*'], - ['get', config.coin + ':charts:hashrate:' + address] +function broadcastWorkerStats (address, destinations) { + let redisCommands = [ + ['hgetall', `${config.coin}:workers:${address}`], + ['zrevrange', `${config.coin}:payments:${address}`, 0, config.api.payments - 1, 'WITHSCORES'], + ['keys', `${config.coin}:unique_workers:${address}~*`], + ['get', `${config.coin}:charts:hashrate:${address}`] ]; - redisClient.multi(redisCommands).exec(function(error, replies){ - if (error || !replies || !replies[0]){ - sendLiveStats({error: 'Not found'}, destinations); - return; - } - - var stats = replies[0]; - stats.hashrate = minerStats[address] && minerStats[address]['hashrate'] ? minerStats[address]['hashrate'] : 0; - stats.roundScore = minerStats[address] && minerStats[address]['roundScore'] ? minerStats[address]['roundScore'] : 0; - stats.roundHashes = minerStats[address] && minerStats[address]['roundHashes'] ? minerStats[address]['roundHashes'] : 0; - if (replies[3]) { - var hr_avg = extractAverageHashrates(replies[3]); - stats.hashrate_1h = hr_avg[0]; - stats.hashrate_6h = hr_avg[1]; - stats.hashrate_24h = hr_avg[2]; - } - - var paymentsData = replies[1]; - - var workersData = []; - for (var j=0; j { + if (pool.enabled) + apis[pool.coin] = { + api: pool.api + } + }) + callback(null, apis) + } + ], function (error, data) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting Api Information' + })); + return; + } + let reply = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(reply, 'utf8') + }); + response.end(reply); + }) +} + +/** + * Return top 10 miners + **/ +function handleBlockExplorers (response) { + async.waterfall([ + function (callback) { + let blockExplorers = {} + blockExplorers[config.coin] = { + "blockchainExplorer": config.blockchainExplorer, + "transactionExplorer": config.transactionExplorer + } + config.childPools.forEach(pool => { + if (pool.enabled) + blockExplorers[pool.coin] = { + "blockchainExplorer": pool.blockchainExplorer, + "transactionExplorer": pool.transactionExplorer + } + }) + callback(null, blockExplorers) + } + ], function (error, data) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting Block Explorer Information' + })); + return; + } + let reply = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(reply, 'utf8') + }); + response.end(reply); + }) } /** * Return top 10 miners **/ -function handleTopMiners(response) { - async.waterfall([ - function(callback) { - redisClient.keys(config.coin + ':workers:*', callback); +function handleTopMiners (response) { + async.waterfall([ + function (callback) { + redisClient.keys(`${config.coin}:workers:*`, callback); }, - function(workerKeys, callback) { - var redisCommands = workerKeys.map(function(k) { - return ['hmget', k, 'lastShare', 'hashes']; - }); - redisClient.multi(redisCommands).exec(function(error, redisData) { - var minersData = []; - for (var i in redisData) { - var keyParts = workerKeys[i].split(':'); - var address = keyParts[keyParts.length-1]; - var data = redisData[i]; - minersData.push({ - miner: address.substring(0,7)+'...'+address.substring(address.length-7), - hashrate: minersHashrate[address] && minerStats[address]['hashrate'] ? minersHashrate[address] : 0, - lastShare: data[0], - hashes: data[1] - }); - } - callback(null, minersData); - }); - } - ], function(error, data) { - if(error) { - response.end(JSON.stringify({error: 'Error collecting top 10 miners stats'})); - return; + function (workerKeys, callback) { + let redisCommands = workerKeys.map(function (k) { + return ['hmget', k, 'lastShare', 'hashes']; + }); + redisClient.multi(redisCommands) + .exec(function (error, redisData) { + let minersData = []; + let keyParts = []; + let address = ''; + let data = ''; + for (let i in redisData) { + keyParts = workerKeys[i].split(':'); + address = keyParts[keyParts.length - 1]; + data = redisData[i]; + minersData.push({ + miner: address.substring(0, 7) + '...' + address.substring(address.length - 7), + hashrate: minersHashrate[address] && minerStats[address]['hashrate'] ? minersHashrate[address] : 0, + lastShare: data[0], + hashes: data[1] + }); + } + callback(null, minersData); + }); } - - data.sort(compareTopMiners); - data = data.slice(0,10); - - var reply = JSON.stringify(data); - - response.writeHead("200", { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(reply, 'utf8') - }); - response.end(reply); - }); + ], function (error, data) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting top 10 miners stats' + })); + return; + } + + data.sort(compareTopMiners); + data = data.slice(0, 10); + + let reply = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(reply, 'utf8') + }); + response.end(reply); + }); } -function compareTopMiners(a,b) { - var v1 = a.hashrate ? parseInt(a.hashrate) : 0; - var v2 = b.hashrate ? parseInt(b.hashrate) : 0; - if (v1 > v2) return -1; - if (v1 < v2) return 1; - return 0; +function compareTopMiners (a, b) { + let v1 = a.hashrate ? parseInt(a.hashrate) : 0; + let v2 = b.hashrate ? parseInt(b.hashrate) : 0; + if (v1 > v2) return -1; + if (v1 < v2) return 1; + return 0; } /** * Miner settings: minimum payout level **/ - -// Get current minimum payout level -function handleGetMinerPayoutLevel(urlParts, response){ - response.writeHead(200, { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json' - }); - response.write('\n'); - - var address = urlParts.query.address; - - // Check the minimal required parameters for this handle. - if (address === undefined) { - response.end(JSON.stringify({status: 'Parameters are incomplete'})); - return; - } - - // Return current miner payout level - redisClient.hget(config.coin + ':workers:' + address, 'minPayoutLevel', function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'Unable to get the current minimum payout level from database'})); - return; - } - - var minLevel = config.payments.minPayment / config.coinUnits; - if (minLevel < 0) minLevel = 0; - - var maxLevel = config.payments.maxPayment ? config.payments.maxPayment / config.coinUnits : null; - var currentLevel = value / config.coinUnits; - if (currentLevel < minLevel) currentLevel = minLevel; - if (maxLevel && currentLevel > maxLevel) currentLevel = maxLevel; - - response.end(JSON.stringify({status: 'done', level: currentLevel})); - }); +// Get current minimum payout level +function handleGetMinerPayoutLevel (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let address = urlParts.query.address; + + // Check the minimal required parameters for this handle. + if (address === undefined) { + response.end(JSON.stringify({ + status: 'Parameters are incomplete' + })); + return; + } + + // Return current miner payout level + redisClient.hget(`${config.coin}:workers:${address}`, 'minPayoutLevel', function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to get the current minimum payout level from database' + })); + return; + } + + let minLevel = config.payments.minPayment / config.coinUnits; + if (minLevel < 0) minLevel = 0; + + let maxLevel = config.payments.maxPayment ? config.payments.maxPayment / config.coinUnits : null; + + let currentLevel = value / config.coinUnits; + if (currentLevel < minLevel) currentLevel = minLevel; + if (maxLevel && currentLevel > maxLevel) currentLevel = maxLevel; + + response.end(JSON.stringify({ + status: 'done', + level: currentLevel + })); + }); } // Set minimum payout level -function handleSetMinerPayoutLevel(urlParts, response){ - response.writeHead(200, { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json' - }); - response.write('\n'); - - var address = urlParts.query.address; - var ip = urlParts.query.ip; - var level = urlParts.query.level; - - // Check the minimal required parameters for this handle. - if (ip === undefined || address === undefined || level === undefined) { - response.end(JSON.stringify({status: 'Parameters are incomplete'})); - return; - } - - // Do not allow wildcards in the queries. - if (ip.indexOf('*') !== -1 || address.indexOf('*') !== -1) { - response.end(JSON.stringify({status: 'Remove the wildcard from your miner address'})); - return; - } - - level = parseFloat(level); - if (isNaN(level)) { - response.end(JSON.stringify({status: 'Your minimum payout level doesn\'t look like a number'})); - return; - } - - var minLevel = config.payments.minPayment / config.coinUnits; - if (minLevel < 0) minLevel = 0; - - var maxLevel = config.payments.maxPayment ? config.payments.maxPayment / config.coinUnits : null; - - if (level < minLevel) { - response.end(JSON.stringify({status: 'The minimum payout level is ' + minLevel})); - return; - } - - if (maxLevel && level > maxLevel) { - response.end(JSON.stringify({status: 'The maximum payout level is ' + maxLevel})); - return; - } - - // Only do a modification if we have seen the IP address in combination with the wallet address. - minerSeenWithIPForAddress(address, ip, function (error, found) { - if (!found || error) { - response.end(JSON.stringify({status: 'We haven\'t seen that IP for your address'})); - return; - } - - var payoutLevel = level * config.coinUnits; - redisClient.hset(config.coin + ':workers:' + address, 'minPayoutLevel', payoutLevel, function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'An error occurred when updating the value in our database'})); - return; - } - - log('info', logSystem, 'Updated minimum payout level for ' + address + ' to: ' + payoutLevel); - response.end(JSON.stringify({status: 'done'})); - }); - }); +function handleSetMinerPayoutLevel (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let address = urlParts.query.address; + let ip = urlParts.query.ip; + let level = urlParts.query.level; + + // Check the minimal required parameters for this handle. + if (ip === undefined || address === undefined || level === undefined) { + response.end(JSON.stringify({ + status: 'Parameters are incomplete' + })); + return; + } + + // Do not allow wildcards in the queries. + if (ip.indexOf('*') !== -1 || address.indexOf('*') !== -1) { + response.end(JSON.stringify({ + status: 'Remove the wildcard from your miner address' + })); + return; + } + + level = parseFloat(level); + if (isNaN(level)) { + response.end(JSON.stringify({ + status: 'Your minimum payout level doesn\'t look like a number' + })); + return; + } + + let minLevel = config.payments.minPayment / config.coinUnits; + if (minLevel < 0) minLevel = 0; + + let maxLevel = config.payments.maxPayment ? config.payments.maxPayment / config.coinUnits : null; + + if (level < minLevel) { + response.end(JSON.stringify({ + status: 'The minimum payout level is ' + minLevel + })); + return; + } + + if (maxLevel && level > maxLevel) { + response.end(JSON.stringify({ + status: 'The maximum payout level is ' + maxLevel + })); + return; + } + + // Only do a modification if we have seen the IP address in combination with the wallet address. + minerSeenWithIPForAddress(address, ip, function (error, found) { + if (!found || error) { + response.end(JSON.stringify({ + status: 'We haven\'t seen that IP for that wallet address in our record' + })); + return; + } + + let payoutLevel = level * config.coinUnits; + redisClient.hset(config.coin + ':workers:' + address, 'minPayoutLevel', payoutLevel, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'An error occurred when updating the value in our database' + })); + return; + } + + log('info', logSystem, 'Updated minimum payout level for ' + address + ' to: ' + payoutLevel); + response.end(JSON.stringify({ + status: 'done' + })); + }); + }); } /** @@ -1086,101 +1366,125 @@ function handleSetMinerPayoutLevel(urlParts, response){ **/ // Get destination for email notifications -function handleGetMinerNotifications(urlParts, response){ - response.writeHead(200, { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json' - }); - response.write('\n'); - - var address = urlParts.query.address; - - // Check the minimal required parameters for this handle. - if (address === undefined) { - response.end(JSON.stringify({status: 'Parameters are incomplete'})); - return; - } - - // Return current email for notifications - redisClient.hget(config.coin + ':notifications', address, function(error, value){ - if (error){ - response.end(JSON.stringify({'status': 'Unable to get current email from database'})); - return; - } - response.end(JSON.stringify({'status': 'done', 'email': value})); - }); +function handleGetMinerNotifications (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let address = urlParts.query.address; + + // Check the minimal required parameters for this handle. + if (address === undefined) { + response.end(JSON.stringify({ + status: 'Parameters are incomplete' + })); + return; + } + + // Return current email for notifications + redisClient.hget(`${config.coin}:notifications`, address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + 'status': 'Unable to get current email from database' + })); + return; + } + response.end(JSON.stringify({ + 'status': 'done', + 'email': value + })); + }); } // Set email notifications -function handleSetMinerNotifications(urlParts, response){ - response.writeHead(200, { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json' - }); - response.write('\n'); - - var email = urlParts.query.email; - var address = urlParts.query.address; - var ip = urlParts.query.ip; - var action = urlParts.query.action; - - // Check the minimal required parameters for this handle. - if (ip === undefined || address === undefined || action === undefined) { - response.end(JSON.stringify({status: 'Parameters are incomplete'})); - return; - } - - // Do not allow wildcards in the queries. - if (ip.indexOf('*') !== -1 || address.indexOf('*') !== -1) { - response.end(JSON.stringify({status: 'Remove the wildcard from your input'})); - return; - } - - // Check the action - if (action === undefined || action === '' || (action != 'enable' && action != 'disable')) { - response.end(JSON.stringify({status: 'Invalid action'})); - return; - } - - // Now only do a modification if we have seen the IP address in combination with the wallet address. - minerSeenWithIPForAddress(address, ip, function (error, found) { - if (!found || error) { - response.end(JSON.stringify({status: 'We haven\'t seen that IP for your address'})); - return; - } - - if (action === "enable") { - if (email === undefined) { - response.end(JSON.stringify({status: 'No email address specified'})); - return; - } - redisClient.hset(config.coin + ':notifications', address, email, function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'Unable to add email address in database'})); - return; - } - - log('info', logSystem, 'Enable email notifications to ' + email + ' for address: ' + address); - notifications.sendToMiner(address, 'emailAdded', { - 'ADDRESS': address, - 'EMAIL': email - }); - }); - response.end(JSON.stringify({status: 'done'})); - } - else if (action === "disable") { - redisClient.hdel(config.coin + ':notifications', address, function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'Unable to remove email address from database'})); - return; - } - log('info', logSystem, 'Disabled email notifications for address: ' + address); - }); - response.end(JSON.stringify({status: 'done'})); - } - }); +function handleSetMinerNotifications (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let email = urlParts.query.email; + let address = urlParts.query.address; + let ip = urlParts.query.ip; + let action = urlParts.query.action; + + // Check the minimal required parameters for this handle. + if (ip === undefined || address === undefined || action === undefined) { + response.end(JSON.stringify({ + status: 'Parameters are incomplete' + })); + return; + } + + // Do not allow wildcards in the queries. + if (ip.indexOf('*') !== -1 || address.indexOf('*') !== -1) { + response.end(JSON.stringify({ + status: 'Remove the wildcard from your input' + })); + return; + } + + // Check the action + if (action === undefined || action === '' || (action != 'enable' && action != 'disable')) { + response.end(JSON.stringify({ + status: 'Invalid action' + })); + return; + } + + // Now only do a modification if we have seen the IP address in combination with the wallet address. + minerSeenWithIPForAddress(address, ip, function (error, found) { + if (!found || error) { + response.end(JSON.stringify({ + status: 'We haven\'t seen that IP for your address' + })); + return; + } + + if (action === "enable") { + if (email === undefined) { + response.end(JSON.stringify({ + status: 'No email address specified' + })); + return; + } + redisClient.hset(config.coin + ':notifications', address, email, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to add email address in database' + })); + return; + } + + log('info', logSystem, 'Enable email notifications to ' + email + ' for address: ' + address); + notifications.sendToMiner(address, 'emailAdded', { + 'ADDRESS': address, + 'EMAIL': email + }); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } else if (action === "disable") { + redisClient.hdel(config.coin + ':notifications', address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to remove email address from database' + })); + return; + } + log('info', logSystem, 'Disabled email notifications for address: ' + address); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + }); } /** @@ -1188,497 +1492,623 @@ function handleSetMinerNotifications(urlParts, response){ **/ // Get destination for telegram notifications -function handleGetTelegramNotifications(urlParts, response){ - response.writeHead(200, { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json' - }); - response.write('\n'); - - var chatId = urlParts.query.chatId; - var address = urlParts.query.address; - var type = urlParts.query.type || 'miner'; - - if (chatId === undefined || chatId === '') { - response.end(JSON.stringify({status: 'No chat id specified'})); - return; - } - - // Default miner address - if (type == 'default') { - redisClient.hget(config.coin + ':telegram:default', chatId, function(error, value){ - if (error){ - response.end(JSON.stringify({'status': 'Unable to get current telegram default miner address from database'})); - return; - } - response.end(JSON.stringify({'status': 'done', 'address': value})); - }); - } - - // Blocks notification - if (type === 'blocks') { - redisClient.hget(config.coin + ':telegram:blocks', chatId, function(error, value){ - if (error){ - response.end(JSON.stringify({'status': 'Unable to get current telegram chat id from database'})); - return; - } - response.end(JSON.stringify({'status': 'done', 'enabled': +value})); - }); - } - - // Miner notification - if (type === 'miner') { - if (address === undefined || address === '') { - response.end(JSON.stringify({status: 'No miner address specified'})); - return; - } - - redisClient.hget(config.coin + ':telegram', address, function(error, value){ - if (error){ - response.end(JSON.stringify({'status': 'Unable to get current telegram chat id from database'})); - return; - } - response.end(JSON.stringify({'status': 'done', 'chatId': value})); - }); - } +function handleGetTelegramNotifications (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let chatId = urlParts.query.chatId; + let address = urlParts.query.address; + let type = urlParts.query.type || 'miner'; + + if (chatId === undefined || chatId === '') { + response.end(JSON.stringify({ + status: 'No chat id specified' + })); + return; + } + + // Default miner address + if (type == 'default') { + redisClient.hget(config.coin + ':telegram:default', chatId, function (error, value) { + if (error) { + response.end(JSON.stringify({ + 'status': 'Unable to get current telegram default miner address from database' + })); + return; + } + response.end(JSON.stringify({ + 'status': 'done', + 'address': value + })); + }); + } + + // Blocks notification + if (type === 'blocks') { + redisClient.hget(config.coin + ':telegram:blocks', chatId, function (error, value) { + if (error) { + response.end(JSON.stringify({ + 'status': 'Unable to get current telegram chat id from database' + })); + return; + } + response.end(JSON.stringify({ + 'status': 'done', + 'enabled': +value + })); + }); + } + + // Miner notification + if (type === 'miner') { + if (address === undefined || address === '') { + response.end(JSON.stringify({ + status: 'No miner address specified' + })); + return; + } + + redisClient.hget(config.coin + ':telegram', address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + 'status': 'Unable to get current telegram chat id from database' + })); + return; + } + response.end(JSON.stringify({ + 'status': 'done', + 'chatId': value + })); + }); + } } // Enable/disable telegram notifications -function handleSetTelegramNotifications(urlParts, response){ - response.writeHead(200, { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json' - }); - response.write('\n'); - - var chatId = urlParts.query.chatId; - var type = urlParts.query.type || 'miner'; - var action = urlParts.query.action; - var address = urlParts.query.address; - - // Check chat id - if (chatId === undefined || chatId === '') { - response.end(JSON.stringify({status: 'No chat id specified'})); - return; - } - - // Check action - if (type !== 'default' && (action === undefined || action === '' || (action != 'enable' && action != 'disable'))) { - response.end(JSON.stringify({status: 'Invalid action'})); - return; - } - - // Default miner address - if (type == 'default') { - if (address === undefined || address === '') { - response.end(JSON.stringify({status: 'No miner address specified'})); - return; - } - - redisClient.hset(config.coin + ':telegram:default', chatId, address, function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'Unable to set default telegram miner address'})); - return; - } - }); - response.end(JSON.stringify({status: 'done'})); - } - - // Blocks notification - if (type === 'blocks') { - // Enable - if (action === "enable") { - redisClient.hset(config.coin + ':telegram:blocks', chatId, 1, function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'Unable to enable telegram notifications'})); - return; - } - - log('info', logSystem, 'Enabled telegram notifications for blocks to ' + chatId); - }); - response.end(JSON.stringify({status: 'done'})); - } - - // Disable - else if (action === "disable") { - redisClient.hdel(config.coin + ':telegram:blocks', chatId, function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'Unable to disable telegram notifications'})); - return; - } - log('info', logSystem, 'Disabled telegram notifications for blocks to ' + chatId); - }); - response.end(JSON.stringify({status: 'done'})); - } - } - - // Miner notification - if (type === 'miner') { - if (address === undefined || address === '') { - response.end(JSON.stringify({status: 'No miner address specified'})); - return; - } - - redisClient.exists(config.coin + ':workers:' + address, function(error, result){ - if (!result){ - response.end(JSON.stringify({status: 'Miner not found in database'})); - return; - } - - // Enable - if (action === "enable") { - redisClient.hset(config.coin + ':telegram', address, chatId, function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'Unable to enable telegram notifications'})); - return; - } - log('info', logSystem, 'Enabled telegram notifications to ' + chatId + ' for address: ' + address); - }); - response.end(JSON.stringify({status: 'done'})); - } - - // Disable - else if (action === "disable") { - redisClient.hdel(config.coin + ':telegram', address, function(error, value){ - if (error){ - response.end(JSON.stringify({status: 'Unable to disable telegram notifications'})); - return; - } - log('info', logSystem, 'Disabled telegram notifications for address: ' + address); - }); - response.end(JSON.stringify({status: 'done'})); - } - }); - } +function handleSetTelegramNotifications (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let chatId = urlParts.query.chatId; + let type = urlParts.query.type || 'miner'; + let action = urlParts.query.action; + let address = urlParts.query.address; + + // Check chat id + if (chatId === undefined || chatId === '') { + response.end(JSON.stringify({ + status: 'No chat id specified' + })); + return; + } + + // Check action + if (type !== 'default' && (action === undefined || action === '' || (action != 'enable' && action != 'disable'))) { + response.end(JSON.stringify({ + status: 'Invalid action' + })); + return; + } + + // Default miner address + if (type == 'default') { + if (address === undefined || address === '') { + response.end(JSON.stringify({ + status: 'No miner address specified' + })); + return; + } + + redisClient.hset(config.coin + ':telegram:default', chatId, address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to set default telegram miner address' + })); + return; + } + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + + // Blocks notification + if (type === 'blocks') { + // Enable + if (action === "enable") { + redisClient.hset(config.coin + ':telegram:blocks', chatId, 1, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to enable telegram notifications' + })); + return; + } + + log('info', logSystem, 'Enabled telegram notifications for blocks to ' + chatId); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + + // Disable + else if (action === "disable") { + redisClient.hdel(config.coin + ':telegram:blocks', chatId, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to disable telegram notifications' + })); + return; + } + log('info', logSystem, 'Disabled telegram notifications for blocks to ' + chatId); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + } + + // Miner notification + if (type === 'miner') { + if (address === undefined || address === '') { + response.end(JSON.stringify({ + status: 'No miner address specified' + })); + return; + } + + redisClient.exists(config.coin + ':workers:' + address, function (error, result) { + if (!result) { + response.end(JSON.stringify({ + status: 'Miner not found in database' + })); + return; + } + + // Enable + if (action === "enable") { + redisClient.hset(config.coin + ':telegram', address, chatId, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to enable telegram notifications' + })); + return; + } + log('info', logSystem, 'Enabled telegram notifications to ' + chatId + ' for address: ' + address); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + + // Disable + else if (action === "disable") { + redisClient.hdel(config.coin + ':telegram', address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to disable telegram notifications' + })); + return; + } + log('info', logSystem, 'Disabled telegram notifications for address: ' + address); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + }); + } } /** * Return miners hashrate **/ -function handleGetMinersHashrate(response) { - var data = {}; - for (var miner in minersHashrate){ - if (miner.indexOf('~') !== -1) continue; - data[miner] = minersHashrate[miner]; - } - - data = { - minersHashrate: data - } - - var reply = JSON.stringify(data); - - response.writeHead("200", { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(reply, 'utf8') - }); - response.end(reply); +function handleGetMinersHashrate (response) { + let data = {}; + for (let miner in minersHashrate) { + if (miner.indexOf('~') !== -1) continue; + data[miner] = minersHashrate[miner]; + } + + data = { + minersHashrate: data + } + + let reply = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(reply, 'utf8') + }); + response.end(reply); } /** * Return workers hashrate **/ -function handleGetWorkersHashrate(response) { - var data = {}; - for (var miner in minersHashrate){ - if (miner.indexOf('~') === -1) continue; - data[miner] = minersHashrate[miner]; - } - - var reply = JSON.stringify({ - workersHashrate: data - }); - - response.writeHead("200", { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - 'Content-Length': reply.length - }); - response.end(reply); +function handleGetWorkersHashrate (response) { + let data = {}; + for (let miner in minersHashrate) { + if (miner.indexOf('~') === -1) continue; + data[miner] = minersHashrate[miner]; + } + let reply = JSON.stringify({ + workersHashrate: data + }); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': reply.length + }); + response.end(reply); } /** * Authorize access to a secured API call **/ -function authorize(request, response){ - var sentPass = url.parse(request.url, true).query.password; - - var remoteAddress = request.connection.remoteAddress; - if(config.api.trustProxyIP && request.headers['x-forwarded-for']){ - remoteAddress = request.headers['x-forwarded-for']; - } - - var bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; - if (typeof sentPass == "undefined" && (remoteAddress === '127.0.0.1' || remoteAddress === '::ffff:127.0.0.1' || remoteAddress === '::1' || (bindIp != "0.0.0.0" && remoteAddress === bindIp))) { - return true; - } - - response.setHeader('Access-Control-Allow-Origin', '*'); - - var cookies = parseCookies(request); - if (typeof sentPass == "undefined" && cookies.sid && cookies.sid === authSid) { - return true; - } - - if (sentPass !== config.api.password){ - response.statusCode = 401; - response.end('Invalid password'); - return; - } - - log('warn', logSystem, 'Admin authorized from %s', [remoteAddress]); - response.statusCode = 200; - - var cookieExpire = new Date( new Date().getTime() + 60*60*24*1000); - response.setHeader('Set-Cookie', 'sid=' + authSid + '; path=/; expires=' + cookieExpire.toUTCString()); - response.setHeader('Cache-Control', 'no-cache'); - response.setHeader('Content-Type', 'application/json'); - - return true; +function authorize (request, response) { + let sentPass = url.parse(request.url, true) + .query.password; + + let remoteAddress = request.connection.remoteAddress; + if (config.api.trustProxyIP && request.headers['x-forwarded-for']) { + remoteAddress = request.headers['x-forwarded-for']; + } + + let bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; + if (typeof sentPass == "undefined" && (remoteAddress === '127.0.0.1' || remoteAddress === '::ffff:127.0.0.1' || remoteAddress === '::1' || (bindIp != "0.0.0.0" && remoteAddress === bindIp))) { + return true; + } + + response.setHeader('Access-Control-Allow-Origin', '*'); + + let cookies = parseCookies(request); + if (typeof sentPass == "undefined" && cookies.sid && cookies.sid === authSid) { + return true; + } + + if (sentPass !== config.api.password) { + response.statusCode = 401; + response.end('Invalid password'); + return; + } + + log('warn', logSystem, 'Admin authorized from %s', [remoteAddress]); + response.statusCode = 200; + + let cookieExpire = new Date(new Date() + .getTime() + 60 * 60 * 24 * 1000); + response.setHeader('Set-Cookie', 'sid=' + authSid + '; path=/; expires=' + cookieExpire.toUTCString()); + response.setHeader('Cache-Control', 'no-cache'); + response.setHeader('Content-Type', 'application/json'); + + return true; } /** * Administration: return pool statistics **/ -function handleAdminStats(response){ - async.waterfall([ +function handleAdminStats (response) { + async.waterfall([ //Get worker keys & unlocked blocks - function(callback){ - redisClient.multi([ - ['keys', config.coin + ':workers:*'], - ['zrange', config.coin + ':blocks:matured', 0, -1] - ]).exec(function(error, replies) { - if (error) { - log('error', logSystem, 'Error trying to get admin data from redis %j', [error]); - callback(true); - return; - } - callback(null, replies[0], replies[1]); - }); + function (callback) { + redisClient.multi([ + ['keys', `${config.coin}:workers:*`], + ['zrange', `${config.coin}:blocks:matured`, 0, -1] + ]) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error trying to get admin data from redis %j', [error]); + callback(true); + return; + } + callback(null, replies[0], replies[1]); + }); }, //Get worker balances - function(workerKeys, blocks, callback){ - var redisCommands = workerKeys.map(function(k){ - return ['hmget', k, 'balance', 'paid']; - }); - redisClient.multi(redisCommands).exec(function(error, replies){ - if (error){ - log('error', logSystem, 'Error with getting balances from redis %j', [error]); - callback(true); - return; - } - - callback(null, replies, blocks); - }); + function (workerKeys, blocks, callback) { + let redisCommands = workerKeys.map(function (k) { + return ['hmget', k, 'balance', 'paid']; + }); + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with getting balances from redis %j', [error]); + callback(true); + return; + } + + callback(null, replies, blocks); + }); }, - function(workerData, blocks, callback){ - var stats = { - totalOwed: 0, - totalPaid: 0, - totalRevenue: 0, - totalDiff: 0, - totalShares: 0, - blocksOrphaned: 0, - blocksUnlocked: 0, - totalWorkers: 0 - }; - - for (var i = 0; i < workerData.length; i++){ - stats.totalOwed += parseInt(workerData[i][0]) || 0; - stats.totalPaid += parseInt(workerData[i][1]) || 0; - stats.totalWorkers++; - } - - for (var i = 0; i < blocks.length; i++){ - var block = blocks[i].split(':'); - if (block[5]) { - stats.blocksUnlocked++; - stats.totalDiff += parseInt(block[2]); - stats.totalShares += parseInt(block[3]); - stats.totalRevenue += parseInt(block[5]); - } - else{ - stats.blocksOrphaned++; - } - } - callback(null, stats); + function (workerData, blocks, callback) { + let stats = { + totalOwed: 0, + totalPaid: 0, + totalRevenue: 0, + totalRevenueSolo: 0, + totalDiff: 0, + totalDiffSolo: 0, + totalShares: 0, + totalSharesSolo: 0, + blocksOrphaned: 0, + blocksUnlocked: 0, + blocksUnlockedSolo: 0, + totalWorkers: 0 + }; + + for (let i = 0; i < workerData.length; i++) { + stats.totalOwed += parseInt(workerData[i][0]) || 0; + stats.totalPaid += parseInt(workerData[i][1]) || 0; + stats.totalWorkers++; + } + + for (let i = 0; i < blocks.length; i++) { + let block = blocks[i].split(':'); + if (block[0] === 'prop' || block[0] === 'solo') { + if (block[7]) { + if (block[0] === 'solo') { + stats.blocksUnlockedSolo++ + stats.totalDiffSolo += parseInt(block[4]) + stats.totalSharesSolo += parseInt(block[5]) + stats.totalRevenueSolo += parseInt(block[7]) + } else { + stats.blocksUnlocked++ + stats.totalDiff += parseInt(block[4]) + stats.totalShares += parseInt(block[5]) + stats.totalRevenue += parseInt(block[7]) + } + } else { + stats.blocksOrphaned++ + } + } else { + if (block[5]) { + stats.blocksUnlocked++; + stats.totalDiff += parseInt(block[2]); + stats.totalShares += parseInt(block[3]); + stats.totalRevenue += parseInt(block[5]); + } else { + stats.blocksOrphaned++; + } + } + } + callback(null, stats); } - ], function(error, stats){ - if (error){ - response.end(JSON.stringify({error: 'Error collecting stats'})); - return; - } - response.end(JSON.stringify(stats)); - } - ); + ], function (error, stats) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting stats' + })); + return; + } + response.end(JSON.stringify(stats)); + }); } /** * Administration: users list **/ -function handleAdminUsers(response){ - async.waterfall([ +function handleAdminUsers (request, response) { + let otherCoin = url.parse(request.url, true) + .query.otherCoin; + async.waterfall([ // get workers Redis keys - function(callback) { - redisClient.keys(config.coin + ':workers:*', callback); + function (callback) { + redisClient.keys(`${config.coin}:workers:*`, callback); }, + // get workers data - function(workerKeys, callback) { - var redisCommands = workerKeys.map(function(k) { - return ['hmget', k, 'balance', 'paid', 'lastShare', 'hashes']; - }); - redisClient.multi(redisCommands).exec(function(error, redisData) { - var workersData = {}; - for(var i in redisData) { - var keyParts = workerKeys[i].split(':'); - var address = keyParts[keyParts.length-1]; - var data = redisData[i]; - workersData[address] = { - pending: data[0], - paid: data[1], - lastShare: data[2], - hashes: data[3], - hashrate: minerStats[address] && minerStats[address]['hashrate'] ? minerStats[address]['hashrate'] : 0, - roundScore: minerStats[address] && minerStats[address]['roundScore'] ? minerStats[address]['roundScore'] : 0, - roundHashes: minerStats[address] && minerStats[address]['roundHashes'] ? minerStats[address]['roundHashes'] : 0 - }; - } - callback(null, workersData); - }); - } - ], function(error, workersData) { - if(error) { - response.end(JSON.stringify({error: 'Error collecting users stats'})); - return; - } - response.end(JSON.stringify(workersData)); + function (workerKeys, callback) { + let allCoins = config.childPools + .filter(pool => pool.enabled) + .map(pool => { + return `${pool.coin}` + }) + + allCoins.push(otherCoin) + + let redisCommands = workerKeys.map(function (k) { + return ['hmget', k, 'balance', 'paid', 'lastShare', 'hashes', ...allCoins]; + }); + redisClient.multi(redisCommands) + .exec(function (error, redisData) { + let workersData = {} + let keyParts = [] + let address = '' + let data = [] + let wallet = '' + let coin = null + for (let i in redisData) { + keyParts = workerKeys[i].split(':'); + address = keyParts[keyParts.length - 1]; + data = redisData[i]; + + for (let a = 0, b = 4; b <= data.length; a++, b++) { + if (data[b]) { + coin = `${allCoins[a]}=${data[b]}` + break + } + } + + workersData[address] = { + pending: data[0], + paid: data[1], + lastShare: data[2], + hashes: data[3], + childWallet: coin, + hashrate: minerStats[address] && minerStats[address]['hashrate'] ? minerStats[address]['hashrate'] : 0, + roundScore: minerStats[address] && minerStats[address]['roundScore'] ? minerStats[address]['roundScore'] : 0, + roundHashes: minerStats[address] && minerStats[address]['roundHashes'] ? minerStats[address]['roundHashes'] : 0 + }; + } + callback(null, workersData); + }); } - ); + ], function (error, workersData) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting users stats' + })); + return; + } + response.end(JSON.stringify(workersData)); + }); } /** * Administration: pool monitoring **/ -function handleAdminMonitoring(response) { - response.writeHead("200", { - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json' - }); - async.parallel({ - monitoring: getMonitoringData, - logs: getLogFiles - }, function(error, result) { - response.end(JSON.stringify(result)); - }); +function handleAdminMonitoring (response) { + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + async.parallel({ + monitoring: getMonitoringData, + logs: getLogFiles + }, function (error, result) { + response.end(JSON.stringify(result)); + }); } /** * Administration: log file data **/ -function handleAdminLog(urlParts, response){ - var file = urlParts.query.file; - var filePath = config.logging.files.directory + '/' + file; - if(!file.match(/^\w+\.log$/)) { - response.end('wrong log file'); - } - response.writeHead(200, { - 'Content-Type': 'text/plain', - 'Cache-Control': 'no-cache', - 'Content-Length': fs.statSync(filePath).size - }); - fs.createReadStream(filePath).pipe(response); +function handleAdminLog (urlParts, response) { + let file = urlParts.query.file; + let filePath = config.logging.files.directory + '/' + file; + if (!file.match(/^\w+\.log$/)) { + response.end('wrong log file'); + } + response.writeHead(200, { + 'Content-Type': 'text/plain', + 'Cache-Control': 'no-cache', + 'Content-Length': fs.statSync(filePath) + .size + }); + fs.createReadStream(filePath) + .pipe(response); } /** * Administration: pool ports usage **/ -function handleAdminPorts(response){ - async.waterfall([ - function(callback) { - redisClient.keys(config.coin + ':ports:*', callback); +function handleAdminPorts (request, response) { + async.waterfall([ + function (callback) { + redisClient.keys(`${config.coin}:ports:*`, callback); }, - function(portsKeys, callback) { - var redisCommands = portsKeys.map(function(k) { - return ['hmget', k, 'port', 'users']; - }); - redisClient.multi(redisCommands).exec(function(error, redisData) { - var portsData = {}; - for (var i in redisData) { - var port = portsKeys[i]; - - var data = redisData[i]; - portsData[port] = { - port: data[0], - users: data[1] - }; - } - callback(null, portsData); - }); - } - ], function(error, portsData) { - if(error) { - response.end(JSON.stringify({error: 'Error collecting Ports stats'})); - return; + function (portsKeys, callback) { + let redisCommands = portsKeys.map(function (k) { + return ['hmget', k, 'port', 'users']; + }); + redisClient.multi(redisCommands) + .exec(function (error, redisData) { + let portsData = {} + let port = '' + let data = [] + for (let i in redisData) { + port = portsKeys[i]; + + data = redisData[i]; + portsData[port] = { + port: data[0], + users: data[1] + }; + } + callback(null, portsData); + }); } - response.end(JSON.stringify(portsData)); - }); + ], function (error, portsData) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting Ports stats' + })); + return; + } + response.end(JSON.stringify(portsData)); + }); } /** * Administration: test email notification **/ -function handleTestEmailNotification(urlParts, response) { - var email = urlParts.query.email; - if (!config.email) { - response.end(JSON.stringify({status: 'Email system is not configured'})); - return; - } - if (!config.email.enabled) { - response.end(JSON.stringify({status: 'Email system is not enabled'})); - return; - } - if (!email) { - response.end(JSON.stringify({status: 'No email specified'})); - return; - } - log('info', logSystem, 'Sending test e-mail notification to %s', [email]); - notifications.sendToEmail(email, 'test', {}); - response.end(JSON.stringify({status: 'done'})); +function handleTestEmailNotification (urlParts, response) { + let email = urlParts.query.email; + if (!config.email) { + response.end(JSON.stringify({ + status: 'Email system is not configured' + })); + return; + } + if (!config.email.enabled) { + response.end(JSON.stringify({ + status: 'Email system is not enabled' + })); + return; + } + if (!email) { + response.end(JSON.stringify({ + status: 'No email specified' + })); + return; + } + log('info', logSystem, 'Sending test e-mail notification to %s', [email]); + notifications.sendToEmail(email, 'test', {}); + response.end(JSON.stringify({ + status: 'done' + })); } /** * Administration: test telegram notification **/ -function handleTestTelegramNotification(urlParts, response) { - if (!config.telegram) { - response.end(JSON.stringify({status: 'Telegram is not configured'})); - return; - } - if (!config.telegram.enabled) { - response.end(JSON.stringify({status: 'Telegram is not enabled'})); - return; - } - if (!config.telegram.token) { - response.end(JSON.stringify({status: 'No telegram bot token specified in configuration'})); - return; - } - if (!config.telegram.channel) { - response.end(JSON.stringify({status: 'No telegram channel specified in configuration'})); - return; - } - log('info', logSystem, 'Sending test telegram channel notification'); - notifications.sendToTelegramChannel('test', {}); - response.end(JSON.stringify({status: 'done'})); +function handleTestTelegramNotification (urlParts, response) { + if (!config.telegram) { + response.end(JSON.stringify({ + status: 'Telegram is not configured' + })); + return; + } + if (!config.telegram.enabled) { + response.end(JSON.stringify({ + status: 'Telegram is not enabled' + })); + return; + } + if (!config.telegram.token) { + response.end(JSON.stringify({ + status: 'No telegram bot token specified in configuration' + })); + return; + } + if (!config.telegram.channel) { + response.end(JSON.stringify({ + status: 'No telegram channel specified in configuration' + })); + return; + } + log('info', logSystem, 'Sending test telegram channel notification'); + notifications.sendToTelegramChannel('test', {}); + response.end(JSON.stringify({ + status: 'done' + })); } /** @@ -1686,122 +2116,144 @@ function handleTestTelegramNotification(urlParts, response) { **/ // Start RPC monitoring -function startRpcMonitoring(rpc, module, method, interval) { - setInterval(function() { - rpc(method, {}, function(error, response) { - var stat = { - lastCheck: new Date() / 1000 | 0, - lastStatus: error ? 'fail' : 'ok', - lastResponse: JSON.stringify(error ? error : response) - }; - if(error) { - stat.lastFail = stat.lastCheck; - stat.lastFailResponse = stat.lastResponse; - } - var key = getMonitoringDataKey(module); - var redisCommands = []; - for(var property in stat) { - redisCommands.push(['hset', key, property, stat[property]]); - } - redisClient.multi(redisCommands).exec(); - }); - }, interval * 1000); +function startRpcMonitoring (rpc, module, method, interval) { + setInterval(function () { + rpc(method, {}, function (error, response) { + let stat = { + lastCheck: new Date() / 1000 | 0, + lastStatus: error ? 'fail' : 'ok', + lastResponse: JSON.stringify(error ? error : response) + }; + if (error) { + stat.lastFail = stat.lastCheck; + stat.lastFailResponse = stat.lastResponse; + } + let key = getMonitoringDataKey(module); + let redisCommands = []; + for (let property in stat) { + redisCommands.push(['hset', key, property, stat[property]]); + } + redisClient.multi(redisCommands) + .exec(); + }); + }, interval * 1000); } +// function startPriceMonitoring(rpc, module, method, endPoint, interval, coin) { +// setInterval(function() { +// let tickers = ['ARQ-BTC', 'ARQ-LTC', 'ARQ-USD', 'ARQ-EUR', 'ARQ-CAD'] +// let exchange = config.prices.source; +// market.get(exchange, tickers, function(data) { +// redisClient.set(`${config.coin}:status:prices`, JSON.stringify(data)) +// }); +// }, interval * 1000); +// } + // Return monitoring data key -function getMonitoringDataKey(module) { - return config.coin + ':status:' + module; +function getMonitoringDataKey (module) { + return config.coin + ':status:' + module; } // Initialize monitoring -function initMonitoring() { - var modulesRpc = { - daemon: apiInterfaces.rpcDaemon, - wallet: apiInterfaces.rpcWallet - }; - var daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; - for(var module in config.monitoring) { - var settings = config.monitoring[module]; - if (daemonType === "bytecoin" && module === "wallet" && settings.rpcMethod === "getbalance") { - settings.rpcMethod = "getBalance"; - } - if(settings.checkInterval) { - startRpcMonitoring(modulesRpc[module], module, settings.rpcMethod, settings.checkInterval); - } - } +function initMonitoring () { + let modulesRpc = { + daemon: apiInterfaces.rpcDaemon, + wallet: apiInterfaces.rpcWallet, + price: apiInterfaces.jsonHttpRequest + }; + let daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; + let settings = ''; + for (let module in config.monitoring) { + settings = config.monitoring[module]; + // if (module === "price") { + // startPriceMonitoring(modulesRpc[module], module, settings.rpcMethod, settings.checkInterval, settings.tickers ) + // break + // } + if (daemonType === "bytecoin" && module === "wallet" && settings.rpcMethod === "getbalance") { + settings.rpcMethod = "getBalance"; + } + if (settings.checkInterval) { + startRpcMonitoring(modulesRpc[module], module, settings.rpcMethod, settings.checkInterval); + } + } } // Get monitoring data -function getMonitoringData(callback) { - var modules = Object.keys(config.monitoring); - var redisCommands = []; - for(var i in modules) { - redisCommands.push(['hgetall', getMonitoringDataKey(modules[i])]) - } - redisClient.multi(redisCommands).exec(function(error, results) { - var stats = {}; - for(var i in modules) { - if(results[i]) { - stats[modules[i]] = results[i]; - } - } - callback(error, stats); - }); +function getMonitoringData (callback) { + let modules = Object.keys(config.monitoring); + let redisCommands = []; + for (let i in modules) { + redisCommands.push(['hgetall', getMonitoringDataKey(modules[i])]) + } + redisClient.multi(redisCommands) + .exec(function (error, results) { + let stats = {}; + for (let i in modules) { + if (results[i]) { + stats[modules[i]] = results[i]; + } + } + callback(error, stats); + }); } /** * Return pool public ports **/ -function getPublicPorts(ports){ - return ports.filter(function(port) { - return !port.hidden; - }); +function getPublicPorts (ports) { + return ports.filter(function (port) { + return !port.hidden; + }); } /** * Return list of pool logs file **/ -function getLogFiles(callback) { - var dir = config.logging.files.directory; - fs.readdir(dir, function(error, files) { - var logs = {}; - for(var i in files) { - var file = files[i]; - var stats = fs.statSync(dir + '/' + file); - logs[file] = { - size: stats.size, - changed: Date.parse(stats.mtime) / 1000 | 0 - } - } - callback(error, logs); - }); +function getLogFiles (callback) { + let dir = config.logging.files.directory; + fs.readdir(dir, function (error, files) { + let logs = {}; + let file = '' + let stats = ''; + for (let i in files) { + file = files[i]; + stats = fs.statSync(dir + '/' + file); + logs[file] = { + size: stats.size, + changed: Date.parse(stats.mtime) / 1000 | 0 + } + } + callback(error, logs); + }); } /** * Check if a miner has been seen with specified IP address **/ -function minerSeenWithIPForAddress(address, ip, callback) { - var ipv4_regex = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/; - if (ipv4_regex.test(ip)) { - ip = '::ffff:' + ip; - } - redisClient.sismember([config.coin + ':workers_ip:' + address, ip], function(error, result) { - var found = result > 0 ? true : false; - callback(error, found); - }); +function minerSeenWithIPForAddress (address, ip, callback) { + let ipv4_regex = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/; + if (ipv4_regex.test(ip)) { + ip = '::ffff:' + ip; + } + redisClient.sismember([`${config.coin}:workers_ip:${address}`, ip], function (error, result) { + let found = result > 0 ? true : false; + callback(error, found); + }); } /** * Parse cookies data **/ -function parseCookies(request) { - var list = {}, - rc = request.headers.cookie; - rc && rc.split(';').forEach(function(cookie) { - var parts = cookie.split('='); - list[parts.shift().trim()] = unescape(parts.join('=')); - }); - return list; +function parseCookies (request) { + let list = {}, + rc = request.headers.cookie; + rc && rc.split(';') + .forEach(function (cookie) { + let parts = cookie.split('='); + list[parts.shift() + .trim()] = unescape(parts.join('=')); + }); + return list; } /** * Start pool API @@ -1814,68 +2266,68 @@ collectStats(); initMonitoring(); // Enable to be bind to a certain ip or all by default -var bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; +let bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; // Start API on HTTP port -var server = http.createServer(function(request, response){ - if (request.method.toUpperCase() === "OPTIONS"){ - response.writeHead("204", "No Content", { - "access-control-allow-origin": '*', - "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", - "access-control-allow-headers": "content-type, accept", - "access-control-max-age": 10, // Seconds. - "content-length": 0 - }); - return(response.end()); - } - - handleServerRequest(request, response); +let server = http.createServer(function (request, response) { + if (request.method.toUpperCase() === "OPTIONS") { + response.writeHead("204", "No Content", { + "access-control-allow-origin": '*', + "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", + "access-control-allow-headers": "content-type, accept", + "access-control-max-age": 10, // Seconds. + "content-length": 0 + }); + return (response.end()); + } + + handleServerRequest(request, response); }); -server.listen(config.api.port, bindIp, function(){ - log('info', logSystem, 'API started & listening on %s port %d', [bindIp, config.api.port]); +server.listen(config.api.port, bindIp, function () { + log('info', logSystem, 'API started & listening on %s port %d', [bindIp, config.api.port]); }); // Start API on SSL port -if (config.api.ssl){ - if (!config.api.sslCert) { - log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate not configured', [bindIp, config.api.sslPort]); - } else if (!config.api.sslKey) { - log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL key not configured', [bindIp, config.api.sslPort]); - } else if (!config.api.sslCA) { - log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate authority not configured', [bindIp, config.api.sslPort]); - } else if (!fs.existsSync(config.api.sslCert)) { - log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate file not found (configuration error)', [bindIp, config.api.sslPort]); - } else if (!fs.existsSync(config.api.sslKey)) { - log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL key file not found (configuration error)', [bindIp, config.api.sslPort]); - } else if (!fs.existsSync(config.api.sslCA)) { - log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate authority file not found (configuration error)', [bindIp, config.api.sslPort]); - } else { - var options = { - key: fs.readFileSync(config.api.sslKey), - cert: fs.readFileSync(config.api.sslCert), - ca: fs.readFileSync(config.api.sslCA), - honorCipherOrder: true - }; - - var ssl_server = https.createServer(options, function(request, response){ - if (request.method.toUpperCase() === "OPTIONS"){ - response.writeHead("204", "No Content", { - "access-control-allow-origin": '*', - "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", - "access-control-allow-headers": "content-type, accept", - "access-control-max-age": 10, // Seconds. - "content-length": 0, - "strict-transport-security": "max-age=604800" - }); - return(response.end()); - } - - handleServerRequest(request, response); - }); - - ssl_server.listen(config.api.sslPort, bindIp, function(){ - log('info', logSystem, 'API started & listening on %s port %d (SSL)', [bindIp, config.api.sslPort]); - }); - } +if (config.api.ssl) { + if (!config.api.sslCert) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate not configured', [bindIp, config.api.sslPort]); + } else if (!config.api.sslKey) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL key not configured', [bindIp, config.api.sslPort]); + } else if (!config.api.sslCA) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate authority not configured', [bindIp, config.api.sslPort]); + } else if (!fs.existsSync(config.api.sslCert)) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate file not found (configuration error)', [bindIp, config.api.sslPort]); + } else if (!fs.existsSync(config.api.sslKey)) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL key file not found (configuration error)', [bindIp, config.api.sslPort]); + } else if (!fs.existsSync(config.api.sslCA)) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate authority file not found (configuration error)', [bindIp, config.api.sslPort]); + } else { + let options = { + key: fs.readFileSync(config.api.sslKey), + cert: fs.readFileSync(config.api.sslCert), + ca: fs.readFileSync(config.api.sslCA), + honorCipherOrder: true + }; + + let ssl_server = https.createServer(options, function (request, response) { + if (request.method.toUpperCase() === "OPTIONS") { + response.writeHead("204", "No Content", { + "access-control-allow-origin": '*', + "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", + "access-control-allow-headers": "content-type, accept", + "access-control-max-age": 10, // Seconds. + "content-length": 0, + "strict-transport-security": "max-age=604800" + }); + return (response.end()); + } + + handleServerRequest(request, response); + }); + + ssl_server.listen(config.api.sslPort, bindIp, function () { + log('info', logSystem, 'API started & listening on %s port %d (SSL)', [bindIp, config.api.sslPort]); + }); + } } diff --git a/lib/apiInterfaces.js b/lib/apiInterfaces.js index 929bd6958..7c09310e1 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -6,123 +6,115 @@ **/ // Load required modules -let http = require('http'); -let https = require('https'); -let request = require('request-promise-native'); +var http = require('http'); +var https = require('https'); -/** - * Send API request using JSON HTTP - **/ -function jsonHttpRequest(host, port, data, callback, path){ - try { - path = path || '/json_rpc'; - callback = callback || function(){}; - let options = { - 'uri': `${(port === 443 ? 'https' : 'http')}://${host}:${port}${path}`, - 'method': data ? 'POST' : 'GET', - 'headers' : { - 'Content-Length': data.length, - 'Content-Type': 'application/json', - 'Accept': 'application/json' - } - } - options['json'] = data +function jsonHttpRequest (host, port, data, callback, path) { + path = path || '/json_rpc'; + callback = callback || function () {}; + var options = { + hostname: host, + port: port, + path: path, + method: data ? 'POST' : 'GET', + headers: { + 'Content-Length': data.length, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }; + var req = (port === 443 ? https : http) + .request(options, function (res) { + var replyData = ''; + res.setEncoding('utf8'); + res.on('data', function (chunk) { + replyData += chunk; + }); + res.on('end', function () { + var replyJson; + try { + replyJson = replyData ? JSON.parse(replyData) : {}; + } catch (e) { + callback(e, {}); + return; + } + callback(null, replyJson); + }); + }); + + req.on('error', function (e) { + callback(e, {}); + }); - request(options) - .then(response => { - response = response ? response : {} - if (response instanceof Array || response instanceof Object) { - callback(null, response) - } else { - callback(null, JSON.parse(response)) - } - }) - .catch(error => { - callback(error, {}) - }) - } catch(error){ - console.log('catch ' , error) - callback(error, {}) - } + req.end(data); } /** * Send RPC request **/ -function rpc(host, port, method, params, callback, username, password){ - let payload = { - id: "0", - jsonrpc: "2.0", - method: method, - params: params - }; - if (password !== undefined) { - if (username !== undefined){ - payload['auth'] = {'user': username, 'password': password, 'sendImmediately':false} - payload['agent'] = new http.Agent({'keepAlive': true, 'maxSockets': 1}) - payload['forever'] = true - } else { - payload['password'] = password; - } - } - //let data = JSON.stringify(payload); - jsonHttpRequest(host, port, payload, function(error, replyJson){ - if (error){ - callback(error, {}); - return; - } - callback(replyJson.error, replyJson.result) - }); +function rpc (host, port, method, params, callback) { + var data = JSON.stringify({ + id: "0", + jsonrpc: "2.0", + method: method, + params: params + }); + jsonHttpRequest(host, port, data, function (error, replyJson) { + if (error) { + callback(error, {}); + return; + } + callback(replyJson.error, replyJson.result) + }); } /** * Send RPC requests in batch mode **/ -function batchRpc(host, port, array, callback){ - let rpcArray = []; - for (let i = 0; i < array.length; i++){ - rpcArray.push({ - id: i.toString(), - jsonrpc: "2.0", - method: array[i][0], - params: array[i][1] - }); - } - let data = JSON.stringify(rpcArray); - jsonHttpRequest(host, port, data, callback); +function batchRpc (host, port, array, callback) { + var rpcArray = []; + for (var i = 0; i < array.length; i++) { + rpcArray.push({ + id: i.toString(), + jsonrpc: "2.0", + method: array[i][0], + params: array[i][1] + }); + } + var data = JSON.stringify(rpcArray); + jsonHttpRequest(host, port, data, callback); } /** * Send RPC request to pool API **/ -function poolRpc(host, port, path, callback){ - jsonHttpRequest(host, port, '', callback, path); +function poolRpc (host, port, path, callback) { + jsonHttpRequest(host, port, '', callback, path); } /** * Exports API interfaces functions **/ -module.exports = function(daemonConfig, walletConfig, poolApiConfig){ - return { - batchRpcDaemon: function(batchArray, callback){ - batchRpc(daemonConfig.host, daemonConfig.port, batchArray, callback); - }, - rpcDaemon: function(method, params, callback, serverConfig){ - if (serverConfig) { - rpc(serverConfig.host, serverConfig.port, method, params, callback); - } else { - rpc(daemonConfig.host, daemonConfig.port, method, params, callback); - } - }, - rpcWallet: function(method, params, callback){ - rpc(walletConfig.host, walletConfig.port, method, params, callback, - walletConfig.user, walletConfig.pass); - }, - pool: function(path, callback){ - let bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; - let poolApi = (bindIp !== "0.0.0.0" ? poolApiConfig.bindIp : "127.0.0.1"); - poolRpc(poolApi, poolApiConfig.port, path, callback); - }, - jsonHttpRequest: jsonHttpRequest - } +module.exports = function (daemonConfig, walletConfig, poolApiConfig) { + return { + batchRpcDaemon: function (batchArray, callback) { + batchRpc(daemonConfig.host, daemonConfig.port, batchArray, callback); + }, + rpcDaemon: function (method, params, callback, serverConfig) { + if (serverConfig) { + rpc(serverConfig.host, serverConfig.port, method, params, callback); + } else { + rpc(daemonConfig.host, daemonConfig.port, method, params, callback); + } + }, + rpcWallet: function (method, params, callback) { + rpc(walletConfig.host, walletConfig.port, method, params, callback); + }, + pool: function (path, callback) { + var bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; + var poolApi = (bindIp !== "0.0.0.0" ? poolApiConfig.bindIp : "127.0.0.1"); + poolRpc(poolApi, poolApiConfig.port, path, callback); + }, + jsonHttpRequest: jsonHttpRequest + } }; diff --git a/lib/blockUnlocker.js b/lib/blockUnlocker.js index b76aed047..889d4d3fb 100644 --- a/lib/blockUnlocker.js +++ b/lib/blockUnlocker.js @@ -6,271 +6,298 @@ **/ // Load required modules -var async = require('async'); +let async = require('async'); -var apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); -var notifications = require('./notifications.js'); -var utils = require('./utils.js'); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api) +let notifications = require('./notifications.js') +let utils = require('./utils.js') -var slushMiningEnabled = config.poolServer.slushMining && config.poolServer.slushMining.enabled; +let slushMiningEnabled = config.poolServer.slushMining && config.poolServer.slushMining.enabled // Initialize log system -var logSystem = 'unlocker'; -require('./exceptionWriter.js')(logSystem); +let logSystem = 'unlocker' +require('./exceptionWriter.js')(logSystem) /** * Run block unlocker **/ - -log('info', logSystem, 'Started'); -function runInterval(){ - async.waterfall([ +log('info', logSystem, 'Started') + +function runInterval () { + async.waterfall([ // Get all block candidates in redis - function(callback){ - redisClient.zrange(config.coin + ':blocks:candidates', 0, -1, 'WITHSCORES', function(error, results){ - if (error){ - log('error', logSystem, 'Error trying to get pending blocks from redis %j', [error]); - callback(true); - return; - } - if (results.length === 0){ - log('info', logSystem, 'No blocks candidates in redis'); - callback(true); - return; - } - - var blocks = []; - - for (var i = 0; i < results.length; i += 2){ - var parts = results[i].split(':'); - blocks.push({ - serialized: results[i], - height: parseInt(results[i + 1]), - hash: parts[0], - time: parts[1], - difficulty: parts[2], - shares: parts[3], - score: parts.length >= 5 ? parts[4] : parts[3] - }); - } - - callback(null, blocks); - }); + function (callback) { + redisClient.zrange(config.coin + ':blocks:candidates', 0, -1, 'WITHSCORES', function (error, results) { + if (error) { + log('error', logSystem, 'Error trying to get pending blocks from redis %j', [error]) + callback(true) + return + } + if (results.length === 0) { + log('info', logSystem, 'No blocks candidates in redis') + callback(true) + return + } + + let blocks = [] + + for (let i = 0; i < results.length; i += 2) { + let parts = results[i].split(':') + blocks.push({ + serialized: results[i], + height: parseInt(results[i + 1]), + rewardType: parts[0], + login: parts[1], + hash: parts[2], + time: parts[3], + difficulty: parts[4], + shares: parts[5], + score: parts.length >= 7 ? parts[6] : parts[5] + }) + } + + callback(null, blocks) + }) }, // Check if blocks are orphaned - function(blocks, callback){ - async.filter(blocks, function(block, mapCback){ - var daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; - var blockHeight = (daemonType === "forknote" || daemonType === "bytecoin" || config.blockUnlocker.fixBlockHeightRPC) ? block.height + 1 : block.height; - var rpcmethod = config.blockUnlocker.useFirstVout ? 'getblock' : 'getblockheaderbyheight'; - apiInterfaces.rpcDaemon(rpcmethod, {height: blockHeight}, function(error, result){ - if (error){ - log('error', logSystem, 'Error with %s RPC request for block %s - %j', [rpcmethod, block.serialized, error]); - block.unlocked = false; - mapCback(); - return; - } - if (!result.block_header){ - log('error', logSystem, 'Error with %s, no details returned for %s - %j', [rpcmethod, block.serialized, result]); - block.unlocked = false; - mapCback(); - return; - } - var blockHeader = result.block_header; - block.orphaned = blockHeader.hash === block.hash ? 0 : 1; - block.unlocked = blockHeader.depth >= config.blockUnlocker.depth; - if (config.blockUnlocker.useFirstVout) { - var vout = JSON.parse(result.json).miner_tx.vout; - if (!vout.length) { - log('error', logSystem, 'Error: tx at height %s has no vouts!', [blockHeight]); - block.unlocked = false; - mapCback(); - return; - } - block.reward = vout[0].amount; - } else { - block.reward = blockHeader.reward; - } - if (config.blockUnlocker.networkFee) { - var networkFeePercent = config.blockUnlocker.networkFee / 100; - block.reward = block.reward - (block.reward * networkFeePercent); - } - mapCback(block.unlocked); - }); - }, function(unlockedBlocks){ - - if (unlockedBlocks.length === 0){ - log('info', logSystem, 'No pending blocks are unlocked yet (%d pending)', [blocks.length]); - callback(true); - return; - } - - callback(null, unlockedBlocks) - }) + function (blocks, callback) { + async.filter(blocks, function (block, mapCback) { + let daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default" + let blockHeight = ((daemonType === "forknote" || daemonType === "bytecoin") && config.blockUnlocker.fixBlockHeightRPC) ? block.height + 1 : block.height + let rpcMethod = config.blockUnlocker.useFirstVout ? 'getblock' : 'getblockheaderbyheight' + apiInterfaces.rpcDaemon(rpcMethod, { + height: blockHeight + }, function (error, result) { + if (error) { + log('error', logSystem, 'Error with %s RPC request for block %s - %j', [rpcMethod, block.serialized, error]) + block.unlocked = false + mapCback() + return + } + if (!result.block_header) { + log('error', logSystem, 'Error with getblockheaderbyheight RPC request for block %s - %j', [block.serialized, error]) + block.unlocked = false + mapCback() + return + } + let blockHeader = result.block_header + block.orphaned = blockHeader.hash === block.hash ? 0 : 1 + block.unlocked = blockHeader.depth >= config.blockUnlocker.depth + block.reward = blockHeader.reward + if (config.blockUnlocker.useFirstVout) { + let vout = JSON.parse(result.json) + .miner_tx.vout + if (!vout.length) { + log('error', logSystem, 'Error: tx at height %s has no vouts!', [blockHeight]) + block.unlocked = false + mapCback() + return + } + block.reward = vout[0].amount + } else { + block.reward = blockHeader.reward + } + if (config.blockUnlocker.networkFee) { + let networkFeePercent = config.blockUnlocker.networkFee / 100 + block.reward = block.reward - (block.reward * networkFeePercent) + } + mapCback(block.unlocked) + }) + }, function (unlockedBlocks) { + + if (unlockedBlocks.length === 0) { + log('info', logSystem, 'No pending blocks are unlocked yet (%d pending)', [blocks.length]) + callback(true) + return + } + + callback(null, unlockedBlocks) + }) }, // Get worker shares for each unlocked block - function(blocks, callback){ - - var redisCommands = blocks.map(function(block){ - return ['hgetall', config.coin + ':scores:round' + block.height]; - }); - - - redisClient.multi(redisCommands).exec(function(error, replies){ - if (error){ - log('error', logSystem, 'Error with getting round shares from redis %j', [error]); - callback(true); - return; - } - for (var i = 0; i < replies.length; i++){ - var workerScores = replies[i]; - blocks[i].workerScores = workerScores; - } - callback(null, blocks); - }); + function (blocks, callback) { + + let redisCommands = blocks.map(function (block) { + if (block.rewardType === 'prop') + return ['hgetall', config.coin + ':scores:prop:round' + block.height] + else + return ['hgetall', config.coin + ':scores:solo:round' + block.height] + }) + + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with getting round shares from redis %j', [error]) + callback(true) + return + } + for (let i = 0; i < replies.length; i++) { + let workerScores = replies[i] + blocks[i].workerScores = workerScores + } + callback(null, blocks) + }) }, // Handle orphaned blocks - function(blocks, callback){ - var orphanCommands = []; - - blocks.forEach(function(block){ - if (!block.orphaned) return; - - orphanCommands.push(['del', config.coin + ':scores:round' + block.height]); - orphanCommands.push(['del', config.coin + ':shares_actual:round' + block.height]); - - orphanCommands.push(['zrem', config.coin + ':blocks:candidates', block.serialized]); - orphanCommands.push(['zadd', config.coin + ':blocks:matured', block.height, [ + function (blocks, callback) { + let orphanCommands = [] + + blocks.forEach(function (block) { + if (!block.orphaned) return + + orphanCommands.push(['del', config.coin + ':scores:solo:round' + block.height]) + orphanCommands.push(['del', config.coin + ':scores:prop:round' + block.height]) + orphanCommands.push(['del', config.coin + ':shares_actual:solo:round' + block.height]) + orphanCommands.push(['del', config.coin + ':shares_actual:prop:round' + block.height]) + orphanCommands.push(['zrem', config.coin + ':blocks:candidates', block.serialized]) + orphanCommands.push(['zadd', config.coin + ':blocks:matured', block.height, [ + block.rewardType, + block.login, block.hash, block.time, block.difficulty, block.shares, block.orphaned - ].join(':')]); - - if (block.workerScores && !slushMiningEnabled) { - var workerScores = block.workerScores; - Object.keys(workerScores).forEach(function (worker) { - orphanCommands.push(['hincrby', config.coin + ':scores:roundCurrent', worker, workerScores[worker]]); - }); - } - - notifications.sendToAll('blockOrphaned', { - 'HEIGHT': block.height, - 'BLOCKTIME': utils.dateFormat(new Date(parseInt(block.time) * 1000), 'yyyy-mm-dd HH:MM:ss Z'), - 'HASH': block.hash, - 'DIFFICULTY': block.difficulty, - 'SHARES': block.shares, - 'EFFORT': Math.round(block.shares / block.difficulty * 100) + '%' - }); - }); - - if (orphanCommands.length > 0){ - redisClient.multi(orphanCommands).exec(function(error, replies){ - if (error){ - log('error', logSystem, 'Error with cleaning up data in redis for orphan block(s) %j', [error]); - callback(true); - return; - } - callback(null, blocks); - }); - } - else{ - callback(null, blocks); - } + ].join(':')]) + + if (block.workerScores && !slushMiningEnabled) { + let workerScores = block.workerScores + Object.keys(workerScores) + .forEach(function (worker) { + orphanCommands.push(['hincrby', config.coin + ':scores:roundCurrent', worker, workerScores[worker]]) + }) + } + + notifications.sendToAll('blockOrphaned', { + 'HEIGHT': block.height, + 'BLOCKTIME': utils.dateFormat(new Date(parseInt(block.time) * 1000), 'yyyy-mm-dd HH:MM:ss Z'), + 'HASH': block.hash, + 'DIFFICULTY': block.difficulty, + 'SHARES': block.shares, + 'EFFORT': Math.round(block.shares / block.difficulty * 100) + '%' + }) + }) + + if (orphanCommands.length > 0) { + redisClient.multi(orphanCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with cleaning up data in redis for orphan block(s) %j', [error]) + callback(true) + return + } + callback(null, blocks) + }) + } else { + callback(null, blocks) + } }, // Handle unlocked blocks - function(blocks, callback){ - var unlockedBlocksCommands = []; - var payments = {}; - var totalBlocksUnlocked = 0; - blocks.forEach(function(block){ - if (block.orphaned) return; - totalBlocksUnlocked++; - - unlockedBlocksCommands.push(['del', config.coin + ':scores:round' + block.height]); - unlockedBlocksCommands.push(['del', config.coin + ':shares_actual:round' + block.height]); - unlockedBlocksCommands.push(['zrem', config.coin + ':blocks:candidates', block.serialized]); - unlockedBlocksCommands.push(['zadd', config.coin + ':blocks:matured', block.height, [ + function (blocks, callback) { + let unlockedBlocksCommands = [] + let payments = {} + let totalBlocksUnlocked = 0 + blocks.forEach(function (block) { + if (block.orphaned) return + totalBlocksUnlocked++ + + unlockedBlocksCommands.push(['del', config.coin + ':scores:solo:round' + block.height]) + unlockedBlocksCommands.push(['del', config.coin + ':scores:prop:round' + block.height]) + unlockedBlocksCommands.push(['del', config.coin + ':shares_actual:solo:round' + block.height]) + unlockedBlocksCommands.push(['del', config.coin + ':shares_actual:prop:round' + block.height]) + unlockedBlocksCommands.push(['zrem', config.coin + ':blocks:candidates', block.serialized]) + unlockedBlocksCommands.push(['zadd', config.coin + ':blocks:matured', block.height, [ + block.rewardType, + block.login, block.hash, block.time, block.difficulty, block.shares, block.orphaned, block.reward - ].join(':')]); - - var feePercent = config.blockUnlocker.poolFee / 100; - - if (Object.keys(donations).length) { - for(var wallet in donations) { - var percent = donations[wallet] / 100; - feePercent += percent; - payments[wallet] = Math.round(block.reward * percent); - log('info', logSystem, 'Block %d donation to %s as %d percent of reward: %d', [block.height, wallet, percent, payments[wallet]]); - } - } - - var reward = Math.round(block.reward - (block.reward * feePercent)); - - log('info', logSystem, 'Unlocked %d block with reward %d and donation fee %d. Miners reward: %d', [block.height, block.reward, feePercent, reward]); - - if (block.workerScores) { - var totalScore = parseFloat(block.score); - Object.keys(block.workerScores).forEach(function (worker) { - var percent = block.workerScores[worker] / totalScore; - var workerReward = Math.round(reward * percent); - payments[worker] = (payments[worker] || 0) + workerReward; - log('info', logSystem, 'Block %d payment to %s for %d%% of total block score: %d', [block.height, worker, percent*100, payments[worker]]); - }); - } - - notifications.sendToAll('blockUnlocked', { - 'HEIGHT': block.height, - 'BLOCKTIME': utils.dateFormat(new Date(parseInt(block.time) * 1000), 'yyyy-mm-dd HH:MM:ss Z'), - 'HASH': block.hash, - 'REWARD': utils.getReadableCoins(block.reward), - 'DIFFICULTY': block.difficulty, - 'SHARES': block.shares, - 'EFFORT': Math.round(block.shares / block.difficulty * 100) + '%' - }); - }); - - for (var worker in payments) { - var amount = parseInt(payments[worker]); - if (amount <= 0){ - delete payments[worker]; - continue; - } - unlockedBlocksCommands.push(['hincrby', config.coin + ':workers:' + worker, 'balance', amount]); - } - - if (unlockedBlocksCommands.length === 0){ - log('info', logSystem, 'No unlocked blocks yet (%d pending)', [blocks.length]); - callback(true); - return; - } - - redisClient.multi(unlockedBlocksCommands).exec(function(error, replies){ - if (error){ - log('error', logSystem, 'Error with unlocking blocks %j', [error]); - callback(true); - return; - } - log('info', logSystem, 'Unlocked %d blocks and update balances for %d workers', [totalBlocksUnlocked, Object.keys(payments).length]); - callback(null); - }); + ].join(':')]) + + let feePercent = config.blockUnlocker.poolFee / 100 + + if (Object.keys(donations) + .length) { + for (let wallet in donations) { + let percent = donations[wallet] / 100 + feePercent += percent + payments[wallet] = Math.round(block.reward * percent) + log('info', logSystem, 'Block %d donation to %s as %d percent of reward: %d', [block.height, wallet, percent, payments[wallet]]) + } + } + + let reward = Math.round(block.reward - (block.reward * feePercent)) + + log('info', logSystem, 'Unlocked %d block with reward %d and donation fee %d. Miners reward: %d', [block.height, block.reward, feePercent, reward]) + + if (block.workerScores) { + let totalScore = parseFloat(block.score) + //deal with solo block + if (block.rewardType === 'solo') { + let worker = block.login + payments[worker] = (payments[worker] || 0) + reward + log('info', logSystem, 'SOLO Block %d payment to %s for %d%% of total block score: %d', [block.height, worker, 100, payments[worker]]) + } else { + Object.keys(block.workerScores) + .forEach(function (worker) { + let percent = block.workerScores[worker] / totalScore + let workerReward = Math.round(reward * percent) + payments[worker] = (payments[worker] || 0) + workerReward + log('info', logSystem, 'PROP Block %d payment to %s for %d%% of total block score: %d', [block.height, worker, percent * 100, payments[worker]]) + }) + } + } + + notifications.sendToAll('blockUnlocked', { + 'HEIGHT': block.height, + 'BLOCKTIME': utils.dateFormat(new Date(parseInt(block.time) * 1000), 'yyyy-mm-dd HH:MM:ss Z'), + 'HASH': block.hash, + 'REWARD': utils.getReadableCoins(block.reward), + 'DIFFICULTY': block.difficulty, + 'SHARES': block.shares, + 'EFFORT': Math.round(block.shares / block.difficulty * 100) + '%' + }) + }) + + for (let worker in payments) { + let amount = parseInt(payments[worker]) + if (amount <= 0) { + delete payments[worker] + continue + } + unlockedBlocksCommands.push(['hincrby', `${config.coin}:workers:${worker}`, 'balance', amount]) + } + + if (unlockedBlocksCommands.length === 0) { + log('info', logSystem, 'No unlocked blocks yet (%d pending)', [blocks.length]) + callback(true) + return + } + + redisClient.multi(unlockedBlocksCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with unlocking blocks %j', [error]) + callback(true) + return + } + log('info', logSystem, 'Unlocked %d blocks and update balances for %d workers', [totalBlocksUnlocked, Object.keys(payments) + .length]) + callback(null) + }) } - ], function(error, result){ - setTimeout(runInterval, config.blockUnlocker.interval * 1000); - }) + ], function (error, result) { + setTimeout(runInterval, config.blockUnlocker.interval * 1000) + }) } -runInterval(); - +runInterval() diff --git a/lib/charts.js b/lib/charts.js index 09b2927cc..2ade98f3a 100644 --- a/lib/charts.js +++ b/lib/charts.js @@ -6,128 +6,135 @@ **/ // Load required modules -var fs = require('fs'); -var async = require('async'); -var http = require('http'); +let fs = require('fs'); +let async = require('async'); +let http = require('http'); -var apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); -var market = require('./market.js'); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +let market = require('./market.js'); // Set charts cleanup interval -var cleanupInterval = config.redis.cleanupInterval && config.redis.cleanupInterval > 0 ? config.redis.cleanupInterval : 15; +let cleanupInterval = config.redis.cleanupInterval && config.redis.cleanupInterval > 0 ? config.redis.cleanupInterval : 15; // Initialize log system -var logSystem = 'charts'; +let logSystem = 'charts'; require('./exceptionWriter.js')(logSystem); /** * Charts data collectors (used by chartsDataCollector.js) **/ - + // Start data collectors -function startDataCollectors() { - async.each(Object.keys(config.charts.pool), function(chartName) { - var settings = config.charts.pool[chartName]; - if(settings.enabled) { - setInterval(function() { - collectPoolStatWithInterval(chartName, settings); - }, settings.updateInterval * 1000); - } - }); - - var settings = config.charts.user.hashrate; - if(settings.enabled) { - setInterval(function() { - collectUsersHashrate('hashrate', settings); - }, settings.updateInterval * 1000) - } - - settings = config.charts.user.worker_hashrate; - if (settings && settings.enabled) { - setInterval(function() { - collectWorkersHashrate('worker_hashrate', settings); - }, settings.updateInterval * 1000); - } +function startDataCollectors () { + async.each(Object.keys(config.charts.pool), function (chartName) { + let settings = config.charts.pool[chartName]; + if (settings.enabled) { + setInterval(function () { + collectPoolStatWithInterval(chartName, settings); + }, settings.updateInterval * 1000); + } + }); + + let userSettings = config.charts.user.hashrate; + if (userSettings.enabled) { + setInterval(function () { + collectUsersHashrate('hashrate', userSettings); + }, userSettings.updateInterval * 1000) + } + + let workerSettings = config.charts.user.worker_hashrate; + if (workerSettings && workerSettings.enabled) { + setInterval(function () { + collectWorkersHashrate('worker_hashrate', workerSettings); + }, workerSettings.updateInterval * 1000); + } } + // Chart data functions -var chartStatFuncs = { - hashrate: getPoolHashrate, - miners: getPoolMiners, - workers: getPoolWorkers, - difficulty: getNetworkDifficulty, - price: getCoinPrice, - profit: getCoinProfit +let chartStatFuncs = { + hashrate: getPoolHashrate, + miners: getPoolMiners, + workers: getPoolWorkers, + difficulty: getNetworkDifficulty, + price: getCoinPrice, + profit: getCoinProfit }; // Statistic value handler -var statValueHandler = { - avg: function(set, value) { - set[1] = (set[1] * set[2] + value) / (set[2] + 1); - }, - avgRound: function(set, value) { - statValueHandler.avg(set, value); - set[1] = Math.round(set[1]); - }, - max: function(set, value) { - if(value > set[1]) { - set[1] = value; - } - } +let statValueHandler = { + avg: function (set, value) { + set[1] = (set[1] * set[2] + value) / (set[2] + 1); + }, + avgRound: function (set, value) { + statValueHandler.avg(set, value); + set[1] = Math.round(set[1]); + }, + max: function (set, value) { + if (value > set[1]) { + set[1] = value; + } + } }; // Presave functions -var preSaveFunctions = { - hashrate: statValueHandler.avgRound, - workers: statValueHandler.max, - difficulty: statValueHandler.avgRound, - price: statValueHandler.avg, - profit: statValueHandler.avg +let preSaveFunctions = { + hashrate: statValueHandler.avgRound, + hashrateSolo: statValueHandler.avgRound, + workers: statValueHandler.max, + workersSolo: statValueHandler.max, + difficulty: statValueHandler.avgRound, + price: statValueHandler.avg, + profit: statValueHandler.avg }; // Store collected values in redis database -function storeCollectedValues(chartName, values, settings) { - for(var i in values) { - storeCollectedValue(chartName + ':' + i, values[i], settings); - } +function storeCollectedValues (chartName, values, settings) { + for (let i in values) { + storeCollectedValue(chartName + ':' + i, [values[i]], settings); + } } // Store collected value in redis database -function storeCollectedValue(chartName, value, settings) { - var now = new Date() / 1000 | 0; - getChartDataFromRedis(chartName, function(sets) { - var lastSet = sets[sets.length - 1]; // [time, avgValue, updatesCount] - if(!lastSet || now - lastSet[0] > settings.stepInterval) { - lastSet = [now, value, 1]; - sets.push(lastSet); - while(now - sets[0][0] > settings.maximumPeriod) { // clear old sets - sets.shift(); - } - } - else { - preSaveFunctions[chartName] - ? preSaveFunctions[chartName](lastSet, value) - : statValueHandler.avgRound(lastSet, value); - lastSet[2]++; - } - - if(getStatsRedisKey(chartName).search(config.coin + ":charts:hashrate") >=0){ - redisClient.set(getStatsRedisKey(chartName), JSON.stringify(sets), 'EX', (86400 * cleanupInterval)); - } - else{ - redisClient.set(getStatsRedisKey(chartName), JSON.stringify(sets)); - } - - log('info', logSystem, chartName + ' chart collected value ' + value + '. Total sets count ' + sets.length); - }); +function storeCollectedValue (chartName, values, settings) { + let now = new Date() / 1000 | 0; + values.forEach((value, index) => { + let name = `${chartName}` + (index === 0 ? '' : 'Solo') + return getChartDataFromRedis(name, function (sets) { + let lastSet = sets[sets.length - 1]; // [time, avgValue, updatesCount] + if (!lastSet || now - lastSet[0] > settings.stepInterval) { + lastSet = [now, value, 1]; + sets.push(lastSet); + while (now - sets[0][0] > settings.maximumPeriod) { // clear old sets + sets.shift(); + } + } else { + preSaveFunctions[name] ? + preSaveFunctions[name](lastSet, value) : + statValueHandler.avgRound(lastSet, value); + lastSet[2]++; + } + + if (getStatsRedisKey(name) + .search(`^${config.coin}:charts:hashrate$`) >= 0) { + redisClient.set(getStatsRedisKey(name), JSON.stringify(sets), 'EX', (86400 * cleanupInterval)); + } else if (getStatsRedisKey(name) + .search(`^${config.coin}:charts:hashrateSolo$`) >= 0) { + redisClient.set(getStatsRedisKey(name), JSON.stringify(sets), 'EX', (86400 * cleanupInterval)); + } else { + redisClient.set(getStatsRedisKey(name), JSON.stringify(sets)); + } + log('info', logSystem, name + ' chart collected value ' + value + '. Total sets count ' + sets.length); + }); + }) } // Collect pool statistics with an interval -function collectPoolStatWithInterval(chartName, settings) { - async.waterfall([ +function collectPoolStatWithInterval (chartName, settings) { + async.waterfall([ chartStatFuncs[chartName], - function(value, callback) { - storeCollectedValue(chartName, value, settings, callback); + function (values, callback) { + storeCollectedValue(chartName, values, settings, callback); } ]); } @@ -135,237 +142,242 @@ function collectPoolStatWithInterval(chartName, settings) { /** * Get chart data from redis database **/ -function getChartDataFromRedis(chartName, callback) { - redisClient.get(getStatsRedisKey(chartName), function(error, data) { - callback(data ? JSON.parse(data) : []); - }); +function getChartDataFromRedis (chartName, callback) { + redisClient.get(getStatsRedisKey(chartName), function (error, data) { + callback(data ? JSON.parse(data) : []); + }); } /** * Return redis key for chart data **/ -function getStatsRedisKey(chartName) { - return config.coin + ':charts:' + chartName; +function getStatsRedisKey (chartName) { + return config.coin + ':charts:' + chartName; } /** * Get pool statistics from API **/ -function getPoolStats(callback) { - apiInterfaces.pool('/stats', function(error, data) { - if (error) { - log('error', logSystem, 'Unable to get API data for stats: ' + error); - } - callback(error, data); - }); +function getPoolStats (callback) { + apiInterfaces.pool('/stats', function (error, data) { + if (error) { + log('error', logSystem, 'Unable to get API data for stats: ' + error); + } + callback(error, data); + }); } /** * Get pool hashrate from API **/ -function getPoolHashrate(callback) { - getPoolStats(function(error, stats) { - callback(error, stats.pool ? Math.round(stats.pool.hashrate) : null); - }); +function getPoolHashrate (callback) { + getPoolStats(function (error, stats) { + callback(error, stats.pool ? [Math.round(stats.pool.hashrate), Math.round(stats.pool.hashrateSolo)] : null); + }); } /** * Get pool miners from API **/ -function getPoolMiners(callback) { - getPoolStats(function(error, stats) { - callback(error, stats.pool ? stats.pool.miners : null); - }); +function getPoolMiners (callback) { + getPoolStats(function (error, stats) { + callback(error, stats.pool ? [stats.pool.miners, stats.pool.minersSolo] : null); + }); } /** * Get pool workers from API **/ -function getPoolWorkers(callback) { - getPoolStats(function(error, stats) { - callback(error, stats.pool ? stats.pool.workers : null); - }); +function getPoolWorkers (callback) { + getPoolStats(function (error, stats) { + callback(error, stats.pool ? [stats.pool.workers, stats.pool.workersSolo] : null); + }); } /** * Get network difficulty from API **/ -function getNetworkDifficulty(callback) { - getPoolStats(function(error, stats) { - callback(error, stats.pool ? stats.network.difficulty : null); - }); +function getNetworkDifficulty (callback) { + getPoolStats(function (error, stats) { + callback(error, stats.pool ? [stats.network.difficulty] : null); + }); } /** * Get users hashrate from API **/ -function getUsersHashrates(callback) { - apiInterfaces.pool('/miners_hashrate', function(error, data) { - if (error) { - log('error', logSystem, 'Unable to get API data for miners_hashrate: ' + error); - } - var resultData = data && data.minersHashrate ? data.minersHashrate : {}; - callback(resultData); - }); +function getUsersHashrates (callback) { + apiInterfaces.pool('/miners_hashrate', function (error, data) { + if (error) { + log('error', logSystem, 'Unable to get API data for miners_hashrate: ' + error); + } + let resultData = data && data.minersHashrate ? data.minersHashrate : {}; + callback(resultData); + }); } /** * Get workers' hashrates from API **/ -function getWorkersHashrates(callback) { - apiInterfaces.pool('/workers_hashrate', function(error, data) { - if (error) { - log('error', logSystem, 'Unable to get API data for workers_hashrate: ' + error); - } - var resultData = data && data.workersHashrate ? data.workersHashrate : {}; - callback(resultData); - }); +function getWorkersHashrates (callback) { + apiInterfaces.pool('/workers_hashrate', function (error, data) { + if (error) { + log('error', logSystem, 'Unable to get API data for workers_hashrate: ' + error); + } + let resultData = data && data.workersHashrate ? data.workersHashrate : {}; + callback(resultData); + }); } /** * Collect users hashrate from API **/ -function collectUsersHashrate(chartName, settings) { - var redisBaseKey = getStatsRedisKey(chartName) + ':'; - redisClient.keys(redisBaseKey + '*', function(keys) { - var hashrates = {}; - for(var i in keys) { - hashrates[keys[i].substr(redisBaseKey.length)] = 0; - } - getUsersHashrates(function(newHashrates) { - for(var address in newHashrates) { - hashrates[address] = newHashrates[address]; - } - storeCollectedValues(chartName, hashrates, settings); - }); - }); +function collectUsersHashrate (chartName, settings) { + let redisBaseKey = getStatsRedisKey(chartName) + ':'; + redisClient.keys(redisBaseKey + '*', function (keys) { + let hashrates = {}; + for (let i in keys) { + hashrates[keys[i].substr(redisBaseKey.length)] = 0; + } + getUsersHashrates(function (newHashrates) { + for (let address in newHashrates) { + hashrates[address] = newHashrates[address]; + } + storeCollectedValues(chartName, hashrates, settings); + }); + }); } /** * Get user hashrate chart data **/ -function getUserHashrateChartData(address, callback) { - getChartDataFromRedis('hashrate:' + address, callback); +function getUserHashrateChartData (address, callback) { + getChartDataFromRedis('hashrate:' + address, callback); } /** * Collect worker hashrates from API **/ -function collectWorkersHashrate(chartName, settings) { - var redisBaseKey = getStatsRedisKey(chartName) + ':'; - redisClient.keys(redisBaseKey + '*', function(keys) { - var hashrates = {}; - for(var i in keys) { - hashrates[keys[i].substr(redisBaseKey.length)] = 0; - } - getWorkersHashrates(function(newHashrates) { - for(var addr_worker in newHashrates) { - hashrates[addr_worker] = newHashrates[addr_worker]; - } - storeCollectedValues(chartName, hashrates, settings); - }); - }); +function collectWorkersHashrate (chartName, settings) { + let redisBaseKey = getStatsRedisKey(chartName) + ':'; + redisClient.keys(redisBaseKey + '*', function (keys) { + let hashrates = {}; + for (let i in keys) { + hashrates[keys[i].substr(redisBaseKey.length)] = 0; + } + getWorkersHashrates(function (newHashrates) { + for (let addr_worker in newHashrates) { + hashrates[addr_worker] = newHashrates[addr_worker]; + } + storeCollectedValues(chartName, hashrates, settings); + }); + }); } /** * Convert payments data to chart **/ -function convertPaymentsDataToChart(paymentsData) { - var data = []; - if(paymentsData && paymentsData.length) { - for(var i = 0; paymentsData[i]; i += 2) { - data.unshift([+paymentsData[i + 1], paymentsData[i].split(':')[1]]); - } - } - return data; +function convertPaymentsDataToChart (paymentsData) { + let data = []; + if (paymentsData && paymentsData.length) { + for (let i = 0; paymentsData[i]; i += 2) { + data.unshift([+paymentsData[i + 1], paymentsData[i].split(':')[1]]); + } + } + return data; } /** * Get current coin market price **/ -function getCoinPrice(callback) { - var source = config.prices.source; - var currency = config.prices.currency; - - var tickers = [config.symbol.toUpperCase() + '-' + currency.toUpperCase()]; - market.get(source, tickers, function(data) { - var error = (!data || !data[0] || !data[0].price) ? 'No exchange data for ' + config.symbol.toUpperCase() + ' to ' + currency.toUpperCase() + ' using ' + source : null; - var price = (data && data[0] && data[0].price) ? data[0].price : null; - callback(error, price); - }); +function getCoinPrice (callback) { + let source = config.prices.source; + let currency = config.prices.currency; + + let tickers = [config.symbol.toUpperCase() + '-' + currency.toUpperCase()]; + market.get(source, tickers, function (data) { + let error = (!data || !data[0] || !data[0].price) ? 'No exchange data for ' + config.symbol.toUpperCase() + ' to ' + currency.toUpperCase() + ' using ' + source : null; + let price = (data && data[0] && data[0].price) ? data[0].price : null; + callback(error, price); + }); } /** * Get current coin profitability **/ -function getCoinProfit(callback) { - getCoinPrice(function(error, price) { - if(error) { - callback(error); - return; - } - getPoolStats(function(error, stats) { - if(error) { - callback(error); - return; - } - callback(null, stats.lastblock.reward * price / stats.network.difficulty / config.coinUnits); - }); - }); +function getCoinProfit (callback) { + getCoinPrice(function (error, price) { + if (error) { + callback(error); + return; + } + getPoolStats(function (error, stats) { + if (error) { + callback(error); + return; + } + callback(null, stats.lastblock.reward * price / stats.network.difficulty / config.coinUnits); + }); + }); } /** * Return pool charts data **/ -function getPoolChartsData(callback) { - var chartsNames = []; - var redisKeys = []; - for(var chartName in config.charts.pool) { - if(config.charts.pool[chartName].enabled) { - chartsNames.push(chartName); - redisKeys.push(getStatsRedisKey(chartName)); - } - } - if(redisKeys.length) { - redisClient.mget(redisKeys, function(error, data) { - var stats = {}; - if(data) { - for(var i in data) { - if(data[i]) { - stats[chartsNames[i]] = JSON.parse(data[i]); - } - } - } - callback(error, stats); - }); - } - else { - callback(null, {}); - } +function getPoolChartsData (callback) { + let chartsNames = []; + let redisKeys = []; + for (let chartName in config.charts.pool) { + if (config.charts.pool[chartName].enabled) { + chartsNames.push(chartName); + redisKeys.push(getStatsRedisKey(chartName)); + } + } + chartsNames.push('hashrateSolo') + chartsNames.push('minersSolo') + chartsNames.push('workersSolo') + redisKeys.push(getStatsRedisKey('hashrateSolo')) + redisKeys.push(getStatsRedisKey('minersSolo')) + redisKeys.push(getStatsRedisKey('workersSolo')) + if (redisKeys.length) { + redisClient.mget(redisKeys, function (error, data) { + let stats = {}; + if (data) { + for (let i in data) { + if (data[i]) { + stats[chartsNames[i]] = JSON.parse(data[i]); + } + } + } + callback(error, stats); + }); + } else { + callback(null, {}); + } } /** * Return user charts data **/ -function getUserChartsData(address, paymentsData, callback) { - var stats = {}; - var chartsFuncs = { - hashrate: function(callback) { - getUserHashrateChartData(address, function(data) { - callback(null, data); - }); - }, - - payments: function(callback) { - callback(null, convertPaymentsDataToChart(paymentsData)); - } - }; - for(var chartName in chartsFuncs) { - if(!config.charts.user[chartName].enabled) { - delete chartsFuncs[chartName]; - } - } - async.parallel(chartsFuncs, callback); +function getUserChartsData (address, paymentsData, callback) { + let stats = {}; + let chartsFuncs = { + hashrate: function (callback) { + getUserHashrateChartData(address, function (data) { + callback(null, data); + }); + }, + + payments: function (callback) { + callback(null, convertPaymentsDataToChart(paymentsData)); + } + }; + for (let chartName in chartsFuncs) { + if (!config.charts.user[chartName].enabled) { + delete chartsFuncs[chartName]; + } + } + async.parallel(chartsFuncs, callback); } @@ -373,7 +385,7 @@ function getUserChartsData(address, paymentsData, callback) { * Exports charts functions **/ module.exports = { - startDataCollectors: startDataCollectors, - getUserChartsData: getUserChartsData, - getPoolChartsData: getPoolChartsData + startDataCollectors: startDataCollectors, + getUserChartsData: getUserChartsData, + getPoolChartsData: getPoolChartsData }; diff --git a/lib/chartsDataCollector.js b/lib/chartsDataCollector.js index c65cf5df4..e26b8da83 100644 --- a/lib/chartsDataCollector.js +++ b/lib/chartsDataCollector.js @@ -6,19 +6,19 @@ **/ // Load required modules -var fs = require('fs'); -var async = require('async'); -var http = require('http'); +let fs = require('fs'); +let async = require('async'); +let http = require('http'); -var charts = require('./charts.js'); +let charts = require('./charts.js'); // Initialize log system -var logSystem = 'chartsDataCollector'; +let logSystem = 'chartsDataCollector'; require('./exceptionWriter.js')(logSystem); /** * Run charts data collector **/ - + log('info', logSystem, 'Started'); charts.startDataCollectors(); diff --git a/lib/childDaemon.js b/lib/childDaemon.js new file mode 100644 index 000000000..b0285efec --- /dev/null +++ b/lib/childDaemon.js @@ -0,0 +1,84 @@ +let async = require('async'); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +let lastHash; +let POOL_NONCE_SIZE = 16 + 1; // +1 for old XMR/new TRTL bugs + +let logSystem = 'childDaemon' +require('./exceptionWriter.js')(logSystem); + + +let pool = config.childPools[process.env.poolId]; + +let blockData = JSON.stringify({ + id: "0", + jsonrpc: "2.0", + method: 'getlastblockheader', + params: {} +}) + +let templateData = JSON.stringify({ + id: "0", + jsonrpc: "2.0", + method: 'getblocktemplate', + params: { + reserve_size: POOL_NONCE_SIZE, + wallet_address: pool.poolAddress + } +}) + + +function runInterval () { + async.waterfall([ + function (callback) { + apiInterfaces.jsonHttpRequest(pool.childDaemon.host, pool.childDaemon.port, blockData, function (err, res) { + if (err) { + log('error', logSystem, '%s error from daemon', [pool.coin]); + setTimeout(runInterval, 3000); + return; + } + if (res && res.result && res.result.status === "OK" && res.result.hasOwnProperty('block_header')) { + let hash = res.result.block_header.hash.toString('hex'); + if (!lastHash || lastHash !== hash) { + lastHash = hash + log('info', logSystem, '%s found new hash %s', [pool.coin, hash]); + callback(null, true); + return; + } else if (config.daemon.alwaysPoll || false) { + callback(null, true); + return; + } else { + callback(true); + return; + } + } else { + log('error', logSystem, '%s bad reponse from daemon', [pool.coin]); + setTimeout(runInterval, 3000); + return; + } + }); + }, + function (getbc, callback) { + apiInterfaces.jsonHttpRequest(pool.childDaemon.host, pool.childDaemon.port, templateData, function (err, res) { + if (err) { + log('error', logSystem, '%s Error polling getblocktemplate %j', [pool.coin, err]) + callback(null) + return + } + process.send({ + type: 'ChildBlockTemplate', + block: res.result, + poolIndex: process.env.poolId + }) + callback(null) + }) + } + ], + function (error) { + if (error) {} + setTimeout(function () { + runInterval() + }, config.poolServer.blockRefreshInterval) + }) +} + +runInterval() diff --git a/lib/configReader.js b/lib/configReader.js index 3c9820b8d..65e39b237 100644 --- a/lib/configReader.js +++ b/lib/configReader.js @@ -9,113 +9,52 @@ let fs = require('fs'); // Set pool software version -global.version = "v1.4.0"; +global.version = "v2.0.0"; /** * Load pool configuration **/ - + // Get configuration file path -let configFile = (function(){ - for (let i = 0; i < process.argv.length; i++){ - if (process.argv[i].indexOf('-config=') === 0) - return process.argv[i].split('=')[1]; - } - return 'config.json'; +let configFile = (function () { + for (let i = 0; i < process.argv.length; i++) { + if (process.argv[i].indexOf('-config=') === 0) + return process.argv[i].split('=')[1]; + } + return 'config.json'; })(); // Read configuration file data try { - global.config = JSON.parse(fs.readFileSync(configFile)); -} -catch(e){ - console.error('Failed to read config file ' + configFile + '\n\n' + e); - return; + global.config = JSON.parse(fs.readFileSync(configFile)); +} catch (e) { + console.error('Failed to read config file ' + configFile + '\n\n' + e); + return; } /** * Developper donation addresses -- thanks for supporting my works! **/ - + let donationAddresses = { - BTC: '34GDVuVbuxyYdR8bPZ7g6r12AhPPCrNfXt', - BCH: 'qpl0gr8u3yu7z4nzep955fqy3w8m6w769sec08u3dp', - ETH: '0xd4d9a4f22475039f115824b15999a5a8143d424c', - LTC: 'LW169WygGDMBN1PGSr8kNbrFBx94emGWfB', - DERO: 'dERirD3WyQi4udWH7478H66Ryqn3syEU8bywCQEu3k5ULohQRcz4uoXP12NjmN4STmEDbpHZWqa7bPRiHNFPFgTBPmcBmB4yyCF8mZmNUanDb', - GRFT: 'GMPHYf5KRkcAyik7Jw9oHRfJtUdw2Kj5f4VTFJ25AaFVYxofetir8Cnh7S76Q854oMXzwaguL8p5KEz1tm3rn1SA6r6p9dMjuV81yqXCgi', - LTHN: 'NaWe5B5NqvZ3TV2Mj1pxYtTgrnTBwQDMDNtqVzMR6Xa5ejxu6hbi6KULHTqd732ebc5qTHvKXonokghUBd3pjLa8czn8PNg57mR2XqEcvr7w', - MSR: '5t5mEm254JNJ9HqRjY9vCiTE8aZALHX3v8TqhyQ3TTF9VHKZQXkRYjPDweT9kK4rJw7dDLtZXGjav2z9y24vXCdRc3DY4daikoNTeK1v4e', - XMR: '4Cf2TfMKhCgJ2vsM3HeBUnYe52tXrvv8X1ajjuQEMUQ8iU8kvUzCSsCEacxFhEmeb2JgPpQ5chdyw3UiTfUgapJBhHdmH87gYyoDR6NMZj', - SUMO: 'SumipDETyjLYi8rqkmyE9c4SftzYzWPCGA3XvcXbGuBYcqDQJWe8wp8NEwNicFyzZgKTSjCjnpuXTitwn6VdBcFZEFXLcUYThVkF1dR9Q1uxEa', - XHV: 'hvi1aCqoAZF19J8pijvqnrUkeAeP8Rvr4XyfDMGJcarhbL15KgYKM1hN7kiHMu3fer5k8JJ8YRLKCahDKFgLFgJMYAfngJjDmkZAVuiRP15qv', - XTL: 'SEiStP7SMy1bvjkWc9dd1t2v1Et5q2DrmaqLqFTQQ9H7JKdZuATcPHUbUL3bRjxzxTDYitHsAPqF8EeCLw3bW8ARe8rYRNQQwys1JcJAs3qSH', - BLOC: 'abLoc7JNzYXijnKnPf7tSFUNSWBuwKrmUPMevvPkH4jc3b1K9LmS76DKpPamgQ5AYAC2CW9dJfTJ91AnXHYDNXAKRqPx5ZrtR49+9ea139b03f8ec0fce656096ad336a3c7b7041210b56386808dfbe4d1be8186c8', - AEON: 'WzWP347dSczJVmPpw65AsAGi5WhT85z5n66D8vcT3RcxRBBj4tFiDcd2CVFcQ1bBpjNQD5Z5kbXrLjVidvoKFaFK6uj4vyX3yrB1ap6jvPzB', - COAL: 'CoFbPDzEmLDHntwuC4WHkw3hQ4cX7g2JdVSGzqndAcDca4XgKyR4Wca7tkJw56eVX12iAQNGRzNPNXsegXmoJvUDSmkUKhL+3f128d248560b076805004a589de88bf6546bc9a0e0011dd56d6040d99b5d622', - XVG: 'DKSE7UW9Pwssq2ZF7rMvQVBG2EDio1GZHP', - BCN: '24WXh1qTEZzgDG3Ly1MtCJX7fRbDNqbzC4iEzpQBVhLwZ9jBeSs7GFQgu1bYxpHFKyjBQvABXicZ2MJ7si6rVBcQKQBCgLj+f122a0901996579ca5e8a7b5bdfb958a8e8d9fde470faeb7794315c3473b9aaa', - TRTL: 'TRTLv1Hqo3wHdqLRXuCyX3MwvzKyxzwXeBtycnkDy8ceFp4E23bm3P467xLEbUusH6Q1mqQUBiYwJ2yULJbvr5nKe8kcyc4uyps+0f2404e298d6e6c132b300713bcd723d7fa61dd1206ee4b5975c254c67783686', - KRB: 'KdAXXrRCGcENDhuRqEkocjZ5tpPfu17U35mqhgDEJTjTFBfs4hxhiBKK95XEpAuY8V9nomNcMeTz1E6tehWEvU6h8vCBs51+b7480fabf430e71b4702fbcf35e2454acba69be935e289fa84c485c2f07f5e63', - CIV: 'CM2ieMxxePe5z1BUkeNm9p5pmpn3k4juX1', - DASH: 'XhVA4JaYjtqvuSLSNFgxYUQKJaJnRhi1ww', - NAH: 'SWNNDA4wUvTkFVYvvhDP24yiCrR2p7SZbt', - PCN: 'P9GzJEnyYRgA6GHfdwtr7FhYq3s365ASKS', - LUX: 'LgozXD5vaHT3BDkNVRLWCBaNvSBmJxn5NT', - TUBE: 'bi1b95WYJRES7oBrvRo2eQV53ExLzFAzjKVM4wp9H9B6irCR6UuQxHf183XsJwemdoQm5PUHhQVwS67Hf5yUE7qg4SwbJJfAjLE1PH6T9V667', - DOGE: 'DM6FYmmLw4R5uFaSNdtYMrBLYuy1ank39R', - NBR: 'NDysWekoQnxeUquciujUQFPvVXTaU2D23eLbbboNFavCDozx2157EB3KzyKxk3mdyJRU1YfarvN35EfkGFbNEAQ4KHbkwy2+643750a2037d089470b8889d3a13edf673fdb0738e9c9bc4812ce41ded0644a7', - XUN: 'Xun3jQ4dLmfdRCnBuavjukJRm8EMntWqhL27JKJqeEf73rwi8nRoMYaMusv8pD9s5Y7oK8aHYCieB9rcNJ4uDfzZ8HUmzRq8yQ+cb22ae07df149caf85050ff14fd8b82d18fd0d62601a714b969855461046111b', - WAE: 'KfDMEcEpi7HANXK5N6vgAorWDLNqFfLnMk', - IRD: 'ir2btddJ78sicpKntYo3oRMLQh91VktzBfZzWbhwZnQxS815QLiG5WCAH9sgVGC5uwLZuMJCwW5CdFigNbJ3WTxU2CG5GnUDe+fe88be6c5a157ab1c97242ec0c6be699f48c604f752def45259097d20405a035', - D: 'DSHxGR1RFja4dwoqG2VXvFoSK5uS35pBWA', - PLURA: 'Pv8xzGjaY4TBPQUc8mLqcPGXCpWJbAYAjZ1uwZJK9Cfg1qvQBx3zZHqGF4XWnZHeYDKfkQVA8yDSjVdE56nz2Jab2Xu16PGb1+bda64dd03e45d67fd6fa4b99d91b507c02230bfe21cf2e862e436f18143d547a', - BTCP: 'b1B64ofQxHBPYXPYJvkuM9z1nexV8NSXtj6', - PIVX: 'DAE6oWhR1TwD8vFSxQHGaTMwyymHcCsmDH', - BSM: 'Sm4gdR5meAJAets9DwXCtq9tRZofxg6uubw6rYPKqYi7EN1MKADYG6obJczbjCmwfS752ThxYHUr3gBqY8KDWrB91dPcCbhJe', - OMB: 'casiLpKfELY6hyxUqS1zYjjAMTSWrcUm5Cpc1bSdwNyCEmP2ii8EfVWLvvjysm2YXBXM2vGvpkGUs42RD1ihi9uDATCr5crEjvw8AfnGHwkaw', - LOKI: 'LK8CGQ17G9R3ys3Xf33wCeViD2B95jgdpjAhcRsjuheJ784dumXn7g3RPAzedWpFq364jJKYL9dkQ8mY66sZG9BiCvqjvv6LgsyHf6H2gy', - BKC: 'bkc1o8uPqS7YD6Eo4yoaGp9N2MgbUrU7pUChtFFXS3K6fWF5hT9SrP2dGJfsTnn6pWZtQckuPYUSbJy5qv2MnLGo8eFS3t6qJw+29c6c1a056b3bc2598d0d57ab396c2d9c0053642dd9b8cbb7cb5bd63742f1eea', - SOLACE: 'Siz7GSHywyu3RxHDAcuW9iBKVLfgjjftDj5p9AucrNb9YV1jkPZNBdjDiWxcjMZK94Kdo97BzMuSZAU87U7UCzUL4JeRiP2zFz87AgfgBiFUW', - RTO: 'ALJj1xFnU8854yRhkrQKLyKeZYUxTt1oFQ4nEpbNsJoneXMBHyCgCJCW9xq2QZLVnbB4hBwxbEopSdWEmiyXXFiC66VQvaF+b40b160bf2f6efda8e56213c5907e960518633c8152f79c4645dbfda637c5122', - ITA: 'iz4FdJrFNkGNEmLSJAA39bT4cuQBVAgQTa818Gqshzg32PadREJSuWWEHFunmdfS8rjHGSHTxZoja5nWSZ8zWwiW51F2SAPWRN42mdXBvWe6', - INTU: 'intuvSUykQhFXJj22j2KJqC5CRMv3AWar467CtzZFVAVBxLUwb8yQi1LF72LzLRHtvAoNGkCYr3EZHYUJmeSqcZCLhxK73jJZDF+e35f3bdc1581764e8d8d778c2acbfd1acb838a2da61553ef637c980765bc3a26', - WOW: 'So2ifgjqGMZJhCrqpFMotQQAiJAiATuJLNAK2HrPLoNzK8hkqNbf9t8gmx6bzAQrXRMnWnoELoiD6GTv8guPBRwH5yoUvyBK1Ku1YYpQf2x8', - XMV: '4Cd7rzeiqwQJe8dCZbQeQxeNHUV4P4w7hAJ2g8U2ciiEao2AmqN9tXEfngQaPGV6T2Sx9mEtCaEMzaCR53iQRJqEgha9Dv26d83ML2wMiT', - XMC: '4H6kZARSRW9WAfxooKC2hkSpNf3RHo7ERBZWFdHN2BiX5BRxoiP5881EEK7P9wdzCZZmUxGWCZNuMjYdKFL1UuqdNUSZQs3uWGhCd4s4Xe', - WTIP: 'WtiptjGr6oRdZVFqumdfLbVd1XMtCYWKBT6dXppmCWA3TAk4tvesMCsbFLewv4rnmQHKimvwvcsbzC5FgWRrkGmc1DXreHk7sS+b319cf450209a800ea35c7f0d66754d0e243314650fe89ffd5db352733d9b9db', - BBS: 'fyT3HPm3Qrh87dR64wwLCi855A6bgYdF13mqtSHvX3RB67XEBb8aRrtVGsHa8u9juzByX8Mv6CPDjeuJwHhfqrjA19e7t2Fad+38adf2a9aadf58bf65374a683853ccbc169f75c9a38dc89cecb1beba3788c764', - XNV: 'NizKdaicW4bVfYB3AVhnsq9qnvUYSKe568YaNV2KQCYCDrNGzpvxqBo6mxF8cBkiQDU5xkgB2PrUGFKf66wVDVoNbQBhu2JRacy6uhsLoUyBJ', - XRN: 'PiyAfA1u1bNH9XjsPjXc5M64sip7LCj6ziBKEneKPmbVWM6kPMMAQs17h26tnCogW5Q72c5pVGJ4QW3kSKso4MW4h6hQJAVW23z6w2YMYKdHB', - XTRI: 'TixxgPgBkxgC4JM39WZuacjMLnJqm9YbjPq1YAR4BJbLXiCzf345r9SVbNuKMAG1CcjRMsv7kpatt7gStpUE3gGJRb8cbvWQHfk6L4pEbmdF7', - RVN: 'RFQvccQyLF3YMhKQby3bKvNXJczhgzEofu', - ETNXP: 'f4VR74XR616Tw2wAMMfaLV1vmYBSBBbmWXBUtaV8YDb6DHsfKRoYkFaCvhPhsGDDfm1afhzLNuf5XGFmNrvodPoQ6m4qGwXTuNt3gNnTEMYVA', - ZEL: 't1gsjJAhgGDB45ohJnJkbxtvr8WcUSzahyr', - RYO: 'SumipDETyjLYi8rqkmyE9c4SftzYzWPCGA3XvcXbGuBYcqDQJWe8wp8NEwNicFyzZgKTSjCjnpuXTitwn6VdBcFZEFXLcSos83oaS9wV7CJdKY', - INC: 'i9ajAmx77JLPtZL7JEs3ZEVbErmXqvQeGbg7PywpCaRKQGyypTv4TTpDYLMtrdGBGXMJM2mugBf14csz4wmNitZuGdXmy2c4hbG2iiUscAxQ', - QUAN: 'QRm4DREddkpmmC48CqXqkhV6S75fhtciBo', - PURK: 'PK2TdygFzH7X9jZPAdjvgQFHBR5bdNmsr6xefs2yQsDRHkKuoAsqQ7hX73nLgjWpiji4GqJMmNx357eu98TpU9Yr2es6SBBtQ+1246e3b3c4fdba89cbcad2113d0dff839d0f1f0a8a6f648b1c292b9bd8a0f8c2', - ACM: 'PDo5Z3pqN8weHSbfqayJEiSeAkAyr32NB3', - ARQ: 'aRi1cDd6LkAcc1p6W58dkPi8xSfbZ5EuYFrHxwH3py1MQ9rFrzmSaghguD4GGpCfHSMmKXWJrd4e5CkabC3viWJKfHuDLYqHNGs9D83sj6BPX', - NCP: 'cczJxhhLKTg7oNGiy1kmFadqTcKDschTb3LUKGvQdhxrVepPo9KjfxjSeqXBUKWVFwcHZRTGLi3k6USWHF1YP82e2TWRse4KD6+70fa2954af5cfc9fd292ce1643e276c8899b769255108c84caf2dcc406ea0678', - XGS: 'GTqypwunRe5ZkNdmAr26B9mmeMoGwhgoCV', - SUQA: 'Sic7A6F5r8RkjBvHjRwA9jWhHawXNrFxFX', - SHB: 'SVSEb9adxGpWckC2KuwuNbD1ved2x7YWTj', - GPKR: 'GdQ4ewDqJyhMU4BpchEs5uyn8U5VeHii7V', - ETNX: 'f4VR74XR616Tw2wAMMfaLV1vmYBSBBbmWXBUtaV8YDb6DHsfKRoYkFaCvhPhsGDDfm1afhzLNuf5XGFmNrvodPoQ6m4qTKUkc8Y2dL6d97BZw', - LMO: 'darkWdeodDHM5YWWGBHKa821DrL3HSzeaBNaVmXTr3svd75GUVcUBbxdSdFqJFUgTWfxfZJJcGTg58rfKct5hedk3Gz9dFLa6U', - TTNZ: 'Tri1J1prCp9VWj3AyjSTHU5aH1kVt4hsJ9xv7jbMauBzCLcqgKzJB2SATr2aypSFfmBW2dNfDVxMW3sQ4ys147Pz5pRgJvKzVS', - FHV: 'fh3ddFK3JqWNRZFsiL7xB5bGa79ejPfBNVbSdXVzdKiR7vRXHC3osL51vg9PyHKmxvWgz4ymZeHzcRZNRTJ5kwzM2BtrZMiq7', - SAFEX: 'Safex5zXVvH6GYJY2tnL4GcJy4W72GRutjhV1aCaRiPhYnfv4CyDjmGfLYQDd4GaJvHEKrpE7r9ux6UMCv5i1PmvjNwxA4r4Roi3K', - MUTX: 'ZYZZZDAz8vEfwGL3QfuRpMHneGuMbm6uTCtJ4h1kyhYkSi8Yjp62fBQWuHEviGoUdcXmSuYM3mDVa6tR9Rv5zRMra5i1F5UKvSXZc8X7MwHCki', - BTCN: 'MvweZSjjwTTdwrKJcRL2SzRZm9YrEAthNNeyzGjdbbeujoPsayzLM4efBhLZQoV6GreZTNgHv2gBZ9Hzz7SkTYVN2EgTbqh', - ARMS: 'gunsGSAGHJweDXBXGwCQrwUqACk67GkZB991b4HHkze8a7bnif8XwPF1NMdoY6oRhm8qjbPu2Jh7F4egLD3mpFTN1oQNydPdNv' + BTC: '18tytWtmoi3fzEdRFXfa96xHXUfBPvtMj5', + BCH: 'qpxcm3r90y6cedvazm4phwr82m3ywwn66gzwllq63l', + ETH: '0x745F2Bc9570B8C8DcD51249d7fdC2528f03efF1c', + LTC: 'LKF12Fi92zuxDhpHLe7gSWBtTdJbcULa85', + XMR: '44c7umSm7TyXxKch9q4R5QfoTAf663A8yEFfJbxmxUJ1JCWq2kFu33oAAydrgNDQA8619rSQhZaFV3ScpESWCfcQB3Fqc6w', + TRTL: 'TRTLv2tGkYa5W7UTH8q9mhhQdY8Ftbo3RNBfpupZYnCm7uHZp6jrxXV3pyPnzdftZj2rfCZsUkpJQWTbdbJ5wx84PB3rTojR7C9', + DOGE: 'DDrA5dZTjjnyYPxT23wmG5X5sxqt7XNMQe', + ARMS: 'guns91W8WhUT8mwBRByeh2erje1JcW7Vq8XP7VEL8CS2NPR7oqRK9mv2YN3Vvup9Hk98mpEQ9S9WeLMzgL52CKRY3xVd1WgTKL', + MNG: 'MEpLh1LswBqihtwVB7VuYAQP7E39SYSwVQwFVyAjpGd6fdALVvZk74YTq5jTo4DNnTdkw2wcWCzJ2EtVJ9k9DhioBWQ7GGq', + WOO: 'WwaFjmWQrNG1h1bSVcP8UjKYL1nHRHp5sGVVSX9pZmtFjfPFfBi4XoJCWewvjXU4gxGmuiNpZSBLd4sgRWnusrNS15x8facrx' }; global.donations = {}; -let percent = config.blockUnlocker.devDonation; -let wallet = donationAddresses[config.symbol]; -if (percent && wallet) { - global.donations[wallet] = percent; -} +global.devFee = config.blockUnlocker.devDonation || 0.2; +if (config.blockUnlocker.devDonation === 0) + global.devFee = 0.2; + +let wallet = donationAddresses[config.symbol.toUpperCase()]; +if (devFee && wallet) + global.donations[wallet] = devFee; diff --git a/lib/daemon.js b/lib/daemon.js new file mode 100644 index 000000000..459138fc9 --- /dev/null +++ b/lib/daemon.js @@ -0,0 +1,92 @@ +let utils = require('./utils.js'); +let async = require('async'); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +let lastHash; + +let POOL_NONCE_SIZE = 16 + 1; // +1 for old XMR/new TRTL bugs +let EXTRA_NONCE_TEMPLATE = "02" + POOL_NONCE_SIZE.toString(16) + "00".repeat(POOL_NONCE_SIZE); +let POOL_NONCE_MM_SIZE = POOL_NONCE_SIZE + utils.cnUtil.get_merged_mining_nonce_size(); +let EXTRA_NONCE_NO_CHILD_TEMPLATE = "02" + POOL_NONCE_MM_SIZE.toString(16) + "00".repeat(POOL_NONCE_MM_SIZE); + + +let logSystem = 'daemon' +let blockData = JSON.stringify({ + id: "0", + jsonrpc: "2.0", + method: 'getlastblockheader', + params: {} +}) + +let templateData = JSON.stringify({ + id: "0", + jsonrpc: "2.0", + method: 'getblocktemplate', + params: { + reserve_size: config.poolServer.mergedMining ? POOL_NONCE_MM_SIZE : POOL_NONCE_SIZE, + wallet_address: config.poolServer.poolAddress + } +}) + + +require('./exceptionWriter.js')(logSystem); + + +function runInterval () { + async.waterfall([ + function (callback) { + apiInterfaces.jsonHttpRequest(config.daemon.host, config.daemon.port, blockData, function (err, res) { + if (err) { + log('error', logSystem, '%s error from daemon', [config.coin]); + setTimeout(runInterval, 3000); + return; + } + if (res && res.result && res.result.status === "OK" && res.result.hasOwnProperty('block_header')) { + let hash = res.result.block_header.hash.toString('hex'); + if (!lastHash || lastHash !== hash) { + lastHash = hash + log('info', logSystem, '%s found new hash %s', [config.coin, hash]); + callback(null, true); + return; + } else if (config.daemon.alwaysPoll || false) { + callback(null, true); + return; + } else { + callback(true); + return; + } + } else { + log('error', logSystem, 'bad reponse from daemon'); + setTimeout(runInterval, 3000); + return; + } + }); + }, + function (getbc, callback) { + apiInterfaces.jsonHttpRequest(config.daemon.host, config.daemon.port, templateData, function (err, res) { + if (err) { + log('error', logSystem, 'Error polling getblocktemplate %j', [err]) + callback(null) + return + } + if (res.error) { + log('error', logSystem, 'Error polling getblocktemplate %j', [res.error]) + callback(null) + return + } + process.send({ + type: 'BlockTemplate', + block: res.result + }) + callback(null) + }) + } + ], + function (error) { + if (error) {} + setTimeout(function () { + runInterval() + }, config.poolServer.blockRefreshInterval) + }) +} + +runInterval() diff --git a/lib/email.js b/lib/email.js index 354de50a5..bee4317d3 100644 --- a/lib/email.js +++ b/lib/email.js @@ -9,59 +9,65 @@ **/ // Load required modules -var nodemailer = require('nodemailer'); -var mailgun = require('mailgun.js'); +let nodemailer = require('nodemailer'); +let mailgun = require('mailgun.js'); // Initialize log system -var logSystem = 'email'; +let logSystem = 'email'; require('./exceptionWriter.js')(logSystem); /** * Sends out an email **/ -exports.sendEmail = function(email, subject, content) { - // Return error if no destination email address - if (!email) { - log('warn', logSystem, 'Unable to send e-mail: no destination email.'); - return ; - } +exports.sendEmail = function (email, subject, content) { + // Return error if no destination email address + if (!email) { + log('warn', logSystem, 'Unable to send e-mail: no destination email.'); + return; + } - // Check email system configuration - if (!config.email) { - log('error', logSystem, 'Email system not configured!'); - return ; - } - - // Do nothing if email system is disabled - if (!config.email.enabled) return ; + // Check email system configuration + if (!config.email) { + log('error', logSystem, 'Email system not configured!'); + return; + } - // Set content data - var messageData = { - from: config.email.fromAddress, - to: email, - subject: subject, - text: content - }; - - // Get email transport - var transportMode = config.email.transport; - var transportCfg = config.email[transportMode] ? config.email[transportMode] : {}; - - if (transportMode === "mailgun") { - var mg = mailgun.client({username: 'api', key: transportCfg.key}); - mg.messages.create(transportCfg.domain, messageData); - log('info', logSystem, 'E-mail sent to %s: %s', [messageData.to, messageData.subject]); - } - - else { - transportCfg['transport'] = transportMode; - var transporter = nodemailer.createTransport(transportCfg); - transporter.sendMail(messageData, function(error){ - if(error){ - log('error', logSystem, 'Unable to send e-mail to %s: %s', [messageData.to, error.toString()]); - } else { - log('info', logSystem, 'E-mail sent to %s: %s', [email, subject]); - } - }); - } + // Do nothing if email system is disabled + if (!config.email.enabled) return; + + // Set content data + let messageData = { + from: config.email.fromAddress, + to: email, + subject: subject, + text: content + }; + + // Get email transport + let transportMode = config.email.transport; + let transportCfg = config.email[transportMode] ? config.email[transportMode] : {}; + + if (transportMode === "mailgun") { + let mg = mailgun.client({ + username: 'api', + key: transportCfg.key + }); + mg.messages.create(transportCfg.domain, messageData) + .then(() => { + log('info', logSystem, 'E-mail sent to %s: %s', [messageData.to, messageData.subject]); + }) + .catch(error => { + log('error', logSystem, 'Unable to send e-mail to %s: %s', [messageData.to, JSON.stringify(error)]); + }); + } else { + transportCfg['transport'] = transportMode; + let transporter = nodemailer.createTransport(transportCfg); + transporter.sendMail(messageData, function (error) { + if (error) { + log('error', logSystem, 'Unable to send e-mail to %s: %s', [messageData.to, error.toString()]); + } else { + log('info', logSystem, 'E-mail sent to %s: %s', [email, subject]); + } + }); + } }; diff --git a/lib/exceptionWriter.js b/lib/exceptionWriter.js index dfe480956..4f84791fc 100644 --- a/lib/exceptionWriter.js +++ b/lib/exceptionWriter.js @@ -6,20 +6,20 @@ **/ // Load required modules -var fs = require('fs'); -var cluster = require('cluster'); -var dateFormat = require('dateformat'); +let fs = require('fs'); +let cluster = require('cluster'); +let dateFormat = require('dateformat'); /** * Handle exceptions **/ -module.exports = function(logSystem){ - process.on('uncaughtException', function(err) { - console.log('\n' + err.stack + '\n'); - var time = dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss'); - fs.appendFile(config.logging.files.directory + '/' + logSystem + '_crash.log', time + '\n' + err.stack + '\n\n', function(err){ - if (cluster.isWorker) - process.exit(); - }); - }); -}; \ No newline at end of file +module.exports = function (logSystem) { + process.on('uncaughtException', function (err) { + console.log('\n' + err.stack + '\n'); + let time = dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss'); + fs.appendFile(config.logging.files.directory + '/' + logSystem + '_crash.log', time + '\n' + err.stack + '\n\n', function (err) { + if (cluster.isWorker) + process.exit(); + }); + }); +}; diff --git a/lib/logger.js b/lib/logger.js index f3cf436c9..619917b2a 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -6,85 +6,88 @@ **/ // Load required modules -var fs = require('fs'); -var util = require('util'); -var dateFormat = require('dateformat'); -var clc = require('cli-color'); +let fs = require('fs'); +let util = require('util'); +let dateFormat = require('dateformat'); +let clc = require('cli-color'); /** * Initialize log system **/ - + // Set CLI colors -var severityMap = { - 'info': clc.blue, - 'warn': clc.yellow, - 'error': clc.red +let severityMap = { + 'info': clc.blue, + 'warn': clc.yellow, + 'error': clc.red }; // Set severity levels -var severityLevels = ['info', 'warn', 'error']; +let severityLevels = ['info', 'warn', 'error']; // Set log directory -var logDir = config.logging.files.directory; +let logDir = config.logging.files.directory; // Create log directory if not exists -if (!fs.existsSync(logDir)){ - try { - fs.mkdirSync(logDir); - } - catch(e){ - throw e; - } +if (!fs.existsSync(logDir)) { + try { + fs.mkdirSync(logDir); + } catch (e) { + throw e; + } } /** * Write log entries to file at specified flush interval - **/ -var pendingWrites = {}; - -setInterval(function(){ - for (var fileName in pendingWrites){ - var data = pendingWrites[fileName]; - fs.appendFile(fileName, data, function(err) { - if (err) { - console.log("Error writing log data to disk: %s", err); - callback(null, "Error writing data to disk"); - } - }); - delete pendingWrites[fileName]; - } + **/ +let pendingWrites = {}; + +setInterval(function () { + for (let fileName in pendingWrites) { + let data = pendingWrites[fileName]; + fs.appendFile(fileName, data, function (err) { + if (err) { + console.log("Error writing log data to disk: %s", err); + callback(null, "Error writing data to disk"); + } + }); + delete pendingWrites[fileName]; + } }, config.logging.files.flushInterval * 1000); /** * Add new log entry **/ -global.log = function(severity, system, text, data){ +global.log = function (severity, system, text, data) { - var logConsole = severityLevels.indexOf(severity) >= severityLevels.indexOf(config.logging.console.level); - var logFiles = severityLevels.indexOf(severity) >= severityLevels.indexOf(config.logging.files.level); + let logConsole = severityLevels.indexOf(severity) >= severityLevels.indexOf(config.logging.console.level); + let logFiles = severityLevels.indexOf(severity) >= severityLevels.indexOf(config.logging.files.level); - if (!logConsole && !logFiles) return; + if (!logConsole && !logFiles) return; - var time = dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss'); - var formattedMessage = text; + let time = dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss'); + let formattedMessage = text; - if (data) { - data.unshift(text); - formattedMessage = util.format.apply(null, data); - } + if (data) { + data.unshift(text); + formattedMessage = util.format.apply(null, data); + } - if (logConsole){ - if (config.logging.console.colors) - console.log(severityMap[severity](time) + clc.white.bold(' [' + system + '] ') + formattedMessage); - else - console.log(time + ' [' + system + '] ' + formattedMessage); - } + if (logConsole) { + if (config.logging.console.colors) + if (system === 'daemon' || system === 'childDaemon') { + console.log(severityMap[severity](time) + clc.green.bold(' [' + system + '] ' + formattedMessage)); + } + else { + console.log(severityMap[severity](time) + clc.white.bold(' [' + system + '] ') + formattedMessage); + } else + console.log(time + ' [' + system + '] ' + formattedMessage); + } - if (logFiles) { - var fileName = logDir + '/' + system + '_' + severity + '.log'; - var fileLine = time + ' ' + formattedMessage + '\n'; - pendingWrites[fileName] = (pendingWrites[fileName] || '') + fileLine; - } -}; \ No newline at end of file + if (logFiles) { + let fileName = logDir + '/' + system + '_' + severity + '.log'; + let fileLine = time + ' ' + formattedMessage + '\n'; + pendingWrites[fileName] = (pendingWrites[fileName] || '') + fileLine; + } +}; diff --git a/lib/market.js b/lib/market.js index 838613da7..2eb538f3f 100644 --- a/lib/market.js +++ b/lib/market.js @@ -6,241 +6,450 @@ **/ // Load required modules -var apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet); // Initialize log system -var logSystem = 'market'; +let logSystem = 'market'; require('./exceptionWriter.js')(logSystem); /** * Get market prices **/ -exports.get = function(exchange, tickers, callback) { - if (!exchange) { - callback('No exchange specified', null); - } - exchange = exchange.toLowerCase(); - - if (!tickers || tickers.length === 0) { - callback('No tickers specified', null); - } - - var marketPrices = []; - var numTickers = tickers.length; - var completedFetches = 0; - - getExchangeMarkets(exchange, function(error, marketData) { - if (!marketData || marketData.length === 0) { - callback({}); - return ; - } - - for (var i in tickers) { - (function(i){ - var pairName = tickers[i]; - var pairParts = pairName.split('-'); - var base = pairParts[0] || null; - var target = pairParts[1] || null; - - if (!marketData[base]) { - completedFetches++; - if (completedFetches === numTickers) callback(marketPrices); - } else { - var price = marketData[base][target] || null; - if (!price || price === 0) { - var cryptonatorBase; - if (marketData[base]['BTC']) cryptonatorBase = 'BTC'; - else if (marketData[base]['ETH']) cryptonatorBase = 'ETH'; - else if (marketData[base]['LTC']) cryptonatorBase = 'LTC'; - - if (!cryptonatorBase) { - completedFetches++; - if (completedFetches === numTickers) callback(marketPrices); - } else { - getExchangePrice("cryptonator", cryptonatorBase, target, function(error, tickerData) { - completedFetches++; - if (tickerData && tickerData.price) { - marketPrices[i] = { - ticker: pairName, - price: tickerData.price * marketData[base][cryptonatorBase], - source: tickerData.source - }; - } - if (completedFetches === numTickers) callback(marketPrices); - }); - } - } else { - completedFetches++; - marketPrices[i] = { ticker: pairName, price: price, source: exchange }; - if (completedFetches === numTickers) callback(marketPrices); - } - } - })(i); - } - }); +exports.get = function (exchange, tickers, callback) { + if (!exchange) { + callback('No exchange specified', null); + } + exchange = exchange.toLowerCase(); + + if (!tickers || tickers.length === 0) { + callback('No tickers specified', null); + } + + let marketPrices = []; + let numTickers = tickers.length; + let completedFetches = 0; + + getExchangeMarkets(exchange, function (error, marketData) { + if (!marketData || marketData.length === 0) { + callback({}); + return; + } + + for (let i in tickers) { + (function (i) { + let pairName = tickers[i]; + let pairParts = pairName.split('-'); + let base = pairParts[0] || null; + let target = pairParts[1] || null; + + if (!marketData[base]) { + completedFetches++; + if (completedFetches === numTickers) callback(marketPrices); + } else { + let price = marketData[base][target] || null; + if (!price || price === 0) { + let cryptonatorBase; + if (marketData[base]['BTC']) cryptonatorBase = 'BTC'; + else if (marketData[base]['ETH']) cryptonatorBase = 'ETH'; + else if (marketData[base]['LTC']) cryptonatorBase = 'LTC'; + + if (!cryptonatorBase) { + completedFetches++; + if (completedFetches === numTickers) callback(marketPrices); + } else { + getExchangePrice("cryptonator", cryptonatorBase, target, function (error, tickerData) { + completedFetches++; + if (tickerData && tickerData.price) { + marketPrices[i] = { + ticker: pairName, + price: tickerData.price * marketData[base][cryptonatorBase], + source: tickerData.source + }; + } + if (completedFetches === numTickers) callback(marketPrices); + }); + } + } else { + completedFetches++; + marketPrices[i] = { + ticker: pairName, + price: price, + source: exchange + }; + if (completedFetches === numTickers) callback(marketPrices); + } + } + })(i); + } + }); } /** * Get Exchange Market Prices **/ -var marketRequestsCache = {}; - -function getExchangeMarkets(exchange, callback) { - callback = callback || function(){}; - if (!exchange) { - callback('No exchange specified', null); - } - exchange = exchange.toLowerCase(); - - // Return cache if available - var cacheKey = exchange; - var currentTimestamp = Date.now() / 1000; - - if (marketRequestsCache[cacheKey] && marketRequestsCache[cacheKey].ts > (currentTimestamp - 60)) { - callback(null, marketRequestsCache[cacheKey].data); - return ; - } - - // Altex - if (exchange == "altex") { - apiInterfaces.jsonHttpRequest('api.altex.exchange', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - - if (error) callback(error, {}); - if (!response || !response.success) callback('No market informations', {}); - - var data = {}; - for (var ticker in response.data) { - tickerParts = ticker.split('_'); - var target = tickerParts[0]; - var symbol = tickerParts[1]; - - var price = +parseFloat(response.data[ticker].last); - if (price === 0) continue; - - if (!data[symbol]) data[symbol] = {}; - data[symbol][target] = price; - } - if (!error) marketRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(null, data); - }, '/v1/ticker'); - } - - // Crex24 - else if (exchange == "crex24") { - apiInterfaces.jsonHttpRequest('api.crex24.com', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - - if (error) callback(error, {}); - if (!response || !response.Tickers) callback('No market informations', {}); - - var data = {}; - for (var i in response.Tickers) { - var ticker = response.Tickers[i]; - - var pairName = ticker.PairName; - pairParts = pairName.split('_'); - var target = pairParts[0]; - var symbol = pairParts[1]; - - var price = +ticker.Last; - if (!price || price === 0) continue; - - if (!data[symbol]) data[symbol] = {}; - data[symbol][target] = price; - } - if (!error) marketRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(null, data); - }, '/CryptoExchangeService/BotPublic/ReturnTicker'); - } - - // Cryptopia - else if (exchange == "cryptopia") { - apiInterfaces.jsonHttpRequest('www.cryptopia.co.nz', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - - if (error) callback(error, {}); - if (!response || !response.Success) callback('No market informations', {}); - - var data = {}; - for (var i in response.Data) { - var ticker = response.Data[i]; - - var pairName = ticker.Label; - var pairParts = pairName.split('/'); - var target = pairParts[1]; - var symbol = pairParts[0]; - - var price = +ticker.LastPrice; - if (!price || price === 0) continue; - - if (!data[symbol]) data[symbol] = {}; - data[symbol][target] = price; - } - if (!error) marketRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(null, data); - }, '/api/GetMarkets'); - } - - // Stocks.Exchange - else if (exchange == "stocks.exchange") { - apiInterfaces.jsonHttpRequest('stocks.exchange', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - - if (error) callback(error, {}); - if (!response) callback('No market informations', {}); - - var data = {}; - for (var i in response) { - var ticker = response[i]; - - var pairName = ticker.market_name; - var pairParts = pairName.split('_'); - var target = pairParts[1]; - var symbol = pairParts[0]; - - var price = +ticker.last; - if (!price || price === 0) continue; - - if (!data[symbol]) data[symbol] = {}; - data[symbol][target] = price; - } - if (!error) marketRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(null, data); - }, '/api2/ticker'); - } - - // TradeOgre - else if (exchange == "tradeogre") { - apiInterfaces.jsonHttpRequest('tradeogre.com', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - - var data = {}; - if (!error && response) { - for (var i in response) { - for (var pairName in response[i]) { - pairParts = pairName.split('-'); - var target = pairParts[0]; - var symbol = pairParts[1]; - - var price = +response[i][pairName].price; - if (price === 0) continue; - - if (!data[symbol]) data[symbol] = {}; - data[symbol][target] = price; - } - } - } - if (!error) marketRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(null, data); - }, '/api/v1/markets'); - } - - // Unknown - else { - callback('Exchange not supported: ' + exchange); - } +let marketRequestsCache = {}; + +function getExchangeMarkets (exchange, callback) { + callback = callback || function () {}; + if (!exchange) { + callback('No exchange specified', null); + } + exchange = exchange.toLowerCase(); + + // Return cache if available + let cacheKey = exchange; + let currentTimestamp = Date.now() / 1000; + + if (marketRequestsCache[cacheKey] && marketRequestsCache[cacheKey].ts > (currentTimestamp - 60)) { + callback(null, marketRequestsCache[cacheKey].data); + return; + } + + let target = null; + let symbol = null; + let price = 0.0; + let data = {}; + + // Altex + if (exchange == "altex") { + apiInterfaces.jsonHttpRequest('api.altex.exchange', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + if (error) callback(error, {}); + if (!response || !response.success) callback('No market informations', {}); + let ticker = null; + for (ticker in response.data) { + tickerParts = ticker.split('_'); + target = tickerParts[0]; + symbol = tickerParts[1]; + + price = +parseFloat(response.data[ticker].last); + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/v1/ticker'); + } + + // Crex24 + else if (exchange == "crex24") { + apiInterfaces.jsonHttpRequest('api.crex24.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + if (error) callback(error, {}); + if (!response || !response.Tickers) callback('No market informations', {}); + + let ticker = null; + let pairName = null; + let pairParts = null; + for (let i in response.Tickers) { + ticker = response.Tickers[i]; + + pairName = ticker.PairName; + pairParts = pairName.split('_'); + target = pairParts[0]; + symbol = pairParts[1]; + + price = +ticker.Last; + if (!price || price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/CryptoExchangeService/BotPublic/ReturnTicker'); + } + + // Cryptopia + else if (exchange == "cryptopia") { + apiInterfaces.jsonHttpRequest('www.cryptopia.co.nz', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + if (error) callback(error, {}); + if (!response || !response.Success) callback('No market informations', {}); + + let ticker = null; + let pairName = null; + let pairParts = null; + for (let i in response.Data) { + ticker = response.Data[i]; + + pairName = ticker.Label; + pairParts = pairName.split('/'); + target = pairParts[1]; + symbol = pairParts[0]; + + price = +ticker.LastPrice; + if (!price || price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/api/GetMarkets'); + } + + // Stocks.Exchange + else if (exchange == "stocks.exchange") { + apiInterfaces.jsonHttpRequest('stocks.exchange', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + if (error) callback(error, {}); + if (!response) callback('No market informations', {}); + + let ticker = null; + let pairName = null; + let pairParts = null; + for (let i in response) { + ticker = response[i]; + + pairName = ticker.market_name; + pairParts = pairName.split('_'); + target = pairParts[1]; + symbol = pairParts[0]; + + price = +ticker.last; + if (!price || price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/api2/ticker'); + } + + // TradeOgre + else if (exchange == "tradeogre") { + apiInterfaces.jsonHttpRequest('tradeogre.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + let pairParts = null; + if (!error && response) { + for (let i in response) { + for (let pairName in response[i]) { + pairParts = pairName.split('-'); + target = pairParts[0]; + symbol = pairParts[1]; + + price = +response[i][pairName].price; + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + } + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/api/v1/markets'); + } else if (exchange == "maplechange") { + apiInterfaces.jsonHttpRequest('maplechange.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + let data = {}; + if (!error && response) { + for (let model in response) { + let len = model.length; + if (len <= 3) continue; + target = model.substring(len - 3, len) + .toUpperCase(); + symbol = model.substring(0, len - 3) + .toUpperCase(); + + price = +response[model]['ticker']['last']; + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/api/v2/tickers'); + } else if (exchange == "stex") { + apiInterfaces.jsonHttpRequest('app.stex.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + let pieces = null; + + if (!error && response) { + for (let model in response) { + pieces = response[model]['market_name'].split('_'); + target = pieces[1]; + symbol = pieces[0]; + + price = +response[model]['last']; + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/api2/ticker'); + } + // Btc-Alpha + else if (exchange == "btcalpha") { + apiInterfaces.jsonHttpRequest('btc-alpha.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + let pieces = null; + if (!error && response) { + for (let model in response) { + pieces = response[model]['pair'].split('_'); + target = pieces[1]; + symbol = pieces[0]; + + price = +response[model]['price']; + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/api/v1/exchanges/' /*JUST FOR 20DEC!! '/api/v1/exchanges/?pair=BDX_BTC'*/ ); + } + // tradesatoshi + else if (exchange == "tradesatoshi") { + apiInterfaces.jsonHttpRequest('tradesatoshi.com', 443, '', function (error, response) { + if (error) console.log('error', 'API request to has failed: ' + error); + + let pieces = null; + if (!error && response.success) { + for (let model in response.result) { + pieces = response.result[model]['market'].split('_'); + target = pieces[1]; + symbol = pieces[0]; + + price = +response.result[model]['last']; + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/api/public/getmarketsummaries'); + } else if (exchange == "coinmarketcap") { + apiInterfaces.jsonHttpRequest('coinmarketcap.coindeal.com', 443, '', function (error, response) { + if (error) console.log('error', 'API request to has failed: ' + error); + var data = {}; + if (!error && response) { + for (var model in response) { + var pieces = model.split('_'); + var target = pieces[1]; + var symbol = pieces[0]; + if (symbol === 'BTC') continue; + var price = +response[model]['last']; + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + } + console.log(data) + }, '/api/v1/ticker'); + } else if (exchange == "tradecx") { + apiInterfaces.jsonHttpRequest('tradecx.io', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + let data = {}; + if (!error && response) { + for (let model in response) { + let len = model.length; + if (len <= 3) continue; + target = model.substring(len - 3, len) + .toUpperCase(); + symbol = model.substring(0, len - 3) + .toUpperCase(); + + price = +response[model]['ticker']['last']; + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, '/api/tickers'); + } else if (exchange == "coingecko") { + apiInterfaces.jsonHttpRequest('api.coingecko.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (!error && response) { + let matchingCoin = response.filter(coin => { + return coin.symbol === config.symbol.toLowerCase() ? coin.name.toLowerCase() : '' + }) + apiInterfaces.jsonHttpRequest('api.coingecko.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + let data = {}; + if (!error && response.tickers) { + for (let model in response.tickers) { + target = response.tickers[model].target + symbol = response.tickers[model].base + + price = +response.tickers[model].last + if (price === 0) continue; + + if (!data[symbol]) data[symbol] = {}; + data[symbol][target] = price; + } + } + if (!error) marketRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(null, data); + }, `/api/v3/coins/${matchingCoin[0].id}/tickers`); + + } + }, `/api/v3/coins/list`); + } + // Unknown + else { + callback('Exchange not supported: ' + exchange); + } } exports.getExchangeMarkets = getExchangeMarkets; @@ -248,138 +457,283 @@ exports.getExchangeMarkets = getExchangeMarkets; * Get Exchange Market Price **/ -var priceRequestsCache = {}; - -function getExchangePrice(exchange, base, target, callback) { - callback = callback || function(){}; - - if (!exchange) { - callback('No exchange specified'); - } - else if (!base) { - callback('No base specified'); - } - else if (!target) { - callback('No target specified'); - } - - exchange = exchange.toLowerCase(); - base = base.toUpperCase(); - target = target.toUpperCase(); - - // Return cache if available - var cacheKey = exchange + '-' + base + '-' + target; - var currentTimestamp = Date.now() / 1000; - - if (priceRequestsCache[cacheKey] && priceRequestsCache[cacheKey].ts > (currentTimestamp - 60)) { - callback(null, priceRequestsCache[cacheKey].data); - return ; - } - - // Cryptonator - if (exchange == "cryptonator") { - var ticker = base + '-' + target; - apiInterfaces.jsonHttpRequest('api.cryptonator.com', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - if (response.error) log('warn', logSystem, 'Cryptonator API error: %s', [response.error]); - - var error = response.error ? response.error : error; - var price = response.success ? +response.ticker.price : null; - if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); - - var data = { ticker: ticker, price: price, source: exchange }; - if (!error) priceRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(error, data); - }, '/api/ticker/' + ticker); - } - - // Altex - else if (exchange == "altex") { - getExchangeMarkets(exchange, function(error, data) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - - var price = null; - if (!error && data[base] && data[base][target]) { - price = data[base][target]; - } - if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); - - var data = { ticker: ticker, price: price, source: exchange }; - if (!error) priceRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(error, data); - }); - } - - // Crex24 - else if (exchange == "crex24") { - var ticker = base + '_' + target; - apiInterfaces.jsonHttpRequest('api.crex24.com', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - if (response.Error) log('warn', logSystem, 'Crex24 API error: %s', [response.Error]); - - var error = response.Error ? response.Error : error; - var price = (response.Tickers && response.Tickers[0]) ? +response.Tickers[0].Last : null; - if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); - - var data = { ticker: ticker, price: price, source: exchange }; - if (!error) priceRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(error, data); - }, '/CryptoExchangeService/BotPublic/ReturnTicker?request=[NamePairs=' + ticker + ']'); - } - - // Cryptopia - else if (exchange == "cryptopia") { - var ticker = base + '_' + target; - apiInterfaces.jsonHttpRequest('www.cryptopia.co.nz', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - if (response.Error) log('warn', logSystem, 'Cryptopia API error: %s', [response.Error]); - - var error = response.Error ? response.Error : error; - var price = (response.Data && response.Data.LastPrice) ? +response.Data.LastPrice : null; - - var data = { ticker: ticker, price: price, source: exchange }; - if (!error) priceRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(error, data); - }, '/api/GetMarket/' + ticker); - } - - // Stocks.Exchange - else if (exchange == "stocks.exchange") { - getExchangeMarkets(exchange, function(error, data) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - - var price; - if (!error && data[base] && data[base][target]) { - price = data[base][target]; - } - if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); - - var data = { ticker: ticker, price: price, source: exchange }; - if (!error) priceRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(error, data); - }); - } - - // TradeOgre - else if (exchange == "tradeogre") { - var ticker = target + '-' + base; - apiInterfaces.jsonHttpRequest('tradeogre.com', 443, '', function(error, response) { - if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); - if (response.message) log('warn', logSystem, 'TradeOgre API error: %s', [response.message]); - - var error = response.message ? response.message : error; - var price = +response.price || null; - if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); - - var data = { ticker: ticker, price: price, source: exchange }; - if (!error) priceRequestsCache[cacheKey] = { ts: currentTimestamp, data: data }; - callback(error, data); - }, '/api/v1/ticker/' + ticker); - } - - // Unknown - else { - callback('Exchange not supported: ' + exchange); - } +let priceRequestsCache = {}; + +function getExchangePrice (exchange, base, target, callback) { + callback = callback || function () {}; + + if (!exchange) { + callback('No exchange specified'); + } else if (!base) { + callback('No base specified'); + } else if (!target) { + callback('No target specified'); + } + + exchange = exchange.toLowerCase(); + base = base.toUpperCase(); + target = target.toUpperCase(); + + // Return cache if available + let cacheKey = exchange + '-' + base + '-' + target; + let currentTimestamp = Date.now() / 1000; + + let error = null; + let price = 0.0; + let data = {}; + let ticker = null; + + if (priceRequestsCache[cacheKey] && priceRequestsCache[cacheKey].ts > (currentTimestamp - 60)) { + callback(null, priceRequestsCache[cacheKey].data); + return; + } + + // Cryptonator + if (exchange == "cryptonator") { + ticker = base + '-' + target; + apiInterfaces.jsonHttpRequest('api.cryptonator.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (response.error) log('warn', logSystem, 'Cryptonator API error: %s', [response.error]); + + error = response.error ? response.error : error; + price = response.success ? +response.ticker.price : null; + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + }; + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }, '/api/ticker/' + ticker); + } + + // Altex + else if (exchange == "altex") { + getExchangeMarkets(exchange, function (error, data) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + price = null; + if (!error && data[base] && data[base][target]) { + price = data[base][target]; + } + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + }; + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }); + } + + // Crex24 + else if (exchange == "crex24") { + ticker = base + '_' + target; + apiInterfaces.jsonHttpRequest('api.crex24.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (response.Error) log('warn', logSystem, 'Crex24 API error: %s', [response.Error]); + + error = response.Error ? response.Error : error; + price = (response.Tickers && response.Tickers[0]) ? +response.Tickers[0].Last : null; + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + }; + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }, '/CryptoExchangeService/BotPublic/ReturnTicker?request=[NamePairs=' + ticker + ']'); + } + + // Cryptopia + else if (exchange == "cryptopia") { + ticker = base + '_' + target; + apiInterfaces.jsonHttpRequest('www.cryptopia.co.nz', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (response.Error) log('warn', logSystem, 'Cryptopia API error: %s', [response.Error]); + + error = response.Error ? response.Error : error; + price = (response.Data && response.Data.LastPrice) ? +response.Data.LastPrice : null; + + data = { + ticker: ticker, + price: price, + source: exchange + }; + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }, '/api/GetMarket/' + ticker); + } + + // Stocks.Exchange + else if (exchange == "stocks.exchange") { + getExchangeMarkets(exchange, function (error, data) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + + if (!error && data[base] && data[base][target]) { + price = data[base][target]; + } + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + }; + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }); + } + + // TradeOgre + else if (exchange == "tradeogre") { + ticker = target + '-' + base; + apiInterfaces.jsonHttpRequest('tradeogre.com', 443, '', function (error, response) { + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (response.message) log('warn', logSystem, 'TradeOgre API error: %s', [response.message]); + + error = response.message ? response.message : error; + price = +response.price || null; + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + }; + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }, '/api/v2/ticker/' + ticker); + } + // Btc-Alpha + else if (exchange == "btcalpha") { + ticker = base + '_' + target; + apiInterfaces.jsonHttpRequest('btc-alpha.com', 443, '', function (error, response) { + + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (response.message) log('warn', logSystem, 'BTC-Alpha API error: %s', [response.message]); + + error = response.message ? response.message : error; + + price = response[0] != undefined ? response[0]['price'] : null; + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + } + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }, '/api/v1/exchanges/?pair=' + ticker + '&limit=1'); + + } + // tradesatoshi + else if (exchange == "tradesatoshi") { + ticker = base + '_' + target; + apiInterfaces.jsonHttpRequest('tradesatoshi.com', 443, '', function (error, response) { + + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (response.message) log('warn', logSystem, 'BTC-Alpha API error: %s', [response.message]); + + error = response.message ? response.message : error; + + price = response.result != undefined ? response.result['last'] : null; + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + } + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }, '/api/public/getmarketsummary?market=' + ticker); + + } + // coinmarketcap + else if (exchange == "coinmarketcap") { + apiInterfaces.jsonHttpRequest('api.coinmarketcap.com', 443, '', function (error, response) { + + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (response.message) log('warn', logSystem, 'CoinMarketCap API error: %s', [response.message]); + + error = response.message ? response.message : error; + + price = response ? +response.data.quotes[ticker].price : null; + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + } + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }, '/v2/ticker/1/?convert=' + target); + + } + // tradecx + else if (exchange == "tradecx") { + apiInterfaces.jsonHttpRequest('tradecx.io', 443, '', function (error, response) { + + if (error) log('error', logSystem, 'API request to %s has failed: %s', [exchange, error]); + if (response.message) log('warn', logSystem, 'CoinMarketCap API error: %s', [response.message]); + + error = response.message ? response.message : error; + + price = response ? +response.data.quotes[ticker].price : null; + if (!price) log('warn', logSystem, 'No exchange data for %s using %s', [ticker, exchange]); + + data = { + ticker: ticker, + price: price, + source: exchange + } + if (!error) priceRequestsCache[cacheKey] = { + ts: currentTimestamp, + data: data + }; + callback(error, data); + }, '/v2/tickers/' + target); + + } + // Unknown + else { + callback('Exchange not supported: ' + exchange); + } } exports.getExchangePrice = getExchangePrice; diff --git a/lib/notifications.js b/lib/notifications.js index decf17781..69036a27d 100644 --- a/lib/notifications.js +++ b/lib/notifications.js @@ -9,52 +9,52 @@ **/ // Load required modules -var fs = require('fs'); +let fs = require('fs'); -var emailSystem = require('./email.js'); -var telegram = require('./telegram.js'); -var utils = require('./utils.js'); +let emailSystem = require('./email.js'); +let telegram = require('./telegram.js'); +let utils = require('./utils.js'); // Initialize log system -var logSystem = 'notifications'; +let logSystem = 'notifications'; require('./exceptionWriter.js')(logSystem); // Load notification settings -var notificationSettings = { - emailTemplate: "email/template.txt", - emailSubject: { - emailAdded: "Your email was registered", - workerConnected: "Worker %WORKER_NAME% connected", - workerTimeout: "Worker %WORKER_NAME% stopped hashing", - workerBanned: "Worker %WORKER_NAME% banned", - blockFound: "Block %HEIGHT% found !", - blockUnlocked: "Block %HEIGHT% unlocked !", - blockOrphaned: "Block %HEIGHT% orphaned !", - payment: "We sent you a payment !" - }, - emailMessage: { - emailAdded: "Your email has been registered to receive pool notifications.", - workerConnected: "Your worker %WORKER_NAME% is now connected.", - workerTimeout: "Your worker %WORKER_NAME% has stopped submitting hashes on %LAST_HASH%.", - workerBanned: "Your worker %WORKER_NAME% has been banned.", - blockFound: "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - blockUnlocked: "Block mined at %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - blockOrphaned: "Block orphaned at height %HEIGHT% :(", - payment: "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - telegramMessage: { - workerConnected: "Your worker _%WORKER_NAME%_ is now connected.", - workerTimeout: "Your worker _%WORKER_NAME%_ has stopped submitting hashes on _%LAST_HASH%_.", - workerBanned: "Your worker _%WORKER_NAME%_ has been banned.", - blockFound: "*Block found at height _%HEIGHT%_ by miner _%MINER%_. Waiting maturity.*", - blockUnlocked: "*Block mined at _%HEIGHT%_ with _%REWARD%_ and _%EFFORT%_ effort on _%TIME%_.*", - blockOrphaned: "*Block orphaned at height _%HEIGHT%_ :(*", - payment: "A payment of _%AMOUNT%_ has been sent." - } +let notificationSettings = { + emailTemplate: "email/template.txt", + emailSubject: { + emailAdded: "Your email was registered", + workerConnected: "Worker %WORKER_NAME% connected", + workerTimeout: "Worker %WORKER_NAME% stopped hashing", + workerBanned: "Worker %WORKER_NAME% banned", + blockFound: "Block %HEIGHT% found !", + blockUnlocked: "Block %HEIGHT% unlocked !", + blockOrphaned: "Block %HEIGHT% orphaned !", + payment: "We sent you a payment !" + }, + emailMessage: { + emailAdded: "Your email has been registered to receive pool notifications.", + workerConnected: "Your worker %WORKER_NAME% is now connected.", + workerTimeout: "Your worker %WORKER_NAME% has stopped submitting hashes on %LAST_HASH%.", + workerBanned: "Your worker %WORKER_NAME% has been banned.", + blockFound: "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + blockUnlocked: "Block mined at %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + blockOrphaned: "Block orphaned at height %HEIGHT% :(", + payment: "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + telegramMessage: { + workerConnected: "Your worker _%WORKER_NAME%_ is now connected.", + workerTimeout: "Your worker _%WORKER_NAME%_ has stopped submitting hashes on _%LAST_HASH%_.", + workerBanned: "Your worker _%WORKER_NAME%_ has been banned.", + blockFound: "*Block found at height _%HEIGHT%_ by miner _%MINER%_. Waiting maturity.*", + blockUnlocked: "*Block mined at _%HEIGHT%_ with _%REWARD%_ and _%EFFORT%_ effort on _%TIME%_.*", + blockOrphaned: "*Block orphaned at height _%HEIGHT%_ :(*", + payment: "A payment of _%AMOUNT%_ has been sent." + } }; if (config.notifications) { - Object.assign(notificationSettings, config.notifications); + Object.assign(notificationSettings, config.notifications); } // Test notification message @@ -65,175 +65,175 @@ notificationSettings.telegramMessage['test'] = "This is a test notification from /** * Send global notification **/ -exports.sendToAll = function(id, variables) { - // Send telegram to channel - sendToTelegramChannel(id, variables); +exports.sendToAll = function (id, variables) { + // Send telegram to channel + sendToTelegramChannel(id, variables); - // Send blocks notifications to telegram - if (id === "blockFound" || id === "blockUnlocked" || id === "blockOrphaned") { - sendBlockTelegram(id, variables); - } + // Send blocks notifications to telegram + if (id === "blockFound" || id === "blockUnlocked" || id === "blockOrphaned") { + sendBlockTelegram(id, variables); + } - // Send to all pool email addresses - sendToAllEmails(id, variables); + // Send to all pool email addresses + sendToAllEmails(id, variables); } /** * Send miner notification **/ -exports.sendToMiner = function(miner, id, variables) { - // Send telegram - sendToMinerTelegram(miner, id, variables); +exports.sendToMiner = function (miner, id, variables) { + // Send telegram + sendToMinerTelegram(miner, id, variables); - // Send email - sendToMinerEmail(miner, id, variables); + // Send email + sendToMinerEmail(miner, id, variables); } /** * Send telegram channel notification **/ -function sendToTelegramChannel(id, variables) { - // Set custom variables - variables = setCustomVariables(variables); - - // Send notification - if (config.telegram && config.telegram.enabled) { - var message = getTelegramMessage(id, variables); - if (!message || message === '') { - log('info', logSystem, 'Notification disabled for %s: empty telegram message.', [id]); - return ; - } - - var channel = config.telegram.channel.replace(/@/g, '') || ''; - if (!channel) { - log('error', logSystem, 'No telegram channel specified in configuration!'); - return ; - } - - var chatId = '@' + channel; - telegram.sendMessage(chatId, message); - } +function sendToTelegramChannel (id, variables) { + // Set custom variables + variables = setCustomVariables(variables); + + // Send notification + if (config.telegram && config.telegram.enabled) { + let message = getTelegramMessage(id, variables); + if (!message || message === '') { + log('info', logSystem, 'Notification disabled for %s: empty telegram message.', [id]); + return; + } + + let channel = config.telegram.channel.replace(/@/g, '') || ''; + if (!channel) { + log('error', logSystem, 'No telegram channel specified in configuration!'); + return; + } + + let chatId = '@' + channel; + telegram.sendMessage(chatId, message); + } } exports.sendToTelegramChannel = sendToTelegramChannel; /** * Send telegram to miner in private message **/ -function sendToMinerTelegram(miner, id, variables) { - // Set custom variables - variables = setCustomVariables(variables); - - // Send telegram - if (config.telegram && config.telegram.enabled) { - var message = getTelegramMessage(id, variables); - if (!message || message === '') { - log('info', logSystem, 'Notification disabled for %s: empty telegram message.', [id]); - return; - } - - redisClient.hget(config.coin + ':telegram', miner, function(error, chatId) { - if (error || !chatId) return; - telegram.sendMessage(chatId, message); - }); - } +function sendToMinerTelegram (miner, id, variables) { + // Set custom variables + variables = setCustomVariables(variables); + + // Send telegram + if (config.telegram && config.telegram.enabled) { + let message = getTelegramMessage(id, variables); + if (!message || message === '') { + log('info', logSystem, 'Notification disabled for %s: empty telegram message.', [id]); + return; + } + + redisClient.hget(config.coin + ':telegram', miner, function (error, chatId) { + if (error || !chatId) return; + telegram.sendMessage(chatId, message); + }); + } } exports.sendToMinerTelegram = sendToMinerTelegram; - + /** * Send block notification telegram to miner in private message **/ -function sendBlockTelegram(id, variables) { - // Set custom variables - variables = setCustomVariables(variables); - - // Send telegram - if (config.telegram && config.telegram.enabled) { - var message = getTelegramMessage(id, variables); - if (!message || message === '') { - log('info', logSystem, 'Notification disabled for %s: empty telegram message.', [id]); - return; - } - - redisClient.hgetall(config.coin + ':telegram:blocks', function(error, data) { - if (error || !data) return ; - for (var chatId in data) { - if (!chatId) continue; - telegram.sendMessage(chatId, message); - } - }); - } +function sendBlockTelegram (id, variables) { + // Set custom variables + variables = setCustomVariables(variables); + + // Send telegram + if (config.telegram && config.telegram.enabled) { + let message = getTelegramMessage(id, variables); + if (!message || message === '') { + log('info', logSystem, 'Notification disabled for %s: empty telegram message.', [id]); + return; + } + + redisClient.hgetall(config.coin + ':telegram:blocks', function (error, data) { + if (error || !data) return; + for (let chatId in data) { + if (!chatId) continue; + telegram.sendMessage(chatId, message); + } + }); + } } exports.sendBlockTelegram = sendBlockTelegram; /** * Send email notification to all pool email addresses **/ -function sendToAllEmails(id, variables) { - // Set custom variables - variables = setCustomVariables(variables); - - // Send email - if (config.email && config.email.enabled) { - var subject = getEmailSubject(id, variables); - var content = getEmailContent(id, variables); - if (!content || content === '') { - log('info', logSystem, 'Notification disabled for %s: empty email content.', [id]); - return; - } - - redisClient.hgetall(config.coin + ':notifications', function(error, data) { - if (error || !data) return ; - for (var address in data) { - var email = data[address]; - emailSystem.sendEmail(email, subject, content); - } - }); - } +function sendToAllEmails (id, variables) { + // Set custom variables + variables = setCustomVariables(variables); + + // Send email + if (config.email && config.email.enabled) { + let subject = getEmailSubject(id, variables); + let content = getEmailContent(id, variables); + if (!content || content === '') { + log('info', logSystem, 'Notification disabled for %s: empty email content.', [id]); + return; + } + + redisClient.hgetall(config.coin + ':notifications', function (error, data) { + if (error || !data) return; + for (let address in data) { + let email = data[address]; + emailSystem.sendEmail(email, subject, content); + } + }); + } } exports.sendToAllEmails = sendToAllEmails; /** * Send email notification to miner email address **/ -function sendToMinerEmail(miner, id, variables) { - // Set custom variables - variables = setCustomVariables(variables); - - // Send email - if (config.email && config.email.enabled) { - var subject = getEmailSubject(id, variables); - var content = getEmailContent(id, variables); - if (!content || content === '') { - log('info', logSystem, 'Notification disabled for %s: empty email content.', [id]); - return; - } - - redisClient.hget(config.coin + ':notifications', miner, function(error, email) { - if (error || !email) return ; - emailSystem.sendEmail(email, subject, content); - }); - } +function sendToMinerEmail (miner, id, variables) { + // Set custom variables + variables = setCustomVariables(variables); + + // Send email + if (config.email && config.email.enabled) { + let subject = getEmailSubject(id, variables); + let content = getEmailContent(id, variables); + if (!content || content === '') { + log('info', logSystem, 'Notification disabled for %s: empty email content.', [id]); + return; + } + + redisClient.hget(config.coin + ':notifications', miner, function (error, email) { + if (error || !email) return; + emailSystem.sendEmail(email, subject, content); + }); + } } exports.sendToMinerEmail = sendToMinerEmail; /** * Send email notification to a specific email address **/ -function sendToEmail(email, id, variables) { - // Set custom variables - variables = setCustomVariables(variables); - - // Send notification - if (config.email && config.email.enabled) { - var subject = getEmailSubject(id, variables); - var content = getEmailContent(id, variables); - if (!content || content === '') { - log('info', logSystem, 'Notification disabled for %s: empty email content.', [id]); - return; - } - - emailSystem.sendEmail(email, subject, content); - } +function sendToEmail (email, id, variables) { + // Set custom variables + variables = setCustomVariables(variables); + + // Send notification + if (config.email && config.email.enabled) { + let subject = getEmailSubject(id, variables); + let content = getEmailContent(id, variables); + if (!content || content === '') { + log('info', logSystem, 'Notification disabled for %s: empty email content.', [id]); + return; + } + + emailSystem.sendEmail(email, subject, content); + } } exports.sendToEmail = sendToEmail; @@ -242,26 +242,26 @@ exports.sendToEmail = sendToEmail; **/ // Get email subject -function getEmailSubject(id, variables) { - var subject = replaceVariables(notificationSettings.emailSubject[id], variables) || ''; - return subject; +function getEmailSubject (id, variables) { + let subject = replaceVariables(notificationSettings.emailSubject[id], variables) || ''; + return subject; } // Get email content -function getEmailContent(id, variables) { - var message = notificationSettings.emailMessage[id] || ''; - if (!message || message === '') return ''; - - var content = message; - if (notificationSettings.emailTemplate) { - if (!fs.existsSync(notificationSettings.emailTemplate)) { - log('warn', logSystem, 'Email template file not found: %s', [notificationSettings.emailTemplate]); - } - content = fs.readFileSync(notificationSettings.emailTemplate, 'utf8'); - content = content.replace(/%MESSAGE%/g, message); - } - content = replaceVariables(content, variables); - return content; +function getEmailContent (id, variables) { + let message = notificationSettings.emailMessage[id] || ''; + if (!message || message === '') return ''; + + let content = message; + if (notificationSettings.emailTemplate) { + if (!fs.existsSync(notificationSettings.emailTemplate)) { + log('warn', logSystem, 'Email template file not found: %s', [notificationSettings.emailTemplate]); + } + content = fs.readFileSync(notificationSettings.emailTemplate, 'utf8'); + content = content.replace(/%MESSAGE%/g, message); + } + content = replaceVariables(content, variables); + return content; } /** @@ -269,40 +269,40 @@ function getEmailContent(id, variables) { **/ // Get telegram message -function getTelegramMessage(id, variables) { - var telegramVars = {}; - if (telegramVars) { - for (var varName in variables) { - var value = variables[varName].toString(); - value = value.replace(/\*/g, '.'); - value = value.replace(/_/g, ' '); - telegramVars[varName] = value; - } - } - var message = replaceVariables(notificationSettings.telegramMessage[id], telegramVars) || ''; - return message; +function getTelegramMessage (id, variables) { + let telegramVars = {}; + if (telegramVars) { + for (let varName in variables) { + let value = variables[varName].toString(); + value = value.replace(/\*/g, '.'); + value = value.replace(/_/g, ' '); + telegramVars[varName] = value; + } + } + let message = replaceVariables(notificationSettings.telegramMessage[id], telegramVars) || ''; + return message; } /** * Handle variables in texts **/ - + // Set custom variables -function setCustomVariables(variables) { - if (!variables) variables = {}; - variables['TIME'] = utils.dateFormat(Date.now(), 'yyyy-mm-dd HH:MM:ss Z'); - variables['POOL_HOST'] = config.poolHost || ''; - return variables; +function setCustomVariables (variables) { + if (!variables) variables = {}; + variables['TIME'] = utils.dateFormat(Date.now(), 'yyyy-mm-dd HH:MM:ss Z'); + variables['POOL_HOST'] = config.poolHost || ''; + return variables; } // Replace variables in a string -function replaceVariables(string, variables) { - if (!string) return ''; - if (variables) { - for (var varName in variables) { - string = string.replace(new RegExp('%'+varName+'%', 'g'), variables[varName]); - } - string = string.replace(/ /g, ' '); - } - return string; +function replaceVariables (string, variables) { + if (!string) return ''; + if (variables) { + for (let varName in variables) { + string = string.replace(new RegExp('%' + varName + '%', 'g'), variables[varName]); + } + string = string.replace(/ /g, ' '); + } + return string; } diff --git a/lib/paymentProcessor.js b/lib/paymentProcessor.js index d12d4dfa9..0a800a70f 100644 --- a/lib/paymentProcessor.js +++ b/lib/paymentProcessor.js @@ -6,297 +6,310 @@ **/ // Load required modules -var fs = require('fs'); -var async = require('async'); +let fs = require('fs'); +let async = require('async'); -var apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); -var notifications = require('./notifications.js'); -var utils = require('./utils.js'); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +let notifications = require('./notifications.js'); +let utils = require('./utils.js'); // Initialize log system -var logSystem = 'payments'; +let logSystem = 'payments'; require('./exceptionWriter.js')(logSystem); /** * Run payments processor **/ - + log('info', logSystem, 'Started'); if (!config.poolServer.paymentId) config.poolServer.paymentId = {}; if (!config.poolServer.paymentId.addressSeparator) config.poolServer.paymentId.addressSeparator = "+"; if (!config.payments.priority) config.payments.priority = 0; -function runInterval(){ - async.waterfall([ +function runInterval () { + async.waterfall([ // Get worker keys - function(callback){ - redisClient.keys(config.coin + ':workers:*', function(error, result) { - if (error) { - log('error', logSystem, 'Error trying to get worker balances from redis %j', [error]); - callback(true); - return; - } - callback(null, result); - }); + function (callback) { + redisClient.keys(config.coin + ':workers:*', function (error, result) { + if (error) { + log('error', logSystem, 'Error trying to get worker balances from redis %j', [error]); + callback(true); + return; + } + callback(null, result); + }); }, // Get worker balances - function(keys, callback){ - var redisCommands = keys.map(function(k){ - return ['hget', k, 'balance']; - }); - redisClient.multi(redisCommands).exec(function(error, replies){ - if (error){ - log('error', logSystem, 'Error with getting balances from redis %j', [error]); - callback(true); - return; - } - - var balances = {}; - for (var i = 0; i < replies.length; i++){ - var parts = keys[i].split(':'); - var workerId = parts[parts.length - 1]; - - balances[workerId] = parseInt(replies[i]) || 0; - } - callback(null, keys, balances); - }); + function (keys, callback) { + let redisCommands = keys.map(function (k) { + return ['hget', k, 'balance']; + }); + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with getting balances from redis %j', [error]); + callback(true); + return; + } + + let balances = {}; + for (let i = 0; i < replies.length; i++) { + let parts = keys[i].split(':'); + let workerId = parts[parts.length - 1]; + + balances[workerId] = parseInt(replies[i]) || 0; + } + callback(null, keys, balances); + }); }, // Get worker minimum payout - function(keys, balances, callback){ - var redisCommands = keys.map(function(k){ - return ['hget', k, 'minPayoutLevel']; - }); - redisClient.multi(redisCommands).exec(function(error, replies){ - if (error){ - log('error', logSystem, 'Error with getting minimum payout from redis %j', [error]); - callback(true); - return; - } - - var minPayoutLevel = {}; - for (var i = 0; i < replies.length; i++){ - var parts = keys[i].split(':'); - var workerId = parts[parts.length - 1]; - - var minLevel = config.payments.minPayment; - var maxLevel = config.payments.maxPayment; - var defaultLevel = minLevel; - - var payoutLevel = parseInt(replies[i]) || minLevel; - if (payoutLevel < minLevel) payoutLevel = minLevel; - if (maxLevel && payoutLevel > maxLevel) payoutLevel = maxLevel; - minPayoutLevel[workerId] = payoutLevel; - - if (payoutLevel !== defaultLevel) { - log('info', logSystem, 'Using payout level of %s for %s (default: %s)', [ utils.getReadableCoins(minPayoutLevel[workerId]), workerId, utils.getReadableCoins(defaultLevel) ]); - } - } - callback(null, balances, minPayoutLevel); - }); + function (keys, balances, callback) { + let redisCommands = keys.map(function (k) { + return ['hget', k, 'minPayoutLevel']; + }); + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with getting minimum payout from redis %j', [error]); + callback(true); + return; + } + + let minPayoutLevel = {}; + for (let i = 0; i < replies.length; i++) { + let parts = keys[i].split(':'); + let workerId = parts[parts.length - 1]; + + let minLevel = config.payments.minPayment; + let maxLevel = config.payments.maxPayment; + let defaultLevel = minLevel; + + let payoutLevel = parseInt(replies[i]) || minLevel; + if (payoutLevel < minLevel) payoutLevel = minLevel; + if (maxLevel && payoutLevel > maxLevel) payoutLevel = maxLevel; + minPayoutLevel[workerId] = payoutLevel; + + if (payoutLevel !== defaultLevel) { + log('info', logSystem, 'Using payout level of %s for %s (default: %s)', [utils.getReadableCoins(minPayoutLevel[workerId]), workerId, utils.getReadableCoins(defaultLevel)]); + } + } + callback(null, balances, minPayoutLevel); + }); }, // Filter workers under balance threshold for payment - function(balances, minPayoutLevel, callback){ - var payments = {}; - - for (var worker in balances){ - var balance = balances[worker]; - if (balance >= minPayoutLevel[worker]){ - var remainder = balance % config.payments.denomination; - var payout = balance - remainder; - - if (config.payments.dynamicTransferFee && config.payments.minerPayFee){ - payout -= config.payments.transferFee; - } - if (payout < 0) continue; - - payments[worker] = payout; - } - } - - if (Object.keys(payments).length === 0){ - log('info', logSystem, 'No workers\' balances reached the minimum payment threshold'); - callback(true); - return; - } - - var transferCommands = []; - var addresses = 0; - var commandAmount = 0; - var commandIndex = 0; - - for (var worker in payments){ - var amount = parseInt(payments[worker]); - if(config.payments.maxTransactionAmount && amount + commandAmount > config.payments.maxTransactionAmount) { - amount = config.payments.maxTransactionAmount - commandAmount; - } - - var address = worker; - var payment_id = null; - - var with_payment_id = false; - - var addr = address.split(config.poolServer.paymentId.addressSeparator); - if ((addr.length === 1 && utils.isIntegratedAddress(address)) || addr.length >= 2){ - with_payment_id = true; - if (addr.length >= 2){ - address = addr[0]; - payment_id = addr[1]; - payment_id = payment_id.replace(/[^A-Za-z0-9]/g,''); - if (payment_id.length !== 16 && payment_id.length !== 64) { - with_payment_id = false; - payment_id = null; - } - } - if (addresses > 0){ - commandIndex++; - addresses = 0; - commandAmount = 0; - } - } - - if (config.poolServer.fixedDiff && config.poolServer.fixedDiff.enabled) { - var addr = address.split(config.poolServer.fixedDiff.addressSeparator); - if (addr.length >= 2) address = addr[0]; - } - - if(!transferCommands[commandIndex]) { - transferCommands[commandIndex] = { - redis: [], - amount : 0, - rpc: { - destinations: [], - fee: config.payments.transferFee, - mixin: config.payments.mixin, - priority: config.payments.priority, - unlock_time: 0 - } - }; - } - - transferCommands[commandIndex].rpc.destinations.push({amount: amount, address: address}); - if (payment_id) transferCommands[commandIndex].rpc.payment_id = payment_id; - - transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'balance', -amount]); - if(config.payments.dynamicTransferFee && config.payments.minerPayFee){ - transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'balance', -config.payments.transferFee]); - } - transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'paid', amount]); - transferCommands[commandIndex].amount += amount; - - addresses++; - commandAmount += amount; - - if (config.payments.dynamicTransferFee){ - transferCommands[commandIndex].rpc.fee = config.payments.transferFee * addresses; - } - - if (addresses >= config.payments.maxAddresses || (config.payments.maxTransactionAmount && commandAmount >= config.payments.maxTransactionAmount) || with_payment_id) { - commandIndex++; - addresses = 0; - commandAmount = 0; - } - } - - var timeOffset = 0; - var notify_miners = []; - - var daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; - - async.filter(transferCommands, function(transferCmd, cback){ - var rpcCommand = "transfer"; - var rpcRequest = transferCmd.rpc; - - if (daemonType === "bytecoin") { - rpcCommand = "sendTransaction"; - rpcRequest = { - transfers: transferCmd.rpc.destinations, - fee: transferCmd.rpc.fee, - anonymity: transferCmd.rpc.mixin, - unlockTime: transferCmd.rpc.unlock_time - }; - if (transferCmd.rpc.payment_id) { - rpcRequest.paymentId = transferCmd.rpc.payment_id; - } - } - - apiInterfaces.rpcWallet(rpcCommand, rpcRequest, function(error, result){ - if (error){ - log('error', logSystem, 'Error with %s RPC request to wallet daemon %j', [rpcCommand, error]); - log('error', logSystem, 'Payments failed to send to %j', transferCmd.rpc.destinations); - cback(false); - return; - } - - var now = (timeOffset++) + Date.now() / 1000 | 0; - var txHash = daemonType === "bytecoin" ? result.transactionHash : result.tx_hash; - txHash = txHash.replace('<', '').replace('>', ''); - - transferCmd.redis.push(['zadd', config.coin + ':payments:all', now, [ + function (balances, minPayoutLevel, callback) { + let payments = {}; + + for (let worker in balances) { + let balance = balances[worker]; + if (balance >= minPayoutLevel[worker]) { + let remainder = balance % config.payments.denomination; + let payout = balance - remainder; + + if (config.payments.dynamicTransferFee && config.payments.minerPayFee) { + payout -= config.payments.transferFee; + } + if (payout < 0) continue; + + payments[worker] = payout; + } + } + + if (Object.keys(payments) + .length === 0) { + log('info', logSystem, 'No workers\' balances reached the minimum payment threshold'); + callback(true); + return; + } + + let transferCommands = []; + let addresses = 0; + let commandAmount = 0; + let commandIndex = 0; + let ringSize = config.payments.ringSize ? config.payments.ringSize : config.payments.mixin; + + for (let worker in payments) { + let amount = parseInt(payments[worker]); + if (config.payments.maxTransactionAmount && amount + commandAmount > config.payments.maxTransactionAmount) { + amount = config.payments.maxTransactionAmount - commandAmount; + } + + let address = worker; + let payment_id = null; + + let with_payment_id = false; + + let addr = address.split(config.poolServer.paymentId.addressSeparator); + if ((addr.length === 1 && utils.isIntegratedAddress(address)) || addr.length >= 2) { + with_payment_id = true; + if (addr.length >= 2) { + address = addr[0]; + payment_id = addr[1]; + payment_id = payment_id.replace(/[^A-Za-z0-9]/g, ''); + if (payment_id.length !== 16 && payment_id.length !== 64) { + with_payment_id = false; + payment_id = null; + } + } + if (addresses > 0) { + commandIndex++; + addresses = 0; + commandAmount = 0; + } + } + + if (config.poolServer.fixedDiff && config.poolServer.fixedDiff.enabled) { + addr = address.split(config.poolServer.fixedDiff.addressSeparator); + if (addr.length >= 2) address = addr[0]; + } + + if (!transferCommands[commandIndex]) { + transferCommands[commandIndex] = { + redis: [], + amount: 0, + rpc: { + destinations: [], + fee: config.payments.transferFee, + priority: config.payments.priority, + unlock_time: 0 + } + }; + if (config.payments.ringSize) + transferCommands[commandIndex].rpc.ring_size = ringSize; + else + transferCommands[commandIndex].rpc.mixin = ringSize; + } + + transferCommands[commandIndex].rpc.destinations.push({ + amount: amount, + address: address + }); + if (payment_id) transferCommands[commandIndex].rpc.payment_id = payment_id; + + transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'balance', -amount]); + if (config.payments.dynamicTransferFee && config.payments.minerPayFee) { + transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'balance', -config.payments.transferFee]); + } + transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'paid', amount]); + transferCommands[commandIndex].amount += amount; + + addresses++; + commandAmount += amount; + + if (config.payments.dynamicTransferFee) { + transferCommands[commandIndex].rpc.fee = config.payments.transferFee * addresses; + } + + if (addresses >= config.payments.maxAddresses || (config.payments.maxTransactionAmount && commandAmount >= config.payments.maxTransactionAmount) || with_payment_id) { + commandIndex++; + addresses = 0; + commandAmount = 0; + } + } + + let timeOffset = 0; + let notify_miners = []; + + let daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; + + async.filter(transferCommands, function (transferCmd, cback) { + let rpcCommand = "transfer"; + let rpcRequest = transferCmd.rpc; + + if (daemonType === "bytecoin") { + rpcCommand = "sendTransaction"; + rpcRequest = { + transfers: transferCmd.rpc.destinations, + fee: transferCmd.rpc.fee, + anonymity: ringSize, + unlockTime: transferCmd.rpc.unlock_time + }; + if (transferCmd.rpc.payment_id) { + rpcRequest.paymentId = transferCmd.rpc.payment_id; + } + } + + apiInterfaces.rpcWallet(rpcCommand, rpcRequest, function (error, result) { + if (error) { + log('error', logSystem, 'Error with %s RPC request to wallet daemon %j', [rpcCommand, error]); + log('error', logSystem, 'Payments failed to send to %j', transferCmd.rpc.destinations); + cback(false); + return; + } + + let now = (timeOffset++) + Date.now() / 1000 | 0; + let txHash = daemonType === "bytecoin" ? result.transactionHash : result.tx_hash; + txHash = txHash.replace('<', '') + .replace('>', ''); + + transferCmd.redis.push(['zadd', config.coin + ':payments:all', now, [ txHash, transferCmd.amount, transferCmd.rpc.fee, - transferCmd.rpc.mixin, - Object.keys(transferCmd.rpc.destinations).length + ringSize, + Object.keys(transferCmd.rpc.destinations) + .length ].join(':')]); - var notify_miners_on_success = []; - for (var i = 0; i < transferCmd.rpc.destinations.length; i++){ - var destination = transferCmd.rpc.destinations[i]; - if (transferCmd.rpc.payment_id){ - destination.address += config.poolServer.paymentId.addressSeparator + transferCmd.rpc.payment_id; - } - transferCmd.redis.push(['zadd', config.coin + ':payments:' + destination.address, now, [ + let notify_miners_on_success = []; + for (let i = 0; i < transferCmd.rpc.destinations.length; i++) { + let destination = transferCmd.rpc.destinations[i]; + if (transferCmd.rpc.payment_id) { + destination.address += config.poolServer.paymentId.addressSeparator + transferCmd.rpc.payment_id; + } + transferCmd.redis.push(['zadd', config.coin + ':payments:' + destination.address, now, [ txHash, destination.amount, transferCmd.rpc.fee, - transferCmd.rpc.mixin + ringSize ].join(':')]); - notify_miners_on_success.push(destination); - } - - log('info', logSystem, 'Payments sent via wallet daemon %j', [result]); - redisClient.multi(transferCmd.redis).exec(function(error, replies){ - if (error){ - log('error', logSystem, 'Super critical error! Payments sent yet failing to update balance in redis, double payouts likely to happen %j', [error]); - log('error', logSystem, 'Double payments likely to be sent to %j', transferCmd.rpc.destinations); - cback(false); - return; - } - - for (var m in notify_miners_on_success) { - notify_miners.push(notify_miners_on_success[m]); - } - - cback(true); - }); - }); - }, function(succeeded){ - var failedAmount = transferCommands.length - succeeded.length; - - for (var m in notify_miners) { - var notify = notify_miners[m]; - log('info', logSystem, 'Payment of %s to %s', [ utils.getReadableCoins(notify.amount), notify.address ]); - notifications.sendToMiner(notify.address, 'payment', { - 'ADDRESS': notify.address.substring(0,7)+'...'+notify.address.substring(notify.address.length-7), - 'AMOUNT': utils.getReadableCoins(notify.amount), - }); - } - log('info', logSystem, 'Payments splintered and %d successfully sent, %d failed', [succeeded.length, failedAmount]); - - callback(null); - }); + notify_miners_on_success.push(destination); + } + + log('info', logSystem, 'Payments sent via wallet daemon %j', [result]); + redisClient.multi(transferCmd.redis) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Super critical error! Payments sent yet failing to update balance in redis, double payouts likely to happen %j', [error]); + log('error', logSystem, 'Double payments likely to be sent to %j', transferCmd.rpc.destinations); + cback(false); + return; + } + + for (let m in notify_miners_on_success) { + notify_miners.push(notify_miners_on_success[m]); + } + + cback(true); + }); + }); + }, function (succeeded) { + let failedAmount = transferCommands.length - succeeded.length; + + for (let m in notify_miners) { + let notify = notify_miners[m]; + log('info', logSystem, 'Payment of %s to %s', [utils.getReadableCoins(notify.amount), notify.address]); + notifications.sendToMiner(notify.address, 'payment', { + 'ADDRESS': notify.address.substring(0, 7) + '...' + notify.address.substring(notify.address.length - 7), + 'AMOUNT': utils.getReadableCoins(notify.amount), + }); + } + log('info', logSystem, 'Payments splintered and %d successfully sent, %d failed', [succeeded.length, failedAmount]); + + callback(null); + }); } - ], function(error, result){ - setTimeout(runInterval, config.payments.interval * 1000); - }); + ], function (error, result) { + setTimeout(runInterval, config.payments.interval * 1000); + }); } runInterval(); diff --git a/lib/pool.js b/lib/pool.js index 9a2a64dd1..c82a34255 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -6,1251 +6,1470 @@ **/ // Load required modules -var fs = require('fs'); -var net = require('net'); -var tls = require('tls'); -var async = require('async'); -var bignum = require('bignum'); +let fs = require('fs'); +let net = require('net'); +let tls = require('tls'); +let async = require('async'); +let bignum = require('bignum'); -var apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); -var notifications = require('./notifications.js'); -var utils = require('./utils.js'); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +let notifications = require('./notifications.js'); +let utils = require('./utils.js'); -var cnHashing = require('cryptonight-hashing'); +config.hashingUtil = config.hashingUtil || false; +let cnHashing = require('cryptonight-hashing'); +if (config.hashingUtil) + cnHashing = require('turtlecoin-multi-hashing'); // Set nonce pattern - must exactly be 8 hex chars -var noncePattern = new RegExp("^[0-9A-Fa-f]{8}$"); +let noncePattern = new RegExp("^[0-9A-Fa-f]{8}$"); // Set redis database cleanup interval -var cleanupInterval = config.redis.cleanupInterval && config.redis.cleanupInterval > 0 ? config.redis.cleanupInterval : 15; +let cleanupInterval = config.redis.cleanupInterval && config.redis.cleanupInterval > 0 ? config.redis.cleanupInterval : 15; +let fallBackCoin = typeof config.poolServer.fallBackCoin !== 'undefined' && config.poolServer.fallBackCoin ? config.poolServer.fallBackCoin : 0 // Initialize log system -var logSystem = 'pool'; +let logSystem = 'pool'; require('./exceptionWriter.js')(logSystem); -var threadId = '(Thread ' + process.env.forkId + ') '; -var log = function(severity, system, text, data){ - global.log(severity, system, threadId + text, data); +let threadId = '(Thread ' + process.env.forkId + ') '; +let log = function (severity, system, text, data) { + global.log(severity, system, threadId + text, data); }; // Set cryptonight algorithm -var cnAlgorithm = config.cnAlgorithm || "cryptonight"; -var cnVariant = config.cnVariant || 0; -var cnBlobType = config.cnBlobType || 0; +let cnAlgorithm = config.cnAlgorithm || "cryptonight"; +let cnVariant = config.cnVariant || 0; +let cnBlobType = config.cnBlobType || 0; -var cryptoNight; +let cryptoNight; if (!cnHashing || !cnHashing[cnAlgorithm]) { - log('error', logSystem, 'Invalid cryptonight algorithm: %s', [cnAlgorithm]); + log('error', logSystem, 'Invalid cryptonight algorithm: %s', [cnAlgorithm]); } else { - cryptoNight = cnHashing[cnAlgorithm]; + cryptoNight = cnHashing[cnAlgorithm]; } // Set instance id -var instanceId = utils.instanceId(); +let instanceId = utils.instanceId(); // Pool variables -var poolStarted = false; -var connectedMiners = {}; +let poolStarted = false; +let connectedMiners = {}; // Get merged mining tag reseved space size -var MERGE_MINING_TAG_RESERVED_SIZE = utils.cnUtil.get_merge_mining_tag_reserved_size(); -var POOL_NONCE_SIZE = 19; // 17 + 2 +let POOL_NONCE_SIZE = 16 + 1; // +1 for old XMR/new TRTL bugs +let EXTRA_NONCE_TEMPLATE = "02" + POOL_NONCE_SIZE.toString(16) + "00".repeat(POOL_NONCE_SIZE); +let POOL_NONCE_MM_SIZE = POOL_NONCE_SIZE + utils.cnUtil.get_merged_mining_nonce_size(); +let EXTRA_NONCE_NO_CHILD_TEMPLATE = "02" + POOL_NONCE_MM_SIZE.toString(16) + "00".repeat(POOL_NONCE_MM_SIZE); +let mergedMining = config.poolServer.mergedMining && (Array.isArray(config.childPools) && config.childPools.length > 0) + +function randomIntFromInterval (min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} -console.log(MERGE_MINING_TAG_RESERVED_SIZE + ', ' + POOL_NONCE_SIZE); // Pool settings -var shareTrustEnabled = config.poolServer.shareTrust && config.poolServer.shareTrust.enabled; -var shareTrustStepFloat = shareTrustEnabled ? config.poolServer.shareTrust.stepDown / 100 : 0; -var shareTrustMinFloat = shareTrustEnabled ? config.poolServer.shareTrust.min / 100 : 0; +let shareTrustEnabled = config.poolServer.shareTrust && config.poolServer.shareTrust.enabled; +let shareTrustStepFloat = shareTrustEnabled ? config.poolServer.shareTrust.stepDown / 100 : 0; +let shareTrustMinFloat = shareTrustEnabled ? config.poolServer.shareTrust.min / 100 : 0; + +let banningEnabled = config.poolServer.banning && config.poolServer.banning.enabled; +let bannedIPs = {}; +let perIPStats = {}; -var banningEnabled = config.poolServer.banning && config.poolServer.banning.enabled; -var bannedIPs = {}; -var perIPStats = {}; -var slushMiningEnabled = config.poolServer.slushMining && config.poolServer.slushMining.enabled; +let slushMiningEnabled = config.poolServer.slushMining && config.poolServer.slushMining.enabled; if (!config.poolServer.paymentId) config.poolServer.paymentId = {}; if (!config.poolServer.paymentId.addressSeparator) config.poolServer.paymentId.addressSeparator = "+"; +if (config.poolServer.paymentId.validation == null) config.poolServer.paymentId.validation = true; +if (config.poolServer.paymentId.ban == null) config.poolServer.paymentId.ban = false; +if (config.poolServer.paymentId.validations == null) { + config.poolServer.paymentId.validations = [] + config.poolServer.paymentId.validation = false; +} + +config.isRandomX = config.isRandomX || false + +let previousOffset = config.previousOffset || 7 +let offset = config.offset || 2 +config.daemonType = config.daemonType || 'default' +if (config.daemonType === 'bytecoin') { + previousOffset = config.previousOffset || 3 + offset = config.offset || 3 +} + +function Create2DArray (rows) { + let arr = []; + + for (let i = 0; i < rows; i++) { + arr[i] = []; + } + + return arr; +} + +if (mergedMining) + config.childPools = config.childPools.filter(pool => pool.enabled) + // Block templates -var validBlockTemplates = []; -var currentBlockTemplate; +let validBlockTemplates = mergedMining ? Create2DArray(config.childPools.length) : Create2DArray(1); +let currentBlockTemplate = []; + // Child Block templates -var validChildBlockTemplates = []; -var currentChildBlockTemplate; +let currentChildBlockTemplate = new Array(mergedMining ? config.childPools.length : 1); // Difficulty buffer -var diff1 = bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16); +let diff1 = bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16); /** * Convert buffer to byte array **/ Buffer.prototype.toByteArray = function () { - return Array.prototype.slice.call(this, 0); + return Array.prototype.slice.call(this, 0); }; /** * Periodical updaters **/ - + // Variable difficulty retarget -setInterval(function(){ - var now = Date.now() / 1000 | 0; - for (var minerId in connectedMiners){ - var miner = connectedMiners[minerId]; - if(!miner.noRetarget) { - miner.retarget(now); - } - } +setInterval(function () { + let now = Date.now() / 1000 | 0; + for (let minerId in connectedMiners) { + let miner = connectedMiners[minerId]; + if (!miner.noRetarget) { + miner.retarget(now); + } + } }, config.poolServer.varDiff.retargetTime * 1000); // Every 30 seconds clear out timed-out miners and old bans -setInterval(function(){ - var now = Date.now(); - var timeout = config.poolServer.minerTimeout * 1000; - for (var minerId in connectedMiners){ - var miner = connectedMiners[minerId]; - if (now - miner.lastBeat > timeout){ - log('warn', logSystem, 'Miner timed out and disconnected %s@%s', [miner.login, miner.ip]); - delete connectedMiners[minerId]; - removeConnectedWorker(miner, 'timeout'); - } - } - - if (banningEnabled){ - for (ip in bannedIPs){ - var banTime = bannedIPs[ip]; - if (now - banTime > config.poolServer.banning.time * 1000) { - delete bannedIPs[ip]; - delete perIPStats[ip]; - log('info', logSystem, 'Ban dropped for %s', [ip]); - } - } - } +setInterval(function () { + let now = Date.now(); + let timeout = config.poolServer.minerTimeout * 1000; + for (let minerId in connectedMiners) { + let miner = connectedMiners[minerId]; + if (now - miner.lastBeat > timeout) { + log('warn', logSystem, 'Miner timed out and disconnected %s@%s', [miner.login, miner.ip]); + delete connectedMiners[minerId]; + removeConnectedWorker(miner, 'timeout'); + } + } + + if (banningEnabled) { + for (ip in bannedIPs) { + let banTime = bannedIPs[ip]; + if (now - banTime > config.poolServer.banning.time * 1000) { + delete bannedIPs[ip]; + delete perIPStats[ip]; + log('info', logSystem, 'Ban dropped for %s', [ip]); + } + } + } }, 30000); /** * Handle multi-thread messages - **/ -process.on('message', function(message) { - switch (message.type) { - case 'banIP': - bannedIPs[message.ip] = Date.now(); - break; - } + **/ +process.on('message', function (message) { + switch (message.type) { + case 'banIP': + bannedIPs[message.ip] = Date.now(); + break; + case 'BlockTemplate': + let buffer = Buffer.from(message.block.blocktemplate_blob, 'hex'); + let new_hash = Buffer.alloc(32); + buffer.copy(new_hash, 0, previousOffset, 39); + try { + if (!currentBlockTemplate[0] || new_hash.toString('hex') !== currentBlockTemplate[0].prev_hash.toString('hex') || (currentBlockTemplate[0].num_transactions == 0 && message.block.num_transactions > 0)) { + log('info', logSystem, 'New %s block to mine at height %d w/ difficulty of %d (%d transactions)', [config.coin, message.block.height, message.block.difficulty, (message.block.num_transactions || 0)]); + if (mergedMining) { + for (var childPoolIndex = 0; childPoolIndex < config.childPools.length; childPoolIndex++) { + processBlockTemplate(message.block, childPoolIndex); + } + } else { + processBlockTemplate(message.block, 0); + } + return; + } else { + return; + } + } catch (e) { + log('error', logSystem, `BlockTemplate ${e}`) + } + break; + case 'ChildBlockTemplate': + let poolIndex = parseInt(message.poolIndex) + try { + if (!currentChildBlockTemplate[poolIndex] || message.block.height > currentChildBlockTemplate[poolIndex].height || (currentChildBlockTemplate[poolIndex].num_transactions == 0 && message.block.num_transactions > 0)) { + log('info', logSystem, 'New %s child block to mine at height %d w/ difficulty of %d (%d transactions)', [config.childPools[poolIndex].coin, message.block.height, message.block.difficulty, (message.block.num_transactions || 0)]); + processChildBlockTemplate(poolIndex, message.block); + return; + } else { + return; + } + } catch (e) { + log('error', logSystem, `ChildBlockTemplate ${e}`) + } + + break; + } }); /** * Block template **/ -function BlockTemplate(template){ - this.blob = template.blocktemplate_blob; - this.difficulty = template.difficulty; - this.height = template.height; - this.reserveOffset = template.reserved_offset + MERGE_MINING_TAG_RESERVED_SIZE - (POOL_NONCE_SIZE - 2); - this.buffer = Buffer.from(this.blob, 'hex'); - instanceId.copy(this.buffer, this.reserveOffset + 4, 0, 3); - this.extraNonce = 0; - - // The clientNonceLocation is the location at which the client pools should set the nonces for each of their clients. - this.clientNonceLocation = this.reserveOffset + 12; - // The clientPoolLocation is for multi-thread/multi-server pools to handle the nonce for each of their tiers. - this.clientPoolLocation = this.reserveOffset + 8; +function BlockTemplate (template, parent, indexOfChildPool) { + this.difficulty = template.difficulty; + this.height; + try { + this.height = template.height; + } catch (e) { + console.log(`BlockTemplate ${e}`) + } + this.num_transactions = template.num_transactions || 0; + this.blocktemplate_blob = template.blocktemplate_blob; + let blob = this.blocktemplate_blob; + this.buffer = Buffer.from(blob, 'hex'); + let template_hex = EXTRA_NONCE_TEMPLATE; + if (parent && mergedMining) { + if (currentChildBlockTemplate[indexOfChildPool]) { + this.childBlockTemplate = currentChildBlockTemplate[indexOfChildPool]; + this.buffer = utils.cnUtil.construct_mm_parent_block_blob(this.buffer, cnBlobType, this.childBlockTemplate.buffer); + blob = this.buffer.toString('hex'); + } else { + template_hex = EXTRA_NONCE_NO_CHILD_TEMPLATE; + } + } + this.isRandomX = config.isRandomX + if (!mergedMining && this.isRandomX) { + this.seed_hash = template.seed_hash; + this.next_seed_hash = template.next_seed_hash; + } + var found_template_at = blob.indexOf(template_hex); + if (found_template_at !== -1) { + this.reserveOffset = offset + (found_template_at >> 1); + if (this.reserveOffset != template.reserved_offset && !mergedMining) { + log('error', logSystem, "INTERNAL ERROR: found reserve offset in unexpected place (found at %d, expected at %d)", + [this.reserveOffset, template.reserved_offset]); + } + } else { + log('error', logSystem, "INTERNAL ERROR: Couldn't find extra nonce data in blob!"); + this.reserveOffset = template.reserveOffset || template.reserved_offset; + } + // Copy the Instance ID to the reserve offset + 4 bytes deeper. Copy in 4 bytes. + instanceId.copy(this.buffer, this.reserveOffset + 4, 0, 4); + + // Reset nonce - this is the per-miner/pool nonce + this.extraNonce = 0; + // The clientNonceLocation is the location at which the client pools should set the nonces for each of their clients. + this.clientNonceLocation = this.reserveOffset + 12; + + this.prev_hash = Buffer.alloc(32); + this.buffer.copy(this.prev_hash, 0, previousOffset, 39); } BlockTemplate.prototype = { - - nextBlob: function(){ - this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); - if (config.poolServer.mergedMining) { - return utils.cnUtil.convert_blob(this.buffer, cnBlobType, currentChildBlockTemplate.buffer, config.childCoinPoW? config.childCoinPoW:-1).toString('hex'); - } - return utils.cnUtil.convert_blob(this.buffer, cnBlobType, 0, config.childCoinPoW? config.childCoinPoW:-1).toString('hex'); - }, - nextBlobWithChildNonce: function(){ - // Write a 32 bit integer, big-endian style to the 0 byte of the reserve offset. - this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); - // Don't convert the blob to something hashable. You bad. - return this.buffer.toString('hex'); - } + nextBlob: function (index) { + this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); + if (mergedMining && this.childBlockTemplate) { + return utils.cnUtil.convert_blob(this.buffer, cnBlobType, this.childBlockTemplate.buffer) + .toString('hex') + } + return utils.cnUtil.convert_blob(this.buffer, cnBlobType) + .toString('hex'); + }, + nextBlobWithChildNonce: function () { + // Write a 32 bit integer, big-endian style to the 0 byte of the reserve offset. + this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); + // Don't convert the blob to something hashable. You bad. + return this.buffer.toString('hex'); + } }; -/** - * Get block template - **/ -function getBlockTemplate(callback){ - apiInterfaces.rpcDaemon('getblocktemplate', - {reserve_size: MERGE_MINING_TAG_RESERVED_SIZE, wallet_address: config.poolServer.poolAddress}, - callback) -} - -/** - * Get child block template - **/ -function getChildBlockTemplate(callback){ - apiInterfaces.rpcDaemon('getblocktemplate', - {reserve_size: MERGE_MINING_TAG_RESERVED_SIZE, wallet_address: config.poolServer.poolChildAddress}, - callback, config.childDaemon) -} /** * Process block template **/ -function processBlockTemplate(template){ - if (currentBlockTemplate) - validBlockTemplates.push(currentBlockTemplate); +function processBlockTemplate (template, indexOfChildPool) { + let block_template = new BlockTemplate(template, true, indexOfChildPool) - if (validBlockTemplates.length > 3) - validBlockTemplates.shift(); + if (currentBlockTemplate[indexOfChildPool]) + validBlockTemplates[indexOfChildPool].push(currentBlockTemplate[indexOfChildPool]); - currentBlockTemplate = new BlockTemplate(template); + while (validBlockTemplates[indexOfChildPool].length > (mergedMining ? 6 : 3)) + validBlockTemplates[indexOfChildPool].shift(); - for (var minerId in connectedMiners){ - var miner = connectedMiners[minerId]; - miner.pushMessage('job', miner.getJob()); - } + currentBlockTemplate[indexOfChildPool] = block_template; + notifyConnectedMiners(indexOfChildPool) } + /** * Process child block template **/ -function processChildBlockTemplate(template){ - if (currentChildBlockTemplate) - validChildBlockTemplates.push(currentChildBlockTemplate); - - if (validChildBlockTemplates.length > 12) - validChildBlockTemplates.shift(); +function processChildBlockTemplate (indexOfChildPool, template) { + let block_template = new BlockTemplate(template, false); - currentChildBlockTemplate = new BlockTemplate(template); + currentChildBlockTemplate[indexOfChildPool] = block_template; - for (var minerId in connectedMiners){ - var miner = connectedMiners[minerId]; - miner.pushMessage('job', miner.getJob()); - } + // Update the parent block template to include this new child + if (currentBlockTemplate[indexOfChildPool]) { + processBlockTemplate(currentBlockTemplate[indexOfChildPool], indexOfChildPool); + } } - -/** - * Job refresh - **/ -function jobRefresh(loop, callback){ - callback = callback || function(){}; - getBlockTemplate(function(error, result){ - - if (loop) - setTimeout(function(){ - jobRefresh(true); - }, config.poolServer.blockRefreshInterval); - - if (error){ - log('error', logSystem, 'Error polling getblocktemplate %j', [error]); - if (!poolStarted) log('error', logSystem, 'Could not start pool'); - callback(false); - return; - } - - - if (!currentBlockTemplate || result.height > currentBlockTemplate.height) { - log('info', logSystem, 'New block to mine at height %d w/ difficulty of %d', [result.height, result.difficulty]); - processBlockTemplate(result); - } - if (!poolStarted) { - startPoolServerTcp(function(successful){ poolStarted = true }); - } - callback(true); - }); - - if (config.poolServer.mergedMining) { - // Get child block template - getChildBlockTemplate(function(cerror, cresult) { - if (cerror){ - log('error', logSystem, 'Error polling getblocktemplate on child daemon %j', [cerror]); - return; - } - - var buffer = new Buffer(cresult.blocktemplate_blob, 'hex'); - var new_hash = new Buffer(32); - buffer.copy(new_hash, 0, 7, 39); - - if (!currentChildBlockTemplate || cresult.height > currentChildBlockTemplate.height) { - log('info', logSystem, 'New child block to mine at height %d w/ difficulty of %d', [cresult.height, cresult.difficulty]); - processChildBlockTemplate(cresult); - } - - }); - } - +function notifyConnectedMiners (indexOfChildPool) { + let now = Date.now() / 1000 | 0; + for (let minerId in connectedMiners) { + let miner = connectedMiners[minerId]; + if (indexOfChildPool === miner.activeChildPool) + miner.pushMessage('job', miner.getJob()); + } } /** * Variable difficulty **/ -var VarDiff = (function(){ - var variance = config.poolServer.varDiff.variancePercent / 100 * config.poolServer.varDiff.targetTime; - return { - variance: variance, - bufferSize: config.poolServer.varDiff.retargetTime / config.poolServer.varDiff.targetTime * 4, - tMin: config.poolServer.varDiff.targetTime - variance, - tMax: config.poolServer.varDiff.targetTime + variance, - maxJump: config.poolServer.varDiff.maxJump - }; +let VarDiff = (function () { + let variance = config.poolServer.varDiff.variancePercent / 100 * config.poolServer.varDiff.targetTime; + return { + variance: variance, + bufferSize: config.poolServer.varDiff.retargetTime / config.poolServer.varDiff.targetTime * 4, + tMin: config.poolServer.varDiff.targetTime - variance, + tMax: config.poolServer.varDiff.targetTime + variance, + maxJump: config.poolServer.varDiff.maxJump + }; })(); +function GetRewardTypeAsKey (rewardType) { + switch (rewardType) { + case 'solo': + return ':solo' + case 'prop': + return '' + default: + return '' + } +} + /** * Miner **/ -function Miner(id, login, pass, ip, port, agent, workerName, startingDiff, noRetarget, pushMessage, childWalletAddress){ - this.id = id; - this.login = login; - this.pass = pass; - this.ip = ip; - this.port = port; - this.proxy = false; - if (agent && agent.includes('xmr-node-proxy')) { - this.proxy = true; - } - this.workerName = workerName; - this.pushMessage = pushMessage; - this.childWalletAddress = childWalletAddress; - this.heartbeat(); - this.noRetarget = noRetarget; - this.difficulty = startingDiff; - this.validJobs = []; - - // Vardiff related variables - this.shareTimeRing = utils.ringBuffer(16); - this.lastShareTime = Date.now() / 1000 | 0; - - if (shareTrustEnabled) { - this.trust = { - threshold: config.poolServer.shareTrust.threshold, - probability: 1, - penalty: 0 - }; - } +function Miner (rewardType, childRewardType, id, childPoolIndex, login, pass, ip, port, agent, childLogin, startingDiff, noRetarget, pushMessage) { + this.rewardType = rewardType + this.childRewardType = childRewardType + this.rewardTypeAsKey = GetRewardTypeAsKey(rewardType) + this.childRewardTypeAsKey = GetRewardTypeAsKey(childRewardType) + + this.lastChildBlockHeight = 0 + this.id = id; + this.activeChildPool = childPoolIndex || 0; + this.login = login; + this.pass = pass; + this.ip = ip; + this.port = port; + this.proxy = false; + if (agent && agent.includes('xmr-node-proxy')) { + this.proxy = true; + } + this.workerName = 'undefined'; + this.childLogin = childLogin; + if (pass.lastIndexOf('@') >= 0 && pass.lastIndexOf('@') < pass.length) { + passDelimiterPos = pass.lastIndexOf('@') + 1; + this.workerName = pass.substr(passDelimiterPos, pass.length) + .trim(); + } + this.pushMessage = pushMessage; + this.heartbeat(); + this.noRetarget = noRetarget; + this.difficulty = startingDiff; + this.validJobs = []; + this.workerName2 = pass; + + // Vardiff related variables + this.shareTimeRing = utils.ringBuffer(16); + this.lastShareTime = Date.now() / 1000 | 0; + + if (shareTrustEnabled) { + this.trust = { + threshold: config.poolServer.shareTrust.threshold, + probability: 1, + penalty: 0 + }; + } } Miner.prototype = { - retarget: function(now){ - - var options = config.poolServer.varDiff; - - var sinceLast = now - this.lastShareTime; - var decreaser = sinceLast > VarDiff.tMax; - - var avg = this.shareTimeRing.avg(decreaser ? sinceLast : null); - var newDiff; - - var direction; - - if (avg > VarDiff.tMax && this.difficulty > options.minDiff){ - newDiff = options.targetTime / avg * this.difficulty; - newDiff = newDiff > options.minDiff ? newDiff : options.minDiff; - direction = -1; - } - else if (avg < VarDiff.tMin && this.difficulty < options.maxDiff){ - newDiff = options.targetTime / avg * this.difficulty; - newDiff = newDiff < options.maxDiff ? newDiff : options.maxDiff; - direction = 1; - } - else{ - return; - } - - if (Math.abs(newDiff - this.difficulty) / this.difficulty * 100 > options.maxJump){ - var change = options.maxJump / 100 * this.difficulty * direction; - newDiff = this.difficulty + change; - } - - this.setNewDiff(newDiff); - this.shareTimeRing.clear(); - if (decreaser) this.lastShareTime = now; - }, - setNewDiff: function(newDiff){ - newDiff = Math.round(newDiff); - if (this.difficulty === newDiff) return; - log('info', logSystem, 'Retargetting difficulty %d to %d for %s', [this.difficulty, newDiff, this.login]); - this.pendingDifficulty = newDiff; - this.pushMessage('job', this.getJob()); - }, - heartbeat: function(){ - this.lastBeat = Date.now(); - }, - getTargetHex: function(){ - if (this.pendingDifficulty){ - this.lastDifficulty = this.difficulty; - this.difficulty = this.pendingDifficulty; - this.pendingDifficulty = null; - } - - var padded = Buffer.alloc(32); - padded.fill(0); - - var diffBuff = diff1.div(this.difficulty).toBuffer(); - diffBuff.copy(padded, 32 - diffBuff.length); - - var buff = padded.slice(0, 4); - var buffArray = buff.toByteArray().reverse(); - var buffReversed = Buffer.from(buffArray); - this.target = buffReversed.readUInt32BE(0); - var hex = buffReversed.toString('hex'); - return hex; - }, - getJob: function(){ - - if (this.lastBlockHeight === currentBlockTemplate.height && (!config.poolServer.mergedMining || this.lastChildBlockHeight === currentChildBlockTemplate.height) && !this.pendingDifficulty && this.cachedJob !== null) { - return this.cachedJob; - } - if (!this.proxy) { - var blob = currentBlockTemplate.nextBlob(); - this.lastBlockHeight = currentBlockTemplate.height; - - if (config.poolServer.mergedMining) { - this.lastChildBlockHeight = currentChildBlockTemplate.height; - } - - var target = this.getTargetHex(); - - var newJob = { - id: utils.uid(), - extraNonce: currentBlockTemplate.extraNonce, - height: currentBlockTemplate.height, - - childHeight: config.poolServer.mergedMining? currentChildBlockTemplate.height:0, - - difficulty: this.difficulty, - diffHex: this.diffHex, - submissions: [] - }; - - this.validJobs.push(newJob); - - if (this.validJobs.length > 4) - this.validJobs.shift(); - - this.cachedJob = { - blob: blob, - job_id: newJob.id, - target: target, - id: this.id - }; - }else { - var blob = currentBlockTemplate.nextBlobWithChildNonce(); - - this.lastBlockHeight = currentBlockTemplate.height; - - if (config.poolServer.mergedMining) { - this.lastChildBlockHeight = currentChildBlockTemplate.height; - } - var target = this.getTargetHex(); - - let newJob = { - id: utils.uid(), - extraNonce: currentBlockTemplate.extraNonce, - height: currentBlockTemplate.height, - - childHeight: config.poolServer.mergedMining? currentChildBlockTemplate.height:0, - - difficulty: this.difficulty, - diffHex: this.diffHex, - clientPoolLocation: currentBlockTemplate.clientPoolLocation, - clientNonceLocation: currentBlockTemplate.clientNonceLocation, - submissions: [] - }; - - this.validJobs.push(newJob); - - if (this.validJobs.length > 4) - this.validJobs.shift(); - - this.cachedJob = { - blocktemplate_blob: blob, - difficulty: currentBlockTemplate.difficulty, - height: currentBlockTemplate.height, - - childHeight: config.poolServer.mergedMining? currentChildBlockTemplate.height:0, - - reserved_offset: currentBlockTemplate.reserveOffset, - client_nonce_offset: currentBlockTemplate.clientNonceLocation, - client_pool_offset: currentBlockTemplate.clientPoolLocation, - target_diff: this.difficulty, - target_diff_hex: this.diffHex, - job_id: newJob.id, - id: this.id - }; - } - - if (typeof config.includeAlgo !== "undefined" && config.includeAlgo) - this.cachedJob['algo'] = config.includeAlgo - if (typeof config.includeHeight !== "undefined" && config.includeHeight) - this.cachedJob['height'] = currentBlockTemplate.height - return this.cachedJob; - }, - checkBan: function(validShare){ - if (!banningEnabled) return; - - // Init global per-ip shares stats - if (!perIPStats[this.ip]){ - perIPStats[this.ip] = { validShares: 0, invalidShares: 0 }; - } - - var stats = perIPStats[this.ip]; - validShare ? stats.validShares++ : stats.invalidShares++; - - if (stats.validShares + stats.invalidShares >= config.poolServer.banning.checkThreshold){ - if (stats.invalidShares / stats.validShares >= config.poolServer.banning.invalidPercent / 100){ - validShare ? this.validShares++ : this.invalidShares++; - log('warn', logSystem, 'Banned %s@%s', [this.login, this.ip]); - bannedIPs[this.ip] = Date.now(); - delete connectedMiners[this.id]; - process.send({type: 'banIP', ip: this.ip}); - removeConnectedWorker(this, 'banned'); - } - else{ - stats.invalidShares = 0; - stats.validShares = 0; - } - } - } + retarget: function (now) { + + let options = config.poolServer.varDiff; + + let sinceLast = now - this.lastShareTime; + let decreaser = sinceLast > VarDiff.tMax; + + let avg = this.shareTimeRing.avg(decreaser ? sinceLast : null); + let newDiff; + + let direction; + + if (avg > VarDiff.tMax && this.difficulty > options.minDiff) { + newDiff = options.targetTime / avg * this.difficulty; + newDiff = newDiff > options.minDiff ? newDiff : options.minDiff; + direction = -1; + } else if (avg < VarDiff.tMin && this.difficulty < options.maxDiff) { + newDiff = options.targetTime / avg * this.difficulty; + newDiff = newDiff < options.maxDiff ? newDiff : options.maxDiff; + direction = 1; + } else { + return; + } + + if (Math.abs(newDiff - this.difficulty) / this.difficulty * 100 > options.maxJump) { + let change = options.maxJump / 100 * this.difficulty * direction; + newDiff = this.difficulty + change; + } + + this.setNewDiff(newDiff); + this.shareTimeRing.clear(); + if (decreaser) this.lastShareTime = now; + }, + setNewDiff: function (newDiff) { + newDiff = Math.round(newDiff); + if (this.difficulty === newDiff) return; + log('info', logSystem, 'Retargetting difficulty %d to %d for %s', [this.difficulty, newDiff, this.login]); + this.pendingDifficulty = newDiff; + this.pushMessage('job', this.getJob()); + }, + heartbeat: function () { + this.lastBeat = Date.now(); + }, + getTargetHex: function () { + if (this.pendingDifficulty) { + this.lastDifficulty = this.difficulty; + this.difficulty = this.pendingDifficulty; + this.pendingDifficulty = null; + } + + let padded = Buffer.alloc(32); + padded.fill(0); + + let diffBuff = diff1.div(this.difficulty) + .toBuffer(); + diffBuff.copy(padded, 32 - diffBuff.length); + + let buff = padded.slice(0, 4); + let buffArray = buff.toByteArray() + .reverse(); + let buffReversed = Buffer.from(buffArray); + this.target = buffReversed.readUInt32BE(0); + let hex = buffReversed.toString('hex'); + return hex; + }, + getJob: function () { + let blockTemplate = currentBlockTemplate[this.activeChildPool] + let newJob = { + id: utils.uid(), + height: blockTemplate.height, + submissions: [] + }; + if (mergedMining) { + if (this.lastBlockHeight === blockTemplate.height && + (!currentChildBlockTemplate[this.activeChildPool] || this.lastChildBlockHeight === currentChildBlockTemplate[this.activeChildPool].height) && + !this.pendingDifficulty && + this.cachedJob !== null && + !config.daemon.alwaysPoll) { + return this.cachedJob; + } + this.lastChildBlockHeight = currentChildBlockTemplate ? currentChildBlockTemplate[this.activeChildPool].height : -1; + newJob.activeChildPool = this.activeChildPool + newJob.childHeight = this.lastChildBlockHeight + } else { + if (this.lastBlockHeight === blockTemplate.height && !this.pendingDifficulty && this.cachedJob !== null && !config.daemon.alwaysPoll) { + return this.cachedJob; + } + } + let blob = this.proxy ? blockTemplate.nextBlobWithChildNonce() : blockTemplate.nextBlob(); + this.lastBlockHeight = blockTemplate.height; + let target = this.getTargetHex(); + + newJob.difficulty = this.difficulty + newJob.diffHex = this.diffHex + newJob.extraNonce = blockTemplate.extraNonce + + if (blockTemplate.isRandomX) { + newJob.seed_hash = blockTemplate.seed_hash + newJob.next_seed_hash = blockTemplate.next_seed_hash + } + + this.validJobs.push(newJob); + + while (this.validJobs.length > 4) + this.validJobs.shift(); + + this.cachedJob = { + job_id: newJob.id, + id: this.id + }; + + if (this.proxy) { + newJob.clientPoolLocation = blockTemplate.clientPoolLocation + newJob.clientNonceLocation = blockTemplate.clientNonceLocation + + this.cachedJob.blocktemplate_blob = blob + this.cachedJob.difficulty = blockTemplate.difficulty + this.cachedJob.height = blockTemplate.height + this.cachedJob.childHeight = this.lastChildBlockHeight + this.cachedJob.reserved_offset = blockTemplate.reserveOffset + this.cachedJob.client_nonce_offset = blockTemplate.clientNonceLocation + this.cachedJob.client_pool_offset = blockTemplate.clientPoolLocation + this.cachedJob.target_diff = this.difficulty + this.cachedJob.target_diff_hex = this.diffHex + + } else { + this.cachedJob.blob = blob + this.cachedJob.target = target + } + + if (typeof config.includeAlgo !== "undefined" && config.includeAlgo) + this.cachedJob.algo = config.includeAlgo + if (typeof config.includeHeight !== "undefined" && config.includeHeight) + this.cachedJob.height = blockTemplate.height + + if (newJob.seed_hash) { + this.cachedJob.seed_hash = newJob.seed_hash; + this.cachedJob.next_seed_hash = newJob.next_seed_hash; + } + return this.cachedJob; + }, + checkBan: function (validShare) { + if (!banningEnabled) return; + + // Init global per-ip shares stats + if (!perIPStats[this.ip]) { + perIPStats[this.ip] = { + validShares: 0, + invalidShares: 0 + }; + } + + let stats = perIPStats[this.ip]; + validShare ? stats.validShares++ : stats.invalidShares++; + + if (stats.validShares + stats.invalidShares >= config.poolServer.banning.checkThreshold) { + if (stats.invalidShares / stats.validShares >= config.poolServer.banning.invalidPercent / 100) { + validShare ? this.validShares++ : this.invalidShares++; + log('warn', logSystem, 'Banned %s@%s', [this.login, this.ip]); + bannedIPs[this.ip] = Date.now(); + delete connectedMiners[this.id]; + process.send({ + type: 'banIP', + ip: this.ip + }); + removeConnectedWorker(this, 'banned'); + } else { + stats.invalidShares = 0; + stats.validShares = 0; + } + } + } }; +validateMinerPaymentId_difficulty = (address, ip, poolServerConfig, coin, sendReply) => { + if (utils.characterCount(address, '\\+') > 1) { + let message = `Invalid paymentId specified for ${coin}login, ${ip}`; + if (poolServerConfig.paymentId.validation) { + process.send({ + type: 'banIP', + ip: ip + }); + message += ` banned for ${poolServerConfig.banning.time / 60} minutes` + } + sendReply(message) + log('warn', logSystem, message); + return false + } + + if (utils.characterCount(address, '\\.') > 1) { + log('warn', logSystem, `Invalid difficulty specified for ${coin}login`); + sendReply(`Invalid difficulty specified for ${coin}login, ${ip}`) + return false + } + return true +} + /** * Handle miner method **/ -function handleMinerMethod(method, params, ip, portData, sendReply, pushMessage){ - var miner = connectedMiners[params.id]; - - // Check for ban here, so preconnected attackers can't continue to screw you - if (IsBannedIp(ip)){ - sendReply('Your IP is banned'); - return; - } - - switch(method){ - case 'login': - var login = params.login; - if (!login){ - sendReply('Missing login'); - return; - } - - var port = portData.port; - - var pass = params.pass; - if (!pass) { pass = ""; } - var workerName = ''; - var childWalletAddress = ""; - - if (config.poolServer.mergedMining && pass.length > 10) { - if (pass.indexOf('@') >= 0) { - passDelimiterPos = pass.lastIndexOf('@'); - workerName = pass.substr(passDelimiterPos).trim(); - pass = pass.substr(0, passDelimiterPos).trim(); - } - // wallet+paymentId@workerName - var addr = pass.split(config.poolServer.paymentId.addressSeparator); - var address = addr[0] || null; - - if (utils.cnUtil.address_decode(new Buffer(config.poolServer.poolChildAddress)) === utils.cnUtil.address_decode(new Buffer(address))) { - childWalletAddress = pass; - log('info', logSystem, 'Merged mining address in password field: %s', [pass]); - pass = 'x'; - } - - } - - if (params.rigid) { - workerName = params.rigid.trim(); - } - else if (pass) { - workerName = (workerName.length > 0)? workerName:pass.trim(); - if (pass.indexOf(':') >= 0 && pass.indexOf('@') >= 0) { - passDelimiterPos = pass.lastIndexOf(':'); - workerName = pass.substr(0, passDelimiterPos).trim(); - } - workerName = workerName.replace(/:/g, ''); - workerName = workerName.replace(/\+/g, ''); - workerName = workerName.replace(/\s/g, ''); - if (workerName.toLowerCase() === 'x') { - workerName = ''; - } - } - if (!workerName || workerName === '') { - workerName = 'undefined'; - } - workerName = utils.cleanupSpecialChars(workerName); - - var difficulty = portData.difficulty; - var noRetarget = false; - if(config.poolServer.fixedDiff.enabled) { - var fixedDiffCharPos = login.lastIndexOf(config.poolServer.fixedDiff.addressSeparator); - if (fixedDiffCharPos !== -1 && (login.length - fixedDiffCharPos < 32)){ - diffValue = login.substr(fixedDiffCharPos + 1); - difficulty = parseInt(diffValue); - login = login.substr(0, fixedDiffCharPos); - if (!difficulty || difficulty != diffValue) { - log('warn', logSystem, 'Invalid difficulty value "%s" for login: %s', [diffValue, login]); - difficulty = portData.difficulty; - } else { - noRetarget = true; - if (difficulty < config.poolServer.varDiff.minDiff) { - difficulty = config.poolServer.varDiff.minDiff; - } - } - } - } - - var addr = login.split(config.poolServer.paymentId.addressSeparator); - var address = addr[0] || null; - - if (!address) { - log('warn', logSystem, 'No address specified for login'); - sendReply('Invalid address used for login'); - } - - if (!utils.validateMinerAddress(address)) { - var addressPrefix = utils.getAddressPrefix(address); - if (!addressPrefix) addressPrefix = 'N/A'; - - log('warn', logSystem, 'Invalid address used for login (prefix: %s): %s', [addressPrefix, address]); - sendReply('Invalid address used for login'); - return; - } - - var minerId = utils.uid(); - miner = new Miner(minerId, login, pass, ip, port, params.agent, workerName, difficulty, noRetarget, pushMessage, childWalletAddress); - connectedMiners[minerId] = miner; - - sendReply(null, { - id: minerId, - job: miner.getJob(), - status: 'OK' - }); - - newConnectedWorker(miner); - break; - case 'getjob': - if (!miner){ - sendReply('Unauthenticated'); - return; - } - miner.heartbeat(); - sendReply(null, miner.getJob()); - break; - case 'submit': - if (!miner){ - sendReply('Unauthenticated'); - return; - } - miner.heartbeat(); - - var job = miner.validJobs.filter(function(job){ - return job.id === params.job_id; - })[0]; - - if (!job){ - sendReply('Invalid job id'); - return; - } - - if (!params.nonce || !params.result) { - sendReply('Attack detected'); - var minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; - log('warn', logSystem, 'Malformed miner share: ' + JSON.stringify(params) + ' from ' + minerText); - return; - } - - if (!noncePattern.test(params.nonce)) { - var minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; - log('warn', logSystem, 'Malformed nonce: ' + JSON.stringify(params) + ' from ' + minerText); - perIPStats[miner.ip] = { validShares: 0, invalidShares: 999999 }; - miner.checkBan(false); - sendReply('Duplicate share'); - return; - } - - // Force lowercase for further comparison - params.nonce = params.nonce.toLowerCase(); - - if (!miner.proxy) { - if (job.submissions.indexOf(params.nonce) !== -1){ - var minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; - log('warn', logSystem, 'Duplicate share: ' + JSON.stringify(params) + ' from ' + minerText); - perIPStats[miner.ip] = { validShares: 0, invalidShares: 999999 }; - miner.checkBan(false); - sendReply('Duplicate share'); - return; - } - - job.submissions.push(params.nonce); - } else { - if (!Number.isInteger(params.poolNonce) || !Number.isInteger(params.workerNonce)) { - var minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; - log('warn', logSystem, 'Malformed nonce: ' + JSON.stringify(params) + ' from ' + minerText); - perIPStats[miner.ip] = { validShares: 0, invalidShares: 999999 }; - miner.checkBan(false); - sendReply('Duplicate share'); - return; - } - let nonce_test = `${params.nonce}_${params.poolNonce}_${params.workerNonce}`; - if (job.submissions.indexOf(nonce_test) !== -1) { - var minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; - log('warn', logSystem, 'Duplicate share: ' + JSON.stringify(params) + ' from ' + minerText); - perIPStats[miner.ip] = { validShares: 0, invalidShares: 999999 }; - miner.checkBan(false); - sendReply('Duplicate share'); - return; - } - job.submissions.push(nonce_test); - - } - - var blockTemplate = currentBlockTemplate.height === job.height ? currentBlockTemplate : validBlockTemplates.filter(function(t){ - return t.height === job.height; - })[0]; - - if (!blockTemplate){ - sendReply('Block expired'); - return; - } - - - var childBlockTemplate = {}; - if (config.poolServer.mergedMining) { - var childBlockTemplate = currentChildBlockTemplate.height === job.childHeight ? currentChildBlockTemplate : validChildBlockTemplates.filter(function(t){ - return t.height === job.childHeight; - })[0]; - } - - if (config.poolServer.mergedMining && !childBlockTemplate){ - sendReply('Child Block expired'); - return; - } - - var shareAccepted = processShare(miner, job, blockTemplate, childBlockTemplate, params); - miner.checkBan(shareAccepted); - - if (shareTrustEnabled){ - if (shareAccepted){ - miner.trust.probability -= shareTrustStepFloat; - if (miner.trust.probability < shareTrustMinFloat) - miner.trust.probability = shareTrustMinFloat; - miner.trust.penalty--; - miner.trust.threshold--; - } - else{ - log('warn', logSystem, 'Share trust broken by %s@%s', [miner.login, miner.ip]); - miner.trust.probability = 1; - miner.trust.penalty = config.poolServer.shareTrust.penalty; - } - } - - if (!shareAccepted){ - sendReply('Rejected share: invalid result'); - return; - } - - var now = Date.now() / 1000 | 0; - miner.shareTimeRing.append(now - miner.lastShareTime); - miner.lastShareTime = now; - //miner.retarget(now); - - sendReply(null, {status: 'OK'}); - break; - case 'keepalived' : - if (!miner){ - sendReply('Unauthenticated'); - return; - } - miner.heartbeat(); - sendReply(null, { status:'KEEPALIVED' }); - break; - default: - sendReply('Invalid method'); - var minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; - log('warn', logSystem, 'Invalid method: %s (%j) from %s', [method, params, minerText]); - break; - } +function handleMinerMethod (method, params, ip, portData, sendReply, pushMessage) { + let miner = connectedMiners[params.id]; + + // Check for ban here, so preconnected attackers can't continue to screw you + if (IsBannedIp(ip)) { + sendReply('Your IP is banned'); + return; + } + + switch (method) { + case 'login': + let login = params.login; + if (!login) { + sendReply('Missing login'); + return; + } + + if (!validateMinerPaymentId_difficulty(login, ip, config.poolServer, 'parent ', sendReply)) + return + + let calculated = utils.determineRewardData(login) + login = calculated.address + let rewardType = calculated.rewardType + + let address = '' + let paymentid = null + let port = portData.port; + let pass = params.pass; + let childLogin = pass.trim(); + let childPoolIndex = 0; + let childRewardType = rewardType + if (mergedMining) { + childPoolIndex = -1 + if (!validateMinerPaymentId_difficulty(pass, ip, config.poolServer, 'child ', sendReply)) + return + + calculated = utils.determineRewardData(pass) + pass = calculated.address + childRewardType = calculated.rewardType + + if (pass.indexOf('@') >= 0 && pass.indexOf('@') >= 0) { + passDelimiterPos = pass.lastIndexOf('@'); + childLogin = pass.substr(0, passDelimiterPos) + .trim(); + } + childLogin = childLogin.replace(/\s/g, ''); + childLogin = utils.cleanupSpecialChars(childLogin); + + + let addr = childLogin.split(config.poolServer.paymentId.addressSeparator); + address = addr[0] || null; + paymentId = addr[1] || null + + if (!address) { + log('warn', logSystem, 'No address specified for login'); + sendReply('No address specified for login'); + } + + + if (paymentId && config.poolServer.paymentId.validation) { + let valid = false; + config.poolServer.paymentId.validations.forEach(validation => { + if (paymentId.match(`^([a-zA-Z0-9]){${validation}}$`)) { + return valid = true + } + }) + if (!valid) { + log('warn', logSystem, 'Invalid paymentId specified for login'); + } + if (config.poolServer.paymentId.ban) { + process.send({ + type: 'banIP', + ip: ip + }); + sendReply(`Invalid paymentId specified for login, ${portData.ip} banned for ${config.poolServer.banning.time / 60} minutes`); + } + } + + for (i = 0; i < config.childPools.length; i++) { + if (config.childPools[i].pattern) { + if (new RegExp(config.childPools[i].pattern, 'i') + .test(address)) { + childPoolIndex = i + break + } + } + } + if (childPoolIndex < 0) { + childPoolIndex = fallBackCoin + address = config.childPools[childPoolIndex].poolAddress + childLogin = config.childPools[childPoolIndex].poolAddress + } + if (!utils.validateChildMinerAddress(address, childPoolIndex)) { + let addressPrefix = utils.getAddressPrefix(address); + if (!addressPrefix) addressPrefix = 'N/A'; + + log('warn', logSystem, 'Invalid address used for childLogin (prefix: %s): %s', [addressPrefix, address]); + sendReply('Invalid address used for childLogin'); + return; + } + } + + + let difficulty = portData.difficulty; + let noRetarget = false; + if (config.poolServer.fixedDiff.enabled) { + let fixedDiffCharPos = login.lastIndexOf(config.poolServer.fixedDiff.addressSeparator); + if (fixedDiffCharPos !== -1 && (login.length - fixedDiffCharPos < 32)) { + diffValue = login.substr(fixedDiffCharPos + 1); + difficulty = parseInt(diffValue); + login = login.substr(0, fixedDiffCharPos); + if (!difficulty || difficulty != diffValue) { + log('warn', logSystem, 'Invalid difficulty value "%s" for login: %s', [diffValue, login]); + difficulty = portData.difficulty; + } else { + noRetarget = true; + if (difficulty < config.poolServer.varDiff.minDiff) { + difficulty = config.poolServer.varDiff.minDiff; + } + } + } + } + + addr = login.split(config.poolServer.paymentId.addressSeparator); + address = addr[0] || null; + paymentId = addr[1] || null; + if (!address) { + log('warn', logSystem, 'No address specified for login'); + sendReply('Invalid address used for login'); + return + } + + if (paymentId && paymentId.match('^([a-zA-Z0-9]){0,15}$')) { + if (config.poolServer.paymentId.validation) { + process.send({ + type: 'banIP', + ip: ip + }); + log('warn', logSystem, 'Invalid paymentId specified for login'); + } else { + log('warn', logSystem, 'Invalid paymentId specified for login'); + } + sendReply(`Invalid paymentId specified for login, ${portData.ip} banned for ${config.poolServer.banning.time / 60} minutes`) + return; + } + + if (!utils.validateMinerAddress(address)) { + let addressPrefix = utils.getAddressPrefix(address); + if (!addressPrefix) addressPrefix = 'N/A'; + + log('warn', logSystem, 'Invalid address used for login (prefix: %s): %s', [addressPrefix, address]); + sendReply('Invalid address used for login'); + return; + } + + let minerId = utils.uid(); + miner = new Miner(rewardType, childRewardType, minerId, childPoolIndex, login, pass, ip, port, params.agent, childLogin, difficulty, noRetarget, pushMessage); + connectedMiners[minerId] = miner; + + sendReply(null, { + id: minerId, + job: miner.getJob(), + status: 'OK' + }); + + newConnectedWorker(miner); + break; + case 'getjob': + if (!miner) { + sendReply('Unauthenticated'); + return; + } + miner.heartbeat(); + sendReply(null, miner.getJob()); + break; + case 'submit': + if (!miner) { + sendReply('Unauthenticated'); + return; + } + miner.heartbeat(); + + let job = miner.validJobs.filter(function (job) { + return job.id === params.job_id; + })[0]; + + if (!job) { + sendReply('Invalid job id'); + return; + } + + if (!params.nonce || !params.result) { + sendReply('Attack detected'); + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Malformed miner share: ' + JSON.stringify(params) + ' from ' + minerText); + return; + } + + if (!noncePattern.test(params.nonce)) { + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Malformed nonce: ' + JSON.stringify(params) + ' from ' + minerText); + perIPStats[miner.ip] = { + validShares: 0, + invalidShares: 999999 + }; + miner.checkBan(false); + sendReply('Duplicate share1'); + return; + } + + // Force lowercase for further comparison + params.nonce = params.nonce.toLowerCase(); + + if (!miner.proxy) { + if (job.submissions.indexOf(params.nonce) !== -1) { + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Duplicate share: ' + JSON.stringify(params) + ' from ' + minerText); + perIPStats[miner.ip] = { + validShares: 0, + invalidShares: 999999 + }; + miner.checkBan(false); + sendReply('Duplicate share2'); + return; + } + + job.submissions.push(params.nonce); + } else { + if (!Number.isInteger(params.poolNonce) || !Number.isInteger(params.workerNonce)) { + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Malformed nonce: ' + JSON.stringify(params) + ' from ' + minerText); + perIPStats[miner.ip] = { + validShares: 0, + invalidShares: 999999 + }; + miner.checkBan(false); + sendReply('Duplicate share3'); + return; + } + let nonce_test = `${params.nonce}_${params.poolNonce}_${params.workerNonce}`; + if (job.submissions.indexOf(nonce_test) !== -1) { + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Duplicate share: ' + JSON.stringify(params) + ' from ' + minerText); + perIPStats[miner.ip] = { + validShares: 0, + invalidShares: 999999 + }; + miner.checkBan(false); + sendReply('Duplicate share4'); + return; + } + job.submissions.push(nonce_test); + + } + + let isJobBlock = function (b) { + return b.height === job.height && job.childHeight === ( + b.childBlockTemplate ? b.childBlockTemplate.height : undefined); + }; + + let blockTemplate = currentBlockTemplate[miner.activeChildPool] + if (job.childHeight) + blockTemplate = isJobBlock(currentBlockTemplate[miner.activeChildPool]) ? currentBlockTemplate[miner.activeChildPool] : validBlockTemplates[miner.activeChildPool].filter(isJobBlock)[0]; + + if (!blockTemplate) { + sendReply('Block expired'); + return; + } + + let shareAccepted = processShare(miner, job, blockTemplate, params); + miner.checkBan(shareAccepted); + + if (shareTrustEnabled) { + if (shareAccepted) { + miner.trust.probability -= shareTrustStepFloat; + if (miner.trust.probability < shareTrustMinFloat) + miner.trust.probability = shareTrustMinFloat; + miner.trust.penalty--; + miner.trust.threshold--; + } else { + log('warn', logSystem, 'Share trust broken by %s@%s', [miner.login, miner.ip]); + miner.trust.probability = 1; + miner.trust.penalty = config.poolServer.shareTrust.penalty; + } + } + + if (!shareAccepted) { + sendReply('Rejected share: invalid result'); + return; + } + + let now = Date.now() / 1000 | 0; + miner.shareTimeRing.append(now - miner.lastShareTime); + miner.lastShareTime = now; + + sendReply(null, { + status: 'OK' + }); + break; + case 'keepalived': + if (!miner) { + sendReply('Unauthenticated'); + return; + } + miner.heartbeat(); + sendReply(null, { + status: 'KEEPALIVED' + }); + break; + default: + sendReply('Invalid method'); + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Invalid method: %s (%j) from %s', [method, params, minerText]); + break; + } } /** * New connected worker **/ -function newConnectedWorker(miner){ - log('info', logSystem, 'Miner connected %s@%s on port', [miner.login, miner.ip, miner.port]); - if (miner.workerName !== 'undefined') log('info', logSystem, 'Worker Name: %s', [miner.workerName]); - if (miner.difficulty) log('info', logSystem, 'Miner difficulty fixed to %s', [miner.difficulty]); - - redisClient.sadd(config.coin + ':workers_ip:' + miner.login, miner.ip); - redisClient.hincrby(config.coin + ':ports:'+miner.port, 'users', 1); - - redisClient.hincrby(config.coin + ':active_connections', miner.login + '~' + miner.workerName, 1, function(error, connectedWorkers) { - if (connectedWorkers === 1) { - notifications.sendToMiner(miner.login, 'workerConnected', { - 'LOGIN' : miner.login, - 'MINER': miner.login.substring(0,7)+'...'+miner.login.substring(miner.login.length-7), - 'IP': miner.ip.replace('::ffff:', ''), - 'PORT': miner.port, - 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '' - }); - } - }); +function newConnectedWorker (miner) { + log('info', logSystem, 'Miner connected %s@%s on port', [miner.login, miner.ip, miner.port]); + if (miner.workerName !== 'undefined') log('info', logSystem, 'Worker Name: %s', [miner.workerName]); + if (miner.difficulty) log('info', logSystem, 'Miner difficulty fixed to %s', [miner.difficulty]); + + redisClient.sadd(`${config.coin}:workers_ip:${miner.login}`, miner.ip); + redisClient.hincrby(`${config.coin}:ports:${miner.port}`, 'users', 1); + + redisClient.hincrby(`${config.coin}:active_connections${miner.rewardTypeAsKey}`, `${miner.login}~${miner.workerName}`, 1, function (error, connectedWorkers) { + if (connectedWorkers === 1) { + notifications.sendToMiner(miner.login, 'workerConnected', { + 'LOGIN': miner.login, + 'MINER': `${miner.login.substring(0,7)}...${miner.login.substring(miner.login.length-7)}`, + 'IP': miner.ip.replace('::ffff:', ''), + 'PORT': miner.port, + 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '' + }); + } + }); + if (config.poolServer.mergedMining) { + redisClient.sadd(`${config.childPools[miner.activeChildPool].coin}:workers_ip:${miner.childLogin}`, miner.ip); + redisClient.hincrby(`${config.childPools[miner.activeChildPool].coin}:ports:${miner.port}`, 'users', 1); + + redisClient.hincrby(`${config.childPools[miner.activeChildPool].coin}:active_connections${miner.childRewardTypeAsKey}`, `${miner.childLogin}~${miner.workerName}`, 1, function (error, connectedWorkers) {}); + + + let redisCommands = config.childPools.map(item => { + return ['hdel', `${config.coin}:workers:${miner.login}`, `${item.coin}`, ] + }) + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Failed to clear childCoins from parent in redis %j \n %j', [err, redisCommands]); + } + }) + + redisClient.hset(`${config.coin}:workers:${miner.login}`, `${config.childPools[miner.activeChildPool].coin}`, miner.childLogin) + + redisClient.hset(`${config.childPools[miner.activeChildPool].coin}:workers:${miner.childLogin}`, `${config.coin}`, miner.login) + + } } /** * Remove connected worker **/ -function removeConnectedWorker(miner, reason){ - redisClient.hincrby(config.coin + ':ports:'+miner.port, 'users', '-1'); - - redisClient.hincrby(config.coin + ':active_connections', miner.login + '~' + miner.workerName, -1, function(error, connectedWorkers) { - if (reason === 'banned') { - notifications.sendToMiner(miner.login, 'workerBanned', { - 'LOGIN' : miner.login, - 'MINER': miner.login.substring(0,7)+'...'+miner.login.substring(miner.login.length-7), - 'IP': miner.ip.replace('::ffff:', ''), - 'PORT': miner.port, - 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '' - }); - } else if (!connectedWorkers || connectedWorkers <= 0) { - notifications.sendToMiner(miner.login, 'workerTimeout', { - 'LOGIN' : miner.login, - 'MINER': miner.login.substring(0,7)+'...'+miner.login.substring(miner.login.length-7), - 'IP': miner.ip.replace('::ffff:', ''), - 'PORT': miner.port, - 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '', - 'LAST_HASH': utils.dateFormat(new Date(miner.lastBeat), 'yyyy-mm-dd HH:MM:ss Z') - }); - } - }); +function removeConnectedWorker (miner, reason) { + redisClient.hincrby(`${config.coin}:ports:${miner.port}`, 'users', '-1'); + if (mergedMining) { + redisClient.hincrby(`${config.childPools[miner.activeChildPool].coin}:ports:${miner.port}`, 'users', '-1'); + redisClient.hincrby(`${config.childPools[miner.activeChildPool].coin}:active_connections${miner.childRewardTypeAsKey}`, `${miner.childLogin}~${miner.workerName}`, 1, function (error, connectedWorkers) {}); + } + + redisClient.hincrby(`${config.coin}:active_connections${miner.rewardTypeAsKey}`, `${miner.login}~${miner.workerName}`, -1, function (error, connectedWorkers) { + if (reason === 'banned') { + notifications.sendToMiner(miner.login, 'workerBanned', { + 'LOGIN': miner.login, + 'MINER': `${miner.login.substring(0,7)}...${miner.login.substring(miner.login.length-7)}`, + 'IP': miner.ip.replace('::ffff:', ''), + 'PORT': miner.port, + 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '' + }); + } else if (!connectedWorkers || connectedWorkers <= 0) { + notifications.sendToMiner(miner.login, 'workerTimeout', { + 'LOGIN': miner.login, + 'MINER': `${miner.login.substring(0,7)}...${miner.login.substring(miner.login.length-7)}`, + 'IP': miner.ip.replace('::ffff:', ''), + 'PORT': miner.port, + 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '', + 'LAST_HASH': utils.dateFormat(new Date(miner.lastBeat), 'yyyy-mm-dd HH:MM:ss Z') + }); + } + }); } /** * Return if IP has been banned **/ -function IsBannedIp(ip){ - if (!banningEnabled || !bannedIPs[ip]) return false; - - var bannedTime = bannedIPs[ip]; - var bannedTimeAgo = Date.now() - bannedTime; - var timeLeft = config.poolServer.banning.time * 1000 - bannedTimeAgo; - if (timeLeft > 0){ - return true; - } - else { - delete bannedIPs[ip]; - log('info', logSystem, 'Ban dropped for %s', [ip]); - return false; - } +function IsBannedIp (ip) { + if (!banningEnabled || !bannedIPs[ip]) return false; + + let bannedTime = bannedIPs[ip]; + let bannedTimeAgo = Date.now() - bannedTime; + let timeLeft = config.poolServer.banning.time * 1000 - bannedTimeAgo; + if (timeLeft > 0) { + return true; + } else { + delete bannedIPs[ip]; + log('info', logSystem, 'Ban dropped for %s', [ip]); + return false; + } } -/** - * Record miner share data - **/ -function recordShareData(miner, job, shareDiff, blockCandidate, hashHex, shareType, blockTemplate, isChildCoin){ - var dateNow = Date.now(); - var dateNowSeconds = dateNow / 1000 | 0; - var coin = isChildCoin? config.childCoin:config.coin; - var wallet_address = isChildCoin? miner.childWalletAddress:miner.login; - - var job_height = isChildCoin? job.childHeight:job.height; - - if (!wallet_address || !coin) { - return; - } - - var updateScore; - // Weighting older shares lower than newer ones to prevent pool hopping - if (slushMiningEnabled) { - // We need to do this via an eval script because we need fetching the last block time and - // calculating the score to run in a single transaction (otherwise we could have a race - // condition where a block gets discovered between the time we look up lastBlockFound and - // insert the score, which would give the miner an erroneously huge proportion on the new block) - updateScore = ['eval', ` +function recordShareData (miner, job, shareDiff, blockCandidate, hashHex, shareType, blockTemplate, pool) { + let dateNow = Date.now(); + let dateNowSeconds = dateNow / 1000 | 0; + let coin = pool !== null ? pool.coin : config.coin; + let login = pool !== null ? miner.childLogin : miner.login; + let job_height = pool !== null ? job.childHeight : job.height + let workerName = miner.workerName; + let rewardType = pool !== null ? miner.childRewardType : miner.rewardType + + let updateScore; + // Weighting older shares lower than newer ones to prevent pool hopping + if (slushMiningEnabled) { + // We need to do this via an eval script because we need fetching the last block time and + // calculating the score to run in a single transaction (otherwise we could have a race + // condition where a block gets discovered between the time we look up lastBlockFound and + // insert the score, which would give the miner an erroneously huge proportion on the new block) + updateScore = ['eval', ` local age = (ARGV[3] - redis.call('hget', KEYS[2], 'lastBlockFound')) / 1000 local score = string.format('%.17g', ARGV[2] * math.exp(age / ARGV[4])) redis.call('hincrbyfloat', KEYS[1], ARGV[1], score) return {score, tostring(age)} `, - 2 /*keys*/, coin + ':scores:roundCurrent', coin + ':stats', - /* args */ wallet_address, job.difficulty, Date.now(), config.poolServer.slushMining.weight]; - } - else { - job.score = job.difficulty; - updateScore = ['hincrbyfloat', coin + ':scores:roundCurrent', wallet_address, job.score] - } - - var redisCommands = [ + 2 /*keys*/ , coin + ':scores:roundCurrent', coin + ':stats', + /* args */ + login, job.difficulty, Date.now(), config.poolServer.slushMining.weight]; + } else { + job.score = job.difficulty; + updateScore = ['hincrbyfloat', `${coin}:scores:${rewardType}:roundCurrent`, login, job.score] + } + + let redisCommands = [ updateScore, - ['hincrby', coin + ':shares_actual:roundCurrent', wallet_address, job.difficulty], - ['zadd', coin + ':hashrate', dateNowSeconds, [job.difficulty, wallet_address, dateNow].join(':')], - ['hincrby', coin + ':workers:' + wallet_address, 'hashes', job.difficulty], - ['hset', coin + ':workers:' + wallet_address, 'lastShare', dateNowSeconds], - ['expire', coin + ':workers:' + wallet_address, (86400 * cleanupInterval)], - ['expire', coin + ':payments:' + wallet_address, (86400 * cleanupInterval)] + ['hincrby', `${coin}:shares_actual:${rewardType}:roundCurrent`, login, job.difficulty], + ['zadd', `${coin}:hashrate`, dateNowSeconds, [job.difficulty, login, dateNow, rewardType].join(':')], + ['hincrby', `${coin}:workers:${login}`, 'hashes', job.difficulty], + ['hset', `${coin}:workers:${login}`, 'lastShare', dateNowSeconds], + ['expire', `${coin}:workers:${login}`, (86400 * cleanupInterval)], + ['expire', `${coin}:payments:${login}`, (86400 * cleanupInterval)] ]; - if (miner.workerName) { - redisCommands.push(['zadd', coin + ':hashrate', dateNowSeconds, [job.difficulty, wallet_address + '~' + miner.workerName, dateNow].join(':')]); - redisCommands.push(['hincrby', coin + ':unique_workers:' + wallet_address + '~' + miner.workerName, 'hashes', job.difficulty]); - redisCommands.push(['hset', coin + ':unique_workers:' + wallet_address + '~' + miner.workerName, 'lastShare', dateNowSeconds]); - redisCommands.push(['expire', coin + ':unique_workers:' + wallet_address + '~' + miner.workerName, (86400 * cleanupInterval)]); - } - - if (blockCandidate){ - redisCommands.push(['hset', coin + ':stats', 'lastBlockFound', Date.now()]); - - redisCommands.push(['rename', coin + ':scores:roundCurrent', coin + ':scores:round' + job_height]); - redisCommands.push(['rename', coin + ':shares_actual:roundCurrent', coin + ':shares_actual:round' + job_height]); - redisCommands.push(['hgetall', coin + ':scores:round' + job_height]); - redisCommands.push(['hgetall', coin + ':shares_actual:round' + job_height]); - - } - - redisClient.multi(redisCommands).exec(function(err, replies){ - if (err){ - log('error', logSystem, 'Failed to insert share data into redis %j \n %j', [err, redisCommands]); - return; - } - - if (slushMiningEnabled) { - job.score = parseFloat(replies[0][0]); - var age = parseFloat(replies[0][1]); - log('info', logSystem, 'Submitted score ' + job.score + ' for difficulty ' + job.difficulty + ' and round age ' + age + 's'); - } - - if (blockCandidate){ - var workerScores = replies[replies.length - 2]; - var workerShares = replies[replies.length - 1]; - var totalScore = Object.keys(workerScores).reduce(function(p, c){ - return p + parseFloat(workerScores[c]) - }, 0); - var totalShares = Object.keys(workerShares).reduce(function(p, c){ - return p + parseInt(workerShares[c]) - }, 0); - - redisClient.zadd(coin + ':blocks:candidates', job_height, [ - + if (workerName) { + redisCommands.push(['zadd', `${coin}:hashrate`, dateNowSeconds, [job.difficulty, login + '~' + workerName, dateNow, rewardType].join(':')]); + redisCommands.push(['hincrby', `${coin}:unique_workers:${login}~${workerName}`, 'hashes', job.difficulty]); + redisCommands.push(['hset', `${coin}:unique_workers:${login}~${workerName}`, 'lastShare', dateNowSeconds]); + redisCommands.push(['expire', `${coin}:unique_workers:${login}~${workerName}`, (86400 * cleanupInterval)]); + } + + if (blockCandidate) { + redisCommands.push(['hset', `${coin}:stats`, `lastBlockFound${rewardType}`, Date.now()]); + redisCommands.push(['rename', `${coin}:scores:prop:roundCurrent`, coin + ':scores:prop:round' + job_height]); + redisCommands.push(['rename', `${coin}:scores:solo:roundCurrent`, coin + ':scores:solo:round' + job_height]); + redisCommands.push(['rename', `${coin}:shares_actual:prop:roundCurrent`, `${coin}:shares_actual:prop:round${job_height}`]); + redisCommands.push(['rename', `${coin}:shares_actual:solo:roundCurrent`, `${coin}:shares_actual:solo:round${job_height}`]); + if (rewardType === 'prop') { + redisCommands.push(['hgetall', `${coin}:scores:prop:round${job_height}`]); + redisCommands.push(['hgetall', `${coin}:shares_actual:prop:round${job_height}`]); + } + if (rewardType === 'solo') { + redisCommands.push(['hget', `${coin}:scores:solo:round${job_height}`, login]); + redisCommands.push(['hget', `${coin}:shares_actual:solo:round${job_height}`, login]); + } + + } + + redisClient.multi(redisCommands) + .exec(function (err, replies) { + if (err) { + log('error', logSystem, 'Failed to insert share data into redis %j \n %j', [err, redisCommands]); + return; + } + + if (slushMiningEnabled) { + job.score = parseFloat(replies[0][0]); + let age = parseFloat(replies[0][1]); + log('info', logSystem, 'Submitted score ' + job.score + ' for difficulty ' + job.difficulty + ' and round age ' + age + 's'); + } + + if (blockCandidate) { + let workerScores = replies[replies.length - 2]; + let workerShares = replies[replies.length - 1]; + let totalScore = 0; + let totalShares = 0; + if (rewardType === 'solo') { + totalScore = workerScores + totalShares = workerShares + } + if (rewardType === 'prop') { + totalScore = Object.keys(workerScores) + .reduce(function (p, c) { + return p + parseFloat(workerScores[c]) + }, 0); + totalShares = Object.keys(workerShares) + .reduce(function (p, c) { + return p + parseInt(workerShares[c]) + }, 0); + } + redisClient.zadd(coin + ':blocks:candidates', job_height, [ + rewardType, + login, hashHex, Date.now() / 1000 | 0, blockTemplate.difficulty, totalShares, totalScore - ].join(':'), function(err, result){ - if (err){ - log('error', logSystem, 'Failed inserting block candidate %s \n %j', [hashHex, err]); - } - }); - - notifications.sendToAll('blockFound', { - 'HEIGHT': job_height, - 'HASH': hashHex, - 'DIFFICULTY': blockTemplate.difficulty, - 'SHARES': totalShares, - 'MINER': wallet_address.substring(0,7)+'...'+wallet_address.substring(miner.login.length-7) - }); - } - - }); - if (!isChildCoin) { - log('info', logSystem, 'Accepted %s share at difficulty %d/%d from %s@%s', [shareType, job.difficulty, shareDiff, miner.login, miner.ip]); - } + ].join(':'), function (err, result) { + if (err) { + log('error', logSystem, 'Failed inserting block candidate %s \n %j', [hashHex, err]); + } + }); + + notifications.sendToAll('blockFound', { + 'HEIGHT': job_height, + 'HASH': hashHex, + 'DIFFICULTY': blockTemplate.difficulty, + 'SHARES': totalShares, + 'MINER': login.substring(0, 7) + '...' + login.substring(login.length - 7) + }); + } + + }); + + log('info', logSystem, 'Accepted %s share at difficulty %d/%d from %s@%s', [shareType, job.difficulty, shareDiff, login, miner.ip]); +} + +function getShareBuffer (miner, job, blockTemplate, params) { + let nonce = params.nonce; + let resultHash = params.result; + let template = Buffer.alloc(blockTemplate.buffer.length); + if (!miner.proxy) { + blockTemplate.buffer.copy(template); + template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); + } else { + blockTemplate.buffer.copy(template); + template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); + template.writeUInt32BE(params.poolNonce, job.clientPoolLocation); + template.writeUInt32BE(params.workerNonce, job.clientNonceLocation); + } + + try { + let shareBuffer = utils.cnUtil.construct_block_blob(template, Buffer.from(nonce, 'hex'), cnBlobType); + return shareBuffer; + } catch (e) { + log('error', logSystem, "Can't get share buffer with nonce %s from %s@%s: %s", [nonce, miner.login, miner.ip, e]); + return null; + } } /** * Process miner share data **/ -function processShare(miner, job, blockTemplate, childBlockTemplate, params){ - var nonce = params.nonce; - var resultHash = params.result; - var template = Buffer.alloc(blockTemplate.buffer.length); - if (!miner.proxy) { - blockTemplate.buffer.copy(template); - template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); - } else { - blockTemplate.buffer.copy(template); - template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); - template.writeUInt32BE(params.poolNonce, job.clientPoolLocation); - template.writeUInt32BE(params.workerNonce, job.clientNonceLocation); - } - var shareBuffer = null; - if (config.poolServer.mergedMining) { - - shareBuffer = utils.cnUtil.construct_block_blob(template, new Buffer(nonce, 'hex'), cnBlobType, childBlockTemplate.buffer, config.childCoinPoW? config.childCoinPoW:-1); - } else { - shareBuffer = utils.cnUtil.construct_block_blob(template, new Buffer(nonce, 'hex'), cnBlobType, 0, config.childCoinPoW? config.childCoinPoW:-1); - } - - var convertedBlob; - var hash; - var shareType; - - if (shareTrustEnabled && miner.trust.threshold <= 0 && miner.trust.penalty <= 0 && Math.random() > miner.trust.probability){ - hash = Buffer.from(resultHash, 'hex'); - shareType = 'trusted'; - } - else { - - convertedBlob = utils.cnUtil.convert_blob(shareBuffer, cnBlobType, 0, config.childCoinPoW? config.childCoinPoW:-1); - - var hard_fork_version = convertedBlob[0]; - if (typeof config.includeHeight !== "undefined" && config.includeHeight) - hash = cryptoNight(convertedBlob, cnVariant, job.height); - else - hash = cryptoNight(convertedBlob, cnVariant); - log('info', logSystem, 'Mining pool algorithm: %s variant %d, Height: %d, Hard fork version: %d', [cnAlgorithm, cnVariant, job.height, hard_fork_version]); - shareType = 'valid'; - } - - if (hash.toString('hex') !== resultHash) { - log('warn', logSystem, 'Bad hash from miner %s@%s', [miner.login, miner.ip]); - return false; - } - - var hashArray = hash.toByteArray().reverse(); - var hashNum = bignum.fromBuffer(Buffer.from(hashArray)); - var hashDiff = diff1.div(hashNum); - - if (hashDiff.ge(blockTemplate.difficulty)){ - - apiInterfaces.rpcDaemon('submitblock', [shareBuffer.toString('hex')], function(error, result){ - if (error){ - log('error', logSystem, 'Error submitting block at height %d from %s@%s, share type: "%s" - %j', [job.height, miner.login, miner.ip, shareType, error]); - recordShareData(miner, job, hashDiff.toString(), false, null, shareType); - } - else{ - var blockFastHash = utils.cnUtil.get_block_id(shareBuffer, cnBlobType).toString('hex'); - log('info', logSystem, - 'Block %s found at height %d by miner %s@%s - submit result: %j', +function processShare (miner, job, blockTemplate, params) { + let shareBuffer = getShareBuffer(miner, job, blockTemplate, params) + if (!shareBuffer) { + return false + } + let resultHash = params.result + let hash; + let shareType; + + if (shareTrustEnabled && miner.trust.threshold <= 0 && miner.trust.penalty <= 0 && Math.random() > miner.trust.probability) { + hash = Buffer.from(resultHash, 'hex'); + shareType = 'trusted'; + } else { + let convertedBlob = utils.cnUtil.convert_blob(shareBuffer, cnBlobType); + let hard_fork_version = convertedBlob[0]; + + if (blockTemplate.isRandomX) { + hash = cryptoNight(convertedBlob, Buffer.from(blockTemplate.seed_hash, 'hex'), cnVariant); + } else { + if (typeof config.includeHeight !== "undefined" && config.includeHeight) + hash = cryptoNight(convertedBlob, cnVariant, job.height); + else + hash = cryptoNight(convertedBlob, cnVariant); + } + log('info', logSystem, 'Mining pool algorithm: %s variant %d, Hard fork version: %d', [cnAlgorithm, cnVariant, hard_fork_version]); + shareType = 'valid' + } + + if (hash.toString('hex') !== resultHash) { + log('warn', logSystem, 'Bad hash from miner %s@%s', [miner.login, miner.ip]); + return false; + } + + let hashArray = hash.toByteArray() + .reverse(); + let hashNum = bignum.fromBuffer(Buffer.from(hashArray)); + let hashDiff = diff1.div(hashNum); + + if (hashDiff.ge(blockTemplate.difficulty)) { + + apiInterfaces.rpcDaemon('submitblock', [shareBuffer.toString('hex')], function (error, result) { + if (error) { + log('error', logSystem, 'Error submitting block at height %d from %s@%s, share type: "%s" - %j', [job.height, miner.login, miner.ip, shareType, error]); + } else { + let blockFastHash = utils.cnUtil.get_block_id(shareBuffer, cnBlobType) + .toString('hex'); + log('info', logSystem, + 'Block %s found at height %d by miner %s@%s - submit result: %j', [blockFastHash.substr(0, 6), job.height, miner.login, miner.ip, result] - ); - recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate); - jobRefresh(); - } - }); - } - - else if (hashDiff.lt(job.difficulty)){ - log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.login, miner.ip]); - return false; - } - else{ - recordShareData(miner, job, hashDiff.toString(), false, null, shareType); - } - - if (config.poolServer.mergedMining){ - if (hashDiff.ge(childBlockTemplate.difficulty)){ - - - var mergedBuffer = utils.cnUtil.merge_blocks(shareBuffer, childBlockTemplate.buffer, config.childCoinPoW? config.childCoinPoW:-1); - apiInterfaces.rpcDaemon('submitblock', [mergedBuffer.toString('hex')], function(error, result){ - if (error){ - log('error', logSystem, 'Error submitting child block at height %d from %s@%s, share type: "%s" - %j', [job.childHeight, miner.login, miner.ip, shareType, error]); - if (miner.childWalletAddress) { - recordShareData(miner, job, hashDiff.toString(), false, null, shareType, null, true); - } - } - else{ - var blockFastHash = utils.cnUtil.get_block_id(mergedBuffer, 2).toString('hex'); - log('info', logSystem, - 'Child Block %s found at height %d by miner %s@%s - submit result: %j', - - [blockFastHash.substr(0, 6), job.childHeight, miner.login, miner.ip, result] - ); - if (miner.childWalletAddress) { - recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, childBlockTemplate, true); - - } - jobRefresh(); - } - }, config.childDaemon); - } else { - if (miner.childWalletAddress) { - recordShareData(miner, job, hashDiff.toString(), false, null, shareType, null, true); - } - } - } - - return true; + ); + recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate, null); + } + }); + } else if (hashDiff.lt(job.difficulty)) { + log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.login, miner.ip]); + return false; + } else { + recordShareData(miner, job, hashDiff.toString(), false, null, shareType, null, null); + } + + if (!job.childHeight) + return true + + var childBlockTemplate = blockTemplate.childBlockTemplate; + + if (childBlockTemplate) { + if (mergedMining) { + let pool = config.childPools[miner.activeChildPool] + if (hashDiff.ge(childBlockTemplate.difficulty)) { + let mergedBuffer = null + try { + mergedBuffer = utils.cnUtil.construct_mm_child_block_blob(shareBuffer, cnBlobType, childBlockTemplate.buffer); + } catch (e) { + log('error', logSystem, "Failed to construct MM child block: " + e); + } + if (mergedBuffer === null) { + recordShareStatusMerged(miner, 'invalid'); + } else { + + let onChildSuccess = (result) => { + let blockFastHash = utils.cnUtil.get_block_id(mergedBuffer, 2) + .toString('hex') + log('info', logSystem, + 'Child Block %s found at height %d by miner %s@%s - submit result: %j', + [blockFastHash.substr(0, 6), job.childHeight, miner.workerName, miner.ip, result]); + recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, childBlockTemplate, pool); + } + + apiInterfaces.rpcDaemon('submitblock', [mergedBuffer.toString('hex')], function (error, result) { + if (error) { + log('error', logSystem, 'Error submitting child block at height %d from %s@%s, share type: "%s" - %j', [job.childHeight, miner.login, miner.ip, shareType, error]); + } else { + onChildSuccess(result) + } + }, pool.childDaemon); + } + } else if (hashDiff.lt(job.difficulty)) { + log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.workerName, miner.ip]); + return false; + } else { + recordShareData(miner, job, hashDiff.toString(), false, null, shareType, null, pool); + } + } + return true; + } + return true; } /** * Start pool server on TCP ports **/ -var httpResponse = ' 200 OK\nContent-Type: text/plain\nContent-Length: 20\n\nMining server online'; - -function startPoolServerTcp(callback){ - log('info', logSystem, 'Clear values for connected workers in redis database.'); - redisClient.del(config.coin + ':active_connections'); - - async.each(config.poolServer.ports, function(portData, cback){ - var handleMessage = function(socket, jsonData, pushMessage){ - if (!jsonData.id) { - log('warn', logSystem, 'Miner RPC request missing RPC id'); - return; - } - else if (!jsonData.method) { - log('warn', logSystem, 'Miner RPC request missing RPC method'); - return; - } - else if (!jsonData.params) { - log('warn', logSystem, 'Miner RPC request missing RPC params'); - return; - } - - var sendReply = function(error, result){ - if(!socket.writable) return; - var sendData = JSON.stringify({ - id: jsonData.id, - jsonrpc: "2.0", - error: error ? {code: -1, message: error} : null, - result: result - }) + "\n"; - socket.write(sendData); - }; - - handleMinerMethod(jsonData.method, jsonData.params, socket.remoteAddress, portData, sendReply, pushMessage); - }; - - var socketResponder = function(socket){ - socket.setKeepAlive(true); - socket.setEncoding('utf8'); - - var dataBuffer = ''; - - var pushMessage = function(method, params){ - if(!socket.writable) return; - var sendData = JSON.stringify({ - jsonrpc: "2.0", - method: method, - params: params - }) + "\n"; - socket.write(sendData); - }; - - socket.on('data', function(d){ - dataBuffer += d; - if (Buffer.byteLength(dataBuffer, 'utf8') > 10240){ //10KB - dataBuffer = null; - log('warn', logSystem, 'Socket flooding detected and prevented from %s', [socket.remoteAddress]); - socket.destroy(); - return; - } - if (dataBuffer.indexOf('\n') !== -1){ - var messages = dataBuffer.split('\n'); - var incomplete = dataBuffer.slice(-1) === '\n' ? '' : messages.pop(); - for (var i = 0; i < messages.length; i++){ - var message = messages[i]; - if (message.trim() === '') continue; - var jsonData; - try{ - jsonData = JSON.parse(message); - } - catch(e){ - if (message.indexOf('GET /') === 0) { - if (message.indexOf('HTTP/1.1') !== -1) { - socket.end('HTTP/1.1' + httpResponse); - break; - } - else if (message.indexOf('HTTP/1.0') !== -1) { - socket.end('HTTP/1.0' + httpResponse); - break; - } - } - - log('warn', logSystem, 'Malformed message from %s: %s', [socket.remoteAddress, message]); - socket.destroy(); - - break; - } - try { - handleMessage(socket, jsonData, pushMessage); - } catch (e) { - log('warn', logSystem, 'Malformed message from ' + socket.remoteAddress + ' generated an exception. Message: ' + message); - if (e.message) log('warn', logSystem, 'Exception: ' + e.message); - } - } - dataBuffer = incomplete; - } - }).on('error', function(err){ - if (err.code !== 'ECONNRESET') - log('warn', logSystem, 'Socket error from %s %j', [socket.remoteAddress, err]); - }).on('close', function(){ - pushMessage = function(){}; - }); - }; - - if (portData.ssl) { - if (!config.poolServer.sslCert) { - log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate not configured', [portData.port]); - cback(true); - } else if (!config.poolServer.sslKey) { - log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL key not configured', [portData.port]); - cback(true); - } else if (!config.poolServer.sslCA) { - log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate authority not configured', [portData.port]); - cback(true); - } else if (!fs.existsSync(config.poolServer.sslCert)) { - log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate file not found (configuration error)', [portData.port]); - cback(true); - } else if (!fs.existsSync(config.poolServer.sslKey)) { - log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL key file not found (configuration error)', [portData.port]); - cback(true); - } else if (!fs.existsSync(config.poolServer.sslCA)) { - log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate authority file not found (configuration error)', [portData.port]); - cback(true); - } else { - var options = { - key: fs.readFileSync(config.poolServer.sslKey), - cert: fs.readFileSync(config.poolServer.sslCert), - ca: fs.readFileSync(config.poolServer.sslCA) - }; - tls.createServer(options, socketResponder).listen(portData.port, function (error, result) { - if (error) { - log('error', logSystem, 'Could not start server listening on port %d (SSL), error: $j', [portData.port, error]); - cback(true); - return; - } - - log('info', logSystem, 'Clear values for SSL port %d in redis database.', [portData.port]); - redisClient.del(config.coin + ':ports:'+portData.port); - redisClient.hset(config.coin + ':ports:'+portData.port, 'port', portData.port); - - log('info', logSystem, 'Started server listening on port %d (SSL)', [portData.port]); - cback(); - }); - } - } - else { - net.createServer(socketResponder).listen(portData.port, function (error, result) { - if (error) { - log('error', logSystem, 'Could not start server listening on port %d, error: $j', [portData.port, error]); - cback(true); - return; - } - - log('info', logSystem, 'Clear values for port %d in redis database.', [portData.port]); - redisClient.del(config.coin + ':ports:'+portData.port); - redisClient.hset(config.coin + ':ports:'+portData.port, 'port', portData.port); - - log('info', logSystem, 'Started server listening on port %d', [portData.port]); - cback(); - }); - } - }, function(err){ - if (err) - callback(false); - else - callback(true); - }); +let httpResponse = ' 200 OK\nContent-Type: text/plain\nContent-Length: 20\n\nMining server online'; + +function startPoolServerTcp (callback) { + log('info', logSystem, 'Clear values for connected workers in redis database.'); + redisClient.del(config.coin + ':active_connections'); + + async.each(config.poolServer.ports, function (portData, cback) { + let handleMessage = function (socket, jsonData, pushMessage) { + if (!jsonData.id) { + log('warn', logSystem, 'Miner RPC request missing RPC id'); + return; + } else if (!jsonData.method) { + log('warn', logSystem, 'Miner RPC request missing RPC method'); + return; + } else if (!jsonData.params) { + log('warn', logSystem, 'Miner RPC request missing RPC params'); + return; + } + + let sendReply = function (error, result) { + if (!socket.writable) return; + let sendData = JSON.stringify({ + id: jsonData.id, + jsonrpc: "2.0", + error: error ? { + code: -1, + message: error + } : null, + result: result + }) + "\n"; + socket.write(sendData); + }; + + handleMinerMethod(jsonData.method, jsonData.params, socket.remoteAddress, portData, sendReply, pushMessage); + }; + + let socketResponder = function (socket) { + socket.setKeepAlive(true); + socket.setEncoding('utf8'); + + let dataBuffer = ''; + + let pushMessage = function (method, params) { + if (!socket.writable) return; + let sendData = JSON.stringify({ + jsonrpc: "2.0", + method: method, + params: params + }) + "\n"; + socket.write(sendData); + }; + + socket.on('data', function (d) { + dataBuffer += d; + if (Buffer.byteLength(dataBuffer, 'utf8') > 10240) { //10KB + dataBuffer = null; + log('warn', logSystem, 'Socket flooding detected and prevented from %s', [socket.remoteAddress]); + socket.destroy(); + return; + } + if (dataBuffer.indexOf('\n') !== -1) { + let messages = dataBuffer.split('\n'); + let incomplete = dataBuffer.slice(-1) === '\n' ? '' : messages.pop(); + for (let i = 0; i < messages.length; i++) { + let message = messages[i]; + if (message.trim() === '') continue; + let jsonData; + try { + jsonData = JSON.parse(message); + } catch (e) { + if (message.indexOf('GET /') === 0) { + if (message.indexOf('HTTP/1.1') !== -1) { + socket.end('HTTP/1.1' + httpResponse); + break; + } else if (message.indexOf('HTTP/1.0') !== -1) { + socket.end('HTTP/1.0' + httpResponse); + break; + } + } + + log('warn', logSystem, 'Malformed message from %s: %s', [socket.remoteAddress, message]); + socket.destroy(); + + break; + } + try { + handleMessage(socket, jsonData, pushMessage); + } catch (e) { + log('warn', logSystem, 'Malformed message from ' + socket.remoteAddress + ' generated an exception. Message: ' + message); + if (e.message) log('warn', logSystem, 'Exception: ' + e.message); + } + } + dataBuffer = incomplete; + } + }) + .on('error', function (err) { + if (err.code !== 'ECONNRESET') + log('warn', logSystem, 'Socket error from %s %j', [socket.remoteAddress, err]); + }) + .on('close', function () { + pushMessage = function () {}; + }); + }; + + if (portData.ssl) { + if (!config.poolServer.sslCert) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate not configured', [portData.port]); + cback(true); + } else if (!config.poolServer.sslKey) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL key not configured', [portData.port]); + cback(true); + } else if (!fs.existsSync(config.poolServer.sslCert)) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate file not found (configuration error)', [portData.port]); + cback(true); + } else if (!fs.existsSync(config.poolServer.sslKey)) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL key file not found (configuration error)', [portData.port]); + cback(true); + } else { + let options = { + key: fs.readFileSync(config.poolServer.sslKey), + cert: fs.readFileSync(config.poolServer.sslCert), + }; + + if (config.poolServer.sslCA && fs.existsSync(config.poolServer.sslCA)) { + options.ca = fs.readFileSync(config.poolServer.sslCA) + } + + tls.createServer(options, socketResponder) + .listen(portData.port, function (error, result) { + if (error) { + log('error', logSystem, 'Could not start server listening on port %d (SSL), error: $j', [portData.port, error]); + cback(true); + return; + } + + log('info', logSystem, 'Clear values for SSL port %d in redis database.', [portData.port]); + redisClient.del(config.coin + ':ports:' + portData.port); + redisClient.hset(config.coin + ':ports:' + portData.port, 'port', portData.port); + + log('info', logSystem, 'Started server listening on port %d (SSL)', [portData.port]); + cback(); + }); + } + } else { + net.createServer(socketResponder) + .listen(portData.port, function (error, result) { + if (error) { + log('error', logSystem, 'Could not start server listening on port %d, error: $j', [portData.port, error]); + cback(true); + return; + } + + log('info', logSystem, 'Clear values for port %d in redis database.', [portData.port]); + redisClient.del(config.coin + ':ports:' + portData.port); + redisClient.hset(config.coin + ':ports:' + portData.port, 'port', portData.port); + + log('info', logSystem, 'Started server listening on port %d', [portData.port]); + cback(); + }); + } + }, function (err) { + if (err) + callback(false); + else + callback(true); + }); } /** * Initialize pool server **/ - -(function init(){ - jobRefresh(true, function(sucessful){ }); + +(function init (loop) { + async.waterfall([ + function (callback) { + if (!poolStarted) { + startPoolServerTcp(function (successful) { + poolStarted = true + }); + setTimeout(init, 1000, loop); + return; + } + callback(true) + } + ], + function (err) { + if (loop === true) { + setTimeout(function () { + init(true); + }, config.poolServer.blockRefreshInterval); + } + } + ); })(); diff --git a/lib/telegram.js b/lib/telegram.js index 56f09c9f8..4ff862d2e 100644 --- a/lib/telegram.js +++ b/lib/telegram.js @@ -8,69 +8,57 @@ **/ // Load required modules -var https = require('https'); -var querystring = require('querystring'); +process.env.NTBA_FIX_319 = 1; +const TelegramBot = require('node-telegram-bot-api'); // Initialize log system -var logSystem = 'telegram'; +const logSystem = 'telegram'; require('./exceptionWriter.js')(logSystem); /** * Send telegram message **/ -exports.sendMessage = function(chatId, messageText) { - // Return error if no text content - if (!messageText) { - log('warn', logSystem, 'No text to send.'); - return ; - } +exports.sendMessage = function (chatId, messageText) { + // Return error if no text content + if (!messageText) { + log('warn', logSystem, 'No text to send.'); + return; + } - // Check telegram configuration - if (!config.telegram) { - log('error', logSystem, 'Telegram is not configured!'); - return ; - } - - // Do nothing if telegram is disabled - if (!config.telegram.enabled) return ; - - // Telegram bot token - var token = config.telegram.token || ''; - if (!token || token === '') { - log('error', logSystem, 'No telegram token specified in configuration!'); - return ; - } - - // Telegram chat id - if (!chatId || chatId === '' || chatId === '@') { - log('error', logSystem, 'No telegram chat id specified!'); - return ; - } + // Check telegram configuration + if (!config.telegram) { + log('error', logSystem, 'Telegram is not configured!'); + return; + } - // Set telegram API URL - var action = "sendMessage"; - var params = { chat_id: chatId, text: messageText, parse_mode: 'Markdown' }; + // Do nothing if telegram is disabled + if (!config.telegram.enabled) return; - var apiURL = 'https://api.telegram.org/bot' + token + '/' + action; - apiURL += '?' + querystring.stringify(params); + // Telegram bot token + const token = config.telegram.token || ''; + if (!token || token === '') { + log('error', logSystem, 'No telegram token specified in configuration!'); + return; + } - https.get(apiURL, function(request) { - var data = ''; - request.on("data", function(chunk) { data += chunk; }); - request.on("end", function() { - if (!data) { - log('error', logSystem, 'Telegram request failed: communication error (no data)'); - return ; - } - var response = JSON.parse(data); - if (response && !response.ok) { - log('error', logSystem, 'Telegram API error: [%s] %s', [response.error_code, response.description]); - return ; - } - log('info', logSystem, 'Telegram message sent to %s: %s', [params.chat_id, messageText]); - }); - }).on("error", function(error) { - log('error', logSystem, 'Telegram request failed: %s', [error.message]); - return ; - }); + // Telegram chat id + if (!chatId || chatId === '' || chatId === '@') { + log('error', logSystem, 'No telegram chat id specified!'); + return; + } + + const bot = new TelegramBot(token); + bot.sendMessage(chatId, messageText, { + parse_mode: 'Markdown' + }) + .then(() => { + log('info', logSystem, 'Telegram message sent to %s: %s', [chatId, messageText]); + }, error => { + log('error', logSystem, 'Telegram request failed: %s', [error.code]); + if (error.code === 'EFATAL') { + log('error', logSystem, 'Telegram request failed: communication error (no data)'); + } else { + log('error', logSystem, 'Telegram API error: [%s] %s', [error.response.body.error_code, error.response.body.description]); + } + }); } diff --git a/lib/telegramBot.js b/lib/telegramBot.js index 678132eed..7626d85a5 100644 --- a/lib/telegramBot.js +++ b/lib/telegramBot.js @@ -9,16 +9,16 @@ // Load required modules process.env.NTBA_FIX_319 = 1; -var TelegramBot = require('node-telegram-bot-api'); +let TelegramBot = require('node-telegram-bot-api'); -var timeAgo = require('time-ago'); +let timeAgo = require('time-ago'); -var apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); -var notifications = require('./notifications.js'); -var utils = require('./utils.js'); +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +let notifications = require('./notifications.js'); +let utils = require('./utils.js'); // Initialize log system -var logSystem = 'telegramBot'; +let logSystem = 'telegramBot'; require('./exceptionWriter.js')(logSystem); /** @@ -27,321 +27,334 @@ require('./exceptionWriter.js')(logSystem); // Check bot settings if (!config.telegram) { - log('error', logSystem, 'Telegram is not enabled'); -} -else if (!config.telegram.enabled) { - log('error', logSystem, 'Telegram is not enabled'); -} -else if (!config.telegram.token) { - log('error', logSystem, 'No telegram token found in configuration'); + log('error', logSystem, 'Telegram is not enabled'); +} else if (!config.telegram.enabled) { + log('error', logSystem, 'Telegram is not enabled'); +} else if (!config.telegram.token) { + log('error', logSystem, 'No telegram token found in configuration'); } // Bot commands -var botCommands = { - stats: "/stats", - report: "/report", - notify: "/notify", - blocks: "/blocks" +let botCommands = { + stats: "/stats", + report: "/report", + notify: "/notify", + blocks: "/blocks" } if (config.telegram.botCommands) { - Object.assign(botCommands, config.telegram.botCommands); + Object.assign(botCommands, config.telegram.botCommands); } // Telegram channel -var channel = config.telegram.channel.replace(/@/g, '') || ''; +let channel = config.telegram.channel.replace(/@/g, '') || ''; // Periodical channel statistics -var periodicalStats = (channel && config.telegram.channelStats && config.telegram.channelStats.enabled) -var statsInterval = (config.telegram.channelStats && config.telegram.channelStats.interval > 0) ? parseInt(config.telegram.channelStats.interval) : 0; - +let periodicalStats = (channel && config.telegram.channelStats && config.telegram.channelStats.enabled) +let statsInterval = (config.telegram.channelStats && config.telegram.channelStats.interval > 0) ? parseInt(config.telegram.channelStats.interval) : 0; + /** * Initialize new telegram bot **/ log('info', logSystem, 'Started'); -var token = config.telegram.token; -var bot = new TelegramBot(token, {polling: true}); +let token = config.telegram.token; +let bot = new TelegramBot(token, { + polling: true +}); /** * Periodical pool statistics **/ if (periodicalStats && statsInterval > 0 && channel) { - log('info', logSystem, 'Sending pool statistics to telegram channel @%s each %d minutes', [channel, statsInterval]); - setInterval(function(){ sendPoolStats('@'+channel); }, (statsInterval*60)*1000); + log('info', logSystem, 'Sending pool statistics to telegram channel @%s each %d minutes', [channel, statsInterval]); + setInterval(function () { + sendPoolStats('@' + channel); + }, (statsInterval * 60) * 1000); } /** * Handle "/start" or "/help" **/ - + bot.onText(new RegExp('^/(start|help)$', 'i'), (telegramMsg) => { - if (telegramMsg.from.id != telegramMsg.chat.id) return ; + if (telegramMsg.from.id != telegramMsg.chat.id) return; - log('info', logSystem, 'Commands list request from @%s (%s)', [telegramMsg.from.username, telegramMsg.from.id]); + log('info', logSystem, 'Commands list request from @%s (%s)', [telegramMsg.from.username, telegramMsg.from.id]); - var message = 'Hi @' + telegramMsg.from.username + ',\n\n' + - 'Here are the commands you can use:\n\n' + - 'Pool statistics: ' + botCommands['stats'] + '\n' + - 'Blocks notifications: ' + botCommands['blocks'] + '\n' + - 'Miner statistics: ' + botCommands['report'] + ' _address_\n' + - 'Miner notifications: ' + botCommands['notify'] + ' _address_\n\n' + - 'Thank you!'; + let message = 'Hi @' + telegramMsg.from.username + ',\n\n' + + 'Here are the commands you can use:\n\n' + + 'Pool statistics: ' + botCommands['stats'] + '\n' + + 'Blocks notifications: ' + botCommands['blocks'] + '\n' + + 'Miner statistics: ' + botCommands['report'] + ' _address_\n' + + 'Miner notifications: ' + botCommands['notify'] + ' _address_\n\n' + + 'Thank you!'; - bot.sendMessage(telegramMsg.from.id, message, { parse_mode: 'Markdown' }); + bot.sendMessage(telegramMsg.from.id, message, { + parse_mode: 'Markdown' + }); }); /** * Pool Statistics **/ -bot.onText(new RegExp('^'+botCommands['stats']+'$', 'i'), (telegramMsg) => { - log('info', logSystem, 'Pool statistics request from @%s (%s)', [telegramMsg.from.username, telegramMsg.from.id]); - sendPoolStats(telegramMsg.chat.id); +bot.onText(new RegExp('^' + botCommands['stats'] + '$', 'i'), (telegramMsg) => { + log('info', logSystem, 'Pool statistics request from @%s (%s)', [telegramMsg.from.username, telegramMsg.from.id]); + sendPoolStats(telegramMsg.chat.id); }); -function sendPoolStats(chatId) { - apiInterfaces.pool('/stats', function(error, stats) { - if (error || !stats) { - log('error', logSystem, 'Unable to get API data for stats: ' + error); - return bot.sendMessage(id, 'Unable to get pool statistics. Please retry.'); - } - - var poolHost = config.poolHost || "Pool"; - var poolHashrate = utils.getReadableHashRate(stats.pool.hashrate); - var poolMiners = stats.pool.miners || 0; - var poolWorkers = stats.pool.workers || 0; - var poolBlocks = stats.pool.totalBlocks || 0; - var poolLastBlock = (stats.pool.lastBlockFound) ? timeAgo.ago(new Date(parseInt(stats.pool.lastBlockFound))) : 'Never'; - - var networkHashrate = utils.getReadableHashRate(stats.network.difficulty / stats.config.coinDifficultyTarget); - var networkDiff = stats.network.difficulty || 'N/A'; - var networkHeight = stats.network.height || 'N/A'; - var networkLastReward = utils.getReadableCoins(stats.lastblock.reward); - var networkLastBlock = (stats.lastblock.timestamp) ? timeAgo.ago(new Date(parseInt(stats.lastblock.timestamp * 1000))) : 'Never'; - - var currentEffort = stats.pool.roundHashes ? (stats.pool.roundHashes / stats.network.difficulty * 100).toFixed(1) + '%' : '0%'; - - var response = ''; - response += '*' + poolHost + '*\n'; - response += 'Hashrate: ' + poolHashrate + '\n'; - response += 'Connected Miners: ' + poolMiners + '\n'; - response += 'Active Workers: ' + poolWorkers + '\n'; - response += 'Blocks Found: ' + poolBlocks + '\n'; - response += 'Last Block: ' + poolLastBlock + '\n'; - response += 'Current Effort: ' + currentEffort + '\n'; - response += '\n'; - response += '*Network*\n'; - response += 'Hashrate: ' + networkHashrate + '\n'; - response += 'Difficulty: ' + networkDiff + '\n'; - response += 'Block Height: ' + networkHeight + '\n'; - response += 'Block Found: ' + networkLastBlock + '\n'; - response += 'Last Reward: ' + networkLastReward; - - return bot.sendMessage(chatId, response, { parse_mode: 'Markdown' }); - }); +function sendPoolStats (chatId) { + apiInterfaces.pool('/stats', function (error, stats) { + if (error || !stats) { + log('error', logSystem, 'Unable to get API data for stats: ' + error); + return bot.sendMessage(id, 'Unable to get pool statistics. Please retry.'); + } + + let poolHost = config.poolHost || "Pool"; + let poolHashrate = utils.getReadableHashRate(stats.pool.hashrate); + let poolMiners = stats.pool.miners || 0; + let poolWorkers = stats.pool.workers || 0; + let poolBlocks = stats.pool.totalBlocks || 0; + let poolLastBlock = (stats.pool.lastBlockFound) ? timeAgo.ago(new Date(parseInt(stats.pool.lastBlockFound))) : 'Never'; + + let networkHashrate = utils.getReadableHashRate(stats.network.difficulty / stats.config.coinDifficultyTarget); + let networkDiff = stats.network.difficulty || 'N/A'; + let networkHeight = stats.network.height || 'N/A'; + let networkLastReward = utils.getReadableCoins(stats.lastblock.reward); + let networkLastBlock = (stats.lastblock.timestamp) ? timeAgo.ago(new Date(parseInt(stats.lastblock.timestamp * 1000))) : 'Never'; + + let currentEffort = stats.pool.roundHashes ? (stats.pool.roundHashes / stats.network.difficulty * 100) + .toFixed(1) + '%' : '0%'; + + let response = ''; + response += '*' + poolHost + '*\n'; + response += 'Hashrate: ' + poolHashrate + '\n'; + response += 'Connected Miners: ' + poolMiners + '\n'; + response += 'Active Workers: ' + poolWorkers + '\n'; + response += 'Blocks Found: ' + poolBlocks + '\n'; + response += 'Last Block: ' + poolLastBlock + '\n'; + response += 'Current Effort: ' + currentEffort + '\n'; + response += '\n'; + response += '*Network*\n'; + response += 'Hashrate: ' + networkHashrate + '\n'; + response += 'Difficulty: ' + networkDiff + '\n'; + response += 'Block Height: ' + networkHeight + '\n'; + response += 'Block Found: ' + networkLastBlock + '\n'; + response += 'Last Reward: ' + networkLastReward; + + return bot.sendMessage(chatId, response, { + parse_mode: 'Markdown' + }); + }); } /** * Miner Statistics **/ -bot.onText(new RegExp('^'+botCommands['report']+'$', 'i'), (telegramMsg) => { - if (telegramMsg.from.id != telegramMsg.chat.id) return ; - - var apiRequest = '/get_telegram_notifications?chatId='+telegramMsg.from.id+'&type=default'; - apiInterfaces.pool(apiRequest, function(error, response) { - if (response.address) { - sendMinerStats(telegramMsg, response.address); - } else { - var message = 'To display miner report you need to specify the miner address on first request'; - bot.sendMessage(telegramMsg.from.id, message, { parse_mode: 'Markdown' }); - } - }); +bot.onText(new RegExp('^' + botCommands['report'] + '$', 'i'), (telegramMsg) => { + if (telegramMsg.from.id != telegramMsg.chat.id) return; + + let apiRequest = '/get_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=default'; + apiInterfaces.pool(apiRequest, function (error, response) { + if (response.address) { + sendMinerStats(telegramMsg, response.address); + } else { + let message = 'To display miner report you need to specify the miner address on first request'; + bot.sendMessage(telegramMsg.from.id, message, { + parse_mode: 'Markdown' + }); + } + }); }); -bot.onText(new RegExp('^'+botCommands['report']+' (.*)$', 'i'), (telegramMsg, match) => { - if (telegramMsg.from.id != telegramMsg.chat.id) return ; +bot.onText(new RegExp('^' + botCommands['report'] + ' (.*)$', 'i'), (telegramMsg, match) => { + if (telegramMsg.from.id != telegramMsg.chat.id) return; - var address = (match && match[1]) ? match[1].trim() : ''; - if (!address || address == '') { - return bot.sendMessage(telegramMsg.from.id, 'No address specified!'); - } + let address = (match && match[1]) ? match[1].trim() : ''; + if (!address || address == '') { + return bot.sendMessage(telegramMsg.from.id, 'No address specified!'); + } - sendMinerStats(telegramMsg, address); + sendMinerStats(telegramMsg, address); }); -function sendMinerStats(telegramMsg, address) { - log('info', logSystem, 'Miner report request from @%s (%s) for address: %s', [telegramMsg.from.username, telegramMsg.from.id, address]); - apiInterfaces.pool('/stats_address?address='+address, function(error, data) { - if (error || !data) { - log('error', logSystem, 'Unable to get API data for miner stats: ' + error); - return bot.sendMessage(telegramMsg.from.id, 'Unable to get miner statistics. Please retry.'); - } - if (!data.stats) { - return bot.sendMessage(telegramMsg.from.id, 'No miner statistics found for that address. Please check the address and try again.'); - } - - var minerHashrate = utils.getReadableHashRate(data.stats.hashrate); - var minerBalance = utils.getReadableCoins(data.stats.balance); - var minerPaid = utils.getReadableCoins(data.stats.paid); - var minerLastShare = timeAgo.ago(new Date(parseInt(data.stats.lastShare * 1000))); - - var response = '*Report for ' + address.substring(0,7)+'...'+address.substring(address.length-7) + '*\n'; - response += 'Hashrate: ' + minerHashrate + '\n'; - response += 'Last share: ' + minerLastShare + '\n'; - response += 'Balance: ' + minerBalance + '\n'; - response += 'Paid: ' + minerPaid + '\n'; - if (data.workers && data.workers.length > 0) { - var f = true; - for (var i in data.workers) { - if (!data.workers[i] || !data.workers[i].hashrate || data.workers[i].hashrate === 0) continue; - if (f) { - response += '\n'; - response += '*Active Workers*\n'; - } - var workerName = data.workers[i].name; - var workerHashrate = utils.getReadableHashRate(data.workers[i].hashrate); - response += workerName + ': ' + workerHashrate + '\n'; - f = false; - } - } - bot.sendMessage(telegramMsg.from.id, response, { parse_mode: 'Markdown' }); - - var apiRequest = '/set_telegram_notifications?chatId='+telegramMsg.from.id+'&type=default&address='+address; - apiInterfaces.pool(apiRequest, function(error, response) {}); - }); +function sendMinerStats (telegramMsg, address) { + log('info', logSystem, 'Miner report request from @%s (%s) for address: %s', [telegramMsg.from.username, telegramMsg.from.id, address]); + apiInterfaces.pool('/stats_address?address=' + address, function (error, data) { + if (error || !data) { + log('error', logSystem, 'Unable to get API data for miner stats: ' + error); + return bot.sendMessage(telegramMsg.from.id, 'Unable to get miner statistics. Please retry.'); + } + if (!data.stats) { + return bot.sendMessage(telegramMsg.from.id, 'No miner statistics found for that address. Please check the address and try again.'); + } + + let minerHashrate = utils.getReadableHashRate(data.stats.hashrate); + let minerBalance = utils.getReadableCoins(data.stats.balance); + let minerPaid = utils.getReadableCoins(data.stats.paid); + let minerLastShare = timeAgo.ago(new Date(parseInt(data.stats.lastShare * 1000))); + + let response = '*Report for ' + address.substring(0, 7) + '...' + address.substring(address.length - 7) + '*\n'; + response += 'Hashrate: ' + minerHashrate + '\n'; + response += 'Last share: ' + minerLastShare + '\n'; + response += 'Balance: ' + minerBalance + '\n'; + response += 'Paid: ' + minerPaid + '\n'; + if (data.workers && data.workers.length > 0) { + let f = true; + for (let i in data.workers) { + if (!data.workers[i] || !data.workers[i].hashrate || data.workers[i].hashrate === 0) continue; + if (f) { + response += '\n'; + response += '*Active Workers*\n'; + } + let workerName = data.workers[i].name; + let workerHashrate = utils.getReadableHashRate(data.workers[i].hashrate); + response += workerName + ': ' + workerHashrate + '\n'; + f = false; + } + } + bot.sendMessage(telegramMsg.from.id, response, { + parse_mode: 'Markdown' + }); + + let apiRequest = '/set_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=default&address=' + address; + apiInterfaces.pool(apiRequest, function (error, response) {}); + }); } /** * Miner notifications **/ -bot.onText(new RegExp('^'+botCommands['notify']+'$', 'i'), (telegramMsg) => { - if (telegramMsg.from.id != telegramMsg.chat.id) return ; - - var apiRequest = '/get_telegram_notifications?chatId='+telegramMsg.from.id+'&type=default'; - apiInterfaces.pool(apiRequest, function(error, response) { - if (response.address) { - toggleMinerNotifications(telegramMsg, response.address); - } else { - var message = 'To enable or disable notifications you need to specify the miner address on first request'; - bot.sendMessage(telegramMsg.from.id, message, { parse_mode: 'Markdown' }); - } - }); +bot.onText(new RegExp('^' + botCommands['notify'] + '$', 'i'), (telegramMsg) => { + if (telegramMsg.from.id != telegramMsg.chat.id) return; + + let apiRequest = '/get_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=default'; + apiInterfaces.pool(apiRequest, function (error, response) { + if (response.address) { + toggleMinerNotifications(telegramMsg, response.address); + } else { + let message = 'To enable or disable notifications you need to specify the miner address on first request'; + bot.sendMessage(telegramMsg.from.id, message, { + parse_mode: 'Markdown' + }); + } + }); }); -bot.onText(new RegExp('^'+botCommands['notify']+' (.*)$', 'i'), (telegramMsg, match) => { - if (telegramMsg.from.id != telegramMsg.chat.id) return ; +bot.onText(new RegExp('^' + botCommands['notify'] + ' (.*)$', 'i'), (telegramMsg, match) => { + if (telegramMsg.from.id != telegramMsg.chat.id) return; - var address = (match && match[1]) ? match[1].trim() : ''; - if (!address || address == '') { - return bot.sendMessage(telegramMsg.from.id, 'No address specified!'); - } + let address = (match && match[1]) ? match[1].trim() : ''; + if (!address || address == '') { + return bot.sendMessage(telegramMsg.from.id, 'No address specified!'); + } - toggleMinerNotifications(telegramMsg, address); + toggleMinerNotifications(telegramMsg, address); }); -function toggleMinerNotifications(telegramMsg, address) { - var apiRequest = '/get_telegram_notifications?chatId='+telegramMsg.from.id+'&type=miner&address='+address; - apiInterfaces.pool(apiRequest, function(error, response) { - if (response.chatId && response.chatId == telegramMsg.from.id) { - disableMinerNotifications(telegramMsg, address); - } else { - enableMinerNotifications(telegramMsg, address); - } - }); +function toggleMinerNotifications (telegramMsg, address) { + let apiRequest = '/get_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=miner&address=' + address; + apiInterfaces.pool(apiRequest, function (error, response) { + if (response.chatId && response.chatId == telegramMsg.from.id) { + disableMinerNotifications(telegramMsg, address); + } else { + enableMinerNotifications(telegramMsg, address); + } + }); } -function enableMinerNotifications(telegramMsg, address) { - log('info', logSystem, 'Enable miner notifications to @%s (%s) for address: %s', [telegramMsg.from.username, telegramMsg.from.id, address]); - var apiRequest = '/set_telegram_notifications?chatId='+telegramMsg.from.id+'&type=miner&address='+address+'&action=enable'; - apiInterfaces.pool(apiRequest, function(error, response) { - if (error) { - log('error', logSystem, 'Unable to enable telegram notifications: ' + error); - return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); - } - if (response.status != 'done') { - return bot.sendMessage(telegramMsg.from.id, response.status); - } - - bot.sendMessage(telegramMsg.from.id, 'Miner notifications enabled for ' + address.substring(0,7)+'...'+address.substring(address.length-7)); - - var apiRequest = '/set_telegram_notifications?chatId='+telegramMsg.from.id+'&type=default&address='+address; - apiInterfaces.pool(apiRequest, function(error, response) {}); - }); +function enableMinerNotifications (telegramMsg, address) { + log('info', logSystem, 'Enable miner notifications to @%s (%s) for address: %s', [telegramMsg.from.username, telegramMsg.from.id, address]); + let apiRequest = '/set_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=miner&address=' + address + '&action=enable'; + apiInterfaces.pool(apiRequest, function (error, response) { + if (error) { + log('error', logSystem, 'Unable to enable telegram notifications: ' + error); + return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); + } + if (response.status != 'done') { + return bot.sendMessage(telegramMsg.from.id, response.status); + } + + bot.sendMessage(telegramMsg.from.id, 'Miner notifications enabled for ' + address.substring(0, 7) + '...' + address.substring(address.length - 7)); + + let apiRequest = '/set_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=default&address=' + address; + apiInterfaces.pool(apiRequest, function (error, response) {}); + }); } -function disableMinerNotifications(telegramMsg, address) { - log('info', logSystem, 'Disable miner notifications to @%s (%s) for address: %s', [telegramMsg.from.username, telegramMsg.from.id, address]); - var apiRequest = '/set_telegram_notifications?chatId='+telegramMsg.from.id+'&type=miner&address='+address+'&action=disable'; - apiInterfaces.pool(apiRequest, function(error, response) { - if (error) { - log('error', logSystem, 'Unable to disable telegram notifications: ' + error); - return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); - } - if (response.status != 'done') { - return bot.sendMessage(telegramMsg.from.id, response.status); - } - - bot.sendMessage(telegramMsg.from.id, 'Miner notifications disabled for ' + address.substring(0,7)+'...'+address.substring(address.length-7)); - - var apiRequest = '/set_telegram_notifications?chatId='+telegramMsg.from.id+'&type=default&address='+address; - apiInterfaces.pool(apiRequest, function(error, response) {}); - }); +function disableMinerNotifications (telegramMsg, address) { + log('info', logSystem, 'Disable miner notifications to @%s (%s) for address: %s', [telegramMsg.from.username, telegramMsg.from.id, address]); + let apiRequest = '/set_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=miner&address=' + address + '&action=disable'; + apiInterfaces.pool(apiRequest, function (error, response) { + if (error) { + log('error', logSystem, 'Unable to disable telegram notifications: ' + error); + return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); + } + if (response.status != 'done') { + return bot.sendMessage(telegramMsg.from.id, response.status); + } + + bot.sendMessage(telegramMsg.from.id, 'Miner notifications disabled for ' + address.substring(0, 7) + '...' + address.substring(address.length - 7)); + + let apiRequest = '/set_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=default&address=' + address; + apiInterfaces.pool(apiRequest, function (error, response) {}); + }); } /** * Blocks notifications **/ -bot.onText(new RegExp('^'+botCommands['blocks']+'$', 'i'), (telegramMsg) => { - if (telegramMsg.from.id != telegramMsg.chat.id) return ; - toggleBlocksNotifications(telegramMsg); +bot.onText(new RegExp('^' + botCommands['blocks'] + '$', 'i'), (telegramMsg) => { + if (telegramMsg.from.id != telegramMsg.chat.id) return; + toggleBlocksNotifications(telegramMsg); }); -function toggleBlocksNotifications(telegramMsg) { - var apiRequest = '/get_telegram_notifications?chatId='+telegramMsg.from.id+'&type=blocks'; - apiInterfaces.pool(apiRequest, function(error, response) { - if (error) { - return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); - } - if (response.enabled) { - disableBlocksNotifications(telegramMsg); - } else { - enableBlocksNotifications(telegramMsg); - } - }); +function toggleBlocksNotifications (telegramMsg) { + let apiRequest = '/get_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=blocks'; + apiInterfaces.pool(apiRequest, function (error, response) { + if (error) { + return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); + } + if (response.enabled) { + disableBlocksNotifications(telegramMsg); + } else { + enableBlocksNotifications(telegramMsg); + } + }); } -function enableBlocksNotifications(telegramMsg) { - log('info', logSystem, 'Enable blocks notifications to @%s (%s)', [telegramMsg.from.username, telegramMsg.from.id]); - var apiRequest = '/set_telegram_notifications?chatId='+telegramMsg.from.id+'&type=blocks&action=enable'; - apiInterfaces.pool(apiRequest, function(error, response) { - if (error) { - log('error', logSystem, 'Unable to enable telegram notifications: ' + error); - return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); - } - if (response.status != 'done') { - return bot.sendMessage(telegramMsg.from.id, response.status); - } - return bot.sendMessage(telegramMsg.from.id, 'Blocks notifications enabled'); - }); +function enableBlocksNotifications (telegramMsg) { + log('info', logSystem, 'Enable blocks notifications to @%s (%s)', [telegramMsg.from.username, telegramMsg.from.id]); + let apiRequest = '/set_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=blocks&action=enable'; + apiInterfaces.pool(apiRequest, function (error, response) { + if (error) { + log('error', logSystem, 'Unable to enable telegram notifications: ' + error); + return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); + } + if (response.status != 'done') { + return bot.sendMessage(telegramMsg.from.id, response.status); + } + return bot.sendMessage(telegramMsg.from.id, 'Blocks notifications enabled'); + }); } -function disableBlocksNotifications(telegramMsg) { - log('info', logSystem, 'Disable blocks notifications to @%s (%s)', [telegramMsg.from.username, telegramMsg.from.id]); - var apiRequest = '/set_telegram_notifications?chatId='+telegramMsg.from.id+'&type=blocks&action=disable'; - apiInterfaces.pool(apiRequest, function(error, response) { - if (error) { - log('error', logSystem, 'Unable to disable telegram notifications: ' + error); - return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); - } - if (response.status != 'done') { - return bot.sendMessage(telegramMsg.from.id, response.status); - } - return bot.sendMessage(telegramMsg.from.id, 'Blocks notifications disabled'); - }); +function disableBlocksNotifications (telegramMsg) { + log('info', logSystem, 'Disable blocks notifications to @%s (%s)', [telegramMsg.from.username, telegramMsg.from.id]); + let apiRequest = '/set_telegram_notifications?chatId=' + telegramMsg.from.id + '&type=blocks&action=disable'; + apiInterfaces.pool(apiRequest, function (error, response) { + if (error) { + log('error', logSystem, 'Unable to disable telegram notifications: ' + error); + return bot.sendMessage(telegramMsg.from.id, 'An error occurred. Please retry.'); + } + if (response.status != 'done') { + return bot.sendMessage(telegramMsg.from.id, response.status); + } + return bot.sendMessage(telegramMsg.from.id, 'Blocks notifications disabled'); + }); } diff --git a/lib/utils.js b/lib/utils.js index 651a5e682..73f9999d7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -6,144 +6,184 @@ **/ // Load required module -var crypto = require('crypto'); +let crypto = require('crypto'); -var dateFormat = require('dateformat'); +let dateFormat = require('dateformat'); exports.dateFormat = dateFormat; -var cnUtil = require('cryptoforknote-util'); +let cnUtil = require('cryptoforknote-util'); exports.cnUtil = cnUtil; /** * Generate random instance id **/ -exports.instanceId = function() { - return crypto.randomBytes(4); +exports.instanceId = function () { + return crypto.randomBytes(4); } /** * Validate miner address **/ -var addressBase58Prefix = parseInt(cnUtil.address_decode(Buffer.from(config.poolServer.poolAddress)).toString()); -var integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1; -var subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(config.poolServer.subAddressPrefix) : "N/A"; - +let addressBase58Prefix = parseInt(cnUtil.address_decode(Buffer.from(config.poolServer.poolAddress)) + .toString()); +let integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1; +let subAddressBase58Prefix = config.poolServer.subAddressPrefix ? parseInt(config.poolServer.subAddressPrefix) : "N/A"; // Get address prefix -function getAddressPrefix(address) { - var addressBuffer = Buffer.from(address); +function getAddressPrefix (address) { + let addressBuffer = Buffer.from(address); - var addressPrefix = cnUtil.address_decode(addressBuffer); - if (addressPrefix) addressPrefix = parseInt(addressPrefix.toString()); + let addressPrefix = cnUtil.address_decode(addressBuffer); + if (addressPrefix) addressPrefix = parseInt(addressPrefix.toString()); - if (!addressPrefix) { - addressPrefix = cnUtil.address_decode_integrated(addressBuffer); - if (addressPrefix) addressPrefix = parseInt(addressPrefix.toString()); - } + if (!addressPrefix) { + addressPrefix = cnUtil.address_decode_integrated(addressBuffer); + if (addressPrefix) addressPrefix = parseInt(addressPrefix.toString()); + } - return addressPrefix || null; + return addressPrefix || null; } exports.getAddressPrefix = getAddressPrefix; // Validate miner address -exports.validateMinerAddress = function(address) { - var addressPrefix = getAddressPrefix(address); - if (addressPrefix === addressBase58Prefix) return true; - else if (addressPrefix === integratedAddressBase58Prefix) return true; - else if (addressPrefix === subAddressBase58Prefix) return true; - return false; +exports.validateMinerAddress = function (address) { + let addressPrefix = getAddressPrefix(address); + if (addressPrefix === addressBase58Prefix) return true; + else if (addressPrefix === integratedAddressBase58Prefix) return true; + else if (addressPrefix === subAddressBase58Prefix) return true; + return false; +} + +function characterCount (string, char) { + let re = new RegExp(char, "gi") + let matches = string.match(re) + return matches === null ? 0 : matches.length; +} +exports.characterCount = characterCount + +// Validate miner address +exports.validateChildMinerAddress = (address, index) => { + let childAddressBase58Prefix = parseInt(cnUtil.address_decode(Buffer.from(config.childPools[index].poolAddress)) + .toString()); + let childIntegratedAddressBase58Prefix = config.poolServer.intChildAddressPrefix ? parseInt(config.childPools[index].intAddressPrefix) : childAddressBase58Prefix + 1; + + let addressPrefix = getAddressPrefix(address); + if (addressPrefix === childAddressBase58Prefix) return true; + else if (addressPrefix === childIntegratedAddressBase58Prefix) return true; + return false; } // Return if value is an integrated address -exports.isIntegratedAddress = function(address) { - var addressPrefix = getAddressPrefix(address); - return (addressPrefix === integratedAddressBase58Prefix); +exports.isIntegratedAddress = function (address) { + let addressPrefix = getAddressPrefix(address); + return (addressPrefix === integratedAddressBase58Prefix); +} + +exports.determineRewardData = (value) => { + let calculatedData = { + 'address': value, + 'rewardType': 'prop' + } + if (/^solo:/i.test(value)) { + calculatedData['address'] = value.substr(5) + calculatedData['rewardType'] = 'solo' + return calculatedData + } + if (/^prop:/i.test(value)) { + calculatedData['address'] = value.substr(5) + calculatedData['rewardType'] = 'prop' + return calculatedData + } + return calculatedData } /** * Cleanup special characters (fix for non latin characters) **/ -function cleanupSpecialChars(str) { - str = str.replace(/[ÀÁÂÃÄÅ]/g,"A"); - str = str.replace(/[àáâãäå]/g,"a"); - str = str.replace(/[ÈÉÊË]/g,"E"); - str = str.replace(/[èéêë]/g,"e"); - str = str.replace(/[ÌÎÏ]/g,"I"); - str = str.replace(/[ìîï]/g,"i"); - str = str.replace(/[ÒÔÖ]/g,"O"); - str = str.replace(/[òôö]/g,"o"); - str = str.replace(/[ÙÛÜ]/g,"U"); - str = str.replace(/[ùûü]/g,"u"); - return str.replace(/[^A-Za-z0-9\-\_]/gi,''); +function cleanupSpecialChars (str) { + str = str.replace(/[ÀÁÂÃÄÅ]/g, "A"); + str = str.replace(/[àáâãäå]/g, "a"); + str = str.replace(/[ÈÉÊË]/g, "E"); + str = str.replace(/[èéêë]/g, "e"); + str = str.replace(/[ÌÎÏ]/g, "I"); + str = str.replace(/[ìîï]/g, "i"); + str = str.replace(/[ÒÔÖ]/g, "O"); + str = str.replace(/[òôö]/g, "o"); + str = str.replace(/[ÙÛÜ]/g, "U"); + str = str.replace(/[ùûü]/g, "u"); + return str.replace(/[^A-Za-z0-9\-\_+]/gi, ''); } exports.cleanupSpecialChars = cleanupSpecialChars; /** * Get readable hashrate **/ -exports.getReadableHashRate = function(hashrate){ - var i = 0; - var byteUnits = [' H', ' KH', ' MH', ' GH', ' TH', ' PH' ]; - while (hashrate > 1000){ - hashrate = hashrate / 1000; - i++; - } - return hashrate.toFixed(2) + byteUnits[i] + '/sec'; +exports.getReadableHashRate = function (hashrate) { + let i = 0; + let byteUnits = [' H', ' KH', ' MH', ' GH', ' TH', ' PH']; + while (hashrate > 1000) { + hashrate = hashrate / 1000; + i++; + } + return hashrate.toFixed(2) + byteUnits[i] + '/sec'; } - + /** * Get readable coins **/ -exports.getReadableCoins = function(coins, digits, withoutSymbol){ - var coinDecimalPlaces = config.coinDecimalPlaces || config.coinUnits.toString().length - 1; - var amount = (parseInt(coins || 0) / config.coinUnits).toFixed(digits || coinDecimalPlaces); - return amount + (withoutSymbol ? '' : (' ' + config.symbol)); +exports.getReadableCoins = function (coins, digits, withoutSymbol) { + let coinDecimalPlaces = config.coinDecimalPlaces || config.coinUnits.toString() + .length - 1; + let amount = (parseInt(coins || 0) / config.coinUnits) + .toFixed(digits || coinDecimalPlaces); + return amount + (withoutSymbol ? '' : (' ' + config.symbol)); } /** * Generate unique id **/ -exports.uid = function(){ - var min = 100000000000000; - var max = 999999999999999; - var id = Math.floor(Math.random() * (max - min + 1)) + min; - return id.toString(); +exports.uid = function () { + let min = 100000000000000; + let max = 999999999999999; + let id = Math.floor(Math.random() * (max - min + 1)) + min; + return id.toString(); }; /** * Ring buffer **/ -exports.ringBuffer = function(maxSize){ - var data = []; - var cursor = 0; - var isFull = false; - - return { - append: function(x){ - if (isFull){ - data[cursor] = x; - cursor = (cursor + 1) % maxSize; - } - else{ - data.push(x); - cursor++; - if (data.length === maxSize){ - cursor = 0; - isFull = true; - } - } - }, - avg: function(plusOne){ - var sum = data.reduce(function(a, b){ return a + b }, plusOne || 0); - return sum / ((isFull ? maxSize : cursor) + (plusOne ? 1 : 0)); - }, - size: function(){ - return isFull ? maxSize : cursor; - }, - clear: function(){ - data = []; - cursor = 0; - isFull = false; - } - }; +exports.ringBuffer = function (maxSize) { + let data = []; + let cursor = 0; + let isFull = false; + + return { + append: function (x) { + if (isFull) { + data[cursor] = x; + cursor = (cursor + 1) % maxSize; + } else { + data.push(x); + cursor++; + if (data.length === maxSize) { + cursor = 0; + isFull = true; + } + } + }, + avg: function (plusOne) { + let sum = data.reduce(function (a, b) { + return a + b + }, plusOne || 0); + return sum / ((isFull ? maxSize : cursor) + (plusOne ? 1 : 0)); + }, + size: function () { + return isFull ? maxSize : cursor; + }, + clear: function () { + data = []; + cursor = 0; + isFull = false; + } + }; }; From 8e8f7e5dbb21207b18300ffef10ec040e715273f Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Sun, 25 Oct 2020 19:51:15 +0100 Subject: [PATCH 96/98] Add files via upload --- website_example/admin.html | 261 +- website_example/browserconfig.xml | 9 + website_example/config.js | 10 +- website_example/favicon.ico | Bin 0 -> 143884 bytes website_example/index.html | 520 +-- website_example/js/common.js | 2943 +++++++++++++++-- website_example/js/custom.js | 2 +- website_example/lang/ca.json | 312 +- website_example/lang/en.json | 314 +- website_example/lang/es.json | 316 +- website_example/lang/fr.json | 316 +- website_example/lang/it.json | 314 +- website_example/lang/ko.json | 312 +- website_example/lang/languages.js | 11 +- website_example/lang/ru.json | 345 +- .../lang/timeago/jquery.timeago.af.js | 54 +- .../lang/timeago/jquery.timeago.am.js | 54 +- .../lang/timeago/jquery.timeago.ar.js | 105 +- .../lang/timeago/jquery.timeago.az.js | 54 +- .../lang/timeago/jquery.timeago.bg.js | 50 +- .../lang/timeago/jquery.timeago.bs.js | 102 +- .../lang/timeago/jquery.timeago.ca.js | 54 +- .../lang/timeago/jquery.timeago.cs.js | 84 +- .../lang/timeago/jquery.timeago.cy.js | 54 +- .../lang/timeago/jquery.timeago.da.js | 50 +- .../lang/timeago/jquery.timeago.de.js | 50 +- .../lang/timeago/jquery.timeago.dv.js | 58 +- .../lang/timeago/jquery.timeago.el.js | 50 +- .../lang/timeago/jquery.timeago.en.js | 54 +- .../lang/timeago/jquery.timeago.es.js | 51 +- .../lang/timeago/jquery.timeago.et.js | 70 +- .../lang/timeago/jquery.timeago.eu.js | 49 +- .../lang/timeago/jquery.timeago.fa.js | 58 +- .../lang/timeago/jquery.timeago.fi.js | 70 +- .../lang/timeago/jquery.timeago.fr.js | 48 +- .../lang/timeago/jquery.timeago.gl.js | 50 +- .../lang/timeago/jquery.timeago.he.js | 54 +- .../lang/timeago/jquery.timeago.hr.js | 100 +- .../lang/timeago/jquery.timeago.hu.js | 50 +- .../lang/timeago/jquery.timeago.hy.js | 50 +- .../lang/timeago/jquery.timeago.id.js | 51 +- .../lang/timeago/jquery.timeago.is.js | 52 +- .../lang/timeago/jquery.timeago.it.js | 46 +- .../lang/timeago/jquery.timeago.ja.js | 52 +- .../lang/timeago/jquery.timeago.jv.js | 50 +- .../lang/timeago/jquery.timeago.ko.js | 55 +- .../lang/timeago/jquery.timeago.ky.js | 86 +- .../lang/timeago/jquery.timeago.lt.js | 54 +- .../lang/timeago/jquery.timeago.lv.js | 54 +- .../lang/timeago/jquery.timeago.mk.js | 54 +- .../lang/timeago/jquery.timeago.nl.js | 54 +- .../lang/timeago/jquery.timeago.no.js | 50 +- .../lang/timeago/jquery.timeago.pl.js | 78 +- .../lang/timeago/jquery.timeago.pt-br.js | 50 +- .../lang/timeago/jquery.timeago.pt.js | 46 +- .../lang/timeago/jquery.timeago.ro.js | 51 +- .../lang/timeago/jquery.timeago.rs.js | 100 +- .../lang/timeago/jquery.timeago.ru.js | 88 +- .../lang/timeago/jquery.timeago.rw.js | 54 +- .../lang/timeago/jquery.timeago.si.js | 50 +- .../lang/timeago/jquery.timeago.sk.js | 84 +- .../lang/timeago/jquery.timeago.sl.js | 84 +- .../lang/timeago/jquery.timeago.sq.js | 46 +- .../lang/timeago/jquery.timeago.sr.js | 100 +- .../lang/timeago/jquery.timeago.sv.js | 50 +- .../lang/timeago/jquery.timeago.th.js | 54 +- .../lang/timeago/jquery.timeago.tr.js | 46 +- .../lang/timeago/jquery.timeago.uk.js | 86 +- .../lang/timeago/jquery.timeago.ur.js | 54 +- .../lang/timeago/jquery.timeago.uz.js | 62 +- .../lang/timeago/jquery.timeago.vi.js | 54 +- .../lang/timeago/jquery.timeago.zh-CN.js | 55 +- .../lang/timeago/jquery.timeago.zh-TW.js | 54 +- website_example/lang/zh-CN.json | 315 +- website_example/pages/admin/monitoring.html | 210 +- website_example/pages/admin/ports.html | 139 +- website_example/pages/admin/statistics.html | 337 +- website_example/pages/admin/tools.html | 237 +- website_example/pages/admin/userslist.html | 237 +- website_example/pages/faq.html | 210 +- website_example/pages/getting_started.html | 843 ++--- website_example/pages/home.html | 661 ++-- website_example/pages/market.html | 485 +-- website_example/pages/payments.html | 323 +- website_example/pages/pool_blocks.html | 434 +-- website_example/pages/settings.html | 318 +- website_example/pages/top10miners.html | 124 +- website_example/pages/worker_stats.html | 804 ++--- website_example/safari-pinned-tab.svg | 15 + website_example/site.webmanifest | 19 + website_example/themes/admin.css | 139 +- website_example/themes/blackout.css | 1363 ++++++++ website_example/themes/custom.css | 152 + website_example/themes/default.css | 1711 ++++++---- website_example/themes/golden.css | 1481 +++++++++ 95 files changed, 12696 insertions(+), 7373 deletions(-) create mode 100644 website_example/browserconfig.xml create mode 100644 website_example/favicon.ico create mode 100644 website_example/safari-pinned-tab.svg create mode 100644 website_example/site.webmanifest create mode 100644 website_example/themes/blackout.css create mode 100644 website_example/themes/golden.css diff --git a/website_example/admin.html b/website_example/admin.html index 62366d195..48609fb2e 100644 --- a/website_example/admin.html +++ b/website_example/admin.html @@ -1,117 +1,164 @@ + - - + + - Mining Pool Admin Panel + Arqma with PLE or XCY or TRTL Mining Pool - Admin Panel - - - - - - + + + + + + + - - - - - - - - - + + + + + + + + + - \ No newline at end of file + + diff --git a/website_example/browserconfig.xml b/website_example/browserconfig.xml new file mode 100644 index 000000000..b3930d0f0 --- /dev/null +++ b/website_example/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/website_example/config.js b/website_example/config.js index f89ad4853..bcabfabc1 100644 --- a/website_example/config.js +++ b/website_example/config.js @@ -1,13 +1,17 @@ -var api = "http://poolhost:8117"; +var api = "https://multi-miner.smartcoinpool.net:8119"; +let parentCoin = "Arqma" var email = "support@poolhost.com"; var telegram = "https://t.me/YourPool"; var discord = "https://discordapp.com/invite/YourPool"; +var facebook = "https://www.facebook.com/"; -var marketCurrencies = ["{symbol}-BTC", "{symbol}-USD", "{symbol}-EUR", "{symbol}-CAD"]; +var marketCurrencies = ["{symbol}-BTC", "{symbol}-LTC", "{symbol}-DOGE", "{symbol}-USDT", "{symbol}-USD", "{symbol}-EUR", "{symbol}-CAD"]; var blockchainExplorer = "http://chainradar.com/{symbol}/block/{id}"; +var blockchainExplorerMerged = "http://explorer.ird.cash/?hash={id}#block"; var transactionExplorer = "http://chainradar.com/{symbol}/transaction/{id}"; +var transactionExplorerMerged = "http://explorer.ird.cash/?hash={id}#transaction"; var themeCss = "themes/default.css"; -var defaultLang = 'en'; \ No newline at end of file +var defaultLang = 'en'; diff --git a/website_example/favicon.ico b/website_example/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2f03f239a51d528760d6343319b6319818020a98 GIT binary patch literal 143884 zcmV)kK%l<>00965000000096X08aA&07d`+0Dyo10096X04N9n0F?6p05C8B0096X z0H`GZ0KEGF03aX$0096X0H_cE0AwKo0EtjeM-2)Z3IG5A4M|8uQUCw}0000100;&E z003NasAd2FAOJ~3K~#90?45U`Whf+L1VFX=BkKzcl(+JoheGt)?5`6U788awK7yRp`-80$p z-P6g$$b@4JO4)Yrv}1KkTC&0tQVJmuXj6ayfd~r~Vd08m>MTuKc}GBdcWDjUD?@8d zL2K2VcQ>{d+;uIkyP_@cE^f``W;Jcv{Kl<^?fDwfB03@ky7+r%+K#C+THnb4K6=^` zgzt*bKj?2xy8Z7%d-fl&Po^w0J{@;PWMZ*CWih+EC504#6o$BkPv|02nD<>|iOeX7k1RrvR*g z*ab2o*`vfSy|>6nwR{Gk>G-XD!tE?6FF&Bv$ZgsueE)&~r461?+P}k;VHk*}-BryS z+ubFtg~B_{`NB)h&CO5VI%Vte?o=}m9?V64`<=_##Fh}Y2vG9mn2!cGzqwtU!NOSbQsZsMaLWI;+wdq~D zHM~Ya#>v|m+GAB@nwOxFC}Cq4HZy4H9ibi&v{Zn!5ti+}Ot7;0KsKiPGq1^R5V90^;=hryfyVTYE@0mE5B;l^hbYEk|mPzJ#PAznY_FeFZ1CG4eJ6mP0O8 zOHCpby-Ufz#Uv!c ziIbOcw6w6K#sX88DuA?jb;$Qvp4|(jLpy_yCX}Z?D6PHg=IAPwYs?qsHs{@!nsd1) zer5gYv2Cy1XE*I+01*aYPq_6Xm-lfeQQXKaLHf=JUIOy?KRht0d;fu7sLW<2SEZ7} zY)jg^w9IBr*=}6;<`Li)wprL;)s2d_mBe(8w7UsmClSIj z#OfXKj~;{A`lJmC@P1k=-dVAhC*~}pJzqe%c^sjTxb)9ThS$W2^_{@Fp%+k>7-)#y zLj<5v3bZhYAW#nkxJny@pfx$A^P6&oSxp7^*`|Eq;aesSd0|^>{8>tc*DnS>eFm^| z8nBDB4TK*hb^%y`oN(d)bQ^i}iKkXoRUA~AN{x*>c9}#VrQnjcj{IPv2ap!n36KtN z)xkHOnu}Z3n{V#lgQKhR*lI1rY|Lj&vF${@r6fG&VFe8z)4xAs`u8un&oGMR4UOC} zuZ3iK6BBCZ;1o8~F4M#cjpUpxjfwuWSy{9&DoJ2Rkj91}v?eCBQyF*0mB;LH?XL6X zt7k5Kt*PKX(Oyt@-ZE*(`j4w+J5!i;B7ja(g!s3X@FgDMA9LINd)E#ac;fi@>Vs9f zd(VxrUhGx>Fq2pQ8QlB_zubx;66J_wHKe*B>=Z5S1yWADlZt+{c7C`5Iv+1NSJ6Nj zwN|USN9ECOE8rq*0p!7LWkuQUJXiB&PwJb}6dKp#2reNiD0s+VbH1=(bKafST5#{Z zb>ficKXh|Ac*SWl>9OBqN1z zst`_+eSYv;M)d7P&&o3DoLTI9rZCfno?%dUy{feUtq~Fm%5<&(vW`78ZCOKGU3Ky| zUw`jEEqV8WTP6*;e>*ksbCq^70Ja+LmlD1OSYN#V>2toEe(tN!)eh}xSu$3$YCV)y zvT#E?De3a^f_h$ke-l63zq0rhb`p{9j?C_YDDRDkWy~e5d1?Ma&i>tfq%u|f`D+)@ z(6oxmWG4uqsJb8S^2eHd{v<7gljM=N7BYU=5YE5;Kdf85h)wmY*=^9+U?xLo1OJr< zOL&Gt{22}q1|EPAXcTc|bY8KD&E2=xxb=^U{p%kh=VTaj`vTeun#vx7IBuY1c<)}M_dFS0xYrPU zyb_V>4v8v6s*;Tb$&C*^%Y84t6;xu_9!K%rKQ3TI%WF8xo}s)VR#L^z8nUeo-^Tl+ zX-g{*1PD8ZNThkRz6Uq`X)2dpcNyu-9^Q1MK|X9R@d|0{u`dIrah>C67@5215yP-p|!^&D{`g#A$n z{*&Mj!PpYKy=(=CU3w3okwTz_4xE^jHs=bB5W-9gdM-8YUpo zSkf2{HAm>?nj?9_saevTTT_k%=;% zdUqae!%t?=m_wMg;Ge8q{swWKXZPOSs7WVC$72z~-AW4HT0$4We;+dGj%t7iudQy@ zbn7|5r~|%}lJ}StW<7%RkN_g^R}B6Z3Z(E>D0q)r!gv=cJog3-Qu%zsKnz>TSSH5N z30qD+|Hb)7x4Y`rJNFwt<0CZmzdn6#7{E44eQV@-U+XsVxKqB8&18>HC*u8A)vw_F zW;iOlh?fwY|MUMa@!CVEZ_Wkpxa{jcrMqZiaq}F8)K+*C-5#Y#8)6AoG_`Z?&;CMY z*kSB_{60to>l1@m;11=Kel9&K^2B{{C@kaVr+ufK2-STc{chogQV0JJDsOAGL}`JP z95$qerwes7Io-UXjlmD!@{a%!aT^u}5lGKIUIJRbW3S2uo#r{}F>_^3U3go9|dDmcTQ#+$K;?CZWmm25LoOL`QnjA;wY zLP(n>8yY$Nj_3KpSN=e@x`+32fhA#V+pFv)=AqkLLFMt?f033c$1Q+%(Qd(;5>zG4 z>a6h}nDH^B$oR2rn(~lIrO1gG|E@lZR+$P`F8Vcyt_?Q+07gwv5q)1_`~j4=uAorl z8{oSXhqeG?A5g(F(Z8GW?tfYfg+JZ3|A@P`LkE1Qw4>Mn{%fNG3orS9vIS}H{p?9M z+%o)>f6n;L?|b~?e@ENPs(1a#^DJMnlq(*dMMGwWLf=2UR5(lxZjGZf-kL^6wrB?#PmebOpwp@+Q# zvwVQXx5F>;E~UBVk>`1R=_=xhI8O62(@qTZmzc#(W_6>tvQbE+@UMH*gWkPnF_bh2 z!p|4sIbOb$;k}lW!F^Inq?Du`YyWJ_`QNjjn|JF;(`F6(xX0ROIsI26fNk{TvjBP2 zJx`xAu3P@%TBm*2n}7WsS{v7J>D3}g*)oio#6(niEm{K7d03AAopO-1!C*p~D-k3>qaJ-t>6uY%tCUCXz2 zc`ewueg$vOc#IM4uX4#jlc3#W0>L-jN<#lQ7(o}qMmTX^W)$tMo7vobUqq%y05gTp z1#}o-MB%#w6SxQa5MF~f{TD)hL*P1K5K%@@4FAn}L8;(x*E`R-?xvQ4drxcL{rNu* z9<}6S=#6boI|>8%uTcTE0`3P;-~S!_m&Z=%J88e0%d_QQLdwj_H7oe>z<0T+=^Hrd z45F->^Z!1dQ+sV-##w{7{_NAZd|DkMmPDj05a|kfBwecGnthxlj7T(+NTrbJ3S_bz zk<9Y)d&@zBkdpH1J_h!K2Um^98v+~;#@bS1NfWfs@(_Y0O@*tw^b5MI78YorlU6kQXhkF#ZteR0~c|9axIcTf6|J@H?W{wrny zIw5}OxGq0n1R{~Lj`-WZuDUJ$+y7b5F4p7vDC((UX?q!S-+Yhe^~+IiflCe;jC1Hu zI4u1pEv?;Iv1T=szkD+n+nl`UIebv5X&h#CiPVZ`V;M%;JPG1C3#@ z(~jBN4%~`+NVtTcv7G(y_sq)oGx;oMvAW&gvg8MnvS$Yb7AfG~uuf}%}q0cDyV z`UwzDf(QZ9a~P5WTuoqG0D>SS;4uV$%2Atgeft7}U}hn1$^C?|2c0zS-H|V>Zn$pE znfrb20+M{DRe%q0<1KaOk$^SfN59|g@==X9^^CWS-%q@Sh^P2&-C>+wyNvJ5FxGk@ zEG$d%{_}rjzi&Ox=06|9Mf;DTnIyvo?Mguie$jLQxuy-g{=hZtJ8m4qD=ntC52kw( z(zd2gRhHpBD(EZgsFU4zE4~M=l`zs>fbAmygs}w>4rV-w=}(;rj{*c9+V|9gNALX! zw;q^8=bE;$*FWI988!U_l0UsEnYD5~qx$y)C&{hTUZ-rr1#}y@2Vez}jI>AmBGCWC zSNKp~YeU#Uu#h5)zsWDCP&EkeiNgNS1`yBQ zhmCo68$0tur5!~D_{=hZE)0EJT(S4fKmYQ))4DCZYE)uPC0eoT4Q6HO9vA(U_om&- zxG((%DI^ECJRczFPdH?8{gf;&-G7kh6u+`F&_CJUv zWj`fF>9k;^TegScujn;`rH$>ZY}iO$n`S|LJOjsmfwD{mQrck*!_W~Ra0ON#WAK*+ zn`u6RJfFY=TR@RvkRk%t5!*mC&$j_6Wu_4QsX>#c0%qVUMVqUhZ_VX?{pjK2{;{2V zZHHk1pGg&98>jay!209gul>z|_NyoKiM2YrUT+Bh(W}njKZz;Kl*1Ww_HP-Je~Sxq z4qYx1WdQ`S%D+Tt5q==SvUfB7EbF2t4Ny8mU2f_ZzP{Xvq~w zC1mH$Jz{_6SUSFe*^N zw{?2V&A1Ai=UuG>`+)FViShgfq%f-qy>1Hvq-wxLBc(7tR1z#@;6GBJl|p;=72{SU ziNU@i7VCM;Q#1QMcKG<;6t(@MNXi{JedZXz2N1s%Iog1g>aj~=&!7GIs^hbNJ~^YB z#jZbjkpsT>3}?07&oE~ZSI=HW)ovrX(0zd1=FN!8o?yq=(A>&Chkc2Cc?yv%-ik$0Ga5Z*JhIFPi<9k>0NN?bBIZ^4kbyHen{vF|(0)GS?B z$J_?Z5yxGPoy>ao8sZ9GFTxdohL-$L=EfUP;<@?i;(ZHzx(hzEFzpTuhFS-9g3>yA zN>gz3IfU?;i|28ub!c$3q!^rXV%Hq~#G5_WzA)|kuU-GGJQS;&|4q!`GsOV9ApTOR zZ+l1Q`2RV7*v1K`ekD^`ebU66W>9F|$dA8qB`2;skDQ%m=#6vP=jv;bT2W|fWOC0G zXb0gWd23A_OGPVZJorymzdxS?zyBPuL>WTB(B^4in?Y;|Ni6W)jaD}Jujst2P|xU% z|EATcqJQh#yjpuUU zy4-uU0b|}c{BK|cpP6aE4hAoi>|abhZxSb8I~6OD_P#z83T(3r?(K(wvzb$U+nlF|M0N$`@eU?e)@aes4pgjk?{?n zA?FH?&rM@md|x(-40E&laP|M)!Nv_M$xpb1o5!_~$*%%wafqJ5{TvCHX{A(IIU?;q zz76v2p5z4~q_OCQHAidL!~)cc&PHp}-N?Qxf6uJiFS04o2Z0O-+}Hv_xa`{owD8N; zoPE&V^mJF!lRSNtm zNIz(aAa2>mQkKc=ckjQif9|v?&k=H10R34<13Nqe*aqTvfY<>)`uWjU4Se;dqt*OO zSMl#fEJM6vFh{1AF-W@n#XSH$XiqjJvsku6RnMXHvHyy8C1{1NTL8U|#9zU7=Zrbb zUbukEkJy_mIr8~-`qy?No=hNON%In=Xjck1mm^_m<2hJViO?z@*Ue0*e~=m7zC@eT zEtGH&%(Ms}enf~s3YUJVRxF(*2{bmWr#$YPz&dUD)^>a6No#BgryY9+@nnWpk}RI{ z0=c>Gp=1o1$swH$h-}I;01`5-h>q^JJk-2>b--Kc7v&J13J_Ew=o1|<6XPSHz1u^h zp}9Y_E^6)h00^VH1pL9*H-f22)A{~%rES|&lId(}-+QM0mQ!R;7e~{@TE6! z!Y}{Im@EI6oB#bL!byVd@2CUeLLIM;LYkFy4J*TndITgq1DSCW^4AU+L{M;D9(!Xh zb63?f_$xP1kxo%eUXfq$Zo)cj^A9T03C5MJXH0Av6BCO#W3K_o>@GaASTk*@qM_A| zr~rnji$9Ds4nO#o;&H$$*Pa#uf#x z6Au82sd_wvzy5O;{^UYHP!SVMQnPtYj^JeR0ypk{3PMQszTg((?Z07Ab&^3fgE@X; z2658e92a|?k@hMc$?wI&*)OrSX$`fR6guA)iS8?kc)$~H&SKq&L+KZ^KRo*L=w|SH zk7`iDV*0u&UR}1ByB~ZXz5HjzQJ-`{GE!> z0)_Dlh||uVo1bGZdnE~6a!B4Ql(TE&baZgqkC83xOuP4 zPz4achMeGyA>StkNGY)efiX}DiS!97LI?`T=iAwB)h)!OCgwKL5&ny!!&W8V(r?8; zIv{QDXYqbrBXgFoVfs7sX)Z|eJxEdEl&-_Y`%{|R7x(>YSc9ipIpwNPD zT!hYTc2BBaRgJ}o=MVaq-RQaA-+YR>jP9LVXw7+Vp;kAv?g|;Aj=~DJJI-n zp2QO%l*_P<|0Kg^Lj^w6@6fT^^cBEzP)>@5HqHC%8(6fuj+J!{tZLXuyUZZU`Z0Lo z#bmNo3>&_uk&?oL+a@fJnYz?In+qcPm25$-Ba%OA0E|xi>5`)v$bK`m|E%^=LC93h za%0VYg_oGp{*p1o5>yJiXqb5h1v`bP7y^xp$t<7F!94{ltRBo}h+MYP$B($?hsT^fbjkN_w)U*iT5zOz znI>XvXwI=@!)C61c0TpVe%$fhtC;AlAZiqBDA>5xdMf2@*nJe>fYvm(=XkHKkx{+7 z6Rbjs8j=cmuF!Xwop~(>5D41_C&kp|n{oO(-U=QVS(uGTXIR0 zlpA4jeiJq2xmAp=yPx%TZyL&Xr!CQoR;R`^-LrW4CO)k|7B*2;ct4n)EXL5=(A~aG zLI_NB<0Oy0xsXTSSVDPKADr^ul=s+!fg^fQ)vXsjYx@&Vq`-sWVG9W6^&^!EAbLRJ zDntja4w(ZwLOnXt3Qwknn92X!GZ1=+K`EaB7*9W?HDlFWrns*a6MrH;t1SQkAOJ~3 zK~$vaMTTPk+)f}W263F4M}FgQbJQyL)>Z3WQ~kN>o%{Iqr84=4M{{#ijG6zdu`J58>KI9x=LaurW_g`sv?GE}M_H9P&ei&0${+eC0CH1gugtX1rRGxYuB*Ko<==5S~^%uz5mBs^* zi0-SQb>lL|H9bU1w|dim(ROT&{_jD3I|jl)e5B3#rW|*@-OPZo#}kVssjBLUw2YIF z*{4H*_SWlZZ`Gpj_~TjIeR7Si_|t@_!1w=mz2^$v(O>)TMy~sQ{wi`^X#%M4YTwV_ z1QN+(eE;)GvB&CrnUY#TOhg2;j-2SCjF1Uvz->o2%tN;|fDp`d#_+?$|DcreR07Dg zx4*J+#nKdp`*ZLmw^Np_ z@sqO+{Q~Kl zp=MDKzVXcXzc+7MO=0FQIB-aJaC05ovZW-X(eYztEUnv&v>leMt*15b^8CEzj6du< z^zAnyP*ePFRssAL*s6){EpKAEZPX`*vMJs_w7_eyRSWS#J#n{@y7+Lkv^;_~^+)I; zf`*8W??L$}Bk=73q0a9eG3SLq?^hID$ZN&!@BN-bYnq8$kw*9_qCP}2ikU#68P%`= z+Rb+DT3Q^P{G(aD=*1~)Yj1sa%9no47e^)`*Al1+T{Jn8B-n98Jj=@qSMv8)mmmeS zTUE4che?x;X17uMU|HeLz}lDQTH*WQ#~WudBL5sZ*FvL8(NJ|Dc`HjSzk#@0kKMM0 zvfLVyqMbGA@hr|Bfxn1Hd2{}O;vA9mr3@fy98}tDAR4Uh^O=G6q`xQm3$A9YdYdUN z&yW_r(UoH24pM{(bjBodn^2AO&7KW_Rxa<^1Nf?awO2PquBGL%wJ%Kl?y^7rd^sJN zK&NOR|Jp_R^UeUaMEp_79|z*GY(@IM-yHM65%!xy(51c@Ub14y%3UE*)y1nmB=K7g z!jAFo@-?h&g@XoGkXJ6N?7lqi973I}!4YO{e+-vitm9<&QKT}_d<{g`oxDTPB$C{= ztRD~EeFHDw_-*ca_9bSv59PQMFAt|aq_L)3VBB0aXswv@%Ke-?U>$L{*}S9EPg0U; z#Srl0ulKe=1LVdi$bxp5%ybi%H9NRKAsV zR}z=TzaC1O8%r~{`~>Q(zJY^{^2&A55!E0FOeZGFSkle#1kx~Cx+D*2}K0H-<6Vx=k& z=^998!H#ugci#KTXyKbf-sc-X{SSlBy^_LwdG2le zs5~8+3#du=oLe{1CT&@y1QOg10thnZ3zEl-DA&-4L+^Psgre zsJ)g~meet$@6Bfoxa!7lW6-KYxG=3V$DtA(^V3P6CIS#8zf#yABSQSw{Q9duxVUo0 z3FV@-Ym-)4Z$$Y3iuhX87S_SVfR3Lbko*$i#KB2GEX8y4moaly6KC(yp9Z`{J6y_>;}Y|kB4HG(9;ijk8kzW&fMF1qMv z0S$!Zil7^OI`H#UWVff`~t#=KT(ySpg^H0XmAzey-2`$rN}GDyPAFT)0td&4Ldp`SETQIMthDF_FV%+f9$e(=CE9QLEz z_}`~q;h%5LXHESE_LQ@j_uDH;?DK5~9QRB1zW9D#RAZR4b`!+P&0c~|(ggFg99$WP zSPEChsBc(D;KZtp#6_3Pi5IoW5XnZe#&9TPaXU)8+_EXyDQ2%~42WMw8K37#7p_J$ zd1eP+Z-2i?e+o1Q9dtUIoW87Tb`kL`B9=kK%FHjGMI@_eEX26#&L_k9dHje##tNW< z`j*l{G3qgDg5N(Ht(8$BJSHG@p1s`H7}fd)j?hJ`4udiyNC?Xi|Ko2h2>iFeEF$Xu6%4hSO04PD|HP@E1G&Az>xNNR-&f+mw$TmrC-O4-x#4# zi=u%Y0ttK?2%s~)zcZab0VJyq{o?QkPig}>@l|S8 zOl4&L4c;34i@+CN2zd1O=X3vs`%+V02FgX}o0+?08ULKQh+`&>W!Qj$JlwPg@yfy2 zQeq3U{MQm9hzxAdys5MH4Srg%XyNOeST(~S3{~i&(ULap@X%|Ed2DtI-}&wx0Uh}M z?xFj!h)91Aa_exC+t=Ow&|Qn>Os8@Fqx7kaF|1Ed2K4Msp-^Dyx_TaZ{#71)X&x7U z{T2oe*&P!M)JR|zMFCpl3g2I!AoQ2-4d^P*+OH#ezcvOx*M%Nx1*6*EWKdxNDd`sL z{P?`z|67=;PbbZsRqMHYdJ_*FQ%jbX@R=gSK$s`su8q60IG5&`*~@9kyBxn;H@>6) z4267f&&%z7H=j0bz)5TGy?Gvx!yth?9nruih6HvLBVboLf8g4SA^sGWmHzu}r`&f` z{GI+=()(rQP)OF4ydpdmMcJa`g{Y{I#)I(hZ!h8ImHp^7@eC5Nm`9X~h9z%MH+MQG z?N-k2eQUTcKZWv&ZVY!;vC!?!!A%cR*}9Zf)%%l|advm#U~24W*2-P@^Y;&AQ~fGz z%i_N8T)@~q)vRk)T>J1VEL`|5=U;gbneIcdq`>hR07eb)7X*R7nPw-xJAdFf8Ws*^6eNVkoSI3wV=zX!jC)c2n-+; zM;-P4BFPV=Zv5@}zx+zoEBh05;wde#thX`zb)nyH8}VC+co}PRk~9C+NRIQ0AT`ZXfWZi1qgrsTnOHM>Ke}5 zs~VkeG6S)wJ0!x%@Z6#etjZq6uw5sF#O}j=QH%mWM;6?a5I&z3cF&(IFYw^EA&%f&n++yZuZcLLPDLPCxpo8h zyu83f`Ber6N})sj8clDtjFa>C&_l052#ZrM{|og=~3^SLfo$ zH~t|Qn_$;Pl9g0V-(aJD}QF<1Kw>`p8du6zUx74oV7v#@` zST;93{vy9!+?}cezQL<+&jekF-b~Th@W)3{o?`!OJ(uh^hIZ0a#oWkOBkXtOa(MFH zDW_b0*_jx@=Gz3KGX$TiJ>ZjK0HwrVD*dDIKc1d&K);K3U420xdsEl-lMbS+w^^+g zyhTJ#OZrfh+!MVnO~`Lj6^!`W&lq~%Jy=d0e<7$u2kYCVHy|>VJ;~M%MhcC}>_T?b zi6qr#vI)B*<$4A-w9A<0XQ*H!8pS~;UB$J3T*UrIU2LSKzr5AwQRt9sd-L8QVQKt1 zdZ7i`o}-y~`SrArF)cbJ*|55rWJwV4Gk5ljsbrZWW<|j~!1h#wnBp84wIq%BV zHx48&_`!u29+aFrJTRzqmYg&q-J`hZ(k?6Iqn-Ko`#I9$wr5^pw<})6vK%AvgT4BE zg7;$hi{kCAeMaDjJd>?=4V)Au^@>QMgbjtZjlrc1jf`n}l(B778QVV1gsjqG*lVpp zd4wqpZAF@6xP|me%VaD0WaY2+Gtz(l z%5`jL&KsRx%92smi6r;QO657b+ai;1Nr)v7PQoign4=Z*&{NEF4vLmtaw0up6id^A zq54OqI50kEF;}NnLi@={;w1f5Zt3xfxk75HgWm!3~rcs{DNpGF6{;E^uttn09ZqR zcNybTt2?&76ba}83ANcXqG<*;Jxi_J04OS2-VMTK1&}%T%QJB6P%SV7tAkKiK2zF# z!lB&zz;d2_KgF+~TtwdyhnsT!rgw0K7Qys5L0SHLg~uGc`}})+Rl#eT315as1KI~F zND*1{uYKkcZuzZ!n6H&5>UD5^>7sX-Ie&qH|3tO~QyROXFE1_KM9zmQoypA(h`^km znVn_oq7(%ugGg5)5?O?kG{0C95l9_dY|bt{RjJ>fJ?7dkz07%h@LGXrH*L4HJrO`x&|lKg-8XUSx6V6#N^;(?Em{2$ zmVy4l>UgP$$h&}IMc2Xy(vC%q*i4FCU`-Yhf0GDfD8v7SRN;rf(pocXo#M&278S$M zE{suCPCHe4J>@hI^BwDjAnri#+^k^E+?SAn@ywH!!T+8lmRf|U7qj_=X{PXN84zs9 z4CrL)&v(ixCQL)fVMq@@7GIF(6MMidB7H2vegY8MX9{@BX+f$yG1^c{lT;hoMJ?f_ zw_Zn8j%5E6zY~V!!YsjcwK92CyVB&w3{;OLAf7?R%Tck6Q4LCsfI@ERPs1-3T=5GB7T@dwgB-fw8l*@M@fS#7n7E%7^%>gFx+ z^+!{rUpzP*h&ccCQQg$HR7Hq5sHMvDmBdtD)Von9s;Q^So5u4N#E4>o(b`8G9x>LA zW%lB?IlL5^1d>0}3(@NlF`95JWaB%yi9L`?ft0wO)Ap-;V`Yq1KUgSFyqF z!-%%2B;8Fs8yiRWZi5lFV+@0yhT!`eXrL`#%ZiPH-QyJ$Tt$6z8!yaWNOMawXYM@= zRGz@ANC47AZaVR2f5AEQc0iN03iKPiE4hKc=e&w(^pqQMM1eA^&1*rTL?%bnfPk}l zeHzO`vKzW(O^E!8wm*Vut5uvmWbIiG_Z#{|`?3XHtr74SU5MhBe*9^BF@RF;M6!FN)bJIZv&Rw+-4zdwbn{;EW$Y#1M+m`ky7ANe z8Q$oZfppVaBb0%(3QddxN(+1UCO{(Id!yHlgHQ2*65L z@!VXSthJms7cJv2&&{W5=)MT2k~8g;iNLH{Lym1Z{O*aDXq@yTY$t)zno-Ug&g!<1 zN7htPt~XH?7oa>j6j((=BJXtkMEJN!c1O3>hXGpfv10h}fo03fueof}x4(16g6|MI z1o_^9QAQ9-hvM^(Ep2ZVpey1Jq<{8+V@DoRzId`(CAWpb5;b6_d@LON0=XD9>_rWJ zeog#q{!vi!VSU(ap6|mtejooWqs4+?(EGwf`OYXB4z^!qpk9LIy7ad<5Cjqprlv}c z!RIW=#~7t|$qLHdHDxIGX+SJFSa&Oea;zdJegtr5xh>@6Aou;Qjui=e*_I~gJ>K=itI46S7T zjgfpEejV+d`)Y!AInR0=62(8Iv?CY z-RNMHEXhaHJ0nQ9Ji=?4FHz9Rz<8%TJsmAqMu@kA4LPfOoYH&rs28sYX6$wr=~RJz484!KoFyz&vyvGp6p7bNeCYEzf>OoM7zrDNy4*DhVlJ_0F_DhS(u_sX} zo?-5yxsd5c?{tBEsw&u9G*Ct|97ld*{pULv|4KS0;Eo!RtU737&Q9$&CA3F-zUI?I6`ggk4|KJPGJZk^cqQP4<`~f0S>8-o>i}uetVT>I- z{UQVxWf)NiUh-%Mc&?$pSj}njU;H?68!1|Gm11e#CJy-CEevy3bGGw16$#nNyL@UZ zqPpU|qWhrI+Q_)phs?qk<*%4ixGF>f+Jo2rpz}jugYTpixEcin>%&l=u$2~(*!o7L z@uj^#P3NZ|&wIDeD3sPFHUa*K)xoee_eX<-`Z~N07WgO)c~`M$Ln}|uTx{YLdW<_6 zk7Fk@Xl<4Z`^$cPWTae$)(W9D4XfsZFdMZNGX02G^)oWXvcXD#w@kU*YUj}F?qFt% z@c`^D8`&i-=$&Y%B5p^Raf#IKFkZSCS3-c5Ky-*mRWe?cV!zbFAqQP>=23(Qz&8kX z6+|RjfsZA9+zjA@q<@ji?}cNV&*^S;9pJ;%&_N`sy+3@cT5^oOIjSNM{rLxvQy2LH z`0lWNImOqCJPCzh-KJJt1+h#mf4}-7hGcU@{C7)wpfe~LsYg)%@clw)y0eOb?yF%C zOu|Jrp_Sev%777sQq>Qa9j%KYQj_mX>aZMNy$K<{e=P{esYB`Q6SCjseqEFXB?#jG zYaI#bslqbM?mVsZ(evo9=TX^XSDsob5O%^uNfyu}>x3JA1jMh3>IbGRvl|m;cV83* z;c#lr94yO0*hz?&A>!rS{L~DZTH5G-${!Kkc0&WTRui5@qWHT9^jtEAI{Ti{aprT) zM742KFHr@e$L>~}xM*zCIc)tpfsb7k*d7R=RQd}#3GjKvb?01kNY$#^E|>ZGg~hU9 zCnH7sXOX@r?UoL%t|Gjz!@VIC4zHEoA3j*Ou?Z2+kT1AQj?brD)kPkOK39T7qRuL% z*S)_3KmdSdg!>#>cR6WyZ7^jPC2*gZ{J(uh5DvVRM&vt2RHV{zQ*?iHya|D@5{P(tQE`#df^U3qc3JHi-#qIq3=`;#0hA(u&T8|a z)ApPO>k!snoUQv z5LgN4m-E6S@C)DS!xk6M}1@wZGV3AiT#%3)(Z>nRA5; z40oTWud_VZ(WsBLk_oi~IQOU1dEp0#_%CIcHJ?RRA7x#28?0Y8;9Ea7GC*Da-2MH!|`{+-4rhs?krw_D5=0lP9G-%DCJQ8 zufwiB)u(;!@W<-9_by$f)gjqQgp-Pt9)(#t(&>?rUaB4h5YA7fFfTH&38$@mjoBEu zL+1clK&HQ&^0>vX%Wp@!d4^|ONXPwUx6n}qh%%lilM3$!(cuaJ03ZNKL_t&ycER=U zjWPzUFeq8);b)5)y$B9CEponOSBJF7ZZygeCP-gU3JPV>q?hWgp zBSfl={;u&<8RaebQ7l`wpoqC{Zq4(=>kByP>-QRh@%up+*qdib>rEyIh^ujx3Ydye z1x`%A%vIf=qGv@i>}RdnXGk@Z`y|-2CdWbP`5cv*!)}Sy9F|?kk=gl-O0FRt^Y$w2 z?7K(CMs&Osk0bwD>Dbf=+YA2|jNb^Sj|}~TDppmVbLCm*(oqfYkwEFlC@JB?r%yr! zD0TgHh7R53&|K9R@qxy_c!hD^@QFR3CtdO*!m<5_OY^&60v%GMOi}ubCX=wxI!ANP zrC)qw1eugPQ}lT>oiPZ}AwP26vsz=tK-%=TmzsSEb!ZC+V(I%U=zKU{Qd;0^8a3`k z2{l64QGGqkFD2{{1q6&hMVL&4NoZ{#yY}vJU5!vKRb@`3uOtN|)kF4UY+>sP zZG5%tZYIR%gv>`NCL|Vcb=7_Bmw1Ok&RQY^1cWAT3sMf)mKOq0M#fL4eICv0YHXA| zQ#$^l-&oqlMYt0|7gYr;mFKv=bw|_D7Vtq4g+Froq>O+83i$R77hJp~mmI4B9SvXA zp=OTJ|D(kgaZl0lZPB*|`CG)&@&)V`oReO*VM z?Z8Cv=k~++=^H6D`zPLF zRB}14tEiUs9GQ5Ya;vG6{*Lx_$=HdO8&&nT^zBQ_>@2Ut3Q+GnmI-54YykxvT)Dd9 z)UTa!I>ihi$^^Cx3izZEfW2qm!jU>rW!*pMUu?+$D4evJ`U|W}5vQLbRRB6j-+&26 zVS_H#^NR9xab9rfB;TvYz}Wi8dn5O5DbLFnUbt*uy8Byi8^TX`L{R~Dl9PXYC&x@2 z#&rj@ld{{5v{#Wm@*JP(s(Q#gNAHZ6tF#A zfjcmPvJ)z2A@Xi>%eLi zY~$r26@U8gKXaDz?Z4iDTWH0Or%9$Os2(tp3B5GEsuCC*m>D)gIEFC_iv@M{INgSD zK>TF}CF?yz@C=t zQ;^R{4lG|&b-aHnzCOT=0v=L5OQND-J6@Su+o!(9duy9GY>#1-#RTg%w&29m%wMsV+o#Rr^fSNCjTbH> zCN_h%d3m`@@BUeo*5G>e)CyZlGb+4+uM}R)g5oh6jLT?0(ZxuL2r~0*NZ>kh&DZ&f zoCkgcMd%{CO(p?h=gHP4XG7bswncXL=yf{SYZjgT44aDld5jyX$Lvvf^83sMt>97iZo>OL+ zm=`ZHnxg9jQXzk??+C?JPH0wG5X^dq7J`a?5=j~fAqE`Bm~$f?6brfQPw z^kn(OdwF2aAj;zFj6bUzg=84Zza2M!1RR}^V1toeLfC-|FX2ZF9@HhrlIv2KEuv)H z6ru9wgL#B4O7D+&j1{>PN9z*F6GhKxMvolJGx-{3u3gOe2M$86Y2ns?FD929!L>iQ zh(nUoiQ(Bubb-qbs>O-j$FqeKXr%(F4UjEpjjcgSQ6!NSggXic=!Ig$O7X}kCh&+c zQ5WA65Pm=-;S7SYJA}ku>aU=*AzI6hqh$=O6iV9U3+>F9wSdi=8dz0V&--iFv$-`- zqOuQ7vYN8$eK=-lj7en+aRQ?EgQIprxSi$^VFa3xi9|ajLgXFFEA6e)V@ap_(|%SQ z?WCdo|G9hbc+HCX@c%t?o_2fN-MhUv%F40}%OXWV0RcgEir1$Hzt~|B}Ts{ zmc$q}Cb65?6{4UhMHFO#1*Etv%l6*e-P@n%%;)>Z%$zglJkPzmz%{?`>o@zl`t1b63yiiJijCiqPs5{YlAe@G(ByTKDG6xk`HxVv+D#q(PTZ zQHfX=88ih%6)5WwN_yD(bB7J*os2SVlim-b74y&NBBxDlX3nyUdE|*I|M1;A87%bB zbo#4VG^vBL)E$%}15&^Tr?eKUz#vTn>*#O30Huxd2M}sX5s4)YI(T&)uHy#3zGRkl zfB?u{_e!yYFQkRtzX&#Q?7h@PMD|@H0aM|0BUb#f8sl*X=#e1v^)F zSFUBpz$5G$jF?gw@f@FfLPN2|o#e)p!I~bZ0u)-81xT_VMTH;B_s(wlO;qG_w?4p^ z@7_vhOPN2uW6Rg@W_z%bYM+==qhl^K@Y z&v(EHB>j~19%20qUv7Uhc?@z~Lh`I+xrPjP0KuTh0)NOk;KTq;lIt==C>acFSk<=Y z^l0*o?)c#DVd}X6F9w8*?-_Fe{}W0;Rk`T8=c`K7nci38Skn?-_|+SE_z&7?&UZbk zprT6JP$S}>H@W2BTGx{Xjd*3A#9a5OAJ9Kq>>Ep)-HqhPHLa6|&mdrv? zBd!e*X<=5oVati#nC8XAZjotH*+2BbPnZ$Oj(;rws}Mni?Iiahp|nZFpj+fAd`83@LdZ9*KjJ-hux@%4Xr(JQ|4kKg%M zQgdL)1$e9Mv*rSRA3A{WSOKaqW%`BMyP}F9#U}pw)fdvN9;#=D3Qd*^NaQL1A%g|r zi~m^puv>zURX0Dm>&9^7pIzqKud=l3QO@r;$l95wb98jA?Mqa0U&rly8*(1`5r>pN z)6sw{F{7M$-plDK9p&7%oopSQ&eU>01ugvIXgB}yz$j+;Ad^}n9^ZC=f)dV{H0Rbd5KmsbLkD?eAIKS%< zt2+;%=y1_wZ6GzfAY-4gIQU?p0#NznU8L{H-G>^l?D`c-VwQK0p!XeNc5#$a1W!$U z9-Y_zHOf>O=|9Y!zr6*KnDvJbvSHT|ZrdcB^Mc;QIY)`zEK@^j!7$;jL=A8h0T2h% z9_YBH;^EK)ZIAr42*+Ve&4=NFk;7H=h6t=vAdP8-10NN_3leqIH`hE zz3waQTpmzEifR#~ZHNd_p+sBvbk4c# zEu447+tG8c<)>TQIrl~Hp|!J@nuIR5sj{sq-kmpf^EJ7@*fhSDx)kG{OPz$m_<_(V zD3-w4htwg-?*SOb8Hj5`#Bu_jEAV?9iWuge{SwZ<;S0R-lQ;ADj-%E$BBRI212GNm z1>ZsC>V{lo9!SG`VR(0f2$Wr;8E!(H%eHgqN&q}W5biMHZiC|6B+cIdFu6FY-go_! zSJOB)V8WsVCxQbQ@1-hg>A1kofn6;cm{uBKR*OOM>Hl>J6@_k-W4tPKyREAmTpPlv1Z>d`9N16NbJ1RT)L?+o*~m-@-kOf z0dY!lbrDsl@VYB5;+0V=UtD)NKY!?M&WJaoXD)MV#wp0OX2$##%v!L5qTRqR-~jB> zNqhmovE13I3TG@_&mjybm306L?f<@n%07S3v;PTafZO@kAq=HbPe4oQZ9))_488lX zKL%L+`Y&;Kg)-$1g14mmU-^GvFb&vA)j*N4&tB#xmr z>;|MjM-0|9wYWxmL0e3k;Sk-SF4{jIeHaz&HsvZps>4tnO~)iL=X4%Ci!^`6cwE3U z76X2-8G)K_c>g(4ET0F;5qlf0Hkku73ItM7$sV}H;Ir5y|CEm~%l$$cy_LwS?oO7C zAmJQB-1x)h)|J~zRv-EvBhp5Rq8|) z+O377(1I#;LU{_G|H%U^yWxYZ+ds;bWiREafhnYm)@{n+D5_E_*(JwPQY(@*R82`T19pPkw!ZSJ!4T|qQikRorxw-f3A*nB|oXpDeXq$T73(V-_8`{H5}Mv#;JQhhSlAK-onZLVFCOII zezq1|rrL~uxdL=#efrCj`1OXv{PT|=f~HEbgF{EUBnevn&20I6|S`;o<*njYX8i2=ViN1)a>e&8%!ES;Nb zSQW+y@1OAiSGMl0k2Zl|zrWyPBrP@=U+9??%KZ6{O7Of2?)i`6LVVAj)5CMR52Y5? z`*`rj4m{3~*6<#6vA}7^+3gEFWfe*~-io z{_SLlDllRy48=u8s)mib4syJIh#T*Ih-+3Y;rjCz+J4nWc+I(!d7^kBe|Os*L^|Tq zGiRaDP#a2#P<~Dl&e2Y$DwH`eXt-_gTxhRQTl6~K`rXg-XXhUT;GQS8a=d?lFTVYH zrZ-oR;XY(^(6#R;$^8~es9MDLHn*@QK8M#WH+*W|?XZYM#7un>gmb(5D?O_(J>&3w zcdjKH9vmWpyidR>mx&GrN`6&W?`ojgR}e8MrMSH5(JWgYHj&qm=Bd5P9Vi;zv>z>B zcZ_-|gv4Ak{UB3YjH57#+yY7{X_(v<>`LU?QDM7Ik*K0Fv!=JO`kcjVJ=B6mP-v94 z)NAUJONq1sbmF?JQQ%*jd`l|~SiPTpHa0>bN%F6VMcF+B#mjTqxse0`pL%Hs4*^dQ zN?c*8Z*eyhGax1rIbxsLS8PU~=8>%|=-or2taA;PqnK6gn~9B3iUJNs1gm?A5Z%m8 zYc})EJJ-?C-cFzD;qZuN=HeR|s%+<`2exv>*>kBxF;kkWym#3)KEl-aFT8fa-0qbYr2c1z6f)a%|eb0Np_W%Q(e~Qu?WuSk_Tlt4ae-0wVoflwQDqQ^0 zf9Cb8XYqmSF9Z%FBPT4U5sHRNQ;Z_oyXR4p*{trk3sorD+!)4?pZz8k^Y+WTuleYG zJi@720{oj!2^sv}%z#oXp9@$jh3$^SF)eDiF#>Rt0|EBU%W2^Xth<+&;mFphGs}eU zux-w6GnBj!ZPE{0DkaT~jzYjSNOtOVgeqF)-qC6kKfHGnKYMtGy=9pKYcT?A4i(AZ zAFJoGK%GPnd%*Jq(ow>U;>}oHhfkbYa|uIgCacs7|LPNe=4AIkAsA9^FRLdyhu~h= zp1?_c+(d2Z8~DQd>HPfLMgDO(8EFaD>asP9a+ZMdVpt@Ir#UI6R`Z^H}>jMd8JK6 zDJcx1=q}Y`x%wso;0!fZ8K5V}s<}Vh7Fh}KW|&&{nh+_tXOiiYT)p6ZNalXB>7_29 z3T^BgDDa(oHuAUMzMIe9XV}xcl1OQEr0urzT066^Mk}3A`!vkh$Emy4U05#~49fj^ zTCDJHv^B9JL1>~2f!psdVDtY$)riY4*x;`L(g-8^d=9}{f(Xc15o+R{d|XtZAyE?6 zj8!JqVqr*{IovnsC~q*|jS#*nSZ#i>i+k1|v+YYBn2ce0=U}UzzF;!-S%RLmHQ@J=5=`m2XPkr5vrKHwJwD)LUUNU? z@!psb0dix>+y3ib8ysh*qM;0N>TNYTPcEy(JoMC|DxduEZ~4?3c(n2&I#&Dwm)*VVzNPjmaiUS;&y@pcZ*H?DwHObW^?)6k1TD*+)#!Vm z7aAjm5Xh74J`x}npP*D6Cz8|?sy{r6TLP?u2XoI|AC&W1{((Tqrubq5vgT~pcBbp_wNXB%;$1668AMP*(zZ69A+ z^#G?YJ&W4Pzu~L5KOB7D;9DzgEeeHJC^RDom-TF6deaD(Pua&q&-)qQTz(I4p1Fa? zo_8}_U-;jAaPc~;pFp8h#CMN9mwVRls9#e`gNmS5<@#y67;QU~k32M)gH^Z7aUBZ) zGb^L&s>_#MOiKOpnS!1X@WZp$0W^l7a?1+K`a5-o6A%{{c2O_qr_Chto+pETQG_xS zr1DyZwO6@k!h6AMGI9GsJE1Bpp$Nf)$rL*_-M>o}I5Yr%^o3ue7hcblRp%2$ilVdW zMp_Xm+_u~*6Q$>fOzJ_JdyPRTxZx{hckxlA2*MH;>r=0kO|4*=pLE=rTmYocH7vpP zRJ$*kTi$=i-wUrN&Ojl;5El#-hzoIS=q>D_7)e$c97OjeFV(`K6T^Jzwk_Pet&5H` zUO{h5E0K!P8V(NX!~^=Wg%KA{?H ze#4%T4(@nrDT%%(iTI3cTDC$fM+U2G+PRwz2#DcLGdJ?4nHzcd zco(|{n;EJpPU~z>bpYWyRS}pP|9kGQxN-Mu8R)r!pKbmQH!Q9o)lsa&Snw^3;i@I0 z&$}6Zz^RD<68w%^CW-^dbU>HN%M+g31FOL8TSlQ!tVaSFq!+U6q-@|yzEc( zP%x)_g8y9cE6|E>Jk`PWe#v6$WQoSiZ#glIdWWEAy?xk(D*;Qdc-M5aKFd0-xJh#> zq1tsOQf#(JV9XK#-FS+<1R(_XRqpxlUeZ?Xe&$+Gw^5(mb<1RE8@gO*=H6p-DU{pY zo_y|>QjX>K+>KcI(F3_siOA=ve~P!f5-pXVW4AY8l3>Fr{1PV`Unf-H<&}Afd{5># z(GeIgrw59>P}!GjuVzxEUdGEXQ2te&%wIEx*yZ%^s&3=-X%*_$YbY-#-&8ZFw}(YD zr%*tc6YXMt`2hDnwTDMHZ%h3eL=j5~$~`>z)LsrA>qh}i^RJ+I>1X)X?!~;~v-h(5 za6d5x7QW%{x#r_HaqDBdg9udEfBXcC-uh|Y{(~Jx#PubhnUw&rwlf22|!0Lmf?~+{Ha`+n1;Aw<+H86uag% z?SYowy9HZ>ky3Lnw}*fTwosXTz>z zpbEh!3qDA4Sb#z+L$wN{gD0pO!zXTfgb#lEe&|@rk!T(Yt4!5OamypS_=_+6fEzcr zP-yCM^RI{sWh(Qop!<0rVrFj-#imZ0ul_cr^Z%B2-nfrL!^*h|3%zY6&VTi1kkj7A z&}fareJ7lp_B7VfcxyLYx4J-FXyUU!IVffT03ZNKL_t)yuO}|{(r21bg<`{JCzWcv z@Z!a*IW=dXlXN2J!pSN;&7vrk&-CUkNnBYafvbnAr(2J@Mt$_yG}P>S44R=1!Tms9 zRCOXkj`eDQxm9?t5zC*{g^Y-yB0ltAzh=Q7e2`N63}@04n_(>ZbL_pgjyj2XPo?X? zzs)!n)3Vsc1=9p58&^bG~|LWgx*JXF{)e9bB-R?oY@yo~UH;ELb z=e(Wotl5mx9`F=jyJ!=$+p6|jQ~~9E^c!)K!8*xtk}Bk211nV`0xQL@xA(C=dLAJB z)h~`gu@bl|2hAJ93l^L6X{-c186p_7OuQ#xR6N6vTR93Ba!s46Ga=e(Cm~6$K?>lF zN-~Kmdm*3hD|0VwH_(eTQZD*kHGLmKEbd+F{I73ZDNb8)8MCH-*qZT_CdtLCv^`fi z3gx}~t-?x##JkUWiMq`?U4jgsCc&Uwgr4&mFjkouyTz^iI2;!d_$|5V^5RGK`dB4@ zJB;}Avz3v`NaOoONHP?8Wnh9VjLli#Efj^0dHizITCQ5vOlNBsV64}i+uYO7{jq^j z-uUSsaD4uoXzp2vF)**KpI9_K^;y`{-|8NO6fqBN(Ol;<_H*aYZYLLFoc_Jh_MC1M@+3b7aJ( za2GMwQ_rtCkC}}{-!98ZR(wg8Ssrn?8>Yq`+ltf*-5`d|O;co_?ah}Rj z5S^VFVp3OOEv_EH#-CxYz0M*xrtmBTZu^LhI&2gQoIK-zJOTdTSxGnqFvbL2gt5CB z8H4BoeO;?r{gywcI&=(x=dWDKuKkCIW9u>6+EixU?g4r)`#3A-H1qmJ_j7jV0WwtB z1DlXqK17()+{f`+8%IYpx3pivo4)i_CbO4$Qzy}V!Cz1tZN~2{s1?L>ujH18f6UeA zEChki{CW!m-L2?I^NC+<;Uibi=FF*`ZVl@c*y9*X)q3&8BWVnFLS)96Ku=`E@OS5q z@P%JkLKsJ_eC7VVyyL8L@U6TrjbTyyNE0XL0Q@IDDe%JtBY@H$e{3PBr3!6z9k_*L z9PLwveM1$t43+4e)B(c-cJe|+fv3{GGqu55kw>H%LxlFmBh)0GgQDtsd6Vp42C?h2M8cNu=pnv&qVQL({?5kXK@Ab6l z5mwLa02!bThDBK17Hd6s*-W$T@qSKS4=`?-Py`UQb}Ru3=>oE`>{{7H4uTZgS=ZOZ zoJlRrXsbZ~5oC18HjwH1NjL&ER+W=b@P?&9oeT$|tdD=O1xoGw?BT84{@c6R-1jE$$I*>YF7dew zhiKC4*mS&!AMTjKU0cs)?=APUe?YTmuuPLWj)_M(V_FN>E^VQ2WRTHnk$Ihky1r%R z*U!Jn4Mbw@Uj2i5&L;P9B4RE(fA;d9Z+mUlywc&T3c}^Y0rhQV)Y=}BN8Fm%|?_) z;beTrEqRT$2k-=e_%@I<8>F%+w|wuzkYaF1Bb>gc#|YF@d(Q=ADy6yt9|R<%?#Lnp zVnAv!5D|(|%o`W{juqWI=qZh6U+P;-WN!B||Cy5W;9k<(m`YhQTIfiZf#bq-RI~_I zyQL%wZ3HUqsNNM+B4O|TJ9z%W4$hif(nICch)9`8U}+mS*C1Mw)aFVG!QafulALVnmZRB_QVk+~NQEBe9z^X~M;_{0u5mCUhm`YLQ+;=c{uP0!KDey&0Bti0a|Rx2}pzW*!c`12!TH!0Ae_^SMf(`C;z>-$nL?WMBxg! z4OARg=GKN6v4%%S_`6>m;FI6Iol8!e%&zj;sAv|deU9ZOj9_$u*)28V;p2Spdk-+E zdidWLw}XtNoettml0GKzU!M)@l#0a6?--dzRtNB`#DED_0_u5)(}p{0WEZ$+eR1X) zOT$JdU5F~}f{}jvF?GSQM}Y=3JQ#Oa4OsuoRGjjh1n;wZ6yCZ103W$|7e^<*nDXSs z6bccM4dAswJ)}xF3$zN45yDNyJWxd1&X2*HA+2k%4-WEPe>&g*EOqyD80<1AE(36a z6nqq&M?gl%Y);H#-Uvd|G zVgxVi)DejAnqqRR=5;NbSlD`$&um)3_WouxHq}^&BoXLs8m4C8z)%S(G*MdmT57eI za_fP|5$SEB)4n z>CM#^#&!gHTGSsV76AyFdE#7Cwbm)(3-OY2-NkCTZ+h}LFPUBB_`nFe51inPX`R+O zkT<6!CC}OibwYBMsqb!PL;;sd3IZ-dvW;lXnoS4z+n*ld=&V;!XzocVhO_aDi}6)D zVDdbr5t|b(5{M8C)*U!@N>8uh7p%4)59D4{a9s|W8q4aZ8FuAKZbZ$J+{e&1Wes*aYXRluAB|wRbx$|f%{fM-HKSBGJS_AS3m~KEI5JiN>V~K4 zwEwY%<7R&CsLE=#`BPoUkzVpy=4o!V2rAm+U=2MR9Ddo50^M|2`nW0O8sc)>3 ze=aW;)NumFCL}Y)a{|^8$XfxtIM6b>o0*+70DwBaqW;>L#5Djo6&xd6H1#kS_1*!% z<`b>F=FkfP3)n?vxV(1{m{DFZWiR(mnacaut>nl^$$d+M_h_|qYHf!qwi7?SWt7v~ zjyoR#-`@ZT#D9ZuJPkyEMgU(gm@%W(C^dOjj~v|D$M_1sQ{o0D!V}Oo+|)V~q!cbb z?I1vD&;=JWt##5}6)8qq=0TxIYgy4Z9P`z+y94>I4z-h=;IK}P&B9`FRc=F-JM3Sn z-6iU@zn}Q&6Kp&Rj~|G+ciRxEsT*`TeUBKX&t6JY>9oqeMB(S{{0=bv^!w+OPsHgz zd`b;t31nitcU};`PZ)!5LwVA80PET3WoZaK|4i!jmw5gK-h5>LY=)jj*X#d`d#{Z( zFd5H4-_Keccn~6%;v0jBy$i8%F$9Lr!f`T=GvRm6=LBd-%Yys7FxoSdvc zDeXd7g*6A7*wru0C@My4F}+jfqx2{eW(Q7q;0EK$PnP36>*$D0aKoH@WPE)7|8?Q8C z3_|+lS^ltYChIVk;@4ala4q(TNoXyMGP`YnR1Ed!M#U#`>H0UO^^BT;Se z_oJNK?!MXyb{zX1v)W1!4}lVvO{%fzct7iocRT-n?dm)CUMFTK=h)*mrb3M66vTD8 zW+atF9GkCj8@{V_Q_p0cf=ToaX2y#NgaDlt3Bs?R@c;}7Y;r%lZtHlh;AS0jo^ZJ0 zOn)w!i1G0YVm<#n2jG%|G|3@4i~S%`{TuTg$g}u<03Or&bDL7~F$j+Z%iW)t%% za_b($ueT2JlGD49p_((9NIHxRHxRIU)&T)kY+}Pv!{7YtO*~M$klKnHlJr|RHr|@+ zttb-tcCtX74;+{fO1TLau6*}hv4bC5e3THuyp`r zeiKidYSIF(m*Ek2j0a@pcsjp8Rf-b`zH)jynWBUP4ge#yMx?I9;}Om}^IuRm)CG7k zC(-zeHS=Xsk(t&6 z7wD^%`24STu%`5fNY^ZvtIdVxy0A`#MFQISA9$RiwC2`ht5~~r59iD(fz&L}6^hPt zBv6?+if@%^Ds{{C>&mhGktQ+m=|Bv!sl1)2q(XC`Z{98(Z~G6OdXx(1H-W~?mrr(M zPCrqyUOm9(#eqiFfN%wk{uxcjdCQ`QSlD(bX!RMClP+g+Y)$@w zQ)wVq-Iuz@ECsN;St-O+?bcp0j(jiJdbx@I%b)Gxy_e2rQF9X#*Wb@K(owE8Gq&RI zX^8<77Y-~k3GpVZlnwNqMtMN<+Uhj>?PF#opI$$o|9b2MFPbZK%(#%xtT|3xZn1gN zJVwtuSAFVyJzxo{`q_(Z-&afFrB1fD=g#rR7)$kQhU;c+;6u;3gN1DeAlW$l z6#i8HTCOy5v{7T!&nOymb&|vIxqw3maY^R7aC|uC$%7-DpoIaNZ8DGX-sPhP3EGTD zITWqrZ8z?LrVc2U85efN&|Z=f&rA(&$2j){*;F=qMs8 zXrf|-jv`wpvd2-R(FMiLJ128!NTbSK^mKO8RJ1a`#RWFYzvsCV_TKeRl=qRBjGa93 zj}IuOkh;uzySZO3)*_IG)P_6q+DtxaaE(vSnMo8XY2OmcEmC)!MM(Z$_Aj&u_;;k) zfkOA*6luhr+{@(-G5l_L9(Qj&%#|~aF|&6%AGxoShxQKG9aNJx8ev#l zVlh-&r=;PzE-6P8Swy2FpFd99JbhXbIlzw`GSXVp*KsaCdT0;#Za&Ue@7m19gK?td zOZV-w{qbIaIN8_l-)9_!kMoo>0i61K5>QkBd;(BDOOKf3&vOo$gdHYJ{cEwQqx8P5 z`s*gsx7e5@?U%2gm>uJki!l-t$FWWM6}>6Q$q=P7;uJkJ&8 z?uXogXXWx#J$xo?BO~{xZM76&R(Bh}t-Oveuj_(hvsLH?Ttc>;IF#h!Mt7{Aj+o9a>?)LBA z2kAmlN@Xy$jjXiw!+@Ve?ds3RSSQLqM|L@mr6Ti6Lr9SyNSY`F}(N8r&zmh1%H1xykbQKMu&iEwyua(05$h(y*%XzT$}FCC+P52 z058icC&w#DoMv7+XCKe&+DdO_i0|xL!p@Oa4v!AAcc|Ho%K@z$gJs=a+AfEfyhDx$ z5}Zr0IK+_g18~F8&Mnu^i6p)SerV#d1i9pPu4&F=@w~xERE_k;1(KnJaaEc(ShW{WjKunZRW&xR}Ki+-SQX0u@TWx_wS^KtIg%TvllS(HaU)zEjImpQL zCRC*P)xoK}ZTKjYBe1J`tYzvDfR<#dbx~LrUyHT%D}~nLqISIuZ=>y_>^;r#p^wf17C=Ma*#UWz29UkBcQ z;CTpA9h3w+2M3&}M-my|ivYwE^PdFAV|_-_y6LqnvPnGtB-B?3aU3Tuz{T1TCY}S3 z07x^v9b-g79S~-op}w8OAxNkrG(j86jo;Uw*EYnvRt@q`zdp>jZdeG_VN#!e2mIq8 z09#GCbn;#vyyi}pwDtoAV$I&+E>8RC9ZYF&qP?le`hCNY0)Hr}J?%1qA^;`ty_>pa ztAra!7qO>g78(+7HH~$VtwC+pi;$epRm-OtSFD|lexSgE?tq#aE%ijJOu03xFtxYA)oZHLt1=CqRy~3w^9^d3M2WM9nqNfI5WEbxm`H0hYXn!lE7r)&lc6e@h@;7UHReh~t+ zUi~$GvFAP*9OC<1wh{l}7sSW6^R@TBnpZ4VTsvb2fA_>nesyRDi9fut+>CZEe>T;Z zwp@nTj6GVb)PJ889a+SaQhUD+y-vdCQ?U25fbZK#Os>?=d-!}KsYx0f`;tDFy<HoJySlL^nTA9P2e)}GN^A{_iHUy*2_1tem z{tTq|l8o@y)A#dl>!%TWFEE>ZAVdJC1n~JXfg*s#l*wLzS`cMfL#=EM_Eb3reiX^# zwZb^g&CSZzTA__X3T3)FI#Gpwq&j5Bn_cOEP?S*yMonT?vweyaPUO0&o49uJCX(1r z9nm_n1z};rsT8KI2B0{HM4#~HZ+wz}d)*=~K65%BJ^M*ePx9`ER`T1UomSit4Y4HV zO`{AFJN92$8QJa#?1@cQEC|_rKb7`gI>-c}B?va)o)RPxOb7MA88;c9bceJidpeLY9 z3-4gd?`R`J5)Y18Q(0pB1`=O-?_#H%IWBTePC5dm`N{etsFog7scFn^m$h)?rBCvM z)xTnTWx$4L$Cfkr$m09>oB8*XiVzX#GGF-BzKp5O10M$b&c-e0{Uz`C?iSwt#asB! zZ#HxL?+)^=)3#C}W?B0Y-nrnnoYir_?S2!=#iZGBW9uAaoHJM&-W%-JBl%@(t56&v4`uXKLJ`ZDBb-1hE?2mu-j%v^8Wv% zO2<$HkVIY$s645{&r428os6Vg2C!OFR0we#rngKR$`Sx$z$U z*qZUn1d0F*W2D(TR)``=fs>fF#M;{Z zoF&J8(Yw6^{H{7ilT2`R+Ev_mbS54DeJ_9a(sN+&I8yD0*tu4QtgCBpPNgq|h;&M{ zVpxiN=D#1rlrD4(bj^1F03ZNKL_t*SyJLYJ642W2ZJW2-0Bb;$ zzvV@IdBb$Nn`->*MUPn4PCBfNr;D@{S~;|P3pLe=)^-;kt#p#mI7uQRjtv*j?pwCr z55oouW$StgARVeb2%Xv_8Ld|ux@v)l(NGKx>jXGE1p%-0OA$b9oZ?SX&ZS%|@v3XC z0uDEN%hMGe_^F%u?CURKQTJpR8L%2w&kcBJYBcU( zk^gr4QNFtUbfVeT{nhW>>n#W#_+kaWd3-&k?N2a!avK|ujbLOd$By^&H($AfN~yro zIg`2K+(kUG7pc5KTK{6sR1n!Nj$K->!IUNoq|uL z^}{vaC?q(Y8cFJS9YsRt68I1Y8-WL+`_45N@;f{-%#ZJ0$N8_FHr{HPxH68OqaJ}g z!*)*EetKcFTBUEehA82LUci?g-pN%9n~9=+l5D_F=T$m_LW!IAp5}<7LZmiIDgxw{ zflo^zIB||ZqWVRgebf?hyI71Q)XyVad#+N+K#`iNJY}@DHM9mJF(THRu-1JZj}6V` zkH7hSu05lT>sHQVL3f+on#t4x2&&o~e-fwD5F~x*9|fjeiist03^Gd6{QcS>V)Os) zWD(!k+(*k(hmo!pWD?YL3pe+zM&c2=_wD1($L?ZZwVfS>=ThmON93p^uHQaAnt-zy z*e>Ng0~7DWJgXnTvqzS}-Sjxn{O9jw?D=D?jg+}fYba7hp>fL9I5GdZ_=gxwEI^5; zhGu~b^Q<|5{Nb1sIaeuU^H26nopLWnJR6z_{WD>6_Mo*OTsUKtn|BMS0{0&|jc+`7 zFK<{@g6gT70tv#g@~(y<2jG#w7z1fsCeREhC+xCuc0HpU z1rH|8r@~5((}kbzb|EfmFkOtwIT%G~>wDka%f{$6eD?82S+ipUE2bB@Xz6r%T1(bf zzGj%y)ykx%7^x0guz&|P?Bup>+nL&GsKzi-gJn|{*PPYM${B4Gm2yZR z^?;w;EWCTcqo5Qgs+!}qBITxb{@|Wf9FrbQ?0oVQ{#>D==0t8!I0pe$>Yy-rIcV?x zEEwaHjaYM?(FPT(1cS9K1Xk8NOnB}x&%Sbf$SqB5$foi+R6&Hr^5N6P0xnG`0B zoFK~qjOPqAZka$4K#mx@<&CA-A$Nr$j*VM{mWoGi1JMNfB~6hf2}aOXpP(HT%&!Yk zNb4MGEwg!Q=H%rHVYdWf%AEc#^k2x06?{oC>3T772)BZiBX3ONQ3+D5_Fp zZ(j|m7?;^7q@9i?dZ1Le4CleFM+U?>0BPlDzYFAX+Xo~-Wnyuu%7Q%vy|W}?+zTt#+oD@5?I>454S_cIBz;x3BOMR zKyL)F34r>#IpA!FDAH>Z0C2VqsXrYTso1_qJi=)$yZI5l?Cfu4?Vgwg3yTN=HyUDupsC ziQ-Fi4w9l6JF;|?_BT-U<=Ff>ww|Pu{ww(Db7{xOnDtbqz4Sb+8vSgnf3%DCn`|z(p-G&GwD78V(>8=8XAv*ZY z?h3D7s$q1HY*ru*QG_ZqLAiyawGv;ycOU=x+Yz=$mm|7BqB+)f-5XOo%J>Sv1|4c# zCXfT*pl5#*6Oz{&d;fTo-b+@sUwlp}=v`uy>sTb=qXjK=drv+~c9o+{DKqUpVyT-W z(dGR0&wj#_d)KjI-V_#3?_qLVkybT~j2y9T0jL674hzpYZ+P9`9^*!y20p!)j`QrPH7Zhr zc$o91jPSDg`}yJKUYgGY3bDtlc`qw;tTd?K@{MP&xw>Rg(M} zi5tBDHFJo14j_-VPN_^_II!&ZG{Qo>PJglX(G>b!W*e^quuzQzG5Si8ZlW0(YXQNf z-F9|$Yc|AfLGG}>2ub!<0a0c8d#~qsAUq=U&(%i27@9SF;EV@(@t_%%PDIYqt$>%en*nWWA=pa4tiY85VpzFnE^OJHVRi0W1P|Rz3hQks?5B_F59| zR;3GvjRU=a%{ga9f+&g*A<+kbWq~b|;638}mf0Fn#>GQBs_$%v;#h9yYit3|e>~%m zi&E4kUqnqVbiLL5`H#QDbt`7r{QqVYOw9V@9X56$pz#VD<)>l`310DLDX{SPOMc7` zcP{219$muf$;bHhe!G8QgmFxbP7t_9;#&K*9J2EcQSSR`2kl`(4?{lOm$WYuBoxVg z^6;5cDIph-UL;9fo+|$WPe5bcilbr^<))6O_rg=|*civBLhSr{$N)I?*T#Km?%Eji zR=jxWF3xQkJh6X-Z{5F>o1at+6&5p8S!g*#Wr>`( zh-IihKyB13TE?;f{Lga$TB+T6Z)Z#Oqmy>W&c9p0VRTGDES1*IcR*4*xCd}duxiE9 zRbzvZw0OOQJ2yXQQ?CN{?*{&J<_1beR&dgzr8hRY!UVzdn(_PPP1Q4vmCZ7hC( zI_UVPhbQxnD?3pVBeg*;nQ@%i>j&5|*z9J{j={3liuO{Cx1F|`Kbp6W(4@F*s^V8y z-wLM2l{2<*`LrEuIMKy@2dA?Bco(||n>jSnOhMZL78D#FDN{2#8H*$wfl9m~bAu<0 z01T}KerCeo+@o1!kt-gJrgfdpBH#u>FF=cY#d2Wc#5oZTo{0IUpKRiyrORk9mcR^+ z`Odm>(&g#_RvMfK$RcxXEO^L2ldsN+adH=HS6cMhSv5o$rNj&c#A7|J! zajfMqSJ7vu8$D(V?}?z8hrT)$W?5dNzMwD^D`Fx4!?m1Y78=zxP#v(G&sgmPU*EEbcip=TaBEb-i)ZcUYv~tC;bd`D#3aKQZKpDF=kBT%V_CC}kPWHaZpZ33it3LWe z{^Y8ay!h-H%!*F%(-+)DEQYSaApO$B*S0L?$>Z(ZFz-obwpN1$tFnzHa+)Qb<$_S5 z1}cv*;F`JH_`v;3?Ouc0r4&SkBYoSvMiY0^GNA|n*lb^)o1DC?D&7a6UWw(m%u3=36n@7sJXavN`AM|>;<1Ho zOPoux76_Gkv4JRf28DE;$rF`HeCX%D;;!GV;ZLqy&iu(ms11WO(Mim^mOReE!HBu- zLxds9!Jw!Eusm#q*A-x@oY#Ap`}TFCMPVElBz`|ZZ#7U>2jKz02NY-a5L7u^KiSv2 zk>>i#IOo|>B}-q0GQZmqSqP0T_MKhJc-yBx&$m8!H4(8@w2qq}uo1vE=vu>L$zB^1 zi1Q;0j~b0%>aQo%Ja^j|=V_WlCJmZEgqP3S#>;00bpfIR$EwQN`U4$+?}|^BBeDex z{Ld;AkS`OtF8~tDrrh>6_;X1p2+6Mm*YgP09$0{JngFZ|v15zv6tx=T^2HE3o}UiKh-isaRls=Lk`< zC0O`p(gB%ZVBx*+F@J7q{PlAlVqQx>wW=Ymr5SboU$Wy)EH;UV!5#+-EVRknI%(1o z@z3Xyf%re))v>On&!yqMi6}+NZl~WT{Z0>5ujHd&zZVJ}pi5*YAy@?2-3P*j5iFKq z-a_hM2k^nS2)_sU_cVme^!?#?l%P-fZ=O8nwv!*LjK^KLQx%o?;S&QX)$^|N;MS=< zmpI6+ApJiO1GF$~{{*P}D+OrNkee*TwP~ z%9$Fyuy6a2a0JN)6Io8cQe+9_InMxcjp&s-AuV3ulQ?(P;yttYDz^= zD6{rxyRG0AU63w%H`~gLZsb{N{=+h%2q3PRCpEgJ6b15LfkqNrb~v}*InXEdwMf7* zti}PSN;B$$Z!#}FI7E;DsGkdKPxW~UtzMvDpEJQ?_K2f-HlximxvOuZbrr4+^W25| zSUy90@O347mMIe43z35FlD{T~i)S5Xe&_vs?(s8u@L&(eM@x)I!N%h!UW8UOvSvl) zL>j@IWVA$V&5z-v5V=xfcJg>hxB&0ap~EUpmxk6 zfjkw@yeT}*0e|MXd}njOJ!X;I(Kx_;va1s3fWI*ks5{R0Pe*L)H{7~&5>m$LywY!q zn^z_Om%TR+x8$hq1b-(Y?|nz671KpI_0phZcvkv zgpSIdpq_%9+Q3)p^4}OjR%IqeqR$ETWlcqr6@fq@G&y(`2JA{C2!YZX0@~ZmfSVlTxedtY~)sITQ2vv)9~*o8R>E+=D&Wj_OZjT&vBJ!76AYt z=Jo0uULg&PL3v-Ud9R5?(shpVCK@|bl~5_e4k4Ng7?2pe_y^A9{y0PBJW^44M*eNi z!J2VYCL;8E>Z4pcj+eRGB(JMbHO5=d-+?>c^G*EC8}Gp~+kr2=Yz$v=FmdXH)X&if zy=?Q@KFRN>9P7F2V&Zwzv+^;~vDRJ_HYKchr2TG0j3ac<{w2KUL$~0r$EJZ|1z^Vk zY9a9{7BHh>qc)=WNTWb85%dVfVxYAY7+DD#JsXF6ivc@{lzFD1uLs71GjPeyn}D}E zScA{3EcCGe>>W$~Hx_7*Ar&h3^**L^={nyo^Rr7Z@&%ByDaiffl?4A^wwj~ zfgE_FLr+$JET0gn>^1Ex#pBSIf%AfFx=efbykE!9efUmXbLVzYYc)_T3&3qBfMO}A zwF=Z;4YXDOt;GT8+bb~ZR)R*)k?kye@z$+)@h^T9ulwCEgWAi$GzL(Cw0LIL{|k&0 z=Y3x7rG`y;A06Mas?R|EIXqC)h)ak-t#Q!!Qmoy2R`@2>73K3qi+Ag4PkP#bujQFG z1A^E;!YL zq0jM%f-j4b5)i-=Ks$e;gIAs1N7VAhNz-5kq}sJ6T|lnPs6R@NV;$p#ix1)lKYt@$ z^4Yh748a@ilYr<#SV7do3kA-`BYzbqIlsQImtpk@aVP>8zkrB?b3{uOch>o{e+B>X zGgsk@-?|t7{a4?HOU^$V0C0GwhyVHITkyzJyYRkuzYcGH#f1QXFWvkg{_Cf{3E~t( zKR^}|Xg?Jr!1xlp`_uPhzV+`yzpA2Hyg@1Z7INK3QT_7zi%z&a;|Rbx|BxZG7r<)D zdnK2hkS0=-KR_K0(&{&f(pLc`+sCk37O|ZY42l+=lt4=o;^=xwh->=E6vw;H-;AF) zzbnofqJWEPH?F_DIsquJryA@l(x@SZypaMo_i@k9-+?Q>@LCusB9+X^5)4JEIIXM$ zmgbx0T1XNH#p~)_kXs9K;E&G`q{OjK-bs?Y)fkDv2;tkrA-4k;99hloneV~v(_8Sb zuDl*+jnAMcEH>?##oXdI!nH5O{r}G$Xm`E@0O*Xr0<&lS=h}D395TSod)|QiHqD^d z>*Mgu9Nu>M*>XbaHz5`24GAK#gN zE6`5tKZ(Yb^Ui0#&mG?XaP7l>=<}EF{gdm-7H@;KVSx{E30C)Ds<{N~rqo{d zM%`d3Md77vxUx6ZZ>Yc*IHP2OMIi_!+Rg*kW%gjFn*%$CH>}x@KY!&tIBUWoI${Wg z3x||#$oAPa3?37*w#e>ns@J_8$E!Z~IvnY@U~G`HjWtpJ)?}uF*Wz*W2%T7|%n%jZ zUe9T=Qg>DqbmL06<)B^>Iln{?_xRaOI^B z;H+__CXtOmqM7*zWK?^Ar$+OTpACg?v>Zp$P*7reY5_I?zF`i^FfiEI z1}Q0$LqO7%vkM%?k!8Oa6O=yCJ%mlWx8t&N^N+4l^W1k~H~{C@W$(+N^{=$W;Fcyq z^;oYP3u-R{O{~B>KlTs~EP8KtQDC6Ljf8iRFCXBBH~p~Xq#*znO8^xJds!=hY@ohD z{7%0|7YR{604);q7mJ4TR>X!Z#d!P%xt9`jgDY%qO2DP8u`XV6!Qy$m@4`oL&Z4>O zX-F^N1_0M{8i433_Q_TXY6kfH_9_zCzh^7T-txdc8ie%{m}S{>C!9&*B-Dl4`?UT- zUYNB%Q(vb3Qq- z*fIKEX)fZS5rP>NS`-ly^}7#Xa5R2sL~v3Nfb;x)L%bQx&u7nJnK)&q#PdgE>+2hk z2D&a{L>)msQ7iIt))qOCg~=3T<|+r#)PwK4_(@#0awY+1z2!n&04vZ^5*sRjuD?Z+ zcSRDLQ+usPs^cE++&m695%L{G9i|CKInqqOFU1U`)MtZazlk+$tkdp9hF~eHF#tJe z>Sj^cQdsfXOY#;=APzAPprqs?2H47R!3Kl*o`F{brT~pD#uIa+xc#yHxM}~Jz(oO8kx_7lna%On&=83P5dI&!V`>#$j-bCOwk zC;3!X2d!EcDYr&JXTctV1boP#oJAQ@{*s8-Ilk0*K}^IT@{)^@f@`+{$P-phsD>8< zz+9!y$D;mWJ_My--OrzEO|xe=aRp>SBNOP4t-#O!%_G<`ej zvUL^yyA_7$iidYb`O)+A;1f`$WK=>OAXaDpTX7v)(@0QNYTutFu z{jh;^&Yuee@oZ!gGa%@&3EvMivDw$&lpx)7h9mEg?RX{qJWV|BL}Ya1s%K zcm57TW(LF)OyYO%Lw;@r7OCPI!rMV<*&!up!qqKXXVEPD#k&JR^lC>yc_q=z7x(aLg1a$VU5J>tfC@G zPllwly-y5mLn)@a(mNRwCkuX7 zw@MxB{h|NjAM3)Q(pyA<+S#y?CBT|+UJ$4-Ax)B1swiTgA+X#71F#KsRG6??16Tt# zLCNU*hafA|XZ<6xW3rUBf$7l{PO1>!tDot5`DWk zAV(7dz1UEs3(2f3vpdGfly~5!zkDcv!_$VzhH~;m008XyyT;&cF)}b`wFHPxL>btq z{Db0J3>6DFYnPv-_3g62BL?>4e9UyWBCa?bUXEnvS85dnYg811jk13fVg}iM)dCMw z2u3SM;sEj}!?yMKoy(uV?_B;AHXdFCBH%?!X8|I-|IU};^Xpd2BV{2H`Tp32;+x=E z^B?kpvn+NY%M$NCeLH8rG2@Yx*FdxuK-<OUWiLz%%&_P)JvdT-0@&a)cOJs~ zUcVaLp9jQyFe?5cQpmZ13|Yr|rTqCXMb^G{oVBZ*ngTRBiR+)3!`f}HLcg^t06S%@ zKbiP@ld@R2AbAftzP-+8)Q*3sa`HF;0Py@>_I{2b^0MC~Gt9e4srezBBsci))1jyS)6&66Y>aJ$p7u01S*nHkqpa<@qGb+ar5k^}L$T zuHLRbQT2L0Jv=U4dK52OGNYY=-}sUBxM0Z~*s(a`=;kVG`h~7CxaD}~Y0AZWE3{M>Zs67c# zD~cG$q~gi}_?|e0)c}1V>R)M>>ZusPmYEiQ^Qyz>73YWFW5gNG*zo<5;~}NKg206& zs$rh<{myr*Gx(V;Cl>)+^MUV!{deG<$ZtU$2+>WhI+NgouYiWU*20JLKFI4huSb}4 zP3FP#9s6JS?SOn{%If8?5uL(^8BlN`7piUGsW&f&14ix7GeGneQk zBxIXK&!fRai02&Z$)@XMgRUd(LwjFL;!Rh63sdu>AUlR?!pPdVRmRkd`>;<#dd&Os zcJe=OV=R8_s!cdJ@|N%!78Nmw(58^djN-@`;oo4=a=rl7vJ$9e)oz^c=P#( z;l=Ga0eL_I9Ff3aKPNoL5kYvb>C3X^!Xs`M&Q3Q@YYD`#OYPN+gGv&jte7C;mad(U zyh^pLrqLoCUj1A6cOSn4N6In4wi|#w8$$rW$)}IXcnxp>`PgU23$Dxa zA2LzJ1QdXVt+SC$`o#n6{I^jJXMD!XN!0-WfcO3;BJ3hIXKCkZ%5D-FPT6PAVRq=! z+3XqCQnWAUX|Q4viY8q^wyAC)b{})RH}BQRhcM&szqb!BTh92g^A-W!X(T(aJ`oie z9Go2^$+4-PBL$3g6)k_Z&w_*0P?>9o^Y2R{c3#tIMxO6-VaarR_OyuZl%B@ZJSQBLEn} z-Lr4Or*C}(zwx@27=v1JvbQPFdMBJ{nuGPPlpk9|Bn`i$p}ucdN}yQ~ae_ZfP=1DC zrZg|)yE3kHH_G14NN*nO$Y-FObPfP(|9BlSzL~SL(NxZ2456SF0yuLI27szkFebm` z!G6en{_}I+&^D$J%dchnsO`8MANt0AJayOr#i-Qhta|M@qUO0Kg}V5AvZu!cHy+=@H$JisC%K$_1OR}Tzs25xG_8`P zyCUTeoddfP@Tws6A`6E-fwOwS)=_>)8t76@WxQb(+b5puvMT9XogAXDjfT{`4SCDtW)r=#f zuf%;@=Rr1P+KK(@T>$*d>Z5%;6xi$Im$gabGI;M$TZ+y@(R(R3!tBIl+QXOqZ)UTh z{!!0ZIVA|dId=m*-zj;`q8mg-!398N(XfapOLguk2dp{r&0D!J69Q{-x z+m$GcYp=$wdoIQ`4^0KOZNMLZ0p!%RrszY4Y$}sEpX5UngOQ?FCtt!DO@N*Kj)jl` z5x^F`9)n%@mZ%GGK`*27Jajxn(;tC01L%7Cu<)IUbEzs015i}|5Sbr!jEO(bSI3qq z^+)6`#Dp4}QS`7QviR7|hq3j5F4g4`)^b+`Me4r_x7GygO-La}(#Wn$Tc@!XhGeB* zq}6_mu7Yh4U7I1D5cc0g=je_3l*LIZrvw4ux(`hg^9^8*?wlHfuaks)@D|ho$ycCN zWROIF@YtBAJ}Y$*p^{<@sH`&Ny-Yyn7*?BG29BC@@%Vvpq%1r6q-dBNmHLEohH?V+ z_B`wzITDcHKa|3w_M93rfJL7R>^cIW`pWWibi|5%2|ztGD~?lwT=Tk$BbSb0cASq* zzG;p^y{=>G#IYzK2Or<-JZDM^(W{Y~5~WUn?4ct+iVb@^f_yW|UmKs;HyKiB1ngS> za-0)`^Pt!5<;s;Hs^s#J_(Bkr?GL0X=C3{N5FN($iSibRr>wK>->1SVdw6dEN@wzb-xW zt34;p;@ z#vSmkj3&I0bdJR_`Ls-kqM>#XSMCi`WiG5HIDz=xwGDK;@Bblo>Qi?PP!}J8X zNDz)P?U8Dm$wcI^x3dK6_Ld;qPS2?zfhHh64kXaDU+6i=>J~r*Txq8j1~B8`Vi~Tv zcM9Lx`NA4SR#{yrF%PopPkWO-%l~x!0qsu(>fE{jAaWww)82bAtC5Jy9M4(Ci%H## z!}-!ht(a{n@4G=OP1g6>>=;1I$on{-VA(i<0kSyK=|XcJL~d2~O8g=L3|!Aq!{v2I zGcwwOQ{*H0}Ye665U_1fNM`xhO&PJ_o6)||nQSYYbDeX8y-}>g`>bYVL zF$c;9aW`cknxhW(Ch)g+9st^l#iS&YCwCY z)@M))Xf46VzP=70Tsw_^>s)XYvFhJl?XLlctlHS*ZM)kV<-X6c_+ZSzdGL-kWd1Ne247oKge;0L=btVs8TbQZmAL5Io5HD5Hl(dlXclw*eA%m}{x*g3f6sX5Td5`@W=hoho4 z32GAv7@Saw1e&%=`|R0Gl)rvFFnLAIWXAAc|KT3|=?#pdV?P%9iZiMosc`DQT%nhg za7m-@v%Rm^E#N{N@AobnH?Gv>@1T6#l!uV>uzsfKLbM(}(-AW6-rUE0r$kP{2m1|! z{T?B|T9@O4!Dpsxr6+7Sv;=^Pxk1*a3>*I4Yu4f5^t^b*x{}Mz+WGnUOmlehxqwrS z0Lp7Wc#UEH=L86n6w=p~UR)a}#EBXvj>O+_a_C`(#1rJO#AzSBuGCYFlLwrzkuWBN zY3d@S#-Ka;68!Q%Y{N}Y%!NP`7_j=gLd{B?_NbA(fotKM(Of@B*%ttQfjuDukD8V~>pz!SX6pO;3c}UZh^mf6b*q401Cs8?*Uka`Xuc71<+@(4cgm zLx3Ai&uKc&&r{vq8;|0?Z3WU&VGzK~mJoAEq#CwA<~7wP_yp8>%CCAG=h8|?`aMnY zA7E6zFLsY0fA(q2HO7FMVF`HWi{=q*|FwP|7!wTkFH&6ue$?6_RprMe1AAvjWd6E1 z&(-Sk#n;>m7>j<3*Gq<+i@0(7iW1s;7r+^E1RuDjW-3Px4@4upEKK3r;uk$&; zOTtK`y|RI7tB$On_fYe-`VFYl5FistfK5wo?@n@mb{v-z(_@1Tomb;u{`t-Di_Qd% zEeA|Hd_!dGRJoZ8x_UmuHTNQsI`pjc6zqZwYQDC=r{rPMZ0 z+M;4X{k+Phq0%mi7;3Fa+?>7}^?P%~5bMK6@lHe$U#Y)phd=U$&1j)p1^&oR>cfWB zWycjj&+o~vW%Ux!aImwe>h$Fu;2gg6`iB8Bps`7GOM>Zlzdrfk=dN|m8RuLb@K0)a zRuBLH5b#f6p39Mdc9fA3C1x1)Nb@3xDo}GW6Gl%@83X52%iuVoLmZ;fG;kykkp%oK z+JDF*4J-~Nx~Pe>OYprzZ^MiKudm>)9S$_QG;jj(<-Y0z08}T@G4_MpK)zw&V>De0 zowH%*JPysO7K$>>xtc;lMqTX-28IH64&-&+f!^bIh)AH%KvthUFPd}JIf$}%={H4$ zq2I|F0nZQ_2)}yC7I8SsfO7z-htK_i1T3A&?22&#W8RF0ITNd#ERVkLHt{wCi0 z$;Yv;Tm%|jCY%6d66Ds5^gRPCCNPKuWHKfNA>wx)8xb@h16UBX(e)Exw9>PYa~#51 zF$c$#{M!{Vdtce>v2N;8toLqg4Onw=Xj3S2t3#>&VlfqEIuRf2^nd--@jr9 zQ1%xF{&)`kH_(p>xi^eoc3U_mplE}}m*TG7BF1_L@(%yUB|9+l^EcoxE_)zwlpfzz#4oJ-tP>4(6tF696(eKf)SQ`;9wDm zgQn~@V!Je`!K|bwDV6}x1eOF5<4Hqrh4t4gH4?c6Ye2Hp85zR<);sX(Ke`EzAGV;; z#Th4n>>Gq#_s7^cKj#Dtbx*_NxUX`~guO>SHcgdrp2i5&ShSzRH>brYGlW^&*jM+Y zeX8g6dx3%b^7eWj??raM+zW{DM#hZdrJA@U-T&K{Zij}QFAV(ooXO`A;y1;IHX(|< zzG#8QmSU%yz<>CsJ(2QH6ea%PrH_MsShI~KDK2BBa=aqG|oT`@G~8O&d-pxk2C0y zOfyCRUvvOrT|)|pz!>Z)ejY#j@dvTtpaHeU0p#9-`97PVjn|vn%kPrkv@rFr-zVoF zTzlVMJhXc>GL8~2B7a(^nVb|L z^JDmYE&yPUxw1se2y~*rUjh7uu4VPQ@W=cpe&R3h!`AL=#renMuimr^lWnzF7@!!z zC+=pTbL586jo-Ro5P;#3DI^E}lg$rpIx(yCnSlIF%@wLA7M2I4JUL0_qxI)VTptjAzRc$q>8{@S}R1jYoC->SI_NTT_6>mY_4b z96$M2k7B3)X#fS3LmM>*p`b=Z8#FqJ@9jK$tC@P@My3840yrfefLcyD0?73M0C?t_ zKVD}UpXE|y$Wr3IojL{~V0f=};n-v`1xf+JxoG^n%CNi+IA!GM3pls9j&R{_jZt1C3diE z{nph}VBZf!r8SEG`@1{PKZ>unc0I5~?EZ$C&Ft)K1e4l^?8tXa0`9|`{I zsD$z^$V-_L1uWiz5XT|$4D{?GM?eVPRytdQ`A}sXk`ax$(SKBFR8c#8v%Iax&Rdgi~*ZA9zVDSPwe*y*@LRUJ)jq; z0rPOg&}8HHwKj<>s2~Fv5Vf&kk3(Np%Vj2LT5kP$1G8=5o8%n>iobMkg_X}(dKM&{ zLZS0__{#e#EPj4m-wzbY>{PpTe)Lj&?cx2PVpN-Dh_nItaU8&ptR4(9EnKzk2wwZ= zcj2zxuLp6;@z25uviG=j>ESSUMkny)_0yQ18vlDUe_$SiGX5zAe&AU`0Ch0{0A{cK z(2eB%r+q1Y+rln#Zc<{+2N0+V*u_-h?8W~=2H8V>*abKuH8GX&;b;eEjt(TC%)ZRY zBEXunUKVvAYb9m1!GZZnY(8WW$FMe$ssP#m^zoi1gwTXAa%7O3^rWe2WBUTmgFd3^@t1K*Y-2YCG6bv^9y??Q$3e|*8>$JpowMp zFIP`JHL~-bdjX&<%lKnEolfff;boy~Pph039Do#FwrJ!t1l;7EgD)juMUzGdCy{XUEm55dDNWs8W~6uV{TL-Xbgw~QkZq4 zcwl?gGBL0LgWSM^Kz}@lpaChM0JWFkru9d$`{<}3Ir+IrkEnbTvg;9;(=$n^xdMJY z#h3P_)+dReh6q?ivvfW<6RLnmd3Dy@r~+3w8Lr$%*lT_q4{V=7vIf+i-30s#aR3$I z4~XYY7he+RMq2JYAR=PMhhMiH0D;CP@y1WwgM&|*KP}5rBip!P!;rd@Ql33ezy>a0 z&p-Xq))7OWg7f<|PJl$o#LfqQY8MoR+5fKDR>tQ&tU*JK7VI0g%0i_c*n5jri zWV%P-1TuvnA{Sr+KSFIB=}ivWuLpn~Rc-)HZH@)#4Q&9xi~y|((8zLJ_wZqS3CG z|HJEuC39xr77DE2t7FkT9>)#*m6;mq$QX>6gXz1pk43y%Gr<5^(D)J@wu|x56X*U- zadgKnB65NR901?#c5{T_v$ve|ayoGU0KiAre(>s+p}z~6Q6*!o1P8G!W|5SBkoE33 zE-I7ALfIuf=WrIa5wrbX%wp8Gmv7fMzy1P zb^&VMB-}Q&r%HcS`>!%I+IKl;!z31jP*+$&yEPs}pa$S(RG+^f@Ym~%1#BBf`zGcj zq)dcl&$wvi9B9!Jm?f+6%0Iuo%bS02opY`az!6c2v>c7kwi?QdZ$ByJq%RDzP$44b zxJW(Xa4sDm`J02Y-Isdr-{GBs0mH+?s21CY3CxaQEQMvq!64%i)|*pPUdqG9(1k1z z*)%{ERmww#YXk{F<;us(lI0}kKLc_>8$p&ZVSyJc?I7l!1JX0oLr`51B>&;@1F~z! zI3@#_c8E?KU4dI3KY(BV_|;$-9N=F7Y>{y*1YLQzq6nxC$nlL^4!HGum@9Quuaje{ z-C}6)oa@&q;rB3v6huPOkZ{fvoCMDfkL~LL7a)%J0PyGa#|!+n1sYj|J#Gx^_gQH0 zXqY|}L)AA<&4CuL#D#x&4R$}a;@4Z$=@U^|mSvxaN+K$Iy`Cb1CM1Ate^Sb`&Hu>O$L*reY|2#CxkLLvi0?SwQjhQuzru6n--{;1dXo- z#?Qoy-uF5D;$J?5?Y;M6hx@l_8Ez7%C{<7;6-W9VdJN5tG-Rvej_kg?F2ASVXC&8| zSpZs0u34Rls}pm4Ug(0Xtz3`4L&qD?m%xK?q&GIuk0%WL$m(ox`k|#~;!BUt;HiU? zl|E&`PE3qlM<;RNA6|o9JAd^3t=T=hoO4|w>dDsc^?H2(bxxp(13>Ng87t2^0;m-R zfUB;$3fKJZn{G1fKgrHP6bH2}t_4_SiaeQDrD9ZNt!V z1iBE#sczmJ6toa;KtV813;5$T0u`g!+HK>)|Nbp}^xFpBOjbdYAoGRbc?3HTy#B9S zyYByk2q5PIKJbAL#QJ}H-9Dv&5zRYnyqfkO?8ZPCcYG7>JVx#xno0g?R}7q2o^ zC8M&X4HW-abkV=qDA`{4Tz(L zAUI937%zx^x5m>l@K0D8S%uZM&LA>X+k001BW zNkln<|fB=af5PpDy4}sC(9zgU6nH`(*3CS2~5o}aV0}1dkoZ;JhA$ z!<`m39(WrL?7eqb6ivJCJ2Rvq=bST04ic0wD2j>-h!R9{QprJb7*LcPB;yQ-$Ay1MGQ?|QmtWM)<@S;CXg zOX(Zje(__ZTMDDeuVr)gg1u*8oGz-QkY>8w+4g8_PsTOZch@MIcm#=3D4(?ptGp^$ zbYUK=%5Y|Mug#Fb>^h|Me-N0MKw013=fTL_Q8+u&{GoE>Xg+hkxl_8)!0{=j7QxuKwXQCS z?W}9JSN(ML~Xy z#9^f-$Cp-gP-8pg@k7I-U+Sc1uM!#Alrz?j`^A{}5|`0My-B<5b28G>&lw?ZU%mRF zue`uEeBXjjq7x1~_Ojtt@_Mn(0e3Zi-sBspl*SKtt8+EiH-rsoKe<-Sao9H7oBG7c zsoS1x7@qT?$A|F$$R|RMAHft;V_x1AMDw|$brN{K=^=}~8jc@APp03tF;9E$waB_K zE}0_S{nC#09wQ&M_LmDG-<$_L+>dbJtx=g|{QUPf1Wtx;SSa`JKCYYzVRNT#inm@v zjBIbWp4qiEKn#se zQxd;y9(N;~WJzqq&xfqFsCa*;Lmu{~@90tPW>i{}RYmgYDtuEIvNpAQNZiMDCI)Rl zen*D-Q&^P=MYxk_IEHSYTY!gzW9FkTq@=bZ=S=&)Bn6lh{A4>uL|iMV@}9ekajORx zQ66ARssHMEq87UT$OBY~s%-cHv}9(2$r6uU%E3_kM2}>ud`?nEX4=Jefg^E>efFz4 zAIo0H+uSfD4iR9mcREBG@HsI`^O0^Fc?{8GH)=Msa_)K*y>)k-^~VNb%fr--H#|ue z6C^KsGd%DU4*8Kh^J-=4n3wqzllGhFAN@hro?8|9p8Z#PYiCWJ0^2O~=<%3c+2^6p zOEulI`0xRj7)X5PM_xGAP4z11e70m;%{Sf{rNk`sccog#lu=!p;8XF$q&PBzuY(>_hG4RAhP7`M`PfF3%3t+Ch-mEDV_1LOMq423KQ>z7O>EE$iRe95}E*RE);39o=Cj zFE?w5AFMFzY4Mp8K>2N{E(N9~ywsugbD<*%lBbWpgW?JzJ*(WAb0~&LnJ))txAD!y z?pX?hzBk$`Dfy9u__d+C8$Vx($2eoj!u(21+)rxrb;|IB4pnGIcQlqJ-PR_3U^)&v$OsquBxD z&Y66-)RQ=cNJBf+_F>DjEWRJ~VdYQScTF2zW>kD$bl*@EwB;LSe93_ZF>niY5nS~i zNq*5kM$;coTr&Gf$*w(^O##nB;S ziZ~Qyszg;Q14?%P{v({qt`k>g2GOXHIr}+k#WnbjJl$I9)g&{LloN$ zE^}XOl?TS@EGTM8VxRQ|M|?-czL?$Fe?MPEZkpoQVRUO@^>W;^oG83d+0eZ3ZKZvq zqBMNox%8Jc3dUkAVuW@k?>lBiCK8dQdyrqp6_xQb)DTnY**5N$-gzHTqItV&MdybJ zS}eS0l+1M`tV+$#Lh0_8gZqgypZD*~Ju=f&2wAA1KD5&7+A!QO*jyy`{O}E$Z#c{1 z&Eg9);GMzClBly{>hYe--wn>!U1naL{qP8+z&oK=e71H&N7~mPdGWZ2Y-h-z-(N=L zP_{y-_ryNbq*_sqc9%Ud0Y~xN0|HwLy|0PnTha}a{Qi8gV{>bV5{H4a= zoRXdX>$)&MiY86BiWOB#J%KY5lI>A3zC{$Dn6#_6lTA?46|ZkUy=JQ?{Dh<{1C`>* zhkY0xN5;%@e;e~)R+`U&;wbYNH3@I%8w#V8K^3L`x?j%@rWJkdzxgY9k_T_C{~`E6 z#>lVCSHZLz6ZpKfcE<{-)WwGL71k8()?1suj>{A)kl}FP58!Jry%8bBw*{^(79PH_ zUTpNU%{C>FH_U6TTCW+!`}z8ciXT;ghq%L3Q5|12(Ulq;{)2W}`VmF_Z&pfms^uP< zXVbVuSPH@fpO!L)&rGjyI{dm}JpWkzpv<95X-C~OZO!ofY=+0;!-W`#$5<(GNBt!t zQS80J$ z@i_}#t0y+#3o`d!{>2r9w28G)g5a_hw7sjSTO<5-wo zth~iFeCI7b-JcN_n>%5>62phg+&eNzEX`-QkR->i8eRSI=+#w83?$|`XMQ=zo>VRKLh47!KX-Gc=27u$uL1lFENFHtb{Qzr&eUo8*& z#WTVwX>fSlL?aTaHI*(+o4dFTQ@%Q5qYb@$K@W+&ZYqd}H4sak-Mw z(Tl|m$k*>9TW)K8>hr->ViZHu-p`A~esqSGe{VOWS@!p@KZ3W8xE%N?6xj)SCY+w( z+MsJz@7$HO->KSZNc!aI)8p@K6_i5<%(cy9w^{3-Ft?xG$1&jx#N zJdTUYVDIKonfy*u!26KO#6~5*B=6(Z+#%&>&b04^)C=OVZ`-7Fi0e^tk~C$#`X)|e z_t$2-+gb(-+>D9YEg$SUUwx5ZK`jtgZrn+aPg$HUwzG&$bv4owN**9{R~UNlKdouM zRvJymr)h9&3#DB!K!5c6D3ku39hnjp4P{JwVTwhLn7d2bSf z&9LV1$WrGV&-du=4w=sRI61Zb`%^1;EvFY9sc(mPS{+#3V3tMBZ>Rr|07C5*9oWj>ijcXQ~1 z-$r%Hf55~(uW8&2m>+B28N*MUaPJ`d$<=jzZ6u!gNW;Ee$6DKo%p2SdAJ_FpGai1E zQDc0|(RFv{{`y085+83%3d`7-+A8p!CRVEo^=!9yGt+W$3q5it&k-m3hS+?*F_Ip& zf?oSI_UWb9>8U1c*M)a7LhP2N!KaVK9X4@)EF*dMyhEbzLOgnYY-^{nFK2$$x_PVG zc{OIej<*9VqhgCe=E`WtaPu}PAzbPf)KfMoAF zskQF~H?{ktiFr6XX6teXAJ5l{hkS}W^(mu3QXx&-hvtf%SU~@!?oqqLr=t$B+NE2t* zJY97vpfw}1y>mJ>lB$*vs+cbW^UUjl=xXKd|$YOwjzDJ;v3>84HG zFG{rIRl%+bWRcsYd1+SPs1GWwn}5~f)Hy2GyiL8}Xk&kaZQm)KB)*-S+a@$DhluAqwqJdo`fBT2 z6|N&Ymn4O&5XiXejNW(hCi0?SmX^k!@}U>w%$!6Y;(q(3qDl@@d3QDCdQ75-8SWT) z204Frm0VQE%i+UfE}IU?x6`bqiVH-+FFuDK6QyOQqkL|~a76Y3JDnCgU7fFR;}G~$ zKn@2U#+N51r&E-1C85ao;3vDH9X4y4tFj6M@=N9G>jN?Amg4amR}Ayj?6tUYcd}U< zR}ZI)m}r_`L9N^*(nMJeRhpZ$1?WSe?+lVN{MJ=UvAvikmS22Y zJ%rfe)xwbO=`W=t8nmCOU8pWO@Z!VGS;oBGUen*)W_q{F$z~(OgA!QMO|QMNM(lLe z=9NH3NE^9;xju@>nHs#At;MhZQWZr!`Ji2p5&yGrIHql;s`f^`v)!cseDDyP;p)~- zo?JJ5Qg&u$DUtfb5{u1-Ud*C1AO7o|-JYQCk+#5^pbbwOhXbAySP>DCiw+K?w{G2H z8}=MdR{44-^gzq__$3|NjuUc&`-zjTEq_V86kh$2PUZG3dHgnIyvB(np;w3b&irt; zksX?|ZtlqtV_GLqx+ZxITY{?V#t&j&a!{Y2UKI$MDrHwW$Y2tboD)>=Yp3?2xFdV? z@mrrSAHb7f*T~bK{^Y#(PEaBJdUUjnzRUW$7}K(7_AW)^r>8HW ztfsh*9z9y}exoz&8(IGLeIl97XIItgCq@IU*zad-n3NM`J?%0Dlk8%)R+#_{)U&@X>|-e02s_!{=|F*NC04k9THqLza!zM39%Ypx$6MAuH+dd<%j z3?4&mr(NHvFS=`K8fyDQ_(O?vtWfjB^9SWQ*h3``?6s>}uqN0J&2L{>`^E38i1Ffd?K&3 z*zm3ST}in^EP9_72WB_#Yrp+9;+x~eJs+GNxv4!dIXU@bu^qD@JEl4Jd^#5O;O&Rd zdu}&qdArA-#1NIpXE!yeuJSjLvHnc7toiw)!)9R zIK?ndp+*s#)uVU+3tQ?}`$!chPP*`ndC4aMQb(Dpu4DF#*e2Of8xHa&$1uFo(3R^O zWxMXXSotZ7k(fl5>phzvjRtz;J!+kUQ=3?cYB^x|#TRa;7*V&4w6*q`wtUf|N}*-R zsHgM>s+yYfvRoxzOo8Vy?P+hw(Ok4!n#&)TmlZ`sMZ5ZW!btcgzL+Gd4BPIE@*9S19Gqm;9m$E#%*je~L zP-fYOU~o#+o3_b~P7RNeC)2G?kh#i$A3{~iSbb`uUxufTuuMd*K6}lE=QpBywau9t zbHwzndFqVdBe(wMGs0;X%0;!MT3H17UtYVH!($rSwqJQg?hz9c9TOW9Te%pi_7kgk zs>kWkPlbeT2_9xIF6@ZEwtQk$z=bjGiXoYuX<7<4kE&HDlgt2{V{@HWqAUD>4v(y% z-UX+I5wEJXcA2;B>B5VWQ6;n92Pn2hIeJ3XY&ylIiaF3+Rf+*>$E>VE!7C%w*sot6 z7~E%9);XW-fHoclg*u+LRo9$Q@T&BKW^Hy)`gS?1SAs#>&EQBO5z4q<4)ftTbEY}Y zwG9koU*ycnyQX;jQEvW>^8Lmi#yJG|C}XA2r^+9@1=*-yv^z7q!E*P`hd}KmH}QR& z4=+FN`XXAXRa~?EvP2DUn(L)sINA5*9qtgt71UtSr$eo*?83%;mDNj|MM5%SW>sq8 z0Tz?ySO%FVv4>A|sdv#!TsR#m{1o|eP%Wmla`ZU6;+e*uXcdeaeUJ%8+!NDv)v&eG zq1aW?jMv0zRlO!6>}F<5cSbu#lfFqZPFe8cRs!q7mF^tuO%>s9-s7mO6O3DyRlIbx zU>*56JI7{x@NO}eRq&rDM=@u2bhy|}UFFSfn|1VA7cLv^y?aH8&&QI6KFlJXh(Xq4 zM~eh2kJ@@HntaK6BYKLcRU?wr{{oJiex1Q>=k25SLDg3emxXZFU!?3DPJB{)^XVI` zfb6T{Txb3BM`y0#?)7s}TVNQfCr?W?ZCiF&)L0Z*-l>%gWbd7C{#^NSJxps(<6V4{k?(o^t-GH0c2M!@waVxNi9s&n2bDh5 zyy&0%{=LM05(7T+Jbby10(Do6!i?$2tEPz;RUVG5W1rQOHqHwqs}VoZL;aAtNJSM` zQ?o_=2NzZQ*I#E_MYC8@mtz9Um`6r0 zA34==Zf)dyiT2ZT-^-uN#C4H{dr{Sz>YRJ|dO&sZsTb)dTy)$^r_2vB+vh9$w)r&d ziw-Y;&@;l=|LSTwRKs%M6cgSeB)yl*w@793(6L5`m_@3_TB@~FY0{`C!c*_V9B4yF zXGi=F*LE?q;sEv5v%O;xn1h3<~k@2l~kSgms&P)`ngwd z-KU;jO*Ya*2eqafT7Ivqew`47qeVR?C!dg47xTCK;dYKbUcKiVn;KaR&V>9xlULwi zAnJOy(hc9xM`|Hkaqk=a6GZprdePu}2Y-6i*|#jG)7+mMdY?|he65&T$Gw>kFEXZK zbWq(AOU{ORJLl8%*_BZtJETJon?oz7z}%vAGHy0_@%~QlXw8R;kS;y{gZ`#3T?1=W z53*5oX>}(`ZgJJpmPJR!kOytHX@+NBHN+to$1B3FVaw4U=wH5P*nU=V(Atcogt?K% zjH$YU#fr+Ap6jePxo6xw?QBc+c2>R~v*oeb8ccoG>mLeII#l(gR~4Rf)t%e#JBk{3 zc0Qj@>kzkp5zQR-33;@bO4f$h^EG}x?5_((*H}Gbzr@)EC6idNQw^Lp*~K@um7vrQ zR+H~^k!qQ~&-QP8zcisddsX?zCmmqo*(vYPwJW#YFuH!k-p_2kve}}bn>I$(mCBRqjrYw zdMmRdW2k6MH^+N{r`z4p;d-Bw?@#33Yl>N252A9imG}0Wu~okPg^z1!TmtJX`v%u4{CWcLigS9+&LI^`_^b`-g)k zw|s+oG<GO*tY_K7pFM`i-&BHtXM@{^7PZxk|W z@x_^Sj5?w&)}1Fs9Y4!IP4UF1$JVgIoU{Fm#b#RZb`xtyQhIqSM)KA2i11*>67f4? zzZ=S}lcQtyy*!xhq#x7;ZJ=W3=uk-r8C{kNewsJ-f*P<89(Ccxj>%KQq+T zqCUwejyjzrWqKX1(dqS+R$PM*U5F36wKczgcyRQLn&;Av&4?{(Hm3~b*fDC{sgXw$ z&|kelnL5RQdHGrh8LE%Z9<%UB>PK(hPZ z9<#lnSB^|XRGt&MOVYgfKw^NIo^SMrO{eUjrlzz$Ga1EaVtOVUDY5c8@Cj8m#>Wv; z9m_%O-_KQjc{gPqW@ATAkJI>$?&L}%T8mwiY(|H!-lIt~By-DVEJDPs&xP*LMDU!08^Mxa|itl!b8y z_NSY7s<-V;(>5sD{4oP}CEHI$se2jljVilbRN?tJM|mOe{5Nk_bY9`~d4G#|eGXmI zH0O%Axg0OK3R;Cnt|8tHpPPT4UmG`e9iOt?^bGCZNM|sh?DL>MEy+}zN%WgDY+PeljoTYq77_N z!|ADe=kIiJ&$O-G@uJb5Ao?VF;zgg(l~bDP#&5a3Xw>68Ke|&CGh+M>*z^0$HRc!A zTJh~#TjhAkc6X|hO>t#k@pZWk_?F)8qmYl>OdpS3lCh&R?zCS#7oE> zBw9Lb>trWg0-3UTf}f6rEkVYg5N zJe4LyC2Zh-I38Vp!IJw*;a2ela{C(b#&rjV#LX9`MW}6T+)`%-uAgDWSn!H^pliFP z(bQYj7~(P2@Dc$^J|Z9eqbuSVX@#Qnyrtt*Sf!xCRX+0f^f=>#AwiA-EP?YizQX~| z0m}w~^UA#7S8C3vX`mBhO}F_Ei-oiq#*?IGVnhMtXGN2?Xg2RtJQ=2Drr(!%m5CkeACfY2gq{_xe&*-x z$A+uuW$*ThpxyI2M>$Nm$zs01UzF_65rNR_CG9Hgf1JzNq*pn8S z^tmfD%5Ph2olnICg1?t6FlEJm+%+9StFhAKd3YOkc&}nRF&E$cc&k(JbfBV6%E6WB zqVDGsIfvLxolO7O$nMCCIRhcDR4bPHLo7mH*z5L>AWg4CN0IXjv;3y_V$rKAOZQj{ z0=t>J<~x{kLf?{$Wgv&t@cWfeE&Hp-i3(=uiLcQ$i%F+NJ>GQ6{&a-(X(Qg2>P+rE zCu%Qhdb{%>snfUA;)RxsOZ>En(d;TUD-uu0@UNSQzi&*c%PM;jAIuM+;N<;i#na{$ zv(|pLlWc541$=A3CUfUfaPs*44K36aOB8SgDo*YII72KF3Zavs&7$s8@#$xH^a0C=8Bn2`N<#6LqGB zb*y2DU_`XD| zIJsBbkF1k$qCH^&Tpt57Xyf({*&ZR8Md^FmJ+K8g(>guL-u8M5T46d^0wXE?- zgqOBZS|iGOEBFvyPWEPPeUh0K>7=7$jnfj1vqeWL&&CLgX7EKG{*%ffc|R^L4pgB- zPA7#q-0Q2`3`ozxdieLFz)Nm+^hB~tlQNA{f)>-}MXQK0QmOrEv zI8kYbG$h9@dR$s6yvSr-s#}$Jd(v}OYC}D`nnHeX`)1`gt=(yyd-^=_;v-@*mc!)V z)zzrVdSt>(CPI!*U{Ia1N22wFW@h+RzY_PO&ZLX4$d}=aObxe5#*B#XaHAP+WK*fG z&ot$$Zn47HN^m+M}_UG7(ggLUYAl!p->e;2#D_>WU zdS91b4^HE1vKC!w`}4OSN3$86Ga4tGTKsw9TnN8qYG|*Qg=g8>4^g(M&QT0f&r*&Y z|MX;|E0y$wG`XqTIbD>3BPr3WcRnSl?!Ft-MXnnyA$Nup=y;AVC(&NVcBc?k zyCizPAad8hC5L9t(mq4G+qmmKm)dO@;ObN|+Q3Vpme{`f6e^R+Qt~^$Qh7MyIIJ_g zm3(k5!DoJVx>rUIS;&5r+cs==Uo&)Rcp=@`x0iP-$1XY5UdCoefBT19=JtK#F!XUM zqXVeRV-)5PaCJ+iv8f!b0@~)8x~w3!%WAQ)#t-DyChjvJ3kKbxuIW@ZAo{s zk>eV8(BSJvCB>ie3Wtjajm;#ZMKHQ_{n529A6;!pHY+g8Uehv^T_JRo8U6P^{M@Oq zbe%u-(_v{Y*LZ0(w>5CRr^0=C;dFhdd+*4)gfZrFtR=p!Z}r9xBhz62ui+25>^a@o ztCU&yb>7c=cQ*On`R|4U^gzm5Ud!EU#R;d!Z&l?*R3RIRAuDTBB@F)GVw8?V+VjQo z+2Dfh^TJlSPT{;TFXK1tc@wBuUWY~#H)Hv}UHU|aE2@k^of*Mvkc<@*89J8X^ip5w zud&Tu>SVgYka4y38r?&s71IF8!e(n#eU0a4*XeBDYrc=lM?y3M*Pn261^bY7_*Li6 zg)~#UU)N00wzA)@J#DnzXt{#oQ&iijoG)+c8`?dq@Va&vb$QS{`T94(hxryAkM7S~ zjBUADPK<`ENN!K@4t#y~%j|81AGs$+gUHoXXp1Q)tIll+;~iCn3Hh=-nzqB+OvTp7 zkMBPaEL76tVvYOZQP9phMjzgwgg;EOidwH3W5IL3JxuZ@mwD~tzO~EDrH@F1&+k^Oe_###aMg1D%R-k14UO;pmNi$w-b2T+weNfS3(L?S6uwK$u4bQ2*!APq z^he8K{CSpm<3k@mdt;~;ye%KP{i1hauEVnX>-;Fwk6rdA@ z@g;RJw~&(EZ>J?cUen*{3`w&$jnwJ*NUcNb=6HivqpRynu?J!$I?Ek&b8c?FJOo!j z9^!Usf$o`oNJ`I!{D5VfT1?gH5Q?hPBV!xCE`@BbGmY-dGbJE_SyN5)Ii`Mi+*!5# zrKyD!L~4{d0;pkdwUQ3uR4rjmJW%DhlZwSpo<}ha<3ot6lZ3|KljCRF>Afnv3-SG3s1-@os`VxBkDqp#7TA|LNb%3r ztILcF7GE*or@hswj|r0wd!kn}RkygL-Px0`qCZNu)1!FJW(S0@SzT+P}&gRna^%^>#>gdewBgc+fJcH&T415q!GAkm1;~RQk^ML+Fwg zXBNf>^W4LBz52nXgRFYZUT4?vTUjsVTCOm@&VALCW|D-H+`J#63f{0aDG-P_*W=$` zKy#$LQ>glJbUAnr)+!a3)6JH7wvRyu{avCe%t$eO^Zdsq3Vkz8AH5}$)}O}Cz2tg5 zK^<&#!tT*Rd1(5Jn77!g2~8HCJ%SH)T7)wk`tdnr;B}eP{FAk2X)A1}j-ahDD!1U( zhehpgVb4q*%BKT^lIxlq5oV6hr@_sN^UI#lAbhdCtghJz_;&nXXk^`7$O z-`R)%a4_<9`cRd_+PkHfGbfYGhl3VZtIR92P;c+ix;PaH6)3BuE1kqJmPuW%N%8|{#rL5GJ-DnAQ)Ofv2L8vLD88l5#Jr-Nb)Tf!=Hhsu zTZmlJ-SMsV&WESEXlRcQ)Nb2e;!kbt`3IxbTJb&Vk6J5k)eD$5@^?N{nDCNKouAa*0^N+U0AC>0a z$54{NsH#m09um#lu^nUNsqBgAXB~|qq*_+&Ev`R7xVDxI$qRlOu&EIJ5VHCk=2Lbt=-+pQP^e zSzv8+t0)u8`&!ACI3(|W`?ciza3q{N`-rmpQbq<-d2B>-$sn&;K>fXETwys^D^wYY z8`O2$+(@4WUY;_wAuR~A=w*M2Qz-5nbC^HWtKM05$MN+tesmHYd%AK{&guhG`xU+E z0JCA*GsOwmuu0p-`U;H(M?NCv#uq2b%m!Q>OIlXKKAtM=F!{cCWr0Gc-L+qLq3&1k zGrQ4NmeaSh7HSyoG%XyxuKTf}{(Lt}_LgF}6|1?FxagKjj(!Zas*yQo3%3(;f`?+2 z3GGd>{Udj9fX?_$s)Fmj-fgLLFEiJkD;Ic=q<6Vi zuElnDeW9GIWv|l3b$2@ByD_&wA8Mm-=?vPL^wwm`7v%%~9V2oX7P_`j#_L zYVJg*s{J-C8asKyaw$&_GaXy`G@`ol$-+!}?HGA{yf1fwR8DP~)`GCTA`h-mLFn#+ zNL!k31ZhC|l{|YlN?yDOzigyvksE%wKXXy6QLo@QP1aMeQi|<*2IeZ-Fps^Pa%NO5 zl)rfRRUW)eby0*#z1I5mB_e;$0>d#^-PskA_Mgg+E$+Rvd39;V#>LZx2bVZ{zXR{R zpGN8c#Q{oR^ET0R4}>hhIzjJ_Vd&lLx#KGP4NZ)@x{hr z6vY9Dp`5k!*G6sNPdq%HM+ZE9L-MwlYi55Ljka&~yQy!3PW^YNZ#pfZ@O6IZxXCD~ zp};GfB^AA9vIY`WE+5q>TnDZkkY2f%g6qpoh>z!mF{@gi`$L2^VSc`KeJ!45UEi0wq8`nE3o(6k_Ug#N#IsLv*TsQ3T=Fnp^V>1%*wdt^2av_pV zX{!Jht|*sm`}x|<=Fxp8P{AHG;_T)O`WJ4+B!->lVy^1{SWRLZTtF^AcjGudef3a% zGcRMai1_^DZgs8uajS0pQNC_($BS`}J@-aQC5Hn>Bhv0f@~s4~)b#})zgKo@xG5mY zai_|x@-q=iy5ENgTa;itHho8pY=AM68)W7ulLzYYJeMXGwpJyZS4VyBx1ipVJ@}|Z z62i$k#pu^s>alvIqXk#-)k&~a7v16f{>>(9GpqUwKK5KPS#5?7XN~J_oPNGoy7gj3 z%~ZWN>p;hoCnH6aA?%c zO7Ow@zFP|@e6QRH$>E5~R-AEmCD)@Pn=$IWAN#X!rVJ0PxJWlJ*kylnJJ^tR=hJSb zMvKt?K@FalRxz4Pv)PXy(QOo{qZnH@h@$xRKWjSOl_u(~=Dvb%t3UpZR(z;^v+uU_ z*kaup$&Nm^o?cYvQsZW9X&nCJ^MgG*{3aVp>~UnM^8A&-b?w56);X@=ihz5FblcmE zlV|3(!hK3oJ0dANj{iDlK4UodI@iLp$byXJjQl=uo9g?;iSO#sboJh$;E}?eRrELe z&$Cz7W{$IZ1n?Xhm0RYSI*0F28L&V7#w>(|AIIwuziby4K= za@ozZ;c4@6(u@wdy|iOQzH90ruBZ##ThvL^estM4>qQxrJ+(~9)_Wqu48R}eo@Th1 zj6+nvHuu!JewnEamKuKBRJGJzxzyZ!0CB^PIr5P*sQ>(EF_keS9;6-|`S8u><()C) z;i`cM+HK;ft6k7y=aA33dEdB`eU4Ak*(2O9q4gEbTi3m*L6?SEGU zVDSG0j^+P~^6wS-Gfw|29%KHBHKF`p(dYkHef;}~{GYYwzZy5F6#*hphky^R{g)vD zZU2PyZ}$CXeG<+~C<){F&q_jDLJ8aMt^axK-^BwQ@_*F!cXQmkj}Q|Wr@eX*^n<#Q z?E!oh3TiLp;4uLTP$xhM>JX~GO2Xs4C7~@W34IXy-g{0$9ggKs_Kp+Q_dZW}ZtuKc zo!|*r66U`*Cb0hBefx7hgvWc=#NN0NVn>KI)PbN6K{INwj{q&GL%>&C{e~Wt40~V% zbqLcQm_Z#v*iUE!kAufU2>rpn!DmN*LpUe&(q2Ci#`kySUOy4$^rtrn^C$R#5Tm~< z!KweHx4#=RVIRTcd)N4XrZHO0oeb76O`a9bWq%X!3#>h zJ@A7%0k|L5VF}xT4~PHTaqxK9520Up4tOqjUV@kY&Nq9#1INABGtj>Tk3e7S^#hE< ze`g<{!TA3(2OU!X@Ia9kV!I7a9bIA(%Z z;2dCXfgXnW1kRC=Kj6Igdg1SU0Au%OY{9O7sqN44!N+jikU!Tu)SZym37W&W!1%ye z!MMQ~!dSxC!dSzY6Lf~UKz*TJP&;{mB0veC0yqFr18DvQ4wMkBJsbjc0)+jry*dCM zdk_Hof&IaL_X89F&@<301fRfh!0`|~A^_@etb71CZh|-V`eLsi2svV}58&D$_<(Rt zz<_<|{{XQ2f9v*l+%)Sa-_q5t9B_i7GvEL=k{55pKjy+r{q#xUMcYpAm_ z0O|vE(gHvoj{tN4Cjch_rvV0kVF=2zfO7z&zt~%c``}~P4j!Wq&;y(T=mKD$M*;90 z@O(G`!6(ooa6Hf-(1&o0(3dcWz&r$f0p}qA-~+(i1?LFo$_#*BfH8ovfH8qSfNKNB z3dW51F9<$BgU5dYcKvVN{v6Lyf#n*$N~S# z9{5vp!ea~=H(}jF?cp4-0H{5j8}vE@V6X1{;IS}3901pj0syWls5y-3Q2^8%u1%;l z)Bwp`8C_oG#4iFDW1SI{%O;Fy%U@%DlSia1x*cfL?&}g7bs(gg$`t zhQ8+mZ~^|#12BHj18^-71Bd|siu%JbLEsqwqje9phjWB;lB4At;N8h=?G!{wfPV zNoWJx=K$cbun*W5>=SwhdIb6do)`KIjsxZuf*;&K{W8EFU=6qcFaewaoCX{N90I_3 zDgofU;rwA7U_4-4;M#z3q61I^C;%{jLJz>$5}mZXv#OHvB@xC6KaC<6TwypaLwDFEm>m?t6uaD334&4+w)g%!e>9vH)Pbpa-Y`&;w9^7*~S+Fy4P^0CxS;w!P-yA0DyCXbK?WRxkBF));-kzDBu(TY7gTD*SrTH2ms>?*LD`5 z5O4>?{_bBueTpGiEO8k#1Mz)99+KvhIMP1n^Vm-j?&Lo*!zh-EBB|DjW2rXE5~;VU zl4*8oQfYQ;(`XP#O&ZN^bqdXH#Z8*+(s-)PJJD3@c@Y#VHv=ga{2j347JAq>DzcA%A)R?E0r{d$ou4z4}9sVgYc>Fn_^0z|Oh{b@Gg6<$@|%WiR%9=P{q>p5 zzuUp~@L1SKO$rTCl|;3BH->UE3s1h}YfCnEQiHUIo1LVZnv&!e@Ip2KULz6#(SQgr zUOWJf-x~mP81#Sz0OkQbz%hUp0KQgG1i)M<3g8F8bpo#gFbB{AU=ASY57*a!lmoz1 z|CH_T^oL`D5cG#@A9|M_0M|YT0OmKs+J|{x6@UZ4Sisx}*M}A0GQbCL4aD*$(7O;& z3F=Km7)%GPDDQibvnS>+T)(nc)ek@`#y zq%n&VY0BnCnsRuM=3KttwB+$4t$6~!fpy3~p5y&T+iWhRA&Ud4&tOOD(pf>DOh|1C z15$mH4ygj?sfedUZe7Q2#(R^^S)3w$!Oul06hTnfD^zD z0COA6aVG)L_t5|FIsn%LTo(cWxK7vrP=C07;5vfq39f$_-+wjzVJ?DWg5HE3Dke zPI#Cak`(|n2&Y0yqv(*zct)f)nH8x^1)8OEBaNASNK=*o(wr@XwB(8aokam+NL#@^ zq`g4mFC;-p;2-4?>3d6}ZZ#a`q>ETG**_cSONJxmIfETWUu?GX7m*M?@BVcb1&;gI(wNC{=xDOBl zb+}GozGMRI)gQ+7zk~kJt5AQKyP$_*E`$0*Z;Jt>0s8^a`%r(l7T}sN2eEPhcmhH} zPEDkuAS#v>A$f63hjBqOqkUJSVjek^{{jhjrbn{Me<1JEd_w$AAo(}xefqq*H$3&``FezNzl(e(5EQU zm>~rE=La6(MeZkaA~i|uNL4&DQXWH(ltxk`#Ss+9?QraFsxR51;~CQD0$il&WTYga zzz2R{KJI`^AdgvrdBQvg^Bv54H~`Fp1pQ%tByy&P&qHuB~%|6D&^M2-3yGJwY5&7aZMB>&OqIh=^F}fazWcn~5 zxACk%e;%YZNeJj71~l3Sw3h~JR|c$Opkp4;GhY$uDAVnad#svch0b5nlzSoUSN`1&w*AMkunz^$ zpFGl>C5JR+$bfT5A@!*e$b)1tOlM?7jZ~x&Gn(-@m*5gBYlR zSZI@ylIl`WV9&`2Vgr4Q$=)Ph#%?7!ks}G0D3Dk?O2jxS3X#6G@^AXf+To9><7DsC0C6T(D(nv$9EYg@Ji!`MJ%`yncfjaO-ru_daupK1s4Zzm`@P1eV0P{cLHQ=Aue|T^8uXz3c zQ-64!`t$zp-|qi`?7@#xkt1d|jd5{Pol%QT4FY%k|7%`+G zN*Ihm5Gjq|1>@mFZilcT`Tk5ut`9wu=}C*Ex=zK^vb-$`-=x;xP#X)g2tMkLvZ35j)JLJr|0!TMkLeeIXHy@p8M zSw~dLmXJ_a6(q@l8Odw-un(4s0*5UB>#M1gBWB>x`3 zwL6jzsk*_tXCL8tfxp^=W1_(EzY#)eqD4R*5G@Moz-u7qK&l{7;z-2}QKbC3Fmmsj zAW{;}kK6&*rlLR&B;S`6$@XLdbD~3%E>a`0HWbKJGwkwtO>7A}Bl$Tj7OMq3paRx} zEXV~SAWrc955E3`ar@6+|Nr?q95nx@ZF|rCr~WWc?bZMHYXG1-H_%;>ii&C(}e}e_hLhe{MeB@0UStiFt{Fva3J?WIgxvKP!b4b z2lYLKvi4zBUxy#D}rto)h)@R}e}c1;K%{F{4Mg^<#4 zA*3Ws0J$5=2RzAx6!>u=Io|AGUd+hNOY}g08sx?WN(66&-Er0-dn3d_;XqD~Jq+Tb z43GwEL;%DOz6V6mALdecO@(oVud(5^7RG$92C)9m-1eS(ul|Jh0N{9kUjsmHW1yg* zV5Ov_F8kW4prB+G*XlpIK^6B`oa zz=7z*04WCLogjYXHpoc@zC1{-Hy6lL?8wcFEJ%zM0}^3M zjf5CdB7P^ai0cv3S#?ot6eak95O80h0&>AV0L%gKbu-MLFpmE!ul>MKes6pAhjr)y z=u61IT?4>8sA*|w>FMa`m^nGvxs=73wS7;LzYaf3zI**V$c0uQC)zV1DVN}y=RmSO zxInJtLNeW<{_IGkBR`^*)%_3sq4ve=h)l^pP%K+Vv`arA+6e{75hqh5*&?32RbV!~DJ(BPFKbSiU_$aUJe=k9TLyMN`cKhpgw_7iz0xect zLfjML2JwWDgb>_aLm)tq1b26LcPUQsLV+Tc^MB5rnK13rpLDz3e?FhXJ4t53y!Sch zd(J)gzVB3)i8hO`Y`*{5&#b@8)8FxYeeR@0j>hn(q$)DWX314$vRXCwhgx zBK7kQ8t!jfxxZWZP~Ii}-@`tb^-w;W|4^FG`by%QYDq@x($d|njP&;*rv#Rhk)h>f zOk@Rop@QT@SCDbkmH(6F@SNP3ijv#GV&n92$4bV=b2R>hPmPbGzDH;B)Z(k)PgU`i zYWPVFnV9^X)n4G!ww~wQYN2n>alSRt{;I_OGm_G>qQrk$PNM6Vk%)SZ682fCA6va$ zYI%)k$~vQ$A84+`7j*8VeRS1R9oPRKw4b+OzU=wyV_OYalK1ZT!V52y;da#J-+TA% zX9F743`lEH=4#h=<)o)aCF$q)tPBpSPTnRjMx*x~E$nj{r*W}0`B`-t7FvbpJtM&> z5%SLT`|^$*_xFhX_hJA3Y-|-pAjje)T zR5dm}p4u;#ycSbKMxgxz0;@`Q&ngn;R70A#`?~}+s32i=%Sz~{r6uHJM+yG8)Q`^Z zmfBpaO1XCA%eSx>@r7rp3$>p5|DC_lfC?2Vlz_YA`|rPBrok8WDuy+AKB#lc(l@f& z!rmSBzE<{Ou#bX$hvyliT9O<09Aj5Qa;W1*M^=@gL6xPCS9ysJZzLbgd|-)rd+u`{ z%7<9(c@L$*+{@Bx$rWkQcZftcE+?Iv;{#6kfO}~f;8RA11ki_RUsi@wdyl{$Mg;4z ztc+COuzqiQJTioz8{>EMA4Y}ZGw8@@{Kw)$>MLkUg!)Q(8B0w+7LCav->8j?u8cq6 z2OXXz|5TwKsUo8ys>#rhs>bh=yoIqoj_18)??)MREVQtOR|857a67 zllyz6w!K)bLaT}uE56Qn)}Usth!5Cuz%TXR5{>_vf9*BE0lhE&;fEiVY|)}cDel)$ zu0yLo`=_@oeY2}mIq79(KSbj`j2JiUHQwPKR})>Wj>cAFyqGIRJR?H_D@q@aauONY zOg_|Dw~Ke>{^48;`;X>7k~#|>$;S&HN#jM=rAe5pbZF>^50t_O9HkFF(BG%D4Du^2 zLz!y~3&ao97wl|U`!eQxa}44!eq!;JCw?+YeIca0#UH}dA1r#I{M8@yX!C&_dKYSA znbVA+<{uvV40TCm>E&5Tk^(=FIy3Ldd*rh^oYNO`@5+}m&d8Ue=8JpXXN(SneNyTt z?++Zeyi&7LBWmWCsh_G)N0q||bPk|7Kz+dG2mcxJmjAVgKl9gOd?rQl=FOXzh>Qp; zo$U5^=kyk3u4SY7%D%r}6&Vs-9UUa!lM8b?)HGT@KCTArt6^1%n`arbXJlkpMHw7W zUV3|!m9XfRQXAf?^B??z`$urE19xTr@uEjkcgZ7Zx#Y664r(b0jY>*-i&E09oul-2 z!w0+f4Zy1SMa-+;REVZk@1j@5eB)U+JypmFs&brb(j%1apeAo8n zT=kddtJL8mE}n<~v((eJxqxjhs6Jq0@SpO7V#?m%-`^oCE6X9POP7+Jyg#g)-m=Wa zu1?f??t1Re8ulY2YLMGu-?65Nb&YvtpWCsD$(`e3Dx;3Z(^Q!x99%ZGU9(Z z2LHw@?~8L{q;#lPLJ}L5pe8IOU7bovFIRlP(-9xQ4}7T)?fgJ%1e;Ho$HosVzG3qP ziyxFRdZ1cRqzBZO7XHdyYXXZNXuUa-oH5v+xhYzf>|RS66`nS^Olw%3$JqP8JNUpC zvu=u$b8U&NS4LvKaKuXe(B_@8!>d(&_HXcinK^;>(sgZE{lHci{zrWvJ3HGUFE6k7 z%)G(XGuu{Lk=3TmPd$~rPh}b0zKV>Xk2^;DJ2BNvO{7{sK9)WW_3{L4yw1~gzC}H) zaicLanA%s{Z|Xcm&!v4tQ>i=mw#j8$&)9rG=d?B*(7K>;@9`4xd0FXDzcfaF;PaCA z8~m+oYQ+lW--iF|+@($F zA9}czlfFI`Wl;NPO>cHIHC+z9-JFik68msBMqj3*&Y3E4tSA$dKXaM-rDb?9K7ftDMuwE({o(`g zR^}S}Iry;74Rp>hJg6L6qgq%-ytC8g;{}#nrnStk`M{@nN5rkk^Yn$#fClOVrKkx? zUVZJk@;=P1Ytvi#hqX_vKA<|FbA$hg1}OiB5AGKqk*8@=Evf-@rryF=_Ri9D>IHIB(+%y$#N=5O;VD4TnhfB;9u%z z+>H(dq5)PL+TIcNYRcbY7XGDW1ne!n1^9=N1FZZ_?l=6)Q#-)lN6)2KDt*hT_@1G z(53-A{A0e9J?3z1$CB4FTREQ1b}mETg`PgW_+cT`@e$PX^y7>AuyD^)=GerziZV%$ zu@#LEOwc|Xv9Ebl$83NX>tyXpOH#X<(wP39)^_$WuR8w)I-&lG(b1^l&LDeZ5K&m#~Nbkbr--p@F5)eKkjP|A`I9 z2Sx;2%o6+7__yb;`Ct1<(5$n;Z~B9T3r|lC;U+ zE3Y{_?JQfSbW=3oBWl4nAgybw+9T9C!5^jpR`$hiTsl>@t4rm=ET>ZBe0r~b)NaAl z@DY`naj*QNzbGY*hpv`SCm#Rd#lO_Z zW{sd8KJXSj0o^aG`vm{c9H9KafBev4LPiVUthS{db|+W&^(jZaSAl*y>>Y8+Vw0sm!rvaBsHlu{4K$^X=bD+g*>vH!Xy@KndyYto%z-|65{w7_5iG+TKPe zYs0)0M=V6m#@^A&zO5{8vo~m`sQ+>d$Ce-zfPkQEv zbK{yi55xybiobhZ`C$5Y@^Sj$i&ZL>Zix@PO&-v-LR}})H3IDoX+PlK{-ylU{pY*1 zE_Je-v({8)WFT|)VZrps&^}#n(KQr{*29~1wR|^Zn%FhEp}i#zn7L+>)qdrZ@+Gm z)?DWSB~1-z;ZIz{ek5#1!g(Z|&Eul)9hr|}p%xphG0)@CVUFZ`xQCa6szM$p zDZx#u$a`at%g6Z_eyaP?3nR;vF8vlhp!YPlX^3scKcAKR?7VLc|oavaOiFgM)Gz`YFf*RsrE)XHJHPNe-}e44(q zSK&INUs}7`eBfs~VDka$!J>HqIiL>T*B>@Vd|)5r^rCz+@q)a^Ug*YQ{?}fuS*9WD zL$#O-=sZyM{|||O6Z_j2zn~ z^ZOi*IZ+zx7UtuWxt=e~)h6Kgw%7|NPMhe2J9AM2dusXjn0pleyWwxPXl z;{)UX?G1c9`+>CVhwoCqdMCz9?Qvg8!?9a`^7y#)#7Y&**CYq%Ub{c6{`>yXgAzl+ z-pI*rSDbpkqzuqJN*w7p>bU6nPmHTTjwj|->y5?^ z>G^5i&t`u>v&3L=T4Q0aHJ);BOigdt+s8Y(y~U2lPvx%6+rnJAJ0195+8z8}Jhxww zjy`X|pZcF3z!3Or4L1h;HXMoFamM&Q=82h@?f_@2jpsPNBb>1bR?CZ3f45l$@*Xt} z`A^pcRM&cj{zaOxHlg#DA|GH*qx}qByD+)H>I1)=7ivA&WZX7MC3kghRYKx?-jsUz z=OxDbAK$$7a)sKgyZxa(pkq50z0uXF_%}V=ic4Sb63q2Wn)+x=L@CCtH1m3{fr#h& z9M$?{;yxAjYV5T#2LEZPRV5$(6WK?h>t@4)D!`V@b}_%;I8;3xk;9@Bk!A@4*6=7 z_G`%5x#-md_)WlXe`fkKtL5qMc&tZzo5;DUO-f*1gU?UGH1BC|OV@^nQv(d3Hg+F5 z-ppBy4^Ypl4(PnL$Om*@Xg?>g)dKKuFkzphu!i0R?ds}WQi9TAq+`%W64}sk;>#~S zul)azxnE|R5)MNn-Wc7@*@2#RabmwX<5+?-Ods6;H5mrlMfOS^@~q%rIp5%-N@-vsteVc!h)&0*gH_AO!G z8m(`G*0)9L+rs{zxGQta74EJ_z85#lyKr{W9%ep$T?{pYx4n9 z3#boJ4{DF|XT1Sy9?*4y&!(J_B)1yUjk!yA;;xHtb?NN)l4Q1Vxb^wFwf+YGKcxSg z+1laduI-9n>*-#c{(f=xSrum-OW@}v(Yw;bb2*uu$aPT3m0e(%v?m8g-+OH^VzX}*^JxAt$T@tR=2WZ%l3y>ch!+Y$55=)5yA??Sx0 z!rcw-%G~|v55|h0w~%~zcHUU6!t;*2A) zA6tg=M)zSq-O65h+uO|a>U>`v4X8%^S0({9@0eh-WvDKsPjzBGp92# zPp)r6+_xp}onYTiYkJtbz}*$@Zg6*pySv6Z+&y8g<^^{zxT|@eL<3GdkltyZFh^lu zXgsX&!>Ot4?@i_UE#8+Nr;?v+M}2HF^Lu@cc^*2(QBA+M9jCJAIA70=wau~YWf{g^ zmR=qe#5He*$z$5H(z-@{;HkBMeIBrCK%?=yB#VBi=GD3~5j7hdH33CCGKeemvJ?Csm}ztISwDTNLjzGpb9G zrPq)d?4vGBt6}B;Ec`1=eEZkY{-e^Av1y9tH$(Hahu;$RCgx#pxTE()+=+Qt*t^-W z_c;25y^XsvSMxb#G5?c~q)+eGyk9sav$wkw?0G*0ysxJkd%p{QW;~DQPq&(RjlN#L z*T01?Uwfx1s9lvW!aNi#IRxs`n@ z*taI%w|*LX`_0DK`ZOYDYr>mr zKGL}!B%_whGv}-^yhZQ^9s{z8Y}3muT``7OriCh)CZLR6!=dt+E$r;f0e~! zP%mk>{h{bNsb6beYiP>@%n5#$2khnpw*GK4_-C{%A>F9|dXfV)27BWJ>I>ani%I9! z#Wp`v&a4CdTYMm^?VlXFIv1O)|6hM!jeQ4dJ_l;&lIVVE_R5#%S_^bv*{74|VLwyZ zD|2ji)^o<@WO2Q8CfD3Bj?>sXKQW#?;IV%Z_eDpfDeRjO8!gcNmehJS_Nw_#_Hl1x zZ`1l;u=laDx99Hn6?y;g6&W_TolMMrhif=ql){V`WJcx-GPBEzGK=fA>}__}m&91s zOXlY@yI7t(gVz+|8wL1+@tw{*#)k^hp5xpxd}?Y6*90W8w<(6bjI4us#I+IMop+=S zeyr!D=cebV=W6kRCv!qGANW}uj z=vnE{p8t@whs9avDV&R*lb)NNqn@kH2W)wus22Q%27EgIfi&wkS`z7Bc4=LlK8b_j zuC-tfjG9bB>le!e{J&)$nC)DwWp59M?*|b3LxYM_J5%#TmnhQxX{nWs?$03hEqV`k zVqUq=<9gU4cs&r#1+&sqJxA@!iG7W_30sK4NzG>i-5+7U_R`p8`FF))j3iwn7)Z)z&n^(H=#-#^EEm%WVaYqQz7 zXpMRFEP_4%A?$r(56mFnN)obkv*9?AK;u@q65vzecJ2Rb5yd}O(ImvRe#64^b|7@3H-@IDg++*o?)c|~}B6Gd$*8)fEu#D08f zbiX`0Sc$RWUObsqt?X;Uyq2-~-MLqeu@_{1w-@-C#pc3)R_61}1G!cz$0ftUu7}ZZrf~8U&~|rqdrGXeL?+1{YEuoMweHZBfNyRKTo~G9_A?avvS?>D6Uf) z6@mq`*GVntS@wJ|=ksUpBm1EGyO)>t(+bTw=(*@Q>AC4S>bbT?16rX0Ei@P41NF5Q zwDkpTa{;@a;D^iw8ZNjajoIIt-nN9m+~fhx0r3rGNKbh8`5Uac$3uVa<1pY&Svue!{|n=}JYQe8i0k_G@67JXb=~P)&z<}t zHT`pNuO``n)g{@tmh@tOb62ltq_ZblPpl8teUIMkdFL8mcmH>#Rlq0WyZV55qFs6} zdQN(7dX9RoZPB|TA0Q7jU1!FKb1NdwHFGepwYeDwA zHYql(xP!S??01_7@V9;Y`8oVFB#4@y*w5)uoa-Hl{S;z9jo8m1_Pe0_+>gny??KFC z3wkPl<&O1)JI8rkM?a^l*4ot5dJT0a_$NI_&a5GQvRjMm=3C-Ie7F)P?!=1+vF}Oj z+v48FJ^=QC=ze?H2U*#NpS&Xza{YuR3B)E2DC*3T5IhC{}%9XLLR6Kf88fw z)`skIfUXVde!&m0hEoqqyk|A8r)4fkZtg`-h@Rl-Y88v=Jr{nr8laDN@i7Cn<|p>Y zXza%k`$^pIOk-bl-<~~MuiO{HdqGd`&DHBA*mLZ`y*7yZxv-zD_if6cj^=vlX{lT@ zon)z_h9|!%-WxAj*b^rn#ETbk}%Xd9J?vqR~1OX@TSD9#ya-_ z8f0O;d z0jsATRV^t;pm zy}XJ!68rlGQ}aKGeMe%yoJ{XriTTj8#Bw#*K1ZIf1!pV&Uhv11z0rLW_vHL8#Cx=i6>t=imd*a2LxbY$9`zn8Q-#+fckKB+!vwF*PxGSqA z1Ku*+m7(%oF^qdY4db3q#<-8yu>Z-pU)0bK@Qe3k3I3yhb58fa@?Kv>?_QLMq{fnT z{A-DVc{uFDU>&M~hIw|3zQsWqo~FkM}A5qf_l-Es1~Ie&By85Ab)Z z4G1WHX&5#CXlnl4*b>Bk8G5D_i2Y{_`?=j}TG;m_?v=a7ey^7e`+42T`PmlDpP5PT z#EyG@axEDU`-*s_2T1$*YsG*0Uh&y^S3J;2PsYhh*%LRujGaGm6hQ0;GN$br+pyzz zW!%JwBJRuJrOXZE;oP@v1owWkv(+QGryHinA{)v5-t>Fhk;f~C*T!!?Km*<*hrG?) z;cxK&i)18zBnkUZTG%UpEX?Ku%AfdG3&szWzrII(ulk<#z3VyXx#&6Rx#>B&?)i}0p0Ijlmm2+kX|=n%>lYMn(tvhAI;ZXKbM%F zt@Au$zEFE6ot`J&pJU$4T$wp@VuyFcbHWsfWPTjKe4`{RSt6dWdKCby48T*2SF5E1>J08m5 zQQou9(?XN~}`_3FJ?e}~mz6T#m#E#Pvw*R_htX(Ff3NvI%p1(|;7$Eb;wls0N zQe)jP=YEc`Ra-meQ>%T((ay%!m9_tatr`6(dhiK0OJGj4^coUddza@WyXnCNtKuGtjG7D4pd8N>u{)?qq#mz z_l5jRbHM?DCE5>F?WgX`jYIpB(f-aAnGZip@3cDl$b8T6XI|gq1^nR!=62fK(YYNx z(G=p{aHrQ#Uu-13-oDh(A*oK{v+aR+uDK`^vKi-W?a|P0>GfAxz`U(sXd}s5xj?3l zc9Iz*o11!U*&y=z5Y6Y@-;-EZ-Ci>q?qgs+_ET9m_A^)dToKb?nbO{a4=D|c(GuNlq#dSPzky?&g9 z^@iLpWCM&htpM4*7MqR_`;geb;;Ko4?&8~k11?+kw@ zG@uP@;jQt3miWNui@uZStQZN7bC%EM-Ib511GFF9Wa@5d5F0EJZ7XpPEcS!2Hkj$+ zaHDDkb6;V-PWWHafFU6za#Z`rnYzzWrlfJt^~}oTb+n&*ozAz#es^o!r(4)hhWoe< z%uOR+lyvUn6%+opc#at_{#zf22Xh@)%#CyP-u9i09nzLLJH0vjEsM2R1NWs`%QFXC zPJY*VY!!9c8uI$uv2f2J&d1e-@#okVu&s~5d4igS>88BKVr=4`za4d;lT`y+!N2Xoi;^AmseR)e`QYkECd(?j>? z=zS7dKi4(&NeS$+j(AC&lYLm%pC#UfYs7Ey5$5&}#hG(*=G^?29+4hX2g=Y12{LnV zJz1zR5BtL2Z*fokw;A_$Wk$buWk7Zv?zR6R_1nj={|ucb*2lwq0?hN^od@rUjbIG# zNwA&-$I0-W+{|M6&1EyTCBH?{Q6JmeX6x_td7F6Mrb$itKN`!LvGu7fKas*wZ6xl@ zH^v7v4?NAH@quVGAe#7(!n7X{4*xJTAQb+=@DC;j1i?R$`1gmuA34B>9NGyOrvNXN-$~=VtrGA3 zobrEIy;3n(*#8;(m#z&P7Eyfdm<}bFqnG6Vc4fKOA^f{mk=fm?{F(1-&Y#J>Hm9d) zU0*|@Bi|ACNsGm2&0TS0UCRXxbLKqSVb1898~*KExKyTQYp$o?-tTYd{F~fM{e6iT z-CcT(iI&Mj8j0_y{?c*XHi;V3m3uPRC3h3=6JQVbO_LhQ=1GlVY~j5nzZp!iDX^Z> zLbgtAiM4uS(^|_`t8JUsMr@`(vwy6w*^=Lqzu%1iud%EeRZpf3ZY&Abo{#o@o@O!d zk5T@Z@{fdn1pLF{9|r%>6Y$3eg3y5WGh#{54qo*14cv@ z-;sm%=Ovfoo`&T9F7*1d$^B^m9JGIKch+)Q(=BA5%oO%LM0NOByyhMh7y4JNmy^R* zs{cNefJH|oX3lDS;;Oiz{ej!RlVJnfp!a{L*Qz}`^q$&o^iU^>?BXoZnXP5evPqJ9 z@VpG(v7Y<$6PLvLCSrZ_gcH?Bw?n zq8ec1j|K$5-=7@d3x99c1Z@1>;jd%lV&xyS;DE$Od>|eRj?2gM|G5WX?i~Sty*Fx$ zv8$B-PcCGpSD=qQm?k6f35u6u6!hkLo;Mr=Idr1ZOYIl60+vBc;V;i(?`Rd9vgkOslDEk zAzAN9`1C1~vE#55%*){O7rx}PJCu6^a<+y0)+tTl-CVZ8eA~2E@NJE?!Qc(M9kAX} z=wfW=3|Fz3o9vwF{xjP(%R_d}`uWdx&g6MB+<2`kf7=NkYmMGCleq;El6c{}UuCa8 zVKhN~fcTH04vdC>6#OIX_=mzj1pb->+S~E>V;<-Oe{bU7lW}q1ijf0c$pJ2F?n!1! zGw~cWkjIbZQ`Q20*#oli*L&nP9|`|<4nhw&4$2>4dVQ$Q1GE?Lf35*W$CTKUn?TQ} zQyG){3%k5K z0`umweOgOc!@ZyltlL_wupQi7;Oi>8V5nS`@9x>2R`cSB&GEJ~A0DgO{Y;;O>1)*w zwimeI18rn|UQ5Z^vr}R(|M073bm15Lqv0P(4u~NB!-;i$N|3a z_kq6`HGqeezZ?9$R$i5u6esar^9`}YJ`vW0f64#df(P=>vzmw*E`u$zm-vfUOd&?fkKB@0Y@Sq;jC-!gRHt&pu zJ^b6k-(~ef>C!)({l3(Q?7x}M9Dfn>JlneL{Gp#opJj7o`0RmFnCD9mPiy>7slmT6 zF~24MOSm_Kc}v+rtnX0ng>B)Z%$?y4^I2}P8|D_?Ua|+Kd*=FJzOr|opVj<1+FJnM z+neoomgn!D?aklyG(NBy9~inOU*h2(_bV2Q2E@X@Bk|u6{qKOqs0P4468;g?0O9lj z!q~GCa)f&D5Iuo|*nawh#J?Xoz?T}(8~$GK_hcU64*$rh^CWQMEOFENX%%ZjXliZl z9b)SNzt86(wCR;2?IL~T-C>)h0sBOo795reFTR!r^DnUf!u0xZ?Fav#*MxH7OD>y0 z?$7TG|E$V}zsCPe_V!I@&+C+w>Jl^{TT;dhmcVYY;575*9h!pIM* z6|Dd1{wwy>EYLnrZ$3i>Uzp$T9ZAn>BHg;wqmTQMsq?kIH|(duzMv(n+hA?s-452S z7MtS^V^7QrmOdD~=fiKlzwBEOAo~`!H@1IakQiGOEc=Tr#O|@(&v~xCZtr}21OM5D z9&9HcELoT=iI=}OKA?W^ON)bl9Q-x+82tU= z?+1S$S_y3Y<-Txs8 z%hpS$Wy|?o9s0G*^R&j_g640Z-a>X3wy|)Z?Lv&Z65k%MQRd!o@`1I5_x^=}hVua! z8(SPA2Ns9QfhA#ba7j28AqSVLMgGG0nf{LF=yT0$c&+-(zWIUV2w&MUJ5YKaKOqUQ zf11Tx`NzRO4*o_1;2#73Xlj5c_(!r95WybxFy&ADhaA8Mi2wHJe*pab;qONd@FD)a zHZfNoH%(ke=7=|SpsUWI=mWH4OzU#bYn}hso^eNfqT5QtnHS{)YX5g(|0bW4@b07= zKb3p_)y^>2_ys?*Za5VxqX^ zE)ZvBPwsb8{?vemKQ)X0ygkyn!&_!{}~oUtL2Bet2TA zPayW=G36f%|5&uYBm6tSKgOOv`X3JeP~}hj2h#&k{(;J$8o-bE_l3XDmPhOxy)OZq ziE;V>Zt!=3zm91G?uVxQ>*Sx6&&RAXpNF9HKmBYJ{fw0NCw~27xtcGgz`QQ@DrTSm z`SySRmE~V5VNxec{7;8}K_=Jkvd4D{*GTp0R9ouLxGEmw7mM4{`_hJW3Mcrtv+~zD zpeyg$m+Lx$7wwlXW7$0!4)TzK`5BUY?%vP%C)#|#8vlkrK4AEx z|Iy_BNGt;W5%7;ZaF2DZ2NwRj?o0cXMI;YYy9e>D{fk0n z5B21l*&&kt)pbci1JsNbSoxd%Kh%D4SW*1LKL-7eLjNPt|Aal4WX1$f>9c&cgy8ql zJ8wwu*<)q+lnj|Xw1wmiZYJaVHIlvqe8qbm`ysRk#2D&W>X_=>TK9q4?jQOy{`wh- z!}ne*Q>LU3+&{$h{!rQj*7^VcbuUPCT`0Z~jA{Ib z&R#?h;6v8k_$JGV#ZF#P{&foRP$?X8-IF#Mf}PA`Wb0&bzgF> zSg}8~fw%U5p2PJ1fWKuOxY!HTioKkdR_10tegFInu8-!rz6o3}+by!DG@N*Vds#n} zcErBMe>?bV4WRtpH1@6hJ@H?|9}V!?Oz&T7TQn|!TBkiVkMa*8{yQFgBoVV0NSCYz ze7-6FFF#>wDGwL&W{z6_Id{M#DSGu{H6Sa!;}_huet_X;^1D3FDKSx)?jP zDN9am?rOE{e}25FE6>X+dcMBa;v4vnY6iM+n4EBcJfri*rOUfW_E%Tn|9uhvB={%7 zK7rVehkqQozoV6Z!p@UYI5pJlRaiBWeJaD*v%>zgrGwv<>3!Z2?@sL`yLUT2U+PtQ zYA?%#o*#>E|8((NaLA0Ej-ide3$dkp|4jVb_>=q1XJl#qPa1X@-c7K-W48Y9|9S4m zYl}T!)uGCSPG!zaPOm`hb58`W>m8i>S7|(MrF=O39-qr<;qMH87jnNV{N1elJ>c(& z&wAs#-o(Ez@$X0c`y2ii4G6OG4~2giwNUKg$C9}1v_y|cm({GlY++4C`#OgI;`YQ~ zIP9Z{cV(XdnJql`$hcB{aiT zLgMO*Ut%)}m{ce}i;jzrYA0i;W2s~6N^I$Up>+S(XA@7#`}sG_=VRIU*LL-s3On1} z|2NP5lz)lhe=5~0y!6t^=)Z}7uJ6q1(nLO(c31SXSU<4wC-#-UoASp$-Fc7tUcK1E zov?*hHhOVU5}iV z%%hj2({cRa1a;!^`_gOMHW|jXXl_}-GPHjaStQ zzb3KUZ%T*l-$?jo_Kt6QWX4X%P{&foRJB$4H_X|>^<}lBL7&_r{%=pdEtOt*U3-5Y zW3OO(Z?NCg`{i|7FO=*O;Fz7yx}Qz|1G+}aTj#781ddddYnAktDE}7U&Gx4sBz9P=g5Z7mR^e$q2Zr1=gw`-uB+dT*yY_UB<P@tU)0_=@od{-)}I>VVdXdzQpVH{!MT)gPqCl^>WpThcUiQRM0tTA<3yh1`W zClVWhs*#M9j+rg~bu4vEHMW$0lcCe3TSyK0V)7Bg-+Vsmg!2z7RIeEfxA!pnwV(fM z?Z?Y4{C&PG*(5)s!s99A|4H1>rA^*?Q)lRBGAn-9TK4XX$(FD@nWbqoiK^QBp2o=zjXS$1)bi<7Q?{?pPl_hx|)f zJlS0`xi)0xDAth%eIV%>b*1Cled3%IAz@SUC1yXYmmM^kpRizs1gkDGHabTBnlIt6 zW2a+iw3IQ`@zwm_Z2B?jkkm|SPyfc`e&zpuzttBVOF6WF-J6(xhTv~r`?c~f@j^9+ zS0|-cxHct|d$(u4D2*l`EBZWUk^>A+aIbTH@X#_#kw=DGSB;~(k|>I)W4Fg`$S zaFRJg&W@#$2K!D}s+E80*+&vRX{d}J(NVa=0d)-Ef-*v;Ba}0SeamRHDTC`K5)}1%jo@*g>tU_0REh)1WN?iVQ@mY1xjH&Yf zr0}+MNo^pl`wph>$LHcw`)fX~6W@Ca-0ET4_xo3JKQA}&U!rn_VwFZDR@gX|wcjb+ z)1g`3j-T;&RsOIy{K@||{@(a%5r2GG`3EY0d^+2<(r@P1=`hExK^BtkTbDX)E#-riC9scO``P~C8?5*4{!~62#Tx>#- zeSIWP?9Kjnmk;sWL*wuf^%eDpQTUM64|Z!#=wozX`l?ZqcJ_gEf_*BnpK^{Pbzjz3 z4N4QJ-9--1mbWB|s6Z z*5o_#=l9>!djQnN^uDmax$nbk^-FVx&Kt^f4k*`U8f(8(>HoUq%rD~q-c(Ecd%)k5 z_vJ;k)sOi1BmVt~|3LV+N7I7fAB+Zs!ascLUFw`$62TZmY7MaCjzsVM zPCDQN9jF82=#{0?H|w(dl&mWBqxbJAd+Em>U@m@){?5q_)P9=#iT`u(*BpOg?{E{# zmk+`I$OO4^WTIR-HW|yuru>TO@ABpAqm!_S_=oz-czi+i0$;$NF6KS^N)coljqh}RJ`(9xZ*WFo?W=@pkUFRk7z#Zu}E=fYi z4VDz{MHr2*>pAPWhfxPbuopgZV4}G`epK2kk`Vu{bdGyNJO=cXhz)nlSn8N+k2R=A ziUbboC+{-{(AZb)ubunV-HO$!Y2W7`n0_|IZ=Lt^QuAfwU;52Im-uLE*Jtld&8jMc zdeoQqCSEf>@D6pL#=j@;$;-;$8~#4<_f`Jn0OcRR^$bP)(EuC&$hGGra>fFQU=O5@ zMf9GB#Kj{C-*j1G_I<~E5FemtmbCwtc=d>qq#o_Wsk5J?4h)j53!<2prO9dLX-5Aw z{>k~O`Kt38*H?~CmaE67%GDFo<=Tk?i=8Z#Yrio4y|Lrd%=3+};0srbFW?7<@dMQY zY6R7oQ#%L9k^>85#)0LsbX%@$U6Cl+yZ1{nbN*!J{OPB^m#*AvsLSD-(&^wWNu=)^ zR4`M*3uj8|;YXY!I;Z(g`K$iHKa70Xaot(zk;Xol$@vnu;gUox*(Z^k*+0ej=~(KR zwpjE~z8E}BykowUk9&-_^!?%gVV5aK9UO}N9`inX{<>$xu}UR}7sjSkIy{ZNe|cFi zOZ|zK8sIJdUnlma|0;jt-yhTW-`*Dg@DD))LdgNqJJ3L`1Bu*zmvcWUomX!)W7A>B z4T(GOP-599p&Fp$*X7h>#`BhRqqoxS*f$a}d5lb-79&gNCYt*19Qj}CHq~s6-7D}@ z_Sa4n%JoyTEOvUfTt7Vrvpb&RvF-P#W?Fp1_=Ea_>Vo=#>VeG%&;hLpw{@fbNRwmq z9yiPllH~PUB?b0L^!pN-^C!~pRZGBs<2i@qgZCtv{)TFs=KLtwM{3?v{_Ou&{!!Z> zN!Yxt60?o90p@-l`70%0)on9|s-?=m{yfV)fg2W_l24~yG4cOa{yllOMe`xB(w^_{ zq5U==F#P%EnH1mhy{2VX`)LYm|IKsO6y<=HydN*(-+LK#D*F?BR#3C9q;_S!+K-;K zKecNh)}9=o`X7ub{}A|xlLI2wU6N2NaQ0g995Ga4R0F63J2FnnUo}9-F99D&(lI3; zbY@N+yJDRr=607k8}i7>-DUs!ZZdOrXIZ;BsFeFuA+zMq-*jXQm}QuXTIaI)3?|El)e^Vj}C<-fjMqTb}Js&}Tc{_iG#0|Bi%e{2j#RzWm|^U640SBM7+cNxy56Vz{cO4aY^T(b9fGo^;&J{E0D%K^N5rV(|qXFCDx1!xkS%WIR(C z+pcr-WcAz#nKeC1mTev{y-wbi?q|sl=Ex1zf)i6sF8KN|b49HMclD9u^qUv18Yq*u&X)9Jcj)<{ z0ldE?*z0>$_WIrpfAwdpf9p9K{;-cE_6>iHedVw5Vdbx5X81FP%3s&miq?CqvH$Mm zTR;B!y?1rJU)Ou|^8w3ay56Vk{?#wj1LTkB8sZC1pO=Xq*5iZ64M#0!zVWT$uQhI2jRM{>=a@Z9cx zvSV{UiJLi90!R0i(42mfK6QX(?!Ro_qrO*t&-&i=9MqTfob=rE9Q9nIls{t-Vdg^c z*SbjgYn~+j4S&s@jG>NY1NMIDK5zTjuQPDfg>vN`o507m*GoSOcBn+J+9AC*ZIUb0awhI!c>B^Sx$`y5zlQbK8|2QFjq>f4O+PdJnfaYQ2j94T zS$#qMKs7=2fZTx=+&D8!uAQ1LXAk8{bpIGW&o4y!=f=y>6JN6y_=990W3Tqk(~`e@ zm}DKgY2K^8XMOK_4tg$nPI_*7j(V1Q7p$PvIYemW3*R{5p*DGyFN{sI|K4yldwf$zN9}H>mAyq2;$PuZHJ(SZ}cKzPeeyy}Cv2UfuRH`}XQq z^Yd@7@OwL7u=s)I3(X6v1M}tjS2N`b_29_;2c-Ae?`7nHLz1(9x1^%^`d%`RJ&;w* z2Ul*-l}?B5i@s;$znp`fOGo9e`o=jL{+x3Wf5yV_XKZwgbgXpDZ2X=Y~@yP(b!i$zxU?=Y5c1%>Ha}oOM3a4iX|FGrnSDLYd|*s+Iw_er0gxG{M+NN zK^p&9F#h{b{PFV$8-Mgqb3qKbFm~$=S+;J7B<#E`ag0~I)dviJ##8wxv4=hxeN9mx z(6PrdsK54qwSkyjYP9^$*Xs<=yKud0xNnua*S5>O>pQTW#_n1l>+$Y2^$VWEb1iOcX%BIZ&W$;ec zd+71UF{kOc<(eezWKEFsO5A>nHNFStoTE5*6aUJeG11tt=dWX@I{F0~YroIyt;zR( zs^cFz0VbOFb*;xf_LbZ3^`-o^p3u5N`$})t3kuC;&iRv#zaHx|4^aN?u^{*d!9Q5p zV`a2RbTCwD;Lmvje8UGXuWd3rrcqxj1Bi)a{tC|Y>(W(xkv7C zyvO$zpHM&8Zt(%t3zIjrW}t?^uWp@3Th7jw{ijyRfRnc*|L|UV`5Pn+{so73$%Q?A z&!_UoLh#>E&LK>V^9e`SA~?TD&NGtpjpDp@3}V<5 zl(7AtjNW}fP7*&6WBN+^j;j(+9#9QP#FDH&kcy>j1Yv0EH(?YDlv*Ww2^cJVy*1I-m$EAaQI3GQ6d`Gd|4<})vt zC6`anl+{}%NajI$zlR^lzI}Oe`9OcUxTlAl+s?Y=rg)jUl68WeS7pZ1-ja$BXY79{ z1v6q~{OFc4aABTw*vfuLt$!E;9Sa>39UC1Z6Dy3Fj$c!*`?Ih4-k$vJPoH>rXun5$ zy}HNC{yOj9Gxqt@=F46KbY1apA2qF)_0FVQKiK=gm#j-F|6uyRhQG!>7RovNcl;B$ z55%m^g|d&mIA^zamUBBYWZ9nivSI5;H1n>+Q!^!~4={#FYUqIR0sNpB>%>=>$7{Xz zEt-Fq9IiTk&ldN@zVU%;XuifhF>m zlFO+1drs^kbC-6PafO|@26C)K(FghO_-ifwfi?DZf4|mU^#&|FRlb7b=Wuuh)0q5C z*LU!`|LvDe18jA`OQlPftn+!#$$S4k`N=#$HNXcAQ2rtGDnp3>P#b@%519DJza#PU z_>H$@#_BP$V_hftYE!hF-IgThcc#mQU71ElSFgyF~A09cvv3XR^zexUVQ>h(0p)% zJfMHa%o%jfNM1n$ZleL;kO!`vohKWQu9nHij?2uAQ|02(9Oi~2j0T+9*;V>3ohg~y zSl{6Ouc_OvNgV9s=<~;~JS;PEx!1rPXBj)CgG4PqA<=7D3toTEj7bDzW8z=+@+qg#e@dKj+JjZB&&JCy|O#JJ7Vmbd8T5~~r3*>=w^oq~Ul(qX8 zO3uvga_#sSxpH`r9A^Jn+M2zR%==AM-QvB+vDO`nCE^EDmJN~cEUt5(y-Fe$9~9Ry z(X@we4{8k4`*e4jt{t6!=C>H|8z z)O=Hup5vRJheV8{7gSUWYCp)Geo!d!AI6%MnvHz~=M||2|7dbu+{Cf0=Q_!WRX%ch zLztY|(vjSmA{Tb1%SHI_+cT2<`K=@{W`;jH(0$J}Il~(LuA@t2$l>d9@f^BOeWrQ( z9Jm+ac|?Fw)L89o$D-vb{vxgYX4aBe*)G)Yay+RG&UF`jr}jkr?$Di>i(yrcU`Go ztE%>TUdB}S|6Mg7fBLCkwmhKuM(2YsmMiC2r(vHdd*8In19iA2S?ho>d>|bD5uA%L zd|J;figQ#;q6Zw-hifaAxXZ~k77aMFC64u+6gjgqL$<9+mp;2LF&BhC-pxw0#l!Vdv(24`z`$MUEd}fw$G5%WgDb7_YLfR{Gs$(JXfw?#z)Y2 zTl`!26aVT5ci?a80<8tL2GAN%`v=s2Un_r&|7Dognv2kZ`TYNLbI)-%AZ<4mKry0-6>fzv4M17yGuGS=Zk^6#)7i9jf=*K%VNv=jxF{- znDq6d+FyPW&6vN6ss7sQzH<4azV!dnJfOa(YYl&X?zKO=)ERgDmMstH-caoc7V)?F zfbz$pIKOE4$8gRmTkc5jvFzzysT#ok{zE;SRZYW~Ch1H&b@*%$IYg<{O*e#wCq>tFHSXWazyH(c3EifaC{bMZbydT3E^h1d{gLeXZ%t3oHXl$Q z)c5n+XD#X`elYR!BbyIs9?-pk5wMR`WBoc39kA!GKG1>lPh5RQ5(b9Il9@h|n%hlc zXDy~a?kt1yVrA^I$+B*1jvUxMO1kG|NvAaj=(Apv;2A5W@5bG7mA>4!=<;3S|K5$Q z7WU+T>z6jjzBxIv+>u$D#AFtb3$ve<)FfizQ;#M#Ep@K*vHYk$dEH9~&zP?0IvZ zvRU4reB-BWez(||ZMFm=mqiJ7rjlBthVw%wPMwWlO^=T2F-uAg}H>muIlflJ$T zfqIY{5KE)B8hr4k?A<+4Zqc8+cWuKH{x`SF=`-u3>mK&sFi%KRv-&}|!w+TDqI|h? zZHHwppnmWT`2b&dV2(OJ+$GaiPT}>}@fq$_!)v>tAH#PaLu<6wSSfeWfNw7^!UyJC z`4^A}rpT?+6UYN&J)PjBG2y2C1wp}so<6s|)=6B?LleRvT=q1Oc1J}pe_(!c} zUvKvw(s=lE@tDDN0XZ#MTN-`*_1CL^1O?6a8gDx0|4H`z_vXvy16qG-Jy@fpW2rYA zWsO?)kIDCo>cLM6xo0nRfX086)s%k>{A2KUwGQ}yN7VtYdyQDgeeKtNE&W-`KfAY^ zoIlW8)*V zx?TB_wB??}!QRGS&o>r7&~w)@(0FiLLX9``Td6nWrc9a{D>J4zU#$1>b9EV8?d@v5 z)p_3k5A1mx=F3(K6!}2Sn&sbZJ$Tx7d_ev_@mu-Z$Yav5fZq2C_BO*mn*D%kF?K#+ z_*;D-fxXWjV+$lWH&2oV2D6{HE9>n&WEH)Dyq&waJ`6vBfAW@F(rM#4$v_9Pwq25) zN7oX!n)`PU+gqjg`kj)-+^f@5ECV0thBkCwwNnyj&6QPKXUc8n3Z`zLZu}PiS$FO$ z*?r-#++r?q`N~e&bN-N=yR379wQ^7SU$NtVan2L|XD6}#JdQeGq+CBfSgs!FEtmIa zOaE0XB#wNq%+-{CN6y*C-)lL2o@w8Scfl4}!d~~yh4rp?3jecniQ>gn?{$2&=F+_T zN9TL~%m4OE-;=&Kn-5fb`OnqsB#iF4YxMM_pFYdoBOeT2BJDDQC1mb#_I$9nlh1k1 zTy<1BuK89XSD^cA@0qdC@krkEtz>MwE1g&m%0ln=Z|y3l_w|*2>o!Zy+_l~SSeOzv`uBZI&-`XMH!ffU+HAE&iF+pdXP;IDPi6+ z>73hF#?2Wbt2a%Oi~p~^?*Pl<=-QT!f@1GR6BA<+do<00CTfZr6>IDT6;z}bDFT9u z1yK-@CRIRCse)J#l%iO`5_`cemRO^t=>6Yk;W3CN#y9!j_x-*vdtHZR_SxMzGw00N znK?7(E@6M-#%_L3a`EHMJdP*xcxIaP7YdF_*UFI9pX#?2)MgEt{+y5QP|A%uS zv;ma=sTH&VOK1Z;khwnr)_GjhDDA#N_KN)3_9rlIPT#mISh8s~_9Qd5*~ZWBcv`(X zSxZYz)yG%0`TsWAi(&{*Lc*hbn?zshw|z_hqlDGn^9QVodD?~h1$)Hb!q@9dFnx0g zrmowH$mogi-MJIiyMBa!S}4pZd#hcKu)N?DviAm34}OGA?nCU@wG8Im8yC6R2WCmR zoDY1Y*Z>>a0nfbiDCN41hd0>w6B)7nKl*`saC;ASf4>V$i*CW5eFY&0A(b9fInSey z&ZFo7-1l9hO*jCPkO1NZQf`+-59HmHyCVOb3i)R#@_(R`|CLSr2CSzISjRp9`y*`s zx6cO92CQU1U>R+|Las@6!$Gc34Jast+iv={SXT30Wt`*T-ieJTki}RQ2h$nnD5d|M zB@Q1<*Va;3?cMorZ1WWH3rA%g5WTJ6wsozKCIrV6ci`TH@^Q>#e9k!yGwQtOmMj$Q zFh&x`aGu-pVV?5{k-6#EljDRnVUu8yb{OMh^D)qQ76y8_Vp?b<>=`4&ntf;?8`7e} zE4ayd|A*2hZ?di4q7JYRAiCg{k%0;Rev~`=mb4QJRn`TSPE?%d@;sM%;e3Gj(1-5B zJ@*tmqF3VZ(M09_KZ+hOpC85k-=O_3&14^7GuwYM`vCm@Ut<67LNw(cM)?O({;SL7 ze{w13gcjl&<$vXnBYlK6I9+IijQl|C*u4bXcg#iMcJ67|%Gg$$zq!5Kxx*C7zVdwj ze`A|}LHHFHs_HS8l^F!s;f zUAJ+xU@nSi@9ncn*#5a*I=_r#f69b3ch66-V*kmCeQ0ZvBV$MBpDj}Q;txe8H+TJJ zqxpYu;UU;_4ZS_r*x0k*sE|qz{@`rRvFI@GrI?ktoBO*ptk-L!C8}=DX zIiLJY{u>ne-@O=BN&cLhkoK>V|55g1DF2HGZMlbJ4%e)ka{uB~oX8o6quUwVXv^Sh zE9P|?LD~PO#`V7^)7QOTSqH?1)nR|2ZSeBm;e#Bf{4&fhh_QThDpKjeADkVj8?KDY=1jZcoyjr&HOiUa`iG_4`Q8dD zm2?KLr?LGfvi&E}{>S3(g-FUjlJ&+q?9en*aZ;@Z)9Z2$Cau>F@5&cT@i z6P(;V8OL^xeOjD3?5wv_mtNZ1>e7#r`um^M=3k~CDjksVii}&hepsW)NVoa^Uk7b2 z`^3%yJ~7TnOS6FMjw7(oC`MFH0xVPZF`l3!e6n(2!+wDkzmHa213!x~#QM27;bSWc zWbau^JInF=<#?s;A6!dgJ4~l6L>@xYE;F!VKlfU)4{2A4?76<^H>#u;D*aH-<@Y#n zKbB)=Aqw|}bFL?yef~_!T;wh!Iu-G`^n3_OY10q_WjPY?{{tu=Ky@!|MRBp z_u{?*4}SmWaSxdT_a|F1zQAm*ahQ%1yCyz4vVBC(SG}9P!*>5aUFKEwK+02jY@ny5 zrCE34%z+ck6H+dZjopgH8X{smmNNCwwhj!xq6a zAz0}v79HEjHJHJ;!!h<d1V86h-2Tc-1gt*xBu2z_WjxROKJPB zSM>cZ9dhFSL|dHOXO7barZ};CO4*^U->xp+GDzl6|F<409gwks*aGR-=yzz>xWk6H zsj1tyS^aF9kcKZelwiavf6PqUkJ$lUoGUnmd3m?sy6X}gb2#U>{X7<=rLrHrgkx%7 z&ank@9hTSu%7uOIyBA}X{`Vcu!5k?HhW&}j1lnNys7XG zpNk2erZA1<-dvGA`-oM^j@MqQ3o4mcK36<%x#c*l!UO1Mpq-(OQRJ?)d)j?|_wVw% zFW)24M``;v&#dBJ(iPlCyo9n}NZGql_KrBW-v(#)n*Dry_oS2~StHvV-Zu1qo4tRP z9!q(J#1<&?MOvEb`dvS4@!{;Km>h?+jIy|`-uNSk7NFuXQ(HqIPZVLn|sf_ zaQTQkW$%RZ2kf4nE|`7cSk8oLM|X^nKJowMLuCt8^F`I`)zPizZu7~w*i`rL=cc4T z9lyjDeWDJ-FnAZfSQ3aynVc)(oPjOJ0?v$!WS_7Rd-4{;DkhdbloHtN{)yv7{{Awm ztOFJQzf$&jCwE?Oup_AoNmwc+Kk$^hw!= zufnmSHIz5Uco$`!Cx&akqPZV{vOg!X4`jO+*>n9TeSVjYa2;!r%Y!p}&0|mI8Gmqm z*F@3ve~uSxpt3D^)<0~~v}S8d$1zSZ=|1O1#_s;rFeDG3u1rOju*;A&roG}HbI$w$ zW)oM+JoO-kxm&<2b~DF}WzTd#Y{hf*nsTqKJI?euxTmILL)rrD&h??}qqyEDocn=7 zIUWh*o*=IKJkHn?-!FN3;gI{Cv-@n;ohmTta58V&KXQLj<}bUV(t~IHL*7)gd6SxN zn%fLD_TRWNZ*zXw!?8(c&_Di~vhPUdlO_I4kAyNM{?NqDr7+oY8>Y#pV4d^$nI1f| z2`bxAaehuWD(On)`>nEW!!`Rd$0Bza-|afg(u)w1x1DkiV~l_hl%D2#Pm%qx<-c4! zvY_PLe(R;D_snc_D&OQExxY-xe|)X#H$lb;%6PGU-RccK>-NScalvC&7UjC1O3YvX z*qA=Vfg7%%Z~P4<7J=+R>lS?rgCcXWF#9l;QnAghJTK~Rs)swK-!Uf7nCt0&w9<9)@s)ClQ+9>44bt4aIV|I z(?|DQ)f{TprkW|E2ocV_l zR(Jp_^A2Nv_Hhi_RI1Qmo=2pXVq*Fg1n=F4qT?yJaW0zeUF6QVVW$HhUp?k~F(=I` zA;f3Icbz*lYj$#v**|UlugUxOp02D1(od9nR(&62twHrB4fX51`*y>&i=4k6k`_DD ze|Ne|?$wilmrKuvJ-B&3`qx_*;=XL+oz&|zcPYwKc z)Bt`va$GC^FRNfpr#!7nR?{d?XFub-JpC+L%|I^P{VlCL@Z0U>FQ}BR=F0ose!KsL zv?1_orJt+4D&4Zm{Vz#BD~-IDzyG__&z0vT{5|)(VU_YQ{j^Gb{9)P^K;`?Zrd#rN zw@NouO^ZOYEB!7SQThItr7PVo0)LLbKaqZ}{Jj6Q(&Xnkc~(ucJeAawOVwYIX8t8r z%JW=Wt;+9AXIH+z+Fwn(K3Cnub9qI&WtHEnrVXn6{;IT2mHS^etx@Iq*G<1Vzvrr( z0)4%-L6!SoH{G(z^RG(3N`6&;cdb%?Rnw^Q`-|!4^s%abR@L9C`u?i+!S?iAdt>^! z_DXy5T-vaTJ#eKxsVp!iD;J3DJ=cK9-g9X&Pn8>VOG#H^$W_vn7?5NogT&Iw@r;3% zO7b@b49n9csvBI((<%$1aK}rk3>5B|R#`lSJ9)&d`~`;czZ?}WEKl%n`3qH#t6tdh z+3)2X{+3i;pvqk?OODTSQ|^08%k!rW*-YArG>N2Etrh=PtLFBOa{8-(DvzmDW9Z7` zY?;Fok~tgI|8iE{qk86#&XUL9d8l}nJcSpP??KjT$ox-jlI(St{kIZxMPeXH+$EvL z6=_+=CvmvsKEYm)J$v%3?1z#45V8(d<}Fp_mb_H?^51{yP(82WTk)6hO|n;1coh7{ z)};4Iok_Bfv^&X=)Q9wW1@-1xa6>*NbtOqmxerKfNN`4w4WXH25X*Ov(X#z=}8ASSm zBxRQJze|#`sP<8dJVf5AGVtF&b*S!F@viK7;*+gN9Z0=MgGt6D!D3!aTA`+<=C7fl z9-^zG5mmRgMndBTnwy$7(n@SpUvonp1C1DB_pDV{S6|Khym|k8l9bDoG?pY~k@{#) zYDp4VNS#%d1CxL1Q1LHi7rhkyZblM4=|LJoG9h`9ml$1xnp;0;S3BQyl1 zd`1%UBXX8@B0BrJG7$bn=cO%)E{fi?B8kq3yuV{T+BT@I5&!7EF7dl&N z>1$2k`};5-75`!<{tEvp{i#DaH?OX%)3Hy-8VeRr(R=7`Ru`_s{T5uBZV}wuXxfta zk#8Wu<1Hk5wMLTHTckHht;)}fnO@B6i{C-gl6R4~q%AfsdKc?G-b92`6Ra_>1K00r zlzz}s*IH9kQ*6{5B&mPVA8ChDpRaSs{qmfSmX=nnMh)t;n%rA|!;%RGPgl=wgdmHi zaI)&nc*~E_k@0N4S{eqs$?w8%ngK#Bh=pcNELknvc)^jZVk!3J15wmMcB>tXfu-zZ>)9)#AI z=p)dyCW26S34Fpg=fiv`Z z)`!=an&=sIN|9wp#slo-vlX)pb)WWWtG8HNTe}q*7hC$L%fQZ~+D5-Q#tTR3J@uPj z7r|zAu-3db!pse@)~uFNf9|8|pjTokx|8o-Tp!V!@moIUI?Mry#TY-S7u<$wVc9rs z@?TCXC+Wa65CA3MiA#Hgu8nBK4cFOAx? zF{fR6i~LJl{!@B%`P9A^o+I_{u9!kyrJjaa7$CyZ0Fjop5jwjTdD4TeQ6uzDy9Gn8 z8SKOO3VpfGr$5*9e#LeDgSno^X{bKD$7_*aZG~1%)xoN%+VG#I!?vMI{i%+inbI!w z;V|uF(w4*?bcrv+zE9*Zu(ffmiBQ<~VV_k*Lsex#l+K9EUi#UgR1FE8nuthca%!Fvg7-LB2;Z=J067{GD+02LxC)N0e=S zth4`(V)(yMtV09k`dZph&uOjEKl_oQi`^M3{S)FQiCz05r~sZLG_YW#ItFx9lm6=; z9&SU_YAqVAe&2tZ2J5Q^^Qemi=Z4rguQ4{bG)9bFLzu60#n-I&fvoqzexZ?sr92F$wwHn}xH z()?x$_E|i?^>adeF^Iem+4nPsv))IM_c4t5E__cfK9Bjys}Z}X3lcrwCT9BENLg4; z$)2r=pWTW)H>WLV%=&3WThA9rdRz5IqvR+%1t9rjE97HPVE~ z2=!l^*HOkizbgZ;G3pWiGc@?!ufck%N1mG?#j`o71oC@1v>f zV_EML7^i;HxnJQO?}5}sZIQn81Eeo&hYV8sGWq`lqhDoC)*yC~OE7q>&0{W~zRmuLrlY?|MQ{IktbUm|Q8W0mH7)sMUP_U zm0!z=nDjGShhcLn9OUxaj{)4ur zf+g{S4E#(q+lE+ZJ&kjzjYN+oY-`QnYu6Uz!!|MIX9V2wC({$&YASHmZn-CGXBE1ILJcl-3W*3PvhPAeQ|6I3+|aW zO1X}qw8wt)`%tIeLNaa3%GHyRzPcCj)BBR=f#lbS__-sAqdo>j$>UL+GKpBaQ;4ZP zm1NBSrxIU#GJhYBv~(v}U1fYNmcf$z&!G;@CjX{ldufBG5Sw=*`5(g=JQFt+V030F z`q2)38dbu*I^p;*^!(2nx;4Hg|1u}?TpO73m1grON4;NSUFyTteK3ZvE5s0f3x~09 zIV~a{&TfOS+_@Xf8M8BqxWD;PUy|?P@8yK+`y!)s~E{PFi7lB>`h{- zG1g-6ANY#_=pm;uhN~xRs}NITOFuMmTEW6;!kKubJ&?Zc#T#aetW*vHrh)5Gf&%e(!CEwh? zD0tfi#9Q9GkeJxS+U8!&l6^}V&p|=dNP*eBxB)$eel}0 z0ZZ3RN0e`WEDUwTtdwlVh%Hg_6-@I1t5flD=qbEw9aTuCWh@|b3snzw+c7mFoV~^} zCT2PIxL{5T*0|tg3uf{Z+UDuhPr;IQ*nJS05k?ATu;9fDPPkx13nuvGL&Wn`FrNkI zmqgr9!HvGKe<_aVy20%{<4Y6Yb?K(%$O!uue$n=ro4OI^+xBAC<~*3~IKddb*I=4h zP>y%+vl^YE@1gmZ6aC1v%n`p}KY2~9S10YE%NF*Pn4Su5x?tuCuImKB$Rz*M@*lw@ z|2AeGct{*X#;?miiuLQJV^4;qf^jbxmx5C)7^Mof>B$PrQevG7Zmr-JUpU~6wZ{*@ z@5nybpMDJISB%UcR_+h>%{O)lEkS>d zfh5MZV3i3*nK8#%(|2=U(Y9j>Mwj4q34WIi?Z%4KKzQYBNASth-0zpdeSpLSQEoSgO_n$GJSO&p-S$pat?vQr{&qweX6g-D=ERTnb?Jx1( zC06&HOPf)2Vlx&TzKV;*OR#UJ1LkkrhWT64;X~YwnT#RyTGlmN@nj&%TtHV>w6CaQUb&{B~_;?3-(h zd-MZVud~IB)Wb^re8JTaY>1944qVjI(Gk6Wq0b`>zvK}8GHBIrr1J-i)oGMkRvxFD zx;QKQF~T;v@mpky!d>&=!5CZK1&6UIE2KQmDlrkRUgo~+eI_~6h9#u1_(Jz^Z#&4&tzHO?5($A|$XF`gOQ`-k&_9qx;WeT-4Y@4NYq z>x|b)yu0KQn6LGD@~P24@j<;V@2YZWY3u4V8{px)yLVtNjFZp8F7piLFdm{cV<64l zT7*5hjDM0<$T$`c5P9Sr`QL-}$Iq9?F{C|l+J6@-a`$2P!89B{pNku%dvS}nB@(mf z9^;}&Y=YZoR^#H)CEPoGQ;D-UZTmyGZj5|7-=@QI4GlGEucFic>Y=GuL$j%&NBGX3 zvA5u#9*JdX3D}eq#@Oq{u-ufzy}0+OM|W}OI%9M)refT_JjUbVzFOKsRct=T!Uu58 z+>L9OB#skftTMjZeeSiFm`V~$P2%WWD0af`{3XcRZuw-HQ|Bd&AMks2{&mZup{uJ| zx1ZUpsCkhKo_Hj0!h9w6^Y^eyKZ<4PDJUh@LwWp<41^y%uEZ58k0r!1RKyLM%kTDy z6Uj=9Ac+qo@gXE0oWuf>7*G=X>vW!RS<2e}v-N9e{*jG;UH;{MPNAyl4j%g9xamQ@ zx2K1%gA?Og&Wg{%mK^4PI*_sULJ++phWn@QDX}r0#lqnCLiNAzPQg2p*e28+#x%Ok zSXS3hEXAcm?5pmXb@FJ|D6#SXzYftujRuWsH#2cD4f`%Ky32tP|wOTbZNDiuPl~&$ys?1pHqJ4mq>M1-otd&_mLf?hgtPpBG)DEpwqlT{fa^7!E8cC9MGjEcl9z-6mD!=3}GL$h*Tb8SDJp)bK zfqk1E`^dlW$y)c8oFifAHKpdgF0FN3HPqGL=li5S|4jMGvYKY&hV|=B?ro6ZGqwKH zAoHfMv;CIqIm*yI>I`hgx1n!HpX(fCtpkaE#*jI+=){zrUVNRlBFFlSGBzIjhR`2J)SqAjwmiy)d zrq_hUq%P>StxWO9_hX;!YxX^j-RCIl2oyg&eenLq^o!H~9z@@`tbG`^CPDEN2@hX* zhGXcAsb&0Pi_H){c;Ps6aI3n%*?Wcld`ATR$T4~OzS z1`9^%eN6xI)3xR`p81xetn0&glYyagl1 z&iI=1Jv}+s^fCR`MjlHrf4Bw)bW(GznmK)tTFWPET#c} z#yOb4dVqQ{qozKmgG(o9G+RAW^Vc}1I`pqKLlXV6@y^ZR=kgIOqXRMa5a*kS8}FU! zi*(d`lG~0=(l=1 z=8x8TP+eQG$Iq%^g|SArwN^UVNdMj@_vQ#;S>}0+!nD+Uu6t{T*nmOESltsl#h(|_ ztHPI8?#J5|`Z+fH_kmf-eZ_xg%DI2>-A&^Bq4@1aP-n$wH!NTSz6jU`^9gT1sjIK7 zH+^P{R?pJ>JbJD^^KXKHd0jCg`5dg`H(}{o2L!GB7OU3T(bj*7f~c>sXWc;hwT93~ zWQ4-?M%WuS6ggqvz~=Nd%%ShZjCEj2--9uIhZ8vm_Z`Q%BX-`$h|tZ7zshiRE|~u} zEWCUVl_SVf%P_`;>v3FKbIm|FhG*Qyx_R$nqgO}xhnUa@*q1)E!HPesC}~`UFG}Ty znnHicICvi}Q2b98^nZx&X$I$i#qTtpYe>eY9mSBXkML>4X?(I~KdLu+L%xmA?7(nC z&34hwwTT&E&H3pE7|yx5sY&}_9=9Iuo5JXa?9V(Ib9>8l`ee-LZ?VRSopvO9lC9!@ zIk4Fj^Uhs^74tV|{x*lMV9}O%ER0zSo7fl(r`_z9@QCx}51#1MZqT3kKP!L3I%@So z<~Atn8F3cFXfwpOGLH5}{3<5Y39AnI0x<5oV%~CrS%^3e1&E7$u0NbC<;rsB*NTDqKE6U%+|6R8HY|JxTEah#%wBUN8EM4#9@H zXK{!)$(;9}LF@=Ah2r(yI@$lUl;wwW2Z zj3Z6ooMq8tSg}33>^*@+`;RjA2j}?&Gg{`qc@`YuTjw?)HfJ5&_a1>e=a9{diP6Kg zC?=f$6Cc44+Msqpm+xy;*L$w+D>0$>j)_lF5BlU}Zd>rLO&OzbLBSbBDhd-1c40|K^?8f%L@m7q)ZL$_Z)e?;Z|BVY@X1Vs?~cvHz&MFZ7qXCZ`VcXh%8+|paCZ)3=ZXCYE&87G zPZ^xMjKhT!fpFhLd&GIy={tVJ%wXSpbz8nEb5PIP+)MISUDE#Wi_c8@MjZXcJ#9N& zcAkaj_Py}VNP;`(Bd=fIgQV||D%c=%*)|pxoJCS0aUO{!Cip&r=^}HFMa662L#(-g zu{&=CHFiw#lkV82+s5$o;}AH!i-xUS#+#oE>Y$Y ze}-%RRmK{YIOdy~KikG>UtD4Q;8S_VmpyE|$hhcVInk#a6AC=DqQn!`3n8c_#bV6|L;@J*@8P9Ox&x~6e@%_#*((nIE2j?fXTDO0% z=V%YFw6XDrA52L;1CP8?#^$<5|KuZ>?R*T2ZC4Rju$36E{kL9~g2fLFex8 tlLmj&D5*r*)Z+@O#&cCt{iaq@@r=Bic^Lf4yqKRW+_)L7q - - - - - - Cryptonote Mining Pool - - - - - - - - - - + + + + + + + Arqma with PLE or XCY or TRTL Mining Pool - Merged Mining by Muscleman + + + + + + + + + + + + + + + + -
- - - - - -
-
Network: N/A
-
Pool: N/A
-
You: N/A
-
Stats Updated  
-
-
- - -
-
-

-
- -
- - - - - - - - - - +
+ + + + + +
+
Network: N/A
+
Prop Pool: N/A
+
Solo Pool: N/A
+
You: N/A
+
Stats Updated  
+
+
+ + +
+
+

+
+ +
+ + + + + + + + + + + diff --git a/website_example/js/common.js b/website_example/js/common.js index ba7341073..bcdf96fe3 100644 --- a/website_example/js/common.js +++ b/website_example/js/common.js @@ -7,45 +7,53 @@ /** * Layout **/ - + // Collapse menu on load for mobile devices -$('#menu-content').collapse('hide'); +$('#menu-content') + .collapse('hide'); /** * Cookies handler **/ var docCookies = { - getItem: function (sKey) { - return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; - }, - setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) { - if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } - var sExpires = ""; - if (vEnd) { - switch (vEnd.constructor) { - case Number: - sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; - break; - case String: - sExpires = "; expires=" + vEnd; - break; - case Date: - sExpires = "; expires=" + vEnd.toUTCString(); - break; - } - } - document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); - return true; - }, - removeItem: function (sKey, sPath, sDomain) { - if (!sKey || !this.hasItem(sKey)) { return false; } - document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : ""); - return true; - }, - hasItem: function (sKey) { - return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); - } + getItem: function (sKey) { + return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey) + .replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; + }, + setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) { + if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { + return false; + } + var sExpires = ""; + if (vEnd) { + switch (vEnd.constructor) { + case Number: + sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; + break; + case String: + sExpires = "; expires=" + vEnd; + break; + case Date: + sExpires = "; expires=" + vEnd.toUTCString(); + break; + } + } + document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); + return true; + }, + removeItem: function (sKey, sPath, sDomain) { + if (!sKey || !this.hasItem(sKey)) { + return false; + } + document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : ""); + return true; + }, + hasItem: function (sKey) { + return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey) + .replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")) + .test(document.cookie); + } }; /** @@ -56,201 +64,228 @@ var docCookies = { var currentPage; // Handle hash change -window.onhashchange = function(){ - routePage(); +window.onhashchange = function () { + routePage(); }; // Route to page var xhrPageLoading; -function routePage(loadedCallback) { - if (currentPage) currentPage.destroy(); - $('#page').html(''); - $('#loading').show(); - - if (xhrPageLoading) { - xhrPageLoading.abort(); - } - - $('.hot_link').parent().removeClass('active'); - var $link = $('a.hot_link[href="' + (window.location.hash || '#') + '"]'); - - $link.parent().addClass('active'); - var page = $link.data('page'); - - loadTranslations(); - - xhrPageLoading = $.ajax({ - url: 'pages/' + page, - cache: false, - success: function (data) { - $('#menu-content').collapse('hide'); - $('#loading').hide(); - $('#page').show().html(data); - loadTranslations(); - if (currentPage) currentPage.update(); - if (loadedCallback) loadedCallback(); - } - }); + +function routePage (loadedCallback) { + if (currentPage) currentPage.destroy(); + $('#page') + .html(''); + $('#loading') + .show(); + + if (xhrPageLoading) { + xhrPageLoading.abort(); + } + + $('.hot_link') + .parent() + .removeClass('active'); + var $link = $('a.hot_link[href="' + (window.location.hash || '#') + '"]'); + + $link.parent() + .addClass('active'); + var page = $link.data('page'); + + loadTranslations(); + + xhrPageLoading = $.ajax({ + url: 'pages/' + page, + cache: false, + success: function (data) { + $('#menu-content') + .collapse('hide'); + $('#loading') + .hide(); + $('#page') + .show() + .html(data); + loadTranslations(); + if (currentPage) currentPage.update(); + if (loadedCallback) loadedCallback(); + } + }); } /** * Strings **/ - + // Add .update() custom jQuery function to update text content -$.fn.update = function(txt){ - var el = this[0]; - if (el.textContent !== txt) - el.textContent = txt; - return this; +$.fn.update = function (txt) { + var el = this[0]; + if (el && el.textContent !== txt) + el.textContent = txt; + return this; }; // Update Text classes -function updateTextClasses(className, text){ - var els = document.getElementsByClassName(className); - if (els) { - for (var i = 0; i < els.length; i++){ - var el = els[i]; - if (el && el.textContent !== text) - el.textContent = text; - } - } +function updateTextClasses (className, text) { + var els = document.getElementsByClassName(className); + if (els) { + for (var i = 0; i < els.length; i++) { + var el = els[i]; + if (el && el.textContent !== text) + el.textContent = text; + } + } } // Update Text content -function updateText(elementId, text){ - var el = document.getElementById(elementId); - if (el && el.textContent !== text){ - el.textContent = text; - } - return el; +function updateText (elementId, text) { + var el = document.getElementById(elementId); + if (el && el.textContent !== text) { + el.textContent = text; + } + return el; } // Convert float to string -function floatToString(float) { - return float.toFixed(6).replace(/\.0+$|0+$/, ''); +function floatToString (float) { + return float.toFixed(6) + .replace(/[0\.]+$/, ''); } // Format number -function formatNumber(number, delimiter){ - if(number != '') { - number = number.split(delimiter).join(''); +function formatNumber (number, delimiter) { + if (number != '') { + number = number.split(delimiter) + .join(''); - var formatted = ''; - var sign = ''; + var formatted = ''; + var sign = ''; - if(number < 0){ - number = -number; - sign = '-'; - } + if (number < 0) { + number = -number; + sign = '-'; + } - while(number >= 1000){ - var mod = number % 1000; + while (number >= 1000) { + var mod = number % 1000; - if(formatted != '') formatted = delimiter + formatted; - if(mod == 0) formatted = '000' + formatted; - else if(mod < 10) formatted = '00' + mod + formatted; - else if(mod < 100) formatted = '0' + mod + formatted; - else formatted = mod + formatted; + if (formatted != '') formatted = delimiter + formatted; + if (mod == 0) formatted = '000' + formatted; + else if (mod < 10) formatted = '00' + mod + formatted; + else if (mod < 100) formatted = '0' + mod + formatted; + else formatted = mod + formatted; - number = parseInt(number / 1000); - } + number = parseInt(number / 1000); + } - if(formatted != '') formatted = sign + number + delimiter + formatted; - else formatted = sign + number; - return formatted; - } - return ''; + if (formatted != '') formatted = sign + number + delimiter + formatted; + else formatted = sign + number; + return formatted; + } + return ''; } // Format date -function formatDate(time){ - if (!time) return ''; - return new Date(parseInt(time) * 1000).toLocaleString(); +function formatDate (time) { + if (!time) return ''; + return new Date(parseInt(time) * 1000) + .toLocaleString(); } // Format percentage -function formatPercent(percent) { - if (!percent && percent !== 0) return ''; - return percent + '%'; +function formatPercent (percent) { + if (!percent && percent !== 0) return ''; + return percent + '%'; } // Get readable time -function getReadableTime(seconds){ - var units = [ [60, 'second'], [60, 'minute'], [24, 'hour'], - [7, 'day'], [4, 'week'], [12, 'month'], [1, 'year'] ]; - - function formatAmounts(amount, unit){ - var rounded = Math.round(amount); - var unit = unit + (rounded > 1 ? 's' : ''); - if (getTranslation(unit)) unit = getTranslation(unit); - return '' + rounded + ' ' + unit; - } - - var amount = seconds; - for (var i = 0; i < units.length; i++){ - if (amount < units[i][0]) { - return formatAmounts(amount, units[i][1]); - } - amount = amount / units[i][0]; - } - return formatAmounts(amount, units[units.length - 1][1]); +function getReadableTime (seconds) { + var units = [[60, 'second'], [60, 'minute'], [24, 'hour'], + [7, 'day'], [4, 'week'], [12, 'month'], [1, 'year']]; + + function formatAmounts (amount, unit) { + var rounded = Math.round(amount); + var unit = unit + (rounded > 1 ? 's' : ''); + if (getTranslation(unit)) unit = getTranslation(unit); + return '' + rounded + ' ' + unit; + } + + var amount = seconds; + for (var i = 0; i < units.length; i++) { + if (amount < units[i][0]) { + return formatAmounts(amount, units[i][1]); + } + amount = amount / units[i][0]; + } + return formatAmounts(amount, units[units.length - 1][1]); } // Get readable hashrate -function getReadableHashRateString(hashrate){ - if (!hashrate) hashrate = 0; - - var i = 0; - var byteUnits = [' H', ' kH', ' MH', ' GH', ' TH', ' PH' ]; - if (hashrate > 0) { - while (hashrate > 1000){ - hashrate = hashrate / 1000; - i++; - } - } - return parseFloat(hashrate).toFixed(2) + byteUnits[i]; -} - -// Get coin decimal places -function getCoinDecimalPlaces() { - if (typeof coinDecimalPlaces != "undefined") return coinDecimalPlaces; - else if (lastStats.config.coinDecimalPlaces) return lastStats.config.coinDecimalPlaces; - else lastStats.config.coinUnits.toString().length - 1; -} - -// Get readable coins -function getReadableCoins(coins, digits, withoutSymbol){ - var coinDecimalPlaces = getCoinDecimalPlaces(); - var amount = parseFloat((parseInt(coins || 0) / lastStats.config.coinUnits).toFixed(digits || coinDecimalPlaces)); - return amount.toString() + (withoutSymbol ? '' : (' ' + lastStats.config.symbol)); +function getReadableHashRateString (hashrate) { + var i = 0; + var byteUnits = [' H', ' KH', ' MH', ' GH', ' TH', ' PH']; + while (hashrate > 1000) { + hashrate = hashrate / 1000; + i++; + } + if (typeof hashrate != 'number') + hashrate = 0; + return hashrate.toFixed(2) + byteUnits[i]; +} + +function getCoinDecimalPlace (stats) { + if (typeof coinDecimalPlaces != "undefined") return coinDecimalPlaces; + else if (stats.config.coinDecimalPlaces) return stats.config.coinDecimalPlaces; + else stats.config.coinUnits.toString() + .length - 1; } +function getReadableCoin (stats, coins, digits, withoutSymbol) { + let coinDecimalPlaces = getCoinDecimalPlace(stats) + let amount = parseFloat((parseInt(coins || 0) / stats.config.coinUnits) + .toFixed(digits || coinDecimalPlaces)) + return amount.toString() + (withoutSymbol ? '' : (' ' + stats.config.symbol)); +} + + // Format payment link -function formatPaymentLink(hash){ - return '' + hash + ''; +function formatPaymentLink (hash, merged) { + return '' + hash + ''; } // Format difficulty -function formatDifficulty(x) { - return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " "); +function formatDifficulty (x) { + return x.toString() + .replace(/\B(?=(\d{3})+(?!\d))/g, " "); } // Format luck / current effort -function formatLuck(difficulty, shares) { - var percent = Math.round(shares / difficulty * 100); - if(!percent){ - return '?'; - } - else if(percent <= 100){ - return '' + percent + '%'; - } - else if(percent >= 101 && percent <= 150){ - return '' + percent + '%'; - } - else{ - return '' + percent + '%'; - } +function formatLuck (difficulty, shares, solo = false) { + // Only an approximation to reverse the calculations done in pool.js, because the shares with their respective times are not recorded in redis + // Approximation assumes equal pool hashrate for the whole round + // Could potentially be replaced by storing the sum of all job.difficulty in the redis db. + if (lastStats.config.slushMiningEnabled) { + // Uses integral calculus to calculate the average of a dynamic function + var accurateShares = 1 / lastStats.config.blockTime * ( // 1/blockTime to get the average + shares * lastStats.config.weight * ( // Basically calculates the 'area below the graph' between 0 and blockTime + 1 - Math.pow( + Math.E, + ((-lastStats.config.blockTime) / lastStats.config.weight) // blockTime is equal to the highest possible result of (dateNowSeconds - scoreTime) + ) + ) + ); + } else { + var accurateShares = shares; + } + + var percent = Math.round(accurateShares / difficulty * 100); + if (!percent) { + return `?` + (solo === true ? `` : ``); + } else if (percent <= 100) { + return `${percent}% ` + (solo === true ? `` : ``); + } else if (percent >= 101 && percent <= 150) { + return `${percent}% ` + (solo === true ? `` : ``); + } else { + return `${percent}% ` + (solo === true ? `` : ``); + } } /** @@ -258,52 +293,72 @@ function formatLuck(difficulty, shares) { **/ // Return pool host -function getPoolHost() { - if (typeof poolHost != "undefined") return poolHost; - if (lastStats.config.poolHost) return lastStats.config.poolHost; - else return window.location.hostname; +function getPoolHost () { + if (typeof poolHost != "undefined") return poolHost; + if (lastStats.config.poolHost) return lastStats.config.poolHost; + else return window.location.hostname; } // Return transaction URL -function getTransactionUrl(id) { - return transactionExplorer.replace(new RegExp('{symbol}', 'g'), lastStats.config.symbol.toLowerCase()).replace(new RegExp('{id}', 'g'), id); +function getTransactionUrl (id, stats) { + if (stats && blockExplorers) { + return blockExplorers[stats.config.coin].transactionExplorer.replace('{symbol}', stats.config.symbol.toLowerCase()) + .replace('{id}', id); + } } // Return blockchain explorer URL -function getBlockchainUrl(id) { - return blockchainExplorer.replace(new RegExp('{symbol}', 'g'), lastStats.config.symbol.toLowerCase()).replace(new RegExp('{id}', 'g'), id); +function getBlockchainUrl (id, stats) { + if (stats && blockExplorers) { + return blockExplorers[stats.config.coin].blockchainExplorer.replace('{symbol}', stats.config.symbol.toLowerCase()) + .replace('{id}', id); + } } - + /** * Tables **/ - + // Sort table cells -function sortTable() { - var table = $(this).parents('table').eq(0), - rows = table.find('tr:gt(0)').toArray().sort(compareTableRows($(this).index())); - this.asc = !this.asc; - if(!this.asc) { - rows = rows.reverse() - } - for(var i = 0; i < rows.length; i++) { - table.append(rows[i]) - } +function sortTable () { + var table = $(this) + .parents('table') + .eq(0), + rows = table.find('tr:gt(0)') + .toArray() + .sort(compareTableRows($(this) + .index())); + this.asc = !this.asc; + if (!this.asc) { + rows = rows.reverse() + } + for (var i = 0; i < rows.length; i++) { + table.append(rows[i]) + } } // Compare table rows -function compareTableRows(index) { - return function(a, b) { - var valA = getCellValue(a, index), valB = getCellValue(b, index); - if (!valA) { valA = 0; } - if (!valB) { valB = 0; } - return $.isNumeric(valA) && $.isNumeric(valB) ? valA - valB : valA.toString().localeCompare(valB.toString()) - } +function compareTableRows (index) { + return function (a, b) { + var valA = getCellValue(a, index), + valB = getCellValue(b, index); + if (!valA) { + valA = 0; + } + if (!valB) { + valB = 0; + } + return $.isNumeric(valA) && $.isNumeric(valB) ? valA - valB : valA.toString() + .localeCompare(valB.toString()) + } } // Get table cell value -function getCellValue(row, index) { - return $(row).children('td').eq(index).data("sort") +function getCellValue (row, index) { + return $(row) + .children('td') + .eq(index) + .data("sort") } /** @@ -311,112 +366,2412 @@ function getCellValue(row, index) { **/ if (typeof langs == "undefined") { - var langs = { en: 'English' }; + var langs = { + en: 'English' + }; } if (typeof defaultLang == "undefined") { - var defaultLang = 'en'; + var defaultLang = 'en'; } var langCode = defaultLang; -var langData = null; +var langData = null; -function getTranslation(key) { - if (!langData || !langData[key]) return null; - return langData[key]; +function getTranslation (key) { + if (!langData || !langData[key]) return null; + return langData[key]; } -var translate = function(data) { - langData = data; +var translate = function (data) { + $("html")[0].lang = langCode; + langData = data; - $("[tkey]").each(function(index) { - var strTr = data[$(this).attr('tkey')]; - $(this).html(strTr); - }); + $("[data-tkey]") + .each(function (index) { + var strTr = data[$(this) + .attr('data-tkey')]; + $(this) + .html(strTr); + }); - $("[tplaceholder]").each(function(index) { - var strTr = data[$(this).attr('tplaceholder')]; - $(this).attr('placeholder', strTr) - }); + $("[data-tplaceholder]") + .each(function (index) { + var strTr = data[$(this) + .attr('data-tplaceholder')]; + $(this) + .attr('placeholder', strTr) + }); - $("[tvalue]").each(function(index) { - var strTr = data[$(this).attr('tvalue')]; - $(this).attr('value', strTr) - }); -} + $("[data-tvalue]") + .each(function (index) { + var strTr = data[$(this) + .attr('data-tvalue')]; + $(this) + .attr('value', strTr) + }); +} // Get language code from URL const $_GET = {}; -const args = location.search.substr(1).split(/&/); -for (var i=0; i' + langs[lang] + ''; - numLangs ++; - } - html += ''; - } - if (html && numLangs > 1) { - $('#langSelector').html(html); - $('#newLang').each(function(){ - $(this).change(function() { - var newLang = $(this).val(); - var url = '?lang=' + newLang; - if (window.location.hash) url += window.location.hash; - window.location.href = url; - }); - }); - } - - // Mobile - var html = ''; - var numLangs = 0; - if (langs) { - html += ''; - } - if (html && numLangs > 1) { - $('#mLangSelector').html(html); - $('#mNewLang').each(function(){ - $(this).change(function() { - var newLang = $(this).val(); - var url = '?lang=' + newLang; - if (window.location.hash) url += window.location.hash; - window.location.href = url; - }); - }); - } +function renderLangSelector () { + // Desktop + var html = ''; + var numLangs = 0; + if (langs) { + html += ''; + } + if (html && numLangs > 1) { + $('#langSelector') + .html(html); + $('#newLang') + .each(function () { + $(this) + .change(function () { + var newLang = $(this) + .val(); + var url = '?lang=' + newLang; + if (window.location.hash) url += window.location.hash; + window.location.href = url; + }); + }); + } + + // Mobile + var html = ''; + var numLangs = 0; + if (langs) { + html += ''; + } + if (html && numLangs > 1) { + $('#mLangSelector') + .html(html); + $('#mNewLang') + .each(function () { + $(this) + .change(function () { + var newLang = $(this) + .val(); + var url = '?lang=' + newLang; + if (window.location.hash) url += window.location.hash; + window.location.href = url; + }); + }); + } +} + + +/* +*************************************************************** +pool_block methods +*************************************************************** +*/ + +function poolBlocks_GenerateChart (data, displayedChart) { + if (displayedChart[data.config.coin] || !data.charts.blocks || data.charts.blocks === "undefined" || !data.charts.blocksSolo || data.charts.blocksSolo === "undefined") return; + let chartDays = data.config.blocksChartDays || null; + let title = getTranslation('poolBlocks') ? getTranslation('poolBlocks') : 'Blocks found'; + if (chartDays) { + if (chartDays === 1) title = getTranslation('blocksFoundLast24') ? getTranslation('blocksFoundLast24') : 'Blocks found in the last 24 hours'; + else title = getTranslation('blocksFoundLastDays') ? getTranslation('blocksFoundLastDays') : 'Blocks found in the last {DAYS} days'; + title = title.replace('{DAYS}', chartDays); + } + updateText(`blocksChartTitle${data.config.coin}`, title); + let labels = []; + let values = []; + let valuesSolo = []; + for (let key in data.charts.blocks) { + let label = key; + if (chartDays && chartDays === 1) { + let keyParts = key.split(' '); + label = keyParts[1].replace(':00', ''); + } + labels.push(label); + values.push(data.charts.blocks[key]); + } + for (let key in data.charts.blocksSolo) { + valuesSolo.push(data.charts.blocksSolo[key]); + } + + let $chart = $(`blocksChartObj${data.config.coin}`); + let bgcolor = null, + bordercolor = null, + borderwidth = null; + let colorelem = $chart.siblings('a.chart-style'); + if (colorelem.length == 1) { + bgcolor = colorelem.css('background-color'); + bordercolor = colorelem.css('border-left-color'); + borderwidth = parseFloat(colorelem.css('width')); + } + if (bgcolor === null) bgcolor = 'rgba(3, 169, 244, .4)'; + if (bordercolor === null) bordercolor = '#03a9f4'; + if (borderwidth === null || isNaN(borderwidth)) borderwidth = 1; + let chartElement = document.getElementById(`blocksChartObj${data.config.coin}`) + if (!chartElement) return + let chart = new Chart(chartElement, { + type: 'bar', + data: { + labels: labels, + datasets: [{ + label: 'Prop Blocks', + data: values, + fill: false, + backgroundColor: bgcolor, + borderColor: bordercolor, + borderWidth: borderwidth + }, + { + label: 'Solo Blocks', + data: valuesSolo, + fill: false, + backgroundColor: 'rgba(0, 230, 64, 1)', + borderColor: bordercolor, + borderWidth: borderwidth + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + legend: { + display: false + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero: true, + userCallback: function (label, index, labels) { + if (Math.floor(label) === label) return label; + } + } + }], + }, + layout: { + padding: { + top: 0, + left: 0, + right: 0, + bottom: 0 + } + } + } + }); + $(`#blocksChart${data.config.coin}`) + .show(); + displayedChart[data.config.coin] = true; +} + +// Parse block data +function poolBlocks_ParseBlock (height, serializedBlock, stats) { + var parts = serializedBlock.split(':'); + let block = {} + if (parts[0].includes('solo') || parts[0].includes('prop')) { + block = { + height: parseInt(height), + solo: parts[0] === 'solo', + address: parts[1], + hash: parts[2], + time: parts[3], + difficulty: parseInt(parts[4]), + shares: parseInt(parts[5]), + orphaned: parts[6], + reward: parts[7] + }; + } else { + block = { + height: parseInt(height), + solo: false, + address: '', + hash: parts[0], + time: parts[1], + difficulty: parseInt(parts[2]), + shares: parseInt(parts[3]), + orphaned: parts[4], + reward: parts[5] + }; + } + + var toGo = stats.config.depth - (stats.network.height - block.height - 1); + if (toGo > 1) { + block.maturity = toGo + ' to go'; + } else if (toGo == 1) { + block.maturity = ""; + } else if (toGo <= 0) { + block.maturity = ""; + } + + switch (block.orphaned) { + case '0': + block.status = 'unlocked'; + block.maturity = ""; + break; + case '1': + block.status = 'orphaned'; + block.maturity = ""; + block.reward = 0; + break; + default: + block.status = 'pending'; + break; + } + return block; +} + +// Get block row element +function getBlockRowElement (block, jsonString, stats) { + function formatBlockLink (hash, stats) { + return '' + hash + ''; + } + + var blockStatusClasses = { + 'pending': 'pending', + 'unlocked': 'unlocked', + 'orphaned': 'orphaned' + }; + + var row = document.createElement('tr'); + row.setAttribute(`data-json`, jsonString); + row.setAttribute(`data-height`, block.height); + row.setAttribute('id', `blockRow${stats.config.coin}${block.height}`); + row.setAttribute('title', block.status); + row.className = blockStatusClasses[block.status]; + + var reward = ""; + if (typeof block.reward == "undefined") { + reward = "Waiting..."; + } else { + reward = getReadableCoin(stats, block.reward, null, true); + } + + var columns = + '' + formatDate(block.time) + '' + + '' + reward + '' + + '' + block.height + '' + + '' + block.difficulty + '' + + '' + formatBlockLink(block.hash, stats) + '' + + '' + block.address + '' + + '' + formatLuck(block.difficulty, block.shares, block.solo) + '' + + '' + block.maturity + ''; + + row.innerHTML = columns; + + return row; +} + +// Render blocks +function poolBlocks_RenderBlocks (blocksResults, stats) { + var $blocksRows = $(`#blocksReport${stats.config.coin}_rows`); + + for (var i = 0; i < blocksResults.length; i += 2) { + var block = poolBlocks_ParseBlock(blocksResults[i + 1], blocksResults[i], stats); + var blockJson = JSON.stringify(block); + + var existingRow = document.getElementById(`blockRow${stats.config.coin}${block.height}`); + if (existingRow && existingRow.getAttribute(`data-json`) !== blockJson) { + $(existingRow) + .replaceWith(getBlockRowElement(block, blockJson, stats)); + } else if (!existingRow) { + var blockElement = getBlockRowElement(block, blockJson, stats); + + var inserted = false; + var rows = $blocksRows.children() + .get(); + for (var f = 0; f < rows.length; f++) { + var bHeight = parseInt(rows[f].getAttribute(`data-height`)); + if (bHeight < block.height) { + inserted = true; + $(rows[f]) + .before(blockElement); + break; + } + } + if (!inserted) { + $blocksRows.append(blockElement); + } + } + } +} + +// Load more blocks button +function poolBlocks_Setup (api, stats, xhrGetBlocks) { + $(`#loadMoreBlocks${stats.config.coin}`) + .click(function (xhrGetBlocks) { + if (xhrGetBlocks[stats.config.coin]) xhrGetBlocks[stats.config.coin].abort(); + xhrGetBlocks[stats.config.coin] = $.ajax({ + url: api + '/get_blocks', + data: { + height: $(`#blocksReport${stats.config.coin}_rows`) + .children() + .last() + .data(`height`) + }, + dataType: 'json', + cache: 'false', + success: function (data) { + poolBlocks_RenderBlocks(data, stats); + } + }); + }); +} + +function poolBlocks_InitTemplate (ranOnce, displayedChart, xhrGetBlocks) { + let coin = lastStats.config.coin + if ($(`#blocksTabs li:contains(${coin})`) + .length == 0) { + let template1 = $('#siblingTemplate') + .html() + Mustache.parse(template1) + let rendered1 = Mustache.render(template1, { + coin: lastStats.config.coin, + active: 'active' + }) + $('#tab-content') + .append(rendered1) + + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: lastStats.config.coin, + symbol: `(${lastStats.config.symbol})`, + active: 'active' + }); + $('#blocksTabs') + .append(rendered) + + poolBlocks_Setup(api, lastStats, xhrGetBlocks) + } + + + updateText(`blocksTotal${coin}`, lastStats.pool.totalBlocks.toString()); + if (lastStats.pool.lastBlockFound) { + var d = new Date(parseInt(lastStats.pool.lastBlockFound)) + .toISOString(); + $(`#lastBlockFound${coin}`) + .timeago('update', d); + } else { + $(`#lastBlockFound${coin}`) + .removeAttr('title') + .data('ts', '') + .update('Never'); + } + + updateText(`blocksTotalSolo${coin}`, lastStats.pool.totalBlocksSolo.toString()); + if (lastStats.pool.lastBlockFoundSolo) { + var d = new Date(parseInt(lastStats.pool.lastBlockFoundSolo)) + .toISOString(); + $(`#lastBlockFoundSolo${coin}`) + .timeago('update', d); + } else { + $(`#lastBlockFoundSolo${coin}`) + .removeAttr('title') + .data('ts', '') + .update('Never'); + } + + updateText(`blocksMaturityCount${coin}`, lastStats.config.depth.toString()); + + $(`#averageLuck${coin}`) + .html(formatLuck(lastStats.pool.totalDiff, lastStats.pool.totalShares)); + + displayedChart[lastStats.config.coin] = false + if (lastStats.charts.blocks) { + poolBlocks_GenerateChart(lastStats, displayedChart); + } + + poolBlocks_RenderBlocks(lastStats.pool.blocks, lastStats); + + + Object.keys(mergedStats) + .forEach(key => { + if ($(`#blocksTabs li:contains(${key})`) + .length == 0) { + let template1 = $('#siblingTemplate') + .html() + Mustache.parse(template1) + let rendered1 = Mustache.render(template1, { + coin: key + }) + $('#tab-content') + .append(rendered1) + + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: key, + symbol: `(${mergedStats[key].config.symbol})` + }); + $('#blocksTabs') + .append(rendered) + + poolBlocks_Setup(mergedApis[key].api, mergedStats[key]) + } + + updateText(`blocksTotal${key}`, mergedStats[key].pool.totalBlocks.toString()); + if (mergedStats[key].pool.lastBlockFound) { + var d = new Date(parseInt(mergedStats[key].pool.lastBlockFound)) + .toISOString(); + $(`#lastBlockFound${key}`) + .timeago('update', d); + } else { + $(`#lastBlockFound${key}`) + .removeAttr('title') + .data('ts', '') + .update('Never'); + } + + updateText(`blocksTotalSolo${key}`, mergedStats[key].pool.totalBlocksSolo.toString()); + if (mergedStats[key].pool.lastBlockFoundSolo) { + var d = new Date(parseInt(mergedStats[key].pool.lastBlockFoundSolo)) + .toISOString(); + $(`#lastBlockFoundSolo${key}`) + .timeago('update', d); + } else { + $(`#lastBlockFoundSolo${key}`) + .removeAttr('title') + .data('ts', '') + .update('Never'); + } + + updateText(`blocksMaturityCount${key}`, mergedStats[key].config.depth.toString()); + + $(`#averageLuck${key}`) + .html(formatLuck(mergedStats[key].pool.totalDiff, mergedStats[key].pool.totalShares)); + displayedChart[key] = false + if (mergedStats[key].charts.blocks) { + poolBlocks_GenerateChart(mergedStats[key], displayedChart); + } + poolBlocks_RenderBlocks(mergedStats[key].pool.blocks, mergedStats[key]); + }) + sortElementList($(`#blocksTabs`), $(`#blocksTabs>div`), mergedStats) + if (!ranOnce) + ranOnce = RunOnce() +} + +/* +*************************************************************** +top10miners methods +*************************************************************** +*/ + +function top10Miners_GetMinerCells (position, data) { + var miner = data.miner; + var hashrate = data.hashrate ? data.hashrate : 0; + var lastShare = data.lastShare ? data.lastShare : 0; + var hashes = (data.hashes || 0) + .toString(); + + return '' + position + '' + + '' + miner + '' + + '' + getReadableHashRateString(hashrate) + '/sec' + + '' + (lastShare ? $.timeago(new Date(parseInt(lastShare) * 1000) + .toISOString()) : 'Never') + '' + + '' + hashes + ''; +} + +// Update top10 miners report +function top10Miners_UpdateTop10 (xhrGetMiners, endPoint, key) { + if (xhrGetMiners[key]) + xhrGetMiners[key].abort() + + $(`#top10miners_rows${key}`) + .empty(); + + xhrGetMiners[key] = $.ajax({ + url: `${endPoint}/get_top10miners`, + data: { + time: $(`#top10_rows${key}`) + .children() + .last() + .data('time') + }, + dataType: 'json', + cache: 'false', + success: function (data) { + if (!data) return; + for (var i = 0; i < data.length; ++i) { + $(`#top10miners_rows${key}`) + .append('' + top10Miners_GetMinerCells(i + 1, data[i]) + ''); + } + } + }); +} + +function top10Miners_InitTemplate (xhrGetMiners, ranOnce) { + let coin = lastStats.config.coin + if ($(`#blocksTabs li:contains(${coin})`) + .length === 0) { + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: lastStats.config.coin, + symbol: `(${lastStats.config.symbol})`, + active: 'active' + }); + $('#blocksTabs') + .append(rendered) + + template = $('#siblingTemplate') + .html() + Mustache.parse(template) + rendered = Mustache.render(template, { + coin: coin, + active: 'active' + }) + $('#tab-content') + .append(rendered) + } + + top10Miners_UpdateTop10(xhrGetMiners, api, coin); + + Object.keys(mergedStats) + .forEach(key => { + if ($(`#blocksTabs li:contains(${key})`) + .length === 0) { + coin = key + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: mergedStats[key].config.coin, + symbol: `(${mergedStats[key].config.symbol})` + }); + $('#blocksTabs') + .append(rendered) + + template = $('#siblingTemplate') + .html() + Mustache.parse(template) + rendered = Mustache.render(template, { + coin: coin + }) + $('#tab-content') + .append(rendered) + } + top10Miners_UpdateTop10(xhrGetMiners, mergedApis[key].api, key); + }) + sortElementList($(`#blocksTabs`), $(`#blocksTabs>li`), mergedStats) + if (!ranOnce) + ranOnce = RunOnce() +} + +/* +*************************************************************** +settings methods +*************************************************************** +*/ + +function settings_Setup (api, stats) { + + var address = getCurrentAddress(stats.config.coin); + if (address) { + $(`#yourAddress${stats.config.coin}`) + .val(address); + settings_GetPayoutLevel(api, address, stats); + settings_GetEmailAddress(api, address, stats); + } + + // Handle click on Set button + $(`#payoutSetButton${stats.config.coin}`) + .click(function () { + var address = $(`#yourAddress${stats.config.coin}`) + .val() + .trim(); + if (!address || address == '') { + settings_ShowError('noMinerAddress', 'No miner address specified', '', false); + return; + } + + var ip = $(`#yourIP${stats.config.coin}`) + .val() + .trim(); + if (!ip || ip == '') { + settings_ShowError('noMinerIP', 'No miner IP address specified', '', false); + return; + } + + var level = $(`#yourPayoutRate${stats.config.coin}`) + .val() + .trim(); + if (!level || level < 0) { + settings_ShowError('noPayoutLevel', 'No payout level specified', '', false); + return; + } + settings_SetPayoutLevel(api, address, ip, level, stats); + }); + + // Handle click on Enable button + $(`#enableButton${stats.config.coin}`) + .click(function () { + var address = $(`#yourAddress${stats.config.coin}`) + .val() + .trim(); + var ip = $(`#yourIP${stats.config.coin}`) + .val() + .trim(); + var email = $(`#yourEmail${stats.config.coin}`) + .val(); + settings_SetEmailNotifications(stats, api, email, address, ip, true); + }); + + // Handle click on Disable button + $(`#disableButton${stats.config.coin}`) + .click(function () { + var address = $(`#yourAddress${stats.config.coin}`) + .val() + .trim(); + var ip = $(`#yourIP${stats.config.coin}`) + .val() + .trim(); + var email = $(`#yourEmail${stats.config.coin}`) + .val(); + settings_SetEmailNotifications(stats, api, email, address, ip, false); + }); +} + +/** + * Error Message + **/ +function settings_ShowError (id, message, extra, stats) { + if (getTranslation(id)) message = getTranslation(id); + message = message.trim(); + if (extra) message += ' ' + extra; + $(`#action_update_message${stats.config.coin}`) + .text(message); + $(`#action_update_message${stats.config.coin}`) + .removeClass() + .addClass('alert alert-danger'); +} + +/** + * Success Message + **/ +function settings_ShowSuccess (id, message, stats) { + if (getTranslation(id)) message = getTranslation(id); + $(`#action_update_message${stats.config.coin}`) + .text(message); + $(`#action_update_message${stats.config.coin}`) + .removeClass() + .addClass('alert alert-success'); +} + +/** + * Payout level + **/ + +// Get current payout level +function settings_GetPayoutLevel (api, address, stats) { + if (!address || address == '') + return; + $.ajax({ + url: `${api}/get_miner_payout_level`, + data: { + address: address + }, + dataType: 'json', + cache: 'false' + }) + .done(function (data) { + if (data.level != undefined) { + $(`#yourPayoutRate${stats.config.coin}`) + .val(data.level); + } + }); +} + +// Set payout level +function settings_SetPayoutLevel (api, address, ip, level, stats) { + let params = { + address: address, + ip: ip, + level: level + } + $.ajax({ + url: `${api}/set_miner_payout_level`, + data: params, + dataType: 'json', + cache: 'false' + }) + .done(function (data) { + if (data.status == 'done') { + settings_ShowSuccess('minerPayoutSet', 'Done! Your minimum payout level was set', stats); + } else { + settings_ShowError('Error:', data.status, null, stats); + } + }); +} + +/** + * Email Notifications + **/ + +// Check if specified value is a valid email +function settings_IsEmail (email) { + var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/; + return regex.test(email); +} + +// Get current email address for notifications +function settings_GetEmailAddress (endPoint, address, stats) { + if (!address || address == '') return; + + $.ajax({ + url: `${endPoint}/get_email_notifications`, + data: { + address: address + }, + dataType: 'json', + cache: 'false' + }) + .done(function (data) { + if (data.email != undefined) { + $(`#yourEmail${stats.config.coin}`) + .val(data.email); + } + }); +} + +// Set email address for notifications +function settings_SetEmailNotifications (stats, endPoint, email, address, ip, enable) { + var address = $(`#yourAddress${stats.config.coin}`) + .val() + .trim(); + if (!address || address == '') { + settings_ShowError('noMinerAddress', 'No miner address specified', null, stats); + return; + } + + var ip = $(`#yourIP${stats.config.coin}`) + .val() + .trim(); + if (!ip || ip == '') { + settings_ShowError('noMinerIP', 'No miner IP address specified', null, stats); + return; + } + + var email = $(`#yourEmail${stats.config.coin}`) + .val() + .trim(); + if (enable && !email) { + settings_ShowError('noEmail', 'No email address specified', null, stats); + return; + } + if (enable && !settings_IsEmail(email)) { + settings_ShowError('invalidEmail', 'Invalid email address specified', null, stats); + return; + } + + $.ajax({ + url: `${endPoint}/set_email_notifications`, + data: { + address: address, + ip: ip, + email: email, + action: enable ? 'enable' : 'disable' + }, + dataType: 'json', + cache: 'false' + }) + .done(function (data) { + if (data.status == "done") { + if (enable) { + settings_ShowSuccess('notificationEnabled', 'Done! Email notifications have been enabled', stats); + } else { + settings_ShowSuccess('notificationDisabled', 'Done! Email notifications have been disabled', stats); + } + } else { + settings_ShowError('error', 'Error:', data.status, stats); + } + }); +} + +function settings_InitTemplate (ranOnce) { + if (!lastStats.config.sendEmails) $(`#emailNotifications${lastStats.config.coin}`) + .hide(); + + let coin = lastStats.config.coin + let template = $('#siblingTemplate') + .html() + if ($(`#blocksTabs li:contains(${coin})`) + .length === 0) { + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: coin, + active: 'active' + }) + $('#tab-content') + .append(rendered) + + template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + rendered = Mustache.render(template, { + coin: lastStats.config.coin, + symbol: `(${lastStats.config.symbol})`, + active: 'active' + }); + $('#blocksTabs') + .append(rendered) + settings_Setup(api, lastStats) + } + + Object.keys(mergedStats) + .forEach(key => { + if ($(`#blocksTabs li:contains(${key})`) + .length === 0) { + if (!mergedStats[key].config.sendEmails) + $(`#emailNotifications${mergedStats[key].config.coin}`) + .hide(); + template = $('#siblingTemplate') + .html() + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: key + }) + $('#tab-content') + .append(rendered) + + template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + rendered = Mustache.render(template, { + coin: key, + symbol: `(${mergedStats[key].config.symbol})` + }); + $('#blocksTabs') + .append(rendered) + + settings_Setup(mergedApis[key].api, mergedStats[key]) + } + }) + sortElementList($(`#blocksTabs`), $(`#blocksTabs>li`), mergedStats) + if (!ranOnce) + ranOnce = RunOnce() +} + +/* +*************************************************************** +payments methods +*************************************************************** +*/ + +// Parse payment data +function payments_ParsePayment (time, serializedPayment) { + var parts = serializedPayment.split(':'); + return { + time: parseInt(time), + hash: parts[0], + amount: parts[1], + fee: parts[2], + mixin: parts[3], + recipients: parts[4] + }; +} + +// Get payment cells +function payments_GetPaymentCells (payment, stats) { + return '' + formatDate(payment.time) + '' + + '' + formatPaymentLink(payment.hash, stats) + '' + + '' + (getReadableCoin(stats, payment.amount)) + '' + + '' + (getReadableCoin(stats, payment.fee)) + '' + + '' + payment.mixin + '' + + '' + payment.recipients + ''; +} + +// Get payment row element +function payments_GetPaymentRowElement (payment, jsonString, stats) { + var row = document.createElement('tr'); + row.setAttribute(`data-json`, jsonString); + row.setAttribute(`data-time`, payment.time); + row.setAttribute('id', `paymentRow${stats.config.coin}${payment.time}`); + + row.innerHTML = payments_GetPaymentCells(payment, stats); + + return row; +} + +// Render payments data +function payments_renderPayments (paymentsResults, stats) { + var $paymentsRows = $(`#paymentsReport${stats.config.coin}_rows`); + for (var i = 0; i < paymentsResults.length; i += 2) { + var payment = payments_ParsePayment(paymentsResults[i + 1], paymentsResults[i]); + var paymentJson = JSON.stringify(payment); + var existingRow = document.getElementById(`paymentRow${stats.config.coin}${payment.time}`); + + if (existingRow && existingRow.getAttribute(`data-json`) !== paymentJson) { + $(existingRow) + .replaceWith(payments_GetPaymentRowElement(payment, paymentJson, stats)); + } else if (!existingRow) { + var paymentElement = payments_GetPaymentRowElement(payment, paymentJson, stats); + + var inserted = false; + var rows = $paymentsRows.children() + .get(); + for (var f = 0; f < rows.length; f++) { + var pTime = parseInt(rows[f].getAttribute(`data-time`)); + if (pTime < payment.time) { + inserted = true; + $(rows[f]) + .before(paymentElement); + break; + } + } + if (!inserted) { + $paymentsRows.append(paymentElement); + } + } + } +} + +// Load more payments button +function payments_Setup (xhrGetPayments, api, stats) { + $(`#loadMorePayments${stats.config.coin}`) + .click(function () { + if (xhrGetPayments[stats.config.coin]) xhrGetPayments[stats.config.coin].abort(); + xhrGetPayments[stats.config.coin] = $.ajax({ + url: api + '/get_payments', + data: { + time: $(`#paymentsReport${stats.config.coin}_rows`) + .children() + .last() + .data(`time`) + }, + dataType: 'json', + cache: 'false', + success: function (data) { + payments_renderPayments(data, stats); + } + }); + }); +} + +function payments_InitTemplate (xhrGetPayments, ranOnce) { + let coin = lastStats.config.coin + if ($(`#blocksTabs li:contains(${coin})`) + .length === 0) { + let template1 = $('#siblingTemplate') + .html() + Mustache.parse(template1) + let rendered1 = Mustache.render(template1, { + coin: coin, + active: 'active' + }) + $('#tab-content') + .append(rendered1) + + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: lastStats.config.coin, + symbol: `(${lastStats.config.symbol})`, + active: 'active' + }); + $('#blocksTabs') + .append(rendered) + + payments_Setup(xhrGetPayments, api, lastStats) + } + updateText(`paymentsTotal${coin}`, lastStats.pool.totalPayments.toString()); + updateText(`paymentsTotalPaid${coin}`, lastStats.pool.totalMinersPaid.toString()); + updateText(`paymentsInterval${coin}`, getReadableTime(lastStats.config.paymentsInterval)); + updateText(`paymentsMinimum${coin}`, getReadableCoin(lastStats, lastStats.config.minPaymentThreshold)); + updateText(`paymentsDenomination${coin}`, getReadableCoin(lastStats, lastStats.config.denominationUnit, 3)); + payments_renderPayments(lastStats.pool.payments, lastStats); + + Object.keys(mergedStats) + .forEach(key => { + if ($(`#blocksTabs li:contains(${key})`) + .length === 0) { + + let template1 = $('#siblingTemplate') + .html() + Mustache.parse(template1) + let rendered1 = Mustache.render(template1, { + coin: key + }) + $('#tab-content') + .append(rendered1) + + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: key, + symbol: `(${mergedStats[key].config.symbol})` + }); + $('#blocksTabs') + .append(rendered) + + payments_Setup(xhrGetPayments, mergedApis[key].api, mergedStats[key]) + } + + updateText(`paymentsTotal${key}`, mergedStats[key].pool.totalPayments.toString()); + updateText(`paymentsTotalPaid${key}`, mergedStats[key].pool.totalMinersPaid.toString()); + updateText(`paymentsInterval${key}`, getReadableTime(mergedStats[key].config.paymentsInterval)); + updateText(`paymentsMinimum${key}`, getReadableCoin(mergedStats[key], mergedStats[key].config.minPaymentThreshold)); + updateText(`paymentsDenomination${key}`, getReadableCoin(mergedStats[key], mergedStats[key].config.denominationUnit, 3)); + payments_renderPayments(mergedStats[key].pool.payments, mergedStats[key]); + }) + sortElementList($(`#blocksTabs`), $(`#blocksTabs>li`), mergedStats) + if (!ranOnce) + ranOnce = RunOnce() +} + +/* +*************************************************************** +market methods +*************************************************************** +*/ + +function market_LoadMarketData (api, stats, loadedData, currencyPairs, xhrMarketGets, marketPrices) { + if (loadedData[stats.config.coin]) return; + + if (typeof marketCurrencies !== 'undefined' && marketCurrencies.length > 0) { + let intervalMarketPolling = setInterval(market_UpdateMarkets(api, stats, currencyPairs, xhrMarketGets, marketPrices), 300000); + market_UpdateMarkets(api, stats, currencyPairs, xhrMarketGets, marketPrices); + } else { + $(`#marketInfos${stats.config.coin}`) + .hide(); + } + + loadedData[stats.config.coin] = true; +} + +// Market data polling (poll data every 5 minutes) +function market_UpdateMarkets (api, stats, currencyPairs, xhrMarketGets, marketPrices) { + if (typeof marketCurrencies === 'undefined' || marketCurrencies.length === 0) return; + + currencyPairs[stats.config.coin] = [] + + for (let i = 0; i < marketCurrencies.length; i++) { + currencyPairs[stats.config.coin].push(marketCurrencies[i].replace('{symbol}', stats.config.symbol) + .toUpperCase()); + } + + if (xhrMarketGets[stats.config.coin]) xhrMarketGets[stats.config.coin].abort() + + xhrMarketGets[stats.config.coin] = $.ajax({ + url: api + '/get_market', + data: { + tickers: currencyPairs[stats.config.coin] + }, + dataType: 'json', + cache: 'false', + success: function (data) { + if (!data || data.length === 0) { + $(`#marketInfos${stats.config.coin}`) + .hide(); + return; + } + + $(`#marketInfos${stats.config.coin}`) + .empty(); + for (let i in data) { + if (!data[i] || !data[i].ticker) continue; + let ticker = data[i].ticker; + let tickerParts = ticker.split('-'); + let tickerBase = tickerParts[0] || null; + let tickerTarget = tickerParts[1] || null; + + let price = data[i].price; + if (!price || price === 0) continue; + + let dataSource = data[i].source; + + market_RenderMarketPrice(tickerBase, tickerTarget, price, dataSource, stats, marketPrices); + } + $(`#marketInfos${stats.config.coin}`) + .show(); + }, + error: function () { + $(`#marketInfos${stats.config.coin}`) + .hide(); + } + }); +} + +// Render market price +function market_RenderMarketPrice (base, target, price, source, stats, marketPrices) { + let icon = 'fa-money'; + if (target == 'BTC') icon = 'fa-btc'; + if (target == 'BCH') icon = 'fa-btc'; + if (target == 'USD') icon = 'fa-dollar'; + if (target == 'CAD') icon = 'fa-dollar'; + if (target == 'EUR') icon = 'fa-eur'; + if (target == 'GBP') icon = 'fa-gbp'; + if (target == 'JPY') icon = 'fa-jpy'; + + if (base == stats.config.symbol.toUpperCase()) { + marketPrices[stats.config.coin][target] = price; + } + + if (target == 'USD' || target == 'CAD' || target == 'EUR' || target == 'GBP' || target == 'JPY') { + price = price.toFixed(4); + } else { + price = price.toFixed(8); + } + + let sourceURL = null; + if (source == 'cryptonator') sourceURL = 'https://www.cryptonator.com/'; + else if (source == 'altex') sourceURL = 'https://altex.exchange/'; + else if (source == 'crex24') sourceURL = 'https://crex24.com/'; + else if (source == 'cryptopia') sourceURL = 'https://www.cryptopia.co.nz/'; + else if (source == 'stocks.exchange') sourceURL = 'https://stocks.exchange/'; + else if (source == 'tradeogre') sourceURL = 'https://tradeogre.com/'; + + source = source.charAt(0) + .toUpperCase() + source.slice(1); + if (sourceURL) source = '' + source + ''; + + $(`#marketInfos${stats.config.coin}`) + .append( + '
' + + '
' + + '
' + + '
' + base + ' to ' + target + '
' + + '
' + price + '
' + + '
Source: ' + source + '
' + + '
' + + '
' + ); +} + +/** + * Market Charts + **/ + +// Create charts +function market_CreateCharts (stats) { + if (!stats || !stats.charts) return; + let data = stats.charts; + let graphData = { + price: market_GetGraphData(data.price), + profit: market_GetGraphData(data.profit) + }; + + for (let graphType in graphData) { + if (graphData[graphType].values.length > 1) { + let $chart = $(`#chart${stats.config.coin}_${graphType}`); + let bgcolor = null, + bordercolor = null, + borderwidth = null; + let colorelem = $chart.siblings('a.chart-style'); + if (colorelem.length == 1) { + bgcolor = colorelem.css('background-color'); + bordercolor = colorelem.css('border-left-color'); + borderwidth = parseFloat(colorelem.css('width')); + } + if (bgcolor === null) bgcolor = 'rgba(3, 169, 244, .4)'; + if (bordercolor === null) bordercolor = '#03a9f4'; + if (borderwidth === null || isNaN(borderwidth)) borderwidth = 1; + let chartObj = new Chart(document.getElementById(`chart${stats.config.coin}_${graphType}`), { + type: 'line', + data: { + labels: graphData[graphType].names, + datasets: [{ + data: graphData[graphType].values, + dataType: graphType, + fill: true, + backgroundColor: bgcolor, + borderColor: bordercolor, + borderWidth: borderwidth + }] + }, + options: { + animation: false, + responsive: true, + maintainAspectRatio: false, + legend: { + display: false + }, + elements: { + point: { + radius: 0, + hitRadius: 10, + hoverRadius: 5 + } + }, + scales: { + xAxes: [{ + display: false, + ticks: { + display: false + }, + gridLines: { + display: false + } + }], + yAxes: [{ + display: false, + ticks: { + display: false, + beginAtZero: true, + userCallback: function (label, index, labels) { + if (Math.floor(label) === label) return label; + } + }, + gridLines: { + display: false + } + }] + }, + layout: { + padding: { + top: 5, + left: 10, + right: 10, + bottom: 10 + } + }, + tooltips: { + callbacks: { + label: function (tooltipItem, data) { + let dataType = data.datasets[tooltipItem.datasetIndex].dataType || ''; + let label = tooltipItem.yLabel; + if (dataType == 'price') label = parseFloat(tooltipItem.yLabel) + .toFixed(4); + else if (dataType == 'profit') label = parseFloat(tooltipItem.yLabel) + .toFixed(10); + return ' ' + label; + } + } + } + } + }); + $chart.closest('.marketChart') + .show(); + } + } +} + +// Get chart data +function market_GetGraphData (rawData) { + let graphData = { + names: [], + values: [] + }; + if (rawData) { + for (let i = 0, xy; xy = rawData[i]; i++) { + graphData.names.push(new Date(xy[0] * 1000) + .toLocaleString()); + graphData.values.push(xy[1]); + } + } + return graphData; +} + + +// Calculate current estimation +function market_CalcEstimateProfit (marketPrices) { + let rateUnit = Math.pow(1000, parseInt($('#calcHashUnit') + .data('mul'))); + let hashRate = parseFloat($('#calcHashRate') + .val()) * rateUnit; + let coin = lastStats.config.coin + try { + if ($(`#calcHashAmount${coin}`) + .length == 0) { + let template = $(`#calcHashResultTemplate`) + .html() + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: coin + }) + $(`#calcHashHolder`) + .append(rendered) + } + let profit = (hashRate * 86400 / lastStats.network.difficulty) * lastStats.lastblock.reward; + if (profit) { + updateText(`calcHashAmount${coin}1`, getReadableCoin(lastStats, profit)); + updateText(`calcHashAmount${coin}2`, market_GetCurrencyPriceText(lastStats, profit, marketPrices)); + //return; + } else { + updateText(`calcHashAmount${coin}1`, ''); + updateText(`calcHashAmount${coin}2`, ''); + } + } catch (e) { + updateText(`calcHashAmount${coin}1`, ''); + updateText(`calcHashAmount${coin}2`, ''); + } + + + + Object.keys(mergedStats) + .forEach(key => { + try { + if ($(`#calcHashAmount${key}`) + .length == 0) { + let template = $(`#calcHashResultTemplate`) + .html() + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: key + }) + $(`#calcHashHolder`) + .append(rendered) + } + + let profit = (hashRate * 86400 / mergedStats[key].network.difficulty) * mergedStats[key].lastblock.reward; + if (profit) { + updateText(`calcHashAmount${key}1`, getReadableCoin(mergedStats[key], profit)); + updateText(`calcHashAmount${key}2`, market_GetCurrencyPriceText(mergedStats[key], profit, marketPrices)); + return; + } else { + updateText(`calcHashAmount${key}1`, ''); + updateText(`calcHashAmount${key}2`, ''); + } + } catch (e) { + updateText(`calcHashAmount${key}1`, ''); + updateText(`calcHashAmount${key}2`, ''); + } + }) +} + + +// Get price in specified currency +function market_GetCurrencyPriceText (stats, coinsRaw, marketPrices) { + if (!priceCurrency || !marketPrices[stats.config.coin] || !marketPrices[stats.config.coin][priceCurrency]) return; + let priceInCurrency = (Math.trunc(getReadableCoin(stats, coinsRaw, 2, true) * marketPrices[stats.config.coin][priceCurrency] * 100) / 100); + return priceInCurrency + ' ' + priceCurrency; +} + +function market_InitTemplate (ranOnce, chartsInitialized, loadedData, marketPrices, intervalChartsUpdate, currencyPairs, xhrMarketGets) { + priceSource = lastStats.config.priceSource || 'cryptonator'; + priceCurrency = lastStats.config.priceCurrency || 'USD'; + + let coin = lastStats.config.coin + if ($(`#blocksTabs li:contains(${coin})`) + .length == 0) { + chartsInitialized[coin] = false + loadedData[coin] = false + marketPrices[coin] = {} + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: lastStats.config.coin, + symbol: `(${lastStats.config.symbol})`, + active: 'active' + }); + $('#blocksTabs') + .append(rendered) + + let template1 = $('#siblingMarketTemplate') + .html() + Mustache.parse(template1) + let rendered1 = Mustache.render(template1, { + coin: coin, + active: 'active' + }) + $(`#tab-content`) + .append(rendered1) + + + let template2 = $('#siblingCalculatorTemplate') + .html() + Mustache.parse(template2) + let rendered2 = Mustache.render(template2, { + coin: coin + }) + $(`#calculator`) + .append(rendered2) + + updateText(`priceChartCurrency${lastStats.config.coin}`, priceCurrency); + updateText(`profitChartProfit${lastStats.config.coin}`, priceCurrency); + + if (lastStats.charts && !chartsInitialized[coin]) { + intervalChartsUpdate[coin] = setInterval(market_CreateCharts(lastStats), 60 * 1000); + market_CreateCharts(lastStats); + chartsInitialized[coin] = true; + } + + } + + market_LoadMarketData(api, lastStats, loadedData, currencyPairs, xhrMarketGets, marketPrices); + + + Object.keys(mergedStats) + .forEach(key => { + if ($(`#blocksTabs li:contains(${key})`) + .length === 0) { + chartsInitialized[key] = false; + loadedData[key] = false + marketPrices[key] = {} + let template1 = $('#siblingMarketTemplate') + .html() + Mustache.parse(template1) + let rendered1 = Mustache.render(template1, { + coin: key + }) + $('#tab-content') + .append(rendered1) + + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: key, + symbol: `(${mergedStats[key].config.symbol})` + }); + $('#blocksTabs') + .append(rendered) + + } + + updateText(`priceChartCurrency${mergedStats[key].config.coin}`, priceCurrency); + updateText(`profitChartProfit${mergedStats[key].config.coin}`, priceCurrency); + + market_LoadMarketData(mergedApis[key].api, mergedStats[key], loadedData, currencyPairs, xhrMarketGets, marketPrices); + + if (mergedStats[key].charts && !chartsInitialized[key]) { + intervalChartsUpdate[key] = setInterval(market_CreateCharts(mergedStats[key]), 60 * 1000); + market_CreateCharts(mergedStats[key]); + chartsInitialized[key] = true; + } + + }) + + market_CalcEstimateProfit(marketPrices); + + sortElementList($(`#blocksTabs`), $(`#blocksTabs>li`), mergedStats) + + if (!ranOnce) + ranOnce = RunOnce() +} + +/* +*************************************************************** +workerstats methods +*************************************************************** +*/ + +function workerstats_Setup (stats, api, addressTimeout, xhrAddressPoll, xhrGetPayments) { + + // Enable time ago on last submitted share + $(`#yourLastShare${stats.config.coin}`) + .timeago(); + + $(`#lookUp${stats.config.coin}`) + .click(function () { + var address = $(`#yourStatsInput${stats.config.coin}`) + .val() + .trim(); + + if (getCurrentAddress(stats.config.coin) != address) { + docCookies.setItem(`mining_address_${stats.config.coin}`, address, Infinity); + + var urlWalletAddress = location.search.split('walletMerged=')[1] || 0; + if (urlWalletAddress) { + window.location.href = "/#worker_stats"; + return; + } else { + docCookies.setItem(`mining_address_${stats.config.coin}`, address, Infinity); + loadLiveStats(true, mergedStats); + } + } + + $(`#addressError${stats.config.coin}, .yourStats${stats.config.coin}, .yourWorkers${stats.config.coin}, .userChart${stats.config.coin}`) + .hide(); + $(`#workersReport_rows_${stats.config.coin}`) + .empty(); + $(`#paymentsReport_rows_${stats.config.coin}`) + .empty(); + + $(`#lookUp${stats.config.coin} > span:first-child`) + .hide(); + $(`#lookUp${stats.config.coin} > span:last-child`) + .show(); + + + if (addressTimeout[stats.config.coin]) clearTimeout(addressTimeout[stats.config.coin]); + + + if (xhrAddressPoll[stats.config.coin]) + xhrAddressPoll[stats.config.coin].abort(); + + $(`#lookUp${stats.config.coin} > span:last-child`) + .hide(); + $(`#lookUp${stats.config.coin} > span:first-child`) + .show(); + + if (!address) { + $(`#yourStatsInput${stats.config.coin}`) + .focus(); + return; + } + + workerstats_FetchAddressStats(false, stats, api, xhrAddressPoll); + + }); + + var address = getCurrentAddress(stats.config.coin); + if (address) { + $(`#yourStatsInput${stats.config.coin}`) + .val(address); + $(`#lookUp${stats.config.coin}`) + .click(); + } else { + $(`#lookUp${stats.config.coin} > span:last-child`) + .hide(); + $(`#lookUp${stats.config.coin} > span:first-child`) + .show(); + $(`#addressError${stats.config.coin}, .yourStats${stats.config.coin}, .yourWorkers${stats.config.coin}, .userChart${stats.config.coin}`) + .hide(); + } + + $(`#yourStatsInput${stats.config.coin}`) + .keyup(function (e) { + if (e.keyCode === 13) + $(`#lookUp${stats.config.coin}`) + .click(); + }); + + + // Handle sort on workers table + //$(`#workersReport${stats.config.coin} th.sort`).on('click', sortTable); + $(`.workerStats th.sort`) + .on('click', sortTable); + + // Load more payments button + $(`#loadMorePayments${stats.config.coin}`) + .click(function (xhrGetPayments) { + if (xhrGetPayments[stats.config.coin]) + xhrGetPayments[stats.config.coin].abort(); + + xhrGetPayments[stats.config.coin] = $.ajax({ + url: `${api}/get_payments`, + data: { + time: $(`#paymentsReport_rows_${stats.config.coin}`) + .children() + .last() + .data('time'), + address: address + }, + dataType: 'json', + cache: 'false', + success: function (data) { + workerstats_RenderPayments(data, stats); + } + }); + }); +} + +/** + * Miner statistics + **/ + + +// Load current miner statistics + +function workerstats_FetchAddressStats (longpoll, stats, api, xhrAddressPoll) { + let address = getCurrentAddress(stats.config.coin) + xhrAddressPoll[stats.config.coin] = $.ajax({ + url: `${api}/stats_address`, + data: { + address: address, + longpoll: longpoll + }, + dataType: 'json', + cache: 'false', + success: function (data) { + if (!data.stats) { + $(`.yourStats${stats.config.coin}, .yourWorkers${stats.config.coin}, .userChart${stats.config.coin}`) + .hide(); + $(`#addressError${stats.config.coin}`) + .text(data.error) + .show(); + docCookies.setItem(`mining_address_${stats.config.coin}`, '', Infinity); + loadLiveStats(true); + return; + } + $(`#addressError${stats.config.coin}`) + .hide(); + + + if (data.stats.lastShare) { + $(`#yourLastShare${stats.config.coin}`) + .timeago('update', new Date(parseInt(data.stats.lastShare) * 1000) + .toISOString()); + } // AQUÍ + else { + updateText(`yourLastShare${stats.config.coin}`, 'Never'); + } + + + updateText(`yourHashrateHolder${stats.config.coin}`, (getReadableHashRateString(data.stats.hashrate) || '0 H') + '/sec'); + if ('hashrate_1h' in data.stats) { + $(`#minerAvgHR${stats.config.coin}`) + .show(); + updateText(`yourHR1h${stats.config.coin}`, (getReadableHashRateString(data.stats.hashrate_1h) || '0 H') + '/s'); + updateText(`yourHR6h${stats.config.coin}`, (getReadableHashRateString(data.stats.hashrate_6h) || '0 H') + '/s'); + updateText(`yourHR24h${stats.config.coin}`, (getReadableHashRateString(data.stats.hashrate_24h) || '0 H') + '/s'); + } else { + $(`#minerAvgHR${stats.config.coin}`) + .hide(); + } + + let totalCoins = data.stats.paid; + let last24hCoins = 0; + let last7dCoins = 0; + + for (let i = 0; i < data.payments.length; i += 2) { + let payment = workerstats_ParsePayment(data.payments[i + 1], data.payments[i]); + let paymentDate = new Date(parseInt(payment.time) * 1000); + let daysDiff = moment() + .diff(moment(paymentDate), 'days'); + + if (daysDiff < 1) { + last24hCoins = last24hCoins + parseInt(payment.amount); + } + + if (daysDiff < 7) { + last7dCoins = last7dCoins + parseInt(payment.amount); + } + } + + + // $.getJSON(`https://api.coingecko.com/api/v3/coins/${stats.config.coin.toLowerCase()}?sparkline=true`, function() {}) + // .done(data => { + // let paidTotalUSD = getReadableCoin(stats, totalCoins, 2, true) * data.market_data.current_price.usd; + // let paid24hUSD = getReadableCoin(stats, last24hCoins, 2, true) * data.market_data.current_price.usd; + // let paid7dUSD = getReadableCoin(stats, last7dCoins, 2, true) * data.market_data.current_price.usd; + + // updateText(`yourPaid${stats.config.coin}`, `${getReadableCoin(stats, totalCoins)} - $${paidTotalUSD.toFixed(2)}`); + // updateText(`paid24h${stats.config.coin}`, `${getReadableCoin(stats, last24hCoins)} - $${paid24hUSD.toFixed(2)}`); + // updateText(`paid7d${stats.config.coin}`, `${getReadableCoin(stats, last7dCoins)} - $${paid7dUSD.toFixed(2)}`); + // }) + // .fail(() => { + updateText(`yourPaid${stats.config.coin}`, getReadableCoin(stats, totalCoins)); + updateText(`paid24h${stats.config.coin}`, getReadableCoin(stats, last24hCoins)); + updateText(`paid7d${stats.config.coin}`, getReadableCoin(stats, last7dCoins)); + // }) + + + + + updateText(`yourHashes${stats.config.coin}`, (data.stats.hashes || 0) + .toString()); + //updateText(`yourPaid${stats.config.coin}`, getReadableCoin(stats, data.stats.paid)); + updateText(`yourPendingBalance${stats.config.coin}`, getReadableCoin(stats, data.stats.balance)); + + let userRoundHashes = parseInt(data.stats.roundHashes || 0); + let poolRoundHashes = parseInt(stats.pool.roundHashes || 0); + let userRoundScore = parseFloat(data.stats.roundScore || 0); + let poolRoundScore = parseFloat(stats.pool.roundScore || 0); + let lastReward = parseFloat(stats.lastblock.reward || 0); + + + let poolFee = stats.config.fee; + if (Object.keys((stats.config.donation)) + .length) { + let totalDonation = 0; + let ldon = stats.config.donation; + for (let i in ldon) { + totalDonation += ldon[i]; + } + poolFee += totalDonation; + } + let transferFee = stats.config.transferFee; + + let share_pct = userRoundHashes * 100 / poolRoundHashes; + let score_pct = userRoundScore * 100 / poolRoundScore; + updateText(`yourRoundShareProportion${stats.config.coin}`, isNaN(share_pct) ? 0.0 : Math.round(share_pct * 1000) / 1000); + updateText(`yourRoundScoreProportion${stats.config.coin}`, isNaN(score_pct) ? 0.0 : Math.round(score_pct * 1000) / 1000); + if (!lastStats.config.slushMiningEnabled) { + $(`#slush_round_info${stats.config.coin}`) + .hide(); + } + + let payoutEstimatePct = parseFloat(userRoundHashes * 100 / poolRoundHashes) + let payoutEstimate = Math.round(lastReward * (payoutEstimatePct / 100)); + if (transferFee) payoutEstimate = payoutEstimate - transferFee; + if (payoutEstimate < 0) + payoutEstimate = 0; + updateText(`yourPayoutEstimate${stats.config.coin}`, getReadableCoin(stats, payoutEstimate)); + + + workerstats_RenderPayments(data.payments, stats); + + if (data.workers && data.workers.length > 0) { + workerstats_RenderWorkers(data.workers, stats); + $(`.yourWorkers${stats.config.coin}`) + .show(); + } + + $(`.yourStats${stats.config.coin}`) + .show(); + workerstats_CreateCharts(data, stats); + + }, + error: function (e) { + if (e.statusText === 'abort') return; + $(`#addressError${stats.config.coin}`) + .text('Connection error') + .show(); + + if (addressTimeout[stats.config.coin]) + clearTimeout(addressTimeout[stats.config.coin]); + + addressTimeout[stats.config.coin] = setTimeout(function () { + workerstats_FetchAddressStats(false, stats, mergedApis[stats.config.coin].api); + }, 2000); + } + }); +} + +/** + * Charts + **/ + +// Create charts +function workerstats_CreateCharts (data, stats) { + if (data.hasOwnProperty("charts")) { + var graphData = { + hashrate: workerstats_GetGraphData(stats, data.charts.hashrate), + payments: workerstats_GetGraphData(stats, data.charts.payments, true) + }; + + for (var graphType in graphData) { + if (graphData[graphType].values.length > 1) { + var settings = jQuery.extend({}, graphSettings); + settings.tooltipValueLookups = { + names: graphData[graphType].names + }; + var $chart = $(`[data-chart=user_${graphType}_${stats.config.coin}]`) + .show() + .find('.chart'); + $chart.sparkline(graphData[graphType].values, settings); + } + } + } +} + +// Get chart data +function workerstats_GetGraphData (stats, rawData, fixValueToCoins) { + var graphData = { + names: [], + values: [] + }; + + if (rawData) { + for (var i = 0, xy; xy = rawData[i]; i++) { + graphData.names.push(new Date(xy[0] * 1000) + .toLocaleString()); + graphData.values.push(fixValueToCoins ? getReadableCoin(stats, xy[1], null, true) : xy[1]); + } + } + + return graphData; +} + +/** + * Workers report + **/ + +// Get worker row id +function workerstats_GetWorkerRowId (workerName) { + var id = btoa(workerName); + id = id.replace(/=/, ''); + return id; +} + +// Get worker row element +function workerstats_GetWorkerRowElement (worker, jsonString, stats) { + var row = document.createElement('tr'); + row.setAttribute('data-json', jsonString); + row.setAttribute('data-name', worker.name); + row.setAttribute('id', 'workerRow' + stats.config.coin + '_' + workerstats_GetWorkerRowId(worker.name)); + + row.innerHTML = workerstats_GetWorkerCells(worker); + + return row; +} + +// Get worker cells +function workerstats_GetWorkerCells (worker) { + let hashrate = worker.hashrate ? worker.hashrate : 0; + let hashrate1h = worker.hashrate_1h || 0; + let hashrate6h = worker.hashrate_6h || 0; + let hashrate24h = worker.hashrate_24h || 0; + let lastShare = worker.lastShare ? worker.lastShare : 0; + let hashes = (worker.hashes || 0) + .toString(); + let status = (hashrate <= 0) ? 'error' : 'ok'; + + return '' + + '' + (worker.name != 'undefined' ? worker.name : 'Undefined') + '' + + '' + getReadableHashRateString(hashrate) + '/s' + + '' + getReadableHashRateString(hashrate1h) + '/s' + + '' + getReadableHashRateString(hashrate6h) + '/s' + + '' + getReadableHashRateString(hashrate24h) + '/s' + + '' + (lastShare ? $.timeago(new Date(parseInt(lastShare) * 1000) + .toISOString()) : 'Never') + '' + + '' + hashes + ''; +} + +// Sort workers +function workerstats_SortWorkers (a, b) { + var aName = a.name.toLowerCase(); + var bName = b.name.toLowerCase(); + return ((aName < bName) ? -1 : ((aName > bName) ? 1 : 0)); +} + +// Render workers list +function workerstats_RenderWorkers (workersData, stats) { + workersData = workersData.sort(workerstats_SortWorkers); + + var $workersRows = $(`#workersReport_rows_${stats.config.coin}`); + + for (var i = 0; i < workersData.length; i++) { + var existingRow = document.getElementById(`workerRow${stats.config.coin}_${workerstats_GetWorkerRowId(workersData[i].name)}`); + if (!existingRow) { + $workersRows.empty(); + break; + } + } + + let have_avg_hr = false; + + for (var i = 0; i < workersData.length; i++) { + var worker = workersData[i]; + if (Date.now() / 1000 - parseInt(worker.lastShare) > 2 * 86400) continue; + if (!have_avg_hr && 'hashrate_1h' in worker) have_avg_hr = true; + var workerJson = JSON.stringify(worker); + var existingRow = document.getElementById(`workerRow${stats.config.coin}_${workerstats_GetWorkerRowId(worker.name)}`); + if (existingRow && existingRow.getAttribute('data-json') !== workerJson) { + $(existingRow) + .replaceWith(workerstats_GetWorkerRowElement(worker, workerJson, stats)); + } else if (!existingRow) { + var workerElement = workerstats_GetWorkerRowElement(worker, workerJson, stats); + $workersRows.append(workerElement); + } + } + if (!have_avg_hr) $(`#workersReport${stats.config.coin} .avghr`) + .hide(); + else $(`#workersReport${stats.config.coin} .avghr`) + .show(); +} + +/** + * Payments report + **/ + +// Parse payment data +function workerstats_ParsePayment (time, serializedPayment) { + var parts = serializedPayment.split(':'); + return { + time: parseInt(time), + hash: parts[0], + amount: parts[1], + fee: parts[2], + mixin: parts[3], + recipients: parts[4] + }; +} + +// Get payment row element +function workerstats_GetPaymentRowElement (payment, jsonString, stats) { + var row = document.createElement('tr'); + row.setAttribute('data-json', jsonString); + row.setAttribute('data-time', payment.time); + row.setAttribute('id', 'paymentRow' + stats.config.coin + payment.time); + + row.innerHTML = workerstats_GetPaymentCells(payment, stats); + + return row; +} + +// Get payment cells +function workerstats_GetPaymentCells (payment, stats) { + return '' + formatDate(payment.time) + '' + + '' + formatPaymentLink(payment.hash, stats) + '' + + '' + getReadableCoin(stats, payment.amount) + '' + + '' + payment.mixin + ''; +} + +// Get summary row element +function workerstats_GetSummaryRowElement (summary, jsonString, stats) { + var row = document.createElement('tr'); + row.setAttribute('data-json', jsonString); + row.setAttribute('data-date', summary.date); + row.setAttribute('id', 'summaryRow' + stats.config.coin + summary.date); + row.setAttribute('class', 'summary'); + + row.innerHTML = workerstats_GetSummaryCells(summary, stats); + + return row; +} + +// Get summary cells +function workerstats_GetSummaryCells (summary, stats) { + var text = getTranslation('paymentSummaryMulti') ? getTranslation('paymentSummaryMulti') : 'On %DATE% you have received %AMOUNT% in %COUNT% payments'; + if (summary.count <= 1) text = getTranslation('paymentSummarySingle') ? getTranslation('paymentSummarySingle') : 'On %DATE% you have received %AMOUNT%'; + text = text.replace(/%DATE%/g, summary.date); + text = text.replace(/%COUNT%/g, summary.count); + text = text.replace(/%AMOUNT%/g, getReadableCoin(stats, summary.amount)); + return '' + text + ''; +} + +// Render payments +function workerstats_RenderPayments (paymentsResults, stats) { + var $paymentsRows = $(`#paymentsReport_rows_${stats.config.coin}`); + var lastPaymentDate = null; + var summaryData = { + date: null, + time: null, + count: 0, + amount: 0 + }; + for (var i = 0; i < paymentsResults.length; i += 2) { + var payment = workerstats_ParsePayment(paymentsResults[i + 1], paymentsResults[i]); + var paymentJson = JSON.stringify(payment); + var paymentElement = workerstats_GetPaymentRowElement(payment, paymentJson, stats); + + var paymentDate = new Date(parseInt(payment.time) * 1000) + .toLocaleDateString(); + if (!lastPaymentDate || lastPaymentDate && paymentDate != lastPaymentDate) { + summaryData = { + date: paymentDate, + time: payment.time, + count: 0, + amount: 0 + }; + } + + var existingRow = document.getElementById(`paymentRow${stats.config.coin}${payment.time}`); + if (existingRow && existingRow.getAttribute('data-json') !== paymentJson) { + $(existingRow) + .replaceWith(workerstats_GetPaymentRowElement(payment, paymentJson, stats)); + } else if (!existingRow) { + var inserted = false; + var rows = $paymentsRows.children() + .get(); + for (var f = 0; f < rows.length; f++) { + var pTime = parseInt(rows[f].getAttribute('data-time')); + if (pTime && pTime < payment.time) { + inserted = true; + $(rows[f]) + .before(paymentElement); + break; + } + } + if (!inserted) { + $paymentsRows.append(paymentElement); + } + } + + summaryData.count++; + summaryData.amount += parseInt(payment.amount); + + var summaryJson = JSON.stringify(summaryData); + var summaryElement = workerstats_GetSummaryRowElement(summaryData, summaryJson, stats); + + var existingSummary = document.getElementById(`summaryRow${stats.config.coin}${summaryData.date}`); + if (existingSummary && existingSummary.getAttribute('data-json') !== summaryJson) { + $(existingSummary) + .replaceWith(summaryElement); + } else if (!existingSummary) { + var inserted = false; + var rows = $paymentsRows.children() + .get(); + for (var f = 0; f < rows.length; f++) { + var pTime = parseInt(rows[f].getAttribute('data-time')); + if (pTime && pTime === summaryData.time) { + inserted = true; + $(rows[f]) + .before(summaryElement); + break; + } + } + if (!inserted) { + $paymentsRows.append(summaryElement); + } + } + lastPaymentDate = paymentDate; + } +} + +function workerstats_InitTemplate (ranOnce, addressTimeout, xhrAddressPoll, xhrGetPayments) { + let coin = lastStats.config.coin + if ($(`#blocksTabs li:contains(${coin})`) + .length === 0) { + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: lastStats.config.coin, + symbol: `(${lastStats.config.symbol})`, + active: 'active' + }); + $('#blocksTabs') + .append(rendered) + + template = $('#siblingTemplate') + .html() + Mustache.parse(template) + rendered = Mustache.render(template, { + coin: coin, + active: 'active' + }) + $('#tab-content') + .append(rendered) + workerstats_Setup(lastStats, api, addressTimeout, xhrAddressPoll, xhrGetPayments) + } + + Object.keys(mergedStats) + .forEach(key => { + if ($(`#blocksTabs li:contains(${key})`) + .length === 0) { + coin = key + let template = $('#siblingTabTemplate') + .html(); + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: mergedStats[key].config.coin, + symbol: `(${mergedStats[key].config.symbol})` + }); + $('#blocksTabs') + .append(rendered) + + template = $('#siblingTemplate') + .html() + Mustache.parse(template) + rendered = Mustache.render(template, { + coin: coin + }) + $('#tab-content') + .append(rendered) + workerstats_Setup(mergedStats[key], mergedApis[key].api, addressTimeout, xhrAddressPoll, xhrGetPayments) + } + }) + + sortElementList($(`#blocksTabs`), $(`#blocksTabs>li`), mergedStats) + + if (!ranOnce) + ranOnce = RunOnce() +} + + +/* +*************************************************************** +workerstats methods +*************************************************************** +*/ + +let home_GraphSettings = { + type: 'line', + width: '100%', + height: '140', + lineColor: '#03a9f4', + fillColor: 'rgba(3, 169, 244, .4)', + spotColor: null, + minSpotColor: null, + maxSpotColor: null, + highlightLineColor: '#236d26', + spotRadius: 3, + chartRangeMin: 0, + drawNormalOnTop: false, + tooltipFormat: '{{y}} – {{offset:names}}' +}; + +function home_CreateCharts (data) { + if (data.hasOwnProperty("charts")) { + var graphData = { + hashrate: { + data: [home_GetGraphData(data.charts.hashrate), home_GetGraphData(data.charts.hashrateSolo)], + options: { + lineColor: 'orange' + } + }, + diff: { + data: [home_GetGraphData(data.charts.difficulty)] + }, + miners: { + data: [home_GetGraphData(data.charts.miners), home_GetGraphData(data.charts.minersSolo)], + options: { + lineColor: 'orange' + } + }, + workers: { + data: [home_GetGraphData(data.charts.workers), home_GetGraphData(data.charts.workersSolo)], + options: { + lineColor: 'orange' + } + }, + }; + + for (var graphType in graphData) { + if (graphData[graphType].data[0].values.length > 1) { + var settings = jQuery.extend({}, home_GraphSettings); + settings.tooltipValueLookups = { + names: graphData[graphType].data[0].names + }; + var $chart = $('[data-chart=' + graphType + '] .chart'); + $chart.closest('.poolChart') + .show(); + settings.tooltipFormat = graphData[graphType].data[1] ? 'PROP: {{y}} – {{offset:names}}' : '{{y}} – {{offset:names}}' + $chart.sparkline(graphData[graphType].data[0].values, settings); + if (graphData[graphType].data[1]) { + settings.composite = true + settings.lineColor = graphData[graphType].options.lineColor + settings.tooltipFormat = 'SOLO: {{y}} – {{offset:names}}' + $chart.sparkline(graphData[graphType].data[1].values, settings); + } + } + } + } +} + +// Get chart data +function home_GetGraphData (rawData, fixValueToCoins) { + var graphData = { + names: [], + values: [] + }; + if (rawData) { + for (var i = 0, xy; xy = rawData[i]; i++) { + graphData.names.push(new Date(xy[0] * 1000) + .toLocaleString()); + graphData.values.push(fixValueToCoins ? getReadableCoin(lastStats, xy[1], null, true) : xy[1]); + } + } + return graphData; +} + +function home_GenerateNetworkStats (key, symbol) { + if ($(`#networkStats${key}`) + .length == 0) { + let template = $('#siblingTemplate') + .html() + if (template) { + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: key, + symbol: symbol + }) + $(`#networkStats`) + .append(rendered) + } + } +} + +function sortElementList (container, siblings, stats) { + let sorted = (a, b) => { + return ((a.id.toLowerCase() < b.id.toLowerCase()) ? -1 : ((a.id.toLowerCase() > b.id.toLowerCase()) ? 1 : 0)) + } + if (stats && siblings.length - 1 === Object.keys(stats) + .length) { + siblings.sort(sorted) + .appendTo(container) + } +} + + +function home_InitTemplate (parentStats, siblingStats) { + $('#networkLastBlockFound') + .timeago('update', new Date(parentStats.lastblock.timestamp * 1000) + .toISOString()); + + let coin = parentStats.config.coin + let minerInfo = [] + let efforts = [] + + if ($(`#networkStats${coin}`) + .length == 0) { + minerInfo.push({ + blocks: parentStats.pool.totalBlocks.toString(), + blocksSolo: parentStats.pool.totalBlocksSolo.toString(), + coin: coin, + symbol: parentStats.config.symbol, + miners: parentStats.pool.miners.toString(), + minersSolo: parentStats.pool.minersSolo.toString() + }) + + efforts.push({ + coin: coin, + effort: `${(parentStats.pool.roundHashes / parentStats.network.difficulty * 100).toFixed(1)}%`, + symbol: parentStats.config.symbol + }) + + let template = $('#siblingTemplate') + .html() + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: coin, + symbol: parentStats.config.symbol + }) + $(`#networkStats`) + .append(rendered) + } + + let lastBlockFound = null + if (parentStats.pool.lastBlockFound) { + lastBlockFound = parseInt(parentStats.pool.lastBlockFound); + } + + + updateText(`networkHashrate${coin}`, getReadableHashRateString(parentStats.network.difficulty / parentStats.config.coinDifficultyTarget) + '/sec'); + updateText(`networkDifficulty${coin}`, formatNumber(parentStats.network.difficulty.toString(), ' ')); + updateText(`blockchainHeight${coin}`, formatNumber(parentStats.network.height.toString(), ' ')); + let rewardMinusNetworkFee = parentStats.lastblock.reward - (parentStats.lastblock.reward * (parentStats.config.networkFee ? parentStats.config.networkFee / 100 : 0)) + updateText(`networkLastReward${coin}`, getReadableCoin(parentStats, rewardMinusNetworkFee)); + + + + Object.keys(siblingStats) + .forEach(key => { + home_GenerateNetworkStats(key, siblingStats[key].config.symbol) + + minerInfo.push({ + blocks: siblingStats[key].pool.totalBlocks.toString(), + blocksSolo: siblingStats[key].pool.totalBlocksSolo.toString(), + coin: key, + symbol: siblingStats[key].config.symbol, + miners: siblingStats[key].pool.miners.toString(), + minersSolo: siblingStats[key].pool.minersSolo.toString() + }) + + efforts.push({ + coin: key, + effort: `${(siblingStats[key].pool.roundHashes / siblingStats[key].network.difficulty * 100).toFixed(1)}%`, + symbol: siblingStats[key].config.symbol + }); + + if (siblingStats[key].pool.lastBlockFound) { + let lastChildBlockFound = parseInt(siblingStats[key].pool.lastBlockFound) + if (lastChildBlockFound > lastBlockFound) + lastBlockFound = lastChildBlockFound + } + + updateText(`networkHashrate${key}`, getReadableHashRateString(siblingStats[key].network.difficulty / siblingStats[key].config.coinDifficultyTarget) + '/sec'); + updateText(`networkDifficulty${key}`, formatNumber(siblingStats[key].network.difficulty.toString(), ' ')); + updateText(`blockchainHeight${key}`, formatNumber(siblingStats[key].network.height.toString(), ' ')); + // updateText(`networkLastReward${key}`, getReadableCoin(siblingStats[key], siblingStats[key].lastblock.reward)); + updateText(`poolMiners${key}`, `${siblingStats[key].pool.miners}/${siblingStats[key].pool.minersSolo}`); + updateText(`blocksTotal${key}`, `${siblingStats[key].pool.totalBlocks}/${siblingStats[key].pool.totalBlocksSolo}`); + updateText(`currentEffort${key}`, (siblingStats[key].pool.roundHashes / siblingStats[key].network.difficulty * 100) + .toFixed(1) + '%'); + }) + + sortElementList($(`#networkStats`), $(`#networkStats>div`), siblingStats) + + if ($(`#poolDetails > div`) + .length == 0) { + let template = $('#poolDetailTemplate') + .html() + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: parentStats.config.coin, + symbol: parentStats.config.symbol, + blocks: minerInfo + }) + $(`#poolDetails`) + .append(rendered) + } + + if ($(`#mainPoolStats > div`) + .length == 0) { + let template = $('#mainPoolTemplate') + .html() + Mustache.parse(template) + let rendered = Mustache.render(template, { + coin: parentStats.config.coin, + blocks: minerInfo, + efforts: efforts + }) + $(`#mainPoolStats`) + .append(rendered) + } + + + if (lastBlockFound) { + $('#poolLastBlockFound') + .timeago('update', new Date(lastBlockFound) + .toISOString()); + } else { + $('#poolLastBlockFound') + .removeAttr('title') + .data('ts', '') + .update('Never'); + } + + let lastHash = updateText('lastHash', parentStats.lastblock.hash) + if (lastHash) + lastHash.setAttribute('href', getBlockchainUrl(parentStats.lastblock.hash, parentStats)); + + + updateText('poolHashrate', `PROP: ${getReadableHashRateString(parentStats.pool.hashrate)}/sec`); + updateText('poolHashrateSolo', `SOLO: ${getReadableHashRateString(parentStats.pool.hashrateSolo)}/sec`); + + + var hashPowerSolo = parentStats.pool.hashrateSolo / (parentStats.network.difficulty / parentStats.config.coinDifficultyTarget) * 100; + updateText('hashPowerSolo', hashPowerSolo.toFixed(2) + '%'); + + var hashPower = parentStats.pool.hashrate / (parentStats.network.difficulty / parentStats.config.coinDifficultyTarget) * 100; + updateText('hashPower', hashPower.toFixed(2) + '%'); + + + updateText(`poolMiners${coin}`, `${parentStats.pool.miners}/${parentStats.pool.minersSolo}`); + updateText('blocksTotal', `${parentStats.pool.totalBlocks}/${parentStats.pool.totalBlocksSolo}`); + + + var totalFee = parentStats.config.fee; + if (Object.keys(parentStats.config.donation) + .length) { + var totalDonation = 0; + for (var i in parentStats.config.donation) { + totalDonation += parentStats.config.donation[i]; + } + totalFee += totalDonation; + } + + updateText('poolFee', (totalFee > 0 && totalFee != 100 ? floatToString(totalFee) : (totalFee == 100 ? '100' : '0')) + '%'); + + updateText('paymentsInterval', getReadableTime(parentStats.config.paymentsInterval)); + updateText('paymentsMinimum', getReadableCoin(parentStats, parentStats.config.minPaymentThreshold)); + + updateText('blockSolvedTime', getReadableTime(parentStats.network.difficulty / parentStats.pool.hashrate)); + + updateText(`currentEffort${coin}`, (parentStats.pool.roundHashes / parentStats.network.difficulty * 100) + .toFixed(1) + '%'); } diff --git a/website_example/js/custom.js b/website_example/js/custom.js index 4bdcd2b11..6188bd4ed 100644 --- a/website_example/js/custom.js +++ b/website_example/js/custom.js @@ -1 +1 @@ -/* Insert your pool's unique Javascript here */ \ No newline at end of file +/* Insert your pool's unique Javascript here */ diff --git a/website_example/lang/ca.json b/website_example/lang/ca.json index aa7f85532..1a30e4beb 100644 --- a/website_example/lang/ca.json +++ b/website_example/lang/ca.json @@ -1,166 +1,166 @@ { - "miningPool": "Mining Pool", - "dashboard": "Tauler", - "gettingStarted": "Començar", - "yourStats": "Estadístiques personals", - "poolBlocks": "Blocs de la Pool", - "settings": "Ajustaments", - "faq": "FAQ", - "telegram": "Grup de Telegram", - "discord": "Discord", - "contactUs": "Contactar", - "network": "Xarxa", - "pool": "Pool", - "you": "La teva taxa", - "statsUpdated": "Estadístiques actualitzades", - - "poolHashrate": "Taxa de la Pool (hash)", - "currentEffort": "Esforç actual", - "networkHashrate": "Taxa de la Xarxa (hash)", - "networkDifficulty": "Dificultat", - "blockchainHeight": "Altura cadena de blocs", - "networkLastReward": "Última recompensa", - "poolMiners": "Miners connectats", - "poolFee": "Tarifa de la Pool", - - "minerStats": "Les teves estadístiques i historial de pagaments", - "workerStats": "Estadístiques personals", - "miner": "Miner", - "miners": "Miners", - "minersCount": "miners", - "workers": "Treballadors", - "workersCount": "treballadors", - "workerName": "Nom Treballador", - "lastHash": "Últim hash", - "hashRate": "Taxa (hash)", - "currentHashRate": "Taxa actual (hash)", - "lastShare": "Última acció enviada", - "totalHashes": "Total d'accions enviades", - "top10miners": "Top 10 Miners", + "miningPool": "Mining Pool", + "dashboard": "Tauler", + "gettingStarted": "Començar", + "yourStats": "Estadístiques personals", + "poolBlocks": "Blocs de la Pool", + "settings": "Ajustaments", + "faq": "FAQ", + "telegram": "Grup de Telegram", + "discord": "Discord", + "contactUs": "Contactar", + "network": "Xarxa", + "pool": "Pool", + "you": "La teva taxa", + "statsUpdated": "Estadístiques actualitzades", - "blocksTotal": "Blocs trobats", - "blockSolvedTime": "Bloc trobat cada", - "blocksMaturityCount": "Maduresa requerida", - "efficiency": "Eficiència", - "averageLuck": "Promitg de sort", - "timeFound": "Data i hora", - "reward": "Recompensa", - "height": "Altura", - "difficulty": "Dificultat", - "blockHash": "Bloc Hash", - "effort": "Esforç", - "blocksFoundLast24": "Blocks found in the last 24 hours", - "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + "poolHashrate": "Taxa de la Pool (hash)", + "currentEffort": "Esforç actual", + "networkHashrate": "Taxa de la Xarxa (hash)", + "networkDifficulty": "Dificultat", + "blockchainHeight": "Altura cadena de blocs", + "networkLastReward": "Última recompensa", + "poolMiners": "Miners connectats", + "poolFee": "Tarifa de la Pool", - "payments": "Pagaments", - "paymentsHistory": "Historial de pagaments", - "paymentsTotal": "Total de pagaments", - "paymentsMinimum": "Pagament mínim", - "paymentsInterval": "Interval de pagaments", - "paymentsDenomination": "Unitat de denominació", - "timeSent": "Data", - "transactionHash": "Hash transacció", - "amount": "Import", - "fee": "Tarifa", - "mixin": "Mixin", - "payees": "Beneficiàris", - "pendingBalance": "Balanç pendent", - "totalPaid": "Total pagat", - "payoutEstimate": "Pagament estimat", - "paymentSummarySingle": "El %DATE% has rebut %AMOUNT%", - "paymentSummaryMulti": "El %DATE% has rebut %AMOUNT% en %COUNT% pagaments", + "minerStats": "Les teves estadístiques i historial de pagaments", + "workerStats": "Estadístiques personals", + "miner": "Miner", + "miners": "Miners", + "minersCount": "miners", + "workers": "Treballadors", + "workersCount": "treballadors", + "workerName": "Nom Treballador", + "lastHash": "Últim hash", + "hashRate": "Taxa (hash)", + "currentHashRate": "Taxa actual (hash)", + "lastShare": "Última acció enviada", + "totalHashes": "Total d'accions enviades", + "top10miners": "Top 10 Miners", - "connectionDetails": "Detalls de connexió", - "cnAlgorithm": "Algoritme", - "miningPoolHost": "Adreça de la Pool", - "username": "Usuari", - "usernameDesc": "La teva adreça del wallet", - "paymentId": "ID de pagament (Exchanges)", - "fixedDiff": "Dificultat fixa", - "address": "adreça", - "addrPaymentId": "IDPagament", - "addrDiff": "diff", - "password": "Contrassenya", - "passwordDesc": "Nom del seu treballador", - "emailNotifications": "Notificacions per E-Mail", - "miningPorts": "Ports de mineria", - "port": "Port", - "portDiff": "Dificultat inicial", - "description": "Descripció", - "miningApps": "Aplicacions de mineria", - "configGeneratorDesc": "Genera la teva configuració personalitzada per minar a la nostra Pool", - "addressField": "Adreça Wallet", - "paymentIdField": "ID de Pagament per a Exchanges (opcional)", - "fixedDiffField": "Dificultat fixa (opcional)", - "workerNameField": "Nom_Treballador", - "emailNotificationsField": "Notificacions per E-Mail (opcional))", - "generateConfig": "Generar configuració", - "appName": "Aplicació", - "appArch": "Arquitectura", - "appDesc": "Característiques", - "download": "Descarregar", - "showConfig": "Veure més", + "blocksTotal": "Blocs trobats", + "blockSolvedTime": "Bloc trobat cada", + "blocksMaturityCount": "Maduresa requerida", + "efficiency": "Eficiència", + "averageLuck": "Promitg de sort", + "timeFound": "Data i hora", + "reward": "Recompensa", + "height": "Altura", + "difficulty": "Dificultat", + "blockHash": "Bloc Hash", + "effort": "Esforç", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", - "market": "Mercat / Calculadora", - "loadingMarket": "Carregant preus del mercat", - "priceIn": "Preu en", - "hashPer": "Hash/", - "estimateProfit": "Beneficis estimats", - "enterYourHashrate": "Entra la teva taxa (hash)", - "perDay": "per dia", - - "verificationFields": "Camps de verificació", - "minerVerification": "Per tenir una mica més de seguretat que l'adreça de la cartera és vostra, us demanem que proporcioneu una de les adreces IP que utilitza el vostre miner.", - "minerAddress": "Adreça del moneder", - "minerIP": "Adreça IP del Miner", - "setMinimumPayout": "Estableix el nivell mínim de pagament", - "minerMinPayout": "Si preferiu un nivell de pagament més alt que el predeterminat del grup, aquí podeu canviar-lo per als vostres miners. L'import que indiqueu aquí es convertirà en l'import mínim dels pagaments de la vostra adreça.", - "minimumPayout": "Pagament mínim", - "enableEmailNotifications": "Activar notificacions per de correu electrònic", - "minerEmailNotify": "Aquesta Pool enviarà una notificació per correu electrònic quan es trobi un bloc i quan es produeixi un pagament.", - "emailAddress": "Adreça de correu electrònic", - "noMinerAddress": "No s'ha especificat cap adreça del moneder", - "noMinerIP": "No s'especificat cap adreça IP", - "noPayoutLevel": "No s'ha especificat cap nivell de pagament", - "noEmail": "No s'ha especificat cap adreça de correu electrònic", - "invalidEmail": "S'ha especificat una adreça de correu electrònic no vàlida", - "minerPayoutSet": "Fet! S'ha establert el nivell mínim de pagament", - "notificationEnabled": "Fet! S'han activat les notificacions per correu electrònic", - "notificationDisabled": "Fet! S'han desactivat les notificacions per correu electrònic", + "payments": "Pagaments", + "paymentsHistory": "Historial de pagaments", + "paymentsTotal": "Total de pagaments", + "paymentsMinimum": "Pagament mínim", + "paymentsInterval": "Interval de pagaments", + "paymentsDenomination": "Unitat de denominació", + "timeSent": "Data", + "transactionHash": "Hash transacció", + "amount": "Import", + "fee": "Tarifa", + "mixin": "Mixin", + "payees": "Beneficiàris", + "pendingBalance": "Balanç pendent", + "totalPaid": "Total pagat", + "payoutEstimate": "Pagament estimat", + "paymentSummarySingle": "El %DATE% has rebut %AMOUNT%", + "paymentSummaryMulti": "El %DATE% has rebut %AMOUNT% en %COUNT% pagaments", - "enterYourAddress": "Introduïu la vostra adreça", - "enterYourMinerIP": "Una adreça IP que utilitzin els miners (qualsevol)", - "enterYourEmail": "Introduïu la vostra adreça de correu electrònic (opcional)", - "lookup": "Cercar", - "searching": "Cercant...", - "loadMore": "Carregar més", - "set": "Establir", - "enable": "Activar", - "disable": "Desactivar", - "status": "Estat", - "updated": "Actualitzat:", - "source": "Origen:", - "error": "Error:", + "connectionDetails": "Detalls de connexió", + "cnAlgorithm": "Algoritme", + "miningPoolHost": "Adreça de la Pool", + "username": "Usuari", + "usernameDesc": "La teva adreça del wallet", + "paymentId": "ID de pagament (Exchanges)", + "fixedDiff": "Dificultat fixa", + "address": "adreça", + "addrPaymentId": "IDPagament", + "addrDiff": "diff", + "password": "Contrassenya", + "passwordDesc": "Nom del seu treballador", + "emailNotifications": "Notificacions per E-Mail", + "miningPorts": "Ports de mineria", + "port": "Port", + "portDiff": "Dificultat inicial", + "description": "Descripció", + "miningApps": "Aplicacions de mineria", + "configGeneratorDesc": "Genera la teva configuració personalitzada per minar a la nostra Pool", + "addressField": "Adreça Wallet", + "paymentIdField": "ID de Pagament per a Exchanges (opcional)", + "fixedDiffField": "Dificultat fixa (opcional)", + "workerNameField": "Nom_Treballador", + "emailNotificationsField": "Notificacions per E-Mail (opcional))", + "generateConfig": "Generar configuració", + "appName": "Aplicació", + "appArch": "Arquitectura", + "appDesc": "Característiques", + "download": "Descarregar", + "showConfig": "Veure més", - "na": "N/A", - "estimated": "estimat", - "never": "Mai", - "second": "segon", - "seconds": "segons", - "minute": "minut", - "minutes": "minuts", - "hour": "hora", - "hours": "hores", - "day": "dia", - "days": "dies", - "week": "setmana", - "weeks": "setmanes", - "month": "mes", - "months": "mesos", - "year": "any", - "years": "anys", + "market": "Mercat / Calculadora", + "loadingMarket": "Carregant preus del mercat", + "priceIn": "Preu en", + "hashPer": "Hash/", + "estimateProfit": "Beneficis estimats", + "enterYourHashrate": "Entra la teva taxa (hash)", + "perDay": "per dia", - "poweredBy": "Powered by", - "openSource": "open sourced under the" + "verificationFields": "Camps de verificació", + "minerVerification": "Per tenir una mica més de seguretat que l'adreça de la cartera és vostra, us demanem que proporcioneu una de les adreces IP que utilitza el vostre miner.", + "minerAddress": "Adreça del moneder", + "minerIP": "Adreça IP del Miner", + "setMinimumPayout": "Estableix el nivell mínim de pagament", + "minerMinPayout": "Si preferiu un nivell de pagament més alt que el predeterminat del grup, aquí podeu canviar-lo per als vostres miners. L'import que indiqueu aquí es convertirà en l'import mínim dels pagaments de la vostra adreça.", + "minimumPayout": "Pagament mínim", + "enableEmailNotifications": "Activar notificacions per de correu electrònic", + "minerEmailNotify": "Aquesta Pool enviarà una notificació per correu electrònic quan es trobi un bloc i quan es produeixi un pagament.", + "emailAddress": "Adreça de correu electrònic", + "noMinerAddress": "No s'ha especificat cap adreça del moneder", + "noMinerIP": "No s'especificat cap adreça IP", + "noPayoutLevel": "No s'ha especificat cap nivell de pagament", + "noEmail": "No s'ha especificat cap adreça de correu electrònic", + "invalidEmail": "S'ha especificat una adreça de correu electrònic no vàlida", + "minerPayoutSet": "Fet! S'ha establert el nivell mínim de pagament", + "notificationEnabled": "Fet! S'han activat les notificacions per correu electrònic", + "notificationDisabled": "Fet! S'han desactivat les notificacions per correu electrònic", + + "enterYourAddress": "Introduïu la vostra adreça", + "enterYourMinerIP": "Una adreça IP que utilitzin els miners (qualsevol)", + "enterYourEmail": "Introduïu la vostra adreça de correu electrònic (opcional)", + "lookup": "Cercar", + "searching": "Cercant...", + "loadMore": "Carregar més", + "set": "Establir", + "enable": "Activar", + "disable": "Desactivar", + "status": "Estat", + "updated": "Actualitzat:", + "source": "Origen:", + "error": "Error:", + + "na": "N/A", + "estimated": "estimat", + "never": "Mai", + "second": "segon", + "seconds": "segons", + "minute": "minut", + "minutes": "minuts", + "hour": "hora", + "hours": "hores", + "day": "dia", + "days": "dies", + "week": "setmana", + "weeks": "setmanes", + "month": "mes", + "months": "mesos", + "year": "any", + "years": "anys", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" } diff --git a/website_example/lang/en.json b/website_example/lang/en.json index ff5f7422e..880806d20 100644 --- a/website_example/lang/en.json +++ b/website_example/lang/en.json @@ -1,168 +1,168 @@ { - "miningPool": "Mining Pool", - "dashboard": "Dashboard", - "gettingStarted": "Getting Started", - "yourStats": "Worker Statistics", - "poolBlocks": "Pool Blocks", - "settings": "Settings", - "faq": "FAQ", - "telegram": "Telegram group", - "discord": "Discord", - "contactUs": "Contact Us", - - "network": "Network", - "pool": "Pool", - "you": "You", - "statsUpdated": "Stats Updated", - - "poolHashrate": "Pool Hash Rate", - "currentEffort": "Current Effort", - "networkHashrate": "Network Hash Rate", - "networkDifficulty": "Difficulty", - "blockchainHeight": "Blockchain Height", - "networkLastReward": "Last Reward", - "poolMiners": "Connected Miners", - "poolFee": "Pool Fee", - - "minerStats": "Your Stats & Payment History", - "workerStats": "Workers Statistics", - "miner": "Miner", - "miners": "Miners", - "minersCount": "miners", - "workers": "Workers", - "workersCount": "workers", - "workerName": "Worker Name", - "lastHash": "Last Hash", - "hashRate": "Hash Rate", - "currentHashRate": "Current Hash Rate", - "lastShare": "Last Share Submitted", - "totalHashes": "Total Hashes Submitted", - "top10miners": "Top 10 miners", + "miningPool": "Mining Pool", + "dashboard": "Dashboard", + "gettingStarted": "Getting Started", + "yourStats": "Worker Statistics", + "poolBlocks": "Pool Blocks", + "settings": "Settings", + "faq": "FAQ", + "telegram": "Telegram group", + "discord": "Discord", + "contactUs": "Contact Us", - "blocksTotal": "Blocks Found", - "blockSolvedTime": "Blocks Found Every", - "blocksMaturityCount": "Maturity Requirement", - "efficiency": "Efficiency", - "averageLuck": "Average Luck", - "timeFound": "Time Found", - "reward": "Reward", - "height": "Height", - "difficulty": "Difficulty", - "blockHash": "Block Hash", - "effort": "Effort", - "blocksFoundLast24": "Blocks found in the last 24 hours", - "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + "network": "Network", + "pool": "Pool", + "you": "You", + "statsUpdated": "Stats Updated", - "payments": "Payments", - "paymentsHistory": "Payments History", - "paymentsTotal": "Total Payments", - "paymentsMinimum": "Minimum Payout", - "paymentsInterval": "Payment Interval", - "paymentsDenomination": "Denomination Unit", - "timeSent": "Time Sent", - "transactionHash": "Transaction Hash", - "amount": "Amount", - "fee": "Fee", - "mixin": "Mixin", - "payees": "Payees", - "pendingBalance": "Pending Balance", - "totalPaid": "Total Paid", - "payoutEstimate": "Current Payout Estimate", - "paymentSummarySingle": "On %DATE% you have received %AMOUNT%", - "paymentSummaryMulti": "On %DATE% you have received %AMOUNT% in %COUNT% payments", + "poolHashrate": "Pool Hash Rate", + "currentEffort": "Current Effort", + "networkHashrate": "Network Hash Rate", + "networkDifficulty": "Difficulty", + "blockchainHeight": "Blockchain Height", + "networkLastReward": "Last Reward", + "poolMiners": "Connected Miners", + "poolFee": "Pool Fee", - "connectionDetails": "Connection Details", - "miningPoolHost": "Mining Pool Address", - "cnAlgorithm": "Algorithm", - "username": "Username", - "usernameDesc": "This is your wallet address", - "paymentId": "Exchange Payment ID", - "fixedDiff": "Difficulty locking", - "address": "address", - "addrPaymentId": "paymentID", - "addrDiff": "diff", - "password": "Password", - "passwordDesc": "This is your worker name", - "emailNotifications": "Email Notifications", - "miningPorts": "Mining Ports", - "port": "Port", - "portDiff": "Starting Difficulty", - "description": "Description", - "miningApps": "Mining Applications", - "configGeneratorDesc": "Generate your custom configuration to mine on our pool", - "addressField": "Wallet Address", - "paymentIdField": "Payment ID for exchanges (optional)", - "fixedDiffField": "Fixed difficulty (optional)", - "workerNameField": "Worker_Name", - "emailNotificationsField": "Email Notifications (optional)", - "generateConfig": "Generate configuration", - "appName": "App Name", - "appArch": "Architecture", - "appDesc": "Features", - "download": "Download", - "showConfig": "See more", + "minerStats": "Your Stats & Payment History", + "workerStats": "Workers Statistics", + "miner": "Miner", + "miners": "Miners", + "minersCount": "miners", + "workers": "Workers", + "workersCount": "workers", + "workerName": "Worker Name", + "lastHash": "Last Hash", + "hashRate": "Hash Rate", + "currentHashRate": "Current Hash Rate", + "lastShare": "Last Share Submitted", + "totalHashes": "Total Hashes Submitted", + "top10miners": "Top 10 miners", - "market": "Market / Calculator", - "loadingMarket": "Loading market prices", - "priceIn": "Price in", - "hashPer": "Hash/", - "estimateProfit": "Estimate Mining Profits", - "enterYourHashrate": "Enter Your Hash Rate", - "perDay": "per day", + "blocksTotal": "Blocks Found", + "blockSolvedTime": "Blocks Found Every", + "blocksMaturityCount": "Maturity Requirement", + "efficiency": "Efficiency", + "averageLuck": "Average Luck", + "timeFound": "Time Found", + "reward": "Reward", + "height": "Height", + "difficulty": "Difficulty", + "blockHash": "Block Hash", + "effort": "Effort", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", - "verificationFields": "Verification fields", - "minerVerification": "In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.", - "minerAddress": "Miner Address", - "minerIP": "Miner IP address", - "setMinimumPayout": "Set your minimal payout level", - "minerMinPayout": "If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.", - "minimumPayout": "Minimum payout", - "enableEmailNotifications": "Enable email notifications", - "minerEmailNotify": "This pool will send out email notification when a block is found and whenever a payout happens.", - "emailAddress": "Email address", - "noMinerAddress": "No miner address specified", - "noMinerIP": "No miner IP address specified", - "noPayoutLevel": "No payout level specified", - "noEmail": "No email address specified", - "invalidEmail": "Invalid email address specified", - "minerPayoutSet": "Done! Your minimum payout level was set", - "notificationEnabled": "Done! Email notifications have been enabled", - "notificationDisabled": "Done! Email notifications have been disabled", + "payments": "Payments", + "paymentsHistory": "Payments History", + "paymentsTotal": "Total Payments", + "paymentsMinimum": "Minimum Payout", + "paymentsInterval": "Payment Interval", + "paymentsDenomination": "Denomination Unit", + "timeSent": "Time Sent", + "transactionHash": "Transaction Hash", + "amount": "Amount", + "fee": "Fee", + "mixin": "Mixin", + "payees": "Payees", + "pendingBalance": "Pending Balance", + "totalPaid": "Total Paid", + "payoutEstimate": "Current Payout Estimate", + "paymentSummarySingle": "On %DATE% you have received %AMOUNT%", + "paymentSummaryMulti": "On %DATE% you have received %AMOUNT% in %COUNT% payments", - "enterYourAddress": "Enter Your Address", - "enterYourMinerIP": "An IP address your miners use (any)", - "enterYourEmail": "Enter Your E-Mail Address (optional)", + "connectionDetails": "Connection Details", + "miningPoolHost": "Mining Pool Address", + "cnAlgorithm": "Algorithm", + "username": "Username", + "usernameDesc": "This is your wallet address", + "paymentId": "Exchange Payment ID", + "fixedDiff": "Difficulty locking", + "address": "address", + "addrPaymentId": "paymentID", + "addrDiff": "diff", + "password": "Password", + "passwordDesc": "This is your worker name", + "emailNotifications": "Email Notifications", + "miningPorts": "Mining Ports", + "port": "Port", + "portDiff": "Starting Difficulty", + "description": "Description", + "miningApps": "Mining Applications", + "configGeneratorDesc": "Generate your custom configuration to mine on our pool", + "addressField": "Wallet Address", + "paymentIdField": "Payment ID for exchanges (optional)", + "fixedDiffField": "Fixed difficulty (optional)", + "workerNameField": "Worker_Name", + "emailNotificationsField": "Email Notifications (optional)", + "generateConfig": "Generate configuration", + "appName": "App Name", + "appArch": "Architecture", + "appDesc": "Features", + "download": "Download", + "showConfig": "See more", - "lookup": "Lookup", - "searching": "Searching...", - "loadMore": "Load more", - "set": "Set", - "enable": "Enable", - "disable": "Disable", - - "status": "Status", - "updated": "Updated:", - "source": "Source:", - "error": "Error:", + "market": "Market / Calculator", + "loadingMarket": "Loading market prices", + "priceIn": "Price in", + "hashPer": "Hash/", + "estimateProfit": "Estimate Mining Profits", + "enterYourHashrate": "Enter Your Hash Rate", + "perDay": "per day", - "na": "N/A", - "estimated": "estimated", - "never": "Never", - "second": "second", - "seconds": "seconds", - "minute": "minute", - "minutes": "minutes", - "hour": "hour", - "hours": "hours", - "day": "day", - "days": "days", - "week": "week", - "weeks": "weeks", - "month": "month", - "months": "months", - "year": "year", - "years": "years", + "verificationFields": "Verification fields", + "minerVerification": "In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.", + "minerAddress": "Miner Address", + "minerIP": "Miner IP address", + "setMinimumPayout": "Set your minimal payout level", + "minerMinPayout": "If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.", + "minimumPayout": "Minimum payout", + "enableEmailNotifications": "Enable email notifications", + "minerEmailNotify": "This pool will send out email notification when a block is found and whenever a payout happens.", + "emailAddress": "Email address", + "noMinerAddress": "No miner address specified", + "noMinerIP": "No miner IP address specified", + "noPayoutLevel": "No payout level specified", + "noEmail": "No email address specified", + "invalidEmail": "Invalid email address specified", + "minerPayoutSet": "Done! Your minimum payout level was set", + "notificationEnabled": "Done! Email notifications have been enabled", + "notificationDisabled": "Done! Email notifications have been disabled", - "poweredBy": "Powered by", - "openSource": "open sourced under the" + "enterYourAddress": "Enter Your Address", + "enterYourMinerIP": "An IP address your miners use (any)", + "enterYourEmail": "Enter Your E-Mail Address (optional)", + + "lookup": "Lookup", + "searching": "Searching...", + "loadMore": "Load more", + "set": "Set", + "enable": "Enable", + "disable": "Disable", + + "status": "Status", + "updated": "Updated:", + "source": "Source:", + "error": "Error:", + + "na": "N/A", + "estimated": "estimated", + "never": "Never", + "second": "second", + "seconds": "seconds", + "minute": "minute", + "minutes": "minutes", + "hour": "hour", + "hours": "hours", + "day": "day", + "days": "days", + "week": "week", + "weeks": "weeks", + "month": "month", + "months": "months", + "year": "year", + "years": "years", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" } diff --git a/website_example/lang/es.json b/website_example/lang/es.json index b667f6f90..f6fe16654 100644 --- a/website_example/lang/es.json +++ b/website_example/lang/es.json @@ -1,168 +1,168 @@ -{ - "miningPool": "Mining Pool", - "dashboard": "Panel", - "gettingStarted": "Empezar", - "yourStats": "Estadísticas personales", - "poolBlocks": "Bloques de la Pool", - "settings": "Ajustes", - "faq": "FAQ", - "telegram": "Grupo de Telegram", - "discord": "Discord", - "contactUs": "Contacto", - - "network": "Red", - "pool": "Pool", - "you": "Tú Tasa", - "statsUpdated": "Estadísticas actualizadas", - - "poolHashrate": "Tasa Pool (hash)", - "currentEffort": "Ronda actual", - "networkHashrate": "Tasa Red (hash)", - "networkDifficulty": "Dificultad", - "blockchainHeight": "Altura", - "networkLastReward": "Última recompensa", - "poolMiners": "Mineros conectados", - "poolFee": "Tarifa Pool", - - "minerStats": "Tus estadísticas e historial de pagos", - "workerStats": "Estadísticas personales", - "miner": "Minero", - "miners": "Mineros", - "minersCount": "mineros", - "workers": "Trabajadores", - "workersCount": "trabajadores", - "workerName": "Nombre Trabajador", - "lastHash": "Último hash", - "hashRate": "Tasa (hash)", - "currentHashRate": "Tasa actual (hash)", - "lastShare": "Última acción enviada", - "totalHashes": "Total de acciones enviadas", - "top10miners": "Top 10 Mineros", +{ + "miningPool": "Mining Pool", + "dashboard": "Panel", + "gettingStarted": "Empezar", + "yourStats": "Estadísticas personales", + "poolBlocks": "Bloques de la Pool", + "settings": "Ajustes", + "faq": "FAQ", + "telegram": "Grupo de Telegram", + "discord": "Discord", + "contactUs": "Contacto", - "blocksTotal": "Bloques encontrados", - "blockSolvedTime": "Bloque encontrado cada", - "blocksMaturityCount": "Madurez requerida", - "efficiency": "Eficiencia", - "averageLuck": "Promedio de suerte", - "timeFound": "Fecha y hora", - "reward": "Recompensa", - "height": "Altura", - "difficulty": "Dificultad", - "blockHash": "Bloque Hash", - "effort": "Esfuerzo", - "blocksFoundLast24": "Bloques encontrados en las últimas 24 horas", - "blocksFoundLastDays": "Bloques encontrados en los últimos {DAYS} días", + "network": "Red", + "pool": "Pool", + "you": "Tú Tasa", + "statsUpdated": "Estadísticas actualizadas", - "payments": "Pagos", - "paymentsHistory": "Historial de pagos", - "paymentsTotal": "Total de pagos", - "paymentsMinimum": "Pago mínimo", - "paymentsInterval": "Intervalo de pagos", - "paymentsDenomination": "Unidad de denominación", - "timeSent": "Fecha y hora", - "transactionHash": "Hash transacción", - "amount": "Importe", - "fee": "Tarifa", - "mixin": "Mixin", - "payees": "Beneficiarios", - "pendingBalance": "Balance pendiente", - "totalPaid": "Total pagado", - "payoutEstimate": "Pago estimado", - "paymentSummarySingle": "El %DATE% has recibido %AMOUNT%", - "paymentSummaryMulti": "El %DATE% has recibido %AMOUNT% en %COUNT% pagos", + "poolHashrate": "Tasa Pool (hash)", + "currentEffort": "Ronda actual", + "networkHashrate": "Tasa Red (hash)", + "networkDifficulty": "Dificultad", + "blockchainHeight": "Altura", + "networkLastReward": "Última recompensa", + "poolMiners": "Mineros conectados", + "poolFee": "Tarifa Pool", - "connectionDetails": "Detalles de conexión", - "miningPoolHost": "Dirección de la Pool", - "cnAlgorithm": "Algoritmo", - "username": "Usuario", - "usernameDesc": "Tu dirección del wallet", - "paymentId": "ID de pago (Exchanges)", - "fixedDiff": "Dificultad fija", - "address": "dirección", - "addrPaymentId": "IDPago", - "addrDiff": "diff", - "password": "Contraseña", - "passwordDesc": "Nombre de su trabajador", - "emailNotifications": "Notificaciones por E-Mail", - "miningPorts": "Puertos de minería", - "port": "Puerto", - "portDiff": "Dificultad inicial", - "description": "Descripción", - "miningApps": "Aplicaciones de minería", - "configGeneratorDesc": "Genera tu configuración personalizada para minar a nuestra Pool", - "addressField": "Dirección Wallet", - "paymentIdField": "ID de Pago para Exchanges (opcional)", - "fixedDiffField": "Dificultad fija (opcional)", - "workerNameField": "Nombre_Trabajador", - "emailNotificationsField": "Notificaciones por E-Mail (opcional)", - "generateConfig": "Generar configuración", - "appName": "Aplicación", - "appArch": "Arquitectura", - "appDesc": "Características", - "download": "Descargar", - "showConfig": "Ver más", + "minerStats": "Tus estadísticas e historial de pagos", + "workerStats": "Estadísticas personales", + "miner": "Minero", + "miners": "Mineros", + "minersCount": "mineros", + "workers": "Trabajadores", + "workersCount": "trabajadores", + "workerName": "Nombre Trabajador", + "lastHash": "Último hash", + "hashRate": "Tasa (hash)", + "currentHashRate": "Tasa actual (hash)", + "lastShare": "Última acción enviada", + "totalHashes": "Total de acciones enviadas", + "top10miners": "Top 10 Mineros", - "market": "Mercado / Calculadora", - "loadingMarket": "Cargando precios del mercado", - "priceIn": "Precio en", - "hashPer": "Hash/", - "estimateProfit": "Beneficios estimados", - "enterYourHashrate": "Entra tu tasa (hash)", - "perDay": "por día", + "blocksTotal": "Bloques encontrados", + "blockSolvedTime": "Bloque encontrado cada", + "blocksMaturityCount": "Madurez requerida", + "efficiency": "Eficiencia", + "averageLuck": "Promedio de suerte", + "timeFound": "Fecha y hora", + "reward": "Recompensa", + "height": "Altura", + "difficulty": "Dificultad", + "blockHash": "Bloque Hash", + "effort": "Esfuerzo", + "blocksFoundLast24": "Bloques encontrados en las últimas 24 horas", + "blocksFoundLastDays": "Bloques encontrados en los últimos {DAYS} días", - "verificationFields": "Campos de verificación", - "minerVerification": "Para tener un poco más de seguridad que la dirección de la cartera es vuestra, le pedimos que proporcione una de las direcciones IP que utiliza su minero.", - "minerAddress": "Dirección del monedero", - "minerIP": "Dirección IP del Minero", - "setMinimumPayout": "Establece el nivel mínimo de pago", - "minerMinPayout": "Si prefiere un nivel de pago más alto que el predeterminado del grupo, aquí puede cambiarlo para sus mineros. El importe que indique aquí se convertirá en el importe mínimo de los pagos de su dirección.", - "minimumPayout": "Pago mínimo", - "enableEmailNotifications": "Activar notificaciones por correo electrónico", - "minerEmailNotify": "Esta Pool enviará una notificación por correo electrónico cuando se encuentre un bloque y cuando se produzca un pago.", - "emailAddress": "Dirección de correo electrónico", - "noMinerAddress": "No se ha especificado ninguna dirección del monedero", - "noMinerIP": "No se especificado ninguna dirección IP", - "noPayoutLevel": "No se ha especificado ningún nivel de pago", - "noEmail": "No se ha especificado ninguna dirección de correo electrónico", - "invalidEmail": "Se ha especificado una dirección de correo electrónico no válida", - "minerPayoutSet": "Hecho! Se ha establecido el nivel mínimo de pago", - "notificationEnabled": "Hecho! Se han activado las notificaciones por correo electrónico", - "notificationDisabled": "Hecho! Se han desactivado las notificaciones por correo electrónico", + "payments": "Pagos", + "paymentsHistory": "Historial de pagos", + "paymentsTotal": "Total de pagos", + "paymentsMinimum": "Pago mínimo", + "paymentsInterval": "Intervalo de pagos", + "paymentsDenomination": "Unidad de denominación", + "timeSent": "Fecha y hora", + "transactionHash": "Hash transacción", + "amount": "Importe", + "fee": "Tarifa", + "mixin": "Mixin", + "payees": "Beneficiarios", + "pendingBalance": "Balance pendiente", + "totalPaid": "Total pagado", + "payoutEstimate": "Pago estimado", + "paymentSummarySingle": "El %DATE% has recibido %AMOUNT%", + "paymentSummaryMulti": "El %DATE% has recibido %AMOUNT% en %COUNT% pagos", - "enterYourAddress": "Introduzca su dirección", - "enterYourMinerIP": "Una dirección IP que utilicen los mineros (cualquiera)", - "enterYourEmail": "Introduzca su dirección de correo electrónico (opcional)", + "connectionDetails": "Detalles de conexión", + "miningPoolHost": "Dirección de la Pool", + "cnAlgorithm": "Algoritmo", + "username": "Usuario", + "usernameDesc": "Tu dirección del wallet", + "paymentId": "ID de pago (Exchanges)", + "fixedDiff": "Dificultad fija", + "address": "dirección", + "addrPaymentId": "IDPago", + "addrDiff": "diff", + "password": "Contraseña", + "passwordDesc": "Nombre de su trabajador", + "emailNotifications": "Notificaciones por E-Mail", + "miningPorts": "Puertos de minería", + "port": "Puerto", + "portDiff": "Dificultad inicial", + "description": "Descripción", + "miningApps": "Aplicaciones de minería", + "configGeneratorDesc": "Genera tu configuración personalizada para minar a nuestra Pool", + "addressField": "Dirección Wallet", + "paymentIdField": "ID de Pago para Exchanges (opcional)", + "fixedDiffField": "Dificultad fija (opcional)", + "workerNameField": "Nombre_Trabajador", + "emailNotificationsField": "Notificaciones por E-Mail (opcional)", + "generateConfig": "Generar configuración", + "appName": "Aplicación", + "appArch": "Arquitectura", + "appDesc": "Características", + "download": "Descargar", + "showConfig": "Ver más", - "lookup": "Buscar", - "searching": "Buscando...", - "loadMore": "Cargar más", - "set": "Establecer", - "enable": "Activar", - "disable": "Desactivar", - - "status": "Estado", - "updated": "Actualizado:", - "source": "Origen:", - "error": "Error:", + "market": "Mercado / Calculadora", + "loadingMarket": "Cargando precios del mercado", + "priceIn": "Precio en", + "hashPer": "Hash/", + "estimateProfit": "Beneficios estimados", + "enterYourHashrate": "Entra tu tasa (hash)", + "perDay": "por día", - "na": "N/A", - "estimated": "estimado", - "never": "Nunca", - "second": "segundo", - "seconds": "segundos", - "minute": "minuto", - "minutes": "minutos", - "hour": "hora", - "hours": "horas", - "day": "día", - "days": "dias", - "week": "semana", - "weeks": "semanas", - "month": "mes", - "months": "meses", - "year": "año", - "years": "años", + "verificationFields": "Campos de verificación", + "minerVerification": "Para tener un poco más de seguridad que la dirección de la cartera es vuestra, le pedimos que proporcione una de las direcciones IP que utiliza su minero.", + "minerAddress": "Dirección del monedero", + "minerIP": "Dirección IP del Minero", + "setMinimumPayout": "Establece el nivel mínimo de pago", + "minerMinPayout": "Si prefiere un nivel de pago más alto que el predeterminado del grupo, aquí puede cambiarlo para sus mineros. El importe que indique aquí se convertirá en el importe mínimo de los pagos de su dirección.", + "minimumPayout": "Pago mínimo", + "enableEmailNotifications": "Activar notificaciones por correo electrónico", + "minerEmailNotify": "Esta Pool enviará una notificación por correo electrónico cuando se encuentre un bloque y cuando se produzca un pago.", + "emailAddress": "Dirección de correo electrónico", + "noMinerAddress": "No se ha especificado ninguna dirección del monedero", + "noMinerIP": "No se especificado ninguna dirección IP", + "noPayoutLevel": "No se ha especificado ningún nivel de pago", + "noEmail": "No se ha especificado ninguna dirección de correo electrónico", + "invalidEmail": "Se ha especificado una dirección de correo electrónico no válida", + "minerPayoutSet": "Hecho! Se ha establecido el nivel mínimo de pago", + "notificationEnabled": "Hecho! Se han activado las notificaciones por correo electrónico", + "notificationDisabled": "Hecho! Se han desactivado las notificaciones por correo electrónico", - "poweredBy": "Powered by", - "openSource": "open sourced bajo " + "enterYourAddress": "Introduzca su dirección", + "enterYourMinerIP": "Una dirección IP que utilicen los mineros (cualquiera)", + "enterYourEmail": "Introduzca su dirección de correo electrónico (opcional)", + + "lookup": "Buscar", + "searching": "Buscando...", + "loadMore": "Cargar más", + "set": "Establecer", + "enable": "Activar", + "disable": "Desactivar", + + "status": "Estado", + "updated": "Actualizado:", + "source": "Origen:", + "error": "Error:", + + "na": "N/A", + "estimated": "estimado", + "never": "Nunca", + "second": "segundo", + "seconds": "segundos", + "minute": "minuto", + "minutes": "minutos", + "hour": "hora", + "hours": "horas", + "day": "día", + "days": "dias", + "week": "semana", + "weeks": "semanas", + "month": "mes", + "months": "meses", + "year": "año", + "years": "años", + + "poweredBy": "Powered by", + "openSource": "open sourced bajo " } diff --git a/website_example/lang/fr.json b/website_example/lang/fr.json index 84ce29157..038a3ea40 100644 --- a/website_example/lang/fr.json +++ b/website_example/lang/fr.json @@ -1,168 +1,168 @@ { - "miningPool": "Mining Pool", - "dashboard": "Tableau de bord", - "gettingStarted": "Comment démarrer", - "yourStats": "Vos statistiques", - "poolBlocks": "Blocs trouvés", - "settings": "Paramètres", - "faq": "FAQ", - "telegram": "Telegram", - "discord": "Discord", - "contactUs": "Nous contacter", - - "network": "Réseau", - "pool": "Pool", - "you": "Vous", - "statsUpdated": "Statistiques mises à jour", - - "poolHashrate": "Taux de Hash du Pool", - "currentEffort": "Effort actuel", - "networkHashrate": "Taux de Hash du réseau", - "networkDifficulty": "Difficulté", - "blockchainHeight": "Hauteur de la BlockChain", - "networkLastReward": "Dernière récompense", - "poolMiners": "Mineurs connectés", - "poolFee": "Frais du pool", - - "minerStats": "Vos statistiques et Historique des paiements", - "workerStats": "Statistiques des travailleurs", - "miner": "Mineur", - "miners": "Mineurs", - "minersCount": "mineurs", - "workers": "Travailleurs", - "workersCount": "travailleurs", - "workerName": "Nom du travailleur", - "lastHash": "Dernier Hash", - "hashRate": "Taux de Hash", - "currentHashRate": "Taux de Hash actuel", - "lastShare": "Dernière transmission", - "totalHashes": "Hashes transmis", - "top10miners": "Top 10 mineurs", + "miningPool": "Mining Pool", + "dashboard": "Tableau de bord", + "gettingStarted": "Comment démarrer", + "yourStats": "Vos statistiques", + "poolBlocks": "Blocs trouvés", + "settings": "Paramètres", + "faq": "FAQ", + "telegram": "Telegram", + "discord": "Discord", + "contactUs": "Nous contacter", - "blocksTotal": "Blocs trouvés", - "blockSolvedTime": "Bloc trouvé chaque", - "blocksMaturityCount": "Maturité requise", - "efficiency": "Efficacité", - "averageLuck": "Chance moyenne", - "timeFound": "Trouvé le", - "reward": "Récompense", - "height": "Hauteur", - "difficulty": "Difficulté", - "blockHash": "Hash du bloc", - "effort": "Effort", - "blocksFoundLast24": "Blocs trouvés dans les 24 dernières heures", - "blocksFoundLastDays": "Blocs trouvés dans les derniers {DAYS} jours", + "network": "Réseau", + "pool": "Pool", + "you": "Vous", + "statsUpdated": "Statistiques mises à jour", - "payments": "Paiements", - "paymentsHistory": "Historique des paiements", - "paymentsTotal": "Nombre de paiements", - "paymentsMinimum": "Minimum avant paiement", - "paymentsInterval": "Intervale de paiement", - "paymentsDenomination": "Unité de dénomination", - "timeSent": "Envoyé le", - "transactionHash": "Hash de transaction", - "amount": "Montant", - "fee": "Frais", - "mixin": "Mixin", - "payees": "Payés", - "pendingBalance": "Balance en attente", - "totalPaid": "Total payé", - "payoutEstimate": "Estimation de paiement", - "paymentSummarySingle": "Le %DATE% vous avez reçu %AMOUNT%", - "paymentSummaryMulti": "Le %DATE% vous avez reçu %AMOUNT% en %COUNT% paiements", + "poolHashrate": "Taux de Hash du Pool", + "currentEffort": "Effort actuel", + "networkHashrate": "Taux de Hash du réseau", + "networkDifficulty": "Difficulté", + "blockchainHeight": "Hauteur de la BlockChain", + "networkLastReward": "Dernière récompense", + "poolMiners": "Mineurs connectés", + "poolFee": "Frais du pool", - "connectionDetails": "Détails de connexion", - "miningPoolHost": "Adresse du pool", - "cnAlgorithm": "Algorithme", - "username": "Nom d'utilisateur", - "usernameDesc": "C'est l'adresse de votre wallet", - "paymentId": "ID de paiement de l'exchange", - "fixedDiff": "Difficulté fixe", - "address": "adresse", - "addrPaymentId": "idPaiement", - "addrDiff": "diff", - "password": "Mot de passe", - "passwordDesc": "C'est l'identifiant de votre travailleur", - "emailNotifications": "Notifications par email", - "miningPorts": "Ports de minage", - "port": "Port", - "portDiff": "Difficulté de départ", - "description": "Description", - "miningApps": "Applications de minage", - "configGeneratorDesc": "Générer votre configuration personalisée pour miner sur notre pool", - "addressField": "Adresse de votre wallet", - "paymentIdField": "ID de paiement pour l'exchange (optionnel)", - "fixedDiffField": "Difficulté fixe (optionnel)", - "workerNameField": "Nom_du_Travailleur", - "emailNotificationsField": "Notifications par Email (optionnel)", - "generateConfig": "Générer la configuration", - "appName": "Nom de l'App", - "appArch": "Architecture", - "appDesc": "Fonctionalités", - "download": "Télécharger", - "showConfig": "Afficher", + "minerStats": "Vos statistiques et Historique des paiements", + "workerStats": "Statistiques des travailleurs", + "miner": "Mineur", + "miners": "Mineurs", + "minersCount": "mineurs", + "workers": "Travailleurs", + "workersCount": "travailleurs", + "workerName": "Nom du travailleur", + "lastHash": "Dernier Hash", + "hashRate": "Taux de Hash", + "currentHashRate": "Taux de Hash actuel", + "lastShare": "Dernière transmission", + "totalHashes": "Hashes transmis", + "top10miners": "Top 10 mineurs", - "market": "Marché et calculateur", - "loadingMarket": "Chargement des prix du marché", - "priceIn": "Prix en", - "hashPer": "Hash/", - "estimateProfit": "Estimation des profits de minage", - "enterYourHashrate": "Entrez votre taux de Hash", - "perDay": "par jour", + "blocksTotal": "Blocs trouvés", + "blockSolvedTime": "Bloc trouvé chaque", + "blocksMaturityCount": "Maturité requise", + "efficiency": "Efficacité", + "averageLuck": "Chance moyenne", + "timeFound": "Trouvé le", + "reward": "Récompense", + "height": "Hauteur", + "difficulty": "Difficulté", + "blockHash": "Hash du bloc", + "effort": "Effort", + "blocksFoundLast24": "Blocs trouvés dans les 24 dernières heures", + "blocksFoundLastDays": "Blocs trouvés dans les derniers {DAYS} jours", - "verificationFields": "Champs de vérification", - "minerVerification": "Afin de nous assurer que l'adresse du mineur est bien la vôtre, nous vous demandons d'entrer une adresse IP utilisée par votre mineur.", - "minerAddress": "Adresse du mineur", - "minerIP": "Adresse IP du mineur", - "setMinimumPayout": "Configurer votre niveau de paiement minimum", - "minerMinPayout": "Si vous préférez un montant de paiement minimum plus élevé que celui du pool c'est ici que vous pouvez le changer pour vos mineurs. Le montant que vous indiquerez ici deviendra le montant minimum pour les paiements à votre adresse.", - "minimumPayout": "Paiement minimum", - "enableEmailNotifications": "Activer les notifications par email", - "minerEmailNotify": "Ce pool peut vous envoyer une notification par email lorsqu'un bloc est trouvé ou bien lorsqu'un paiement vous est transmis.", - "emailAddress": "Adresse email", - "noMinerAddress": "Aucune adresse de mineur spécifiée", - "noMinerIP": "Aucune adresse IP pour votre mineur spécifiée", - "noPayoutLevel": "Aucun niveau de paiement spécifié", - "noEmail": "Aucune adresse email spécifiée", - "invalidEmail": "L'adresse email spécifiée est invalide", - "minerPayoutSet": "Fait! Votre niveau de paiement minimum a été configuré", - "notificationEnabled": "Fait! Les notifications par email ont été activées", - "notificationDisabled": "Fait! Les notifications par email ont été désactivées", + "payments": "Paiements", + "paymentsHistory": "Historique des paiements", + "paymentsTotal": "Nombre de paiements", + "paymentsMinimum": "Minimum avant paiement", + "paymentsInterval": "Intervale de paiement", + "paymentsDenomination": "Unité de dénomination", + "timeSent": "Envoyé le", + "transactionHash": "Hash de transaction", + "amount": "Montant", + "fee": "Frais", + "mixin": "Mixin", + "payees": "Payés", + "pendingBalance": "Balance en attente", + "totalPaid": "Total payé", + "payoutEstimate": "Estimation de paiement", + "paymentSummarySingle": "Le %DATE% vous avez reçu %AMOUNT%", + "paymentSummaryMulti": "Le %DATE% vous avez reçu %AMOUNT% en %COUNT% paiements", - "enterYourAddress": "Entrez votre adresse", - "enterYourMinerIP": "Une adresse IP utilisée par votre mineur (peu importe)", - "enterYourEmail": "Votre adresse email", + "connectionDetails": "Détails de connexion", + "miningPoolHost": "Adresse du pool", + "cnAlgorithm": "Algorithme", + "username": "Nom d'utilisateur", + "usernameDesc": "C'est l'adresse de votre wallet", + "paymentId": "ID de paiement de l'exchange", + "fixedDiff": "Difficulté fixe", + "address": "adresse", + "addrPaymentId": "idPaiement", + "addrDiff": "diff", + "password": "Mot de passe", + "passwordDesc": "C'est l'identifiant de votre travailleur", + "emailNotifications": "Notifications par email", + "miningPorts": "Ports de minage", + "port": "Port", + "portDiff": "Difficulté de départ", + "description": "Description", + "miningApps": "Applications de minage", + "configGeneratorDesc": "Générer votre configuration personalisée pour miner sur notre pool", + "addressField": "Adresse de votre wallet", + "paymentIdField": "ID de paiement pour l'exchange (optionnel)", + "fixedDiffField": "Difficulté fixe (optionnel)", + "workerNameField": "Nom_du_Travailleur", + "emailNotificationsField": "Notifications par Email (optionnel)", + "generateConfig": "Générer la configuration", + "appName": "Nom de l'App", + "appArch": "Architecture", + "appDesc": "Fonctionalités", + "download": "Télécharger", + "showConfig": "Afficher", - "lookup": "Chercher", - "searching": "Recherche...", - "loadMore": "Charger plus", - "set": "Configurer", - "enable": "Activer", - "disable": "Désactiver", - - "status": "Statut", - "updated": "Mis à jour:", - "source": "Source:", - "error": "Erreur:", + "market": "Marché et calculateur", + "loadingMarket": "Chargement des prix du marché", + "priceIn": "Prix en", + "hashPer": "Hash/", + "estimateProfit": "Estimation des profits de minage", + "enterYourHashrate": "Entrez votre taux de Hash", + "perDay": "par jour", - "na": "N/D", - "estimated": "estimé", - "never": "Jamais", - "second": "seconde", - "seconds": "secondes", - "minute": "minute", - "minutes": "minutes", - "hour": "heure", - "hours": "heures", - "day": "jour", - "days": "jours", - "week": "semaine", - "weeks": "weeks", - "month": "mois", - "months": "mois", - "year": "année", - "years": "années", + "verificationFields": "Champs de vérification", + "minerVerification": "Afin de nous assurer que l'adresse du mineur est bien la vôtre, nous vous demandons d'entrer une adresse IP utilisée par votre mineur.", + "minerAddress": "Adresse du mineur", + "minerIP": "Adresse IP du mineur", + "setMinimumPayout": "Configurer votre niveau de paiement minimum", + "minerMinPayout": "Si vous préférez un montant de paiement minimum plus élevé que celui du pool c'est ici que vous pouvez le changer pour vos mineurs. Le montant que vous indiquerez ici deviendra le montant minimum pour les paiements à votre adresse.", + "minimumPayout": "Paiement minimum", + "enableEmailNotifications": "Activer les notifications par email", + "minerEmailNotify": "Ce pool peut vous envoyer une notification par email lorsqu'un bloc est trouvé ou bien lorsqu'un paiement vous est transmis.", + "emailAddress": "Adresse email", + "noMinerAddress": "Aucune adresse de mineur spécifiée", + "noMinerIP": "Aucune adresse IP pour votre mineur spécifiée", + "noPayoutLevel": "Aucun niveau de paiement spécifié", + "noEmail": "Aucune adresse email spécifiée", + "invalidEmail": "L'adresse email spécifiée est invalide", + "minerPayoutSet": "Fait! Votre niveau de paiement minimum a été configuré", + "notificationEnabled": "Fait! Les notifications par email ont été activées", + "notificationDisabled": "Fait! Les notifications par email ont été désactivées", - "poweredBy": "Propulsé par", - "openSource": "et libre de droits sous licence" -} \ No newline at end of file + "enterYourAddress": "Entrez votre adresse", + "enterYourMinerIP": "Une adresse IP utilisée par votre mineur (peu importe)", + "enterYourEmail": "Votre adresse email", + + "lookup": "Chercher", + "searching": "Recherche...", + "loadMore": "Charger plus", + "set": "Configurer", + "enable": "Activer", + "disable": "Désactiver", + + "status": "Statut", + "updated": "Mis à jour:", + "source": "Source:", + "error": "Erreur:", + + "na": "N/D", + "estimated": "estimé", + "never": "Jamais", + "second": "seconde", + "seconds": "secondes", + "minute": "minute", + "minutes": "minutes", + "hour": "heure", + "hours": "heures", + "day": "jour", + "days": "jours", + "week": "semaine", + "weeks": "weeks", + "month": "mois", + "months": "mois", + "year": "année", + "years": "années", + + "poweredBy": "Propulsé par", + "openSource": "et libre de droits sous licence" +} diff --git a/website_example/lang/it.json b/website_example/lang/it.json index cbe084fc1..26618aab2 100644 --- a/website_example/lang/it.json +++ b/website_example/lang/it.json @@ -1,168 +1,168 @@ { - "miningPool": "Mining Pool", - "dashboard": "Dashboard", - "gettingStarted": "Come Iniziare", - "yourStats": "Statistiche del Worker", - "poolBlocks": "Pool Blocks", - "settings": "Impostazioni", - "faq": "FAQ", - "telegram": "Gruppo Telegram", - "discord": "Discord", - "contactUs": "Contattaci", - - "network": "Rete", - "pool": "Pool", - "you": "Tu", - "statsUpdated": "Stats Aggiornati", - - "poolHashrate": "Pool Hash Rate", - "currentEffort": "Current Effort", - "networkHashrate": "Network Hash Rate", - "networkDifficulty": "Difficoltà", - "blockchainHeight": "Blockchain Height", - "networkLastReward": "Ultimo Reward", - "poolMiners": "Miners Connessi", - "poolFee": "Pool Fee", - - "minerStats": "Stats Personali & Storia dei tuoi pagamenti", - "workerStats": "Statistiche del Worker", - "miner": "Miner", - "miners": "Miners", - "minersCount": "miners", - "workers": "Workers", - "workersCount": "workers", - "workerName": "Worker Name", - "lastHash": "Ultimo Hash", - "hashRate": "Hash Rate", - "currentHashRate": "Attuale Hash Rate", - "lastShare": "Ultimo Share Trasmesso", - "totalHashes": "Totale Hashes Trasmessi", - "top10miners": "Top 10 miners", + "miningPool": "Mining Pool", + "dashboard": "Dashboard", + "gettingStarted": "Come Iniziare", + "yourStats": "Statistiche del Worker", + "poolBlocks": "Pool Blocks", + "settings": "Impostazioni", + "faq": "FAQ", + "telegram": "Gruppo Telegram", + "discord": "Discord", + "contactUs": "Contattaci", - "blocksTotal": "Blocchi trovati", - "blockSolvedTime": "Blocco trovato ogni", - "blocksMaturityCount": "Maturità richiesta", - "efficiency": "Efficenza", - "averageLuck": "Fortuna", - "timeFound": "Orario trovato", - "reward": "Ricompensa", - "height": "Height", - "difficulty": "Difficoltà", - "blockHash": "Block Hash", - "effort": "Effort", - "blocksFoundLast24": "Blocks found in the last 24 hours", - "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + "network": "Rete", + "pool": "Pool", + "you": "Tu", + "statsUpdated": "Stats Aggiornati", - "payments": "Pagamenti", - "paymentsHistory": "Storia Pagamenti", - "paymentsTotal": "Pagamenti totali", - "paymentsMinimum": "Minimo pagamento", - "paymentsInterval": "intervallo pagamento", - "paymentsDenomination": "Unità di denominazione", - "timeSent": "Orario Inviato", - "transactionHash": "Transazione Hash", - "amount": "Quantità", - "fee": "Fee", - "mixin": "Mixin", - "payees": "Pagati", - "pendingBalance": "In sospeso", - "totalPaid": "Totale pagato", - "payoutEstimate": "Stima del pagamento corrente", - "paymentSummarySingle": "In %DATE% hai ricevuto %AMOUNT%", - "paymentSummaryMulti": "In %DATE% hai ricevuto %AMOUNT% in %COUNT% pagamenti", + "poolHashrate": "Pool Hash Rate", + "currentEffort": "Current Effort", + "networkHashrate": "Network Hash Rate", + "networkDifficulty": "Difficoltà", + "blockchainHeight": "Blockchain Height", + "networkLastReward": "Ultimo Reward", + "poolMiners": "Miners Connessi", + "poolFee": "Pool Fee", - "connectionDetails": "Detagli Connessione", - "miningPoolHost": "Mining Pool Address", - "cnAlgorithm": "Algorithmo", - "username": "Username", - "usernameDesc": "questo è L'indrizzo del wallet", - "paymentId": "Exchange Payment ID", - "fixedDiff": "Difficoltà di blocco", - "address": "indrizzo", - "addrPaymentId": "paymentID", - "addrDiff": "diff", - "password": "Password", - "passwordDesc": "Questo è il nome del Worker", - "emailNotifications": "Notifiche Email", - "miningPorts": "Mining Ports", - "port": "Port", - "portDiff": "Difficoltà avvio", - "description": "Descrizione", - "miningApps": "Applicazioni Mining", - "configGeneratorDesc": "Genera La tua configurazione per minare sulla nostra pool", - "addressField": "Indrizzo wallet", - "paymentIdField": "Pagamento ID per exchanges (optionale)", - "fixedDiffField": "Fixed difficulty (optionale)", - "workerNameField": "Worker_Name", - "emailNotificationsField": "Notifiche email (optionale)", - "generateConfig": "Genera configuratione", - "appName": "App Name", - "appArch": "Architettura", - "appDesc": "Features", - "download": "Download", - "showConfig": "Vedi", + "minerStats": "Stats Personali & Storia dei tuoi pagamenti", + "workerStats": "Statistiche del Worker", + "miner": "Miner", + "miners": "Miners", + "minersCount": "miners", + "workers": "Workers", + "workersCount": "workers", + "workerName": "Worker Name", + "lastHash": "Ultimo Hash", + "hashRate": "Hash Rate", + "currentHashRate": "Attuale Hash Rate", + "lastShare": "Ultimo Share Trasmesso", + "totalHashes": "Totale Hashes Trasmessi", + "top10miners": "Top 10 miners", - "market": "Market / Calculatoe", - "loadingMarket": "Loading market prices", - "priceIn": "Price in", - "hashPer": "Hash/", - "estimateProfit": "Stima dei profitti", - "enterYourHashrate": "Inserisci il tuo Hashrate", - "perDay": "al giorno", + "blocksTotal": "Blocchi trovati", + "blockSolvedTime": "Blocco trovato ogni", + "blocksMaturityCount": "Maturità richiesta", + "efficiency": "Efficenza", + "averageLuck": "Fortuna", + "timeFound": "Orario trovato", + "reward": "Ricompensa", + "height": "Height", + "difficulty": "Difficoltà", + "blockHash": "Block Hash", + "effort": "Effort", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", - "verificationFields": "Campi di verifica", - "minerVerification": "Per avere un po 'più di fiducia che l'indirizzo del tuo wallet è tuo ti chiediamo di dare uno degli indirizzi IP che viene utilizzato dal tuo miners.", - "minerAddress": "Miner Address", - "minerIP": "Miner IP address", - "setMinimumPayout": "Inserisci il pagamento minimo ", - "minerMinPayout": "Se preferisci un livello di pagamento più alto rispetto al valore predefinito del pool, è qui che puoi cambiarlo per i tuoi miner. L'importo indicato qui diventerà l'importo minimo per i pagamenti del pool al tuo indirizzo.", - "minimumPayout": "Payout Minimo", - "enableEmailNotifications": "Abilitare notifiche email", - "minerEmailNotify": "Questa pool invierà una notifica via email quando viene trovato un blocco e ogni volta che si verifica un pagamento.", - "emailAddress": "Email address", - "noMinerAddress": "Nessun indrizzo wallet indicato", - "noMinerIP": "Nessun indrizzo ip indicato del miner", - "noPayoutLevel": "Nessun livello specificato", - "noEmail": "Nessun indirizzo email specificato", - "invalidEmail": "Indrizzo email invalido", - "minerPayoutSet": "Fatto! Il tuo livello di pagamento minimo è stato impostato", - "notificationEnabled": "Fatto! Le notifiche email sono state abilitate", - "notificationDisabled": "Fatto! Le notifiche email sono state disabilitate", + "payments": "Pagamenti", + "paymentsHistory": "Storia Pagamenti", + "paymentsTotal": "Pagamenti totali", + "paymentsMinimum": "Minimo pagamento", + "paymentsInterval": "intervallo pagamento", + "paymentsDenomination": "Unità di denominazione", + "timeSent": "Orario Inviato", + "transactionHash": "Transazione Hash", + "amount": "Quantità", + "fee": "Fee", + "mixin": "Mixin", + "payees": "Pagati", + "pendingBalance": "In sospeso", + "totalPaid": "Totale pagato", + "payoutEstimate": "Stima del pagamento corrente", + "paymentSummarySingle": "In %DATE% hai ricevuto %AMOUNT%", + "paymentSummaryMulti": "In %DATE% hai ricevuto %AMOUNT% in %COUNT% pagamenti", - "enterYourAddress": "Inserisci il tuo indrizzo", - "enterYourMinerIP": "Un indrizzo ip di qualsiasi tuo miner", - "enterYourEmail": "Inserisci il tuo indrizzo email (optionale)", + "connectionDetails": "Detagli Connessione", + "miningPoolHost": "Mining Pool Address", + "cnAlgorithm": "Algorithmo", + "username": "Username", + "usernameDesc": "questo è L'indrizzo del wallet", + "paymentId": "Exchange Payment ID", + "fixedDiff": "Difficoltà di blocco", + "address": "indrizzo", + "addrPaymentId": "paymentID", + "addrDiff": "diff", + "password": "Password", + "passwordDesc": "Questo è il nome del Worker", + "emailNotifications": "Notifiche Email", + "miningPorts": "Mining Ports", + "port": "Port", + "portDiff": "Difficoltà avvio", + "description": "Descrizione", + "miningApps": "Applicazioni Mining", + "configGeneratorDesc": "Genera La tua configurazione per minare sulla nostra pool", + "addressField": "Indrizzo wallet", + "paymentIdField": "Pagamento ID per exchanges (optionale)", + "fixedDiffField": "Fixed difficulty (optionale)", + "workerNameField": "Worker_Name", + "emailNotificationsField": "Notifiche email (optionale)", + "generateConfig": "Genera configuratione", + "appName": "App Name", + "appArch": "Architettura", + "appDesc": "Features", + "download": "Download", + "showConfig": "Vedi", - "lookup": "Consulto..", - "searching": "Cerco...", - "loadMore": "Carica Di più", - "set": "imposta", - "enable": "abilita", - "disable": "Disabilita", - - "status": "Stato", - "updated": "Aggiornato:", - "source": "Fonte:", - "error": "Errore:", + "market": "Market / Calculatoe", + "loadingMarket": "Loading market prices", + "priceIn": "Price in", + "hashPer": "Hash/", + "estimateProfit": "Stima dei profitti", + "enterYourHashrate": "Inserisci il tuo Hashrate", + "perDay": "al giorno", - "na": "N/A", - "estimated": "stimato", - "never": "mai", - "second": "secondo", - "seconds": "secondi", - "minute": "minuto", - "minutes": "minuti", - "hour": "ora", - "hours": "ore", - "day": "giorno", - "days": "giorni", - "week": "settimana", - "weeks": "settimane", - "month": "mese", - "months": "mesi", - "year": "anno", - "years": "anni", + "verificationFields": "Campi di verifica", + "minerVerification": "Per avere un po 'più di fiducia che l'indirizzo del tuo wallet è tuo ti chiediamo di dare uno degli indirizzi IP che viene utilizzato dal tuo miners.", + "minerAddress": "Miner Address", + "minerIP": "Miner IP address", + "setMinimumPayout": "Inserisci il pagamento minimo ", + "minerMinPayout": "Se preferisci un livello di pagamento più alto rispetto al valore predefinito del pool, è qui che puoi cambiarlo per i tuoi miner. L'importo indicato qui diventerà l'importo minimo per i pagamenti del pool al tuo indirizzo.", + "minimumPayout": "Payout Minimo", + "enableEmailNotifications": "Abilitare notifiche email", + "minerEmailNotify": "Questa pool invierà una notifica via email quando viene trovato un blocco e ogni volta che si verifica un pagamento.", + "emailAddress": "Email address", + "noMinerAddress": "Nessun indrizzo wallet indicato", + "noMinerIP": "Nessun indrizzo ip indicato del miner", + "noPayoutLevel": "Nessun livello specificato", + "noEmail": "Nessun indirizzo email specificato", + "invalidEmail": "Indrizzo email invalido", + "minerPayoutSet": "Fatto! Il tuo livello di pagamento minimo è stato impostato", + "notificationEnabled": "Fatto! Le notifiche email sono state abilitate", + "notificationDisabled": "Fatto! Le notifiche email sono state disabilitate", - "poweredBy": "Powered by", - "openSource": "open sourced under the" + "enterYourAddress": "Inserisci il tuo indrizzo", + "enterYourMinerIP": "Un indrizzo ip di qualsiasi tuo miner", + "enterYourEmail": "Inserisci il tuo indrizzo email (optionale)", + + "lookup": "Consulto..", + "searching": "Cerco...", + "loadMore": "Carica Di più", + "set": "imposta", + "enable": "abilita", + "disable": "Disabilita", + + "status": "Stato", + "updated": "Aggiornato:", + "source": "Fonte:", + "error": "Errore:", + + "na": "N/A", + "estimated": "stimato", + "never": "mai", + "second": "secondo", + "seconds": "secondi", + "minute": "minuto", + "minutes": "minuti", + "hour": "ora", + "hours": "ore", + "day": "giorno", + "days": "giorni", + "week": "settimana", + "weeks": "settimane", + "month": "mese", + "months": "mesi", + "year": "anno", + "years": "anni", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" } diff --git a/website_example/lang/ko.json b/website_example/lang/ko.json index 229d66d51..17e77a01e 100644 --- a/website_example/lang/ko.json +++ b/website_example/lang/ko.json @@ -1,167 +1,167 @@ { - "miningPool": "Mining Pool", - "dashboard": "풀상황", - "gettingStarted": "도움말:시작", - "yourStats": "마이너(워커) 상황", - "poolBlocks": "풀블럭상태", - "settings": "설정", - "telegram": "텔레그램연결", - "discord": "Discord", - "contactUs": "문의하기", - - "network": "네트워크", - "pool": "풀", - "you": "you", - "statsUpdated": "업데이트", - - "poolHashrate": "풀의 해시레이트", - "currentEffort": "Current Effort", - "networkHashrate": "네크워크 해시레이트", - "networkDifficulty": "난이도", - "blockchainHeight": "블록체인 높이", - "networkLastReward": "최근 보상량", - "poolMiners": "연결된 마이너수", - "poolFee": "풀 수수료", - - "minerStats": "마이너 상태와 지급상황", - "workerStats": "워커 상태", - "miner": "Miner", - "miners": "Miners", - "minersCount": "miners", - "workers": "Workers", - "workersCount": "workers", - "workerName": "Worker Name", - "lastHash": "Last Hash", - "hashRate": "Hash Rate", - "currentHashRate": "Current Hash Rate", - "lastShare": "Last Share Submitted", - "totalHashes": "Total Hashes Submitted", - "top10miners": "상위 10 채굴자", + "miningPool": "Mining Pool", + "dashboard": "풀상황", + "gettingStarted": "도움말:시작", + "yourStats": "마이너(워커) 상황", + "poolBlocks": "풀블럭상태", + "settings": "설정", + "telegram": "텔레그램연결", + "discord": "Discord", + "contactUs": "문의하기", - "blocksTotal": "블록 발견", - "blockSolvedTime": "Blocks Found Every", - "blocksMaturityCount": "적립되기전 블럭수량", - "efficiency": "Efficiency", - "averageLuck": "Average Luck", - "timeFound": "블럭 발견시작", - "reward": "보상", - "height": "블럭번호", - "difficulty": "난이도", - "blockHash": "블럭 해시", - "effort": "풀의노력", - "blocksFoundLast24": "Blocks found in the last 24 hours", - "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + "network": "네트워크", + "pool": "풀", + "you": "you", + "statsUpdated": "업데이트", - "payments": "출금상황", - "paymentsHistory": "출금 내역", - "paymentsTotal": "전체 출금 수", - "paymentsMinimum": "최소 출금 수량", - "paymentsInterval": "출금 주기", - "paymentsDenomination": "최소 출금 단위", - "timeSent": "출금 시작", - "transactionHash": "트랜잭션 아이디", - "amount": "수량", - "fee": "수수료", - "mixin": "Mixin", - "payees": "출금대상수", - "pendingBalance": "적립 대기", - "totalPaid": "전체 출금", - "payoutEstimate": "Current Payout Estimate", - "paymentSummarySingle": "On %DATE% you have received %AMOUNT%", - "paymentSummaryMulti": "On %DATE% you have received %AMOUNT% in %COUNT% payments", + "poolHashrate": "풀의 해시레이트", + "currentEffort": "Current Effort", + "networkHashrate": "네크워크 해시레이트", + "networkDifficulty": "난이도", + "blockchainHeight": "블록체인 높이", + "networkLastReward": "최근 보상량", + "poolMiners": "연결된 마이너수", + "poolFee": "풀 수수료", - "connectionDetails": "연결정보", - "miningPoolHost": "풀 접속주소", - "cnAlgorithm": "채굴 알고리즘", - "username": "Username", - "usernameDesc": "This is your wallet address", - "paymentId": "거래소 Payment ID", - "fixedDiff": "고정 난이도 설정", - "address": "지갑주소", - "addrPaymentId": "paymentID", - "addrDiff": "난이도", - "password": "암호", - "passwordDesc": "워커 이름 설정", - "emailNotifications": "Email Notifications", - "miningPorts": "채굴 포트", - "port": "접속포트", - "portDiff": "시작 난이도", - "description": "설명", - "miningApps": "마이닝 프로그램", - "configGeneratorDesc": "Generate your custom configuration to mine on our pool", - "addressField": "지갑 주소", - "paymentIdField": "거래소용 Payment ID (optional)", - "fixedDiffField": "고정난이도 (optional)", - "workerNameField": "워커 이름", - "emailNotificationsField": "Email Notifications (optional)", - "generateConfig": "설정 예제 생성", - "appName": "프로그램 이름", - "appArch": "Architecture", - "appDesc": "Features", - "download": "Download", - "showConfig": "See more", + "minerStats": "마이너 상태와 지급상황", + "workerStats": "워커 상태", + "miner": "Miner", + "miners": "Miners", + "minersCount": "miners", + "workers": "Workers", + "workersCount": "workers", + "workerName": "Worker Name", + "lastHash": "Last Hash", + "hashRate": "Hash Rate", + "currentHashRate": "Current Hash Rate", + "lastShare": "Last Share Submitted", + "totalHashes": "Total Hashes Submitted", + "top10miners": "상위 10 채굴자", - "market": "시장가격 / 채굴량계산기", - "loadingMarket": "Loading market prices", - "priceIn": "Price in", - "hashPer": "Hash/", - "estimateProfit": "Estimate Mining Profits", - "enterYourHashrate": "Enter Your Hash Rate", - "perDay": "per day", + "blocksTotal": "블록 발견", + "blockSolvedTime": "Blocks Found Every", + "blocksMaturityCount": "적립되기전 블럭수량", + "efficiency": "Efficiency", + "averageLuck": "Average Luck", + "timeFound": "블럭 발견시작", + "reward": "보상", + "height": "블럭번호", + "difficulty": "난이도", + "blockHash": "블럭 해시", + "effort": "풀의노력", + "blocksFoundLast24": "Blocks found in the last 24 hours", + "blocksFoundLastDays": "Blocks found in the last {DAYS} days", - "verificationFields": "Verification fields", - "minerVerification": "In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.", - "minerAddress": "Miner Address", - "minerIP": "Miner IP address", - "setMinimumPayout": "Set your minimal payout level", - "minerMinPayout": "If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.", - "minimumPayout": "Minimum payout", - "enableEmailNotifications": "Enable email notifications", - "minerEmailNotify": "This pool will send out email notification when a block is found and whenever a payout happens.", - "emailAddress": "Email address", - "noMinerAddress": "No miner address specified", - "noMinerIP": "No miner IP address specified", - "noPayoutLevel": "No payout level specified", - "noEmail": "No email address specified", - "invalidEmail": "Invalid email address specified", - "minerPayoutSet": "Done! Your minimum payout level was set", - "notificationEnabled": "Done! Email notifications have been enabled", - "notificationDisabled": "Done! Email notifications have been disabled", + "payments": "출금상황", + "paymentsHistory": "출금 내역", + "paymentsTotal": "전체 출금 수", + "paymentsMinimum": "최소 출금 수량", + "paymentsInterval": "출금 주기", + "paymentsDenomination": "최소 출금 단위", + "timeSent": "출금 시작", + "transactionHash": "트랜잭션 아이디", + "amount": "수량", + "fee": "수수료", + "mixin": "Mixin", + "payees": "출금대상수", + "pendingBalance": "적립 대기", + "totalPaid": "전체 출금", + "payoutEstimate": "Current Payout Estimate", + "paymentSummarySingle": "On %DATE% you have received %AMOUNT%", + "paymentSummaryMulti": "On %DATE% you have received %AMOUNT% in %COUNT% payments", - "enterYourAddress": "Enter Your Address", - "enterYourMinerIP": "An IP address your miners use (any)", - "enterYourEmail": "Enter Your E-Mail Address (optional)", + "connectionDetails": "연결정보", + "miningPoolHost": "풀 접속주소", + "cnAlgorithm": "채굴 알고리즘", + "username": "Username", + "usernameDesc": "This is your wallet address", + "paymentId": "거래소 Payment ID", + "fixedDiff": "고정 난이도 설정", + "address": "지갑주소", + "addrPaymentId": "paymentID", + "addrDiff": "난이도", + "password": "암호", + "passwordDesc": "워커 이름 설정", + "emailNotifications": "Email Notifications", + "miningPorts": "채굴 포트", + "port": "접속포트", + "portDiff": "시작 난이도", + "description": "설명", + "miningApps": "마이닝 프로그램", + "configGeneratorDesc": "Generate your custom configuration to mine on our pool", + "addressField": "지갑 주소", + "paymentIdField": "거래소용 Payment ID (optional)", + "fixedDiffField": "고정난이도 (optional)", + "workerNameField": "워커 이름", + "emailNotificationsField": "Email Notifications (optional)", + "generateConfig": "설정 예제 생성", + "appName": "프로그램 이름", + "appArch": "Architecture", + "appDesc": "Features", + "download": "Download", + "showConfig": "See more", - "lookup": "Lookup", - "searching": "Searching...", - "loadMore": "Load more", - "set": "Set", - "enable": "Enable", - "disable": "Disable", - - "status": "Status", - "updated": "Updated:", - "source": "Source:", - "error": "Error:", + "market": "시장가격 / 채굴량계산기", + "loadingMarket": "Loading market prices", + "priceIn": "Price in", + "hashPer": "Hash/", + "estimateProfit": "Estimate Mining Profits", + "enterYourHashrate": "Enter Your Hash Rate", + "perDay": "per day", - "na": "N/A", - "estimated": "estimated", - "never": "Never", - "second": "second", - "seconds": "seconds", - "minute": "minute", - "minutes": "minutes", - "hour": "hour", - "hours": "hours", - "day": "day", - "days": "days", - "week": "week", - "weeks": "weeks", - "month": "month", - "months": "months", - "year": "year", - "years": "years", + "verificationFields": "Verification fields", + "minerVerification": "In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.", + "minerAddress": "Miner Address", + "minerIP": "Miner IP address", + "setMinimumPayout": "Set your minimal payout level", + "minerMinPayout": "If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.", + "minimumPayout": "Minimum payout", + "enableEmailNotifications": "Enable email notifications", + "minerEmailNotify": "This pool will send out email notification when a block is found and whenever a payout happens.", + "emailAddress": "Email address", + "noMinerAddress": "No miner address specified", + "noMinerIP": "No miner IP address specified", + "noPayoutLevel": "No payout level specified", + "noEmail": "No email address specified", + "invalidEmail": "Invalid email address specified", + "minerPayoutSet": "Done! Your minimum payout level was set", + "notificationEnabled": "Done! Email notifications have been enabled", + "notificationDisabled": "Done! Email notifications have been disabled", - "poweredBy": "Powered by", - "openSource": "open sourced under the" + "enterYourAddress": "Enter Your Address", + "enterYourMinerIP": "An IP address your miners use (any)", + "enterYourEmail": "Enter Your E-Mail Address (optional)", + + "lookup": "Lookup", + "searching": "Searching...", + "loadMore": "Load more", + "set": "Set", + "enable": "Enable", + "disable": "Disable", + + "status": "Status", + "updated": "Updated:", + "source": "Source:", + "error": "Error:", + + "na": "N/A", + "estimated": "estimated", + "never": "Never", + "second": "second", + "seconds": "seconds", + "minute": "minute", + "minutes": "minutes", + "hour": "hour", + "hours": "hours", + "day": "day", + "days": "days", + "week": "week", + "weeks": "weeks", + "month": "month", + "months": "months", + "year": "year", + "years": "years", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" } diff --git a/website_example/lang/languages.js b/website_example/lang/languages.js index b73a776a2..bb54cb1cb 100644 --- a/website_example/lang/languages.js +++ b/website_example/lang/languages.js @@ -1 +1,10 @@ -var langs = { 'en': 'English', 'es': 'Español', 'fr': 'Français', 'it': 'Italiano', 'ru': 'Русский', 'ca': 'Català', 'ko': '한국어', 'zh-CN': '简体中文' }; \ No newline at end of file +var langs = { + 'en': 'English', + 'es': 'Español', + 'fr': 'Français', + 'it': 'Italiano', + 'ru': 'Русский', + 'ca': 'Català', + 'ko': '한국어', + 'zh-CN': '简体中文' +}; diff --git a/website_example/lang/ru.json b/website_example/lang/ru.json index a3ce4b1a0..0ddb75da4 100644 --- a/website_example/lang/ru.json +++ b/website_example/lang/ru.json @@ -1,181 +1,182 @@ { - "miningPool": "Майнинг пул", - "dashboard": "Главная", - "gettingStarted": "Присоединиться", - "yourStats": "Статистика", - "poolBlocks": "Блоки пула", - "settings": "Настройки", - "faq": "FAQ", - "telegram": "Группа Telegram", - "discord": "Discord", - "contactUs": "Почта", - - "network": "Сеть", - "pool": "Пул", - "you": "Вы", - "statsUpdated": "Статистика обновлена", - - "poolHashrate": "Скорость пула", - "currentEffort": "Сложность раунда", - "networkHashrate": "Скорость сети", - "networkDifficulty": "Сложность", - "blockchainHeight": "№ последнего блока", - "networkLastReward": "Последнее вознаграждение", - "poolMiners": "Пользователи пула", - "poolFee": "Комиссия пула", - - "minerStats": "Ваша статистика и история платежей", - "workerStats": "Статистика ферм", - "miner": "Miner", - "miners": "Пользователи", - "minersCount": "miners", - "workers": "Фермы", - "workersCount": "фермы", - "workerName": "Имя фермы", - "lastHash": "Последний хеш", - "hashRate": "Скорость", - "currentHashRate": "Текущая скорость", - "lastShare": "Последняя шара принята", - "totalHashes": "Всего принято хешей", - "top10miners": "Лучшие 10 майнеров", - "blocksTotal": "Найдено блоков", - "blockSolvedTime": "Время нахождения блока", - "blocksMaturityCount": "Требование подтверждения", - "efficiency": "Эффективность", - "averageLuck": "Средняя удача", - "timeFound": "Время нахождения", - "reward": "Выплата", - "height": "№ блока", - "difficulty": "Сложность", - "blockHash": "Хеш блока", - "effort": "Усилие", - "blocksFoundLast24": "Blocks found in the last 24 hours", - "blocksFoundLastDays": "Blocks found in the last {DAYS} days", + "miningPool": "Майнинг пул", + "dashboard": "Главная", + "gettingStarted": "Присоединиться", + "yourStats": "Статистика", + "poolBlocks": "Блоки пула", + "settings": "Настройки", + "faq": "FAQ", + "telegram": "Группа Telegram", + "discord": "Discord", + "contactUs": "Почта", - "payments": "Платежи", - "paymentsHistory": "История платежей", - "paymentsTotal": "Всего платежей", - "paymentsMinimum": "Минимальный платёж", - "paymentsInterval": "Интервал платежей", - "paymentsDenomination": "Единица измерения", - "timeSent": "Время отправления", - "transactionHash": "Хеш транзакции", - "amount": "Сумма", - "fee": "Комиссия", - "mixin": "Mixin", - "payees": "Получателей", - "pendingBalance": "Ожидающий баланс", - "totalPaid": "Всего выплачено", - "payoutEstimate": "Текущая оценка выплат", + "network": "Сеть", + "pool": "Пул", + "you": "Вы", + "statsUpdated": "Статистика обновлена", - "connectionDetails": "Детали подключения", - "miningPoolHost": "Адрес майнинг пула", - "cnAlgorithm": "Алгоритм", - "username": "Имя пользователя", - "usernameDesc": "Это адрес вашего кошелька", - "paymentId": "Биржевой Payment ID", - "fixedDiff": "Фиксированная сложность", - "address": "address", - "addrPaymentId": "paymentID", - "addrDiff": "diff", - "password": "Пароль", - "passwordDesc": "Это имя вашей фермы в статистике", - "emailNotifications": "Уведомление по почте", - "miningPorts": "Порты для майнинга", - "port": "Порт", - "portDiff": "Стартовая сложность", - "description": "Описание", - "miningApps": "Программы для майнинга", - "configGeneratorDesc": "Создайте свою собственную конфигурацию для этого пула", - "addressField": "Адрес вашего кошелька", - "paymentIdField": "Payment ID для биржи (опция)", - "fixedDiffField": "Фиксированная сложность (опция)", - "workerNameField": "Имя_Фермы", - "emailNotificationsField": "Уведомление по почте (опция)", - "generateConfig": "Создать конфигурацию", - "appName": "Программа", - "appArch": "Архитектура", - "appDesc": "Особенности", - "download": "Скачать", - "showConfig": "Посмотреть", + "poolHashrate": "Скорость пула", + "currentEffort": "Сложность раунда", + "networkHashrate": "Скорость сети", + "networkDifficulty": "Сложность", + "blockchainHeight": "№ последнего блока", + "networkLastReward": "Последнее вознаграждение", + "poolMiners": "Пользователи пула", + "poolFee": "Комиссия пула", - "market": "Рынок / Калькулятор", - "loadingMarket": "Загрузка стоимости", - "priceIn": "Стоимость в", - "hashPer": "Стоимость хеша/", - "estimateProfit": "Рассчёт прибыли", - "enterYourHashrate": "Введите вашу скорость", - "perDay": "/в день", + "minerStats": "Ваша статистика и история платежей", + "workerStats": "Статистика ферм", + "miner": "Miner", + "miners": "Пользователи", + "minersCount": "miners", + "workers": "Фермы", + "workersCount": "фермы", + "workerName": "Имя фермы", + "lastHash": "Последний хеш", + "hashRate": "Скорость", + "currentHashRate": "Текущая скорость", + "lastShare": "Последняя шара принята", + "totalHashes": "Всего принято хешей", + "top10miners": "Лучшие 10 майнеров", + "blocksTotal": "Найдено блоков", + "blockSolvedTime": "Время нахождения блока", + "blocksMaturityCount": "Требование подтверждения", + "efficiency": "Эффективность", + "averageLuck": "Средняя удача", + "timeFound": "Время нахождения", + "reward": "Выплата", + "height": "№ блока", + "difficulty": "Сложность", + "blockHash": "Хеш блока", + "effort": "Усилие", + "blocksFoundLast24": "Блоки найдены за последние 24 часа", + "blocksFoundLastDays": "Блоки найдены за последние {DAYS} дней", + "propSoloConnectedMiners": "PROP / SOLO Подключенные шахтеры", - "verificationFields": "Проверочные данные", - "minerVerification": "Чтобы быть уверенным в том, что адрес кошелька принадлежит вам, мы просим вас указать один из IP-адресов, который используется вашими фермами.", - "minerAddress": "Ваш кошелёк", - "minerIP": "IP адрес фермы", - "setMinimumPayout": "Установите минимальный уровень выплат", - "minerMinPayout": "Вы можете установить минимальный порог оплаты, если предпочитаете более высокий уровень выплат, чем значение по умолчанию. Сумма, которую вы здесь укажете, станет минимальной суммой для платежей пула на ваш адрес.", - "minimumPayout": "Минимальная выплата", - "enableEmailNotifications": "Включить уведомление по почте", - "minerEmailNotify": "Пул отправит уведомление по электронной почте, когда будет найден блок или когда произведёт выплату.", - "emailAddress": "E-mail адрес", - "noMinerAddress": "Вы не ввели свой кошелёк", - "noMinerIP": "Вы не ввели IP адрес фермы", - "noPayoutLevel": "Вы не ввели уровень оплаты", - "noEmail": "Вы не ввели e-mail адрес", - "invalidEmail": "Не правильный e-mail адрес", - "minerPayoutSet": "Минимальный уровень оплаты успешно установлен !", - "notificationEnabled": "Уведомления по электронной почте успешно включены !", - "notificationDisabled": "Уведомления по электронной почте успешно выключены !", + "payments": "Платежи", + "paymentsHistory": "История платежей", + "paymentsTotal": "Всего платежей", + "paymentsMinimum": "Минимальный платёж", + "paymentsInterval": "Интервал платежей", + "paymentsDenomination": "Единица измерения", + "timeSent": "Время отправления", + "transactionHash": "Хеш транзакции", + "amount": "Сумма", + "fee": "Комиссия", + "mixin": "Mixin", + "payees": "Получателей", + "pendingBalance": "Ожидающий баланс", + "totalPaid": "Всего выплачено", + "payoutEstimate": "Текущая оценка выплат", - "enterYourAddress": "Введите адрес своего кошелька", - "enterYourMinerIP": "IP-адрес, который используют ваши фермы", - "enterYourEmail": "Введите свой E-Mail адрес (опция)", + "connectionDetails": "Детали подключения", + "miningPoolHost": "Адрес майнинг пула", + "cnAlgorithm": "Алгоритм", + "username": "Имя пользователя", + "usernameDesc": "Это адрес вашего кошелька", + "paymentId": "Биржевой Payment ID", + "fixedDiff": "Фиксированная сложность", + "address": "address", + "addrPaymentId": "paymentID", + "addrDiff": "diff", + "password": "Пароль", + "passwordDesc": "Это имя вашей фермы в статистике", + "emailNotifications": "Уведомление по почте", + "miningPorts": "Порты для майнинга", + "port": "Порт", + "portDiff": "Стартовая сложность", + "description": "Описание", + "miningApps": "Программы для майнинга", + "configGeneratorDesc": "Создайте свою собственную конфигурацию для этого пула", + "addressField": "Адрес вашего кошелька", + "paymentIdField": "Payment ID для биржи (опция)", + "fixedDiffField": "Фиксированная сложность (опция)", + "workerNameField": "Имя_Фермы", + "emailNotificationsField": "Уведомление по почте (опция)", + "generateConfig": "Создать конфигурацию", + "appName": "Программа", + "appArch": "Архитектура", + "appDesc": "Особенности", + "download": "Скачать", + "showConfig": "Посмотреть", - "lookup": "Посмотреть", - "searching": "Поиск ...", - "loadMore": "Загрузить ещё", - "set": "Установить", - "enable": "Включить", - "disable": "Выключить", - - "status": "Статус", - "updated": "Обновлено:", - "source": "Source:", - "error": "Ошибка:", + "market": "Рынок / Калькулятор", + "loadingMarket": "Загрузка стоимости", + "priceIn": "Стоимость в", + "hashPer": "Стоимость хеша/", + "estimateProfit": "Рассчёт прибыли", + "enterYourHashrate": "Введите вашу скорость", + "perDay": "/в день", - "na": "N/A", - "estimated": "примерно", - "never": "Не найден", - "second": "секунда", - "seconds": "секунд", - "minute": "минута", - "minutes": "минут", - "hour": "час", - "hours": "часов", - "day": "день", - "days": "дней", - "week": "неделя", - "weeks": "недель", - "month": "месяц", - "months": "месяцев", - "year": "год", - "years": "года", + "verificationFields": "Проверочные данные", + "minerVerification": "Чтобы быть уверенным в том, что адрес кошелька принадлежит вам, мы просим вас указать один из IP-адресов, который используется вашими фермами.", + "minerAddress": "Ваш кошелёк", + "minerIP": "IP адрес фермы", + "setMinimumPayout": "Установите минимальный уровень выплат", + "minerMinPayout": "Вы можете установить минимальный порог оплаты, если предпочитаете более высокий уровень выплат, чем значение по умолчанию. Сумма, которую вы здесь укажете, станет минимальной суммой для платежей пула на ваш адрес.", + "minimumPayout": "Минимальная выплата", + "enableEmailNotifications": "Включить уведомление по почте", + "minerEmailNotify": "Пул отправит уведомление по электронной почте, когда будет найден блок или когда произведёт выплату.", + "emailAddress": "E-mail адрес", + "noMinerAddress": "Вы не ввели свой кошелёк", + "noMinerIP": "Вы не ввели IP адрес фермы", + "noPayoutLevel": "Вы не ввели уровень оплаты", + "noEmail": "Вы не ввели e-mail адрес", + "invalidEmail": "Не правильный e-mail адрес", + "minerPayoutSet": "Минимальный уровень оплаты успешно установлен !", + "notificationEnabled": "Уведомления по электронной почте успешно включены !", + "notificationDisabled": "Уведомления по электронной почте успешно выключены !", - "timeagoPrefixAgo": null, - "timeagoPrefixFromNow": null, - "timeagoSuffixAgo": "назад", - "timeagoSuffixFromNow": "from now", - "timeagoSeconds": "меньше минуты", - "timeagoMinute": "около минуты", - "timeagoMinutes": "%d минут", - "timeagoHour": "about an hour", - "timeagoHours": "about %d hours", - "timeagoDay": " день", - "timeagoDays": "%d дней", - "timeagoMonth": "about месяц", - "timeagoMonths": "%d месяцев", - "timeagoYear": "about год", - "timeagoYears": "%d лет", - - "poweredBy": "Powered by", - "openSource": "open sourced under the" -} \ No newline at end of file + "enterYourAddress": "Введите адрес своего кошелька", + "enterYourMinerIP": "IP-адрес, который используют ваши фермы", + "enterYourEmail": "Введите свой E-Mail адрес (опция)", + + "lookup": "Посмотреть", + "searching": "Поиск ...", + "loadMore": "Загрузить ещё", + "set": "Установить", + "enable": "Включить", + "disable": "Выключить", + + "status": "Статус", + "updated": "Обновлено:", + "source": "Source:", + "error": "Ошибка:", + + "na": "N/A", + "estimated": "примерно", + "never": "Не найден", + "second": "секунда", + "seconds": "секунд", + "minute": "минута", + "minutes": "минут", + "hour": "час", + "hours": "часов", + "day": "день", + "days": "дней", + "week": "неделя", + "weeks": "недель", + "month": "месяц", + "months": "месяцев", + "year": "год", + "years": "года", + + "timeagoPrefixAgo": null, + "timeagoPrefixFromNow": null, + "timeagoSuffixAgo": "назад", + "timeagoSuffixFromNow": "from now", + "timeagoSeconds": "меньше минуты", + "timeagoMinute": "около минуты", + "timeagoMinutes": "%d минут", + "timeagoHour": "about an hour", + "timeagoHours": "about %d hours", + "timeagoDay": " день", + "timeagoDays": "%d дней", + "timeagoMonth": "about месяц", + "timeagoMonths": "%d месяцев", + "timeagoYear": "about год", + "timeagoYears": "%d лет", + + "poweredBy": "Powered by", + "openSource": "open sourced under the" +} diff --git a/website_example/lang/timeago/jquery.timeago.af.js b/website_example/lang/timeago/jquery.timeago.af.js index 817a7fa59..4b8ea4654 100644 --- a/website_example/lang/timeago/jquery.timeago.af.js +++ b/website_example/lang/timeago/jquery.timeago.af.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Afrikaans - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "gelede", - suffixFromNow: "van nou af", - seconds: "%d sekondes", - minute: "1 minuut", - minutes: "%d minute", - hour: "1 uur", - hours: "%d ure", - day: "1 dag", - days: "%d dae", - month: "1 maand", - months: "%d maande", - year: "1 jaar", - years: "%d jaar", - wordSeparator: " ", - numbers: [] - }; + // Afrikaans + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "gelede", + suffixFromNow: "van nou af", + seconds: "%d sekondes", + minute: "1 minuut", + minutes: "%d minute", + hour: "1 uur", + hours: "%d ure", + day: "1 dag", + days: "%d dae", + month: "1 maand", + months: "%d maande", + year: "1 jaar", + years: "%d jaar", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.am.js b/website_example/lang/timeago/jquery.timeago.am.js index 65502c39d..e0e6abf51 100644 --- a/website_example/lang/timeago/jquery.timeago.am.js +++ b/website_example/lang/timeago/jquery.timeago.am.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Amharic - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "በፊት", - suffixFromNow: "በኋላ", - seconds: "ከአንድ ደቂቃ በታች", - minute: "ከአንድ ደቂቃ ገደማ", - minutes: "ከ%d ደቂቃ", - hour: "ከአንድ ሰዓት ገደማ", - hours: "ከ%d ሰዓት ገደማ", - day: "ከአንድ ቀን", - days: "ከ%d ቀን", - month: "ከአንድ ወር ገደማ", - months: "ከ%d ወር", - year: "ከአንድ ዓመት ገደማ", - years: "ከ%d ዓመት", - wordSeparator: " ", - numbers: [] - }; + // Amharic + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "በፊት", + suffixFromNow: "በኋላ", + seconds: "ከአንድ ደቂቃ በታች", + minute: "ከአንድ ደቂቃ ገደማ", + minutes: "ከ%d ደቂቃ", + hour: "ከአንድ ሰዓት ገደማ", + hours: "ከ%d ሰዓት ገደማ", + day: "ከአንድ ቀን", + days: "ከ%d ቀን", + month: "ከአንድ ወር ገደማ", + months: "ከ%d ወር", + year: "ከአንድ ዓመት ገደማ", + years: "ከ%d ዓመት", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.ar.js b/website_example/lang/timeago/jquery.timeago.ar.js index 14cd18f23..dce606435 100644 --- a/website_example/lang/timeago/jquery.timeago.ar.js +++ b/website_example/lang/timeago/jquery.timeago.ar.js @@ -1,104 +1,127 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - function numpf(n, a) { - return a[plural=n===0 ? 0 : n===1 ? 1 : n===2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5]; - } - - jQuery.timeago.settings.strings = { - prefixAgo: "منذ", - prefixFromNow: "بعد", - suffixAgo: null, - suffixFromNow: null, // null OR "من الآن" - second: function(value) { return numpf(value, [ + function numpf (n, a) { + return a[plural = n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: "منذ", + prefixFromNow: "بعد", + suffixAgo: null, + suffixFromNow: null, // null OR "من الآن" + second: function (value) { + return numpf(value, [ 'أقل من ثانية', 'ثانية واحدة', 'ثانيتين', '%d ثوانٍ', '%d ثانية', - '%d ثانية']); }, - seconds: function(value) { return numpf(value, [ + '%d ثانية']); + }, + seconds: function (value) { + return numpf(value, [ 'أقل من ثانية', 'ثانية واحدة', 'ثانيتين', '%d ثوانٍ', '%d ثانية', - '%d ثانية']); }, - minute: function(value) { return numpf(value, [ + '%d ثانية']); + }, + minute: function (value) { + return numpf(value, [ 'أقل من دقيقة', 'دقيقة واحدة', 'دقيقتين', '%d دقائق', '%d دقيقة', - 'دقيقة']); }, - minutes: function(value) { return numpf(value, [ + 'دقيقة']); + }, + minutes: function (value) { + return numpf(value, [ 'أقل من دقيقة', 'دقيقة واحدة', 'دقيقتين', '%d دقائق', '%d دقيقة', - 'دقيقة']); }, - hour: function(value) { return numpf(value, [ + 'دقيقة']); + }, + hour: function (value) { + return numpf(value, [ 'أقل من ساعة', 'ساعة واحدة', 'ساعتين', '%d ساعات', '%d ساعة', - '%d ساعة']); }, - hours: function(value) { return numpf(value, [ + '%d ساعة']); + }, + hours: function (value) { + return numpf(value, [ 'أقل من ساعة', 'ساعة واحدة', 'ساعتين', '%d ساعات', '%d ساعة', - '%d ساعة']); }, - day: function(value) { return numpf(value, [ + '%d ساعة']); + }, + day: function (value) { + return numpf(value, [ 'أقل من يوم', 'يوم واحد', 'يومين', '%d أيام', '%d يومًا', - '%d يوم']); }, - days: function(value) { return numpf(value, [ + '%d يوم']); + }, + days: function (value) { + return numpf(value, [ 'أقل من يوم', 'يوم واحد', 'يومين', '%d أيام', '%d يومًا', - '%d يوم']); }, - month: function(value) { return numpf(value, [ + '%d يوم']); + }, + month: function (value) { + return numpf(value, [ 'أقل من شهر', 'شهر واحد', 'شهرين', '%d أشهر', '%d شهرًا', - '%d شهر']); }, - months: function(value) { return numpf(value, [ + '%d شهر']); + }, + months: function (value) { + return numpf(value, [ 'أقل من شهر', 'شهر واحد', 'شهرين', '%d أشهر', '%d شهرًا', - '%d شهر']); }, - year: function(value) { return numpf(value, [ + '%d شهر']); + }, + year: function (value) { + return numpf(value, [ 'أقل من عام', 'عام واحد', '%d عامين', '%d أعوام', '%d عامًا']); - }, - years: function(value) { return numpf(value, [ + }, + years: function (value) { + return numpf(value, [ 'أقل من عام', 'عام واحد', 'عامين', '%d أعوام', '%d عامًا', - '%d عام']);} - }; + '%d عام']); + } + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.az.js b/website_example/lang/timeago/jquery.timeago.az.js index 8332c41cf..b3908c772 100644 --- a/website_example/lang/timeago/jquery.timeago.az.js +++ b/website_example/lang/timeago/jquery.timeago.az.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Azerbaijani - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: 'əvvəl', - suffixFromNow: 'sonra', - seconds: 'saniyələr', - minute: '1 dəqiqə', - minutes: '%d dəqiqə', - hour: '1 saat', - hours: '%d saat', - day: '1 gün', - days: '%d gün', - month: '1 ay', - months: '%d ay', - year: '1 il', - years: '%d il', - wordSeparator: '', - numbers: [] - }; + // Azerbaijani + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: 'əvvəl', + suffixFromNow: 'sonra', + seconds: 'saniyələr', + minute: '1 dəqiqə', + minutes: '%d dəqiqə', + hour: '1 saat', + hours: '%d saat', + day: '1 gün', + days: '%d gün', + month: '1 ay', + months: '%d ay', + year: '1 il', + years: '%d il', + wordSeparator: '', + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.bg.js b/website_example/lang/timeago/jquery.timeago.bg.js index a3bd343af..886384fbb 100644 --- a/website_example/lang/timeago/jquery.timeago.bg.js +++ b/website_example/lang/timeago/jquery.timeago.bg.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Bulgarian - jQuery.timeago.settings.strings = { - prefixAgo: "преди", - prefixFromNow: "след", - suffixAgo: null, - suffixFromNow: null, - seconds: "по-малко от минута", - minute: "една минута", - minutes: "%d минути", - hour: "един час", - hours: "%d часа", - day: "един ден", - days: "%d дни", - month: "един месец", - months: "%d месеца", - year: "една година", - years: "%d години" - }; + // Bulgarian + jQuery.timeago.settings.strings = { + prefixAgo: "преди", + prefixFromNow: "след", + suffixAgo: null, + suffixFromNow: null, + seconds: "по-малко от минута", + minute: "една минута", + minutes: "%d минути", + hour: "един час", + hours: "%d часа", + day: "един ден", + days: "%d дни", + month: "един месец", + months: "%d месеца", + year: "една година", + years: "%d години" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.bs.js b/website_example/lang/timeago/jquery.timeago.bs.js index cbb178069..3a045498e 100644 --- a/website_example/lang/timeago/jquery.timeago.bs.js +++ b/website_example/lang/timeago/jquery.timeago.bs.js @@ -1,55 +1,55 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Bosnian - var numpf = function(n, f, s, t) { - var n10; - n10 = n % 10; - if (n10 === 1 && (n === 1 || n > 20)) { - return f; - } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { - return s; - } else { - return t; - } - }; + // Bosnian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; + + jQuery.timeago.settings.strings = { + prefixAgo: "prije", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekund", + seconds: function (value) { + return numpf(value, "%d sekund", "%d sekunde", "%d sekundi"); + }, + minute: "oko minut", + minutes: function (value) { + return numpf(value, "%d minut", "%d minute", "%d minuta"); + }, + hour: "oko sat", + hours: function (value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "oko jednog dana", + days: function (value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mjesec dana", + months: function (value) { + return numpf(value, "%d mjesec", "%d mjeseca", "%d mjeseci"); + }, + year: "prije godinu dana ", + years: function (value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; - jQuery.timeago.settings.strings = { - prefixAgo: "prije", - prefixFromNow: "za", - suffixAgo: null, - suffixFromNow: null, - second: "sekund", - seconds: function(value) { - return numpf(value, "%d sekund", "%d sekunde", "%d sekundi"); - }, - minute: "oko minut", - minutes: function(value) { - return numpf(value, "%d minut", "%d minute", "%d minuta"); - }, - hour: "oko sat", - hours: function(value) { - return numpf(value, "%d sat", "%d sata", "%d sati"); - }, - day: "oko jednog dana", - days: function(value) { - return numpf(value, "%d dan", "%d dana", "%d dana"); - }, - month: "mjesec dana", - months: function(value) { - return numpf(value, "%d mjesec", "%d mjeseca", "%d mjeseci"); - }, - year: "prije godinu dana ", - years: function(value) { - return numpf(value, "%d godinu", "%d godine", "%d godina"); - }, - wordSeparator: " " - }; - })); diff --git a/website_example/lang/timeago/jquery.timeago.ca.js b/website_example/lang/timeago/jquery.timeago.ca.js index e4cb5cab7..65e914d5f 100644 --- a/website_example/lang/timeago/jquery.timeago.ca.js +++ b/website_example/lang/timeago/jquery.timeago.ca.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Catalan - jQuery.timeago.settings.strings = { - prefixAgo: "fa", - prefixFromNow: "d'aquí", - suffixAgo: null, - suffixFromNow: null, - seconds: "menys d'un minut", - minute: "un minut", - minutes: "%d minuts", - hour: "una hora", - hours: "%d hores", - day: "un dia", - days: "%d dies", - month: "un mes", - months: "%d mesos", - year: "un any", - years: "%d anys", - wordSeparator: " ", - numbers: [] - }; + // Catalan + jQuery.timeago.settings.strings = { + prefixAgo: "fa", + prefixFromNow: "d'aquí", + suffixAgo: null, + suffixFromNow: null, + seconds: "menys d'un minut", + minute: "un minut", + minutes: "%d minuts", + hour: "una hora", + hours: "%d hores", + day: "un dia", + days: "%d dies", + month: "un mes", + months: "%d mesos", + year: "un any", + years: "%d anys", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.cs.js b/website_example/lang/timeago/jquery.timeago.cs.js index b940f6949..db29ad634 100644 --- a/website_example/lang/timeago/jquery.timeago.cs.js +++ b/website_example/lang/timeago/jquery.timeago.cs.js @@ -1,34 +1,56 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Czech - (function() { - function f(n, d, a) { - return a[d>=0 ? 0 : a.length===2 || n<5 ? 1 : 2]; - } - - jQuery.timeago.settings.strings = { - prefixAgo: 'před', - prefixFromNow: 'za', - suffixAgo: null, - suffixFromNow: null, - seconds: function(n, d) {return f(n, d, ['méně než minutou', 'méně než minutu']);}, - minute: function(n, d) {return f(n, d, ['minutou', 'minutu']);}, - minutes: function(n, d) {return f(n, d, ['%d minutami', '%d minuty', '%d minut']);}, - hour: function(n, d) {return f(n, d, ['hodinou', 'hodinu']);}, - hours: function(n, d) {return f(n, d, ['%d hodinami', '%d hodiny', '%d hodin']);}, - day: function(n, d) {return f(n, d, ['%d dnem', '%d den']);}, - days: function(n, d) {return f(n, d, ['%d dny', '%d dny', '%d dní']);}, - month: function(n, d) {return f(n, d, ['%d měsícem', '%d měsíc']);}, - months: function(n, d) {return f(n, d, ['%d měsíci', '%d měsíce', '%d měsíců']);}, - year: function(n, d) {return f(n, d, ['%d rokem', '%d rok']);}, - years: function(n, d) {return f(n, d, ['%d lety', '%d roky', '%d let']);} - }; - })(); + // Czech + (function () { + function f (n, d, a) { + return a[d >= 0 ? 0 : a.length === 2 || n < 5 ? 1 : 2]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: 'před', + prefixFromNow: 'za', + suffixAgo: null, + suffixFromNow: null, + seconds: function (n, d) { + return f(n, d, ['méně než minutou', 'méně než minutu']); + }, + minute: function (n, d) { + return f(n, d, ['minutou', 'minutu']); + }, + minutes: function (n, d) { + return f(n, d, ['%d minutami', '%d minuty', '%d minut']); + }, + hour: function (n, d) { + return f(n, d, ['hodinou', 'hodinu']); + }, + hours: function (n, d) { + return f(n, d, ['%d hodinami', '%d hodiny', '%d hodin']); + }, + day: function (n, d) { + return f(n, d, ['%d dnem', '%d den']); + }, + days: function (n, d) { + return f(n, d, ['%d dny', '%d dny', '%d dní']); + }, + month: function (n, d) { + return f(n, d, ['%d měsícem', '%d měsíc']); + }, + months: function (n, d) { + return f(n, d, ['%d měsíci', '%d měsíce', '%d měsíců']); + }, + year: function (n, d) { + return f(n, d, ['%d rokem', '%d rok']); + }, + years: function (n, d) { + return f(n, d, ['%d lety', '%d roky', '%d let']); + } + }; + })(); })); diff --git a/website_example/lang/timeago/jquery.timeago.cy.js b/website_example/lang/timeago/jquery.timeago.cy.js index 4c514a8df..67a7a5ec3 100644 --- a/website_example/lang/timeago/jquery.timeago.cy.js +++ b/website_example/lang/timeago/jquery.timeago.cy.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Welsh - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "yn ôl", - suffixFromNow: "o hyn", - seconds: "llai na munud", - minute: "am funud", - minutes: "%d munud", - hour: "tua awr", - hours: "am %d awr", - day: "y dydd", - days: "%d diwrnod", - month: "tua mis", - months: "%d mis", - year: "am y flwyddyn", - years: "%d blynedd", - wordSeparator: " ", - numbers: [] - }; + // Welsh + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "yn ôl", + suffixFromNow: "o hyn", + seconds: "llai na munud", + minute: "am funud", + minutes: "%d munud", + hour: "tua awr", + hours: "am %d awr", + day: "y dydd", + days: "%d diwrnod", + month: "tua mis", + months: "%d mis", + year: "am y flwyddyn", + years: "%d blynedd", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.da.js b/website_example/lang/timeago/jquery.timeago.da.js index 236c34c44..eea75ac4a 100644 --- a/website_example/lang/timeago/jquery.timeago.da.js +++ b/website_example/lang/timeago/jquery.timeago.da.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Danish - jQuery.timeago.settings.strings = { - prefixAgo: "for", - prefixFromNow: "om", - suffixAgo: "siden", - suffixFromNow: "", - seconds: "mindre end et minut", - minute: "ca. et minut", - minutes: "%d minutter", - hour: "ca. en time", - hours: "ca. %d timer", - day: "en dag", - days: "%d dage", - month: "ca. en måned", - months: "%d måneder", - year: "ca. et år", - years: "%d år" - }; + // Danish + jQuery.timeago.settings.strings = { + prefixAgo: "for", + prefixFromNow: "om", + suffixAgo: "siden", + suffixFromNow: "", + seconds: "mindre end et minut", + minute: "ca. et minut", + minutes: "%d minutter", + hour: "ca. en time", + hours: "ca. %d timer", + day: "en dag", + days: "%d dage", + month: "ca. en måned", + months: "%d måneder", + year: "ca. et år", + years: "%d år" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.de.js b/website_example/lang/timeago/jquery.timeago.de.js index 6a877a231..6f3f57c81 100644 --- a/website_example/lang/timeago/jquery.timeago.de.js +++ b/website_example/lang/timeago/jquery.timeago.de.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // German - jQuery.timeago.settings.strings = { - prefixAgo: "vor", - prefixFromNow: "in", - suffixAgo: "", - suffixFromNow: "", - seconds: "wenigen Sekunden", - minute: "etwa einer Minute", - minutes: "%d Minuten", - hour: "etwa einer Stunde", - hours: "%d Stunden", - day: "etwa einem Tag", - days: "%d Tagen", - month: "etwa einem Monat", - months: "%d Monaten", - year: "etwa einem Jahr", - years: "%d Jahren" - }; + // German + jQuery.timeago.settings.strings = { + prefixAgo: "vor", + prefixFromNow: "in", + suffixAgo: "", + suffixFromNow: "", + seconds: "wenigen Sekunden", + minute: "etwa einer Minute", + minutes: "%d Minuten", + hour: "etwa einer Stunde", + hours: "%d Stunden", + day: "etwa einem Tag", + days: "%d Tagen", + month: "etwa einem Monat", + months: "%d Monaten", + year: "etwa einem Jahr", + years: "%d Jahren" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.dv.js b/website_example/lang/timeago/jquery.timeago.dv.js index 0d70a493c..1158b35b1 100644 --- a/website_example/lang/timeago/jquery.timeago.dv.js +++ b/website_example/lang/timeago/jquery.timeago.dv.js @@ -1,32 +1,32 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - /** - * Dhivehi time in Thaana for timeago.js - **/ - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "ކުރިން", - suffixFromNow: "ފަހުން", - seconds: "ސިކުންތުކޮޅެއް", - minute: "މިނިޓެއްވަރު", - minutes: "%d މިނިޓު", - hour: "ގަޑިއެއްވަރު", - hours: "ގާތްގަނޑަކަށް %d ގަޑިއިރު", - day: "އެއް ދުވަސް", - days: "މީގެ %d ދުވަސް", - month: "މަހެއްވަރު", - months: "މީގެ %d މަސް", - year: "އަހަރެއްވަރު", - years: "މީގެ %d އަހަރު", - wordSeparator: " ", - numbers: [] - }; + /** + * Dhivehi time in Thaana for timeago.js + **/ + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ކުރިން", + suffixFromNow: "ފަހުން", + seconds: "ސިކުންތުކޮޅެއް", + minute: "މިނިޓެއްވަރު", + minutes: "%d މިނިޓު", + hour: "ގަޑިއެއްވަރު", + hours: "ގާތްގަނޑަކަށް %d ގަޑިއިރު", + day: "އެއް ދުވަސް", + days: "މީގެ %d ދުވަސް", + month: "މަހެއްވަރު", + months: "މީގެ %d މަސް", + year: "އަހަރެއްވަރު", + years: "މީގެ %d އަހަރު", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.el.js b/website_example/lang/timeago/jquery.timeago.el.js index 2db9ebea8..e9edabc6a 100644 --- a/website_example/lang/timeago/jquery.timeago.el.js +++ b/website_example/lang/timeago/jquery.timeago.el.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Greek - jQuery.timeago.settings.strings = { - prefixAgo: "πριν", - prefixFromNow: "σε", - suffixAgo: "", - suffixFromNow: "", - seconds: "λιγότερο από ένα λεπτό", - minute: "περίπου ένα λεπτό", - minutes: "%d λεπτά", - hour: "περίπου μία ώρα", - hours: "περίπου %d ώρες", - day: "μία μέρα", - days: "%d μέρες", - month: "περίπου ένα μήνα", - months: "%d μήνες", - year: "περίπου ένα χρόνο", - years: "%d χρόνια" - }; + // Greek + jQuery.timeago.settings.strings = { + prefixAgo: "πριν", + prefixFromNow: "σε", + suffixAgo: "", + suffixFromNow: "", + seconds: "λιγότερο από ένα λεπτό", + minute: "περίπου ένα λεπτό", + minutes: "%d λεπτά", + hour: "περίπου μία ώρα", + hours: "περίπου %d ώρες", + day: "μία μέρα", + days: "%d μέρες", + month: "περίπου ένα μήνα", + months: "%d μήνες", + year: "περίπου ένα χρόνο", + years: "%d χρόνια" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.en.js b/website_example/lang/timeago/jquery.timeago.en.js index 8ca50afff..2bb2d6e2d 100644 --- a/website_example/lang/timeago/jquery.timeago.en.js +++ b/website_example/lang/timeago/jquery.timeago.en.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // English (Template) - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "ago", - suffixFromNow: "from now", - seconds: "less than a minute", - minute: "about a minute", - minutes: "%d minutes", - hour: "about an hour", - hours: "about %d hours", - day: "a day", - days: "%d days", - month: "about a month", - months: "%d months", - year: "about a year", - years: "%d years", - wordSeparator: " ", - numbers: [] - }; + // English (Template) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ago", + suffixFromNow: "from now", + seconds: "less than a minute", + minute: "about a minute", + minutes: "%d minutes", + hour: "about an hour", + hours: "about %d hours", + day: "a day", + days: "%d days", + month: "about a month", + months: "%d months", + year: "about a year", + years: "%d years", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.es.js b/website_example/lang/timeago/jquery.timeago.es.js index 0785b3f42..b78c9ab81 100644 --- a/website_example/lang/timeago/jquery.timeago.es.js +++ b/website_example/lang/timeago/jquery.timeago.es.js @@ -1,29 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Spanish - jQuery.timeago.settings.strings = { - prefixAgo: "hace", - prefixFromNow: "dentro de", - suffixAgo: "", - suffixFromNow: "", - seconds: "menos de un minuto", - minute: "un minuto", - minutes: "unos %d minutos", - hour: "una hora", - hours: "%d horas", - day: "un día", - days: "%d días", - month: "un mes", - months: "%d meses", - year: "un año", - years: "%d años" - }; + // Spanish + jQuery.timeago.settings.strings = { + prefixAgo: "hace", + prefixFromNow: "dentro de", + suffixAgo: "", + suffixFromNow: "", + seconds: "menos de un minuto", + minute: "un minuto", + minutes: "unos %d minutos", + hour: "una hora", + hours: "%d horas", + day: "un día", + days: "%d días", + month: "un mes", + months: "%d meses", + year: "un año", + years: "%d años" + }; })); - diff --git a/website_example/lang/timeago/jquery.timeago.et.js b/website_example/lang/timeago/jquery.timeago.et.js index ac2461ec7..9ffd9095f 100644 --- a/website_example/lang/timeago/jquery.timeago.et.js +++ b/website_example/lang/timeago/jquery.timeago.et.js @@ -1,28 +1,48 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Estonian - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "tagasi", - suffixFromNow: "pärast", - seconds: function(n, d) { return d < 0 ? "vähem kui minuti aja" : "vähem kui minut aega"; }, - minute: function(n, d) { return d < 0 ? "umbes minuti aja" : "umbes minut aega"; }, - minutes: function(n, d) { return d < 0 ? "%d minuti" : "%d minutit"; }, - hour: function(n, d) { return d < 0 ? "umbes tunni aja" : "umbes tund aega"; }, - hours: function(n, d) { return d < 0 ? "%d tunni" : "%d tundi"; }, - day: function(n, d) { return d < 0 ? "umbes päeva" : "umbes päev"; }, - days: "%d päeva", - month: function(n, d) { return d < 0 ? "umbes kuu aja" : "umbes kuu aega"; }, - months: function(n, d) { return d < 0 ? "%d kuu" : "%d kuud"; }, - year: function(n, d) { return d < 0 ? "umbes aasta aja" : "umbes aasta aega"; }, - years: function(n, d) { return d < 0 ? "%d aasta" : "%d aastat"; } - }; + // Estonian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "tagasi", + suffixFromNow: "pärast", + seconds: function (n, d) { + return d < 0 ? "vähem kui minuti aja" : "vähem kui minut aega"; + }, + minute: function (n, d) { + return d < 0 ? "umbes minuti aja" : "umbes minut aega"; + }, + minutes: function (n, d) { + return d < 0 ? "%d minuti" : "%d minutit"; + }, + hour: function (n, d) { + return d < 0 ? "umbes tunni aja" : "umbes tund aega"; + }, + hours: function (n, d) { + return d < 0 ? "%d tunni" : "%d tundi"; + }, + day: function (n, d) { + return d < 0 ? "umbes päeva" : "umbes päev"; + }, + days: "%d päeva", + month: function (n, d) { + return d < 0 ? "umbes kuu aja" : "umbes kuu aega"; + }, + months: function (n, d) { + return d < 0 ? "%d kuu" : "%d kuud"; + }, + year: function (n, d) { + return d < 0 ? "umbes aasta aja" : "umbes aasta aega"; + }, + years: function (n, d) { + return d < 0 ? "%d aasta" : "%d aastat"; + } + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.eu.js b/website_example/lang/timeago/jquery.timeago.eu.js index 5c2c32c7e..de63b09f1 100644 --- a/website_example/lang/timeago/jquery.timeago.eu.js +++ b/website_example/lang/timeago/jquery.timeago.eu.js @@ -1,28 +1,27 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - jQuery.timeago.settings.strings = { - prefixAgo: "duela", - prefixFromNow: "hemendik", - suffixAgo: "", - suffixFromNow: "barru", - seconds: "minutu bat bainu gutxiago", - minute: "minutu bat", - minutes: "%d minutu inguru", - hour: "ordu bat", - hours: "%d ordu", - day: "egun bat", - days: "%d egun", - month: "hilabete bat", - months: "%d hilabete", - year: "urte bat", - years: "%d urte" - }; + jQuery.timeago.settings.strings = { + prefixAgo: "duela", + prefixFromNow: "hemendik", + suffixAgo: "", + suffixFromNow: "barru", + seconds: "minutu bat bainu gutxiago", + minute: "minutu bat", + minutes: "%d minutu inguru", + hour: "ordu bat", + hours: "%d ordu", + day: "egun bat", + days: "%d egun", + month: "hilabete bat", + months: "%d hilabete", + year: "urte bat", + years: "%d urte" + }; })); - diff --git a/website_example/lang/timeago/jquery.timeago.fa.js b/website_example/lang/timeago/jquery.timeago.fa.js index ec8ccb952..41fd81e17 100644 --- a/website_example/lang/timeago/jquery.timeago.fa.js +++ b/website_example/lang/timeago/jquery.timeago.fa.js @@ -1,32 +1,32 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Persian - // Use DIR attribute for RTL text in Persian Language for ABBR tag . - // By MB.seifollahi@gmail.com - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "پیش", - suffixFromNow: "از حال", - seconds: "کمتر از یک دقیقه", - minute: "حدود یک دقیقه", - minutes: "%d دقیقه", - hour: "حدود یک ساعت", - hours: "حدود %d ساعت", - day: "یک روز", - days: "%d روز", - month: "حدود یک ماه", - months: "%d ماه", - year: "حدود یک سال", - years: "%d سال", - wordSeparator: " ", - numbers: ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] - }; + // Persian + // Use DIR attribute for RTL text in Persian Language for ABBR tag . + // By MB.seifollahi@gmail.com + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "پیش", + suffixFromNow: "از حال", + seconds: "کمتر از یک دقیقه", + minute: "حدود یک دقیقه", + minutes: "%d دقیقه", + hour: "حدود یک ساعت", + hours: "حدود %d ساعت", + day: "یک روز", + days: "%d روز", + month: "حدود یک ماه", + months: "%d ماه", + year: "حدود یک سال", + years: "%d سال", + wordSeparator: " ", + numbers: ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.fi.js b/website_example/lang/timeago/jquery.timeago.fi.js index b5f7e696d..153a3710c 100644 --- a/website_example/lang/timeago/jquery.timeago.fi.js +++ b/website_example/lang/timeago/jquery.timeago.fi.js @@ -1,38 +1,38 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Finnish - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "sitten", - suffixFromNow: "tulevaisuudessa", - seconds: "alle minuutti", - minute: "minuutti", - minutes: "%d minuuttia", - hour: "tunti", - hours: "%d tuntia", - day: "päivä", - days: "%d päivää", - month: "kuukausi", - months: "%d kuukautta", - year: "vuosi", - years: "%d vuotta" - }; - - // The above is not a great localization because one would usually - // write "2 days ago" in Finnish as "2 päivää sitten", however - // one would write "2 days into the future" as "2:n päivän päästä" - // which cannot be achieved with localization support this simple. - // This is because Finnish has word suffixes (attached directly - // to the end of the word). The word "day" is "päivä" in Finnish. - // As workaround, the above localizations will say - // "2 päivää tulevaisuudessa" which is understandable but - // not as fluent. + // Finnish + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "sitten", + suffixFromNow: "tulevaisuudessa", + seconds: "alle minuutti", + minute: "minuutti", + minutes: "%d minuuttia", + hour: "tunti", + hours: "%d tuntia", + day: "päivä", + days: "%d päivää", + month: "kuukausi", + months: "%d kuukautta", + year: "vuosi", + years: "%d vuotta" + }; + + // The above is not a great localization because one would usually + // write "2 days ago" in Finnish as "2 päivää sitten", however + // one would write "2 days into the future" as "2:n päivän päästä" + // which cannot be achieved with localization support this simple. + // This is because Finnish has word suffixes (attached directly + // to the end of the word). The word "day" is "päivä" in Finnish. + // As workaround, the above localizations will say + // "2 päivää tulevaisuudessa" which is understandable but + // not as fluent. })); diff --git a/website_example/lang/timeago/jquery.timeago.fr.js b/website_example/lang/timeago/jquery.timeago.fr.js index 1bb052aa1..97e264270 100644 --- a/website_example/lang/timeago/jquery.timeago.fr.js +++ b/website_example/lang/timeago/jquery.timeago.fr.js @@ -1,27 +1,27 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // French - jQuery.timeago.settings.strings = { - // environ ~= about, it's optional - prefixAgo: "il y a", - prefixFromNow: "d'ici", - seconds: "moins d'une minute", - minute: "environ une minute", - minutes: "environ %d minutes", - hour: "environ une heure", - hours: "environ %d heures", - day: "environ un jour", - days: "environ %d jours", - month: "environ un mois", - months: "environ %d mois", - year: "un an", - years: "%d ans" - }; + // French + jQuery.timeago.settings.strings = { + // environ ~= about, it's optional + prefixAgo: "il y a", + prefixFromNow: "d'ici", + seconds: "moins d'une minute", + minute: "environ une minute", + minutes: "environ %d minutes", + hour: "environ une heure", + hours: "environ %d heures", + day: "environ un jour", + days: "environ %d jours", + month: "environ un mois", + months: "environ %d mois", + year: "un an", + years: "%d ans" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.gl.js b/website_example/lang/timeago/jquery.timeago.gl.js index 277bbf70f..eb1785481 100644 --- a/website_example/lang/timeago/jquery.timeago.gl.js +++ b/website_example/lang/timeago/jquery.timeago.gl.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Galician - jQuery.timeago.settings.strings = { - prefixAgo: "hai", - prefixFromNow: "dentro de", - suffixAgo: "", - suffixFromNow: "", - seconds: "menos dun minuto", - minute: "un minuto", - minutes: "uns %d minutos", - hour: "unha hora", - hours: "%d horas", - day: "un día", - days: "%d días", - month: "un mes", - months: "%d meses", - year: "un ano", - years: "%d anos" - }; + // Galician + jQuery.timeago.settings.strings = { + prefixAgo: "hai", + prefixFromNow: "dentro de", + suffixAgo: "", + suffixFromNow: "", + seconds: "menos dun minuto", + minute: "un minuto", + minutes: "uns %d minutos", + hour: "unha hora", + hours: "%d horas", + day: "un día", + days: "%d días", + month: "un mes", + months: "%d meses", + year: "un ano", + years: "%d anos" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.he.js b/website_example/lang/timeago/jquery.timeago.he.js index 2cd31ab60..c6584f337 100644 --- a/website_example/lang/timeago/jquery.timeago.he.js +++ b/website_example/lang/timeago/jquery.timeago.he.js @@ -1,26 +1,34 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Hebrew - jQuery.timeago.settings.strings = { - prefixAgo: "לפני", - prefixFromNow: "עוד", - seconds: "פחות מדקה", - minute: "דקה", - minutes: "%d דקות", - hour: "שעה", - hours: function(number){return (number===2) ? "שעתיים" : "%d שעות";}, - day: "יום", - days: function(number){return (number===2) ? "יומיים" : "%d ימים";}, - month: "חודש", - months: function(number){return (number===2) ? "חודשיים" : "%d חודשים";}, - year: "שנה", - years: function(number){return (number===2) ? "שנתיים" : "%d שנים";} - }; + // Hebrew + jQuery.timeago.settings.strings = { + prefixAgo: "לפני", + prefixFromNow: "עוד", + seconds: "פחות מדקה", + minute: "דקה", + minutes: "%d דקות", + hour: "שעה", + hours: function (number) { + return (number === 2) ? "שעתיים" : "%d שעות"; + }, + day: "יום", + days: function (number) { + return (number === 2) ? "יומיים" : "%d ימים"; + }, + month: "חודש", + months: function (number) { + return (number === 2) ? "חודשיים" : "%d חודשים"; + }, + year: "שנה", + years: function (number) { + return (number === 2) ? "שנתיים" : "%d שנים"; + } + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.hr.js b/website_example/lang/timeago/jquery.timeago.hr.js index bd142979a..6566d5a45 100644 --- a/website_example/lang/timeago/jquery.timeago.hr.js +++ b/website_example/lang/timeago/jquery.timeago.hr.js @@ -1,54 +1,54 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Croatian - var numpf = function (n, f, s, t) { - var n10; - n10 = n % 10; - if (n10 === 1 && (n === 1 || n > 20)) { - return f; - } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { - return s; - } else { - return t; - } - }; + // Croatian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; - jQuery.timeago.settings.strings = { - prefixAgo: "prije", - prefixFromNow: "za", - suffixAgo: null, - suffixFromNow: null, - second: "sekundu", - seconds: function (value) { - return numpf(value, "%d sekundu", "%d sekunde", "%d sekundi"); - }, - minute: "oko minutu", - minutes: function (value) { - return numpf(value, "%d minutu", "%d minute", "%d minuta"); - }, - hour: "oko jedan sat", - hours: function (value) { - return numpf(value, "%d sat", "%d sata", "%d sati"); - }, - day: "jedan dan", - days: function (value) { - return numpf(value, "%d dan", "%d dana", "%d dana"); - }, - month: "mjesec dana", - months: function (value) { - return numpf(value, "%d mjesec", "%d mjeseca", "%d mjeseci"); - }, - year: "prije godinu dana", - years: function (value) { - return numpf(value, "%d godinu", "%d godine", "%d godina"); - }, - wordSeparator: " " - }; + jQuery.timeago.settings.strings = { + prefixAgo: "prije", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekundu", + seconds: function (value) { + return numpf(value, "%d sekundu", "%d sekunde", "%d sekundi"); + }, + minute: "oko minutu", + minutes: function (value) { + return numpf(value, "%d minutu", "%d minute", "%d minuta"); + }, + hour: "oko jedan sat", + hours: function (value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "jedan dan", + days: function (value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mjesec dana", + months: function (value) { + return numpf(value, "%d mjesec", "%d mjeseca", "%d mjeseci"); + }, + year: "prije godinu dana", + years: function (value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.hu.js b/website_example/lang/timeago/jquery.timeago.hu.js index 0009de9e2..182082a04 100644 --- a/website_example/lang/timeago/jquery.timeago.hu.js +++ b/website_example/lang/timeago/jquery.timeago.hu.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Hungarian - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: null, - suffixFromNow: null, - seconds: "kevesebb mint egy perce", - minute: "körülbelül egy perce", - minutes: "%d perce", - hour: "körülbelül egy órája", - hours: "körülbelül %d órája", - day: "körülbelül egy napja", - days: "%d napja", - month: "körülbelül egy hónapja", - months: "%d hónapja", - year: "körülbelül egy éve", - years: "%d éve" - }; + // Hungarian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: null, + seconds: "kevesebb mint egy perce", + minute: "körülbelül egy perce", + minutes: "%d perce", + hour: "körülbelül egy órája", + hours: "körülbelül %d órája", + day: "körülbelül egy napja", + days: "%d napja", + month: "körülbelül egy hónapja", + months: "%d hónapja", + year: "körülbelül egy éve", + years: "%d éve" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.hy.js b/website_example/lang/timeago/jquery.timeago.hy.js index 3f0de6e7d..2367af709 100644 --- a/website_example/lang/timeago/jquery.timeago.hy.js +++ b/website_example/lang/timeago/jquery.timeago.hy.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Armenian - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "առաջ", - suffixFromNow: "հետո", - seconds: "վայրկյաններ", - minute: "մեկ րոպե", - minutes: "%d րոպե", - hour: "մեկ ժամ", - hours: "%d ժամ", - day: "մեկ օր", - days: "%d օր", - month: "մեկ ամիս", - months: "%d ամիս", - year: "մեկ տարի", - years: "%d տարի" - }; + // Armenian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "առաջ", + suffixFromNow: "հետո", + seconds: "վայրկյաններ", + minute: "մեկ րոպե", + minutes: "%d րոպե", + hour: "մեկ ժամ", + hours: "%d ժամ", + day: "մեկ օր", + days: "%d օր", + month: "մեկ ամիս", + months: "%d ամիս", + year: "մեկ տարի", + years: "%d տարի" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.id.js b/website_example/lang/timeago/jquery.timeago.id.js index ca530ccf8..4a83497ee 100644 --- a/website_example/lang/timeago/jquery.timeago.id.js +++ b/website_example/lang/timeago/jquery.timeago.id.js @@ -1,29 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Indonesian - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "yang lalu", - suffixFromNow: "dari sekarang", - seconds: "kurang dari semenit", - minute: "sekitar satu menit", - minutes: "%d menit", - hour: "sekitar sejam", - hours: "sekitar %d jam", - day: "sehari", - days: "%d hari", - month: "sekitar sebulan", - months: "%d bulan", - year: "sekitar setahun", - years: "%d tahun" - }; + // Indonesian + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "yang lalu", + suffixFromNow: "dari sekarang", + seconds: "kurang dari semenit", + minute: "sekitar satu menit", + minutes: "%d menit", + hour: "sekitar sejam", + hours: "sekitar %d jam", + day: "sehari", + days: "%d hari", + month: "sekitar sebulan", + months: "%d bulan", + year: "sekitar setahun", + years: "%d tahun" + }; })); - diff --git a/website_example/lang/timeago/jquery.timeago.is.js b/website_example/lang/timeago/jquery.timeago.is.js index e3d4b1fd1..62327e264 100644 --- a/website_example/lang/timeago/jquery.timeago.is.js +++ b/website_example/lang/timeago/jquery.timeago.is.js @@ -1,29 +1,29 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - jQuery.timeago.settings.strings = { - prefixAgo: "fyrir", - prefixFromNow: "eftir", - suffixAgo: "síðan", - suffixFromNow: null, - seconds: "minna en mínútu", - minute: "mínútu", - minutes: "%d mínútum", - hour: "klukkutíma", - hours: "um %d klukkutímum", - day: "degi", - days: "%d dögum", - month: "mánuði", - months: "%d mánuðum", - year: "ári", - years: "%d árum", - wordSeparator: " ", - numbers: [] - }; + jQuery.timeago.settings.strings = { + prefixAgo: "fyrir", + prefixFromNow: "eftir", + suffixAgo: "síðan", + suffixFromNow: null, + seconds: "minna en mínútu", + minute: "mínútu", + minutes: "%d mínútum", + hour: "klukkutíma", + hours: "um %d klukkutímum", + day: "degi", + days: "%d dögum", + month: "mánuði", + months: "%d mánuðum", + year: "ári", + years: "%d árum", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.it.js b/website_example/lang/timeago/jquery.timeago.it.js index e1cac8431..a01e97f87 100644 --- a/website_example/lang/timeago/jquery.timeago.it.js +++ b/website_example/lang/timeago/jquery.timeago.it.js @@ -1,26 +1,26 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Italian - jQuery.timeago.settings.strings = { - suffixAgo: "fa", - suffixFromNow: "da ora", - seconds: "meno di un minuto", - minute: "circa un minuto", - minutes: "%d minuti", - hour: "circa un'ora", - hours: "circa %d ore", - day: "un giorno", - days: "%d giorni", - month: "circa un mese", - months: "%d mesi", - year: "circa un anno", - years: "%d anni" - }; + // Italian + jQuery.timeago.settings.strings = { + suffixAgo: "fa", + suffixFromNow: "da ora", + seconds: "meno di un minuto", + minute: "circa un minuto", + minutes: "%d minuti", + hour: "circa un'ora", + hours: "circa %d ore", + day: "un giorno", + days: "%d giorni", + month: "circa un mese", + months: "%d mesi", + year: "circa un anno", + years: "%d anni" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.ja.js b/website_example/lang/timeago/jquery.timeago.ja.js index 30f3308c3..8953d7689 100644 --- a/website_example/lang/timeago/jquery.timeago.ja.js +++ b/website_example/lang/timeago/jquery.timeago.ja.js @@ -1,29 +1,29 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Japanese - jQuery.timeago.settings.strings = { - prefixAgo: "", - prefixFromNow: "今から", - suffixAgo: "前", - suffixFromNow: "後", - seconds: "1 分未満", - minute: "約 1 分", - minutes: "%d 分", - hour: "約 1 時間", - hours: "約 %d 時間", - day: "約 1 日", - days: "約 %d 日", - month: "約 1 ヶ月", - months: "約 %d ヶ月", - year: "約 1 年", - years: "約 %d 年", - wordSeparator: "" - }; + // Japanese + jQuery.timeago.settings.strings = { + prefixAgo: "", + prefixFromNow: "今から", + suffixAgo: "前", + suffixFromNow: "後", + seconds: "1 分未満", + minute: "約 1 分", + minutes: "%d 分", + hour: "約 1 時間", + hours: "約 %d 時間", + day: "約 1 日", + days: "約 %d 日", + month: "約 1 ヶ月", + months: "約 %d ヶ月", + year: "約 1 年", + years: "約 %d 年", + wordSeparator: "" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.jv.js b/website_example/lang/timeago/jquery.timeago.jv.js index 0344053d7..9273ff93f 100644 --- a/website_example/lang/timeago/jquery.timeago.jv.js +++ b/website_example/lang/timeago/jquery.timeago.jv.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Javanesse (Boso Jowo) - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "kepungkur", - suffixFromNow: "seko saiki", - seconds: "kurang seko sakmenit", - minute: "kurang luwih sakmenit", - minutes: "%d menit", - hour: "kurang luwih sakjam", - hours: "kurang luwih %d jam", - day: "sedina", - days: "%d dina", - month: "kurang luwih sewulan", - months: "%d wulan", - year: "kurang luwih setahun", - years: "%d tahun" - }; + // Javanesse (Boso Jowo) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "kepungkur", + suffixFromNow: "seko saiki", + seconds: "kurang seko sakmenit", + minute: "kurang luwih sakmenit", + minutes: "%d menit", + hour: "kurang luwih sakjam", + hours: "kurang luwih %d jam", + day: "sedina", + days: "%d dina", + month: "kurang luwih sewulan", + months: "%d wulan", + year: "kurang luwih setahun", + years: "%d tahun" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.ko.js b/website_example/lang/timeago/jquery.timeago.ko.js index 23d1d3780..d446542cb 100644 --- a/website_example/lang/timeago/jquery.timeago.ko.js +++ b/website_example/lang/timeago/jquery.timeago.ko.js @@ -1,31 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Korean - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "전", - suffixFromNow: "후", - seconds: "1분", - minute: "약 1분", - minutes: "%d분", - hour: "약 1시간", - hours: "약 %d시간", - day: "하루", - days: "%d일", - month: "약 1개월", - months: "%d개월", - year: "약 1년", - years: "%d년", - wordSeparator: " ", - numbers: [] - }; + // Korean + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "전", + suffixFromNow: "후", + seconds: "1분", + minute: "약 1분", + minutes: "%d분", + hour: "약 1시간", + hours: "약 %d시간", + day: "하루", + days: "%d일", + month: "약 1개월", + months: "%d개월", + year: "약 1년", + years: "%d년", + wordSeparator: " ", + numbers: [] + }; })); - diff --git a/website_example/lang/timeago/jquery.timeago.ky.js b/website_example/lang/timeago/jquery.timeago.ky.js index 58dba293a..6f8bbafad 100644 --- a/website_example/lang/timeago/jquery.timeago.ky.js +++ b/website_example/lang/timeago/jquery.timeago.ky.js @@ -1,42 +1,52 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Russian - function numpf(n, f, s, t) { - // f - 1, 21, 31, ... - // s - 2-4, 22-24, 32-34 ... - // t - 5-20, 25-30, ... - var n10 = n % 10; - if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { - return f; - } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { - return s; - } else { - return t; - } - } + // Russian + function numpf (n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + var n10 = n % 10; + if ((n10 === 1) && ((n === 1) || (n > 20))) { + return f; + } else if ((n10 > 1) && (n10 < 5) && ((n > 20) || (n < 10))) { + return s; + } else { + return t; + } + } - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: "через", - suffixAgo: "мурун", - suffixFromNow: null, - seconds: "1 минуттан аз", - minute: "минута", - minutes: function(value) { return numpf(value, "%d минута", "%d минута", "%d минут"); }, - hour: "саат", - hours: function(value) { return numpf(value, "%d саат", "%d саат", "%d саат"); }, - day: "күн", - days: function(value) { return numpf(value, "%d күн", "%d күн", "%d күн"); }, - month: "ай", - months: function(value) { return numpf(value, "%d ай", "%d ай", "%d ай"); }, - year: "жыл", - years: function(value) { return numpf(value, "%d жыл", "%d жыл", "%d жыл"); } - }; + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "мурун", + suffixFromNow: null, + seconds: "1 минуттан аз", + minute: "минута", + minutes: function (value) { + return numpf(value, "%d минута", "%d минута", "%d минут"); + }, + hour: "саат", + hours: function (value) { + return numpf(value, "%d саат", "%d саат", "%d саат"); + }, + day: "күн", + days: function (value) { + return numpf(value, "%d күн", "%d күн", "%d күн"); + }, + month: "ай", + months: function (value) { + return numpf(value, "%d ай", "%d ай", "%d ай"); + }, + year: "жыл", + years: function (value) { + return numpf(value, "%d жыл", "%d жыл", "%d жыл"); + } + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.lt.js b/website_example/lang/timeago/jquery.timeago.lt.js index 2079fccd3..2f849f005 100644 --- a/website_example/lang/timeago/jquery.timeago.lt.js +++ b/website_example/lang/timeago/jquery.timeago.lt.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - //Lithuanian - jQuery.timeago.settings.strings = { - prefixAgo: "prieš", - prefixFromNow: null, - suffixAgo: null, - suffixFromNow: "nuo dabar", - seconds: "%d sek.", - minute: "min.", - minutes: "%d min.", - hour: "val.", - hours: "%d val.", - day: "1 d.", - days: "%d d.", - month: "mėn.", - months: "%d mėn.", - year: "metus", - years: "%d metus", - wordSeparator: " ", - numbers: [] - }; + //Lithuanian + jQuery.timeago.settings.strings = { + prefixAgo: "prieš", + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "nuo dabar", + seconds: "%d sek.", + minute: "min.", + minutes: "%d min.", + hour: "val.", + hours: "%d val.", + day: "1 d.", + days: "%d d.", + month: "mėn.", + months: "%d mėn.", + year: "metus", + years: "%d metus", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.lv.js b/website_example/lang/timeago/jquery.timeago.lv.js index 855d1a4dd..da352aa3d 100644 --- a/website_example/lang/timeago/jquery.timeago.lv.js +++ b/website_example/lang/timeago/jquery.timeago.lv.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - //Latvian - jQuery.timeago.settings.strings = { - prefixAgo: "pirms", - prefixFromNow: null, - suffixAgo: null, - suffixFromNow: "no šī brīža", - seconds: "%d sek.", - minute: "min.", - minutes: "%d min.", - hour: "st.", - hours: "%d st.", - day: "1 d.", - days: "%d d.", - month: "mēnesis.", - months: "%d mēnesis.", - year: "gads", - years: "%d gads", - wordSeparator: " ", - numbers: [] - }; + //Latvian + jQuery.timeago.settings.strings = { + prefixAgo: "pirms", + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "no šī brīža", + seconds: "%d sek.", + minute: "min.", + minutes: "%d min.", + hour: "st.", + hours: "%d st.", + day: "1 d.", + days: "%d d.", + month: "mēnesis.", + months: "%d mēnesis.", + year: "gads", + years: "%d gads", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.mk.js b/website_example/lang/timeago/jquery.timeago.mk.js index 301a5da83..0222e8145 100644 --- a/website_example/lang/timeago/jquery.timeago.mk.js +++ b/website_example/lang/timeago/jquery.timeago.mk.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Macedonian - (function() { - jQuery.timeago.settings.strings={ - prefixAgo: "пред", - prefixFromNow: "за", - suffixAgo: null, - suffixFromNow: null, - seconds: "%d секунди", - minute: "%d минута", - minutes: "%d минути", - hour: "%d час", - hours: "%d часа", - day: "%d ден", - days: "%d денови" , - month: "%d месец", - months: "%d месеци", - year: "%d година", - years: "%d години" - }; - })(); + // Macedonian + (function () { + jQuery.timeago.settings.strings = { + prefixAgo: "пред", + prefixFromNow: "за", + suffixAgo: null, + suffixFromNow: null, + seconds: "%d секунди", + minute: "%d минута", + minutes: "%d минути", + hour: "%d час", + hours: "%d часа", + day: "%d ден", + days: "%d денови", + month: "%d месец", + months: "%d месеци", + year: "%d година", + years: "%d години" + }; + })(); })); diff --git a/website_example/lang/timeago/jquery.timeago.nl.js b/website_example/lang/timeago/jquery.timeago.nl.js index 2c5de89c0..511504100 100644 --- a/website_example/lang/timeago/jquery.timeago.nl.js +++ b/website_example/lang/timeago/jquery.timeago.nl.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Dutch - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: "over", - suffixAgo: "geleden", - suffixFromNow: null, - seconds: "minder dan een minuut", - minute: "ongeveer een minuut", - minutes: "%d minuten", - hour: "ongeveer een uur", - hours: "ongeveer %d uur", - day: "een dag", - days: "%d dagen", - month: "ongeveer een maand", - months: "%d maanden", - year: "ongeveer een jaar", - years: "%d jaar", - wordSeparator: " ", - numbers: [] - }; + // Dutch + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "over", + suffixAgo: "geleden", + suffixFromNow: null, + seconds: "minder dan een minuut", + minute: "ongeveer een minuut", + minutes: "%d minuten", + hour: "ongeveer een uur", + hours: "ongeveer %d uur", + day: "een dag", + days: "%d dagen", + month: "ongeveer een maand", + months: "%d maanden", + year: "ongeveer een jaar", + years: "%d jaar", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.no.js b/website_example/lang/timeago/jquery.timeago.no.js index c896337c7..5dcddacd2 100644 --- a/website_example/lang/timeago/jquery.timeago.no.js +++ b/website_example/lang/timeago/jquery.timeago.no.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Norwegian - jQuery.timeago.settings.strings = { - prefixAgo: "for", - prefixFromNow: "om", - suffixAgo: "siden", - suffixFromNow: "", - seconds: "mindre enn et minutt", - minute: "ca. et minutt", - minutes: "%d minutter", - hour: "ca. en time", - hours: "ca. %d timer", - day: "en dag", - days: "%d dager", - month: "ca. en måned", - months: "%d måneder", - year: "ca. et år", - years: "%d år" - }; + // Norwegian + jQuery.timeago.settings.strings = { + prefixAgo: "for", + prefixFromNow: "om", + suffixAgo: "siden", + suffixFromNow: "", + seconds: "mindre enn et minutt", + minute: "ca. et minutt", + minutes: "%d minutter", + hour: "ca. en time", + hours: "ca. %d timer", + day: "en dag", + days: "%d dager", + month: "ca. en måned", + months: "%d måneder", + year: "ca. et år", + years: "%d år" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.pl.js b/website_example/lang/timeago/jquery.timeago.pl.js index 48427846a..43903b727 100644 --- a/website_example/lang/timeago/jquery.timeago.pl.js +++ b/website_example/lang/timeago/jquery.timeago.pl.js @@ -1,39 +1,47 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Polish - function numpf(n, s, t) { - // s - 2-4, 22-24, 32-34 ... - // t - 5-21, 25-31, ... - var n10 = n % 10; - if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { - return s; - } else { - return t; - } - } + // Polish + function numpf (n, s, t) { + // s - 2-4, 22-24, 32-34 ... + // t - 5-21, 25-31, ... + var n10 = n % 10; + if ((n10 > 1) && (n10 < 5) && ((n > 20) || (n < 10))) { + return s; + } else { + return t; + } + } - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: "za", - suffixAgo: "temu", - suffixFromNow: null, - seconds: "mniej niż minutę", - minute: "minutę", - minutes: function(value) { return numpf(value, "%d minuty", "%d minut"); }, - hour: "godzinę", - hours: function(value) { return numpf(value, "%d godziny", "%d godzin"); }, - day: "dzień", - days: "%d dni", - month: "miesiąc", - months: function(value) { return numpf(value, "%d miesiące", "%d miesięcy"); }, - year: "rok", - years: function(value) { return numpf(value, "%d lata", "%d lat"); } - }; + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "za", + suffixAgo: "temu", + suffixFromNow: null, + seconds: "mniej niż minutę", + minute: "minutę", + minutes: function (value) { + return numpf(value, "%d minuty", "%d minut"); + }, + hour: "godzinę", + hours: function (value) { + return numpf(value, "%d godziny", "%d godzin"); + }, + day: "dzień", + days: "%d dni", + month: "miesiąc", + months: function (value) { + return numpf(value, "%d miesiące", "%d miesięcy"); + }, + year: "rok", + years: function (value) { + return numpf(value, "%d lata", "%d lat"); + } + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.pt-br.js b/website_example/lang/timeago/jquery.timeago.pt-br.js index a8701a880..551242f10 100644 --- a/website_example/lang/timeago/jquery.timeago.pt-br.js +++ b/website_example/lang/timeago/jquery.timeago.pt-br.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Brazilian Portuguese - jQuery.timeago.settings.strings = { - prefixAgo: "há", - prefixFromNow: "em", - suffixAgo: null, - suffixFromNow: null, - seconds: "alguns segundos", - minute: "um minuto", - minutes: "%d minutos", - hour: "uma hora", - hours: "%d horas", - day: "um dia", - days: "%d dias", - month: "um mês", - months: "%d meses", - year: "um ano", - years: "%d anos" - }; + // Brazilian Portuguese + jQuery.timeago.settings.strings = { + prefixAgo: "há", + prefixFromNow: "em", + suffixAgo: null, + suffixFromNow: null, + seconds: "alguns segundos", + minute: "um minuto", + minutes: "%d minutos", + hour: "uma hora", + hours: "%d horas", + day: "um dia", + days: "%d dias", + month: "um mês", + months: "%d meses", + year: "um ano", + years: "%d anos" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.pt.js b/website_example/lang/timeago/jquery.timeago.pt.js index 13791a037..c28bcfd34 100644 --- a/website_example/lang/timeago/jquery.timeago.pt.js +++ b/website_example/lang/timeago/jquery.timeago.pt.js @@ -1,26 +1,26 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Portuguese - jQuery.timeago.settings.strings = { - suffixAgo: "atrás", - suffixFromNow: "a partir de agora", - seconds: "menos de um minuto", - minute: "cerca de um minuto", - minutes: "%d minutos", - hour: "cerca de uma hora", - hours: "cerca de %d horas", - day: "um dia", - days: "%d dias", - month: "cerca de um mês", - months: "%d meses", - year: "cerca de um ano", - years: "%d anos" - }; + // Portuguese + jQuery.timeago.settings.strings = { + suffixAgo: "atrás", + suffixFromNow: "a partir de agora", + seconds: "menos de um minuto", + minute: "cerca de um minuto", + minutes: "%d minutos", + hour: "cerca de uma hora", + hours: "cerca de %d horas", + day: "um dia", + days: "%d dias", + month: "cerca de um mês", + months: "%d meses", + year: "cerca de um ano", + years: "%d anos" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.ro.js b/website_example/lang/timeago/jquery.timeago.ro.js index fe59db900..722f26977 100644 --- a/website_example/lang/timeago/jquery.timeago.ro.js +++ b/website_example/lang/timeago/jquery.timeago.ro.js @@ -1,29 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Romanian - jQuery.timeago.settings.strings = { - prefixAgo: "acum", - prefixFromNow: "in timp de", - suffixAgo: "", - suffixFromNow: "", - seconds: "mai putin de un minut", - minute: "un minut", - minutes: "%d minute", - hour: "o ora", - hours: "%d ore", - day: "o zi", - days: "%d zile", - month: "o luna", - months: "%d luni", - year: "un an", - years: "%d ani" - }; + // Romanian + jQuery.timeago.settings.strings = { + prefixAgo: "acum", + prefixFromNow: "in timp de", + suffixAgo: "", + suffixFromNow: "", + seconds: "mai putin de un minut", + minute: "un minut", + minutes: "%d minute", + hour: "o ora", + hours: "%d ore", + day: "o zi", + days: "%d zile", + month: "o luna", + months: "%d luni", + year: "un an", + years: "%d ani" + }; })); - diff --git a/website_example/lang/timeago/jquery.timeago.rs.js b/website_example/lang/timeago/jquery.timeago.rs.js index b9e518825..3421223ac 100644 --- a/website_example/lang/timeago/jquery.timeago.rs.js +++ b/website_example/lang/timeago/jquery.timeago.rs.js @@ -1,54 +1,54 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Serbian - var numpf = function (n, f, s, t) { - var n10; - n10 = n % 10; - if (n10 === 1 && (n === 1 || n > 20)) { - return f; - } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { - return s; - } else { - return t; - } - }; + // Serbian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; - jQuery.timeago.settings.strings = { - prefixAgo: "pre", - prefixFromNow: "za", - suffixAgo: null, - suffixFromNow: null, - second: "sekund", - seconds: function (value) { - return numpf(value, "%d sekund", "%d sekunde", "%d sekundi"); - }, - minute: "oko minut", - minutes: function (value) { - return numpf(value, "%d minut", "%d minuta", "%d minuta"); - }, - hour: "oko jedan sat", - hours: function (value) { - return numpf(value, "%d sat", "%d sata", "%d sati"); - }, - day: "jedan dan", - days: function (value) { - return numpf(value, "%d dan", "%d dana", "%d dana"); - }, - month: "mesec dana", - months: function (value) { - return numpf(value, "%d mesec", "%d meseca", "%d meseci"); - }, - year: "godinu dana", - years: function (value) { - return numpf(value, "%d godinu", "%d godine", "%d godina"); - }, - wordSeparator: " " - }; + jQuery.timeago.settings.strings = { + prefixAgo: "pre", + prefixFromNow: "za", + suffixAgo: null, + suffixFromNow: null, + second: "sekund", + seconds: function (value) { + return numpf(value, "%d sekund", "%d sekunde", "%d sekundi"); + }, + minute: "oko minut", + minutes: function (value) { + return numpf(value, "%d minut", "%d minuta", "%d minuta"); + }, + hour: "oko jedan sat", + hours: function (value) { + return numpf(value, "%d sat", "%d sata", "%d sati"); + }, + day: "jedan dan", + days: function (value) { + return numpf(value, "%d dan", "%d dana", "%d dana"); + }, + month: "mesec dana", + months: function (value) { + return numpf(value, "%d mesec", "%d meseca", "%d meseci"); + }, + year: "godinu dana", + years: function (value) { + return numpf(value, "%d godinu", "%d godine", "%d godina"); + }, + wordSeparator: " " + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.ru.js b/website_example/lang/timeago/jquery.timeago.ru.js index 4ff3f8d30..cd9967f85 100644 --- a/website_example/lang/timeago/jquery.timeago.ru.js +++ b/website_example/lang/timeago/jquery.timeago.ru.js @@ -1,43 +1,53 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Russian - function numpf(n, f, s, t) { - // f - 1, 21, 31, ... - // s - 2-4, 22-24, 32-34 ... - // t - 5-20, 25-30, ... - n = n % 100; - var n10 = n % 10; - if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { - return f; - } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { - return s; - } else { - return t; - } - } + // Russian + function numpf (n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + n = n % 100; + var n10 = n % 10; + if ((n10 === 1) && ((n === 1) || (n > 20))) { + return f; + } else if ((n10 > 1) && (n10 < 5) && ((n > 20) || (n < 10))) { + return s; + } else { + return t; + } + } - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: "через", - suffixAgo: "назад", - suffixFromNow: null, - seconds: "меньше минуты", - minute: "минуту", - minutes: function(value) { return numpf(value, "%d минуту", "%d минуты", "%d минут"); }, - hour: "час", - hours: function(value) { return numpf(value, "%d час", "%d часа", "%d часов"); }, - day: "день", - days: function(value) { return numpf(value, "%d день", "%d дня", "%d дней"); }, - month: "месяц", - months: function(value) { return numpf(value, "%d месяц", "%d месяца", "%d месяцев"); }, - year: "год", - years: function(value) { return numpf(value, "%d год", "%d года", "%d лет"); } - }; + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "назад", + suffixFromNow: null, + seconds: "меньше минуты", + minute: "минуту", + minutes: function (value) { + return numpf(value, "%d минуту", "%d минуты", "%d минут"); + }, + hour: "час", + hours: function (value) { + return numpf(value, "%d час", "%d часа", "%d часов"); + }, + day: "день", + days: function (value) { + return numpf(value, "%d день", "%d дня", "%d дней"); + }, + month: "месяц", + months: function (value) { + return numpf(value, "%d месяц", "%d месяца", "%d месяцев"); + }, + year: "год", + years: function (value) { + return numpf(value, "%d год", "%d года", "%d лет"); + } + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.rw.js b/website_example/lang/timeago/jquery.timeago.rw.js index 50119e1e8..1d6150016 100644 --- a/website_example/lang/timeago/jquery.timeago.rw.js +++ b/website_example/lang/timeago/jquery.timeago.rw.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Kinyarwanda - jQuery.timeago.settings.strings = { - prefixAgo: "hashize", - prefixFromNow: "mu", - suffixAgo: null, - suffixFromNow: null, - seconds: "amasegonda macye", - minute: "umunota", - minutes: "iminota %d", - hour: "isaha", - hours: "amasaha %d", - day: "umunsi", - days: "iminsi %d", - month: "ukwezi", - months: "amezi %d", - year: "umwaka", - years: "imyaka %d", - wordSeparator: " ", - numbers: [] - }; + // Kinyarwanda + jQuery.timeago.settings.strings = { + prefixAgo: "hashize", + prefixFromNow: "mu", + suffixAgo: null, + suffixFromNow: null, + seconds: "amasegonda macye", + minute: "umunota", + minutes: "iminota %d", + hour: "isaha", + hours: "amasaha %d", + day: "umunsi", + days: "iminsi %d", + month: "ukwezi", + months: "amezi %d", + year: "umwaka", + years: "imyaka %d", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.si.js b/website_example/lang/timeago/jquery.timeago.si.js index 6fa215e9a..20525fff7 100644 --- a/website_example/lang/timeago/jquery.timeago.si.js +++ b/website_example/lang/timeago/jquery.timeago.si.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Sinhalese (SI) - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "පෙර", - suffixFromNow: "පසුව", - seconds: "තත්පර කිහිපයකට", - minute: "මිනිත්තුවකට පමණ", - minutes: "මිනිත්තු %d කට", - hour: "පැයක් පමණ ", - hours: "පැය %d කට පමණ", - day: "දවසක ට", - days: "දවස් %d කට ", - month: "මාසයක් පමණ", - months: "මාස %d කට", - year: "වසරක් පමණ", - years: "වසරක් %d කට පමණ" - }; + // Sinhalese (SI) + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "පෙර", + suffixFromNow: "පසුව", + seconds: "තත්පර කිහිපයකට", + minute: "මිනිත්තුවකට පමණ", + minutes: "මිනිත්තු %d කට", + hour: "පැයක් පමණ ", + hours: "පැය %d කට පමණ", + day: "දවසක ට", + days: "දවස් %d කට ", + month: "මාසයක් පමණ", + months: "මාස %d කට", + year: "වසරක් පමණ", + years: "වසරක් %d කට පමණ" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.sk.js b/website_example/lang/timeago/jquery.timeago.sk.js index e28ab7c9b..4b878df9f 100644 --- a/website_example/lang/timeago/jquery.timeago.sk.js +++ b/website_example/lang/timeago/jquery.timeago.sk.js @@ -1,34 +1,56 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Slovak - (function() { - function f(n, d, a) { - return a[d>=0 ? 0 : a.length===2 || n<5 ? 1 : 2]; - } - - jQuery.timeago.settings.strings = { - prefixAgo: 'pred', - prefixFromNow: 'o', - suffixAgo: null, - suffixFromNow: null, - seconds: function(n, d) {return f(n, d, ['menej ako minútou', 'menej ako minútu']);}, - minute: function(n, d) {return f(n, d, ['minútou', 'minútu']);}, - minutes: function(n, d) {return f(n, d, ['%d minútami', '%d minúty', '%d minút']);}, - hour: function(n, d) {return f(n, d, ['hodinou', 'hodinu']);}, - hours: function(n, d) {return f(n, d, ['%d hodinami', '%d hodiny', '%d hodín']);}, - day: function(n, d) {return f(n, d, ['%d dňom', '%d deň']);}, - days: function(n, d) {return f(n, d, ['%d dňami', '%d dni', '%d dní']);}, - month: function(n, d) {return f(n, d, ['%d mesiacom', '%d mesiac']);}, - months: function(n, d) {return f(n, d, ['%d mesiacmi', '%d mesiace', '%d mesiacov']);}, - year: function(n, d) {return f(n, d, ['%d rokom', '%d rok']);}, - years: function(n, d) {return f(n, d, ['%d rokmi', '%d roky', '%d rokov']);} - }; - })(); + // Slovak + (function () { + function f (n, d, a) { + return a[d >= 0 ? 0 : a.length === 2 || n < 5 ? 1 : 2]; + } + + jQuery.timeago.settings.strings = { + prefixAgo: 'pred', + prefixFromNow: 'o', + suffixAgo: null, + suffixFromNow: null, + seconds: function (n, d) { + return f(n, d, ['menej ako minútou', 'menej ako minútu']); + }, + minute: function (n, d) { + return f(n, d, ['minútou', 'minútu']); + }, + minutes: function (n, d) { + return f(n, d, ['%d minútami', '%d minúty', '%d minút']); + }, + hour: function (n, d) { + return f(n, d, ['hodinou', 'hodinu']); + }, + hours: function (n, d) { + return f(n, d, ['%d hodinami', '%d hodiny', '%d hodín']); + }, + day: function (n, d) { + return f(n, d, ['%d dňom', '%d deň']); + }, + days: function (n, d) { + return f(n, d, ['%d dňami', '%d dni', '%d dní']); + }, + month: function (n, d) { + return f(n, d, ['%d mesiacom', '%d mesiac']); + }, + months: function (n, d) { + return f(n, d, ['%d mesiacmi', '%d mesiace', '%d mesiacov']); + }, + year: function (n, d) { + return f(n, d, ['%d rokom', '%d rok']); + }, + years: function (n, d) { + return f(n, d, ['%d rokmi', '%d roky', '%d rokov']); + } + }; + })(); })); diff --git a/website_example/lang/timeago/jquery.timeago.sl.js b/website_example/lang/timeago/jquery.timeago.sl.js index 9f0329ac6..0affd8fbb 100644 --- a/website_example/lang/timeago/jquery.timeago.sl.js +++ b/website_example/lang/timeago/jquery.timeago.sl.js @@ -1,46 +1,46 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Slovenian with support for dual - var numpf = function (n, a) { - return a[n%100===1 ? 1 : n%100===2 ? 2 : n%100===3 || n%100===4 ? 3 : 0]; - }; + // Slovenian with support for dual + var numpf = function (n, a) { + return a[n % 100 === 1 ? 1 : n % 100 === 2 ? 2 : n % 100 === 3 || n % 100 === 4 ? 3 : 0]; + }; - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: "čez", - suffixAgo: "nazaj", - suffixFromNow: null, - second: "sekundo", - seconds: function (value) { - return numpf(value, ["%d sekund", "%d sekundo", "%d sekundi", "%d sekunde"]); - }, - minute: "minuto", - minutes: function (value) { - return numpf(value, ["%d minut", "%d minuto", "%d minuti", "%d minute"]); - }, - hour: "eno uro", - hours: function (value) { - return numpf(value, ["%d ur", "%d uro", "%d uri", "%d ure"]); - }, - day: "en dan", - days: function (value) { - return numpf(value, ["%d dni", "%d dan", "%d dneva", "%d dni"]); - }, - month: "en mesec", - months: function (value) { - return numpf(value, ["%d mesecev", "%d mesec", "%d meseca", "%d mesece"]); - }, - year: "eno leto", - years: function (value) { - return numpf(value, ["%d let", "%d leto", "%d leti", "%d leta"]); - }, - wordSeparator: " " - }; + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "čez", + suffixAgo: "nazaj", + suffixFromNow: null, + second: "sekundo", + seconds: function (value) { + return numpf(value, ["%d sekund", "%d sekundo", "%d sekundi", "%d sekunde"]); + }, + minute: "minuto", + minutes: function (value) { + return numpf(value, ["%d minut", "%d minuto", "%d minuti", "%d minute"]); + }, + hour: "eno uro", + hours: function (value) { + return numpf(value, ["%d ur", "%d uro", "%d uri", "%d ure"]); + }, + day: "en dan", + days: function (value) { + return numpf(value, ["%d dni", "%d dan", "%d dneva", "%d dni"]); + }, + month: "en mesec", + months: function (value) { + return numpf(value, ["%d mesecev", "%d mesec", "%d meseca", "%d mesece"]); + }, + year: "eno leto", + years: function (value) { + return numpf(value, ["%d let", "%d leto", "%d leti", "%d leta"]); + }, + wordSeparator: " " + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.sq.js b/website_example/lang/timeago/jquery.timeago.sq.js index cb8ae703a..43b47a884 100644 --- a/website_example/lang/timeago/jquery.timeago.sq.js +++ b/website_example/lang/timeago/jquery.timeago.sq.js @@ -1,26 +1,26 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Albanian SQ - jQuery.timeago.settings.strings = { - suffixAgo: "më parë", - suffixFromNow: "tani", - seconds: "më pak se një minutë", - minute: "rreth një minutë", - minutes: "%d minuta", - hour: "rreth një orë", - hours: "rreth %d orë", - day: "një ditë", - days: "%d ditë", - month: "rreth një muaj", - months: "%d muaj", - year: "rreth një vit", - years: "%d vjet" - }; + // Albanian SQ + jQuery.timeago.settings.strings = { + suffixAgo: "më parë", + suffixFromNow: "tani", + seconds: "më pak se një minutë", + minute: "rreth një minutë", + minutes: "%d minuta", + hour: "rreth një orë", + hours: "rreth %d orë", + day: "një ditë", + days: "%d ditë", + month: "rreth një muaj", + months: "%d muaj", + year: "rreth një vit", + years: "%d vjet" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.sr.js b/website_example/lang/timeago/jquery.timeago.sr.js index bd1efe79a..9db1e9ff4 100644 --- a/website_example/lang/timeago/jquery.timeago.sr.js +++ b/website_example/lang/timeago/jquery.timeago.sr.js @@ -1,54 +1,54 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Serbian - var numpf = function (n, f, s, t) { - var n10; - n10 = n % 10; - if (n10 === 1 && (n === 1 || n > 20)) { - return f; - } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { - return s; - } else { - return t; - } - }; + // Serbian + var numpf = function (n, f, s, t) { + var n10; + n10 = n % 10; + if (n10 === 1 && (n === 1 || n > 20)) { + return f; + } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { + return s; + } else { + return t; + } + }; - jQuery.timeago.settings.strings = { - prefixAgo: "пре", - prefixFromNow: "за", - suffixAgo: null, - suffixFromNow: null, - second: "секунд", - seconds: function (value) { - return numpf(value, "%d секунд", "%d секунде", "%d секунди"); - }, - minute: "један минут", - minutes: function (value) { - return numpf(value, "%d минут", "%d минута", "%d минута"); - }, - hour: "један сат", - hours: function (value) { - return numpf(value, "%d сат", "%d сата", "%d сати"); - }, - day: "један дан", - days: function (value) { - return numpf(value, "%d дан", "%d дана", "%d дана"); - }, - month: "месец дана", - months: function (value) { - return numpf(value, "%d месец", "%d месеца", "%d месеци"); - }, - year: "годину дана", - years: function (value) { - return numpf(value, "%d годину", "%d године", "%d година"); - }, - wordSeparator: " " - }; + jQuery.timeago.settings.strings = { + prefixAgo: "пре", + prefixFromNow: "за", + suffixAgo: null, + suffixFromNow: null, + second: "секунд", + seconds: function (value) { + return numpf(value, "%d секунд", "%d секунде", "%d секунди"); + }, + minute: "један минут", + minutes: function (value) { + return numpf(value, "%d минут", "%d минута", "%d минута"); + }, + hour: "један сат", + hours: function (value) { + return numpf(value, "%d сат", "%d сата", "%d сати"); + }, + day: "један дан", + days: function (value) { + return numpf(value, "%d дан", "%d дана", "%d дана"); + }, + month: "месец дана", + months: function (value) { + return numpf(value, "%d месец", "%d месеца", "%d месеци"); + }, + year: "годину дана", + years: function (value) { + return numpf(value, "%d годину", "%d године", "%d година"); + }, + wordSeparator: " " + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.sv.js b/website_example/lang/timeago/jquery.timeago.sv.js index caf09dbb0..5100d8558 100644 --- a/website_example/lang/timeago/jquery.timeago.sv.js +++ b/website_example/lang/timeago/jquery.timeago.sv.js @@ -1,28 +1,28 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Swedish - jQuery.timeago.settings.strings = { - prefixAgo: "för", - prefixFromNow: "om", - suffixAgo: "sedan", - suffixFromNow: "", - seconds: "mindre än en minut", - minute: "ungefär en minut", - minutes: "%d minuter", - hour: "ungefär en timme", - hours: "ungefär %d timmar", - day: "en dag", - days: "%d dagar", - month: "ungefär en månad", - months: "%d månader", - year: "ungefär ett år", - years: "%d år" - }; + // Swedish + jQuery.timeago.settings.strings = { + prefixAgo: "för", + prefixFromNow: "om", + suffixAgo: "sedan", + suffixFromNow: "", + seconds: "mindre än en minut", + minute: "ungefär en minut", + minutes: "%d minuter", + hour: "ungefär en timme", + hours: "ungefär %d timmar", + day: "en dag", + days: "%d dagar", + month: "ungefär en månad", + months: "%d månader", + year: "ungefär ett år", + years: "%d år" + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.th.js b/website_example/lang/timeago/jquery.timeago.th.js index 23d59d48e..0769c6355 100644 --- a/website_example/lang/timeago/jquery.timeago.th.js +++ b/website_example/lang/timeago/jquery.timeago.th.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Thai - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "ที่แล้ว", - suffixFromNow: "จากตอนนี้", - seconds: "น้อยกว่าหนึ่งนาที", - minute: "ประมาณหนึ่งนาที", - minutes: "%d นาที", - hour: "ประมาณหนึ่งชั่วโมง", - hours: "ประมาณ %d ชั่วโมง", - day: "หนึ่งวัน", - days: "%d วัน", - month: "ประมาณหนึ่งเดือน", - months: "%d เดือน", - year: "ประมาณหนึ่งปี", - years: "%d ปี", - wordSeparator: "", - numbers: [] - }; + // Thai + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ที่แล้ว", + suffixFromNow: "จากตอนนี้", + seconds: "น้อยกว่าหนึ่งนาที", + minute: "ประมาณหนึ่งนาที", + minutes: "%d นาที", + hour: "ประมาณหนึ่งชั่วโมง", + hours: "ประมาณ %d ชั่วโมง", + day: "หนึ่งวัน", + days: "%d วัน", + month: "ประมาณหนึ่งเดือน", + months: "%d เดือน", + year: "ประมาณหนึ่งปี", + years: "%d ปี", + wordSeparator: "", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.tr.js b/website_example/lang/timeago/jquery.timeago.tr.js index 8e0d2d4e9..8e16e8e3b 100644 --- a/website_example/lang/timeago/jquery.timeago.tr.js +++ b/website_example/lang/timeago/jquery.timeago.tr.js @@ -1,26 +1,26 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Turkish - jQuery.timeago.settings.strings = { - suffixAgo: 'önce', - suffixFromNow: null, - seconds: 'birkaç saniye', - minute: '1 dakika', - minutes: '%d dakika', - hour: '1 saat', - hours: '%d saat', - day: '1 gün', - days: '%d gün', - month: '1 ay', - months: '%d ay', - year: '1 yıl', - years: '%d yıl' - }; + // Turkish + jQuery.timeago.settings.strings = { + suffixAgo: 'önce', + suffixFromNow: null, + seconds: 'birkaç saniye', + minute: '1 dakika', + minutes: '%d dakika', + hour: '1 saat', + hours: '%d saat', + day: '1 gün', + days: '%d gün', + month: '1 ay', + months: '%d ay', + year: '1 yıl', + years: '%d yıl' + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.uk.js b/website_example/lang/timeago/jquery.timeago.uk.js index 489963b5d..9d6880d07 100644 --- a/website_example/lang/timeago/jquery.timeago.uk.js +++ b/website_example/lang/timeago/jquery.timeago.uk.js @@ -1,42 +1,52 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Ukrainian - function numpf(n, f, s, t) { - // f - 1, 21, 31, ... - // s - 2-4, 22-24, 32-34 ... - // t - 5-20, 25-30, ... - var n10 = n % 10; - if ( (n10 === 1) && ( (n === 1) || (n > 20) ) ) { - return f; - } else if ( (n10 > 1) && (n10 < 5) && ( (n > 20) || (n < 10) ) ) { - return s; - } else { - return t; - } - } + // Ukrainian + function numpf (n, f, s, t) { + // f - 1, 21, 31, ... + // s - 2-4, 22-24, 32-34 ... + // t - 5-20, 25-30, ... + var n10 = n % 10; + if ((n10 === 1) && ((n === 1) || (n > 20))) { + return f; + } else if ((n10 > 1) && (n10 < 5) && ((n > 20) || (n < 10))) { + return s; + } else { + return t; + } + } - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: "через", - suffixAgo: "тому", - suffixFromNow: null, - seconds: "менше хвилини", - minute: "хвилина", - minutes: function(value) { return numpf(value, "%d хвилина", "%d хвилини", "%d хвилин"); }, - hour: "година", - hours: function(value) { return numpf(value, "%d година", "%d години", "%d годин"); }, - day: "день", - days: function(value) { return numpf(value, "%d день", "%d дні", "%d днів"); }, - month: "місяць", - months: function(value) { return numpf(value, "%d місяць", "%d місяці", "%d місяців"); }, - year: "рік", - years: function(value) { return numpf(value, "%d рік", "%d роки", "%d років"); } - }; + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "через", + suffixAgo: "тому", + suffixFromNow: null, + seconds: "менше хвилини", + minute: "хвилина", + minutes: function (value) { + return numpf(value, "%d хвилина", "%d хвилини", "%d хвилин"); + }, + hour: "година", + hours: function (value) { + return numpf(value, "%d година", "%d години", "%d годин"); + }, + day: "день", + days: function (value) { + return numpf(value, "%d день", "%d дні", "%d днів"); + }, + month: "місяць", + months: function (value) { + return numpf(value, "%d місяць", "%d місяці", "%d місяців"); + }, + year: "рік", + years: function (value) { + return numpf(value, "%d рік", "%d роки", "%d років"); + } + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.ur.js b/website_example/lang/timeago/jquery.timeago.ur.js index 9d0cd402d..f0c1d8f6d 100644 --- a/website_example/lang/timeago/jquery.timeago.ur.js +++ b/website_example/lang/timeago/jquery.timeago.ur.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Urdu - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "پہلے", - suffixFromNow: "اب سے", - seconds: "کچھ سیکنڈز", - minute: "تقریباً ایک منٹ", - minutes: "%d منٹ", - hour: "تقریباً ایک گھنٹہ", - hours: "تقریباً %d گھنٹے", - day: "ایک دن", - days: "%d دن", - month: "تقریباً ایک مہینہ", - months: "%d مہینے", - year: "تقریباً ایک سال", - years: "%d سال", - wordSeparator: " ", - numbers: [] - }; + // Urdu + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "پہلے", + suffixFromNow: "اب سے", + seconds: "کچھ سیکنڈز", + minute: "تقریباً ایک منٹ", + minutes: "%d منٹ", + hour: "تقریباً ایک گھنٹہ", + hours: "تقریباً %d گھنٹے", + day: "ایک دن", + days: "%d دن", + month: "تقریباً ایک مہینہ", + months: "%d مہینے", + year: "تقریباً ایک سال", + years: "%d سال", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.uz.js b/website_example/lang/timeago/jquery.timeago.uz.js index f4ce8b33b..31c148f80 100644 --- a/website_example/lang/timeago/jquery.timeago.uz.js +++ b/website_example/lang/timeago/jquery.timeago.uz.js @@ -1,29 +1,39 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - //Uzbek - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: "keyin", - suffixAgo: "avval", - suffixFromNow: null, - seconds: "bir necha soniya", - minute: "1 daqiqa", - minutes: function(value) { return "%d daqiqa"; }, - hour: "1 soat", - hours: function(value) { return "%d soat"; }, - day: "1 kun", - days: function(value) { return "%d kun"; }, - month: "1 oy", - months: function(value) { return "%d oy"; }, - year: "1 yil", - years: function(value) { return "%d yil"; }, - wordSeparator: " " - }; + //Uzbek + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: "keyin", + suffixAgo: "avval", + suffixFromNow: null, + seconds: "bir necha soniya", + minute: "1 daqiqa", + minutes: function (value) { + return "%d daqiqa"; + }, + hour: "1 soat", + hours: function (value) { + return "%d soat"; + }, + day: "1 kun", + days: function (value) { + return "%d kun"; + }, + month: "1 oy", + months: function (value) { + return "%d oy"; + }, + year: "1 yil", + years: function (value) { + return "%d yil"; + }, + wordSeparator: " " + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.vi.js b/website_example/lang/timeago/jquery.timeago.vi.js index 30f592ac3..c4ae61e9c 100644 --- a/website_example/lang/timeago/jquery.timeago.vi.js +++ b/website_example/lang/timeago/jquery.timeago.vi.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Vietnamese - jQuery.timeago.settings.strings = { - prefixAgo: 'cách đây', - prefixFromNow: null, - suffixAgo: null, - suffixFromNow: "trước", - seconds: "chưa đến một phút", - minute: "khoảng một phút", - minutes: "%d phút", - hour: "khoảng một tiếng", - hours: "khoảng %d tiếng", - day: "một ngày", - days: "%d ngày", - month: "khoảng một tháng", - months: "%d tháng", - year: "khoảng một năm", - years: "%d năm", - wordSeparator: " ", - numbers: [] - }; + // Vietnamese + jQuery.timeago.settings.strings = { + prefixAgo: 'cách đây', + prefixFromNow: null, + suffixAgo: null, + suffixFromNow: "trước", + seconds: "chưa đến một phút", + minute: "khoảng một phút", + minutes: "%d phút", + hour: "khoảng một tiếng", + hours: "khoảng %d tiếng", + day: "một ngày", + days: "%d ngày", + month: "khoảng một tháng", + months: "%d tháng", + year: "khoảng một năm", + years: "%d năm", + wordSeparator: " ", + numbers: [] + }; })); diff --git a/website_example/lang/timeago/jquery.timeago.zh-CN.js b/website_example/lang/timeago/jquery.timeago.zh-CN.js index c21a2874a..41971f12a 100644 --- a/website_example/lang/timeago/jquery.timeago.zh-CN.js +++ b/website_example/lang/timeago/jquery.timeago.zh-CN.js @@ -1,31 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Simplified Chinese - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "之前", - suffixFromNow: "之后", - seconds: "不到1分钟", - minute: "大约1分钟", - minutes: "%d分钟", - hour: "大约1小时", - hours: "大约%d小时", - day: "1天", - days: "%d天", - month: "大约1个月", - months: "%d月", - year: "大约1年", - years: "%d年", - numbers: [], - wordSeparator: "" - }; + // Simplified Chinese + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "之前", + suffixFromNow: "之后", + seconds: "不到1分钟", + minute: "大约1分钟", + minutes: "%d分钟", + hour: "大约1小时", + hours: "大约%d小时", + day: "1天", + days: "%d天", + month: "大约1个月", + months: "%d月", + year: "大约1年", + years: "%d年", + numbers: [], + wordSeparator: "" + }; })); - diff --git a/website_example/lang/timeago/jquery.timeago.zh-TW.js b/website_example/lang/timeago/jquery.timeago.zh-TW.js index 15f562699..2321e39f8 100644 --- a/website_example/lang/timeago/jquery.timeago.zh-TW.js +++ b/website_example/lang/timeago/jquery.timeago.zh-TW.js @@ -1,30 +1,30 @@ (function (factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + factory(require('jquery')); + } else { + factory(jQuery); + } }(function (jQuery) { - // Traditional Chinese, zh-tw - jQuery.timeago.settings.strings = { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "之前", - suffixFromNow: "之後", - seconds: "不到1分鐘", - minute: "大約1分鐘", - minutes: "%d分鐘", - hour: "大約1小時", - hours: "%d小時", - day: "大約1天", - days: "%d天", - month: "大約1個月", - months: "%d個月", - year: "大約1年", - years: "%d年", - numbers: [], - wordSeparator: "" - }; + // Traditional Chinese, zh-tw + jQuery.timeago.settings.strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "之前", + suffixFromNow: "之後", + seconds: "不到1分鐘", + minute: "大約1分鐘", + minutes: "%d分鐘", + hour: "大約1小時", + hours: "%d小時", + day: "大約1天", + days: "%d天", + month: "大約1個月", + months: "%d個月", + year: "大約1年", + years: "%d年", + numbers: [], + wordSeparator: "" + }; })); diff --git a/website_example/lang/zh-CN.json b/website_example/lang/zh-CN.json index 10ad0ba7c..9939bab7c 100644 --- a/website_example/lang/zh-CN.json +++ b/website_example/lang/zh-CN.json @@ -1,168 +1,169 @@ { - "miningPool": "矿池", - "dashboard": "仪表盘", - "gettingStarted": "挖矿指导", - "yourStats": "挖矿数据", - "poolBlocks": "池中区块", - "settings": "设置", - "faq": "常见问题", - "telegram": "Telegram群组", - "discord": "Discord", - "contactUs": "联系方式", - - "network": "全网", - "pool": "矿池", - "you": "用户", - "statsUpdated": "数据更新", - - "poolHashrate": "矿池算力", - "currentEffort": "当前效益", - "networkHashrate": "全网算力", - "networkDifficulty": "难度", - "blockchainHeight": "区块高度", - "networkLastReward": "上次收益", - "poolMiners": "在线矿工", - "poolFee": "矿池税", - - "minerStats": "用户数据及支付历史", - "workerStats": "设备数据", - "miner": "矿工", - "miners": "矿工", - "minersCount": "矿工数", - "workers": "挖矿设备", - "workersCount": "设备数", - "workerName": "设备名", - "lastHash": "上次提交", - "hashRate": "算力", - "currentHashRate": "当前算力", - "lastShare": "上次提交", - "totalHashes": "总提交", - "top10miners": "前十矿工", + "miningPool": "矿池", + "dashboard": "仪表盘", + "gettingStarted": "挖矿指导", + "yourStats": "挖矿数据", + "poolBlocks": "池中区块", + "settings": "设置", + "faq": "常见问题", + "telegram": "Telegram群组", + "discord": "Discord", + "contactUs": "联系方式", - "blocksTotal": "已发现区块", - "blockSolvedTime": "区块发现用时", - "blocksMaturityCount": "成熟要求", - "efficiency": "效率", - "averageLuck": "平均幸运值", - "timeFound": "发现时间", - "reward": "收益", - "height": "高度", - "difficulty": "难度", - "blockHash": "区块Hash", - "effort": "Effort", - "blocksFoundLast24": "过去24小时内发现区块", - "blocksFoundLastDays": "过去{DAYS}天中发现区块", + "network": "全网", + "pool": "矿池", + "you": "用户", + "statsUpdated": "数据更新", - "payments": "支付", - "paymentsHistory": "支付历史", - "paymentsTotal": "总支付", - "paymentsMinimum": "最小支付额", - "paymentsInterval": "支付周期", - "paymentsDenomination": "面额单位", - "timeSent": "Time Sent", - "transactionHash": "交易Hash", - "amount": "数额", - "fee": "费用", - "mixin": "Mixin", - "payees": "收款人", - "pendingBalance": "处理中余额", - "totalPaid": "总计支付", - "payoutEstimate": "当前支付估计", - "paymentSummarySingle": "在%DATE%您收到%AMOUNT%", - "paymentSummaryMulti": "在%DATE%,通过%COUNT%笔支付,您收到%AMOUNT%", + "poolHashrate": "矿池算力", + "currentEffort": "当前效益", + "networkHashrate": "全网算力", + "networkDifficulty": "难度", + "blockchainHeight": "区块高度", + "networkLastReward": "上次收益", + "poolMiners": "在线矿工", + "poolFee": "矿池税", - "connectionDetails": "连接信息", - "miningPoolHost": "矿池地址", - "cnAlgorithm": "算法", - "username": "用户名", - "usernameDesc": "这是你的钱包地址", - "paymentId": "交易所支付ID", - "fixedDiff": "固定难度", - "address": "地址", - "addrPaymentId": "交易ID", - "addrDiff": "难度", - "password": "密码", - "passwordDesc": "这是你的设备名", - "emailNotifications": "邮件提醒", - "miningPorts": "挖矿端口", - "port": "端口", - "portDiff": "初始难度", - "description": "介绍", - "miningApps": "挖矿应用", - "configGeneratorDesc": "生成您定制的配置", - "addressField": "钱包地址", - "paymentIdField": "为交易所的交易ID (可选)", - "fixedDiffField": "固定难度 (可选)", - "workerNameField": "设备名", - "emailNotificationsField": "邮件提醒 (可选)", - "generateConfig": "生成配置", - "appName": "应用名", - "appArch": "架构平台", - "appDesc": "特征", - "download": "下载", - "showConfig": "了解更多", + "minerStats": "用户数据及支付历史", + "workerStats": "设备数据", + "miner": "矿工", + "miners": "矿工", + "minersCount": "矿工数", + "workers": "挖矿设备", + "workersCount": "设备数", + "workerName": "设备名", + "lastHash": "上次提交", + "hashRate": "算力", + "currentHashRate": "当前算力", + "lastShare": "上次提交", + "totalHashes": "总提交", + "top10miners": "前十矿工", - "market": "市场计算器", - "loadingMarket": "加载市场价格", - "priceIn": "价格", - "hashPer": "Hash/", - "estimateProfit": "预计挖矿收益", - "enterYourHashrate": "确认您的算力", - "perDay": "每天", + "blocksTotal": "已发现区块", + "blockSolvedTime": "区块发现用时", + "blocksMaturityCount": "成熟要求", + "efficiency": "效率", + "averageLuck": "平均幸运值", + "timeFound": "发现时间", + "reward": "收益", + "height": "高度", + "difficulty": "难度", + "blockHash": "区块Hash", + "effort": "功夫", + "blocksFoundLast24": "过去24小时内发现区块", + "blocksFoundLastDays": "过去{DAYS}天中发现区块", + "propSoloConnectedMiners": "成比例的 / 独奏 连接的 矿工", - "verificationFields": "验证字段", - "minerVerification": "为了确保钱包地址是您的,我们要求您提供您的矿工使用的IP地址之一.", - "minerAddress": "矿工地址", - "minerIP": "矿工IP地址", - "setMinimumPayout": "设置您的最低支付水平", - "minerMinPayout": "如果你喜欢比池的默认值更高的支付水平,那么这是你可以为你的矿工改变它。您在此处显示的金额将成为您的地址的最低付款金额.", - "minimumPayout": "最低付款金额", - "enableEmailNotifications": "启用电子邮件通知", - "minerEmailNotify": "当发现一个区块并且每当支付发生时,该池将发送电子邮件通知.", - "emailAddress": "邮箱地址", - "noMinerAddress": "没有指定矿工地址", - "noMinerIP": "没有指定矿工IP地址", - "noPayoutLevel": "没有指定最小付款金额", - "noEmail": "没有指定邮箱地址", - "invalidEmail": "指定的电子邮件地址无效", - "minerPayoutSet": "完成! 您的最低支出水平已设定", - "notificationEnabled": "完成! 电子邮件通知已启用", - "notificationDisabled": "完成! 电子邮件通知已被禁用", + "payments": "支付", + "paymentsHistory": "支付历史", + "paymentsTotal": "总支付", + "paymentsMinimum": "最小支付额", + "paymentsInterval": "支付周期", + "paymentsDenomination": "面额单位", + "timeSent": "Time Sent", + "transactionHash": "交易Hash", + "amount": "数额", + "fee": "费用", + "mixin": "Mixin", + "payees": "收款人", + "pendingBalance": "处理中余额", + "totalPaid": "总计支付", + "payoutEstimate": "当前支付估计", + "paymentSummarySingle": "在%DATE%您收到%AMOUNT%", + "paymentSummaryMulti": "在%DATE%,通过%COUNT%笔支付,您收到%AMOUNT%", - "enterYourAddress": "输入您的地址", - "enterYourMinerIP": "您的矿工使用的IP地址 (任一)", - "enterYourEmail": "输入你的电子邮箱地址 (可选)", + "connectionDetails": "连接信息", + "miningPoolHost": "矿池地址", + "cnAlgorithm": "算法", + "username": "用户名", + "usernameDesc": "这是你的钱包地址", + "paymentId": "交易所支付ID", + "fixedDiff": "固定难度", + "address": "地址", + "addrPaymentId": "交易ID", + "addrDiff": "难度", + "password": "密码", + "passwordDesc": "这是你的设备名", + "emailNotifications": "邮件提醒", + "miningPorts": "挖矿端口", + "port": "端口", + "portDiff": "初始难度", + "description": "介绍", + "miningApps": "挖矿应用", + "configGeneratorDesc": "生成您定制的配置", + "addressField": "钱包地址", + "paymentIdField": "为交易所的交易ID (可选)", + "fixedDiffField": "固定难度 (可选)", + "workerNameField": "设备名", + "emailNotificationsField": "邮件提醒 (可选)", + "generateConfig": "生成配置", + "appName": "应用名", + "appArch": "架构平台", + "appDesc": "特征", + "download": "下载", + "showConfig": "了解更多", - "lookup": "查找", - "searching": "搜索中...", - "loadMore": "加载更多", - "set": "设置", - "enable": "允许", - "disable": "禁止", - - "status": "状态", - "updated": "已更新:", - "source": "源:", - "error": "错误:", + "market": "市场计算器", + "loadingMarket": "加载市场价格", + "priceIn": "价格", + "hashPer": "Hash/", + "estimateProfit": "预计挖矿收益", + "enterYourHashrate": "确认您的算力", + "perDay": "每天", - "na": "无", - "estimated": "预计", - "never": "从不", - "second": "秒", - "seconds": "秒", - "minute": "分", - "minutes": "分", - "hour": "小时", - "hours": "小时", - "day": "天", - "days": "天", - "week": "周", - "weeks": "周", - "month": "月", - "months": "月", - "year": "年", - "years": "年", + "verificationFields": "验证字段", + "minerVerification": "为了确保钱包地址是您的,我们要求您提供您的矿工使用的IP地址之一.", + "minerAddress": "矿工地址", + "minerIP": "矿工IP地址", + "setMinimumPayout": "设置您的最低支付水平", + "minerMinPayout": "如果你喜欢比池的默认值更高的支付水平,那么这是你可以为你的矿工改变它。您在此处显示的金额将成为您的地址的最低付款金额.", + "minimumPayout": "最低付款金额", + "enableEmailNotifications": "启用电子邮件通知", + "minerEmailNotify": "当发现一个区块并且每当支付发生时,该池将发送电子邮件通知.", + "emailAddress": "邮箱地址", + "noMinerAddress": "没有指定矿工地址", + "noMinerIP": "没有指定矿工IP地址", + "noPayoutLevel": "没有指定最小付款金额", + "noEmail": "没有指定邮箱地址", + "invalidEmail": "指定的电子邮件地址无效", + "minerPayoutSet": "完成! 您的最低支出水平已设定", + "notificationEnabled": "完成! 电子邮件通知已启用", + "notificationDisabled": "完成! 电子邮件通知已被禁用", - "poweredBy": "驱动源于", - "openSource": "开源于" + "enterYourAddress": "输入您的地址", + "enterYourMinerIP": "您的矿工使用的IP地址 (任一)", + "enterYourEmail": "输入你的电子邮箱地址 (可选)", + + "lookup": "查找", + "searching": "搜索中...", + "loadMore": "加载更多", + "set": "设置", + "enable": "允许", + "disable": "禁止", + + "status": "状态", + "updated": "已更新:", + "source": "源:", + "error": "错误:", + + "na": "无", + "estimated": "预计", + "never": "从不", + "second": "秒", + "seconds": "秒", + "minute": "分", + "minutes": "分", + "hour": "小时", + "hours": "小时", + "day": "天", + "days": "天", + "week": "周", + "weeks": "周", + "month": "月", + "months": "月", + "year": "年", + "years": "年", + + "poweredBy": "驱动源于", + "openSource": "开源于" } diff --git a/website_example/pages/admin/monitoring.html b/website_example/pages/admin/monitoring.html index 5e0707d87..cbfd1d49d 100644 --- a/website_example/pages/admin/monitoring.html +++ b/website_example/pages/admin/monitoring.html @@ -1,10 +1,21 @@ + + + +
+
+ + + - -
-
-
-
-
- diff --git a/website_example/pages/admin/ports.html b/website_example/pages/admin/ports.html index 04b7a7f25..1867d6dfa 100644 --- a/website_example/pages/admin/ports.html +++ b/website_example/pages/admin/ports.html @@ -2,84 +2,89 @@

Current Ports Usage (0 users)

-
- - - - - - - - - - -
PortConnected Users 
-
+
+ + + + + + + + + + +
PortConnected Users 
+
+ $.ajax({ + url: api + '/admin_ports', + data: { + password: password + }, + cache: false, + dataType: 'json', + success: function (data) { + docCookies.setItem('password', password, Infinity); + renderTemplate(parsePorts(data), '#portsListTable', '#template'); + }, + error: function (e) { + docCookies.removeItem('password'); + } + }); + } + $(function () { + $('[data-toggle="tooltip"]') + .tooltip(); + createPortsTable(); + }); + diff --git a/website_example/pages/admin/statistics.html b/website_example/pages/admin/statistics.html index e1fdc9d73..fde9ba0b4 100644 --- a/website_example/pages/admin/statistics.html +++ b/website_example/pages/admin/statistics.html @@ -1,170 +1,221 @@

Pool Statistics

- -
- - -
-
-
- -
-
-
Total Mined
-
...
+
+
+ + diff --git a/website_example/pages/admin/tools.html b/website_example/pages/admin/tools.html index ae54c36a8..b1ccd3247 100644 --- a/website_example/pages/admin/tools.html +++ b/website_example/pages/admin/tools.html @@ -1,124 +1,137 @@ -

Test E-Mail notifications

+

Test E-Mail notifications

-
- -
-
-
- -
-
- -
-
+
+ +
+
+
+ +
+
+ +
+
-

Test Telegram Channel notifications

+

Test Telegram Channel notifications

-
- -
-
-
- -
-
+
+ +
+
+
+ +
+
diff --git a/website_example/pages/admin/userslist.html b/website_example/pages/admin/userslist.html index 7bb6ce5a1..8b6a155a1 100644 --- a/website_example/pages/admin/userslist.html +++ b/website_example/pages/admin/userslist.html @@ -1,96 +1,153 @@ -

Users List (0 users)

- - - - -
-
- - - - - - - - - - - - - - -
WalletHashrate Hashes Pending Paid Last share
+ - diff --git a/website_example/pages/faq.html b/website_example/pages/faq.html index cd3100db4..1ea081c38 100644 --- a/website_example/pages/faq.html +++ b/website_example/pages/faq.html @@ -1,78 +1,150 @@

Frequently Asked Questions (FAQ)

-

What is difficulty?

-
Difficulty is a measure of how difficult it is to find a hash below a given target.
+

How do I pick which coins to merge mine?

+
+

The hash work of your miner application to the parent coin is also applied to the child coin you specify in the mining software pool password field.

+ +

You must mine at one parent coin, which in this case is ARQ for ArQmA. + Specify your ArQmA wallet address and static difficulty per the Getting Started page. + Using the pool password field in your miner to specify one of the child coin wallet addresses. See which child coins are listed on the pool's Dashboard page. + You will need at least a wallet address from an exchange or a wallet/paper wallet application to get an coin address for the parent coin and each child coin you wish to merge mine.

+ +

You can also connect multiple rigs with the same wallet address for the parent coin, and different child coin wallet address to apply hash to that child coin also.

+ +

Specify your rig_id in your miner software.

+

Put your child coin wallet address in the pool password field and put "@rig_id" at the end. + It can be a direct to exchange address also.

+

For example in XMR-STAK, the pools.txt config file looks something like this: + "rig_id" : "Johnny5", + An example wallet for Turtle child coin is "TRTL2aedfsr23blahlongaddressfield+paymentIDsomething" + so make it formatted for the pool password as + "pool_password" : "TRTL2aedfsr23blahlongaddressfield+paymentIDsomething@Johnny5"

+ +

You can also use the configuration generator within the Getting Started section of the pool.

+

Also, you can segment your cpu mining on one mining application to be applied to + the parent coin and specify your hash work one of the several child coins. + There is no limit on how many workers you can specify. You can also customize your miner application to mine only with certain gpus to a particular parent/child combination. Probably you can run several mining applications in tandem as long as they use only resources you specify.

-

What is luck?

-
Mining is probabilistic in nature: if you find a block earlier than you statistically should on average you are lucky if it takes longer, you are unlucky. In a perfect World pool would find a block on 100% luck value. Less then 100% means the pool was lucky. More then 100% means the pool was unlucky.
- -

What is share?

-
Share is a possible valid hash for the block. Shares are beings sent by your rigs to the pool to prove their work.
- -

What is block?

-
Transaction data is recorded in blocks. New transactions are being processes by miners into new blocks which are added to the end of the blockchain.
- -

How long does it take to find a block?

-
It depends on amount of active miners. The more miners work on pool → the more hashrate pool has → the more blocks are found by the pool. However the more miners are active → the less reward you get from each block found.
- -

Which payouts scheme is used?

-
Proportional (Share-based): Every time a block is found, its reward is split between miners according to the number of shares they submitted.
- -

How current payout estimate is calculated?

-
The estimated payout is a calculated using your percentage of valid shares on the total for current round. This percentage is then applied to the reward of the last block found by the network.
- -

I have been mining on this pool for 1 hour but still have not received any payouts. WTF?

-
As soon as the block is found you will get your reward. Please wait a little bit more time.
- -

My hashrate is wrong! Why?

-
Since you start to mine your hashrate grows gradually. Please wait. The pool determines your hashrate based on the amount of shares sent by your mining rigs (workers). This value could be a little bit different from reported hasrate (in your mining software).
+
-
-

How to use the Telegram Bot?

-
-
Telegram bot will report via Telegram, when your worker is connected, disconnected or banned, and payments have been made to your wallet. Also you can check pool stats directly from him.
-
+

Where are the stats for the pool? Why hasn't anything changed?

+
The dashboard lists the coins being mined, reward for each block, current effort on each block for the merged mining. Sometimes the Pool Blocks section and Worker Statistics needs a refresh by your browser to get updated stats. Typically that is pressing CTRL+F5 to flush the cache and ask your brower for fresh data.
- Bot name: @Pool_bot
-
- Commands:
- /stats - Pool statistics
- /blocks - Blocks notifications (enable or disable)
- /report address - Miner statistics
- /notify address - Miner notifications (enable or disable)
-
+

What is difficulty?

+
Difficulty is a measure of how difficult it is to find a hash below a given target. The target difficulty changes from block to block based on the network hashrate attempted by all nodes on a coin network. However, the difficulty for child coins can be dissimlar to the parent coin you start mining with
- Multiple addresses monitoring available.
-
+

What is luck?

+
Mining is probabilistic in nature: if you find a block earlier than you statistically should on average you are lucky if it takes longer, you are unlucky. In a perfect World pool would find a block on 100% luck value. Less then 100% means the pool was lucky. More then 100% means the pool was unlucky.
- - \ No newline at end of file +

What is share?

+
Share is a possible valid hash for the block. Shares are beings sent by your rigs to the pool to prove their work. You should set your fixed difficulty to be somewhere around your hash rate average on your miner multiplied by 28-30. Aim for your average number of accepted shares to be within the block time (30 seconds typically for child coins, 2 minutes for ARQ) + If your shares are accepted faster, but with lower difficulty each share is worth less, but you should get at least one share in per average block time. It doesn't help the pool by spamming a large amount of low difficulty shares. It tends to tie up more resources and might get you banned.
+ +

What is block?

+
Transaction data is recorded in blocks. New transactions are being processes by miners into new blocks which are added to the end of the blockchain.
+ +

How long does it take to find a block?

+
It depends on amount of active miners. The more miners work on pool → the more hashrate pool has → the more blocks are found by the pool. However the more miners are active → the less reward you get from each block found. + Probablistically, even smaller pools will less miners or hash rate will eventually get a block. + Decentralizing the hash proof of work across several pools on any network is important for security of the coin network, for both the parent coin and the child coins. + The block time is how long it can take on average amount effort . The average for ArQmA is 120 seconds. However, some of the child coins have block timeframes on the average of 15 to 30 seconds. Check your child coins network specification. Some blocks are solved faster than the average block time, some blocks can have a higher than average block effort time. + Also some other pools might solve the block first, and there is a slim possibility of an orphan block on a child coin being mined.
+ +

What is an orphan block?

+
An orphan is a block that didn't meet the requirement as a solution to the block found. Also, there can be a situation where another pool or solo miner found the block solution first, and narrowly won the race. Blocks are confirmed by hash verfication by all the nodes on the coin's network. If there is a consensus of which node(pool,solo) found it first, then that block is added to the block chain permanenty with credit applying to that pool/solo/node. + A few orphan blocks on child coins can happen when the parent coin's hash work doesn't meet the requirements.
+ + +

Which payouts scheme is used?

+
Proportional (Share-based): Every time a block is found, its reward is split between miners according to the number of shares they submitted.
+ +

How current payout estimate is calculated?

+
The estimated payout is a calculated using your percentage of valid shares on the total for current round in proportion to the total shares submitted by all miners/workers in that round. This percentage is then applied to the reward of the last block found by the network and subtracting the small pool fee.. See the Worker Statistics page on the pool web page. +
+ +

I have been mining on this pool for 1 hour but still have not received any payouts. WTF?

+
+

As soon as the block is found you will get your reward. Please wait a little bit more time. In the case of ArQmA, a block has at least 18 confirmations before the pool starts its payout cycle. Typically every 36 minutes for a completed block plus the time interval set by the pool operator on the pool's Dashboard screeen. Payments can be halted for various reasons within the automated pool software, but highly unlikely. There is usually a minimum payout for each parent coin and child coins. Payments for matured blocks that meet the minimum can also be triggered manually by a pool operator/administrator. Ask nicely in Telegram or Discord support. Please have your information all in order before you ask such as miner application, version number, gpu and cpu information, wallet addresses, and error messages reported. +

+

The merge mined child coins have a different number of confirmation blocks needed, but at least the pool checks for fully verified blocks set at the interval on the Dashboard page. Not every parent block found generates a child block. + Check the Pool Blocks page on the pool. Or configure your settings to get an email alert if you like on the Settings pool page. +

+
+ +

My hashrate is wrong! Why?

+
Since you start to mine your hashrate grows gradually. Please wait. The pool determines your hashrate based on the amount of shares sent by your mining rigs (workers). This value could be a little bit different from reported hasrate (in your mining software). +
+ +

What is solo mining?

+
+

Solo mining is attempting to find a block by yourself separate from the pool of proportional miners collectively solving the current block.You get almost the entire block reward minus a small fee for the pool. You can specfy your mining software by changing the wallet address with a prefix of "solo:" added to your wallet specification. + However, if the coin network has a high hash in total (see Dashboard), the difficulty is also higher. Typically only very large mining rigs with at least 5-10% of the coin network total is needed to get timely solo blocks. However, you could get a string of blocks on lucky streak, then not find a block solution for a very long time. The more hash rate, the higher the probability(luck). You can generate a solo configuration on the Getting Started page on the pool. Tick the checkbox "solo mining". However, as solo you are also competing against the pool proportional share miners, and evvery single node on the parent coin network also. +

+

In the case of solo merged mining specified in the pool password field, you are also competing against the main pool users on proportional shares, and the entire child coin network and the other pools on the parent network that are also merge mining. However the reward is much higher since you might get two coin blocks(or more) for the same effort. +

+
+ +

I've been banned by the pool itself. What do I do now?

+
+

Disconnect your miner for five minutes at least. Then try again. Please pay close attention to error messages reported by the pool software to your miner client software. Typical errors are invalid parent wallet address format, or bad child coin wallet address specified in the pool password field. Check your miner logs if you have them turned on. +

+

Did you forget to turn off nicehash option?

+

For persistent banning, check with the pool operator or Admininstrator to see if there is something else going wrong outside of your control. Links for Telegram and Discord support are on the left side of the pool web page front end.

+
+ + +
+

How to use the Telegram Bot?

+
+
Telegram bot will report via Telegram, when your worker is connected, disconnected or banned, and payments have been made to your wallet. Also you can check pool stats directly from him.
+
+ + Bot name: @Pool_bot
+
+ + Commands:
+ /stats - Pool statistics
+ /blocks - Blocks notifications (enable or disable)
+ /report address - Miner statistics
+ /notify address - Miner notifications (enable or disable)
+
+ + Multiple addresses monitoring available. +
+
+
+ + + diff --git a/website_example/pages/getting_started.html b/website_example/pages/getting_started.html index 2a40f0e71..d75b505ce 100644 --- a/website_example/pages/getting_started.html +++ b/website_example/pages/getting_started.html @@ -1,134 +1,156 @@
-
-

Connection Details

-
-
-
Mining Pool Address:
-
Algorithm:
-
-
- -

Username

-
-
-
This is your wallet address
-
Exchange Payment ID: address.paymentID
-
Difficulty locking: address+diff
-
-
- -

Password

-
-
-
This is your worker name
-
-
-
- - -
-

Mining Ports

-
-
- - - - - - - - - - - - - - - -
PortStarting DifficultyDescription
-
-
-
- +
+

Connection Details

+
+
+
Mining Pool Address:
+
Algorithm:
+
+
+ +

Username

+
+
+
This is your wallet address
+
Exchange Payment ID: parent address.paymentID
+
Difficulty locking: parent address+diff
+
+
+ +

Password

+
+
+
Use your Iridium OR Nibble wallet address in the password field for merged mining.
+
Exchange Payment ID: child address+paymentID
+
Worker Name: child-address@workerName
+
Payment ID and Worker Name: child-address+paymentID@workerName
+
+
+
+ + +
+

Mining Ports

+
+
+ + + + + + + + + + + + + + + +
PortStarting DifficultyDescription
+
+
+
+
-

Mining Applications

+

Mining Applications

-

Generate your custom configuration to mine on our pool

-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
+

Generate your custom configuration to mine on our pool

+
+
+
+ + +
+
+
+
+ Solo Mining + + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
-
- - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
App Name Architecture Features Download Configuration
XMR StakCPU & GPU (AMD/NVIDIA)Easy to use CPU + GPU Mining AppDownloadSee more
-
- -
"pool_list": [
+	
+ + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - -
App Name Architecture Features Download Configuration
XMR StakCPU & GPU (AMD/NVIDIA)Easy to use CPU + GPU Mining AppDownloadSee more
+
+ +
"pool_list": [
     {
         "pool_address": "POOL_HOST:PORT",
         "wallet_address": "YOUR_WALLET_ADDRESS",
-        "rig_id": "YOUR_WORKER_NAME",
-        "pool_password": "x",
+        "rig_id": "",
+        "pool_password": /* Solo Mining set [solo:] */"YOUR_WORKER_NAME",
         "use_nicehash": false,
         "use_tls": false, /* Set to true if you are using an SSL port */
         "tls_fingerprint": "",
@@ -137,328 +159,345 @@ 

Mining Applications

], "currency": "",
-
-
XMRigCPULigthweight but powerful CPU Mining AppDownloadSee more
-
- -
"pools": [
+						
+
XMRigCPULigthweight but powerful CPU Mining AppDownloadSee more
+
+ +
"pools": [
     {
         "url": "POOL_HOST:PORT",
         "user": "YOUR_WALLET_ADDRESS",
         "pass": "YOUR_WORKER_NAME",
         "keepalive": true,
         "nicehash": false,
-        "variant": 1
+        "variant": "trtl",
+        "algo": "cryptonight-pico/trtl"
     }
 ],
-
-
XMRig-AMDOpenCL (AMD)XMRIG version for AMD GPUDownloadSee more
-
- -
"pools": [
+						
+
XMRig-AMDOpenCL (AMD)XMRIG version for AMD GPUDownloadSee more
+
+ +
"pools": [
     {
         "url": "POOL_HOST:PORT",
         "user": "YOUR_WALLET_ADDRESS",
         "pass": "YOUR_WORKER_NAME"
         "keepalive": true,
         "nicehash": false,
-        "variant": 1
+        "variant": "trtl",
+        "algo": "cryptonight-pico/trtl"
     }
 ],
-
-
XMRig-NVIDIACuda (Nvidia)XMRIG version for Nvidia GPUDownloadSee more
-
- -
"pools": [
+						
+
XMRig-NVIDIACuda (Nvidia)XMRIG version for Nvidia GPUDownloadSee more
+
+ +
"pools": [
     {
         "url": "POOL_HOST:PORT",
         "user": "YOUR_WALLET_ADDRESS",
         "pass": "YOUR_WORKER_NAME"
         "keepalive": true,
-        "nicehash": false,
-        "variant": 1
+        "nicehash": false
+        "variant": "trtl",
+        "algo": "cryptonight-pico/trtl"
     }
 ],
-
-
XMRigCCCPUXMRIG Fork, optimized with remote controlDownloadSee more
-
- -
"pools": [
+						
+
XMRigCCCPUXMRIG Fork, optimized with remote controlDownloadSee more
+
+ +
"pools": [
     {
         "url": "POOL_HOST:PORT",
         "user": "YOUR_WALLET_ADDRESS",
         "pass": "YOUR_WORKER_NAME",
         "keepalive": true,
-        "nicehash": false
+        "nicehash": false,
+        "algo": "cryptonight-ultralite",
+        "pow-variant": "2"
     },
 ],
-
-
SRBMiner Cryptonight AMD GPU MinerOpenCL (AMD)XMRIG version for AMD GPUDownloadSee more
-
- -

-{
-/* Type can be : normal, lite, heavy  */
-"cryptonight_type" : "heavy",
-
-/* Intensity 0-> auto intensity, or value from 1-200  */
-"intensity" : 0,
-
-/* To use 2 threads per card set double_threads to true  */
-"double_threads" : false,
-
-/* Gpu target temperature, leave it on 0 if you don't want to use this option  */
-"target_temperature" : 0,
-
-/* If you use a SSL/TLS encrypted pool set this to true*/
-"pool_use_tls" : false,
-
-/* Mining pool address WITHOUT the stratum+tcp:// or stratum+tls:// part  */
-"pool" : "POOL_HOST:PORT",
-
-/* Address of you wallet  */
-"wallet" : "YOUR_WALLET_ADDRESS",
-
-/* Password for your wallet, probably just x  */
-"password" : "YOUR_WORKER_NAME",
-
-/* Location for devfee servers, to get better latency  */
-"location" : "europe",
-
-/* If you want to log console output, put a filename here  */
-"log_file" : "",
-
-/* Charity mining, if you want to disable charity mining just delete these lines, or set values to "" (empty) */
-"charity_pool" : "POOL_HOST:PORT",
-"charity_wallet" : "YOUR_WALLET_ADDRESS",
-"charity_password" : "YOUR_WORKER_NAME",
-
-/* Settings for each GPU manually */
-/* Put in devices that you want to use, if you for ex. don't want to use gpu 2, just don't insert it,like in this example */
-/* Id starts from 0 , not from 1 !! */
-/* To get a list of available devices with their id's, use --listdevices parameter */
-/* This is just an example, edit it and remove comment lines (the slash and star) !! */
-/*"gpu_conf" : 
-[ 
-	{ "id" : 0, "intensity" : 0, "double_threads" : true},
-	{ "id" : 1, "intensity" : 60, "double_threads" : false},
-	{ "id" : 3, "intensity" : 50, "double_threads" : true},
-	{ "id" : 4, "intensity" : 0, "double_threads" : true},
-]*/
-}		    
-	    
-            
-
+
+
Team Red MinerGPUAMD GPU Optimized Cryptocurrency MinerDownloadSee more
+
+
teamredminer.ext -a cnv8 trtle -o stratum+tcp//POOL_HOST:PORT -u "YOUR_WALLET_ADDRESS" -p "YOUR_WORKER_NAME" 
+
+ +
+
+
diff --git a/website_example/pages/home.html b/website_example/pages/home.html index a32d22a44..e200b7c18 100644 --- a/website_example/pages/home.html +++ b/website_example/pages/home.html @@ -1,391 +1,316 @@ -
-
+
+
+
+
- -
-
-
- -
-
-
Pool Hash Rate
-
N/A (0%)
-
-
-
- - -
-
-
- -
-
-
Blocks Found
-
N/A (Never)
-
-
-
- - -
-
-
- -
-
-
Blocks Found Every
-
N/A (estimated)
-
-
-
- - -
-
-
- -
-
-
Current Effort
-
N/A
-
-
-
- - -
-
-
- -
-
-
Network Hash Rate
-
N/A
-
-
-
- - -
-
-
- -
-
-
Difficulty
-
N/A
-
-
-
- - -
-
-
- -
-
-
Blockchain Height
-
N/A
-
-
-
+ + + + +
-
-
-

Hash Rate

-
- - -
-
-
-
-
-

Difficulty

-
- - -
-
-
-
-
-

Miners

-
- - -
-
-
-
-
-

Workers

-
- - -
-
-
+
+

Hash Rate

+
+
+
+
+
+

Difficulty

+
+
+
+
+
+

Miners

+
+
+
+
+
+

Workers

+
+
+
+
diff --git a/website_example/pages/market.html b/website_example/pages/market.html index c4bdb3bdd..2a54947d8 100644 --- a/website_example/pages/market.html +++ b/website_example/pages/market.html @@ -1,339 +1,164 @@ - -
-
Loading market prices ...
+ -
-
-

Price in USD

-
- - -
-
-
-
-
-

Hash/USD

-
- - -
-
-
+
+
+ +
- - -
-

Estimate Mining Profits

-
-
- -
- - + + + + + + + + + -
\ No newline at end of file + // Market settings + let priceSource = 'cryptonator'; + let priceCurrency = 'USD'; + + if (typeof cryptonatorWidget !== 'undefined' && typeof marketCurrencies === 'undefined') { + let marketCurrencies = cryptonatorWidget; + } + + // Charts initialized + let chartsInitialized = {} + let loadedData = {} + let intervalChartsUpdate = {} + let ranOnce = false + let xhrMarketGets = {} + let marketPrices = {} + let currencyPairs = {} + // Update current page + currentPage = { + destroy: function () { + if (chartsInitialized) { + Object.keys(chartsInitialized) + .forEach(key => { + chartsInitialized[key] = false; + clearInterval(intervalChartsUpdate[key]); + }) + } + if (xhrMarketGets) { + Object.keys(xhrMarketGets) + .forEach(key => { + if (xhrMarketGets[key]) + xhrMarketGets[key].abort(); + }) + } + + $('#blocksTabs a') + .off('click') + $('#calcHashUnits > li > a') + .off('click') + $('#calcHashRate') + .off('keyup') + }, + update: function (updateKey) { + + } + }; + + function RunOnce () { + + /** + * Hash Profitability Calculator + **/ + + // Automatically update profit calculation on key press + $('#calcHashRate') + .keyup((e) => { + market_CalcEstimateProfit(marketPrices) + }) + .change((e) => { + market_CalcEstimateProfit(marketPrices) + }) + + + // Click on button + $('#calcHashUnits > li > a') + .click(function (e) { + e.preventDefault(); + $('#calcHashUnit') + .text($(this) + .text()) + .data('mul', $(this) + .data('mul')); + market_CalcEstimateProfit(marketPrices); + }); + + $('#blocksTabs a') + .click(function (e) { + e.preventDefault() + $(this) + .tab('show') + }) + return true + } + + // Handle tooltip + $(function () { + $('[data-toggle="tooltip"]') + .tooltip(); + }); + + + market_InitTemplate(ranOnce, chartsInitialized, loadedData, marketPrices, intervalChartsUpdate, currencyPairs, xhrMarketGets) + + diff --git a/website_example/pages/payments.html b/website_example/pages/payments.html index f890e8eaf..4128ae327 100644 --- a/website_example/pages/payments.html +++ b/website_example/pages/payments.html @@ -1,200 +1,141 @@ - -
+ - -
-
-
- -
-
-
Total Payments
-
N/A (N/A miners)
-
-
-
- - -
-
-
- -
-
-
Minimum Payout
-
N/A
-
-
-
- - -
-
-
- -
-
-
Payment Interval
-
N/A
-
-
-
- - -
-
-
- -
-
-
Denomination Unit
-
N/A
-
-
-
- - -
-
-
Payment system
-
-

This pool uses the "Score" payment system, under which later - shares submitted by miners receive more weight in determining the miner's reward than earlier shares. - -

For more details (including the specific mathematics involved) see the slushpool reward - description.

-
-
-
-
- +
- -
-
- - - - - - - - - - - - - - -
Time SentTransaction HashAmountFeeMixinPayees
-
-
+ -

- -

+ diff --git a/website_example/pages/pool_blocks.html b/website_example/pages/pool_blocks.html index 6e595b7f6..252e89828 100644 --- a/website_example/pages/pool_blocks.html +++ b/website_example/pages/pool_blocks.html @@ -1,314 +1,152 @@ - -
+ - -
-
-
- +
+
+ + + + diff --git a/website_example/pages/settings.html b/website_example/pages/settings.html index 2acdf0b54..1c037d8a5 100644 --- a/website_example/pages/settings.html +++ b/website_example/pages/settings.html @@ -1,254 +1,88 @@ - -

Verification fields

+ -
-

In order to get a little more confidence that the wallet address is yours we ask you to give one of the IP addresses that is used by your miner.

-
- -
-
- -
- -
-
-
- -
- -
-
+
- -

Set your minimal payout level

-
-

If you prefer a higher payout level than the pool's default then this is where you can change it for your miners. The amount you indicate here will become the minimum amount for pool payments to your address.

-
- -
- -
-
- -
-
-
+ + + diff --git a/website_example/pages/top10miners.html b/website_example/pages/top10miners.html index f8f365145..83ccd178e 100644 --- a/website_example/pages/top10miners.html +++ b/website_example/pages/top10miners.html @@ -1,68 +1,74 @@ -

Top 10 miners

-
-
- - - - - - - - - - - - -
#MinerHash RateLast Share SubmittedTotal Hashes Submitted
-
+

Top 10 miners

+ + + +
+ + + + \ No newline at end of file + diff --git a/website_example/pages/worker_stats.html b/website_example/pages/worker_stats.html index b9a3f3566..596400634 100644 --- a/website_example/pages/worker_stats.html +++ b/website_example/pages/worker_stats.html @@ -1,624 +1,216 @@ -

Your Stats & Payment History

+

Your Stats & Payment History

-
-
- - -
-
+ + +
+
+ + - -
-

  Hash Rate

-
-
-
Current Hash Rate:
-
Average 1/6/24-hour Hash Rate: - / /
-
Last Share Submitted:
-
Total Hashes Submitted:
+ diff --git a/website_example/safari-pinned-tab.svg b/website_example/safari-pinned-tab.svg new file mode 100644 index 000000000..5b362c28b --- /dev/null +++ b/website_example/safari-pinned-tab.svg @@ -0,0 +1,15 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/website_example/site.webmanifest b/website_example/site.webmanifest new file mode 100644 index 000000000..a1553eb86 --- /dev/null +++ b/website_example/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-384x384.png", + "sizes": "384x384", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/website_example/themes/admin.css b/website_example/themes/admin.css index 4d03fbb64..773f64108 100644 --- a/website_example/themes/admin.css +++ b/website_example/themes/admin.css @@ -1,103 +1,122 @@ #page-wrapper { - padding: 0 15px 0; - min-height: 568px; + padding: 0 15px 0; + min-height: 568px; } + @media (min-width: 992px) { - #page-wrapper { - padding: 0 30px; - } + #page-wrapper { + padding: 0 30px; + } } .nav-side-menu li.sign-out { - border-left: 3px solid darkred; - background-color: rgba(255,0,0,.2); + border-left: 3px solid darkred; + background-color: rgba(255, 0, 0, .2); } .nav-side-menu li.sign-out:hover { - border-left: 3px solid darkred; - background-color: darkred; + border-left: 3px solid darkred; + background-color: darkred; } /* Pool Statistics */ #poolStats .luckGood { - color: #5eff5e; + color: #5eff5e; } + #poolStats .luckBad { - color: red; + color: red; } + #poolStats .luckMid { - color: #FFF500; + color: #FFF500; } /* Users list */ .usersList { - word-wrap: break-word; + word-wrap: break-word; } + .usersList .tooltip-inner { - max-width: 100%; + max-width: 100%; } + @media (min-width: 768px) { - .usersList { - table-layout: fixed; - } - .usersList tr > th, - .usersList tr > td { - text-align: center; - white-space: nowrap; - } - .usersList .col1 { - white-space: normal; - } - .usersList .col2 { - width: 100px; - } - .usersList .col3 { - width: 100px; - } - .usersList .col4 { - width: 100px; - } - .usersList .col5 { - width: 100px; - } - .usersList .col6 { - width: 120px; - white-space: normal; - } + .usersList { + table-layout: fixed; + } + + .usersList tr>th, + .usersList tr>td { + text-align: center; + white-space: nowrap; + } + + .usersList .col1 { + white-space: normal; + } + + .usersList .col2 { + width: 100px; + } + + .usersList .col3 { + width: 100px; + } + + .usersList .col4 { + width: 100px; + } + + .usersList .col5 { + width: 100px; + } + + .usersList .col6 { + width: 120px; + white-space: normal; + } } /* Monitoring */ .adminMonitor code { - white-space: normal; + white-space: normal; } + .adminMonitor .tab-pane li { - margin-bottom: 10px; + margin-bottom: 10px; } + .adminMonitor .infos { - margin-bottom: 20px; + margin-bottom: 20px; } + .adminMonitor #logTable th { - white-space: nowrap; + white-space: nowrap; } /* Ports Usage */ -#portsUsage tr > td { - vertical-align: middle; - font-size: 0.95em; +#portsUsage tr>td { + vertical-align: middle; + font-size: 0.95em; } + #portsUsage table .col1, #portsUsage table .col2 { - text-align: center; -} + text-align: center; +} + @media (min-width: 768px) { - #portsUsage table th { - white-space: nowrap; - } - #portsUsage table .col1 { - width:60px; - } - #portsUsage table .col2 { - width:160px; - text-align: center; - } + #portsUsage table th { + white-space: nowrap; + } + + #portsUsage table .col1 { + width: 60px; + } + + #portsUsage table .col2 { + width: 160px; + text-align: center; + } } diff --git a/website_example/themes/blackout.css b/website_example/themes/blackout.css new file mode 100644 index 000000000..db3247ff7 --- /dev/null +++ b/website_example/themes/blackout.css @@ -0,0 +1,1363 @@ +@import url(//fonts.googleapis.com/css?family=Roboto+Condensed:400,700); +@import url(//fonts.googleapis.com/css?family=Roboto:400,300,500,700); +@import url(//fonts.googleapis.com/css?family=Inconsolata:400,700); + +body { + background-color: #000; + font-family: 'Roboto', Helvetica, Arial, sans-serif; + line-height: 1.428571429; + color: #262626; + margin: 0; + padding: 0; + margin-bottom: 85px; + font-size: 16px; + overflow-y: scroll; +} + +a { + color: #014e71; +} + +a:hover { + color: #0274a8; +} + +hr { + border-top-color: #0274a8; +} + +strong, +b { + font-weight: 500; +} + +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-weight: 400; + -webkit-font-smoothing: antialiased; + font-family: 'Roboto Condensed', Arial, sans-serif; +} + +h3, +.h3 { + font-size: 32px; + margin-bottom: 14px; +} + +code { + color: #0274a8; + background-color: #efefef; + padding: 2px 10px; + border-radius: 0; +} + +.padding-5 { + padding: 5px !important; +} + +.padding-10 { + padding: 10px !important; +} + +.padding-15 { + padding: 15px !important; +} + +.padding-20 { + padding: 20px !important; +} + +.padding-25 { + padding: 25px !important; +} + +.padding-30 { + padding: 30px !important; +} + +.padding-t-5 { + padding-top: 5px !important; +} + +.padding-t-10 { + padding-top: 10px !important; +} + +.padding-t-15 { + padding-top: 15px !important; +} + +.padding-t-20 { + padding-top: 20px !important; +} + +.padding-t-25 { + padding-top: 25px !important; +} + +.padding-t-30 { + padding-top: 30px !important; +} + +.padding-b-5 { + padding-bottom: 5px !important; +} + +.padding-b-10 { + padding-bottom: 10px !important; +} + +.padding-b-15 { + padding-bottom: 15px !important; +} + +.padding-b-20 { + padding-bottom: 20px !important; +} + +.padding-b-25 { + padding-bottom: 25px !important; +} + +.padding-b-30 { + padding-bottom: 30px !important; +} + +.padding-l-5 { + padding-left: 5px !important; +} + +.padding-l-10 { + padding-left: 10px !important; +} + +.padding-l-15 { + padding-left: 15px !important; +} + +.padding-l-20 { + padding-left: 20px !important; +} + +.padding-l-25 { + padding-left: 25px !important; +} + +.padding-l-30 { + padding-left: 30px !important; +} + +.padding-r-5 { + padding-right: 5px !important; +} + +.padding-r-10 { + padding-right: 10px !important; +} + +.padding-r-15 { + padding-right: 15px !important; +} + +.padding-r-20 { + padding-right: 20px !important; +} + +.padding-r-25 { + padding-right: 25px !important; +} + +.padding-r-30 { + padding-right: 30px !important; +} + +.push-up-5 { + margin-top: 5px !important; +} + +.push-up-10 { + margin-top: 10px !important; +} + +.push-up-15 { + margin-top: 15px !important; +} + +.push-up-20 { + margin-top: 20px !important; +} + +.push-up-25 { + margin-top: 25px !important; +} + +.push-up-30 { + margin-top: 30px !important; +} + +.push-down-5 { + margin-bottom: 5px !important; +} + +.push-down-10 { + margin-bottom: 10px !important; +} + +.push-down-15 { + margin-bottom: 15px !important; +} + +.push-down-20 { + margin-bottom: 20px !important; +} + +.push-down-25 { + margin-bottom: 25px !important; +} + +.push-down-30 { + margin-bottom: 30px !important; +} + +/* Footer */ +footer { + position: fixed; + bottom: 0; + width: 100%; + background-color: #000; + font-size: 14px; + color: #fff; + z-index: 9999; +} + +footer a { + color: #6ED5EE; +} + +footer>div { + color: #fff; + margin: 10px auto; + text-align: center; +} + +/* Wrappers */ +#wrapper { + width: 100%; +} + +#page-wrapper { + padding: 0 15px; + min-height: 568px; +} + +@media (min-width: 992px) { + #page-wrapper { + position: inherit; + margin: 0 0 0 225px; + padding: 60px 30px 0 30px; + } +} + +#loading { + font-size: 2em; +} + +/* Sidebar */ +.nav-side-menu { + overflow: auto; + font-size: 16px; + font-weight: 200; + background-color: rgba(0, 0, 0, .8); + position: fixed; + top: 0; + width: 225px; + height: 100%; + color: #fff; + z-index: 1000; +} + +.nav-side-menu .brand { + background-color: #000; + line-height: 50px; + display: block; + text-align: center; + font-size: 20px; + color: #fff; + font-weight: 400; +} + +.nav-side-menu .brand a { + color: #fff; + text-decoration: none; +} + +.nav-side-menu .toggle-btn { + display: none; +} + +.nav-side-menu ul, +.nav-side-menu li { + list-style: none; + padding: 0; + margin: 0; + line-height: 35px; + cursor: pointer; +} + +.nav-side-menu ul :not(collapsed) .arrow:before, +.nav-side-menu li :not(collapsed) .arrow:before { + font-family: FontAwesome; + content: "\f078"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; + float: right; +} + +.nav-side-menu ul .active, +.nav-side-menu li .active { + border-left: 3px solid #fff; + background-color: #191919; +} + +.nav-side-menu ul .active a, +.nav-side-menu li .active a { + color: #fff; +} + +.nav-side-menu ul .sub-menu li.active, +.nav-side-menu li .sub-menu li.active { + color: #03a9f4; +} + +.nav-side-menu ul .sub-menu li a, +.nav-side-menu li .sub-menu li a { + display: inline-block; + width: 85%; +} + +.nav-side-menu ul .sub-menu li.active a, +.nav-side-menu li .sub-menu li.active a { + color: #03a9f4; +} + +.nav-side-menu ul .sub-menu li, +.nav-side-menu li .sub-menu li { + background-color: #181c20; + border: none; + line-height: 28px; + border-bottom: 1px solid #23282e; + margin-left: 0; +} + +.nav-side-menu ul .sub-menu li:hover, +.nav-side-menu li .sub-menu li:hover { + background-color: #020203; +} + +.nav-side-menu ul .sub-menu li:before, +.nav-side-menu li .sub-menu li:before { + font-family: FontAwesome; + content: "\f105"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; +} + +.nav-side-menu li { + padding-left: 0; + border-left: 3px solid #2e353d; + border-bottom: 1px solid #23282e; +} + +.nav-side-menu li a { + text-decoration: none; + color: #fff; + display: block; + width: 100%; + padding: 2px 0; +} + +.nav-side-menu li a i { + padding-left: 10px; + width: 20px; + padding-right: 20px; +} + +.nav-side-menu li:hover { + border-left: 3px solid #fff; + background-color: #191919; + -webkit-transition: all 1s ease; + -moz-transition: all 1s ease; + -o-transition: all 1s ease; + -ms-transition: all 1s ease; + transition: all 1s ease; +} + +.nav-side-menu li:hover a { + color: #fff; +} + +@media (max-width: 991px) { + .nav-side-menu { + position: relative; + width: 100%; + } + + .nav-side-menu .toggle-btn { + display: block; + cursor: pointer; + position: absolute; + right: 10px; + top: 10px; + z-index: 10 !important; + padding: 3px; + background-color: #ffffff; + color: #000; + width: 40px; + text-align: center; + border-radius: 3px; + } + + .brand { + text-align: left !important; + font-size: 22px; + padding-left: 20px; + line-height: 58px !important; + } +} + +@media (min-width: 992px) { + .nav-side-menu .menu-list .menu-content { + display: block; + } +} + +/* Top Bar */ +#top-bar { + background-color: #000; + line-height: 50px; + display: block; + font-size: 17px; + color: #fff; + padding: 0 15px; + z-index: 1000; +} + +#top-bar div { + display: inline-block; + padding: 0 15px; +} + +#top-bar div strong { + font-weight: 700; +} + +#top-bar #statsUpdated { + font-weight: normal; +} + +@media (max-width: 1199px) { + #top-bar #statsUpdated .text { + display: none; + } +} + +@media (min-width: 992px) { + #top-bar { + overflow: auto; + position: fixed; + top: 0; + left: 225px; + right: 0; + padding: 0 15px; + } +} + +@media (max-width: 767px) { + #top-bar { + display: none; + } +} + +/* Bootstrap Buttons */ +.btn-default { + color: #ffffff; + background-color: #014e71; + border-color: #014e71; +} + +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open>.dropdown-toggle.btn-default { + color: #ffffff; + background-color: #013d58; + border-color: #013d58; +} + +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #014e71; + border-color: #014e71; +} + +.btn-default .badge { + color: #014e71; + background-color: #ffffff; +} + +.btn-primary { + color: #ffffff; + background-color: #03a678; + border-color: #03a678; +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open>.dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #027454; + border-color: #026a4d; +} + +.btn-primary:active, +.btn-primary.active, +.open>.dropdown-toggle.btn-primary { + background-image: none; +} + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #03a678; + border-color: #03a678; +} + +.btn-primary .badge { + color: #03a678; + background-color: #ffffff; +} + +/* Bootstrap Form Controls */ +.input-group-addon { + color: #014e71; +} + +/* Bootstrap Tables */ +.table>thead>tr>td.success, +.table>tbody>tr>td.success, +.table>tfoot>tr>td.success, +.table>thead>tr>th.success, +.table>tbody>tr>th.success, +.table>tfoot>tr>th.success, +.table>thead>tr.success>td, +.table>tbody>tr.success>td, +.table>tfoot>tr.success>td, +.table>thead>tr.success>th, +.table>tbody>tr.success>th, +.table>tfoot>tr.success>th { + background-color: #000000; +} + +.table-hover>tbody>tr>td.success:hover, +.table-hover>tbody>tr>th.success:hover, +.table-hover>tbody>tr.success:hover>td, +.table-hover>tbody>tr:hover>.success, +.table-hover>tbody>tr.success:hover>th { + background-color: #000000; +} + +.table>thead>tr>th { + color: #014e71; + border-bottom-color: #014e71; + font-weight: 400; +} + +.table-striped>tbody>tr:nth-child(odd)>td, +.table-striped>tbody>tr:nth-child(odd)>th { + background-color: rgba(151, 222, 255, 0.3); +} + +.table>tbody>tr:hover td, +.table>tbody>tr:hover th { + background-color: rgba(3, 169, 244, 0.3); +} + +.table>tbody>tr>td { + border-top-color: #c9e0e9; +} + +table th.sort:hover { + cursor: pointer; +} + +/* jQuery sparkline tooltip */ +.jqstooltip { + border: none !important; + background: rgba(0, 0, 0, 0.8) !important; + border-radius: 2px !important; + margin-top: -20px !important; + /* Override Bootstrap defaults to fix jQuery.Sparkline tooltips appearance */ + -webkit-box-sizing: content-box !important; + -moz-box-sizing: content-box; + box-sizing: content-box !important; +} + +.jqstooltip .jqsfield { + color: #ccc; + font-size: 13px !important; +} + +.jqstooltip .jqsfield b { + color: #fff; +} + +/* Content Card */ +.card { + display: inline-block; + position: relative; + width: 100%; + margin: 0; + padding: 0; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + border-radius: 3px; + color: rgba(0, 0, 0, 0.87); + background: #fff; +} + +.card table, +.card .table { + margin-bottom: 0; +} + +/* Info box */ +.infoBox { + background-color: #191919; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + height: 90px; + cursor: default; + position: relative; + overflow: hidden; + margin-bottom: 30px; + border-radius: 3px; +} + +@media (max-width: 767px) { + .infoBox { + margin-bottom: 15px; + } +} + +.infoBox .icon { + position: absolute; + right: 10px; + top: 10px; + text-align: center; + font-size: 60px; + line-height: 60px; + width: 75px; + text-align: center; + color: rgba(255, 255, 255, 0.3); +} + +.infoBox .content { + display: inline-block; + padding: 7px 16px; +} + +.infoBox .content .text { + font-size: 13px; + margin-top: 11px; + color: #fff; + text-transform: uppercase; +} + +.infoBox .content .value { + font-size: 22px; + font-weight: 700; + margin-top: -4px; + color: #fff; + text-shadow: 1px 1px 1px #000; +} + +.infoBox .content .value .smallText { + font-size: 16px; + font-weight: normal; +} + +#marketInfos .infoBox { + height: 100px; +} + +#marketInfos .infoBox .source { + font-size: 13px; + font-style: italic; + color: #fff; +} + +#marketInfos .infoBox .source a, +#marketInfos .infoBox .source a:hover { + color: #fff; +} + + +/* Hash info box */ +.hashInfo { + background-color: #191919; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + cursor: default; + padding: 15px; + border-radius: 3px; + color: #fff; + /* margin-bottom: 30px; +} +@media (max-width: 767px){ + .hashInfo { + margin-bottom: 15px; + } */ +} + +.hashInfo .text { + font-size: 13px; + color: #fff; + text-transform: uppercase; +} + +.hashInfo .content { + overflow: hidden; +} + +.hashInfo .content .value { + float: left; + font-family: 'Inconsolata', monospace; + font-size: 16px; + font-weight: 800; + color: #fff; + padding-right: 8px; +} + +.hashInfo .content .value a { + color: #fff; +} + +.hashInfo .content .time { + float: left; + font-size: 15px; + font-weight: normal; +} + +/* Slush payment info box */ +.slushInfo { + background-color: #014e71; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + cursor: default; + padding: 15px; + border-radius: 3px; + color: #fff; + margin-bottom: 30px; +} + +.slushInfo .text { + font-size: 13px; + color: #fff; + text-transform: uppercase; +} + +@media (max-width: 767px) { + .slushInfo { + margin-bottom: 15px; + } +} + +.slushInfo .content .value { + float: left; + font-size: 16px; + font-weight: 400; + color: #fff; + padding: 8px 8px 0 0; +} + +.slushInfo .content .value a { + color: #fff; +} + +/* Pool Statistics */ +.stats>div:not(#addressError) { + color: #262626; + padding: 10px 0px; +} + +.stats>div:not(#addressError).marketFooter { + padding: 5px 0; +} + +.stats>div:not(#addressError)>i.fa { + color: #0274a8; + font-size: 21px; + width: 30px; + text-align: center; +} + +.stats>div:not(#addressError)>span:not(.input-group-btn):first-of-type { + font-weight: 500; + padding: 0 2px; + color: #0274a8; +} + +/* Pool and Worker Charts */ +.poolChart, +.marketChart, +.userChart { + display: none; +} + +.poolChart .chart, +.marketChart .chart, +.userChart .chart { + height: 140px; +} + +.poolChart, +.marketChart { + margin-bottom: 30px; +} + +@media (max-width: 767px) { + + .poolChart, + .marketChart { + margin-bottom: 15px; + } +} + +.poolChart h4, +.marketChart h4, +.userChart h4 { + text-align: center; + font-size: 21px; +} + +.chart a.chart-style { + display: none; + /* never displayed; we just use it to store the following colors: */ + background-color: rgba(3, 169, 244, .4); + border-color: #03a9f4; + width: 1px; + /* controls the chart border width (must be specified in px) */ +} + +/* Market Rates */ +#marketStats { + margin-top: 20px; +} + +#marketUpdate { + display: none; +} + +/* Mining Profit Calculator */ +#miningProfitCalc { + margin: 35px 0; +} + +#miningProfitCalc .input-group-addon { + padding: 6px; +} + +#miningProfitCalc .input-group-addon, +#miningProfitCalc .input-group-btn .btn, +#miningProfitCalc .input-group .form-control { + height: 45px; +} + +#calcHashDropdown { + border-radius: 0; + border-left: 0; + border-right: 0; +} + +#calcHashHolder { + width: 590px; + max-width: 100%; +} + +#calcHashRate { + z-index: inherit; + font-family: 'Inconsolata', monospace; +} + +#calcHashAmount { + font-family: 'Inconsolata', monospace; + display: inline-block; + text-align: center; + vertical-align: middle; + overflow: hidden; +} + +#calcHashPeriod { + font-family: 'Inconsolata', monospace; + display: inline-block; + width: 60px; + vertical-align: middle; +} + +#calcHashResultsHolder { + min-width: 170px; + max-width: 170px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +@media (min-width: 768px) { + #calcHashResultsHolder { + min-width: 225px; + max-width: 225px; + } +} + +/* Mining Ports */ +#miningPorts tr>td { + vertical-align: middle; + font-size: 0.95em; +} + +#miningPorts table .col1, +#miningPorts table .col2 { + text-align: center; +} + +@media (min-width: 768px) { + #miningPorts table th { + white-space: nowrap; + } + + #miningPorts table .col1 { + width: 60px; + } + + #miningPorts table .col2 { + width: 160px; + text-align: center; + } +} + +/* Mining Apps */ +#miningApps .table>thead>tr>th { + white-space: nowrap; +} + +#miningApps_rows>tr:first-child>td { + padding-top: 10px; +} + +#miningApps_rows>tr>td { + border-top: none; +} + +#miningApps_rows .appInfo td { + padding: 10px; + margin: 0; +} + +#miningApps_rows .appConfig td { + padding: 0 10px; +} + +#miningApps_rows tr:hover, +#miningApps_rows tr:hover>td, +#miningApps_rows tr:hover>th { + background-color: transparent; +} + +#miningApps .btn { + width: 100%; +} + +#miningApps .miningAppTitle { + font-weight: bold; +} + +#miningApps .exampleAddress, +#miningApps .exampleWorkerName { + font-style: italic; +} + +/* Worker Statistics */ +#yourStatsInput { + z-index: inherit; + font-family: 'Inconsolata', monospace; +} + +#yourAddressDisplay { + word-wrap: break-word; +} + +#yourAddressDisplay>span { + font-family: 'Inconsolata', monospace; +} + +#lookUp>span:nth-child(2) { + display: none; +} + +.yourStats, +.yourWorkers { + display: none; +} + +#yourAddressDisplay { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.9em; +} + +#addressError { + color: red; +} + +#workersReport td { + vertical-align: middle; +} + +#workersReport .status-ok { + color: #17a600; +} + +#workersReport .status-error { + color: #a60000; +} + +#workersReport table .col1, +#workersReport table .col3, +#workersReport table .col4, +#workersReport table .col5 { + text-align: center; +} + +@media (min-width: 768px) { + #workersReport table th { + white-space: nowrap; + } + + #workersReport table .col1 { + width: 80px; + } + + #workersReport table .col3, + #workersReport table .col4, + #workersReport table .col5, + #workersReport table .col6 { + width: 120px; + } + + #workersReport table .col7 { + width: 210px; + } + + #workersReport table .col8 { + width: 210px; + } +} + +#workerPayments td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} + +#workerPayments table .col3, +#workerPayments table .col4 { + text-align: center; +} + +#workerPayments table .summary { + font-weight: 700; +} + +@media (min-width: 768px) { + #workerPayments table { + table-layout: fixed; + } + + #workerPayments table th { + white-space: nowrap; + } + + #workerPayments table .col1 { + width: 190px; + } + + #workerPayments table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + + #workerPayments table .col3 { + width: 130px; + } + + #workerPayments table .col4 { + width: 70px; + } +} + +/* Pool Blocks */ +#blocksStats { + margin-top: 20px; +} + +#blocksStats .infoBox { + margin-bottom: 15px; +} + +#blocksStats .luckGood { + color: #5eff5e; +} + +#blocksStats .luckBad { + color: red; +} + +#blocksStats .luckMid { + color: #FFF500; +} + +#blocksReport tr>td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} + +#blocksReport table .col2, +#blocksReport table .col3, +#blocksReport table .col4, +#blocksReport table .col6, +#blocksReport table .col7 { + text-align: center; +} + +@media (min-width: 768px) { + #blocksReport table { + table-layout: fixed; + } + + #blocksReport table th { + white-space: nowrap; + } + + #blocksReport table .col1 { + width: 190px; + } + + #blocksReport table .col2 { + width: 80px; + } + + #blocksReport table .col3 { + width: 90px; + } + + #blocksReport table .col4 { + width: 120px; + } + + #blocksReport table .col5 { + overflow: hidden; + text-overflow: ellipsis; + } + + #blocksReport table .col6 { + width: 55px; + } + + #blocksReport table .col7 { + width: 80px; + } +} + +#blocksReport .pending td { + background-color: rgba(255, 255, 255, .3); +} + +#blocksReport .unlocked td { + background-color: rgba(0, 255, 0, .1); +} + +#blocksReport .orphaned td { + background-color: rgba(255, 0, 0, .1); +} + +#blocksReport .luckGood, +#blocksReport .unlocked .col7 { + color: #17a600; +} + +#blocksReport .luckBad, +#blocksReport .orphaned .col7 { + color: #a60000; +} + +#blocksReport .luckMid { + color: #df9d00; +} + +/* Payments */ +#paymentsStats { + margin-top: 20px; +} + +#paymentsStats .infoBox { + margin-bottom: 15px; +} + +#paymentsReport td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} + +#paymentsReport table .col3, +#paymentsReport table .col4, +#paymentsReport table .col5, +#paymentsReport table .col6 { + text-align: center; +} + +@media (min-width: 768px) { + #paymentsReport table { + table-layout: fixed; + } + + #paymentsReport table th { + white-space: nowrap; + } + + #paymentsReport table .col1 { + width: 190px; + } + + #paymentsReport table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + + #paymentsReport table .col3 { + width: 130px; + } + + #paymentsReport table .col4 { + width: 110px; + } + + #paymentsReport table .col5 { + width: 70px; + } + + #paymentsReport table .col6 { + width: 70px; + } +} + +/* Top 10 miners */ +#top10miners td { + vertical-align: middle; +} + +#top10miners table .col1, +#top10miners table .col3, +#top10miners table .col4, +#top10miners table .col5 { + text-align: center; +} + +@media (min-width: 768px) { + #top10miners table th { + white-space: nowrap; + } + + #top10miners table .col1 { + width: 80px; + } + + #top10miners table .col3 { + width: 150px; + } + + #top10miners table .col4 { + width: 210px; + } + + #top10miners table .col5 { + width: 210px; + } +} + +/* Language selector */ +#langSelector { + float: right; + padding-right: 0 !important; +} + +#langSelector select { + display: inline-block; + width: auto; + height: 32px; + padding: 6px 10px; +} + +#mLangSelector { + display: none; + padding: 10px; +} + +@media (max-width: 767px) { + #mLangSelector { + display: block; + } +} diff --git a/website_example/themes/custom.css b/website_example/themes/custom.css index fef01c798..466cd98ee 100644 --- a/website_example/themes/custom.css +++ b/website_example/themes/custom.css @@ -1 +1,153 @@ /* Insert your pool's unique CSS here */ + +.nav-side-menu { + background: #164450; +} + +.nav-side-menu .brand { + background: #164450; + color: #fff; + padding: 10px; +} + +.nav-side-menu .brand a { + color: #fff; + font-size: 16px; +} + +.brand { + padding-top: 5px +} + +.brand img.img-responsive { + margin: 0 auto; +} + +.nav-side-menu ul .active, +.nav-side-menu li .active { + border-left: 3px solid #5fbcd3; + background-color: #2c89a0; +} + +.infoBox { + /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#2c89a0+0,5fbcd3+100 */ + background: #2c89a0; + /* Old browsers */ + background: -moz-linear-gradient(45deg, #2c89a0 0%, #5fbcd3 100%); + /* FF3.6-15 */ + background: -webkit-linear-gradient(45deg, #2c89a0 0%, #5fbcd3 100%); + /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(45deg, #2c89a0 0%, #5fbcd3 100%); + /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#2c89a0', endColorstr='#5fbcd3', GradientType=1); + /* IE6-9 fallback on horizontal gradient */ +} + +.mergedBg { + background: #3b5ca3; +} + +#top-bar { + background-color: #164450; +} + +.infoBox .content .value sup { + top: -.9em; + text-decoration: none; + text-shadow: none; + font-size: 45%; + right: -3px; + font-weight: normal; +} + +#infoBlocksTotal .content .value .smallText { + font-size: 15px; + font-weight: normal; + /*display: block;*/ + /*text-shadow: none;*/ + margin-top: -3px; +} + +#infoBlocksFound .content .value .smallText { + font-size: 15px; + font-weight: normal; + /*display: block;*/ + /*text-shadow: none;*/ + margin-top: -3px; + /* Insert your pool's unique CSS here */ + + .nav-side-menu { + background: #164450; + } + + .nav-side-menu .brand { + background: #164450; + color: #fff; + padding: 10px; + } + + .nav-side-menu .brand a { + color: #fff; + font-size: 16px; + } + + .brand { + padding-top: 5px + } + + .brand img.img-responsive { + margin: 0 auto; + } + + .nav-side-menu ul .active, + .nav-side-menu li .active { + border-left: 3px solid #5fbcd3; + background-color: #2c89a0; + } + + .infoBox { + /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#2c89a0+0,5fbcd3+100 */ + background: #2c89a0; + /* Old browsers */ + background: -moz-linear-gradient(45deg, #2c89a0 0%, #5fbcd3 100%); + /* FF3.6-15 */ + background: -webkit-linear-gradient(45deg, #2c89a0 0%, #5fbcd3 100%); + /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(45deg, #2c89a0 0%, #5fbcd3 100%); + /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#2c89a0', endColorstr='#5fbcd3', GradientType=1); + /* IE6-9 fallback on horizontal gradient */ + } + + .mergedBg { + background: #3b5ca3; + } + + #top-bar { + background-color: #164450; + } + + .infoBox .content .value sup { + top: -.9em; + text-decoration: none; + text-shadow: none; + font-size: 45%; + right: -3px; + font-weight: normal; + } + + #infoBlocksTotal .content .value .smallText { + font-size: 15px; + font-weight: normal; + /*display: block;*/ + /*text-shadow: none;*/ + margin-top: -3px; + } + + #infoBlocksFound .content .value .smallText { + font-size: 15px; + font-weight: normal; + /*display: block;*/ + /*text-shadow: none;*/ + margin-top: -3px; + } diff --git a/website_example/themes/default.css b/website_example/themes/default.css index d38bad192..280190ba4 100644 --- a/website_example/themes/default.css +++ b/website_example/themes/default.css @@ -2,1015 +2,1362 @@ @import url(//fonts.googleapis.com/css?family=Roboto:400,300,500,700); @import url(//fonts.googleapis.com/css?family=Inconsolata:400,700); -body { - background-color: #f5f5f5; - font-family: 'Roboto', Helvetica, Arial, sans-serif; - line-height: 1.428571429; - color: #262626; - margin: 0; - padding: 0; - margin-bottom: 85px; - font-size: 16px; - overflow-y: scroll; +body { + background-color: #f5f5f5; + font-family: 'Roboto', Helvetica, Arial, sans-serif; + line-height: 1.428571429; + color: #262626; + margin: 0; + padding: 0; + margin-bottom: 85px; + font-size: 16px; + overflow-y: scroll; } + a { - color: #014e71; + color: #014e71; } + a:hover { - color: #0274a8; + color: #0274a8; } + hr { - border-top-color: #0274a8; + border-top-color: #0274a8; } -strong, b { - font-weight: 500; + +strong, +b { + font-weight: 500; } -h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { - font-weight: 400; - -webkit-font-smoothing: antialiased; - font-family: 'Roboto Condensed', Arial, sans-serif; + +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-weight: 400; + -webkit-font-smoothing: antialiased; + font-family: 'Roboto Condensed', Arial, sans-serif; } -h3,.h3 { - font-size: 32px; - margin-bottom: 14px; + +h3, +.h3 { + font-size: 32px; + margin-bottom: 14px; } + code { - color: #0274a8; - background-color: #efefef; - padding: 2px 10px; - border-radius: 0; -} - -.padding-5 { padding: 5px !important; } -.padding-10 { padding: 10px !important; } -.padding-15 { padding: 15px !important; } -.padding-20 { padding: 20px !important; } -.padding-25 { padding: 25px !important; } -.padding-30 { padding: 30px !important; } - -.padding-t-5 { padding-top: 5px !important; } -.padding-t-10 { padding-top: 10px !important; } -.padding-t-15 { padding-top: 15px !important; } -.padding-t-20 { padding-top: 20px !important; } -.padding-t-25 { padding-top: 25px !important; } -.padding-t-30 { padding-top: 30px !important; } - -.padding-b-5 { padding-bottom: 5px !important; } -.padding-b-10 { padding-bottom: 10px !important; } -.padding-b-15 { padding-bottom: 15px !important; } -.padding-b-20 { padding-bottom: 20px !important; } -.padding-b-25 { padding-bottom: 25px !important; } -.padding-b-30 { padding-bottom: 30px !important; } - -.padding-l-5 { padding-left: 5px !important; } -.padding-l-10 { padding-left: 10px !important; } -.padding-l-15 { padding-left: 15px !important; } -.padding-l-20 { padding-left: 20px !important; } -.padding-l-25 { padding-left: 25px !important; } -.padding-l-30 { padding-left: 30px !important; } - -.padding-r-5 { padding-right: 5px !important; } -.padding-r-10 { padding-right: 10px !important; } -.padding-r-15 { padding-right: 15px !important; } -.padding-r-20 { padding-right: 20px !important; } -.padding-r-25 { padding-right: 25px !important; } -.padding-r-30 { padding-right: 30px !important; } - -.push-up-5 { margin-top: 5px !important; } -.push-up-10 { margin-top: 10px !important; } -.push-up-15 { margin-top: 15px !important; } -.push-up-20 { margin-top: 20px !important; } -.push-up-25 { margin-top: 25px !important; } -.push-up-30 { margin-top: 30px !important; } - -.push-down-5 { margin-bottom: 5px !important; } -.push-down-10 { margin-bottom: 10px !important; } -.push-down-15 { margin-bottom: 15px !important; } -.push-down-20 { margin-bottom: 20px !important; } -.push-down-25 { margin-bottom: 25px !important; } -.push-down-30 { margin-bottom: 30px !important; } + color: #0274a8; + background-color: #efefef; + padding: 2px 10px; + border-radius: 0; +} + +.padding-5 { + padding: 5px !important; +} + +.padding-10 { + padding: 10px !important; +} + +.padding-15 { + padding: 15px !important; +} + +.padding-20 { + padding: 20px !important; +} + +.padding-25 { + padding: 25px !important; +} + +.padding-30 { + padding: 30px !important; +} + +.padding-t-5 { + padding-top: 5px !important; +} + +.padding-t-10 { + padding-top: 10px !important; +} + +.padding-t-15 { + padding-top: 15px !important; +} + +.padding-t-20 { + padding-top: 20px !important; +} + +.padding-t-25 { + padding-top: 25px !important; +} + +.padding-t-30 { + padding-top: 30px !important; +} + +.padding-b-5 { + padding-bottom: 5px !important; +} + +.padding-b-10 { + padding-bottom: 10px !important; +} + +.padding-b-15 { + padding-bottom: 15px !important; +} + +.padding-b-20 { + padding-bottom: 20px !important; +} + +.padding-b-25 { + padding-bottom: 25px !important; +} + +.padding-b-30 { + padding-bottom: 30px !important; +} + +.padding-l-5 { + padding-left: 5px !important; +} + +.padding-l-10 { + padding-left: 10px !important; +} + +.padding-l-15 { + padding-left: 15px !important; +} + +.padding-l-20 { + padding-left: 20px !important; +} + +.padding-l-25 { + padding-left: 25px !important; +} + +.padding-l-30 { + padding-left: 30px !important; +} + +.padding-r-5 { + padding-right: 5px !important; +} + +.padding-r-10 { + padding-right: 10px !important; +} + +.padding-r-15 { + padding-right: 15px !important; +} + +.padding-r-20 { + padding-right: 20px !important; +} + +.padding-r-25 { + padding-right: 25px !important; +} + +.padding-r-30 { + padding-right: 30px !important; +} + +.push-up-5 { + margin-top: 5px !important; +} + +.push-up-10 { + margin-top: 10px !important; +} + +.push-up-15 { + margin-top: 15px !important; +} + +.push-up-20 { + margin-top: 20px !important; +} + +.push-up-25 { + margin-top: 25px !important; +} + +.push-up-30 { + margin-top: 30px !important; +} + +.push-down-5 { + margin-bottom: 5px !important; +} + +.push-down-10 { + margin-bottom: 10px !important; +} + +.push-down-15 { + margin-bottom: 15px !important; +} + +.push-down-20 { + margin-bottom: 20px !important; +} + +.push-down-25 { + margin-bottom: 25px !important; +} + +.push-down-30 { + margin-bottom: 30px !important; +} /* Footer */ -footer{ - position: fixed; - bottom: 0; - width: 100%; - background-color: #262626; - font-size: 14px; - color: #efefef; - z-index: 9999; +footer { + position: fixed; + bottom: 0; + width: 100%; + background-color: #262626; + font-size: 14px; + color: #efefef; + z-index: 9999; } + footer a { - color: #6ED5EE; + color: #6ED5EE; } -footer > div{ - color: #fff; - margin: 10px auto; - text-align: center; + +footer>div { + color: #fff; + margin: 10px auto; + text-align: center; } /* Wrappers */ #wrapper { - width: 100%; + width: 100%; } + #page-wrapper { - padding: 0 15px; - min-height: 568px; + padding: 0 15px; + min-height: 568px; } + @media (min-width: 992px) { - #page-wrapper { - position: inherit; - margin: 0 0 0 225px; - padding: 60px 30px 0 30px; - } + #page-wrapper { + position: inherit; + margin: 0 0 0 225px; + padding: 60px 30px 0 30px; + } } -#loading{ - font-size: 2em; + +#loading { + font-size: 2em; } /* Sidebar */ .nav-side-menu { - overflow: auto; - font-size: 16px; - font-weight: 200; - background-color: rgba(0, 0, 0, .8); - position: fixed; - top: 0; - width: 225px; - height: 100%; - color: #fff; - z-index: 1000; + overflow: auto; + font-size: 16px; + font-weight: 200; + background-color: rgba(0, 0, 0, .8); + position: fixed; + top: 0; + width: 225px; + height: 100%; + color: #fff; + z-index: 1000; } + .nav-side-menu .brand { - background-color: #014e71; - line-height: 50px; - display: block; - text-align: center; - font-size: 20px; - color: #fff; - font-weight: 400; + background-color: #014e71; + line-height: 50px; + display: block; + text-align: center; + font-size: 20px; + color: #fff; + font-weight: 400; } + .nav-side-menu .brand a { - color: #fff; - text-decoration: none; + color: #fff; + text-decoration: none; } .nav-side-menu .toggle-btn { - display: none; + display: none; } + .nav-side-menu ul, .nav-side-menu li { - list-style: none; - padding: 0; - margin: 0; - line-height: 35px; - cursor: pointer; + list-style: none; + padding: 0; + margin: 0; + line-height: 35px; + cursor: pointer; } + .nav-side-menu ul :not(collapsed) .arrow:before, .nav-side-menu li :not(collapsed) .arrow:before { - font-family: FontAwesome; - content: "\f078"; - display: inline-block; - padding-left: 10px; - padding-right: 10px; - vertical-align: middle; - float: right; + font-family: FontAwesome; + content: "\f078"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; + float: right; } + .nav-side-menu ul .active, .nav-side-menu li .active { - border-left: 3px solid #03a9f4; - background-color: #0274a8; + border-left: 3px solid #03a9f4; + background-color: #0274a8; } + .nav-side-menu ul .active a, .nav-side-menu li .active a { - color: #fff; + color: #fff; } + .nav-side-menu ul .sub-menu li.active, .nav-side-menu li .sub-menu li.active { - color: #03a9f4; + color: #03a9f4; } + .nav-side-menu ul .sub-menu li a, .nav-side-menu li .sub-menu li a { - display: inline-block; - width: 85%; + display: inline-block; + width: 85%; } + .nav-side-menu ul .sub-menu li.active a, .nav-side-menu li .sub-menu li.active a { - color: #03a9f4; + color: #03a9f4; } + .nav-side-menu ul .sub-menu li, .nav-side-menu li .sub-menu li { - background-color: #181c20; - border: none; - line-height: 28px; - border-bottom: 1px solid #23282e; - margin-left: 0; + background-color: #181c20; + border: none; + line-height: 28px; + border-bottom: 1px solid #23282e; + margin-left: 0; } + .nav-side-menu ul .sub-menu li:hover, .nav-side-menu li .sub-menu li:hover { - background-color: #020203; + background-color: #020203; } + .nav-side-menu ul .sub-menu li:before, .nav-side-menu li .sub-menu li:before { - font-family: FontAwesome; - content: "\f105"; - display: inline-block; - padding-left: 10px; - padding-right: 10px; - vertical-align: middle; + font-family: FontAwesome; + content: "\f105"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; } + .nav-side-menu li { - padding-left: 0; - border-left: 3px solid #2e353d; - border-bottom: 1px solid #23282e; + padding-left: 0; + border-left: 3px solid #2e353d; + border-bottom: 1px solid #23282e; } + .nav-side-menu li a { - text-decoration: none; - color: #fff; - display: block; - width: 100%; - padding: 2px 0; + text-decoration: none; + color: #fff; + display: block; + width: 100%; + padding: 2px 0; } + .nav-side-menu li a i { - padding-left: 10px; - width: 20px; - padding-right: 20px; + padding-left: 10px; + width: 20px; + padding-right: 20px; } + .nav-side-menu li:hover { - border-left: 3px solid #03a9f4; - background-color: #0274a8; - -webkit-transition: all 1s ease; - -moz-transition: all 1s ease; - -o-transition: all 1s ease; - -ms-transition: all 1s ease; - transition: all 1s ease; + border-left: 3px solid #03a9f4; + background-color: #0274a8; + -webkit-transition: all 1s ease; + -moz-transition: all 1s ease; + -o-transition: all 1s ease; + -ms-transition: all 1s ease; + transition: all 1s ease; } + .nav-side-menu li:hover a { - color: #fff; + color: #fff; } + @media (max-width: 991px) { - .nav-side-menu { - position: relative; - width: 100%; - } - .nav-side-menu .toggle-btn { - display: block; - cursor: pointer; - position: absolute; - right: 10px; - top: 10px; - z-index: 10 !important; - padding: 3px; - background-color: #ffffff; - color: #000; - width: 40px; - text-align: center; - border-radius: 3px; - } - .brand { - text-align: left !important; - font-size: 22px; - padding-left: 20px; - line-height: 58px !important; - } + .nav-side-menu { + position: relative; + width: 100%; + } + + .nav-side-menu .toggle-btn { + display: block; + cursor: pointer; + position: absolute; + right: 10px; + top: 10px; + z-index: 10 !important; + padding: 3px; + background-color: #ffffff; + color: #000; + width: 40px; + text-align: center; + border-radius: 3px; + } + + .brand { + text-align: left !important; + font-size: 22px; + padding-left: 20px; + line-height: 58px !important; + } } + @media (min-width: 992px) { - .nav-side-menu .menu-list .menu-content { - display: block; - } + .nav-side-menu .menu-list .menu-content { + display: block; + } } /* Top Bar */ -#top-bar { - background-color: #0274a8; - line-height: 50px; - display: block; - font-size: 17px; - color: #fff; - padding: 0 15px; - z-index: 1000; +#top-bar { + background-color: #0274a8; + line-height: 50px; + display: block; + font-size: 17px; + color: #fff; + padding: 0 15px; + z-index: 1000; } + #top-bar div { - display: inline-block; - padding: 0 15px; + display: inline-block; + padding: 0 15px; } + #top-bar div strong { - font-weight: 700; + font-weight: 700; } + #top-bar #statsUpdated { - font-weight: normal; + font-weight: normal; } + @media (max-width: 1199px) { - #top-bar #statsUpdated .text { - display: none; - } + #top-bar #statsUpdated .text { + display: none; + } } + @media (min-width: 992px) { - #top-bar { - overflow: auto; - position: fixed; - top: 0; - left: 225px; - right: 0; - padding: 0 15px; - } + #top-bar { + overflow: auto; + position: fixed; + top: 0; + left: 225px; + right: 0; + padding: 0 15px; + } } + @media (max-width: 767px) { - #top-bar { - display: none; - } + #top-bar { + display: none; + } } /* Bootstrap Buttons */ .btn-default { - color: #ffffff; - background-color: #014e71; - border-color: #014e71; + color: #ffffff; + background-color: #014e71; + border-color: #014e71; } -.btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { - color: #ffffff; - background-color: #013d58; - border-color: #013d58; + +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open>.dropdown-toggle.btn-default { + color: #ffffff; + background-color: #013d58; + border-color: #013d58; } -.btn-default.disabled,.btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { - background-color: #014e71; - border-color: #014e71; + +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #014e71; + border-color: #014e71; } + .btn-default .badge { - color: #014e71; - background-color: #ffffff; + color: #014e71; + background-color: #ffffff; } + .btn-primary { - color: #ffffff; - background-color: #03a678; - border-color: #03a678; + color: #ffffff; + background-color: #03a678; + border-color: #03a678; } -.btn-primary:hover,.btn-primary:focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { - color: #ffffff; - background-color: #027454; - border-color: #026a4d; + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open>.dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #027454; + border-color: #026a4d; } -.btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { - background-image: none; + +.btn-primary:active, +.btn-primary.active, +.open>.dropdown-toggle.btn-primary { + background-image: none; } -.btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { - background-color: #03a678; - border-color: #03a678; + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #03a678; + border-color: #03a678; } + .btn-primary .badge { - color: #03a678; - background-color: #ffffff; + color: #03a678; + background-color: #ffffff; } /* Bootstrap Form Controls */ .input-group-addon { - color: #014e71; + color: #014e71; } /* Bootstrap Tables */ -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #000000; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #000000; -} -.table > thead > tr > th { - color: #014e71; - border-bottom-color: #014e71; - font-weight: 400; -} -.table-striped > tbody > tr:nth-child(odd) > td, -.table-striped > tbody > tr:nth-child(odd) > th { - background-color: rgba(151, 222, 255, 0.3); -} -.table > tbody > tr:hover td, -.table > tbody > tr:hover th { - background-color: rgba(3, 169, 244, 0.3); -} -.table > tbody > tr > td { - border-top-color: #c9e0e9; -} -table th.sort:hover{ - cursor: pointer; +.table>thead>tr>td.success, +.table>tbody>tr>td.success, +.table>tfoot>tr>td.success, +.table>thead>tr>th.success, +.table>tbody>tr>th.success, +.table>tfoot>tr>th.success, +.table>thead>tr.success>td, +.table>tbody>tr.success>td, +.table>tfoot>tr.success>td, +.table>thead>tr.success>th, +.table>tbody>tr.success>th, +.table>tfoot>tr.success>th { + background-color: #000000; +} + +.table-hover>tbody>tr>td.success:hover, +.table-hover>tbody>tr>th.success:hover, +.table-hover>tbody>tr.success:hover>td, +.table-hover>tbody>tr:hover>.success, +.table-hover>tbody>tr.success:hover>th { + background-color: #000000; +} + +.table>thead>tr>th { + color: #014e71; + border-bottom-color: #014e71; + font-weight: 400; +} + +.table-striped>tbody>tr:nth-child(odd)>td, +.table-striped>tbody>tr:nth-child(odd)>th { + background-color: rgba(151, 222, 255, 0.3); +} + +.table>tbody>tr:hover td, +.table>tbody>tr:hover th { + background-color: rgba(3, 169, 244, 0.3); +} + +.table>tbody>tr>td { + border-top-color: #c9e0e9; +} + +table th.sort:hover { + cursor: pointer; } /* jQuery sparkline tooltip */ .jqstooltip { - border: none !important; - background: rgba(0, 0, 0, 0.8) !important; - border-radius: 2px !important; - margin-top: -20px !important; - /* Override Bootstrap defaults to fix jQuery.Sparkline tooltips appearance */ - -webkit-box-sizing: content-box !important; - -moz-box-sizing: content-box; - box-sizing: content-box !important; + border: none !important; + background: rgba(0, 0, 0, 0.8) !important; + border-radius: 2px !important; + margin-top: -20px !important; + /* Override Bootstrap defaults to fix jQuery.Sparkline tooltips appearance */ + -webkit-box-sizing: content-box !important; + -moz-box-sizing: content-box; + box-sizing: content-box !important; } + .jqstooltip .jqsfield { - color: #ccc; - font-size: 13px !important; + color: #ccc; + font-size: 13px !important; } + .jqstooltip .jqsfield b { - color: #fff; + color: #fff; } /* Content Card */ .card { - display: inline-block; - position: relative; - width: 100%; - margin: 0; - padding: 0; - box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); - border-radius: 3px; - color: rgba(0, 0, 0, 0.87); - background: #fff; + display: inline-block; + position: relative; + width: 100%; + margin: 0; + padding: 0; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + border-radius: 3px; + color: rgba(0, 0, 0, 0.87); + background: #fff; } -.card table, .card .table { - margin-bottom: 0; +.card table, +.card .table { + margin-bottom: 0; } /* Info box */ .infoBox { - background-color: #03a9f4; - box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); - height: 80px; - cursor: default; - position: relative; - overflow: hidden; - margin-bottom: 30px; - border-radius: 3px; + background-color: #03a9f4; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + height: 90px; + cursor: default; + position: relative; + overflow: hidden; + margin-bottom: 30px; + border-radius: 3px; } -@media (max-width: 767px){ - .infoBox{ - margin-bottom: 15px; - } + +@media (max-width: 767px) { + .infoBox { + margin-bottom: 15px; + } } + .infoBox .icon { - position: absolute; - right: 10px; - top: 10px; - text-align: center; - font-size: 60px; - line-height: 60px; - width: 75px; - text-align: center; - color: rgba(255, 255, 255, 0.3); + position: absolute; + right: 10px; + top: 10px; + text-align: center; + font-size: 60px; + line-height: 60px; + width: 75px; + text-align: center; + color: rgba(255, 255, 255, 0.3); } + .infoBox .content { - display: inline-block; - padding: 7px 16px; + display: inline-block; + padding: 7px 16px; } + .infoBox .content .text { - font-size: 13px; - margin-top: 11px; - color: #fff; - text-transform: uppercase; + font-size: 13px; + margin-top: 11px; + color: #fff; + text-transform: uppercase; } + .infoBox .content .value { - font-size: 26px; - font-weight: 800; - margin-top: -4px; - color: #fff; - text-shadow: 1px 1px 1px #000; + font-size: 22px; + font-weight: 700; + margin-top: -4px; + color: #fff; + text-shadow: 1px 1px 1px #000; } + .infoBox .content .value .smallText { - font-size: 16px; - font-weight: normal; + font-size: 16px; + font-weight: normal; } + #marketInfos .infoBox { - height: 100px; + height: 100px; } + #marketInfos .infoBox .source { - font-size: 13px; - font-style: italic; - color: #fff; + font-size: 13px; + font-style: italic; + color: #fff; } -#marketInfos .infoBox .source a, #marketInfos .infoBox .source a:hover { - color: #fff; + +#marketInfos .infoBox .source a, +#marketInfos .infoBox .source a:hover { + color: #fff; } /* Hash info box */ .hashInfo { - background-color: #014e71; - box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); - cursor: default; - padding: 15px; - border-radius: 3px; - color: #fff; - margin-bottom: 30px; + background-color: #014e71; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + cursor: default; + padding: 15px; + border-radius: 3px; + color: #fff; + /* margin-bottom: 30px; } @media (max-width: 767px){ .hashInfo { margin-bottom: 15px; - } + } */ } + .hashInfo .text { - font-size: 13px; - color: #fff; - text-transform: uppercase; + font-size: 13px; + color: #fff; + text-transform: uppercase; } + .hashInfo .content { - overflow: hidden; + overflow: hidden; } + .hashInfo .content .value { - float: left; - font-family: 'Inconsolata', monospace; - font-size: 16px; - font-weight: 800; - color: #fff; - padding-right: 8px; + float: left; + font-family: 'Inconsolata', monospace; + font-size: 16px; + font-weight: 800; + color: #fff; + padding-right: 8px; } + .hashInfo .content .value a { - color: #fff; + color: #fff; } + .hashInfo .content .time { - float: left; - font-size: 15px; - font-weight: normal; + float: left; + font-size: 15px; + font-weight: normal; } /* Slush payment info box */ .slushInfo { - background-color: #014e71; - box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); - cursor: default; - padding: 15px; - border-radius: 3px; - color: #fff; - margin-bottom: 30px; + background-color: #014e71; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + cursor: default; + padding: 15px; + border-radius: 3px; + color: #fff; + margin-bottom: 30px; } + .slushInfo .text { - font-size: 13px; - color: #fff; - text-transform: uppercase; + font-size: 13px; + color: #fff; + text-transform: uppercase; } -@media (max-width: 767px){ - .slushInfo { - margin-bottom: 15px; - } + +@media (max-width: 767px) { + .slushInfo { + margin-bottom: 15px; + } } + .slushInfo .content .value { - float: left; - font-size: 16px; - font-weight: 400; - color: #fff; - padding: 8px 8px 0 0; + float: left; + font-size: 16px; + font-weight: 400; + color: #fff; + padding: 8px 8px 0 0; } + .slushInfo .content .value a { - color: #fff; + color: #fff; } /* Pool Statistics */ -.stats > div:not(#addressError) { - color: #262626; - padding: 10px 0px; +.stats>div:not(#addressError) { + color: #262626; + padding: 10px 0px; } -.stats > div:not(#addressError).marketFooter { - padding: 5px 0; + +.stats>div:not(#addressError).marketFooter { + padding: 5px 0; } -.stats > div:not(#addressError) > i.fa { - color: #0274a8; - font-size: 21px; - width: 30px; - text-align: center; + +.stats>div:not(#addressError)>i.fa { + color: #0274a8; + font-size: 21px; + width: 30px; + text-align: center; } -.stats > div:not(#addressError) > span:not(.input-group-btn):first-of-type { - font-weight: 500; - padding: 0 2px; - color: #0274a8; + +.stats>div:not(#addressError)>span:not(.input-group-btn):first-of-type { + font-weight: 500; + padding: 0 2px; + color: #0274a8; } /* Pool and Worker Charts */ -.poolChart, .marketChart, .userChart { - display: none; +.poolChart, +.marketChart, +.userChart { + display: none; } -.poolChart .chart, .marketChart .chart, .userChart .chart { - height: 140px; + +.poolChart .chart, +.marketChart .chart, +.userChart .chart { + height: 140px; } -.poolChart, .marketChart { - margin-bottom: 30px; + +.poolChart, +.marketChart { + margin-bottom: 30px; } -@media (max-width: 767px){ - .poolChart, .marketChart { - margin-bottom: 15px; - } + +@media (max-width: 767px) { + + .poolChart, + .marketChart { + margin-bottom: 15px; + } } + .poolChart h4, .marketChart h4, .userChart h4 { - text-align: center; - font-size: 21px; + text-align: center; + font-size: 21px; } + .chart a.chart-style { - display: none; /* never displayed; we just use it to store the following colors: */ - background-color: rgba(3, 169, 244, .4); - border-color: #03a9f4; - width: 1px; /* controls the chart border width (must be specified in px) */ + display: none; + /* never displayed; we just use it to store the following colors: */ + background-color: rgba(3, 169, 244, .4); + border-color: #03a9f4; + width: 1px; + /* controls the chart border width (must be specified in px) */ } /* Market Rates */ -#marketStats { - margin-top: 20px; +#marketStats { + margin-top: 20px; } + #marketUpdate { - display: none; + display: none; } /* Mining Profit Calculator */ -#miningProfitCalc{ - margin: 35px 0; -} -#miningProfitCalc .input-group-addon{ - padding: 6px; -} -#miningProfitCalc .input-group-addon, -#miningProfitCalc .input-group-btn .btn, -#miningProfitCalc .input-group .form-control{ - height:45px; -} -#calcHashDropdown{ - border-radius: 0; - border-left: 0; - border-right: 0; -} -#calcHashHolder{ - width: 590px; - max-width: 100%; -} -#calcHashRate{ - z-index: inherit; - font-family: 'Inconsolata', monospace; -} -#calcHashAmount{ - font-family: 'Inconsolata', monospace; - display: inline-block; - text-align: center; - vertical-align: middle; - overflow: hidden; -} -#calcHashPeriod{ - font-family: 'Inconsolata', monospace; - display: inline-block; - width: 60px; - vertical-align: middle; -} -#calcHashResultsHolder{ - min-width: 170px; - max-width: 170px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; +#miningProfitCalc { + margin: 35px 0; +} + +#miningProfitCalc .input-group-addon { + padding: 6px; +} + +#miningProfitCalc .input-group-addon, +#miningProfitCalc .input-group-btn .btn, +#miningProfitCalc .input-group .form-control { + height: 45px; +} + +#calcHashDropdown { + border-radius: 0; + border-left: 0; + border-right: 0; +} + +#calcHashHolder { + width: 590px; + max-width: 100%; +} + +#calcHashRate { + z-index: inherit; + font-family: 'Inconsolata', monospace; +} + +#calcHashAmount { + font-family: 'Inconsolata', monospace; + display: inline-block; + text-align: center; + vertical-align: middle; + overflow: hidden; } + +#calcHashPeriod { + font-family: 'Inconsolata', monospace; + display: inline-block; + width: 60px; + vertical-align: middle; +} + +#calcHashResultsHolder { + min-width: 170px; + max-width: 170px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + @media (min-width: 768px) { - #calcHashResultsHolder{ - min-width: 225px; - max-width: 225px; - } + #calcHashResultsHolder { + min-width: 225px; + max-width: 225px; + } } /* Mining Ports */ -#miningPorts tr > td { - vertical-align: middle; - font-size: 0.95em; +#miningPorts tr>td { + vertical-align: middle; + font-size: 0.95em; } + #miningPorts table .col1, #miningPorts table .col2 { - text-align: center; -} + text-align: center; +} + @media (min-width: 768px) { - #miningPorts table th { - white-space: nowrap; - } - #miningPorts table .col1 { - width:60px; - } - #miningPorts table .col2 { - width:160px; - text-align: center; - } + #miningPorts table th { + white-space: nowrap; + } + + #miningPorts table .col1 { + width: 60px; + } + + #miningPorts table .col2 { + width: 160px; + text-align: center; + } } /* Mining Apps */ -#miningApps .table > thead > tr > th { - white-space: nowrap; +#miningApps .table>thead>tr>th { + white-space: nowrap; } -#miningApps_rows > tr:first-child > td { - padding-top: 10px; + +#miningApps_rows>tr:first-child>td { + padding-top: 10px; } -#miningApps_rows > tr > td { - border-top: none; + +#miningApps_rows>tr>td { + border-top: none; } + #miningApps_rows .appInfo td { - padding: 10px; - margin: 0; + padding: 10px; + margin: 0; } + #miningApps_rows .appConfig td { - padding: 0 10px; + padding: 0 10px; } + #miningApps_rows tr:hover, -#miningApps_rows tr:hover > td, -#miningApps_rows tr:hover > th { - background-color: transparent; +#miningApps_rows tr:hover>td, +#miningApps_rows tr:hover>th { + background-color: transparent; } + #miningApps .btn { - width: 100%; + width: 100%; } + #miningApps .miningAppTitle { - font-weight: bold; + font-weight: bold; } + #miningApps .exampleAddress, #miningApps .exampleWorkerName { - font-style: italic; + font-style: italic; } /* Worker Statistics */ #yourStatsInput { - z-index: inherit; - font-family: 'Inconsolata', monospace; + z-index: inherit; + font-family: 'Inconsolata', monospace; } -#yourAddressDisplay { - word-wrap: break-word; + +#yourAddressDisplay { + word-wrap: break-word; } -#yourAddressDisplay > span { - font-family: 'Inconsolata', monospace; + +#yourAddressDisplay>span { + font-family: 'Inconsolata', monospace; } -#lookUp > span:nth-child(2) { - display: none; + +#lookUp>span:nth-child(2) { + display: none; } + .yourStats, .yourWorkers { - display: none; + display: none; } + #yourAddressDisplay { - display: inline-block; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: middle; - font-family: 'Inconsolata', monospace; - font-size: 0.9em; + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.9em; } + #addressError { - color: red; + color: red; } + #workersReport td { - vertical-align: middle; + vertical-align: middle; } -#workersReport .status-ok{ - color: #17a600; + +#workersReport .status-ok { + color: #17a600; } -#workersReport .status-error{ - color: #a60000; + +#workersReport .status-error { + color: #a60000; } + #workersReport table .col1, #workersReport table .col3, #workersReport table .col4, #workersReport table .col5 { - text-align: center; + text-align: center; } + @media (min-width: 768px) { - #workersReport table th { - white-space: nowrap; - } - #workersReport table .col1 { - width:80px; - } - #workersReport table .col3, - #workersReport table .col4, - #workersReport table .col5, - #workersReport table .col6 { - width:120px; - } - #workersReport table .col7 { - width: 210px; - } - #workersReport table .col8 { - width: 210px; - } + #workersReport table th { + white-space: nowrap; + } + + #workersReport table .col1 { + width: 80px; + } + + #workersReport table .col3, + #workersReport table .col4, + #workersReport table .col5, + #workersReport table .col6 { + width: 120px; + } + + #workersReport table .col7 { + width: 210px; + } + + #workersReport table .col8 { + width: 210px; + } } + #workerPayments td { - vertical-align: middle; - font-family: 'Inconsolata', monospace; - font-size: 0.95em; + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; } + #workerPayments table .col3, #workerPayments table .col4 { - text-align: center; + text-align: center; } + #workerPayments table .summary { - font-weight: 700; + font-weight: 700; } + @media (min-width: 768px) { - #workerPayments table { - table-layout:fixed; - } - #workerPayments table th { - white-space: nowrap; - } - #workerPayments table .col1 { - width:190px; - } - #workerPayments table .col2 { - overflow: hidden; - text-overflow: ellipsis; - } - #workerPayments table .col3 { - width:130px; - } - #workerPayments table .col4 { - width: 70px; - } + #workerPayments table { + table-layout: fixed; + } + + #workerPayments table th { + white-space: nowrap; + } + + #workerPayments table .col1 { + width: 190px; + } + + #workerPayments table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + + #workerPayments table .col3 { + width: 130px; + } + + #workerPayments table .col4 { + width: 70px; + } } /* Pool Blocks */ -#blocksStats { - margin-top: 20px; +#blocksStats { + margin-top: 20px; } -#blocksStats .infoBox { - margin-bottom: 15px; + +#blocksStats .infoBox { + margin-bottom: 15px; } + #blocksStats .luckGood { - color: #5eff5e; + color: #5eff5e; } + #blocksStats .luckBad { - color: red; + color: red; } + #blocksStats .luckMid { - color: #FFF500; + color: #FFF500; } -#blocksReport tr > td { - vertical-align: middle; - font-family: 'Inconsolata', monospace; - font-size: 0.95em; + +#blocksReport tr>td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; } + #blocksReport table .col2, #blocksReport table .col3, #blocksReport table .col4, #blocksReport table .col6, #blocksReport table .col7 { - text-align: center; -} + text-align: center; +} + @media (min-width: 768px) { - #blocksReport table { - table-layout:fixed; - } - #blocksReport table th { - white-space: nowrap; - } - #blocksReport table .col1 { - width:190px; - } - #blocksReport table .col2 { - width:80px; - } - #blocksReport table .col3 { - width:90px; - } - #blocksReport table .col4 { - width:120px; - } - #blocksReport table .col5 { - overflow: hidden; - text-overflow: ellipsis; - } - #blocksReport table .col6 { - width: 55px; - } - #blocksReport table .col7 { - width: 80px; - } -} -#blocksReport .pending td{ - background-color:rgba(255, 255, 255, .3); -} -#blocksReport .unlocked td{ - background-color:rgba(0, 255, 0, .1); -} -#blocksReport .orphaned td{ - background-color:rgba(255, 0, 0, .1); + #blocksReport table { + table-layout: fixed; + } + + #blocksReport table th { + white-space: nowrap; + } + + #blocksReport table .col1 { + width: 190px; + } + + #blocksReport table .col2 { + width: 80px; + } + + #blocksReport table .col3 { + width: 90px; + } + + #blocksReport table .col4 { + width: 120px; + } + + #blocksReport table .col5 { + overflow: hidden; + text-overflow: ellipsis; + } + + #blocksReport table .col6 { + width: 55px; + } + + #blocksReport table .col7 { + width: 80px; + } +} + +#blocksReport .pending td { + background-color: rgba(255, 255, 255, .3); } + +#blocksReport .unlocked td { + background-color: rgba(0, 255, 0, .1); +} + +#blocksReport .orphaned td { + background-color: rgba(255, 0, 0, .1); +} + #blocksReport .luckGood, #blocksReport .unlocked .col7 { - color: #17a600; + color: #17a600; } + #blocksReport .luckBad, #blocksReport .orphaned .col7 { - color: #a60000; + color: #a60000; } + #blocksReport .luckMid { - color: #df9d00; + color: #df9d00; } /* Payments */ -#paymentsStats { - margin-top: 20px; +#paymentsStats { + margin-top: 20px; } -#paymentsStats .infoBox { - margin-bottom: 15px; + +#paymentsStats .infoBox { + margin-bottom: 15px; } + #paymentsReport td { - vertical-align: middle; - font-family: 'Inconsolata', monospace; - font-size: 0.95em; + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; } + #paymentsReport table .col3, #paymentsReport table .col4, #paymentsReport table .col5, #paymentsReport table .col6 { - text-align: center; + text-align: center; } + @media (min-width: 768px) { - #paymentsReport table { - table-layout:fixed; - } - #paymentsReport table th { - white-space: nowrap; - } - #paymentsReport table .col1 { - width:190px; - } - #paymentsReport table .col2 { - overflow: hidden; - text-overflow: ellipsis; - } - #paymentsReport table .col3 { - width:130px; - } - #paymentsReport table .col4 { - width:110px; - } - #paymentsReport table .col5 { - width: 70px; - } - #paymentsReport table .col6 { - width: 70px; - } + #paymentsReport table { + table-layout: fixed; + } + + #paymentsReport table th { + white-space: nowrap; + } + + #paymentsReport table .col1 { + width: 190px; + } + + #paymentsReport table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + + #paymentsReport table .col3 { + width: 130px; + } + + #paymentsReport table .col4 { + width: 110px; + } + + #paymentsReport table .col5 { + width: 70px; + } + + #paymentsReport table .col6 { + width: 70px; + } } /* Top 10 miners */ #top10miners td { - vertical-align: middle; + vertical-align: middle; } + #top10miners table .col1, #top10miners table .col3, #top10miners table .col4, #top10miners table .col5 { - text-align: center; + text-align: center; } + @media (min-width: 768px) { - #top10miners table th { - white-space: nowrap; - } - #top10miners table .col1 { - width:80px; - } - #top10miners table .col3 { - width:150px; - } - #top10miners table .col4 { - width: 210px; - } - #top10miners table .col5 { - width: 210px; - } + #top10miners table th { + white-space: nowrap; + } + + #top10miners table .col1 { + width: 80px; + } + + #top10miners table .col3 { + width: 150px; + } + + #top10miners table .col4 { + width: 210px; + } + + #top10miners table .col5 { + width: 210px; + } } /* Language selector */ #langSelector { - float: right; - padding-right: 0 !important; + float: right; + padding-right: 0 !important; } #langSelector select { - display: inline-block; - width: auto; - height: 32px; - padding: 6px 10px; + display: inline-block; + width: auto; + height: 32px; + padding: 6px 10px; } #mLangSelector { - display: none; - padding: 10px; + display: none; + padding: 10px; } + @media (max-width: 767px) { - #mLangSelector { - display: block; - } -} \ No newline at end of file + #mLangSelector { + display: block; + } +} diff --git a/website_example/themes/golden.css b/website_example/themes/golden.css new file mode 100644 index 000000000..890e9cdd1 --- /dev/null +++ b/website_example/themes/golden.css @@ -0,0 +1,1481 @@ +@import url(//fonts.googleapis.com/css?family=Roboto+Condensed:400,700); +@import url(//fonts.googleapis.com/css?family=Roboto:400,300,500,700); +@import url(//fonts.googleapis.com/css?family=Inconsolata:400,700); + +body { + background-color: #232323; + font-family: 'Roboto', Helvetica, Arial, sans-serif; + line-height: 1.428571429; + color: #f5f5f5; + margin: 0; + padding: 0; + margin-bottom: 85px; + font-size: 16px; + overflow-y: scroll; +} +a { + color: #262626; +} +a:hover { + color: #262626; +} +hr { + border-top-color: #262626; +} +strong, b { + font-weight: 500; +} +h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { + font-weight: 400; + -webkit-font-smoothing: antialiased; + font-family: 'Roboto Condensed', Arial, sans-serif; +} +h3,.h3 { + font-size: 32px; + margin-bottom: 14px; +} +code { + color: #262626; + background-color: #efefef; + padding: 2px 10px; + border-radius: 0; +} + +.padding-5 { padding: 5px !important; } +.padding-10 { padding: 10px !important; } +.padding-15 { padding: 15px !important; } +.padding-20 { padding: 20px !important; } +.padding-25 { padding: 25px !important; } +.padding-30 { padding: 30px !important; } + +.padding-t-5 { padding-top: 5px !important; } +.padding-t-10 { padding-top: 10px !important; } +.padding-t-15 { padding-top: 15px !important; } +.padding-t-20 { padding-top: 20px !important; } +.padding-t-25 { padding-top: 25px !important; } +.padding-t-30 { padding-top: 30px !important; } + +.padding-b-5 { padding-bottom: 5px !important; } +.padding-b-10 { padding-bottom: 10px !important; } +.padding-b-15 { padding-bottom: 15px !important; } +.padding-b-20 { padding-bottom: 20px !important; } +.padding-b-25 { padding-bottom: 25px !important; } +.padding-b-30 { padding-bottom: 30px !important; } + +.padding-l-5 { padding-left: 5px !important; } +.padding-l-10 { padding-left: 10px !important; } +.padding-l-15 { padding-left: 15px !important; } +.padding-l-20 { padding-left: 20px !important; } +.padding-l-25 { padding-left: 25px !important; } +.padding-l-30 { padding-left: 30px !important; } + +.padding-r-5 { padding-right: 5px !important; } +.padding-r-10 { padding-right: 10px !important; } +.padding-r-15 { padding-right: 15px !important; } +.padding-r-20 { padding-right: 20px !important; } +.padding-r-25 { padding-right: 25px !important; } +.padding-r-30 { padding-right: 30px !important; } + +.push-up-5 { margin-top: 5px !important; } +.push-up-10 { margin-top: 10px !important; } +.push-up-15 { margin-top: 15px !important; } +.push-up-20 { margin-top: 20px !important; } +.push-up-25 { margin-top: 25px !important; } +.push-up-30 { margin-top: 30px !important; } + +.push-down-5 { margin-bottom: 5px !important; } +.push-down-10 { margin-bottom: 10px !important; } +.push-down-15 { margin-bottom: 15px !important; } +.push-down-20 { margin-bottom: 20px !important; } +.push-down-25 { margin-bottom: 25px !important; } +.push-down-30 { margin-bottom: 30px !important; } + +/* Footer */ +footer{ + position: fixed; + bottom: 0; + width: 100%; + background-color: #262626; + font-size: 14px; + color: #efefef; + z-index: 9999; +} +footer a { + color: #ACAFB1; +} +footer > div{ + color: #fff; + margin: 10px auto; + text-align: center; +} + +/* Wrappers */ +#wrapper { + width: 100%; +} +#page-wrapper { + padding: 0 15px; + min-height: 568px; +} +@media (min-width: 992px) { + #page-wrapper { + position: inherit; + margin: 0 0 0 225px; + padding: 60px 30px 0 30px; + } +} +#loading{ + font-size: 2em; +} + +/* Sidebar */ +.nav-side-menu { +overflow-y: scroll; + font-size: 16px; + font-weight: 200; + background-color: rgba(0, 0, 0, .8); +background: -moz-linear-gradient(left, rgba(245,245,245,0.4) 0%, rgba(35,35,35,0.93) 88%, rgba(35,35,35,1) 100%); +background: -webkit-linear-gradient(left, rgba(245,245,245,0.4) 0%,rgba(35,35,35,0.93) 88%,rgba(35,35,35,1) 100%); +background: linear-gradient(to right, rgba(245,245,245,0.4) 0%,rgba(35,35,35,0.93) 88%,rgba(35,35,35,1) 100%); +filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#66f5f5f5', endColorstr='#232323',GradientType=1 ); + position: fixed; + top: 0; + width: 225px; + height: 100%; + color: #fff; + z-index: 1000; +} +.nav-side-menu::-webkit-scrollbar { +display: none; +} +.nav-side-menu .brand { + background-color: #262626; + line-height: 22px; + display: block; + text-align: center; + font-size: 20px; + color: #fff; + font-weight: 400; +} +.nav-side-menu .brand a { + color: #fff; + text-decoration: none; +} + +.nav-side-menu .toggle-btn { + display: none; +} +.nav-side-menu ul, +.nav-side-menu li { + list-style: none; + padding: 0; + margin: 0; + line-height: 35px; + cursor: pointer; +} +.nav-side-menu ul :not(collapsed) .arrow:before, +.nav-side-menu li :not(collapsed) .arrow:before { + font-family: FontAwesome; + content: "\f078"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; +} +.nav-side-menu ul .active, +.nav-side-menu li .active { + border-left: 3px solid #A3A3A3; +background: -moz-linear-gradient(left, rgba(245,245,245,0.8) 0%, rgba(35,35,35,0.98) 88%, rgba(35,35,35,1) 100%); +background: -webkit-linear-gradient(left, rgba(245,245,245,0.8) 0%,rgba(35,35,35,0.98) 88%,rgba(35,35,35,1) 100%); +background: linear-gradient(to right, rgba(245,245,245,0.8) 0%,rgba(35,35,35,0.98) 88%,rgba(35,35,35,1) 100%); +filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ccf5f5f5', endColorstr='#232323',GradientType=1 ); +} +.nav-side-menu ul .active a, +.nav-side-menu li .active a { + color: #fff; +} +.nav-side-menu ul .sub-menu li.active, +.nav-side-menu li .sub-menu li.active { + color: #A3A3A3; +} +.nav-side-menu ul .sub-menu li a, +.nav-side-menu li .sub-menu li a { + display: inline-block; + width: 85%; +} +.nav-side-menu ul .sub-menu li.active a, +.nav-side-menu li .sub-menu li.active a { + color: #A3A3A3; +} +.nav-side-menu ul .sub-menu li, +.nav-side-menu li .sub-menu li { + background-color: #181c20; + border: none; + line-height: 28px; + border-bottom: 1px solid #23282e; + margin-left: 0; +} +.nav-side-menu ul .sub-menu li:hover, +.nav-side-menu li .sub-menu li:hover { + background-color: #020203; +} +.nav-side-menu ul .sub-menu li:before, +.nav-side-menu li .sub-menu li:before { + font-family: FontAwesome; + content: "\f105"; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; +} +.nav-side-menu li { + padding-left: 0; + border-left: 3px solid #2e353d; + border-bottom: 1px solid #23282e; +} +.nav-side-menu li a { + text-decoration: none; + color: #fff; + display: block; + width: 100%; + padding: 2px 0; +} +.nav-side-menu li a i { + padding-left: 10px; + width: 20px; + padding-right: 20px; +} +.nav-side-menu li:hover { + border-left: 3px solid #A3A3A3; +background: -moz-linear-gradient(left, rgba(245,245,245,0.8) 0%, rgba(35,35,35,0.98) 88%, rgba(35,35,35,1) 100%); +background: -webkit-linear-gradient(left, rgba(245,245,245,0.8) 0%,rgba(35,35,35,0.98) 88%,rgba(35,35,35,1) 100%); +background: linear-gradient(to right, rgba(245,245,245,0.8) 0%,rgba(35,35,35,0.98) 88%,rgba(35,35,35,1) 100%); +filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ccf5f5f5', endColorstr='#232323',GradientType=1 ); +} +.nav-side-menu li:hover a { + color: #fff; +} +@media (max-width: 991px) { + .nav-side-menu { + position: relative; + width: 100%; + } + .nav-side-menu .toggle-btn { + display: block; + cursor: pointer; + position: absolute; + right: 10px; + top: 10px; + z-index: 10 !important; + padding: 3px; + background-color: #ffffff; + color: #000; + width: 40px; + text-align: center; + border-radius: 3px; + } + .brand { + text-align: left !important; + font-size: 20px; + padding-left: 20px; + line-height: 20px !important; + } +} +@media (min-width: 992px) { + .nav-side-menu .menu-list .menu-content { + display: block; + } +} + +/* Top Bar */ +#top-bar { + background-color: #262626; + line-height: 50px; + display: block; + font-size: 17px; + color: #fff; + padding: 0 15px; + z-index: 1000; +} +#top-bar a{ + color: #fff; +} +#top-bar div { + display: inline-block; + padding: 0 15px; +} +#top-bar div strong { + font-weight: 700; +} +#top-bar #statsUpdated { + font-weight: normal; +} +@media (max-width: 1199px) { + #top-bar #statsUpdated .text { + display: none; + } +} +@media (min-width: 992px) { + #top-bar { + overflow: auto; + position: fixed; + top: 0; + left: 225px; + right: 0; + padding: 0 15px; + } +} +@media (max-width: 767px) { + #top-bar { + display: none; + } +} + +/* Bootstrap Buttons */ +.btn-default { + outline: none; + + text-align: center; + box-shadow: inset hsla(0,0%,15%, 1) 0 0px 0px 1px, /* border */ + inset hsla(0,0%,15%, .8) 0 0px 10px 4px, /* soft SD */ + + + hsla(0,0%, 0%,.16) 0 0px 10px 6px, /* outer SD */ + hsla(0,0%,100%,.5) 0 0px 0px -6px; /* outer HL */ + + transition: color .2s; +/* background-color: #A3A3A3; + border-color: #A3A3A3; +*/ +} +.btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { + color: #ffffff; + background-color: #A3A3A3; + border-color: #A3A3A3; +} +.btn-default.disabled,.btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { + background-color: #A3A3A3; + border-color: #A3A3A3; +} +.btn-default .badge { + color: #A3A3A3; + background-color: #ffffff; +} +.btn-primary { + color: #ffffff; + background-color: #A3A3A3; + border-color: #A3A3A3; +} +.btn-primary:hover,.btn-primary:focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #027454; + border-color: #026a4d; +} +.btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { + background-color: #03a678; + border-color: #03a678; +} +.btn-primary .badge { + color: #03a678; + background-color: #ffffff; +} +.button-small { +margin-left:0px; +padding:3px; +} +/* Bootstrap Form Controls */ +.input-group-addon { + color: #A3A3A3; +} + +/* Bootstrap Tables */ +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #000000; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #000000; +} +.table > thead > tr > th { + color: #A3A3A3; + border-bottom-color: #A3A3A3; + font-weight: 400; +} +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: rgba(165,165,165,0.70); +} +.table > tbody > tr:hover td, +.table > tbody > tr:hover th { + background-color: rgba(190,190,190,1.00); +} +.table > tbody > tr > td { + border-top-color: #262626; +} +table th.sort:hover{ + cursor: pointer; +} + +/* jQuery sparkline tooltip */ +.jqstooltip { + border: none !important; + background: rgba(0, 0, 0, 0.8) !important; + border-radius: 2px !important; + margin-top: -20px !important; + /* Override Bootstrap defaults to fix jQuery.Sparkline tooltips appearance */ + -webkit-box-sizing: content-box !important; + -moz-box-sizing: content-box; + box-sizing: content-box !important; +} +.jqstooltip .jqsfield { + color: #ccc; + font-size: 13px !important; +} +.jqstooltip .jqsfield b { + color: #fff; +} + +/* Content Card */ +.card { + display: inline-block; + position: relative; + width: 100%; + margin: 0; + padding: 0; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + border-radius: 3px; + color: rgba(0, 0, 0, 0.87); + background: #fff; +} + +.card table, .card .table { + margin-bottom: 0; +} + +/* Info box */ +.infoBox { + background: radial-gradient(ellipse farthest-corner at right bottom, #FEDB37 0%, #FDB931 8%, #9f7928 30%, #8A6E2F 40%, transparent 80%),radial-gradient(ellipse farthest-corner at left top, #FFFFFF 0%, #FFFFAC 8%, #D1B464 25%, #5d4a1f 62.5%, #5d4a1f 100%); + /*box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14);*/ +box-shadow: 0 0 20px #FFFFFF; + height: 80px; + cursor: default; + position: relative; + overflow: hidden; + margin-bottom: 30px; + border-radius: 3px; +} +@media (max-width: 767px){ + .infoBox{ + margin-bottom: 15px; + } +} +.infoBox .icon { + position: absolute; + right: 10px; + top: 10px; + text-align: center; + font-size: 60px; + line-height: 60px; + width: 75px; + text-align: center; + color: rgba(255, 255, 255, 0.3); +} +.infoBox .content { + display: inline-block; + padding: 7px 16px; +} +.infoBox .content .text { + font-size: 13px; + margin-top: 11px; + color: #fff; + text-transform: uppercase; +} +.infoBox .content .value { + font-size: 26px; + font-weight: 800; + margin-top: -4px; + color: #fff; + text-shadow: 1px 1px 1px #000; +} +.infoBox .content .value .smallText { + font-size: 16px; + font-weight: normal; +} +.smallerText { + font-size: 9px; + font-weight: normal; +} +#marketInfos .infoBox { + height: 100px; +} +#marketInfos .infoBox .source { + font-size: 13px; + font-style: italic; + color: #fff; +} +#marketInfos .infoBox .source a, #marketInfos .infoBox .source a:hover { + color: #fff; +} + + +/* Hash info box */ +.hashInfo { +background-color: #d7d7d7; background-image: linear-gradient(147deg, #353535 0%, #d7d7d7 100%); + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + cursor: default; + padding: 15px; + border-radius: 3px; + color: #fff; + margin-bottom: 30px; +} +@media (max-width: 767px){ + .hashInfo { + margin-bottom: 15px; + } +} +.hashInfo .text { + font-size: 13px; + color: #fff; + text-transform: uppercase; +} +.hashInfo .content { + overflow: hidden; +} +.hashInfo .content .value { + float: left; + font-family: 'Inconsolata', monospace; + font-size: 16px; + font-weight: 800; + color: #fff; + padding-right: 8px; +} +.hashInfo .content .value a { + color: #fff; +} +.hashInfo .content .time { + float: left; + font-size: 15px; + font-weight: normal; +} + +/* Slush payment info box */ +.slushInfo { + background-color: #262626; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); + cursor: default; + padding: 15px; + border-radius: 3px; + color: #fff; + margin-bottom: 30px; +} +.slushInfo .text { + font-size: 13px; + color: #fff; + text-transform: uppercase; +} +@media (max-width: 767px){ + .slushInfo { + margin-bottom: 15px; + } +} +.slushInfo .content .value { + float: left; + font-size: 16px; + font-weight: 400; + color: #fff; + padding: 8px 8px 0 0; +} +.slushInfo .content .value a { + color: #fff; +} + +/* Pool Statistics */ +.stats > div:not(#addressError) { + color: #262626; + padding: 10px 0px; +} +.stats > div:not(#addressError).marketFooter { + padding: 5px 0; +} +.stats > div:not(#addressError) > i.fa { + color: #A3A3A3; + font-size: 21px; + width: 30px; + text-align: center; +} +.stats > div:not(#addressError) > span:not(.input-group-btn):first-of-type { + font-weight: 500; + padding: 0 2px; + color: #A3A3A3; +} + +/* Pool and Worker Charts */ +.poolChart, .marketChart, .userChart, .userChart_coin2, .userChart_coin3 { + display: none; +} +.poolChart .chart, .marketChart .chart, .userChart .chart, .userChart_coin2 .chart, .userChart_coin3 .chart { + height: 140px; +} +.poolChart, .marketChart { + margin-bottom: 30px; +} +@media (max-width: 767px){ + .poolChart, .marketChart { + margin-bottom: 15px; + } +} +.poolChart h4, +.marketChart h4, +.userChart h4, +.userChart_coin2 h4, +.userChart_coin3 h4 { + text-align: center; + font-size: 21px; +} +.chart a.chart-style { + display: none; /* never displayed; we just use it to store the following colors: */ + background-color: rgba(58,58,58,0.60); + border-color: #161616; + width: 2px; /* controls the chart border width (must be specified in px) */ +} + +/* Market Rates */ +#marketStats { + margin-top: 20px; +} +#marketUpdate { + display: none; +} + +/* Mining Profit Calculator */ +#miningProfitCalc{ + margin: 35px 0; +} +#miningProfitCalc .input-group-addon{ + padding: 6px; +} +#miningProfitCalc .input-group-addon, +#miningProfitCalc .input-group-btn .btn, +#miningProfitCalc .input-group .form-control{ + height:45px; +} +#calcHashDropdown{ + border-radius: 0; + border-left: 0; + border-right: 0; +} +#calcHashHolder{ + width: 590px; + max-width: 100%; +} +#calcHashRate{ + z-index: inherit; + font-family: 'Inconsolata', monospace; +} +#calcHashAmount{ + font-family: 'Inconsolata', monospace; + display: inline-block; + text-align: center; + vertical-align: middle; + overflow: hidden; +} +#calcHashPeriod{ + font-family: 'Inconsolata', monospace; + display: inline-block; + width: 60px; + vertical-align: middle; +} +#calcHashResultsHolder{ + min-width: 170px; + max-width: 170px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +@media (min-width: 768px) { + #calcHashResultsHolder{ + min-width: 225px; + max-width: 225px; + } +} + +/* Mining Ports */ +#miningPorts tr > td { + vertical-align: middle; + font-size: 0.95em; +} +#miningPorts table .col1, +#miningPorts table .col2 { + text-align: center; +} +@media (min-width: 768px) { + #miningPorts table th { + white-space: nowrap; + } + #miningPorts table .col1 { + width:60px; + } + #miningPorts table .col2 { + width:160px; + text-align: center; + } +} + +/* Mining Apps */ +#miningApps .table > thead > tr > th { + white-space: nowrap; +} +#miningApps_rows > tr:first-child > td { + padding-top: 10px; +} +#miningApps_rows > tr > td { + border-top: none; +} +#miningApps_rows .appInfo td { + padding: 10px; + margin: 0; +} +#miningApps_rows .appConfig td { + padding: 0 10px; +} +#miningApps_rows tr:hover, +#miningApps_rows tr:hover > td, +#miningApps_rows tr:hover > th { + background-color: transparent; +} +#miningApps .btn { + width: 100%; +} +#miningApps .miningAppTitle { + font-weight: bold; +} +#miningApps .exampleAddress, +#miningApps .exampleWorkerName { + font-style: italic; +} + +/* Worker Statistics */ +#yourStatsInput, +#yourStatsInput_coin2, +#yourStatsInput_coin3 { + z-index: inherit; + font-family: 'Inconsolata', monospace; +} +#yourAddressDisplay { + word-wrap: break-word; +} +#yourAddressDisplay > span { + font-family: 'Inconsolata', monospace; +} +#lookUp > span:nth-child(2), +#lookUp_coin2 > span:nth-child(2), +#lookUp_coin3 > span:nth-child(2) { + display: none; +} +.yourStats, +.yourWorkers, +.yourStats_coin2, +.yourWorkers_coin2, +.yourStats_coin3, +.yourWorkers_coin3, +.pool_yourStats, +.solo_yourStats, +.solo_yourWorkers, +.pool_yourStats_coin2, +.solo_yourStats_coin2, +.solo_yourWorkers_coin2, +.pool_yourStats_coin3, +.solo_yourStats_coin3, +.solo_yourWorkers_coin3 { + display: none; +} +.tab-pane #workerStats, +.tab-pane #workerStats_coin2, +.tab-pane #workerStats_coin3 { + padding-top: 1em; +} +#yourAddressDisplay { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.9em; +} +#addressError, +#addressError_coin2, +#addressError_coin3 { + color: red; +} +#workersReport td, +#workersReport_coin2 td, +#workersReport_coin3 td { + vertical-align: middle; +} +#workersReport .status-ok, +#workersReport_coin2 .status-ok, +#workersReport_coin3 .status-ok { + color: #17a600; +} +#workersReport .status-error, +#workersReport_coin2 .status-error, +#workersReport_coin3 .status-error { + color: #a60000; +} +#workersReport table .col1, +#workersReport table .col3, +#workersReport table .col4, +#workersReport table .col5, +#workersReport_coin2 table .col1, +#workersReport_coin2 table .col3, +#workersReport_coin2 table .col4, +#workersReport_coin2 table .col5, +#workersReport_coin3 table .col1, +#workersReport_coin3 table .col3, +#workersReport_coin3 table .col4, +#workersReport_coin3 table .col5 { + text-align: center; +} + +#solo_workersReport td, +#solo_workersReport_coin2 td, +#solo_workersReport_coin3 td { + vertical-align: middle; +} +#solo_workersReport .status-ok, +#solo_workersReport_coin2 .status-ok, +#solo_workersReport_coin3 .status-ok { + color: #17a600; +} +#solo_workersReport .status-error, +#solo_workersReport_coin2 .status-error, +#solo_workersReport_coin3 .status-error { + color: #a60000; +} +#solo_workersReport table .col1, +#solo_workersReport table .col3, +#solo_workersReport table .col4, +#solo_workersReport table .col5, +#solo_workersReport_coin2 table .col1, +#solo_workersReport_coin2 table .col3, +#solo_workersReport_coin2 table .col4, +#solo_workersReport_coin2 table .col5, +#solo_workersReport_coin3 table .col1, +#solo_workersReport_coin3 table .col3, +#solo_workersReport_coin3 table .col4, +#solo_workersReport_coin3 table .col5 { + text-align: center; +} +@media (min-width: 768px) { + #workersReport table th, + #workersReport_coin2 table th, + #workersReport_coin3 table th { + white-space: nowrap; + } + #workersReport table .col1, + #workersReport_coin2 table .col1, + #workersReport_coin3 table .col1 { + width:80px; + } + #workersReport table .col3, + #workersReport table .col4, + #workersReport table .col5, + #workersReport table .col6, + #workersReport_coin2 table .col3, + #workersReport_coin2 table .col4, + #workersReport_coin2 table .col5, + #workersReport_coin2 table .col6, + #workersReport_coin3 table .col3, + #workersReport_coin3 table .col4, + #workersReport_coin3 table .col5, + #workersReport_coin3 table .col6 { + width:120px; + } + #workersReport table .col7, + #workersReport_coin2 table .col7, + #workersReport_coin3 table .col7 { + width: 210px; + } + #workersReport table .col8, + #workersReport_coin2 table .col8, + #workersReport_coin3 table .col8 { + width: 210px; + } + + #solo_workersReport table th, + #solo_workersReport_coin2 table th, + #solo_workersReport_coin3 table th { + white-space: nowrap; + } + #solo_workersReport table .col1, + #solo_workersReport_coin2 table .col1, + #solo_workersReport_coin3 table .col1 { + width:80px; + } + #solo_workersReport table .col3, + #solo_workersReport table .col4, + #solo_workersReport table .col5, + #solo_workersReport table .col6, + #solo_workersReport_coin2 table .col3, + #solo_workersReport_coin2 table .col4, + #solo_workersReport_coin2 table .col5, + #solo_workersReport_coin2 table .col6, + #solo_workersReport_coin3 table .col3, + #solo_workersReport_coin3 table .col4, + #solo_workersReport_coin3 table .col5, + #solo_workersReport_coin3 table .col6 { + width:120px; + } + #solo_workersReport table .col7, + #solo_workersReport_coin2 table .col7, + #solo_workersReport_coin3 table .col7 { + width: 210px; + } + #solo_workersReport table .col8, + #solo_workersReport_coin2 table .col8, + #solo_workersReport_coin3 table .col8 { + width: 210px; + } +} +#workerPayments td, +#workerPayments_coin2 td, +#workerPayments_coin3 td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} +#workerPayments table .col3, +#workerPayments table .col4, +#workerPayments_coin2 table .col3, +#workerPayments_coin2 table .col4, +#workerPayments_coin3 table .col3, +#workerPayments_coin3 table .col4 { + text-align: center; +} +#workerPayments table .summary, +#workerPayments_coin2 table .summary, +#workerPayments_coin3 table .summary { + font-weight: 700; +} +@media (min-width: 768px) { + #workerPayments table, + #workerPayments_coin2 table, + #workerPayments_coin3 table { + table-layout:fixed; + } + #workerPayments table th, + #workerPayments_coin2 table th, + #workerPayments_coin3 table th { + white-space: nowrap; + } + #workerPayments table .col1, + #workerPayments_coin2 table .col1, + #workerPayments_coin3 table .col1 { + width:190px; + } + #workerPayments table .col2, + #workerPayments_coin2 table .col2, + #workerPayments_coin3 table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + #workerPayments table .col3, + #workerPayments_coin2 table .col3, + #workerPayments_coin3 table .col3 { + width:130px; + } + #workerPayments table .col4, + #workerPayments_coin2 table .col4, + #workerPayments_coin3 table .col4 { + width: 70px; + } +} + +/* Pool Blocks */ +#blocksStats, +#blocksStats_coin2, +#blocksStats_coin3 { + margin-top: 20px; +} +#blocksStats .infoBox, +#blocksStats_coin2 .infoBox, +#blocksStats_coin3 .infoBox { + margin-bottom: 15px; +} +#blocksStats .luckGood, +#blocksStats_coin2 .luckGood, +#blocksStats_coin3 .luckGood { + color: #5eff5e; +} +#blocksStats .luckBad, +#blocksStats_coin2 .luckBad, +#blocksStats_coin3 .luckBad { + color: red; +} +#blocksStats .luckMid, +#blocksStats_coin2 .luckMid, +#blocksStats_coin3 .luckMid { + color: #FFF500; +} +#blocksReport tr > td, +#blocksReport_coin2 tr > td, +#blocksReport_coin3 tr > td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} +#blocksReport table .col2, +#blocksReport table .col3, +#blocksReport table .col4, +#blocksReport table .col6, +#blocksReport table .col7, +#blocksReport_coin2 table .col2, +#blocksReport_coin2 table .col3, +#blocksReport_coin2 table .col4, +#blocksReport_coin2 table .col6, +#blocksReport_coin2 table .col7, +#blocksReport_coin3 table .col2, +#blocksReport_coin3 table .col3, +#blocksReport_coin3 table .col4, +#blocksReport_coin3 table .col6, +#blocksReport_coin3 table .col7 { + text-align: center; +} +#solo_blocksReport tr > td, +#solo_blocksReport_coin2 tr > td, +#solo_blocksReport_coin3 tr > td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} +#solo_blocksReport table .col2, +#solo_blocksReport table .col3, +#solo_blocksReport table .col4, +#solo_blocksReport table .col6, +#solo_blocksReport table .col7, +#solo_blocksReport_coin2 table .col2, +#solo_blocksReport_coin2 table .col3, +#solo_blocksReport_coin2 table .col4, +#solo_blocksReport_coin2 table .col6, +#solo_blocksReport_coin2 table .col7, +#solo_blocksReport_coin3 table .col2, +#solo_blocksReport_coin3 table .col3, +#solo_blocksReport_coin3 table .col4, +#solo_blocksReport_coin3 table .col6, +#solo_blocksReport_coin3 table .col7 { + text-align: center; +} +@media (min-width: 768px) { + #blocksReport table, + #blocksReport_coin2 table, + #blocksReport_coin3 table { + table-layout:fixed; + } + #blocksReport table th, + #blocksReport_coin2 table th, + #blocksReport_coin3 table th { + white-space: nowrap; + } + #blocksReport table .col1, + #blocksReport_coin2 table .col1, + #blocksReport_coin3 table .col1 { + width:100px; + } + #blocksReport table .col2, + #blocksReport_coin2 table .col2, + #blocksReport_coin3 table .col2 { + width:170px; + } + #blocksReport table .col3, + #blocksReport_coin2 table .col3 { + width:90px; + } + #blocksReport table .col4, + #blocksReport_coin2 table .col4, + #blocksReport_coin3 table .col4 { + width:120px; + } + #blocksReport table .col5, + #blocksReport_coin2 table .col5, + #blocksReport_coin3 table .col5 { + overflow: hidden; + text-overflow: ellipsis; + } + #blocksReport table .col6, + #blocksReport_coin2 table .col6, + #blocksReport_coin3 table .col6 { + text-shadow: 1px 1px #000000; + width: 55px; + } + #blocksReport table .col7, + #blocksReport_coin2 table .col7, + #blocksReport_coin3 table .col7 { + width: 80px; + } + #solo_blocksReport table, + #solo_blocksReport_coin2 table, + #solo_blocksReport_coin3 table { + table-layout:fixed; + } + #solo_blocksReport table th, + #solo_blocksReport_coin2 table th, + #solo_blocksReport_coin3 table th { + white-space: nowrap; + } + #solo_blocksReport table .col1, + #solo_blocksReport_coin2 table .col1, + #solo_blocksReport_coin3 table .col1 { + width:100px; + } + #solo_blocksReport table .col2, + #solo_blocksReport_coin2 table .col2, + #solo_blocksReport_coin3 table .col2 { + width:170px; + } + #solo_blocksReport table .col3, + #solo_blocksReport_coin2 table .col3, + #solo_blocksReport_coin3 table .col3 { + width:90px; + } + #solo_blocksReport table .col4, + #solo_blocksReport_coin2 table .col4, + #solo_blocksReport_coin3 table .col4 { + width:120px; + } + #solo_blocksReport table .col5, + #solo_blocksReport_coin2 table .col5, + #solo_blocksReport_coin3 table .col5 { + overflow: hidden; + text-overflow: ellipsis; + } + #solo_blocksReport table .col6, + #solo_blocksReport_coin2 table .col6, + #solo_blocksReport_coin3 table .col6 { + text-shadow: 1px 1px #000000; + width: 55px; + } + #solo_blocksReport table .col7, + #solo_blocksReport_coin2 table .col7, + #solo_blocksReport_coin3 table .col7 { + width: 80px; + } +} +#blocksReport .pending td, +#blocksReport_coin2 .pending td, +#blocksReport_coin3 .pending td { + background-color:rgba(255, 255, 255, .3); +} +#blocksReport .unlocked td, +#blocksReport_coin2 .unlocked td, +#blocksReport_coin3 .unlocked td { +background: #76b852; /* fallback for old browsers */ +background: -webkit-linear-gradient(to right, #8DC26F, #76b852); /* Chrome 10-25, Safari 5.1-6 */ +background: linear-gradient(to right, #8DC26F, #76b852); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + +} +#blocksReport .orphaned td, +#blocksReport_coin2 .orphaned td, +#blocksReport_coin3 .orphaned td { +background: #e53935; /* fallback for old browsers */ +background: -webkit-linear-gradient(to right, #e35d5b, #e53935); /* Chrome 10-25, Safari 5.1-6 */ +background: linear-gradient(to right, #e35d5b, #e53935); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + +} +#blocksReport .luckGood, +#blocksReport .unlocked .col7, +#blocksReport_coin2 .luckGood, +#blocksReport_coin2 .unlocked .col7, +#blocksReport_coin3 .luckGood, +#blocksReport_coin3 .unlocked .col7 { + color: #17a600; +} +#blocksReport .luckBad, +#blocksReport .orphaned .col7, +#blocksReport_coin2 .luckBad, +#blocksReport_coin2 .orphaned .col7, +#blocksReport_coin3 .luckBad, +#blocksReport_coin3 .orphaned .col7 { + color: #a60000; +} +#blocksReport .luckMid, +#blocksReport_coin2 .luckMid, +#blocksReport_coin3 .luckMid { + color: #df9d00; +} + +#solo_blocksReport .pending td, +#solo_blocksReport_coin2 .pending td, +#solo_blocksReport_coin3 .pending td { + background-color:rgba(255, 255, 255, .3); +} +#solo_blocksReport .unlocked td, +#solo_blocksReport_coin2 .unlocked td, +#solo_blocksReport_coin3 .unlocked td { +background: #76b852; /* fallback for old browsers */ +background: -webkit-linear-gradient(to right, #8DC26F, #76b852); /* Chrome 10-25, Safari 5.1-6 */ +background: linear-gradient(to right, #8DC26F, #76b852); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + +} +#solo_blocksReport .orphaned td, +#solo_blocksReport_coin2 .orphaned td, +#solo_blocksReport_coin3 .orphaned td { +background: #e53935; /* fallback for old browsers */ +background: -webkit-linear-gradient(to right, #e35d5b, #e53935); /* Chrome 10-25, Safari 5.1-6 */ +background: linear-gradient(to right, #e35d5b, #e53935); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + +} +#solo_blocksReport .luckGood, +#solo_blocksReport .unlocked .col7, +#solo_blocksReport_coin2 .luckGood, +#solo_blocksReport_coin2 .unlocked .col7, +#solo_blocksReport_coin3 .luckGood, +#solo_blocksReport_coin3 .unlocked .col7 { + color: #17a600; +} +#solo_blocksReport .luckBad, +#solo_blocksReport .orphaned .col7, +#solo_blocksReport_coin2 .luckBad, +#solo_blocksReport_coin2 .orphaned .col7, +#solo_blocksReport_coin3 .luckBad, +#solo_blocksReport_coin3 .orphaned .col7 { + color: #a60000; +} +#solo_blocksReport .luckMid, +#solo_blocksReport_coin2 .luckMid, +#solo_blocksReport_coin3 .luckMid { + color: #df9d00; +} +/* Payments */ +#paymentsStats { + margin-top: 20px; +} +#paymentsStats .infoBox { + margin-bottom: 15px; +} +#paymentsReport td, +#paymentsReport_coin2 td, +#paymentsReport_coin3 td { + vertical-align: middle; + font-family: 'Inconsolata', monospace; + font-size: 0.95em; +} +#paymentsReport table .col3, +#paymentsReport table .col4, +#paymentsReport table .col5, +#paymentsReport table .col6, +#paymentsReport_coin2 table .col3, +#paymentsReport_coin2 table .col4, +#paymentsReport_coin2 table .col5, +#paymentsReport_coin2 table .col6, +#paymentsReport_coin3 table .col3, +#paymentsReport_coin3 table .col4, +#paymentsReport_coin3 table .col5, +#paymentsReport_coin3 table .col6 { + text-align: center; +} +@media (min-width: 768px) { + #paymentsReport table { + table-layout:fixed; + } + #paymentsReport table th { + white-space: nowrap; + } + #paymentsReport table .col1 { + width:190px; + } + #paymentsReport table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + #paymentsReport table .col3 { + width:130px; + } + #paymentsReport table .col4 { + width:110px; + } + #paymentsReport table .col5 { + width: 70px; + } + #paymentsReport table .col6 { + width: 70px; + } + + #paymentsReport_coin2 table, + #paymentsReport_coin3 table { + table-layout:fixed; + } + #paymentsReport_coin2 table th, + #paymentsReport_coin3 table th { + white-space: nowrap; + } + #paymentsReport_coin2 table .col1, + #paymentsReport_coin3 table .col1 { + width:190px; + } + #paymentsReport_coin2 table .col2, + #paymentsReport_coin3 table .col2 { + overflow: hidden; + text-overflow: ellipsis; + } + #paymentsReport_coin2 table .col3, + #paymentsReport_coin3 table .col3 { + width:130px; + } + #paymentsReport_coin2 table .col4, + #paymentsReport_coin3 table .col4 { + width:110px; + } + #paymentsReport_coin2 table .col5, + #paymentsReport_coin3 table .col5 { + width: 70px; + } + #paymentsReport_coin2 table .col6, + #paymentsReport_coin3 table .col6 { + width: 70px; + } +} + +/* Top 10 miners */ +#top10miners td { + vertical-align: middle; +} +#top10miners table .col1, +#top10miners table .col3, +#top10miners table .col4, +#top10miners table .col5, +#top10miners table .col6 { + text-align: center; +} +@media (min-width: 768px) { + #top10miners table th { + white-space: nowrap; + } + #top10miners table .col1 { + width:80px; + } + #top10miners table .col3 { + width:150px; + } + #top10miners table .col4 { + width: 210px; + } + #top10miners table .col5 { + width: 210px; + } + #top10miners table .col6 { + width: 210px; + } +} + +/* Language selector */ +#langSelector { + float: right; + padding-right: 0 !important; +} + +#langSelector select { + display: inline-block; + width: auto; + height: 32px; + padding: 6px 10px; +} + +#mLangSelector { + display: none; + padding: 10px; +} +@media (max-width: 767px) { + #mLangSelector { + display: block; + } +} + +.circle-white { + width: 32px; + height: 32px; + /*padding: 2px;*/ + margin-top:-4px; + margin-right:15px; + background-color:#FFFFFF; + -moz-border-radius: 70px; + -webkit-border-radius: 70px; + border-radius: 70px; +} +.px5-right { + margin-right: 5px; +} +.px5-left { + margin-left: 5px; +} +.px15-left { + margin-left: 15px; +} +.px15-top { + margin-top: 15px; +} +.wrapMe { + word-wrap: break-word; +} + + +@media print { + body { + -webkit-print-color-adjust: exact; + background-image: none; + } + .noprint { + display: none; + } + .px15-left { + margin-left: 0px; + } + h3.replaceHeader:after { + content: "Cryptonote Club - Paper Wallet"; + } + .fa-bars { + display: none !important; + } + .address_widget, .gui_key_widget, .seed_key_widget, .cli_key_widget { + font-size: 10px !important; + font-weight: bold; + } + .seed_key_widget{ + max-width: 70%; + } + .wallet, .instruction { + margin-left:12px; + } + a.noTextPrint:after { + content: ' '; + font-size:16px; /* original font size */ + } + ::placeholder { + color: transparent; + opacity: 1; /* Firefox */ + } + :-ms-input-placeholder { /* Internet Explorer 10-11 */ + color: transparent; + } + ::-ms-input-placeholder { /* Microsoft Edge */ + color: transparent; + } + +} From f7385d3dd04a2c788079f282e8ba84fd144491a4 Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Sun, 25 Oct 2020 19:54:36 +0100 Subject: [PATCH 97/98] Add files via upload --- config_examples/2acoin.json | 323 +++ config_examples/aeon-k12/aeon.json | 303 +++ config_examples/aeon-k12/api.js | 2063 ++++++++++++++++++ config_examples/aeon-k12/blockUnlocker.js | 279 +++ config_examples/aeon-k12/package.json | 33 + config_examples/aeon-k12/pool.js | 1212 ++++++++++ config_examples/aeon-k12/utils.js | 151 ++ config_examples/aeon.json | 577 ++--- config_examples/alloy.json | 599 ++--- config_examples/arqma.json | 588 ++--- config_examples/bbscoin.json | 599 ++--- config_examples/bloc.json | 320 +++ config_examples/bytecoin.json | 585 ++--- config_examples/bytecoin/apiInterfaces.js | 128 ++ config_examples/bytecoin/paymentProcessor.js | 441 ++++ config_examples/croat.json | 599 ++--- config_examples/dero.json | 597 ++--- config_examples/digitalnote.json | 597 ++--- config_examples/electronero.json | 588 ++--- config_examples/electroneum.json | 599 ++--- config_examples/equilibria.json | 328 +++ config_examples/fandomgold.json | 317 +++ config_examples/festival.json | 586 ++--- config_examples/graft.json | 599 ++--- config_examples/haven.json | 599 ++--- config_examples/intensecoin.json | 599 ++--- config_examples/intucoin.json | 599 ++--- config_examples/iridium.json | 599 ++--- config_examples/italocoin.json | 604 ++--- config_examples/kepl.json | 319 +++ config_examples/krypton.json | 311 +++ config_examples/lethean.json | 327 +++ config_examples/loki.json | 601 ++--- config_examples/luka.json | 599 ++--- config_examples/mangocoin.json | 320 +++ config_examples/masari.json | 601 ++--- config_examples/masari/masari.json | 322 +++ config_examples/masari/notes.txt | 1 + config_examples/masari/package.json | 32 + config_examples/monero.json | 604 ++--- config_examples/myztic.json | 595 ++--- config_examples/newton.json | 355 +-- config_examples/qwertycoin.json | 587 ++--- config_examples/safex/config.json | 325 +++ config_examples/safex/package.json | 30 + config_examples/stellite.json | 600 ++--- config_examples/sumokoin.json | 601 ++--- config_examples/superior.json | 595 ++--- config_examples/talleo.json | 332 +++ config_examples/turtlecoin.json | 320 +++ config_examples/ultranote.json | 565 ++--- config_examples/woolong.json | 320 +++ config_examples/worktips.json | 318 +++ config_examples/wownero.json | 588 ++--- config_examples/x-cash/package.json | 32 + config_examples/x-cash/x-cash.json | 323 +++ config_examples/xmv.json | 595 ++--- 57 files changed, 18414 insertions(+), 8715 deletions(-) create mode 100644 config_examples/2acoin.json create mode 100644 config_examples/aeon-k12/aeon.json create mode 100644 config_examples/aeon-k12/api.js create mode 100644 config_examples/aeon-k12/blockUnlocker.js create mode 100644 config_examples/aeon-k12/package.json create mode 100644 config_examples/aeon-k12/pool.js create mode 100644 config_examples/aeon-k12/utils.js create mode 100644 config_examples/bloc.json create mode 100644 config_examples/bytecoin/apiInterfaces.js create mode 100644 config_examples/bytecoin/paymentProcessor.js create mode 100644 config_examples/equilibria.json create mode 100644 config_examples/fandomgold.json create mode 100644 config_examples/kepl.json create mode 100644 config_examples/krypton.json create mode 100644 config_examples/lethean.json create mode 100644 config_examples/mangocoin.json create mode 100644 config_examples/masari/masari.json create mode 100644 config_examples/masari/notes.txt create mode 100644 config_examples/masari/package.json create mode 100644 config_examples/safex/config.json create mode 100644 config_examples/safex/package.json create mode 100644 config_examples/talleo.json create mode 100644 config_examples/turtlecoin.json create mode 100644 config_examples/woolong.json create mode 100644 config_examples/worktips.json create mode 100644 config_examples/x-cash/package.json create mode 100644 config_examples/x-cash/x-cash.json diff --git a/config_examples/2acoin.json b/config_examples/2acoin.json new file mode 100644 index 000000000..824d98ab6 --- /dev/null +++ b/config_examples/2acoin.json @@ -0,0 +1,323 @@ +{ + "poolHost": "your.pool.host", + + "coin": "2ACoin", + "symbol": "ARMS", + "coinUnits": 100000000, + "coinDecimalPlaces": 8, + "coinDifficultyTarget": 90, + + "daemonType": "2acoin", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 2, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "CPU - (30-40 H/s)" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware", + "hidden": true + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "CPU (50-200H/s)" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "CPU/GPU (>300 H/s)" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true, + "hidden": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+" + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 60 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 50000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 500000000, + "maxPayment": 7500000000, + "maxTransactionAmount": 15000000000, + "denomination": 10000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 30, + "poolFee": 0.5, + "devDonation": 0.2, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 19760, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 19761, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": false + }, + + "daemon": { + "host": "127.0.0.1", + "port": 17910 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 17760, + "password": "**WALLET RPC PASSWORD**" + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelBlocks": false, + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "firstcryptobank", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/aeon-k12/aeon.json b/config_examples/aeon-k12/aeon.json new file mode 100644 index 000000000..ce8a55fdb --- /dev/null +++ b/config_examples/aeon-k12/aeon.json @@ -0,0 +1,303 @@ +{ + "poolHost": "your.pool.host", + + "coin": "aeon", + "symbol": "AEON", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 12, + "coinDifficultyTarget": 240, + + "cnAlgorithm": "K12", + "cnVariant": 1, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "hashingUtil": true, + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "Your pool wallet address", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 4333, + "difficulty": 100000000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 500000000, + "desc": "Mid range hardware" + }, + { + "port": 4555, + "difficulty": 1000000000, + "desc": "High end hardware" + }, + { + "port": 4777, + "difficulty": 10000000000, + "desc": "Test" + }, + { + "port": 4888, + "difficulty": 100000000000, + "desc": "Test" + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": "+", + "validation": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 120, + "maxAddresses": 20, + "mixin": 4, + "priority": 0, + "transferFee": 10000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 250000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 10000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.0, + "devDonation": 0.2, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 3, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 21181 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 21190 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } +} diff --git a/config_examples/aeon-k12/api.js b/config_examples/aeon-k12/api.js new file mode 100644 index 000000000..45f281f40 --- /dev/null +++ b/config_examples/aeon-k12/api.js @@ -0,0 +1,2063 @@ +/** + * Cryptonote Node.JS Pool + * https://github.com/dvandal/cryptonote-nodejs-pool + * + * Pool API + **/ + +// Load required modules +let fs = require('fs'); +let http = require('http'); +let https = require('https'); +let url = require("url"); +let async = require('async'); + +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet); +let authSid = Math.round(Math.random() * 10000000000) + '' + Math.round(Math.random() * 10000000000); + +let charts = require('./charts.js'); +let notifications = require('./notifications.js'); +let market = require('./market.js'); +let utils = require('./utils.js'); + +// Initialize log system +let logSystem = 'api'; +require('./exceptionWriter.js')(logSystem); + +// Data storage variables used for live statistics +let currentStats = {}; +let minerStats = {}; +let minersHashrate = {}; + +let liveConnections = {}; +let addressConnections = {}; + +/** + * Handle server requests + **/ +function handleServerRequest (request, response) { + let urlParts = url.parse(request.url, true); + + switch (urlParts.pathname) { + // Pool statistics + case '/stats': + handleStats(urlParts, request, response); + break; + case '/live_stats': + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Connection': 'keep-alive' + }); + + let address = urlParts.query.address ? urlParts.query.address : 'undefined'; + let uid = Math.random() + .toString(); + let key = address + ':' + uid; + + response.on("finish", function () { + delete liveConnections[key]; + }); + response.on("close", function () { + delete liveConnections[key]; + }); + + liveConnections[key] = response; + break; + + // Worker statistics + case '/stats_address': + handleMinerStats(urlParts, response); + break; + + // Payments + case '/get_payments': + handleGetPayments(urlParts, response); + break; + + // Blocks + case '/get_blocks': + handleGetBlocks(urlParts, response); + break; + + // Get market prices + case '/get_market': + handleGetMarket(urlParts, response); + break; + + // Top 10 miners + case '/get_top10miners': + handleTopMiners(response); + break; + + // Miner settings + case '/get_miner_payout_level': + handleGetMinerPayoutLevel(urlParts, response); + break; + case '/set_miner_payout_level': + handleSetMinerPayoutLevel(urlParts, response); + break; + case '/get_email_notifications': + handleGetMinerNotifications(urlParts, response); + break; + case '/set_email_notifications': + handleSetMinerNotifications(urlParts, response); + break; + case '/get_telegram_notifications': + handleGetTelegramNotifications(urlParts, response); + break; + case '/set_telegram_notifications': + handleSetTelegramNotifications(urlParts, response); + break; + + // Miners/workers hashrate (used for charts) + case '/miners_hashrate': + if (!authorize(request, response)) { + return; + } + handleGetMinersHashrate(response); + break; + case '/workers_hashrate': + if (!authorize(request, response)) { + return; + } + handleGetWorkersHashrate(response); + break; + + // Pool Administration + case '/admin_stats': + if (!authorize(request, response)) + return; + handleAdminStats(response); + break; + case '/admin_monitoring': + if (!authorize(request, response)) { + return; + } + handleAdminMonitoring(response); + break; + case '/admin_log': + if (!authorize(request, response)) { + return; + } + handleAdminLog(urlParts, response); + break; + case '/admin_users': + if (!authorize(request, response)) { + return; + } + handleAdminUsers(response); + break; + case '/admin_ports': + if (!authorize(request, response)) { + return; + } + handleAdminPorts(response); + break; + + // Test notifications + case '/test_email_notification': + if (!authorize(request, response)) { + return; + } + handleTestEmailNotification(urlParts, response); + break; + case '/test_telegram_notification': + if (!authorize(request, response)) { + return; + } + handleTestTelegramNotification(urlParts, response); + break; + + // Default response + default: + response.writeHead(404, { + 'Access-Control-Allow-Origin': '*' + }); + response.end('Invalid API call'); + break; + } +} + +/** + * Collect statistics data + **/ +function collectStats () { + let startTime = Date.now(); + let redisFinished; + let daemonFinished; + + let redisCommands = [ + ['zremrangebyscore', config.coin + ':hashrate', '-inf', ''], + ['zrange', config.coin + ':hashrate', 0, -1], + ['hgetall', config.coin + ':stats'], + ['zrange', config.coin + ':blocks:candidates', 0, -1, 'WITHSCORES'], + ['zrevrange', config.coin + ':blocks:matured', 0, config.api.blocks - 1, 'WITHSCORES'], + ['hgetall', config.coin + ':scores:roundCurrent'], + ['hgetall', config.coin + ':stats'], + ['zcard', config.coin + ':blocks:matured'], + ['zrevrange', config.coin + ':payments:all', 0, config.api.payments - 1, 'WITHSCORES'], + ['zcard', config.coin + ':payments:all'], + ['keys', config.coin + ':payments:*'], + ['hgetall', config.coin + ':shares_actual:roundCurrent'], + ['zrange', config.coin + ':blocks:matured', 0, config.api.luckBlocks ? config.api.luckBlocks : -1, 'WITHSCORES'] + ]; + + let windowTime = (((Date.now() / 1000) - config.api.hashrateWindow) | 0) + .toString(); + redisCommands[0][3] = '(' + windowTime; + + async.parallel({ + pool: function (callback) { + redisClient.multi(redisCommands) + .exec(function (error, replies) { + redisFinished = Date.now(); + let dateNowSeconds = Date.now() / 1000 | 0; + + if (error) { + log('error', logSystem, 'Error getting redis data %j', [error]); + callback(true); + return; + } + + let data = { + stats: replies[2], + blocks: replies[3].concat(replies[4]), + totalBlocks: parseInt(replies[7]) + (replies[3].length / 2), + totalDiff: 0, + totalShares: 0, + payments: replies[8], + totalPayments: parseInt(replies[9]), + totalMinersPaid: replies[10] && replies[10].length > 0 ? replies[10].length - 1 : 0, + miners: 0, + workers: 0, + hashrate: 0, + roundScore: 0, + roundHashes: 0 + }; + + let blocksForCalculation = replies[3].concat(replies[12]); + for (var i = 0; i < blocksForCalculation.length; i++) { + var block = blocksForCalculation[i].split(':'); + if (block[5]) { + var blockShares = parseInt(block[3]); + var blockDiff = parseInt(block[2]); + data.totalDiff += blockDiff; + data.totalShares += blockShares; + } + } + + minerStats = {}; + minersHashrate = {}; + + let hashrates = replies[1]; + for (let i = 0; i < hashrates.length; i++) { + let hashParts = hashrates[i].split(':'); + minersHashrate[hashParts[1]] = (minersHashrate[hashParts[1]] || 0) + parseInt(hashParts[0]); + } + + let totalShares = 0; + + for (let miner in minersHashrate) { + if (miner.indexOf('~') !== -1) { + data.workers++; + } else { + totalShares += minersHashrate[miner]; + data.miners++; + } + + minersHashrate[miner] = Math.round(minersHashrate[miner] / config.api.hashrateWindow); + + if (!minerStats[miner]) { + minerStats[miner] = {}; + } + minerStats[miner]['hashrate'] = minersHashrate[miner]; + } + + data.hashrate = Math.round(totalShares / config.api.hashrateWindow); + + data.roundScore = 0; + + if (replies[5]) { + for (let miner in replies[5]) { + let roundScore = parseFloat(replies[5][miner]); + + data.roundScore += roundScore; + + if (!minerStats[miner]) { + minerStats[miner] = {}; + } + minerStats[miner]['roundScore'] = roundScore; + } + } + + data.roundHashes = 0; + + if (replies[11]) { + for (let miner in replies[11]) { + let roundHashes = parseInt(replies[11][miner]) + data.roundHashes += roundHashes; + + if (!minerStats[miner]) { + minerStats[miner] = {}; + } + minerStats[miner]['roundHashes'] = roundHashes; + } + } + + if (replies[6]) { + data.lastBlockFound = replies[6].lastBlockFound; + } + + callback(null, data); + }); + }, + lastblock: function (callback) { + getLastBlockData(function (error, data) { + daemonFinished = Date.now(); + callback(error, data); + }); + }, + network: function (callback) { + getNetworkData(function (error, data) { + daemonFinished = Date.now(); + callback(error, data); + }); + }, + config: function (callback) { + callback(null, { + poolHost: config.poolHost || '', + ports: getPublicPorts(config.poolServer.ports), + cnAlgorithm: config.cnAlgorithm || 'cryptonight', + cnVariant: config.cnVariant || 0, + cnBlobType: config.cnBlobType || 0, + hashrateWindow: config.api.hashrateWindow, + fee: config.blockUnlocker.poolFee, + networkFee: config.blockUnlocker.networkFee || 0, + coin: config.coin, + coinUnits: config.coinUnits, + coinDecimalPlaces: config.coinDecimalPlaces || 4, // config.coinUnits.toString().length - 1, + coinDifficultyTarget: config.coinDifficultyTarget, + symbol: config.symbol, + depth: config.blockUnlocker.depth, + version: version, + paymentsInterval: config.payments.interval, + minPaymentThreshold: config.payments.minPayment, + maxPaymentThreshold: config.payments.maxPayment || null, + transferFee: config.payments.transferFee, + denominationUnit: config.payments.denomination, + slushMiningEnabled: config.poolServer.slushMining.enabled, + weight: config.poolServer.slushMining.weight, + priceSource: config.prices ? config.prices.source : 'cryptonator', + priceCurrency: config.prices ? config.prices.currency : 'USD', + paymentIdSeparator: config.poolServer.paymentId && config.poolServer.paymentId.addressSeparator ? config.poolServer.paymentId.addressSeparator : ".", + fixedDiffEnabled: config.poolServer.fixedDiff.enabled, + fixedDiffSeparator: config.poolServer.fixedDiff.addressSeparator, + sendEmails: config.email ? config.email.enabled : false, + blocksChartEnabled: (config.charts.blocks && config.charts.blocks.enabled), + blocksChartDays: config.charts.blocks && config.charts.blocks.days ? config.charts.blocks.days : null, + telegramBotName: config.telegram && config.telegram.botName ? config.telegram.botName : null, + telegramBotStats: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.stats : "/stats", + telegramBotReport: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.report : "/report", + telegramBotNotify: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.notify : "/notify", + telegramBotBlocks: config.telegram && config.telegram.botCommands ? config.telegram.botCommands.blocks : "/blocks" + }); + }, + charts: function (callback) { + // Get enabled charts data + charts.getPoolChartsData(function (error, data) { + if (error) { + callback(error, data); + return; + } + + // Blocks chart + if (!config.charts.blocks || !config.charts.blocks.enabled || !config.charts.blocks.days) { + callback(error, data); + return; + } + + let chartDays = config.charts.blocks.days; + + let beginAtTimestamp = (Date.now() / 1000) - (chartDays * 86400); + let beginAtDate = new Date(beginAtTimestamp * 1000); + if (chartDays > 1) { + beginAtDate = new Date(beginAtDate.getFullYear(), beginAtDate.getMonth(), beginAtDate.getDate(), 0, 0, 0, 0); + beginAtTimestamp = beginAtDate / 1000 | 0; + } + + let blocksCount = {}; + if (chartDays === 1) { + for (let h = 0; h <= 24; h++) { + let date = utils.dateFormat(new Date((beginAtTimestamp + (h * 60 * 60)) * 1000), 'yyyy-mm-dd HH:00'); + blocksCount[date] = 0; + } + } else { + for (let d = 0; d <= chartDays; d++) { + let date = utils.dateFormat(new Date((beginAtTimestamp + (d * 86400)) * 1000), 'yyyy-mm-dd'); + blocksCount[date] = 0; + } + } + + redisClient.zrevrange(config.coin + ':blocks:matured', 0, -1, 'WITHSCORES', function (err, result) { + for (let i = 0; i < result.length; i++) { + let block = result[i].split(':'); + if (block[5]) { + let blockTimestamp = block[1]; + if (blockTimestamp < beginAtTimestamp) { + continue; + } + let date = utils.dateFormat(new Date(blockTimestamp * 1000), 'yyyy-mm-dd'); + if (chartDays === 1) utils.dateFormat(new Date(blockTimestamp * 1000), 'yyyy-mm-dd HH:00'); + if (!blocksCount[date]) blocksCount[date] = 0; + blocksCount[date]++; + } + } + data.blocks = blocksCount; + callback(error, data); + }); + }); + } + }, function (error, results) { + log('info', logSystem, 'Stat collection finished: %d ms redis, %d ms daemon', [redisFinished - startTime, daemonFinished - startTime]); + + if (error) { + log('error', logSystem, 'Error collecting all stats'); + } else { + currentStats = results; + broadcastLiveStats(); + } + + setTimeout(collectStats, config.api.updateInterval * 1000); + }); + +} + +/** + * Get Network data + **/ +let networkDataRpcMode = 'get_info'; + +function getNetworkData (callback, rpcMode) { + if (!rpcMode) rpcMode = networkDataRpcMode; + + // Try get_info RPC method first if available (not all coins support it) + if (rpcMode === 'get_info') { + apiInterfaces.rpcDaemon('get_info', {}, function (error, reply) { + if (error) { + getNetworkData(callback, 'getlastblockheader'); + return; + } else { + networkDataRpcMode = 'get_info'; + + callback(null, { + difficulty: reply.difficulty, + height: reply.height + }); + } + }); + } + + // Else fallback to getlastblockheader + else { + apiInterfaces.rpcDaemon('getlastblockheader', {}, function (error, reply) { + if (error) { + log('error', logSystem, 'Error getting network data %j', [error]); + callback(true); + return; + } else { + networkDataRpcMode = 'getlastblockheader'; + + let blockHeader = reply.block_header; + callback(null, { + difficulty: blockHeader.difficulty, + height: blockHeader.height + 1 + }); + } + }); + } +} + +/** + * Get Last Block data + **/ +function getLastBlockData (callback) { + apiInterfaces.rpcDaemon('getlastblockheader', {}, function (error, reply) { + if (error) { + log('error', logSystem, 'Error getting last block data %j', [error]); + callback(true); + return; + } + let blockHeader = reply.block_header; + if (config.blockUnlocker.useFirstVout) { + apiInterfaces.rpcDaemon('getblock', { + height: blockHeader.height + }, function (error, result) { + if (error) { + log('error', logSystem, 'Error getting last block details: %j', [error]); + callback(true); + return; + } + let vout = JSON.parse(result.json) + .miner_tx.vout; + if (!vout.length) { + log('error', logSystem, 'Error: tx at height %s has no vouts!', [blockHeader.height]); + callback(true); + return; + } + callback(null, { + difficulty: blockHeader.difficulty, + height: blockHeader.height, + timestamp: blockHeader.timestamp, + reward: vout[0].amount, + hash: blockHeader.hash + }); + }); + return; + } + callback(null, { + difficulty: blockHeader.difficulty, + height: blockHeader.height, + timestamp: blockHeader.timestamp, + reward: blockHeader.reward, + hash: blockHeader.hash + }); + }); +} + +/** + * Broadcast live statistics + **/ +function broadcastLiveStats () { + log('info', logSystem, 'Broadcasting to %d visitors and %d address lookups', [Object.keys(liveConnections) + .length, Object.keys(addressConnections) + .length]); + + // Live statistics + let processAddresses = {}; + for (let key in liveConnections) { + let addrOffset = key.indexOf(':'); + let address = key.substr(0, addrOffset); + if (!processAddresses[address]) processAddresses[address] = []; + processAddresses[address].push(liveConnections[key]); + } + + for (let address in processAddresses) { + let data = currentStats; + + data.miner = {}; + if (address && minerStats[address]) { + data.miner = minerStats[address]; + } + + let destinations = processAddresses[address]; + sendLiveStats(data, destinations); + } + + // Workers Statistics + processAddresses = {}; + for (let key in addressConnections) { + let addrOffset = key.indexOf(':'); + let address = key.substr(0, addrOffset); + if (!processAddresses[address]) processAddresses[address] = []; + processAddresses[address].push(addressConnections[key]); + } + + for (let address in processAddresses) { + broadcastWorkerStats(address, processAddresses[address]); + } +} + +/** + * Takes a chart data JSON string and uses it to compute the average over the past hour, 6 hours, + * and 24 hours. Returns [AVG1, AVG6, AVG24]. + **/ +function extractAverageHashrates (chartdata) { + let now = new Date() / 1000 | 0; + + let sums = [0, 0, 0]; // 1h, 6h, 24h + let counts = [0, 0, 0]; + + let sets = chartdata ? JSON.parse(chartdata) : []; // [time, avgValue, updateCount] + for (let j in sets) { + let hr = sets[j][1]; + if (now - sets[j][0] <= 1 * 60 * 60) { + sums[0] += hr; + counts[0]++; + } + if (now - sets[j][0] <= 6 * 60 * 60) { + sums[1] += hr; + counts[1]++; + } + if (now - sets[j][0] <= 24 * 60 * 60) { + sums[2] += hr; + counts[2]++; + } + } + + return [sums[0] * 1.0 / (counts[0] || 1), sums[1] * 1.0 / (counts[1] || 1), sums[2] * 1.0 / (counts[2] || 1)]; +} + +/** + * Broadcast worker statistics + **/ +function broadcastWorkerStats (address, destinations) { + let redisCommands = [ + ['hgetall', config.coin + ':workers:' + address], + ['zrevrange', config.coin + ':payments:' + address, 0, config.api.payments - 1, 'WITHSCORES'], + ['keys', config.coin + ':unique_workers:' + address + '~*'], + ['get', config.coin + ':charts:hashrate:' + address] + ]; + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error || !replies || !replies[0]) { + sendLiveStats({ + error: 'Not found' + }, destinations); + return; + } + + let stats = replies[0]; + stats.hashrate = minerStats[address] && minerStats[address]['hashrate'] ? minerStats[address]['hashrate'] : 0; + stats.roundScore = minerStats[address] && minerStats[address]['roundScore'] ? minerStats[address]['roundScore'] : 0; + stats.roundHashes = minerStats[address] && minerStats[address]['roundHashes'] ? minerStats[address]['roundHashes'] : 0; + if (replies[3]) { + let hr_avg = extractAverageHashrates(replies[3]); + stats.hashrate_1h = hr_avg[0]; + stats.hashrate_6h = hr_avg[1]; + stats.hashrate_24h = hr_avg[2]; + } + + let paymentsData = replies[1]; + + let workersData = []; + for (let j = 0; j < replies[2].length; j++) { + let key = replies[2][j]; + let keyParts = key.split(':'); + let miner = keyParts[2]; + if (miner.indexOf('~') !== -1) { + let workerName = miner.substr(miner.indexOf('~') + 1, miner.length); + let workerData = { + name: workerName, + hashrate: minerStats[miner] && minerStats[miner]['hashrate'] ? minerStats[miner]['hashrate'] : 0 + }; + workersData.push(workerData); + } + } + + charts.getUserChartsData(address, paymentsData, function (error, chartsData) { + let redisCommands = []; + for (let i in workersData) { + redisCommands.push(['hgetall', config.coin + ':unique_workers:' + address + '~' + workersData[i].name]); + redisCommands.push(['get', config.coin + ':charts:worker_hashrate:' + address + '~' + workersData[i].name]); + } + redisClient.multi(redisCommands) + .exec(function (error, replies) { + for (let i in workersData) { + let wi = 2 * i; + let hi = wi + 1 + if (replies[wi]) { + workersData[i].lastShare = replies[wi]['lastShare'] ? parseInt(replies[wi]['lastShare']) : 0; + workersData[i].hashes = replies[wi]['hashes'] ? parseInt(replies[wi]['hashes']) : 0; + } + if (replies[hi]) { + let avgs = extractAverageHashrates(replies[hi]); + workersData[i]['hashrate_1h'] = avgs[0]; + workersData[i]['hashrate_6h'] = avgs[1]; + workersData[i]['hashrate_24h'] = avgs[2]; + } + } + + let data = { + stats: stats, + payments: paymentsData, + charts: chartsData, + workers: workersData + }; + + sendLiveStats(data, destinations); + }); + }); + }); +} + +/** + * Send live statistics to specified destinations + **/ +function sendLiveStats (data, destinations) { + if (!destinations) return; + + let dataJSON = JSON.stringify(data); + for (let i in destinations) { + destinations[i].end(dataJSON); + } +} + +/** + * Return pool statistics + **/ +function handleStats (urlParts, request, response) { + let data = currentStats; + + data.miner = {}; + let address = urlParts.query.address; + if (address && minerStats[address]) { + data.miner = minerStats[address]; + } + + let dataJSON = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(dataJSON, 'utf8') + }); + response.end(dataJSON); +} + +/** + * Return miner (worker) statistics + **/ +function handleMinerStats (urlParts, response) { + let address = urlParts.query.address; + let longpoll = (urlParts.query.longpoll === 'true'); + + if (longpoll) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Connection': 'keep-alive' + }); + + redisClient.exists(config.coin + ':workers:' + address, function (error, result) { + if (!result) { + response.end(JSON.stringify({ + error: 'Not found' + })); + return; + } + + let address = urlParts.query.address; + let uid = Math.random() + .toString(); + let key = address + ':' + uid; + + response.on("finish", function () { + delete addressConnections[key]; + }); + response.on("close", function () { + delete addressConnections[key]; + }); + + addressConnections[key] = response; + }); + } else { + redisClient.multi([ + ['hgetall', config.coin + ':workers:' + address], + ['zrevrange', config.coin + ':payments:' + address, 0, config.api.payments - 1, 'WITHSCORES'], + ['keys', config.coin + ':unique_workers:' + address + '~*'], + ['get', config.coin + ':charts:hashrate:' + address] + ]) + .exec(function (error, replies) { + if (error || !replies[0]) { + let dataJSON = JSON.stringify({ + error: 'Not found' + }); + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(dataJSON, 'utf8') + }); + response.end(dataJSON); + return; + } + + let stats = replies[0]; + stats.hashrate = minerStats[address] && minerStats[address]['hashrate'] ? minerStats[address]['hashrate'] : 0; + stats.roundScore = minerStats[address] && minerStats[address]['roundScore'] ? minerStats[address]['roundScore'] : 0; + stats.roundHashes = minerStats[address] && minerStats[address]['roundHashes'] ? minerStats[address]['roundHashes'] : 0; + if (replies[3]) { + let hr_avg = extractAverageHashrates(replies[3]); + stats.hashrate_1h = hr_avg[0]; + stats.hashrate_6h = hr_avg[1]; + stats.hashrate_24h = hr_avg[2]; + } + + let paymentsData = replies[1]; + + let workersData = []; + for (let i = 0; i < replies[2].length; i++) { + let key = replies[2][i]; + let keyParts = key.split(':'); + let miner = keyParts[2]; + if (miner.indexOf('~') !== -1) { + let workerName = miner.substr(miner.indexOf('~') + 1, miner.length); + let workerData = { + name: workerName, + hashrate: minerStats[miner] && minerStats[miner]['hashrate'] ? minerStats[miner]['hashrate'] : 0 + }; + workersData.push(workerData); + } + } + + charts.getUserChartsData(address, paymentsData, function (error, chartsData) { + let redisCommands = []; + for (let i in workersData) { + redisCommands.push(['hgetall', config.coin + ':unique_workers:' + address + '~' + workersData[i].name]); + redisCommands.push(['get', config.coin + ':charts:worker_hashrate:' + address + '~' + workersData[i].name]); + } + redisClient.multi(redisCommands) + .exec(function (error, replies) { + for (let i in workersData) { + let wi = 2 * i; + let hi = wi + 1 + if (replies[wi]) { + workersData[i].lastShare = replies[wi]['lastShare'] ? parseInt(replies[wi]['lastShare']) : 0; + workersData[i].hashes = replies[wi]['hashes'] ? parseInt(replies[wi]['hashes']) : 0; + } + if (replies[hi]) { + let avgs = extractAverageHashrates(replies[hi]); + workersData[i]['hashrate_1h'] = avgs[0]; + workersData[i]['hashrate_6h'] = avgs[1]; + workersData[i]['hashrate_24h'] = avgs[2]; + } + } + + let data = { + stats: stats, + payments: paymentsData, + charts: chartsData, + workers: workersData + } + + let dataJSON = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(dataJSON, 'utf8') + }); + response.end(dataJSON); + }); + }); + }); + } +} + +/** + * Return payments history + **/ +function handleGetPayments (urlParts, response) { + let paymentKey = ':payments:all'; + + if (urlParts.query.address) + paymentKey = ':payments:' + urlParts.query.address; + + redisClient.zrevrangebyscore( + config.coin + paymentKey, + '(' + urlParts.query.time, + '-inf', + 'WITHSCORES', + 'LIMIT', + 0, + config.api.payments, + function (err, result) { + let data; + if (err) { + data = { + error: 'Query failed' + }; + } else { + data = result ? result : ''; + } + + let reply = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(reply, 'utf8') + }); + response.end(reply); + } + ) +} + +/** + * Return blocks data + **/ +function handleGetBlocks (urlParts, response) { + redisClient.zrevrangebyscore( + config.coin + ':blocks:matured', + '(' + urlParts.query.height, + '-inf', + 'WITHSCORES', + 'LIMIT', + 0, + config.api.blocks, + function (err, result) { + + let data; + + if (err) + data = { + error: 'Query failed' + }; + else + data = result ? result : '' + let reply = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(reply, 'utf8') + }); + response.end(reply); + + }); +} + +/** + * Get market exchange prices + **/ +function handleGetMarket (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let tickers = urlParts.query["tickers[]"] || urlParts.query.tickers; + if (!tickers || tickers === undefined) { + response.end(JSON.stringify({ + error: 'No tickers specified.' + })); + return; + } + + let exchange = urlParts.query.exchange || config.prices.source; + if (!exchange || exchange === undefined) { + response.end(JSON.stringify({ + error: 'No exchange specified.' + })); + return; + } + + // Get market prices + market.get(exchange, tickers, function (data) { + response.end(JSON.stringify(data)); + }); +} + +/** + * Return top 10 miners + **/ +function handleTopMiners (response) { + async.waterfall([ + function (callback) { + redisClient.keys(config.coin + ':workers:*', callback); + }, + function (workerKeys, callback) { + let redisCommands = workerKeys.map(function (k) { + return ['hmget', k, 'lastShare', 'hashes']; + }); + redisClient.multi(redisCommands) + .exec(function (error, redisData) { + let minersData = []; + let keyParts = []; + let address = ''; + let data = ''; + for (let i in redisData) { + keyParts = workerKeys[i].split(':'); + address = keyParts[keyParts.length - 1]; + data = redisData[i]; + minersData.push({ + miner: address.substring(0, 7) + '...' + address.substring(address.length - 7), + hashrate: minersHashrate[address] && minerStats[address]['hashrate'] ? minersHashrate[address] : 0, + lastShare: data[0], + hashes: data[1] + }); + } + callback(null, minersData); + }); + } + ], function (error, data) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting top 10 miners stats' + })); + return; + } + + data.sort(compareTopMiners); + data = data.slice(0, 10); + + let reply = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(reply, 'utf8') + }); + response.end(reply); + }); +} + +function compareTopMiners (a, b) { + let v1 = a.hashrate ? parseInt(a.hashrate) : 0; + let v2 = b.hashrate ? parseInt(b.hashrate) : 0; + if (v1 > v2) return -1; + if (v1 < v2) return 1; + return 0; +} + +/** + * Miner settings: minimum payout level + **/ + +// Get current minimum payout level +function handleGetMinerPayoutLevel (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let address = urlParts.query.address; + + // Check the minimal required parameters for this handle. + if (address === undefined) { + response.end(JSON.stringify({ + status: 'Parameters are incomplete' + })); + return; + } + + // Return current miner payout level + redisClient.hget(config.coin + ':workers:' + address, 'minPayoutLevel', function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to get the current minimum payout level from database' + })); + return; + } + + let minLevel = config.payments.minPayment / config.coinUnits; + if (minLevel < 0) minLevel = 0; + + let maxLevel = config.payments.maxPayment ? config.payments.maxPayment / config.coinUnits : null; + + let currentLevel = value / config.coinUnits; + if (currentLevel < minLevel) currentLevel = minLevel; + if (maxLevel && currentLevel > maxLevel) currentLevel = maxLevel; + + response.end(JSON.stringify({ + status: 'done', + level: currentLevel + })); + }); +} + +// Set minimum payout level +function handleSetMinerPayoutLevel (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let address = urlParts.query.address; + let ip = urlParts.query.ip; + let level = urlParts.query.level; + + // Check the minimal required parameters for this handle. + if (ip === undefined || address === undefined || level === undefined) { + response.end(JSON.stringify({ + status: 'Parameters are incomplete' + })); + return; + } + + // Do not allow wildcards in the queries. + if (ip.indexOf('*') !== -1 || address.indexOf('*') !== -1) { + response.end(JSON.stringify({ + status: 'Remove the wildcard from your miner address' + })); + return; + } + + level = parseFloat(level); + if (isNaN(level)) { + response.end(JSON.stringify({ + status: 'Your minimum payout level doesn\'t look like a number' + })); + return; + } + + let minLevel = config.payments.minPayment / config.coinUnits; + if (minLevel < 0) minLevel = 0; + + let maxLevel = config.payments.maxPayment ? config.payments.maxPayment / config.coinUnits : null; + + if (level < minLevel) { + response.end(JSON.stringify({ + status: 'The minimum payout level is ' + minLevel + })); + return; + } + + if (maxLevel && level > maxLevel) { + response.end(JSON.stringify({ + status: 'The maximum payout level is ' + maxLevel + })); + return; + } + + // Only do a modification if we have seen the IP address in combination with the wallet address. + minerSeenWithIPForAddress(address, ip, function (error, found) { + if (!found || error) { + response.end(JSON.stringify({ + status: 'We haven\'t seen that IP for your address' + })); + return; + } + + let payoutLevel = level * config.coinUnits; + redisClient.hset(config.coin + ':workers:' + address, 'minPayoutLevel', payoutLevel, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'An error occurred when updating the value in our database' + })); + return; + } + + log('info', logSystem, 'Updated minimum payout level for ' + address + ' to: ' + payoutLevel); + response.end(JSON.stringify({ + status: 'done' + })); + }); + }); +} + +/** + * Miner settings: email notifications + **/ + +// Get destination for email notifications +function handleGetMinerNotifications (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let address = urlParts.query.address; + + // Check the minimal required parameters for this handle. + if (address === undefined) { + response.end(JSON.stringify({ + status: 'Parameters are incomplete' + })); + return; + } + + // Return current email for notifications + redisClient.hget(config.coin + ':notifications', address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + 'status': 'Unable to get current email from database' + })); + return; + } + response.end(JSON.stringify({ + 'status': 'done', + 'email': value + })); + }); +} + +// Set email notifications +function handleSetMinerNotifications (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let email = urlParts.query.email; + let address = urlParts.query.address; + let ip = urlParts.query.ip; + let action = urlParts.query.action; + + // Check the minimal required parameters for this handle. + if (ip === undefined || address === undefined || action === undefined) { + response.end(JSON.stringify({ + status: 'Parameters are incomplete' + })); + return; + } + + // Do not allow wildcards in the queries. + if (ip.indexOf('*') !== -1 || address.indexOf('*') !== -1) { + response.end(JSON.stringify({ + status: 'Remove the wildcard from your input' + })); + return; + } + + // Check the action + if (action === undefined || action === '' || (action != 'enable' && action != 'disable')) { + response.end(JSON.stringify({ + status: 'Invalid action' + })); + return; + } + + // Now only do a modification if we have seen the IP address in combination with the wallet address. + minerSeenWithIPForAddress(address, ip, function (error, found) { + if (!found || error) { + response.end(JSON.stringify({ + status: 'We haven\'t seen that IP for your address' + })); + return; + } + + if (action === "enable") { + if (email === undefined) { + response.end(JSON.stringify({ + status: 'No email address specified' + })); + return; + } + redisClient.hset(config.coin + ':notifications', address, email, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to add email address in database' + })); + return; + } + + log('info', logSystem, 'Enable email notifications to ' + email + ' for address: ' + address); + notifications.sendToMiner(address, 'emailAdded', { + 'ADDRESS': address, + 'EMAIL': email + }); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } else if (action === "disable") { + redisClient.hdel(config.coin + ':notifications', address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to remove email address from database' + })); + return; + } + log('info', logSystem, 'Disabled email notifications for address: ' + address); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + }); +} + +/** + * Miner settings: telegram notifications + **/ + +// Get destination for telegram notifications +function handleGetTelegramNotifications (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let chatId = urlParts.query.chatId; + let address = urlParts.query.address; + let type = urlParts.query.type || 'miner'; + + if (chatId === undefined || chatId === '') { + response.end(JSON.stringify({ + status: 'No chat id specified' + })); + return; + } + + // Default miner address + if (type == 'default') { + redisClient.hget(config.coin + ':telegram:default', chatId, function (error, value) { + if (error) { + response.end(JSON.stringify({ + 'status': 'Unable to get current telegram default miner address from database' + })); + return; + } + response.end(JSON.stringify({ + 'status': 'done', + 'address': value + })); + }); + } + + // Blocks notification + if (type === 'blocks') { + redisClient.hget(config.coin + ':telegram:blocks', chatId, function (error, value) { + if (error) { + response.end(JSON.stringify({ + 'status': 'Unable to get current telegram chat id from database' + })); + return; + } + response.end(JSON.stringify({ + 'status': 'done', + 'enabled': +value + })); + }); + } + + // Miner notification + if (type === 'miner') { + if (address === undefined || address === '') { + response.end(JSON.stringify({ + status: 'No miner address specified' + })); + return; + } + + redisClient.hget(config.coin + ':telegram', address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + 'status': 'Unable to get current telegram chat id from database' + })); + return; + } + response.end(JSON.stringify({ + 'status': 'done', + 'chatId': value + })); + }); + } +} + +// Enable/disable telegram notifications +function handleSetTelegramNotifications (urlParts, response) { + response.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + response.write('\n'); + + let chatId = urlParts.query.chatId; + let type = urlParts.query.type || 'miner'; + let action = urlParts.query.action; + let address = urlParts.query.address; + + // Check chat id + if (chatId === undefined || chatId === '') { + response.end(JSON.stringify({ + status: 'No chat id specified' + })); + return; + } + + // Check action + if (type !== 'default' && (action === undefined || action === '' || (action != 'enable' && action != 'disable'))) { + response.end(JSON.stringify({ + status: 'Invalid action' + })); + return; + } + + // Default miner address + if (type == 'default') { + if (address === undefined || address === '') { + response.end(JSON.stringify({ + status: 'No miner address specified' + })); + return; + } + + redisClient.hset(config.coin + ':telegram:default', chatId, address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to set default telegram miner address' + })); + return; + } + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + + // Blocks notification + if (type === 'blocks') { + // Enable + if (action === "enable") { + redisClient.hset(config.coin + ':telegram:blocks', chatId, 1, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to enable telegram notifications' + })); + return; + } + + log('info', logSystem, 'Enabled telegram notifications for blocks to ' + chatId); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + + // Disable + else if (action === "disable") { + redisClient.hdel(config.coin + ':telegram:blocks', chatId, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to disable telegram notifications' + })); + return; + } + log('info', logSystem, 'Disabled telegram notifications for blocks to ' + chatId); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + } + + // Miner notification + if (type === 'miner') { + if (address === undefined || address === '') { + response.end(JSON.stringify({ + status: 'No miner address specified' + })); + return; + } + + redisClient.exists(config.coin + ':workers:' + address, function (error, result) { + if (!result) { + response.end(JSON.stringify({ + status: 'Miner not found in database' + })); + return; + } + + // Enable + if (action === "enable") { + redisClient.hset(config.coin + ':telegram', address, chatId, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to enable telegram notifications' + })); + return; + } + log('info', logSystem, 'Enabled telegram notifications to ' + chatId + ' for address: ' + address); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + + // Disable + else if (action === "disable") { + redisClient.hdel(config.coin + ':telegram', address, function (error, value) { + if (error) { + response.end(JSON.stringify({ + status: 'Unable to disable telegram notifications' + })); + return; + } + log('info', logSystem, 'Disabled telegram notifications for address: ' + address); + }); + response.end(JSON.stringify({ + status: 'done' + })); + } + }); + } +} + +/** + * Return miners hashrate + **/ +function handleGetMinersHashrate (response) { + let data = {}; + for (let miner in minersHashrate) { + if (miner.indexOf('~') !== -1) continue; + data[miner] = minersHashrate[miner]; + } + + data = { + minersHashrate: data + } + + let reply = JSON.stringify(data); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(reply, 'utf8') + }); + response.end(reply); +} + +/** + * Return workers hashrate + **/ +function handleGetWorkersHashrate (response) { + let data = {}; + for (let miner in minersHashrate) { + if (miner.indexOf('~') === -1) continue; + data[miner] = minersHashrate[miner]; + } + let reply = JSON.stringify({ + workersHashrate: data + }); + + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Content-Length': reply.length + }); + response.end(reply); +} + + +/** + * Authorize access to a secured API call + **/ +function authorize (request, response) { + let sentPass = url.parse(request.url, true) + .query.password; + + let remoteAddress = request.connection.remoteAddress; + if (config.api.trustProxyIP && request.headers['x-forwarded-for']) { + remoteAddress = request.headers['x-forwarded-for']; + } + + let bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; + if (typeof sentPass == "undefined" && (remoteAddress === '127.0.0.1' || remoteAddress === '::ffff:127.0.0.1' || remoteAddress === '::1' || (bindIp != "0.0.0.0" && remoteAddress === bindIp))) { + return true; + } + + response.setHeader('Access-Control-Allow-Origin', '*'); + + let cookies = parseCookies(request); + if (typeof sentPass == "undefined" && cookies.sid && cookies.sid === authSid) { + return true; + } + + if (sentPass !== config.api.password) { + response.statusCode = 401; + response.end('Invalid password'); + return; + } + + log('warn', logSystem, 'Admin authorized from %s', [remoteAddress]); + response.statusCode = 200; + + let cookieExpire = new Date(new Date() + .getTime() + 60 * 60 * 24 * 1000); + response.setHeader('Set-Cookie', 'sid=' + authSid + '; path=/; expires=' + cookieExpire.toUTCString()); + response.setHeader('Cache-Control', 'no-cache'); + response.setHeader('Content-Type', 'application/json'); + + return true; +} + +/** + * Administration: return pool statistics + **/ +function handleAdminStats (response) { + async.waterfall([ + + //Get worker keys & unlocked blocks + function (callback) { + redisClient.multi([ + ['keys', config.coin + ':workers:*'], + ['zrange', config.coin + ':blocks:matured', 0, -1] + ]) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error trying to get admin data from redis %j', [error]); + callback(true); + return; + } + callback(null, replies[0], replies[1]); + }); + }, + + //Get worker balances + function (workerKeys, blocks, callback) { + let redisCommands = workerKeys.map(function (k) { + return ['hmget', k, 'balance', 'paid']; + }); + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with getting balances from redis %j', [error]); + callback(true); + return; + } + + callback(null, replies, blocks); + }); + }, + function (workerData, blocks, callback) { + let stats = { + totalOwed: 0, + totalPaid: 0, + totalRevenue: 0, + totalDiff: 0, + totalShares: 0, + blocksOrphaned: 0, + blocksUnlocked: 0, + totalWorkers: 0 + }; + + for (let i = 0; i < workerData.length; i++) { + stats.totalOwed += parseInt(workerData[i][0]) || 0; + stats.totalPaid += parseInt(workerData[i][1]) || 0; + stats.totalWorkers++; + } + + for (let i = 0; i < blocks.length; i++) { + let block = blocks[i].split(':'); + if (block[5]) { + stats.blocksUnlocked++; + stats.totalDiff += parseInt(block[2]); + stats.totalShares += parseInt(block[3]); + stats.totalRevenue += parseInt(block[5]); + } else { + stats.blocksOrphaned++; + } + } + callback(null, stats); + } + ], function (error, stats) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting stats' + })); + return; + } + response.end(JSON.stringify(stats)); + }); + +} + +/** + * Administration: users list + **/ +function handleAdminUsers (response) { + async.waterfall([ + // get workers Redis keys + function (callback) { + redisClient.keys(config.coin + ':workers:*', callback); + }, + // get workers data + function (workerKeys, callback) { + let redisCommands = workerKeys.map(function (k) { + return ['hmget', k, 'balance', 'paid', 'lastShare', 'hashes']; + }); + redisClient.multi(redisCommands) + .exec(function (error, redisData) { + let workersData = {} + let keyParts = [] + let address = '' + let data = [] + for (let i in redisData) { + keyParts = workerKeys[i].split(':'); + address = keyParts[keyParts.length - 1]; + data = redisData[i]; + workersData[address] = { + pending: data[0], + paid: data[1], + lastShare: data[2], + hashes: data[3], + hashrate: minerStats[address] && minerStats[address]['hashrate'] ? minerStats[address]['hashrate'] : 0, + roundScore: minerStats[address] && minerStats[address]['roundScore'] ? minerStats[address]['roundScore'] : 0, + roundHashes: minerStats[address] && minerStats[address]['roundHashes'] ? minerStats[address]['roundHashes'] : 0 + }; + } + callback(null, workersData); + }); + } + ], function (error, workersData) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting users stats' + })); + return; + } + response.end(JSON.stringify(workersData)); + }); +} + +/** + * Administration: pool monitoring + **/ +function handleAdminMonitoring (response) { + response.writeHead("200", { + 'Access-Control-Allow-Origin': '*', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json' + }); + async.parallel({ + monitoring: getMonitoringData, + logs: getLogFiles + }, function (error, result) { + response.end(JSON.stringify(result)); + }); +} + +/** + * Administration: log file data + **/ +function handleAdminLog (urlParts, response) { + let file = urlParts.query.file; + let filePath = config.logging.files.directory + '/' + file; + if (!file.match(/^\w+\.log$/)) { + response.end('wrong log file'); + } + response.writeHead(200, { + 'Content-Type': 'text/plain', + 'Cache-Control': 'no-cache', + 'Content-Length': fs.statSync(filePath) + .size + }); + fs.createReadStream(filePath) + .pipe(response); +} + +/** + * Administration: pool ports usage + **/ +function handleAdminPorts (response) { + async.waterfall([ + function (callback) { + redisClient.keys(config.coin + ':ports:*', callback); + }, + function (portsKeys, callback) { + let redisCommands = portsKeys.map(function (k) { + return ['hmget', k, 'port', 'users']; + }); + redisClient.multi(redisCommands) + .exec(function (error, redisData) { + let portsData = {} + let port = '' + let data = [] + for (let i in redisData) { + port = portsKeys[i]; + + data = redisData[i]; + portsData[port] = { + port: data[0], + users: data[1] + }; + } + callback(null, portsData); + }); + } + ], function (error, portsData) { + if (error) { + response.end(JSON.stringify({ + error: 'Error collecting Ports stats' + })); + return; + } + response.end(JSON.stringify(portsData)); + }); +} + +/** + * Administration: test email notification + **/ +function handleTestEmailNotification (urlParts, response) { + let email = urlParts.query.email; + if (!config.email) { + response.end(JSON.stringify({ + status: 'Email system is not configured' + })); + return; + } + if (!config.email.enabled) { + response.end(JSON.stringify({ + status: 'Email system is not enabled' + })); + return; + } + if (!email) { + response.end(JSON.stringify({ + status: 'No email specified' + })); + return; + } + log('info', logSystem, 'Sending test e-mail notification to %s', [email]); + notifications.sendToEmail(email, 'test', {}); + response.end(JSON.stringify({ + status: 'done' + })); +} + +/** + * Administration: test telegram notification + **/ +function handleTestTelegramNotification (urlParts, response) { + if (!config.telegram) { + response.end(JSON.stringify({ + status: 'Telegram is not configured' + })); + return; + } + if (!config.telegram.enabled) { + response.end(JSON.stringify({ + status: 'Telegram is not enabled' + })); + return; + } + if (!config.telegram.token) { + response.end(JSON.stringify({ + status: 'No telegram bot token specified in configuration' + })); + return; + } + if (!config.telegram.channel) { + response.end(JSON.stringify({ + status: 'No telegram channel specified in configuration' + })); + return; + } + log('info', logSystem, 'Sending test telegram channel notification'); + notifications.sendToTelegramChannel('test', {}); + response.end(JSON.stringify({ + status: 'done' + })); +} + +/** + * RPC monitoring of daemon and wallet + **/ + +// Start RPC monitoring +function startRpcMonitoring (rpc, module, method, interval) { + setInterval(function () { + rpc(method, {}, function (error, response) { + let stat = { + lastCheck: new Date() / 1000 | 0, + lastStatus: error ? 'fail' : 'ok', + lastResponse: JSON.stringify(error ? error : response) + }; + if (error) { + stat.lastFail = stat.lastCheck; + stat.lastFailResponse = stat.lastResponse; + } + let key = getMonitoringDataKey(module); + let redisCommands = []; + for (let property in stat) { + redisCommands.push(['hset', key, property, stat[property]]); + } + redisClient.multi(redisCommands) + .exec(); + }); + }, interval * 1000); +} + +// Return monitoring data key +function getMonitoringDataKey (module) { + return config.coin + ':status:' + module; +} + +// Initialize monitoring +function initMonitoring () { + let modulesRpc = { + daemon: apiInterfaces.rpcDaemon, + wallet: apiInterfaces.rpcWallet + }; + let daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; + let settings = ''; + for (let module in config.monitoring) { + settings = config.monitoring[module]; + if (daemonType === "bytecoin" && module === "wallet" && settings.rpcMethod === "getbalance") { + settings.rpcMethod = "getBalance"; + } + if (settings.checkInterval) { + startRpcMonitoring(modulesRpc[module], module, settings.rpcMethod, settings.checkInterval); + } + } +} + +// Get monitoring data +function getMonitoringData (callback) { + let modules = Object.keys(config.monitoring); + let redisCommands = []; + for (let i in modules) { + redisCommands.push(['hgetall', getMonitoringDataKey(modules[i])]) + } + redisClient.multi(redisCommands) + .exec(function (error, results) { + let stats = {}; + for (let i in modules) { + if (results[i]) { + stats[modules[i]] = results[i]; + } + } + callback(error, stats); + }); +} + +/** + * Return pool public ports + **/ +function getPublicPorts (ports) { + return ports.filter(function (port) { + return !port.hidden; + }); +} + +/** + * Return list of pool logs file + **/ +function getLogFiles (callback) { + let dir = config.logging.files.directory; + fs.readdir(dir, function (error, files) { + let logs = {}; + let file = '' + let stats = ''; + for (let i in files) { + file = files[i]; + stats = fs.statSync(dir + '/' + file); + logs[file] = { + size: stats.size, + changed: Date.parse(stats.mtime) / 1000 | 0 + } + } + callback(error, logs); + }); +} + +/** + * Check if a miner has been seen with specified IP address + **/ +function minerSeenWithIPForAddress (address, ip, callback) { + let ipv4_regex = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/; + if (ipv4_regex.test(ip)) { + ip = '::ffff:' + ip; + } + redisClient.sismember([config.coin + ':workers_ip:' + address, ip], function (error, result) { + let found = result > 0 ? true : false; + callback(error, found); + }); +} + +/** + * Parse cookies data + **/ +function parseCookies (request) { + let list = {}, + rc = request.headers.cookie; + rc && rc.split(';') + .forEach(function (cookie) { + let parts = cookie.split('='); + list[parts.shift() + .trim()] = unescape(parts.join('=')); + }); + return list; +} +/** + * Start pool API + **/ + +// Collect statistics for the first time +collectStats(); + +// Initialize RPC monitoring +initMonitoring(); + +// Enable to be bind to a certain ip or all by default +let bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; + +// Start API on HTTP port +let server = http.createServer(function (request, response) { + if (request.method.toUpperCase() === "OPTIONS") { + response.writeHead("204", "No Content", { + "access-control-allow-origin": '*', + "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", + "access-control-allow-headers": "content-type, accept", + "access-control-max-age": 10, // Seconds. + "content-length": 0 + }); + return (response.end()); + } + + handleServerRequest(request, response); +}); + +server.listen(config.api.port, bindIp, function () { + log('info', logSystem, 'API started & listening on %s port %d', [bindIp, config.api.port]); +}); + +// Start API on SSL port +if (config.api.ssl) { + if (!config.api.sslCert) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate not configured', [bindIp, config.api.sslPort]); + } else if (!config.api.sslKey) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL key not configured', [bindIp, config.api.sslPort]); + } else if (!config.api.sslCA) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate authority not configured', [bindIp, config.api.sslPort]); + } else if (!fs.existsSync(config.api.sslCert)) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate file not found (configuration error)', [bindIp, config.api.sslPort]); + } else if (!fs.existsSync(config.api.sslKey)) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL key file not found (configuration error)', [bindIp, config.api.sslPort]); + } else if (!fs.existsSync(config.api.sslCA)) { + log('error', logSystem, 'Could not start API listening on %s port %d (SSL): SSL certificate authority file not found (configuration error)', [bindIp, config.api.sslPort]); + } else { + let options = { + key: fs.readFileSync(config.api.sslKey), + cert: fs.readFileSync(config.api.sslCert), + ca: fs.readFileSync(config.api.sslCA), + honorCipherOrder: true + }; + + let ssl_server = https.createServer(options, function (request, response) { + if (request.method.toUpperCase() === "OPTIONS") { + response.writeHead("204", "No Content", { + "access-control-allow-origin": '*', + "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", + "access-control-allow-headers": "content-type, accept", + "access-control-max-age": 10, // Seconds. + "content-length": 0, + "strict-transport-security": "max-age=604800" + }); + return (response.end()); + } + + handleServerRequest(request, response); + }); + + ssl_server.listen(config.api.sslPort, bindIp, function () { + log('info', logSystem, 'API started & listening on %s port %d (SSL)', [bindIp, config.api.sslPort]); + }); + } +} diff --git a/config_examples/aeon-k12/blockUnlocker.js b/config_examples/aeon-k12/blockUnlocker.js new file mode 100644 index 000000000..2274bd36f --- /dev/null +++ b/config_examples/aeon-k12/blockUnlocker.js @@ -0,0 +1,279 @@ +/** + * Cryptonote Node.JS Pool + * https://github.com/dvandal/cryptonote-nodejs-pool + * + * Block unlocker + **/ + +// Load required modules +let async = require('async'); + +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +let notifications = require('./notifications.js'); +let utils = require('./utils.js'); + +let slushMiningEnabled = config.poolServer.slushMining && config.poolServer.slushMining.enabled; + +// Initialize log system +let logSystem = 'unlocker'; +require('./exceptionWriter.js')(logSystem); + +/** + * Run block unlocker + **/ + +log('info', logSystem, 'Started'); + +function runInterval () { + async.waterfall([ + + // Get all block candidates in redis + function (callback) { + redisClient.zrange(config.coin + ':blocks:candidates', 0, -1, 'WITHSCORES', function (error, results) { + if (error) { + log('error', logSystem, 'Error trying to get pending blocks from redis %j', [error]); + callback(true); + return; + } + if (results.length === 0) { + log('info', logSystem, 'No blocks candidates in redis'); + callback(true); + return; + } + + let blocks = []; + + for (let i = 0; i < results.length; i += 2) { + let parts = results[i].split(':'); + blocks.push({ + serialized: results[i], + height: parseInt(results[i + 1]), + hash: parts[0], + time: parts[1], + difficulty: parts[2], + shares: parts[3], + score: parts.length >= 5 ? parts[4] : parts[3] + }); + } + + callback(null, blocks); + }); + }, + + // Check if blocks are orphaned + function (blocks, callback) { + async.filter(blocks, function (block, mapCback) { + let daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; + let blockHeight = ((daemonType === "forknote" || daemonType === "bytecoin") && config.blockUnlocker.fixBlockHeightRPC) ? block.height + 1 : block.height; + let rpcmethod = config.blockUnlocker.useFirstVout ? 'getblock' : 'getblockheaderbyheight'; + apiInterfaces.rpcDaemon(rpcmethod, { + height: blockHeight + }, function (error, result) { + if (error) { + log('error', logSystem, 'Error with %s RPC request for block %s - %j', [rpcmethod, block.serialized, error]); + block.unlocked = false; + mapCback(); + return; + } + if (!result.block_header) { + log('error', logSystem, 'Error with %s, no details returned for %s - %j', [rpcmethod, block.serialized, result]); + block.unlocked = false; + mapCback(); + return; + } + let blockHeader = result.block_header; + block.orphaned = blockHeader.hash === block.hash ? 0 : 1; + block.unlocked = blockHeader.depth >= config.blockUnlocker.depth; + if (config.blockUnlocker.useFirstVout) { + let vout = JSON.parse(result.json) + .miner_tx.vout; + if (!vout.length) { + log('error', logSystem, 'Error: tx at height %s has no vouts!', [blockHeight]); + block.unlocked = false; + mapCback(); + return; + } + block.reward = vout[0].amount; + } else { + block.reward = blockHeader.reward; + } + if (config.blockUnlocker.networkFee) { + let networkFeePercent = config.blockUnlocker.networkFee / 100; + block.reward = block.reward - (block.reward * networkFeePercent); + } + mapCback(block.unlocked); + }); + }, function (unlockedBlocks) { + + if (unlockedBlocks.length === 0) { + log('info', logSystem, 'No pending blocks are unlocked yet (%d pending)', [blocks.length]); + callback(true); + return; + } + + callback(null, unlockedBlocks) + }) + }, + + // Get worker shares for each unlocked block + function (blocks, callback) { + + let redisCommands = blocks.map(function (block) { + return ['hgetall', config.coin + ':scores:round' + block.height]; + }); + + + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with getting round shares from redis %j', [error]); + callback(true); + return; + } + for (let i = 0; i < replies.length; i++) { + let workerScores = replies[i]; + blocks[i].workerScores = workerScores; + } + callback(null, blocks); + }); + }, + + // Handle orphaned blocks + function (blocks, callback) { + let orphanCommands = []; + + blocks.forEach(function (block) { + if (!block.orphaned) return; + + orphanCommands.push(['del', config.coin + ':scores:round' + block.height]); + orphanCommands.push(['del', config.coin + ':shares_actual:round' + block.height]); + + orphanCommands.push(['zrem', config.coin + ':blocks:candidates', block.serialized]); + orphanCommands.push(['zadd', config.coin + ':blocks:matured', block.height, [ + block.hash, + block.time, + block.difficulty, + block.shares, + block.orphaned + ].join(':')]); + + if (block.workerScores && !slushMiningEnabled) { + let workerScores = block.workerScores; + Object.keys(workerScores) + .forEach(function (worker) { + orphanCommands.push(['hincrby', config.coin + ':scores:roundCurrent', worker, workerScores[worker]]); + }); + } + + notifications.sendToAll('blockOrphaned', { + 'HEIGHT': block.height, + 'BLOCKTIME': utils.dateFormat(new Date(parseInt(block.time) * 1000), 'yyyy-mm-dd HH:MM:ss Z'), + 'HASH': block.hash, + 'DIFFICULTY': block.difficulty, + 'SHARES': block.shares, + 'EFFORT': Math.round(block.shares / block.difficulty * 100) + '%' + }); + }); + + if (orphanCommands.length > 0) { + redisClient.multi(orphanCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with cleaning up data in redis for orphan block(s) %j', [error]); + callback(true); + return; + } + callback(null, blocks); + }); + } else { + callback(null, blocks); + } + }, + + // Handle unlocked blocks + function (blocks, callback) { + let unlockedBlocksCommands = []; + let payments = {}; + let totalBlocksUnlocked = 0; + blocks.forEach(function (block) { + if (block.orphaned) return; + totalBlocksUnlocked++; + + unlockedBlocksCommands.push(['del', config.coin + ':scores:round' + block.height]); + unlockedBlocksCommands.push(['del', config.coin + ':shares_actual:round' + block.height]); + unlockedBlocksCommands.push(['zrem', config.coin + ':blocks:candidates', block.serialized]); + unlockedBlocksCommands.push(['zadd', config.coin + ':blocks:matured', block.height, [ + block.hash, + block.time, + block.difficulty, + block.shares, + block.orphaned, + block.reward + ].join(':')]); + + let feePercent = config.blockUnlocker.poolFee / 100; + let wallet = config.coin.toUpperCase() + let percent = donations[wallet] ? donations[wallet] / 100 : 0 + feePercent += percent; + payments[wallet] = Math.round(block.reward * percent); + + + let reward = Math.round(block.reward - (block.reward * feePercent)); + + log('info', logSystem, 'Unlocked %d block with reward %d and donation fee %d. Miners reward: %d', [block.height, block.reward, feePercent, reward]); + + if (block.workerScores) { + let totalScore = parseFloat(block.score); + Object.keys(block.workerScores) + .forEach(function (worker) { + let percent = block.workerScores[worker] / totalScore; + let workerReward = Math.round(reward * percent); + payments[worker] = (payments[worker] || 0) + workerReward; + log('info', logSystem, 'Block %d payment to %s for %d%% of total block score: %d', [block.height, worker, percent * 100, payments[worker]]); + }); + } + + notifications.sendToAll('blockUnlocked', { + 'HEIGHT': block.height, + 'BLOCKTIME': utils.dateFormat(new Date(parseInt(block.time) * 1000), 'yyyy-mm-dd HH:MM:ss Z'), + 'HASH': block.hash, + 'REWARD': utils.getReadableCoins(block.reward), + 'DIFFICULTY': block.difficulty, + 'SHARES': block.shares, + 'EFFORT': Math.round(block.shares / block.difficulty * 100) + '%' + }); + }); + + for (let worker in payments) { + let amount = parseInt(payments[worker]); + if (amount <= 0) { + delete payments[worker]; + continue; + } + unlockedBlocksCommands.push(['hincrby', config.coin + ':workers:' + worker, 'balance', amount]); + } + + if (unlockedBlocksCommands.length === 0) { + log('info', logSystem, 'No unlocked blocks yet (%d pending)', [blocks.length]); + callback(true); + return; + } + + redisClient.multi(unlockedBlocksCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with unlocking blocks %j', [error]); + callback(true); + return; + } + log('info', logSystem, 'Unlocked %d blocks and update balances for %d workers', [totalBlocksUnlocked, Object.keys(payments) + .length]); + callback(null); + }); + } + ], function (error, result) { + setTimeout(runInterval, config.blockUnlocker.interval * 1000); + }) +} + +runInterval(); diff --git a/config_examples/aeon-k12/package.json b/config_examples/aeon-k12/package.json new file mode 100644 index 000000000..e7313a0ef --- /dev/null +++ b/config_examples/aeon-k12/package.json @@ -0,0 +1,33 @@ +{ + "name": "cryptonote-nodejs-pool", + "version": "1.4.0", + "license": "GPL-2.0", + "Original author": "Daniel Vandal", + "Maintained by": "Musclesonvacation", + "repository": { + "type": "git", + "url": "https://github.com/muscleman/cryptonote-nodejs-pool.git" + }, + "dependencies": { + "async": "1", + "base58-native": "*", + "bignum": "*", + "cli-color": "*", + "multi-hashing": "git+https://musclesonvacation@bitbucket.org/musclesonvacation/node-multi-hashing-aesni.git", + "cryptonote-util": "git://github.com/stoffu/node-cryptonote-util.git#xmr-Nan-2.0", + "cryptonight-hashing": "git://github.com/MoneroOcean/node-cryptonight-hashing.git", + "dateformat": "*", + "mailgun.js": "*", + "node-telegram-bot-api": "*", + "nodemailer": "2.7.2", + "nodemailer-sendmail-transport": "*", + "redis": "*", + "socket.io": "^2.1.1", + "time-ago": "*", + "request": "^2.88.0", + "request-promise-native": "^1.0.5" + }, + "engines": { + "node": ">=8.11.3" + } +} diff --git a/config_examples/aeon-k12/pool.js b/config_examples/aeon-k12/pool.js new file mode 100644 index 000000000..8cb847cae --- /dev/null +++ b/config_examples/aeon-k12/pool.js @@ -0,0 +1,1212 @@ +/** + * Cryptonote Node.JS Pool + * https://github.com/dvandal/cryptonote-nodejs-pool + * + * Pool TCP daemon + **/ + +// Load required modules +let fs = require('fs'); +let net = require('net'); +let tls = require('tls'); +let async = require('async'); +let bignum = require('bignum'); + +let apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +let notifications = require('./notifications.js'); +let utils = require('./utils.js'); + +let cnHashing = require('cryptonight-hashing'); +if (config.hashingUtil) + cnHashing = require('multi-hashing'); + +// Set nonce pattern - must exactly be 8 hex chars +//let noncePattern = new RegExp("^[0-9A-Fa-f]{16}$"); +let noncePattern = new RegExp("^[0-9a-f]{16}$"); +// Set redis database cleanup interval +let cleanupInterval = config.redis.cleanupInterval && config.redis.cleanupInterval > 0 ? config.redis.cleanupInterval : 15; + +// Initialize log system +let logSystem = 'pool'; +require('./exceptionWriter.js')(logSystem); + +let threadId = '(Thread ' + process.env.forkId + ') '; +let log = function (severity, system, text, data) { + global.log(severity, system, threadId + text, data); +}; + +// Set cryptonight algorithm +let cnAlgorithm = config.cnAlgorithm || "cryptonight"; +let cnVariant = config.cnVariant || 0; +let cnBlobType = config.cnBlobType || 0; + +let cryptoNight; +//if (!cnHashing || !cnHashing[cnAlgorithm]) { +// log('error', logSystem, 'Invalid cryptonight algorithm: %s', [cnAlgorithm]); +//} else { +cryptoNight = cnHashing.k12; //[cnAlgorithm]; +//} + +// Set instance id +let instanceId = utils.instanceId(); + +// Pool variables +let poolStarted = false; +let connectedMiners = {}; + +// Pool settings +let shareTrustEnabled = config.poolServer.shareTrust && config.poolServer.shareTrust.enabled; +let shareTrustStepFloat = shareTrustEnabled ? config.poolServer.shareTrust.stepDown / 100 : 0; +let shareTrustMinFloat = shareTrustEnabled ? config.poolServer.shareTrust.min / 100 : 0; + +let banningEnabled = config.poolServer.banning && config.poolServer.banning.enabled; +let bannedIPs = {}; +let perIPStats = {}; + +let slushMiningEnabled = config.poolServer.slushMining && config.poolServer.slushMining.enabled; + +if (!config.poolServer.paymentId) config.poolServer.paymentId = {}; +if (!config.poolServer.paymentId.addressSeparator) config.poolServer.paymentId.addressSeparator = "+"; +if (config.poolServer.paymentId.validation == null) config.poolServer.paymentId.validation = true; + +config.isRandomX = config.isRandomX || false + + + +// Block templates +let validBlockTemplates = []; +let currentBlockTemplate; + +// Difficulty buffer +let diff1 = bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16); + +/** + * Convert buffer to byte array + **/ +Buffer.prototype.toByteArray = function () { + return Array.prototype.slice.call(this, 0); +}; + +/** + * Periodical updaters + **/ + +// Variable difficulty retarget +setInterval(function () { + let now = Date.now() / 1000 | 0; + for (let minerId in connectedMiners) { + let miner = connectedMiners[minerId]; + if (!miner.noRetarget) { + miner.retarget(now); + } + } +}, config.poolServer.varDiff.retargetTime * 1000); + +// Every 30 seconds clear out timed-out miners and old bans +setInterval(function () { + let now = Date.now(); + let timeout = config.poolServer.minerTimeout * 1000; + for (let minerId in connectedMiners) { + let miner = connectedMiners[minerId]; + if (now - miner.lastBeat > timeout) { + log('warn', logSystem, 'Miner timed out and disconnected %s@%s', [miner.login, miner.ip]); + delete connectedMiners[minerId]; + removeConnectedWorker(miner, 'timeout'); + } + } + + if (banningEnabled) { + for (ip in bannedIPs) { + let banTime = bannedIPs[ip]; + if (now - banTime > config.poolServer.banning.time * 1000) { + delete bannedIPs[ip]; + delete perIPStats[ip]; + log('info', logSystem, 'Ban dropped for %s', [ip]); + } + } + } + +}, 30000); + +/** + * Handle multi-thread messages + **/ +process.on('message', function (message) { + switch (message.type) { + case 'banIP': + bannedIPs[message.ip] = Date.now(); + break; + } +}); + +/** + * Block template + **/ +function BlockTemplate (template, isRandomX) { + this.isRandomX = isRandomX + this.blob = template.blocktemplate_blob; + this.difficulty = template.difficulty; + this.height = template.height; + if (this.isRandomX) { + this.seed_hash = template.seed_hash; + this.next_seed_hash = template.next_seed_hash; + } + this.reserveOffset = template.reserved_offset; + this.buffer = Buffer.from(this.blob, 'hex'); + instanceId.copy(this.buffer, this.reserveOffset + 4, 0, 3); + this.previous_hash = Buffer.alloc(32); + this.buffer.copy(this.previous_hash, 0, 7, 39); + this.extraNonce = 0; + + // The clientNonceLocation is the location at which the client pools should set the nonces for each of their clients. + this.clientNonceLocation = this.reserveOffset + 12; + // The clientPoolLocation is for multi-thread/multi-server pools to handle the nonce for each of their tiers. + this.clientPoolLocation = this.reserveOffset + 8; +} +BlockTemplate.prototype = { + + nextBlob: function () { + this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); + return utils.cnUtil.convert_blob(this.buffer, cnBlobType) + .toString('hex'); + }, + nextBlobWithChildNonce: function () { + // Write a 32 bit integer, big-endian style to the 0 byte of the reserve offset. + this.buffer.writeUInt32BE(++this.extraNonce, this.reserveOffset); + // Don't convert the blob to something hashable. You bad. + return this.buffer.toString('hex'); + } +}; + +/** + * Get block template + **/ +function getBlockTemplate (callback) { + apiInterfaces.rpcDaemon('getblocktemplate', { + reserve_size: 17 /*8*/ , + wallet_address: config.poolServer.poolAddress + }, + callback) +} + +/** + * Process block template + **/ +function processBlockTemplate (template, isRandomX) { + if (currentBlockTemplate) + validBlockTemplates.push(currentBlockTemplate); + + if (validBlockTemplates.length > 3) + validBlockTemplates.shift(); + + currentBlockTemplate = new BlockTemplate(template, isRandomX); + + for (let minerId in connectedMiners) { + let miner = connectedMiners[minerId]; + miner.pushMessage('job', miner.getJob()); + } +} + +/** + * Get LastBlock Header + **/ +function getLastBlockHeader (daemon, callback) { + apiInterfaces.rpcDaemon('getlastblockheader', {}, callback, daemon); +} + +/** + * Job refresh + **/ +function jobRefresh (loop) { + async.waterfall([ + function (callback) { + if (!poolStarted) { + startPoolServerTcp(function (successful) { + poolStarted = true + }); + setTimeout(jobRefresh, 1000, loop); + return; + } + + getLastBlockHeader(null, function (err, res) { + if (err) { + setTimeout(jobRefresh, 1000, loop); + return; + } + if (res.status === "OK" && res.hasOwnProperty('block_header')) { + let LastHash = res.block_header.hash.toString('hex'); + if (!currentBlockTemplate || LastHash !== currentBlockTemplate.previous_hash.toString('hex')) { + callback(null, true); + return; + } else { + callback(true); + return; + } + } else { + setTimeout(jobRefresh, 3000, loop); + return; + } + }); + + }, + function (getbc, callback) { + let start = new Date(); + getBlockTemplate(function (err, result) { + if (err) { + log('error', logSystem, 'Error polling getblocktemplate %j', [err]); + if (!poolStarted) log('error', logSystem, 'Could not start pool'); + setTimeout(jobRefresh, 1000, loop); + return; + } + + let buffer = Buffer.from(result.blocktemplate_blob, 'hex'); + let new_hash = Buffer.alloc(32); + buffer.copy(new_hash, 0, 7, 39); + try { + if (!currentBlockTemplate || new_hash.toString('hex') !== currentBlockTemplate.previous_hash.toString('hex')) { + log('info', logSystem, 'New %s block to mine at height %d w/ difficulty of %d', [config.coin, result.height, result.difficulty]); + processBlockTemplate(result, config.isRandomX); + callback(null); + return; + } else { + callback("Duplicate Template Blocked"); + return; + } + } catch (e) { + console.log(`getBlockTemplate ${e}`) + } + }) + }], + function (err) { + if (loop === true) { + setTimeout(function () { + jobRefresh(true); + }, config.poolServer.blockRefreshInterval); + } + } + ); +} + +/** + * Variable difficulty + **/ +let VarDiff = (function () { + let variance = config.poolServer.varDiff.variancePercent / 100 * config.poolServer.varDiff.targetTime; + return { + variance: variance, + bufferSize: config.poolServer.varDiff.retargetTime / config.poolServer.varDiff.targetTime * 4, + tMin: config.poolServer.varDiff.targetTime - variance, + tMax: config.poolServer.varDiff.targetTime + variance, + maxJump: config.poolServer.varDiff.maxJump + }; +})(); + +/** + * Miner + **/ +function Miner (id, login, pass, ip, port, agent, workerName, startingDiff, noRetarget, pushMessage) { + this.id = id; + this.login = login; + this.pass = pass; + this.ip = ip; + this.port = port; + this.proxy = false; + if (agent && agent.includes('xmr-node-proxy')) { + this.proxy = true; + } + this.workerName = workerName; + this.pushMessage = pushMessage; + this.heartbeat(); + this.noRetarget = noRetarget; + this.difficulty = startingDiff; + this.validJobs = []; + + // Vardiff related variables + this.shareTimeRing = utils.ringBuffer(16); + this.lastShareTime = Date.now() / 1000 | 0; + + if (shareTrustEnabled) { + this.trust = { + threshold: config.poolServer.shareTrust.threshold, + probability: 1, + penalty: 0 + }; + } +} +Miner.prototype = { + retarget: function (now) { + + let options = config.poolServer.varDiff; + + let sinceLast = now - this.lastShareTime; + let decreaser = sinceLast > VarDiff.tMax; + + let avg = this.shareTimeRing.avg(decreaser ? sinceLast : null); + let newDiff; + + let direction; + + if (avg > VarDiff.tMax && this.difficulty > options.minDiff) { + newDiff = options.targetTime / avg * this.difficulty; + newDiff = newDiff > options.minDiff ? newDiff : options.minDiff; + direction = -1; + } else if (avg < VarDiff.tMin && this.difficulty < options.maxDiff) { + newDiff = options.targetTime / avg * this.difficulty; + newDiff = newDiff < options.maxDiff ? newDiff : options.maxDiff; + direction = 1; + } else { + return; + } + + if (Math.abs(newDiff - this.difficulty) / this.difficulty * 100 > options.maxJump) { + let change = options.maxJump / 100 * this.difficulty * direction; + newDiff = this.difficulty + change; + } + + this.setNewDiff(newDiff); + this.shareTimeRing.clear(); + if (decreaser) this.lastShareTime = now; + }, + setNewDiff: function (newDiff) { + newDiff = Math.round(newDiff); + if (this.difficulty === newDiff) return; + log('info', logSystem, 'Retargetting difficulty %d to %d for %s', [this.difficulty, newDiff, this.login]); + this.pendingDifficulty = newDiff; + this.pushMessage('job', this.getJob()); + }, + heartbeat: function () { + this.lastBeat = Date.now(); + }, + getTargetHex: function () { + if (this.pendingDifficulty) { + this.lastDifficulty = this.difficulty; + this.difficulty = this.pendingDifficulty; + this.pendingDifficulty = null; + } + + let padded = Buffer.alloc(32); + padded.fill(0); + + let diffBuff = diff1.div(this.difficulty) + .toBuffer(); + diffBuff.copy(padded, 32 - diffBuff.length); + + let buff = padded.slice(0, 8); + let buffArray = buff.toByteArray() + .reverse(); + let buffReversed = Buffer.from(buffArray); + //this.target = buffReversed.readUInt32BE(0); + let hex = buffReversed.toString('hex'); + return hex; + }, + getJob: function () { + if (this.lastBlockHeight === currentBlockTemplate.height && !this.pendingDifficulty && this.cachedJob !== null) { + return this.cachedJob; + } + if (!this.proxy) { + let blob = currentBlockTemplate.nextBlob(); + this.lastBlockHeight = currentBlockTemplate.height; + let target = this.getTargetHex(); + + let newJob = { + id: utils.uid(), + extraNonce: currentBlockTemplate.extraNonce, + height: currentBlockTemplate.height, + difficulty: this.difficulty, + diffHex: this.diffHex, + submissions: [] + }; + if (currentBlockTemplate.isRandomX) { + newJob['seed_hash'] = currentBlockTemplate.seed_hash + newJob['next_seed_hash'] = currentBlockTemplate.next_seed_hash + } + this.validJobs.push(newJob); + + while (this.validJobs.length > 4) + this.validJobs.shift(); + + this.cachedJob = { + blob: blob, + job_id: newJob.id, + target: target, + id: this.id + }; + if (newJob.seed_hash) { + this.cachedJob.seed_hash = newJob.seed_hash; + this.cachedJob.next_seed_hash = newJob.next_seed_hash; + } + } else { + let blob = currentBlockTemplate.nextBlobWithChildNonce(); + + this.lastBlockHeight = currentBlockTemplate.height; + let target = this.getTargetHex(); + + let newJob = { + id: utils.uid(), + extraNonce: currentBlockTemplate.extraNonce, + height: currentBlockTemplate.height, + difficulty: this.difficulty, + diffHex: this.diffHex, + clientPoolLocation: currentBlockTemplate.clientPoolLocation, + clientNonceLocation: currentBlockTemplate.clientNonceLocation, + submissions: [] + }; + + if (currentBlockTemplate.isRandomX) { + newJob['seed_hash'] = currentBlockTemplate.seed_hash + newJob['next_seed_hash'] = currentBlockTemplate.next_seed_hash + } + + this.validJobs.push(newJob); + + while (this.validJobs.length > 4) + this.validJobs.shift(); + + this.cachedJob = { + blocktemplate_blob: blob, + difficulty: currentBlockTemplate.difficulty, + height: currentBlockTemplate.height, + reserved_offset: currentBlockTemplate.reserveOffset, + client_nonce_offset: currentBlockTemplate.clientNonceLocation, + client_pool_offset: currentBlockTemplate.clientPoolLocation, + target_diff: this.difficulty, + target_diff_hex: this.diffHex, + job_id: newJob.id, + id: this.id + }; + // if (newJob.seed_hash) { + // this.cachedJob.seed_hash = newJob.seed_hash; + // this.cachedJob.next_seed_hash = newJob.next_seed_hash; + // } + } + if (typeof config.includeAlgo !== "undefined" && config.includeAlgo) + this.cachedJob['algo'] = config.includeAlgo + if (typeof config.includeHeight !== "undefined" && config.includeHeight) + this.cachedJob['height'] = currentBlockTemplate.height + return this.cachedJob; + }, + checkBan: function (validShare) { + if (!banningEnabled) return; + + // Init global per-ip shares stats + if (!perIPStats[this.ip]) { + perIPStats[this.ip] = { + validShares: 0, + invalidShares: 0 + }; + } + + let stats = perIPStats[this.ip]; + validShare ? stats.validShares++ : stats.invalidShares++; + + if (stats.validShares + stats.invalidShares >= config.poolServer.banning.checkThreshold) { + if (stats.invalidShares / stats.validShares >= config.poolServer.banning.invalidPercent / 100) { + validShare ? this.validShares++ : this.invalidShares++; + log('warn', logSystem, 'Banned %s@%s', [this.login, this.ip]); + bannedIPs[this.ip] = Date.now(); + delete connectedMiners[this.id]; + process.send({ + type: 'banIP', + ip: this.ip + }); + removeConnectedWorker(this, 'banned'); + } else { + stats.invalidShares = 0; + stats.validShares = 0; + } + } + } +}; + +/** + * Handle miner method + **/ +function handleMinerMethod (method, params, ip, portData, sendReply, pushMessage) { + let miner = connectedMiners[params.id]; + + // Check for ban here, so preconnected attackers can't continue to screw you + if (IsBannedIp(ip)) { + sendReply('Your IP is banned'); + return; + } + + switch (method) { + case 'login': + let login = params.login; + if (!login) { + sendReply('Missing login'); + return; + } + + let port = portData.port; + + let pass = params.pass; + let workerName = ''; + if (params.rigid) { + workerName = params.rigid.trim(); + } else if (pass) { + workerName = pass.trim(); + if (pass.indexOf(':') >= 0 && pass.indexOf('@') >= 0) { + passDelimiterPos = pass.lastIndexOf(':'); + workerName = pass.substr(0, passDelimiterPos) + .trim(); + } + workerName = workerName.replace(/:/g, ''); + workerName = workerName.replace(/\+/g, ''); + workerName = workerName.replace(/\s/g, ''); + if (workerName.toLowerCase() === 'x') { + workerName = ''; + } + } + if (!workerName || workerName === '') { + workerName = 'undefined'; + } + workerName = utils.cleanupSpecialChars(workerName); + + let difficulty = portData.difficulty; + let noRetarget = false; + if (config.poolServer.fixedDiff.enabled) { + let fixedDiffCharPos = login.lastIndexOf(config.poolServer.fixedDiff.addressSeparator); + if (fixedDiffCharPos !== -1 && (login.length - fixedDiffCharPos < 32)) { + diffValue = login.substr(fixedDiffCharPos + 1); + difficulty = parseInt(diffValue); + login = login.substr(0, fixedDiffCharPos); + if (!difficulty || difficulty != diffValue) { + log('warn', logSystem, 'Invalid difficulty value "%s" for login: %s', [diffValue, login]); + difficulty = portData.difficulty; + } else { + noRetarget = true; + if (difficulty < config.poolServer.varDiff.minDiff) { + difficulty = config.poolServer.varDiff.minDiff; + } + } + } + } + + let addr = login.split(config.poolServer.paymentId.addressSeparator); + let address = addr[0] || null; + let paymentId = addr[1] || null; + + if (!address) { + log('warn', logSystem, 'No address specified for login'); + sendReply('Invalid address used for login'); + return; + } + + if (paymentId && paymentId.match('^([a-zA-Z0-9]){0,15}$')) { + if (config.poolServer.paymentId.validation) { + process.send({ + type: 'banIP', + ip: ip + }); + log('warn', logSystem, 'Invalid paymentId specified for login'); + } else { + log('warn', logSystem, 'Invalid paymentId specified for login'); + } + sendReply(`Invalid paymentId specified for login, ${portData.ip} banned for ${config.poolServer.banning.time / 60} minutes`); + return + } + + + if (!utils.validateMinerAddress(address)) { + let addressPrefix = utils.getAddressPrefix(address); + if (!addressPrefix) addressPrefix = 'N/A'; + + log('warn', logSystem, 'Invalid address used for login (prefix: %s): %s', [addressPrefix, address]); + sendReply('Invalid address used for login'); + return; + } + + let minerId = utils.uid(); + miner = new Miner(minerId, login, pass, ip, port, params.agent, workerName, difficulty, noRetarget, pushMessage); + connectedMiners[minerId] = miner; + + sendReply(null, { + id: minerId, + job: miner.getJob(), + status: 'OK' + }); + + newConnectedWorker(miner); + break; + case 'getjob': + if (!miner) { + sendReply('Unauthenticated'); + return; + } + miner.heartbeat(); + sendReply(null, miner.getJob()); + break; + case 'submit': + if (!miner) { + sendReply('Unauthenticated'); + return; + } + miner.heartbeat(); + + let job = miner.validJobs.filter(function (job) { + return job.id === params.job_id; + })[0]; + + if (!job) { + sendReply('Invalid job id'); + return; + } + + if (!params.nonce || !params.result) { + sendReply('Attack detected'); + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Malformed miner share: ' + JSON.stringify(params) + ' from ' + minerText); + return; + } + + params.nonce = params.nonce.substr(0, 16) + .toLowerCase(); + + if (!noncePattern.test(params.nonce)) { + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Malformed nonce: ' + JSON.stringify(params) + ' from ' + minerText); + perIPStats[miner.ip] = { + validShares: 0, + invalidShares: 999999 + }; + miner.checkBan(false); + sendReply('Duplicate share'); + return; + } + + // Force lowercase for further comparison + // params.nonce = params.nonce.toLowerCase(); + + if (!miner.proxy) { + if (job.submissions.indexOf(params.nonce) !== -1) { + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Duplicate share: ' + JSON.stringify(params) + ' from ' + minerText); + perIPStats[miner.ip] = { + validShares: 0, + invalidShares: 999999 + }; + miner.checkBan(false); + sendReply('Duplicate share'); + return; + } + + job.submissions.push(params.nonce); + } else { + if (!Number.isInteger(params.poolNonce) || !Number.isInteger(params.workerNonce)) { + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Malformed nonce: ' + JSON.stringify(params) + ' from ' + minerText); + perIPStats[miner.ip] = { + validShares: 0, + invalidShares: 999999 + }; + miner.checkBan(false); + sendReply('Duplicate share'); + return; + } + let nonce_test = `${params.nonce}_${params.poolNonce}_${params.workerNonce}`; + if (job.submissions.indexOf(nonce_test) !== -1) { + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Duplicate share: ' + JSON.stringify(params) + ' from ' + minerText); + perIPStats[miner.ip] = { + validShares: 0, + invalidShares: 999999 + }; + miner.checkBan(false); + sendReply('Duplicate share'); + return; + } + job.submissions.push(nonce_test); + + } + + let blockTemplate = currentBlockTemplate.height === job.height ? currentBlockTemplate : validBlockTemplates.filter(function (t) { + return t.height === job.height; + })[0]; + + if (!blockTemplate) { + sendReply('Block expired'); + return; + } + + let shareAccepted = processShare(miner, job, blockTemplate, params); + miner.checkBan(shareAccepted); + + if (shareTrustEnabled) { + if (shareAccepted) { + miner.trust.probability -= shareTrustStepFloat; + if (miner.trust.probability < shareTrustMinFloat) + miner.trust.probability = shareTrustMinFloat; + miner.trust.penalty--; + miner.trust.threshold--; + } else { + log('warn', logSystem, 'Share trust broken by %s@%s', [miner.login, miner.ip]); + miner.trust.probability = 1; + miner.trust.penalty = config.poolServer.shareTrust.penalty; + } + } + + if (!shareAccepted) { + sendReply('Rejected share: invalid result'); + return; + } + + let now = Date.now() / 1000 | 0; + miner.shareTimeRing.append(now - miner.lastShareTime); + miner.lastShareTime = now; + //miner.retarget(now); + + sendReply(null, { + status: 'OK' + }); + break; + case 'keepalived': + if (!miner) { + sendReply('Unauthenticated'); + return; + } + miner.heartbeat(); + sendReply(null, { + status: 'KEEPALIVED' + }); + break; + default: + sendReply('Invalid method'); + let minerText = miner ? (' ' + miner.login + '@' + miner.ip) : ''; + log('warn', logSystem, 'Invalid method: %s (%j) from %s', [method, params, minerText]); + break; + } +} + +/** + * New connected worker + **/ +function newConnectedWorker (miner) { + log('info', logSystem, 'Miner connected %s@%s on port', [miner.login, miner.ip, miner.port]); + if (miner.workerName !== 'undefined') log('info', logSystem, 'Worker Name: %s', [miner.workerName]); + if (miner.difficulty) log('info', logSystem, 'Miner difficulty fixed to %s', [miner.difficulty]); + + redisClient.sadd(config.coin + ':workers_ip:' + miner.login, miner.ip); + redisClient.hincrby(config.coin + ':ports:' + miner.port, 'users', 1); + + redisClient.hincrby(config.coin + ':active_connections', miner.login + '~' + miner.workerName, 1, function (error, connectedWorkers) { + if (connectedWorkers === 1) { + notifications.sendToMiner(miner.login, 'workerConnected', { + 'LOGIN': miner.login, + 'MINER': miner.login.substring(0, 7) + '...' + miner.login.substring(miner.login.length - 7), + 'IP': miner.ip.replace('::ffff:', ''), + 'PORT': miner.port, + 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '' + }); + } + }); +} + +/** + * Remove connected worker + **/ +function removeConnectedWorker (miner, reason) { + redisClient.hincrby(config.coin + ':ports:' + miner.port, 'users', '-1'); + + redisClient.hincrby(config.coin + ':active_connections', miner.login + '~' + miner.workerName, -1, function (error, connectedWorkers) { + if (reason === 'banned') { + notifications.sendToMiner(miner.login, 'workerBanned', { + 'LOGIN': miner.login, + 'MINER': miner.login.substring(0, 7) + '...' + miner.login.substring(miner.login.length - 7), + 'IP': miner.ip.replace('::ffff:', ''), + 'PORT': miner.port, + 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '' + }); + } else if (!connectedWorkers || connectedWorkers <= 0) { + notifications.sendToMiner(miner.login, 'workerTimeout', { + 'LOGIN': miner.login, + 'MINER': miner.login.substring(0, 7) + '...' + miner.login.substring(miner.login.length - 7), + 'IP': miner.ip.replace('::ffff:', ''), + 'PORT': miner.port, + 'WORKER_NAME': miner.workerName !== 'undefined' ? miner.workerName : '', + 'LAST_HASH': utils.dateFormat(new Date(miner.lastBeat), 'yyyy-mm-dd HH:MM:ss Z') + }); + } + }); +} + +/** + * Return if IP has been banned + **/ +function IsBannedIp (ip) { + if (!banningEnabled || !bannedIPs[ip]) return false; + + let bannedTime = bannedIPs[ip]; + let bannedTimeAgo = Date.now() - bannedTime; + let timeLeft = config.poolServer.banning.time * 1000 - bannedTimeAgo; + if (timeLeft > 0) { + return true; + } else { + delete bannedIPs[ip]; + log('info', logSystem, 'Ban dropped for %s', [ip]); + return false; + } +} + +/** + * Record miner share data + **/ +function recordShareData (miner, job, shareDiff, blockCandidate, hashHex, shareType, blockTemplate) { + let dateNow = Date.now(); + let dateNowSeconds = dateNow / 1000 | 0; + + let updateScore; + // Weighting older shares lower than newer ones to prevent pool hopping + if (slushMiningEnabled) { + // We need to do this via an eval script because we need fetching the last block time and + // calculating the score to run in a single transaction (otherwise we could have a race + // condition where a block gets discovered between the time we look up lastBlockFound and + // insert the score, which would give the miner an erroneously huge proportion on the new block) + updateScore = ['eval', ` + local age = (ARGV[3] - redis.call('hget', KEYS[2], 'lastBlockFound')) / 1000 + local score = string.format('%.17g', ARGV[2] * math.exp(age / ARGV[4])) + redis.call('hincrbyfloat', KEYS[1], ARGV[1], score) + return {score, tostring(age)} + `, + 2 /*keys*/ , config.coin + ':scores:roundCurrent', config.coin + ':stats', + /* args */ + miner.login, job.difficulty, Date.now(), config.poolServer.slushMining.weight]; + } else { + job.score = job.difficulty; + updateScore = ['hincrbyfloat', config.coin + ':scores:roundCurrent', miner.login, job.score] + } + + let redisCommands = [ + updateScore, + ['hincrby', config.coin + ':shares_actual:roundCurrent', miner.login, job.difficulty], + ['zadd', config.coin + ':hashrate', dateNowSeconds, [job.difficulty, miner.login, dateNow].join(':')], + ['hincrby', config.coin + ':workers:' + miner.login, 'hashes', job.difficulty], + ['hset', config.coin + ':workers:' + miner.login, 'lastShare', dateNowSeconds], + ['expire', config.coin + ':workers:' + miner.login, (86400 * cleanupInterval)], + ['expire', config.coin + ':payments:' + miner.login, (86400 * cleanupInterval)] + ]; + + if (miner.workerName) { + redisCommands.push(['zadd', config.coin + ':hashrate', dateNowSeconds, [job.difficulty, miner.login + '~' + miner.workerName, dateNow].join(':')]); + redisCommands.push(['hincrby', config.coin + ':unique_workers:' + miner.login + '~' + miner.workerName, 'hashes', job.difficulty]); + redisCommands.push(['hset', config.coin + ':unique_workers:' + miner.login + '~' + miner.workerName, 'lastShare', dateNowSeconds]); + redisCommands.push(['expire', config.coin + ':unique_workers:' + miner.login + '~' + miner.workerName, (86400 * cleanupInterval)]); + } + + if (blockCandidate) { + redisCommands.push(['hset', config.coin + ':stats', 'lastBlockFound', Date.now()]); + redisCommands.push(['rename', config.coin + ':scores:roundCurrent', config.coin + ':scores:round' + job.height]); + redisCommands.push(['rename', config.coin + ':shares_actual:roundCurrent', config.coin + ':shares_actual:round' + job.height]); + redisCommands.push(['hgetall', config.coin + ':scores:round' + job.height]); + redisCommands.push(['hgetall', config.coin + ':shares_actual:round' + job.height]); + } + + redisClient.multi(redisCommands) + .exec(function (err, replies) { + if (err) { + log('error', logSystem, 'Failed to insert share data into redis %j \n %j', [err, redisCommands]); + return; + } + + if (slushMiningEnabled) { + job.score = parseFloat(replies[0][0]); + let age = parseFloat(replies[0][1]); + log('info', logSystem, 'Submitted score ' + job.score + ' for difficulty ' + job.difficulty + ' and round age ' + age + 's'); + } + + if (blockCandidate) { + let workerScores = replies[replies.length - 2]; + let workerShares = replies[replies.length - 1]; + let totalScore = Object.keys(workerScores) + .reduce(function (p, c) { + return p + parseFloat(workerScores[c]) + }, 0); + let totalShares = Object.keys(workerShares) + .reduce(function (p, c) { + return p + parseInt(workerShares[c]) + }, 0); + redisClient.zadd(config.coin + ':blocks:candidates', job.height, [ + hashHex, + Date.now() / 1000 | 0, + blockTemplate.difficulty, + totalShares, + totalScore + ].join(':'), function (err, result) { + if (err) { + log('error', logSystem, 'Failed inserting block candidate %s \n %j', [hashHex, err]); + } + }); + + notifications.sendToAll('blockFound', { + 'HEIGHT': job.height, + 'HASH': hashHex, + 'DIFFICULTY': blockTemplate.difficulty, + 'SHARES': totalShares, + 'MINER': miner.login.substring(0, 7) + '...' + miner.login.substring(miner.login.length - 7) + }); + } + + }); + + log('info', logSystem, 'Accepted %s share at difficulty %d/%d from %s@%s', [shareType, job.difficulty, shareDiff, miner.login, miner.ip]); +} + +/** + * Process miner share data + **/ +function processShare (miner, job, blockTemplate, params) { + let nonce = params.nonce; + let resultHash = params.result; + let template = Buffer.alloc(blockTemplate.buffer.length); + if (!miner.proxy) { + blockTemplate.buffer.copy(template); + template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); + } else { + blockTemplate.buffer.copy(template); + template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); + template.writeUInt32BE(params.poolNonce, job.clientPoolLocation); + template.writeUInt32BE(params.workerNonce, job.clientNonceLocation); + } + let shareBuffer = utils.cnUtil.construct_block_blob(template, Buffer.from(nonce, 'hex'), cnBlobType); + + let convertedBlob; + let hash; + let shareType; + + if (shareTrustEnabled && miner.trust.threshold <= 0 && miner.trust.penalty <= 0 && Math.random() > miner.trust.probability) { + hash = Buffer.from(resultHash, 'hex'); + shareType = 'trusted'; + } else { + convertedBlob = utils.cnUtil.convert_blob(shareBuffer, cnBlobType); + let hard_fork_version = convertedBlob[0]; + if (config.hashingUtil) { + hash = cryptoNight(convertedBlob); + } else if (blockTemplate.isRandomX) { + hash = cryptoNight(convertedBlob, Buffer.from(blockTemplate.seed_hash, 'hex')); + } else { + if (typeof config.includeHeight !== "undefined" && config.includeHeight) + hash = cryptoNight(convertedBlob, cnVariant, job.height); + else + hash = cryptoNight(convertedBlob, cnVariant); + } + log('info', logSystem, 'Mining pool algorithm: %s variant %d, Hard fork version: %d', [cnAlgorithm, cnVariant, hard_fork_version]); + shareType = 'valid'; + } + + if (hash.toString('hex') !== resultHash) { + log('warn', logSystem, 'Bad hash from miner %s@%s', [miner.login, miner.ip]); + return false; + } + + let hashArray = hash.toByteArray() + .reverse(); + let hashNum = bignum.fromBuffer(Buffer.from(hashArray)); + let hashDiff = diff1.div(hashNum); + + if (hashDiff.ge(blockTemplate.difficulty)) { + + apiInterfaces.rpcDaemon('submitblock', [shareBuffer.toString('hex')], function (error, result) { + if (error) { + log('error', logSystem, 'Error submitting block at height %d from %s@%s, share type: "%s" - %j', [job.height, miner.login, miner.ip, shareType, error]); + recordShareData(miner, job, hashDiff.toString(), false, null, shareType); + } else { + let blockFastHash = utils.cnUtil.get_block_id(shareBuffer, cnBlobType) + .toString('hex'); + log('info', logSystem, + 'Block %s found at height %d by miner %s@%s - submit result: %j', + [blockFastHash.substr(0, 6), job.height, miner.login, miner.ip, result] + ); + recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate); + jobRefresh(); + } + }); + } else if (hashDiff.lt(job.difficulty)) { + log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.login, miner.ip]); + return false; + } else { + recordShareData(miner, job, hashDiff.toString(), false, null, shareType); + } + + return true; +} + +/** + * Start pool server on TCP ports + **/ +let httpResponse = ' 200 OK\nContent-Type: text/plain\nContent-Length: 20\n\nMining server online'; + +function startPoolServerTcp (callback) { + log('info', logSystem, 'Clear values for connected workers in redis database.'); + redisClient.del(config.coin + ':active_connections'); + + async.each(config.poolServer.ports, function (portData, cback) { + let handleMessage = function (socket, jsonData, pushMessage) { + if (!jsonData.id) { + log('warn', logSystem, 'Miner RPC request missing RPC id'); + return; + } else if (!jsonData.method) { + log('warn', logSystem, 'Miner RPC request missing RPC method'); + return; + } else if (!jsonData.params) { + log('warn', logSystem, 'Miner RPC request missing RPC params'); + return; + } + + let sendReply = function (error, result) { + if (!socket.writable) return; + let sendData = JSON.stringify({ + id: jsonData.id, + jsonrpc: "2.0", + error: error ? { + code: -1, + message: error + } : null, + result: result + }) + "\n"; + socket.write(sendData); + }; + + handleMinerMethod(jsonData.method, jsonData.params, socket.remoteAddress, portData, sendReply, pushMessage); + }; + + let socketResponder = function (socket) { + socket.setKeepAlive(true); + socket.setEncoding('utf8'); + + let dataBuffer = ''; + + let pushMessage = function (method, params) { + if (!socket.writable) return; + let sendData = JSON.stringify({ + jsonrpc: "2.0", + method: method, + params: params + }) + "\n"; + socket.write(sendData); + }; + + socket.on('data', function (d) { + dataBuffer += d; + if (Buffer.byteLength(dataBuffer, 'utf8') > 10240) { //10KB + dataBuffer = null; + log('warn', logSystem, 'Socket flooding detected and prevented from %s', [socket.remoteAddress]); + socket.destroy(); + return; + } + if (dataBuffer.indexOf('\n') !== -1) { + let messages = dataBuffer.split('\n'); + let incomplete = dataBuffer.slice(-1) === '\n' ? '' : messages.pop(); + for (let i = 0; i < messages.length; i++) { + let message = messages[i]; + if (message.trim() === '') continue; + let jsonData; + try { + jsonData = JSON.parse(message); + } catch (e) { + if (message.indexOf('GET /') === 0) { + if (message.indexOf('HTTP/1.1') !== -1) { + socket.end('HTTP/1.1' + httpResponse); + break; + } else if (message.indexOf('HTTP/1.0') !== -1) { + socket.end('HTTP/1.0' + httpResponse); + break; + } + } + + log('warn', logSystem, 'Malformed message from %s: %s', [socket.remoteAddress, message]); + socket.destroy(); + + break; + } + try { + handleMessage(socket, jsonData, pushMessage); + } catch (e) { + log('warn', logSystem, 'Malformed message from ' + socket.remoteAddress + ' generated an exception. Message: ' + message); + if (e.message) log('warn', logSystem, 'Exception: ' + e.message); + } + } + dataBuffer = incomplete; + } + }) + .on('error', function (err) { + if (err.code !== 'ECONNRESET') + log('warn', logSystem, 'Socket error from %s %j', [socket.remoteAddress, err]); + }) + .on('close', function () { + pushMessage = function () {}; + }); + }; + + if (portData.ssl) { + if (!config.poolServer.sslCert) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate not configured', [portData.port]); + cback(true); + } else if (!config.poolServer.sslKey) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL key not configured', [portData.port]); + cback(true); + } else if (!config.poolServer.sslCA) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate authority not configured', [portData.port]); + cback(true); + } else if (!fs.existsSync(config.poolServer.sslCert)) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate file not found (configuration error)', [portData.port]); + cback(true); + } else if (!fs.existsSync(config.poolServer.sslKey)) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL key file not found (configuration error)', [portData.port]); + cback(true); + } else if (!fs.existsSync(config.poolServer.sslCA)) { + log('error', logSystem, 'Could not start server listening on port %d (SSL): SSL certificate authority file not found (configuration error)', [portData.port]); + cback(true); + } else { + let options = { + key: fs.readFileSync(config.poolServer.sslKey), + cert: fs.readFileSync(config.poolServer.sslCert), + ca: fs.readFileSync(config.poolServer.sslCA) + }; + tls.createServer(options, socketResponder) + .listen(portData.port, function (error, result) { + if (error) { + log('error', logSystem, 'Could not start server listening on port %d (SSL), error: $j', [portData.port, error]); + cback(true); + return; + } + + log('info', logSystem, 'Clear values for SSL port %d in redis database.', [portData.port]); + redisClient.del(config.coin + ':ports:' + portData.port); + redisClient.hset(config.coin + ':ports:' + portData.port, 'port', portData.port); + + log('info', logSystem, 'Started server listening on port %d (SSL)', [portData.port]); + cback(); + }); + } + } else { + net.createServer(socketResponder) + .listen(portData.port, function (error, result) { + if (error) { + log('error', logSystem, 'Could not start server listening on port %d, error: $j', [portData.port, error]); + cback(true); + return; + } + + log('info', logSystem, 'Clear values for port %d in redis database.', [portData.port]); + redisClient.del(config.coin + ':ports:' + portData.port); + redisClient.hset(config.coin + ':ports:' + portData.port, 'port', portData.port); + + log('info', logSystem, 'Started server listening on port %d', [portData.port]); + cback(); + }); + } + }, function (err) { + if (err) + callback(false); + else + callback(true); + }); +} + +/** + * Initialize pool server + **/ + +(function init () { + jobRefresh(true, function (sucessful) {}); +})(); diff --git a/config_examples/aeon-k12/utils.js b/config_examples/aeon-k12/utils.js new file mode 100644 index 000000000..e9397065c --- /dev/null +++ b/config_examples/aeon-k12/utils.js @@ -0,0 +1,151 @@ +/** + * Cryptonote Node.JS Pool + * https://github.com/dvandal/cryptonote-nodejs-pool + * + * Utilities functions + **/ + +// Load required module +let crypto = require('crypto'); + +let dateFormat = require('dateformat'); +exports.dateFormat = dateFormat; + +//let cnUtil = require('cryptoforknote-util'); +let cnUtil = require('cryptonote-util'); +exports.cnUtil = cnUtil; + +/** + * Generate random instance id + **/ +exports.instanceId = function () { + return crypto.randomBytes(4); +} + +/** + * Validate miner address + **/ +let addressBase58Prefix = parseInt(cnUtil.address_decode(Buffer.from(config.poolServer.poolAddress)) + .toString()); +let integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1; + +// Get address prefix +function getAddressPrefix (address) { + let addressBuffer = Buffer.from(address); + + let addressPrefix = cnUtil.address_decode(addressBuffer); + if (addressPrefix) addressPrefix = parseInt(addressPrefix.toString()); + + if (!addressPrefix) { + addressPrefix = cnUtil.address_decode_integrated(addressBuffer); + if (addressPrefix) addressPrefix = parseInt(addressPrefix.toString()); + } + + return addressPrefix || null; +} +exports.getAddressPrefix = getAddressPrefix; + +// Validate miner address +exports.validateMinerAddress = function (address) { + let addressPrefix = getAddressPrefix(address); + if (addressPrefix === addressBase58Prefix) return true; + else if (addressPrefix === integratedAddressBase58Prefix) return true; + return false; +} + +// Return if value is an integrated address +exports.isIntegratedAddress = function (address) { + let addressPrefix = getAddressPrefix(address); + return (addressPrefix === integratedAddressBase58Prefix); +} + +/** + * Cleanup special characters (fix for non latin characters) + **/ +function cleanupSpecialChars (str) { + str = str.replace(/[ÀÁÂÃÄÅ]/g, "A"); + str = str.replace(/[à áâãäå]/g, "a"); + str = str.replace(/[ÈÉÊË]/g, "E"); + str = str.replace(/[èéêë]/g, "e"); + str = str.replace(/[ÌÎÏ]/g, "I"); + str = str.replace(/[ìîï]/g, "i"); + str = str.replace(/[ÒÔÖ]/g, "O"); + str = str.replace(/[òôö]/g, "o"); + str = str.replace(/[ÙÛÜ]/g, "U"); + str = str.replace(/[ùûü]/g, "u"); + return str.replace(/[^A-Za-z0-9\-\_]/gi, ''); +} +exports.cleanupSpecialChars = cleanupSpecialChars; + +/** + * Get readable hashrate + **/ +exports.getReadableHashRate = function (hashrate) { + let i = 0; + let byteUnits = [' H', ' KH', ' MH', ' GH', ' TH', ' PH']; + while (hashrate > 1000) { + hashrate = hashrate / 1000; + i++; + } + return hashrate.toFixed(2) + byteUnits[i] + '/sec'; +} + +/** + * Get readable coins + **/ +exports.getReadableCoins = function (coins, digits, withoutSymbol) { + let coinDecimalPlaces = config.coinDecimalPlaces || config.coinUnits.toString() + .length - 1; + let amount = (parseInt(coins || 0) / config.coinUnits) + .toFixed(digits || coinDecimalPlaces); + return amount + (withoutSymbol ? '' : (' ' + config.symbol)); +} + +/** + * Generate unique id + **/ +exports.uid = function () { + let min = 100000000000000; + let max = 999999999999999; + let id = Math.floor(Math.random() * (max - min + 1)) + min; + return id.toString(); +}; + +/** + * Ring buffer + **/ +exports.ringBuffer = function (maxSize) { + let data = []; + let cursor = 0; + let isFull = false; + + return { + append: function (x) { + if (isFull) { + data[cursor] = x; + cursor = (cursor + 1) % maxSize; + } else { + data.push(x); + cursor++; + if (data.length === maxSize) { + cursor = 0; + isFull = true; + } + } + }, + avg: function (plusOne) { + let sum = data.reduce(function (a, b) { + return a + b + }, plusOne || 0); + return sum / ((isFull ? maxSize : cursor) + (plusOne ? 1 : 0)); + }, + size: function () { + return isFull ? maxSize : cursor; + }, + clear: function () { + data = []; + cursor = 0; + isFull = false; + } + }; +}; diff --git a/config_examples/aeon.json b/config_examples/aeon.json index a34bcce39..b5796bb6b 100644 --- a/config_examples/aeon.json +++ b/config_examples/aeon.json @@ -1,304 +1,315 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "aeon", - "symbol": "AEON", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 12, - "coinDifficultyTarget": 240, + "coin": "aeon", + "symbol": "AEON", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 12, + "coinDifficultyTarget": 240, + "blockchainExplorer": "https://aeonblockexplorer.com/search?value={id}", + "transactionExplorer": "https://aeonblockexplorer.com/search?value={id}", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 0, - "cnAlgorithm": "cryptonight_light", - "cnVariant": 1, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 600, - "maxAddresses": 20, - "mixin": 3, - "priority": 0, - "transferFee": 10000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 250000000000, - "maxTransactionAmount": 0, - "denomination": 10000000000 - }, + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 20, + "mixin": 3, + "priority": 0, + "transferFee": 10000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 250000000000, + "maxTransactionAmount": 0, + "denomination": 10000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.0, - "devDonation": 0.0, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 3, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 3, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 11181 - }, + "daemon": { + "host": "127.0.0.1", + "port": 11181 + }, - "wallet": { - "host": "127.0.0.1", - "port": 11188 - }, + "wallet": { + "host": "127.0.0.1", + "port": 11188 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "enable": "/enable", - "disable": "/disable" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } } diff --git a/config_examples/alloy.json b/config_examples/alloy.json index c660ebb68..6a450ece4 100644 --- a/config_examples/alloy.json +++ b/config_examples/alloy.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "alloy", - "symbol": "XAO", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 180, + "coin": "alloy", + "symbol": "XAO", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 180, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 2, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 2, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 10000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 10000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 1811 - }, + "daemon": { + "host": "127.0.0.1", + "port": 1811 + }, - "wallet": { - "host": "127.0.0.1", - "port": 1812 - }, + "wallet": { + "host": "127.0.0.1", + "port": 1812 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/arqma.json b/config_examples/arqma.json index 443becae2..723b52d19 100644 --- a/config_examples/arqma.json +++ b/config_examples/arqma.json @@ -1,311 +1,319 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "arqma", - "symbol": "ARQ", - "coinUnits": 1000000000, - "coinDecimalPlaces": 9, - "coinDifficultyTarget": 120, + "coin": "arqma", + "symbol": "ARQ", + "coinUnits": 1000000000, + "coinDecimalPlaces": 9, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "cnAlgorithm": "randomx", + "cnVariant": 2, + "cnBlobType": 0, + "isRandomX": true, + "includeHeight": false, - "cnAlgorithm": "cryptonight_light", - "cnVariant": 1, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 19, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": "0x116bc7", + "subAddressPrefix": "0x6847", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": true, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 600, - "maxAddresses": 20, - "mixin": 6, - "priority": 0, - "transferFee": 2000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000, - "maxTransactionAmount": 0, - "denomination": 100000000 - }, + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 20, + "mixin": 10, + "priority": 0, + "transferFee": 100000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 25000000, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 18, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 18, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 19994 - }, + "daemon": { + "host": "127.0.0.1", + "port": 19994 + }, - "wallet": { - "host": "127.0.0.1", - "port": 19995 - }, + "wallet": { + "host": "127.0.0.1", + "port": 19999 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "enable": "/enable", - "disable": "/disable" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, - "prices": { - "source": "altex", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "altex", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } } diff --git a/config_examples/bbscoin.json b/config_examples/bbscoin.json index e04a5c534..88dda948c 100644 --- a/config_examples/bbscoin.json +++ b/config_examples/bbscoin.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "bbscoin", - "symbol": "BBS", - "coinUnits": 100000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "bbscoin", + "symbol": "BBS", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight_light", - "cnVariant": 1, - "cnBlobType": 2, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 10000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 500000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 10000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 500000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 21204 - }, + "daemon": { + "host": "127.0.0.1", + "port": 21204 + }, - "wallet": { - "host": "127.0.0.1", - "port": 21205 - }, + "wallet": { + "host": "127.0.0.1", + "port": 21205 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/bloc.json b/config_examples/bloc.json new file mode 100644 index 000000000..f546bde70 --- /dev/null +++ b/config_examples/bloc.json @@ -0,0 +1,320 @@ +{ + "poolHost": "your.pool.host", + + "coin": "BLOC.MONEY", + "symbol": "BLOC", + "coinUnits": 10000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_heavy", + "cnVariant": 1, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 249588424, + "blockRefreshInterval": 1000, + "minerTimeout": 300, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 900, + "maxAddresses": 50, + "mixin": 0, + "priority": 0, + "transferFee": 1, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 50000, + "maxTransactionAmount": 250000000, + "denomination": 10000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 50, + "poolFee": 0.8, + "devDonation": 0.0, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 2086 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8070, + "password": "RPCpassword" + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getBalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } +} diff --git a/config_examples/bytecoin.json b/config_examples/bytecoin.json index ee2a5fe17..046461905 100644 --- a/config_examples/bytecoin.json +++ b/config_examples/bytecoin.json @@ -1,312 +1,317 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "bytecoin", - "symbol": "BCN", - "coinUnits": 100000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 240, + "coin": "bytecoin", + "symbol": "BCN", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 2, - "daemonType": "bytecoin", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 2, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 6, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 1, - "priority": 0, - "transferFee": 50000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000, - "maxTransactionAmount": 50000000000000, - "denomination": 1000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 1, + "priority": 0, + "transferFee": 50000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000, + "maxTransactionAmount": 50000000000000, + "denomination": 1000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 10, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0, - "fixBlockHeightRPC": true - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 8081 - }, + "daemon": { + "host": "127.0.0.1", + "port": 8070 + }, - "wallet": { - "host": "127.0.0.1", - "port": 8082 - }, + "wallet": { + "host": "127.0.0.1", + "port": 8071 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "enable": "/enable", - "disable": "/disable" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "get_status" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "get_status" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "get_balance" + } + }, - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - } - } + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } } diff --git a/config_examples/bytecoin/apiInterfaces.js b/config_examples/bytecoin/apiInterfaces.js new file mode 100644 index 000000000..57a0cf38c --- /dev/null +++ b/config_examples/bytecoin/apiInterfaces.js @@ -0,0 +1,128 @@ +/** + * Cryptonote Node.JS Pool + * https://github.com/dvandal/cryptonote-nodejs-pool + * + * Handle communications to APIs + **/ + +// Load required modules +var http = require('http'); +var https = require('https'); + +/** + * Send API request using JSON HTTP + **/ +function jsonHttpRequest (host, port, data, callback, path) { + path = path || '/json_rpc'; + + + var options = { + hostname: host, + port: port, + path: path, + method: data ? 'POST' : 'GET', + headers: { + 'Authorization': 'Basic ' + Buffer.from("user:pass") + .toString('base64'), + 'Content-Length': data.length, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }; + + var req = (port === 443 ? https : http) + .request(options, function (res) { + var replyData = ''; + res.setEncoding('utf8'); + res.on('data', function (chunk) { + replyData += chunk; + }); + res.on('end', function () { + var replyJson; + try { + replyJson = JSON.parse(replyData); + } catch (e) { + callback(e, {}); + return; + } + callback(null, replyJson); + }); + }); + + req.on('error', function (e) { + callback(e, {}); + }); + + req.end(data); +} + +/** + * Send RPC request + **/ +function rpc (host, port, method, params, callback, password) { + var request = { + id: "0", + jsonrpc: "2.0", + method: method, + params: params + }; + if (password !== undefined) { + request['password'] = password; + } + var data = JSON.stringify(request); + jsonHttpRequest(host, port, data, function (error, replyJson) { + if (error) { + callback(error, {}); + return; + } + callback(replyJson.error, replyJson.result) + }); +} + +/** + * Send RPC requests in batch mode + **/ +function batchRpc (host, port, array, callback) { + var rpcArray = []; + for (var i = 0; i < array.length; i++) { + rpcArray.push({ + id: i.toString(), + jsonrpc: "2.0", + method: array[i][0], + params: array[i][1] + }); + } + var data = JSON.stringify(rpcArray); + jsonHttpRequest(host, port, data, callback); +} + +/** + * Send RPC request to pool API + **/ +function poolRpc (host, port, path, callback) { + jsonHttpRequest(host, port, '', callback, path); +} + +/** + * Exports API interfaces functions + **/ +module.exports = function (daemonConfig, walletConfig, poolApiConfig) { + return { + batchRpcDaemon: function (batchArray, callback) { + batchRpc(daemonConfig.host, daemonConfig.port, batchArray, callback); + }, + rpcDaemon: function (method, params, callback) { + rpc(daemonConfig.host, daemonConfig.port, method, params, callback); + }, + rpcWallet: function (method, params, callback) { + rpc(walletConfig.host, walletConfig.port, method, params, callback, + walletConfig.password); + }, + pool: function (path, callback) { + var bindIp = config.api.bindIp ? config.api.bindIp : "0.0.0.0"; + var poolApi = (bindIp !== "0.0.0.0" ? poolApiConfig.bindIp : "127.0.0.1"); + poolRpc(poolApi, poolApiConfig.port, path, callback); + }, + jsonHttpRequest: jsonHttpRequest + } +}; diff --git a/config_examples/bytecoin/paymentProcessor.js b/config_examples/bytecoin/paymentProcessor.js new file mode 100644 index 000000000..2095a8529 --- /dev/null +++ b/config_examples/bytecoin/paymentProcessor.js @@ -0,0 +1,441 @@ +/** + * Cryptonote Node.JS Pool + * https://github.com/dvandal/cryptonote-nodejs-pool + * + * Payments processor + **/ + +// Load required modules +var fs = require('fs'); +var async = require('async'); + +var apiInterfaces = require('./apiInterfaces.js')(config.daemon, config.wallet, config.api); +var notifications = require('./notifications.js'); +var utils = require('./utils.js'); + +// Initialize log system +var logSystem = 'payments'; +require('./exceptionWriter.js')(logSystem); + +/** + * Run payments processor + **/ + +log('info', logSystem, 'Started'); + +if (!config.poolServer.paymentId) config.poolServer.paymentId = {}; +if (!config.poolServer.paymentId.addressSeparator) config.poolServer.paymentId.addressSeparator = "+"; +if (!config.payments.priority) config.payments.priority = 0; + +var daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; +/*************************** + BYTECOIN FUNCTIONS + ***************************/ +function bytecoin_create_transaction (transferCmd, callback) { + rpcCommand = "create_transaction"; + rpcRequest = { + transaction: { + anonymity: transferCmd.rpc.mixin, + // payment_id: "", + transfers: transferCmd.rpc.destinations + }, + spend_addresses: [ + config.poolServer.poolAddress + ], + change_address: config.poolServer.poolAddress, + optimization: "minimal" + }; + if (transferCmd.rpc.payment_id) { + rpcRequest.transaction.payment_id = transferCmd.rpc.payment_id; + } + console.log(rpcCommand); + apiInterfaces.rpcWallet(rpcCommand, rpcRequest, function (error, result) { + callback(error, result, transferCmd); + }); +} + + +function bytecoin_send_transaction (createTransactionResult, transferCmd, callback) { + var tx_hash = ""; + var timeOffset = 0; + var notify_miners = []; + + if (!(createTransactionResult && createTransactionResult.binary_transaction)) { + callback(createTransactionResult); + } else { + rpcCommand = "send_transaction"; + rpcRequest = { + binary_transaction: createTransactionResult.binary_transaction + }; + tx_hash = createTransactionResult.transaction.hash; + console.log(rpcCommand); + apiInterfaces.rpcWallet(rpcCommand, rpcRequest, function (error, result) { + if (error) { + log('error', logSystem, 'Error with %s RPC request to wallet daemon %j', [rpcCommand, error]); + log('error', logSystem, 'Payments failed to send to %j', transferCmd.rpc.destinations); + callback(error); + } + + var now = (timeOffset++) + Date.now() / 1000 | 0; + var txHash = daemonType === "bytecoin" ? (tx_hash ? tx_hash : result.transactionHash) : result.tx_hash; + txHash = txHash.replace('<', '') + .replace('>', ''); + + transferCmd.redis.push(['zadd', config.coin + ':payments:all', now, [ + txHash, + transferCmd.amount, + transferCmd.rpc.fee, + transferCmd.rpc.mixin, + Object.keys(transferCmd.rpc.destinations) + .length + ].join(':')]); + + var notify_miners_on_success = []; + for (var i = 0; i < transferCmd.rpc.destinations.length; i++) { + var destination = transferCmd.rpc.destinations[i]; + if (transferCmd.rpc.payment_id) { + destination.address += config.poolServer.paymentId.addressSeparator + transferCmd.rpc.payment_id; + } + transferCmd.redis.push(['zadd', config.coin + ':payments:' + destination.address, now, [ + txHash, + destination.amount, + transferCmd.rpc.fee, + transferCmd.rpc.mixin + ].join(':')]); + + notify_miners_on_success.push(destination); + } + + log('info', logSystem, 'Payments sent via wallet daemon %j', [result]); + redisClient.multi(transferCmd.redis) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Super critical error! Payments sent yet failing to update balance in redis, double payouts likely to happen %j', [error]); + log('error', logSystem, 'Double payments likely to be sent to %j', transferCmd.rpc.destinations); + callback(error); + return; + } + + for (var m in notify_miners_on_success) { + notify_miners.push(notify_miners_on_success[m]); + } + + callback(null, true); + }); + }); + + } // endif result && result.binary_transaction + + +} +/*************************** + /BYTECOIN FUNCTIONS + ***************************/ + + + + +function runInterval () { + async.waterfall([ + + // Get worker keys + function (callback) { + redisClient.keys(config.coin + ':workers:*', function (error, result) { + if (error) { + log('error', logSystem, 'Error trying to get worker balances from redis %j', [error]); + callback(true); + return; + } + callback(null, result); + }); + }, + + // Get worker balances + function (keys, callback) { + var redisCommands = keys.map(function (k) { + return ['hget', k, 'balance']; + }); + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with getting balances from redis %j', [error]); + callback(true); + return; + } + + var balances = {}; + for (var i = 0; i < replies.length; i++) { + var parts = keys[i].split(':'); + var workerId = parts[parts.length - 1]; + + balances[workerId] = parseInt(replies[i]) || 0; + } + callback(null, keys, balances); + }); + }, + + // Get worker minimum payout + function (keys, balances, callback) { + var redisCommands = keys.map(function (k) { + return ['hget', k, 'minPayoutLevel']; + }); + redisClient.multi(redisCommands) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Error with getting minimum payout from redis %j', [error]); + callback(true); + return; + } + + var minPayoutLevel = {}; + for (var i = 0; i < replies.length; i++) { + var parts = keys[i].split(':'); + var workerId = parts[parts.length - 1]; + + var minLevel = config.payments.minPayment; + var maxLevel = config.payments.maxPayment; + var defaultLevel = minLevel; + + var payoutLevel = parseInt(replies[i]) || minLevel; + if (payoutLevel < minLevel) payoutLevel = minLevel; + if (maxLevel && payoutLevel > maxLevel) payoutLevel = maxLevel; + minPayoutLevel[workerId] = payoutLevel; + + if (payoutLevel !== defaultLevel) { + log('info', logSystem, 'Using payout level of %s for %s (default: %s)', [utils.getReadableCoins(minPayoutLevel[workerId]), workerId, utils.getReadableCoins(defaultLevel)]); + } + } + callback(null, balances, minPayoutLevel); + }); + }, + + // Filter workers under balance threshold for payment + function (balances, minPayoutLevel, callback) { + var payments = {}; + + for (var worker in balances) { + var balance = balances[worker]; + if (balance >= minPayoutLevel[worker]) { + var remainder = balance % config.payments.denomination; + var payout = balance - remainder; + + if (config.payments.dynamicTransferFee && config.payments.minerPayFee) { + payout -= config.payments.transferFee; + } + if (payout < 0) continue; + + payments[worker] = payout; + } + } + + if (Object.keys(payments) + .length === 0) { + log('info', logSystem, 'No workers\' balances reached the minimum payment threshold'); + callback(true); + return; + } + + var transferCommands = []; + var addresses = 0; + var commandAmount = 0; + var commandIndex = 0; + + for (var worker in payments) { + var amount = parseInt(payments[worker]); + if (config.payments.maxTransactionAmount && amount + commandAmount > config.payments.maxTransactionAmount) { + amount = config.payments.maxTransactionAmount - commandAmount; + } + + var address = worker; + var payment_id = null; + + var with_payment_id = false; + + var addr = address.split(config.poolServer.paymentId.addressSeparator); + if ((addr.length === 1 && utils.isIntegratedAddress(address)) || addr.length >= 2) { + with_payment_id = true; + if (addr.length >= 2) { + address = addr[0]; + payment_id = addr[1]; + payment_id = payment_id.replace(/[^A-Za-z0-9]/g, ''); + if (payment_id.length !== 16 && payment_id.length !== 64) { + with_payment_id = false; + payment_id = null; + } + } + if (addresses > 0) { + commandIndex++; + addresses = 0; + commandAmount = 0; + } + } + + if (config.poolServer.fixedDiff && config.poolServer.fixedDiff.enabled) { + var addr = address.split(config.poolServer.fixedDiff.addressSeparator); + if (addr.length >= 2) address = addr[0]; + } + + if (!transferCommands[commandIndex]) { + transferCommands[commandIndex] = { + redis: [], + amount: 0, + rpc: { + destinations: [], + fee: config.payments.transferFee, + mixin: config.payments.mixin, + priority: config.payments.priority, + unlock_time: 0 + } + }; + } + + transferCommands[commandIndex].rpc.destinations.push({ + amount: amount, + address: address + }); + if (payment_id) transferCommands[commandIndex].rpc.payment_id = payment_id; + + transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'balance', -amount]); + if (config.payments.dynamicTransferFee && config.payments.minerPayFee) { + transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'balance', -config.payments.transferFee]); + } + transferCommands[commandIndex].redis.push(['hincrby', config.coin + ':workers:' + worker, 'paid', amount]); + transferCommands[commandIndex].amount += amount; + + addresses++; + commandAmount += amount; + + if (config.payments.dynamicTransferFee) { + transferCommands[commandIndex].rpc.fee = config.payments.transferFee * addresses; + } + + if (addresses >= config.payments.maxAddresses || (config.payments.maxTransactionAmount && commandAmount >= config.payments.maxTransactionAmount) || with_payment_id) { + commandIndex++; + addresses = 0; + commandAmount = 0; + } + } + + var timeOffset = 0; + var notify_miners = []; + + var daemonType = config.daemonType ? config.daemonType.toLowerCase() : "default"; + + if (daemonType === "bytecoin" && transferCommands.length > 1) { // TODO: Limit to sent just 1 transaction. I don't know why it fails if I sent more than one + transferCommands = [transferCommands[0]]; + } + + async.filter(transferCommands, function (transferCmd, cback) { + var rpcCommand = "transfer"; + var rpcRequest = transferCmd.rpc; + var tx_hash = ''; + + if (daemonType === "bytecoin") { + rpcCommand = "sendTransaction"; + rpcRequest = { + transfers: transferCmd.rpc.destinations, + fee: transferCmd.rpc.fee, + anonymity: transferCmd.rpc.mixin, + unlockTime: transferCmd.rpc.unlock_time + }; + if (transferCmd.rpc.payment_id) { + rpcRequest.paymentId = transferCmd.rpc.payment_id; + } + + // Nueva versión from Bytecoin/walletd 3.2.0-beta + async.waterfall([ + async.apply(bytecoin_create_transaction, transferCmd), + bytecoin_send_transaction + ], function (err, res) { + if (err) { + log('error', logSystem, 'Error waterfall %j , %j', [err, res]); + } + }); + // Para que no se ejecute la siguiente llamada + rpcCommand = ""; + + + } // endif bytecoin + + if (rpcCommand) { + apiInterfaces.rpcWallet(rpcCommand, rpcRequest, function (error, result) { + if (error) { + log('error', logSystem, 'Error with %s RPC request to wallet daemon %j', [rpcCommand, error]); + log('error', logSystem, 'Payments failed to send to %j', transferCmd.rpc.destinations); + cback(false); + return; + } + + var now = (timeOffset++) + Date.now() / 1000 | 0; + var txHash = daemonType === "bytecoin" ? (tx_hash ? tx_hash : result.transactionHash) : result.tx_hash; + txHash = txHash.replace('<', '') + .replace('>', ''); + + transferCmd.redis.push(['zadd', config.coin + ':payments:all', now, [ + txHash, + transferCmd.amount, + transferCmd.rpc.fee, + transferCmd.rpc.mixin, + Object.keys(transferCmd.rpc.destinations) + .length + ].join(':')]); + + var notify_miners_on_success = []; + for (var i = 0; i < transferCmd.rpc.destinations.length; i++) { + var destination = transferCmd.rpc.destinations[i]; + if (transferCmd.rpc.payment_id) { + destination.address += config.poolServer.paymentId.addressSeparator + transferCmd.rpc.payment_id; + } + transferCmd.redis.push(['zadd', config.coin + ':payments:' + destination.address, now, [ + txHash, + destination.amount, + transferCmd.rpc.fee, + transferCmd.rpc.mixin + ].join(':')]); + + notify_miners_on_success.push(destination); + } + + log('info', logSystem, 'Payments sent via wallet daemon %j', [result]); + redisClient.multi(transferCmd.redis) + .exec(function (error, replies) { + if (error) { + log('error', logSystem, 'Super critical error! Payments sent yet failing to update balance in redis, double payouts likely to happen %j', [error]); + log('error', logSystem, 'Double payments likely to be sent to %j', transferCmd.rpc.destinations); + cback(false); + return; + } + + for (var m in notify_miners_on_success) { + notify_miners.push(notify_miners_on_success[m]); + } + + cback(true); + }); + }); + } // end if rpcCommand + + }, function (succeeded) { + var failedAmount = transferCommands.length - succeeded.length; + + for (var m in notify_miners) { + var notify = notify_miners[m]; + log('info', logSystem, 'Payment of %s to %s', [utils.getReadableCoins(notify.amount), notify.address]); + notifications.sendToMiner(notify.address, 'payment', { + 'ADDRESS': notify.address.substring(0, 7) + '...' + notify.address.substring(notify.address.length - 7), + 'AMOUNT': utils.getReadableCoins(notify.amount), + }); + } + log('info', logSystem, 'Payments splintered and %d successfully sent, %d failed', [succeeded.length, failedAmount]); + + callback(null); + }); + + } + + ], function (error, result) { + setTimeout(runInterval, config.payments.interval * 1000); + }); +} + +runInterval(); diff --git a/config_examples/croat.json b/config_examples/croat.json index 7078ca256..9b10f2b98 100644 --- a/config_examples/croat.json +++ b/config_examples/croat.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "croat", - "symbol": "CROAT", - "coinUnits": 100000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 60, + "coin": "croat", + "symbol": "CROAT", + "coinUnits": 100000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 60, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 2, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 2, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 66, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 66, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 40, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 40, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 600, - "maxAddresses": 100, - "mixin": 1, - "priority": 0, - "transferFee": 50000000, - "dynamicTransferFee": false, - "minerPayFee" : true, - "minPayment": 100000000000, - "maxPayment": null, - "maxTransactionAmount": 500000000000000, - "denomination": 100000000000 - }, + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 100, + "mixin": 1, + "priority": 0, + "transferFee": 50000000, + "dynamicTransferFee": false, + "minerPayFee": true, + "minPayment": 100000000000, + "maxPayment": null, + "maxTransactionAmount": 500000000000000, + "denomination": 100000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 10, - "poolFee": 0.8, - "devDonation": 0.0, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 0.8, + "devDonation": 0.0, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 46348 - }, + "daemon": { + "host": "127.0.0.1", + "port": 46348 + }, - "wallet": { - "host": "127.0.0.1", - "port": 46349 - }, + "wallet": { + "host": "127.0.0.1", + "port": 46349 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/dero.json b/config_examples/dero.json index 69c7848e5..5f5564036 100644 --- a/config_examples/dero.json +++ b/config_examples/dero.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "dero", - "symbol": "DERO", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 12, + "coin": "dero", + "symbol": "DERO", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 12, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 659160, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 659160, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 50000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 1000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 50000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 10, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 18091 - }, + "daemon": { + "host": "127.0.0.1", + "port": 18091 + }, - "wallet": { - "host": "127.0.0.1", - "port": 18092 - }, + "wallet": { + "host": "127.0.0.1", + "port": 18092 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/digitalnote.json b/config_examples/digitalnote.json index 55a57f7a6..827fa331e 100644 --- a/config_examples/digitalnote.json +++ b/config_examples/digitalnote.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "digitalnote", - "symbol": "XDN", - "coinUnits": 100000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 240, + "coin": "digitalnote", + "symbol": "XDN", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 240, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 50000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 1000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 50000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 10, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 42081 - }, + "daemon": { + "host": "127.0.0.1", + "port": 42081 + }, - "wallet": { - "host": "127.0.0.1", - "port": 48082 - }, + "wallet": { + "host": "127.0.0.1", + "port": 48082 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/electronero.json b/config_examples/electronero.json index ca6db3bc8..7dc2e8ca0 100644 --- a/config_examples/electronero.json +++ b/config_examples/electronero.json @@ -1,313 +1,317 @@ { - "poolHost": "POOL_URL", + "poolHost": "POOL_URL", - "coin": "electronero", - "symbol": "ETNX", - "coinUnits": 100, - "coinDecimalPlaces": 8, - "coinDifficultyTarget": 60, + "coin": "electronero", + "symbol": "ETNX", + "coinUnits": 100, + "coinDecimalPlaces": 2, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 4, + "cnBlobType": 3, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 4, - "cnBlobType": 3, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "POOL_ADDRESS", - "intAddressPrefix": 18019, - "subAddressPrefix": 42, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "cert.pem", - "sslKey": "privkey.pem", - "sslCA": "fullchain.pem", - "ports": [ - { - "port": 1122, - "difficulty": 10000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "POOL_ADDRESS", + "intAddressPrefix": 18019, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "fullchain.pem", + "ports": [ + { + "port": 1122, + "difficulty": 10000, + "desc": "Low end hardware" }, - { - "port": 2233, - "difficulty": 100000, - "desc": "Mid range hardware" + { + "port": 2233, + "difficulty": 100000, + "desc": "Mid range hardware" }, - { - "port": 3344, - "difficulty": 500000, - "desc": "High end hardware" + { + "port": 3344, + "difficulty": 500000, + "desc": "High end hardware" }, - { - "port": 4455, - "difficulty": 1000000, - "desc": "Cloud-mining / NiceHash" + { + "port": 4455, + "difficulty": 1000000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 6666, - "difficulty": 3000, - "desc": "Hidden port", - "hidden": true + { + "port": 6666, + "difficulty": 3000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 6677, - "difficulty": 1000000, - "desc": "SSL connection", - "ssl": true + { + "port": 6677, + "difficulty": 1000000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 1000000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 1000000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 2160, - "maxAddresses": 10, - "mixin": 2, - "priority": 0, - "transferFee": 20000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 100000, - "maxPayment": 25000000, - "maxTransactionAmount": 50000000, - "denomination": 100 - }, + "payments": { + "enabled": true, + "interval": 2160, + "maxAddresses": 10, + "mixin": 2, + "priority": 0, + "transferFee": 20000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 100000, + "maxPayment": 25000000, + "maxTransactionAmount": 50000000, + "denomination": 100 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 30, - "poolFee": 0.0, - "devDonation": 0.0, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 30, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "admin_pass", - "ssl": true, - "sslPort": 8119, - "sslCert": "cert.pem", - "sslKey": "privkey.pem", - "sslCA": "fullchain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "admin_pass", + "ssl": true, + "sslPort": 8119, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "fullchain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 3000 - }, + "daemon": { + "host": "127.0.0.1", + "port": 3000 + }, - "wallet": { - "host": "127.0.0.1", - "port": 3000 - }, + "wallet": { + "host": "127.0.0.1", + "port": 3000 + }, - "redis": { - "host": "127.0.0.1", - "port": 3279, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 3279, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": true, - "botName": "POOL_BOT", - "token": "POOL_BOT_TOKEN", - "channel": "POOL_BOT_CHANNEL", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": true, + "botName": "POOL_BOT", + "token": "POOL_BOT_TOKEN", + "channel": "POOL_BOT_CHANNEL", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/electroneum.json b/config_examples/electroneum.json index a3172e175..a3ac4d944 100644 --- a/config_examples/electroneum.json +++ b/config_examples/electroneum.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "electroneum", - "symbol": "ETN", - "coinUnits": 100, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 60, + "coin": "electroneum", + "symbol": "ETN", + "coinUnits": 100, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 60, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 18019, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 18019, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 10, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 10000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 10, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 10000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 18, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 18, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 26968 - }, + "daemon": { + "host": "127.0.0.1", + "port": 26968 + }, - "wallet": { - "host": "127.0.0.1", - "port": 26969 - }, + "wallet": { + "host": "127.0.0.1", + "port": 26969 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/equilibria.json b/config_examples/equilibria.json new file mode 100644 index 000000000..c2502536c --- /dev/null +++ b/config_examples/equilibria.json @@ -0,0 +1,328 @@ +{ + "poolHost": "your.pool.host", + + "coin": "equilibria", + "symbol": "XEQ", + "coinUnits": 10000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "https://explorer.equilibria.network/block/{id}", + "transactionExplorer": "https://explorer.equilibria.network/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight_gpu", + "cnVariant": 11, + "cnBlobType": 5, + "isRandomX": false, + "includeHeight": false, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": "25247", + "subAddressPrefix": "22944", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": true, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 3600, + "maxAddresses": 50, + "mixin": 15, + "priority": 3, + "transferFee": 25, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 5000, + "maxPayment": 300000, + "maxTransactionAmount": null, + "denomination": 100 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 18, + "poolFee": 0, + "soloFee": 0.5, + "devDonation": 0, + "networkFee": 0, + "useFirstVout": true + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 9231 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 18081 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/fandomgold.json b/config_examples/fandomgold.json new file mode 100644 index 000000000..e7e9ffbc6 --- /dev/null +++ b/config_examples/fandomgold.json @@ -0,0 +1,317 @@ +{ + "poolHost": "your.pool.host", + + "coin": "fango", + "symbol": "XFG", + "coinUnits": 10000000, + "coinDecimalPlaces": 7, + "coinDifficultyTarget": 480, + "blockchainExplorer": "http://explorer.fandom.gold/block/{id}", + "transactionExplorer": "http://explorer.fandom.gold/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 8, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet fango- address **", + "intAddressPrefix": 6, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 2, + "priority": 0, + "transferFee": 800000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000, + "maxTransactionAmount": 8000000000000, + "denomination": 1000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 8070 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8071 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "get_status" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "get_balance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } +} diff --git a/config_examples/festival.json b/config_examples/festival.json index b90ee60d6..f78c61fbd 100644 --- a/config_examples/festival.json +++ b/config_examples/festival.json @@ -1,312 +1,316 @@ { - "poolHost": "your.poolhost.tld", + "poolHost": "your.poolhost.tld", - "coin": "festival", - "symbol": "FEST", - "coinUnits": 100000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "festival", + "symbol": "FEST", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 2, - "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 2, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "**Your Pool Wallet Address**", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "**Your Pool Wallet Address**", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 120, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "." - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "+" - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 120, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "+" + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 60, - "maxAddresses": 50, - "mixin": 0, - "priority": 0, - "transferFee": 1000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 10000000, - "maxPayment": 10000000000, - "maxTransactionAmount": 10000000000, - "denomination": 100000000 - }, + "payments": { + "enabled": true, + "interval": 60, + "maxAddresses": 50, + "mixin": 0, + "priority": 0, + "transferFee": 1000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 10000000, + "maxPayment": 10000000000, + "maxTransactionAmount": 10000000000, + "denomination": 100000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 10, - "poolFee": 1.0, - "devDonation": 0, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 1.0, + "devDonation": 0, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 900, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "CHANGE ME", - "ssl": true, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 900, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "CHANGE ME", + "ssl": true, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 8349 - }, + "daemon": { + "host": "127.0.0.1", + "port": 8349 + }, - "wallet": { - "host": "127.0.0.1", - "port": 8350 - }, + "wallet": { + "host": "127.0.0.1", + "port": 8350 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } -} + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/graft.json b/config_examples/graft.json index 8776e5cfe..78c1f1bf2 100644 --- a/config_examples/graft.json +++ b/config_examples/graft.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "graft", - "symbol": "GRFT", - "coinUnits": 10000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "graft", + "symbol": "GRFT", + "coinUnits": 10000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 15, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 14, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 91, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 91, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 4000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 100000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 1000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 4000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 100000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 18981 - }, + "daemon": { + "host": "127.0.0.1", + "port": 18981 + }, - "wallet": { - "host": "127.0.0.1", - "port": 18982 - }, + "wallet": { + "host": "127.0.0.1", + "port": 18982 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/haven.json b/config_examples/haven.json index d0161d7d5..be997c033 100644 --- a/config_examples/haven.json +++ b/config_examples/haven.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "haven", - "symbol": "XHV", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "haven", + "symbol": "XHV", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "https://explorer.havenprotocol.org/block/{id}", + "transactionExplorer": "https://explorer.havenprotocol.org/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight_heavy", + "cnVariant": null, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight_heavy", - "cnVariant": null, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 841588, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 841588, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 5000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 5000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 17750 - }, + "daemon": { + "host": "127.0.0.1", + "port": 17750 + }, - "wallet": { - "host": "127.0.0.1", - "port": 17752 - }, + "wallet": { + "host": "127.0.0.1", + "port": 17752 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/intensecoin.json b/config_examples/intensecoin.json index b0bfe51f9..f85c27380 100644 --- a/config_examples/intensecoin.json +++ b/config_examples/intensecoin.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "intensecoin", - "symbol": "ITNS", - "coinUnits": 100000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "intensecoin", + "symbol": "ITNS", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 129, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 129, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 4000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 500000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 10000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 4000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 500000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 10000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 48782 - }, + "daemon": { + "host": "127.0.0.1", + "port": 48782 + }, - "wallet": { - "host": "127.0.0.1", - "port": 48783 - }, + "wallet": { + "host": "127.0.0.1", + "port": 48783 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/intucoin.json b/config_examples/intucoin.json index b702ccb10..52b02990c 100644 --- a/config_examples/intucoin.json +++ b/config_examples/intucoin.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "intuCoin", - "symbol": "INTU", - "coinUnits": 100, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "intuCoin", + "symbol": "INTU", + "coinUnits": 100, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "forknote", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 2, - "daemonType": "forknote", - "cnAlgorithm": "cryptonight_light", - "cnVariant": 1, - "cnBlobType": 2, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 3, - "priority": 0, - "transferFee": 10, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 1 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 10, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 10, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0, - "fixBlockHeightRPC": true - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0, + "fixBlockHeightRPC": true + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 31570 - }, + "daemon": { + "host": "127.0.0.1", + "port": 31570 + }, - "wallet": { - "host": "127.0.0.1", - "port": 31571 - }, + "wallet": { + "host": "127.0.0.1", + "port": 31571 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/iridium.json b/config_examples/iridium.json index f956c56d1..57a05ac84 100644 --- a/config_examples/iridium.json +++ b/config_examples/iridium.json @@ -1,318 +1,323 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "iridium", - "symbol": "IRD", - "coinUnits": 100000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 175, + "coin": "Iridium", + "symbol": "IRD", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 175, + "blockchainExplorer": "http://explorer.ird.cash/?hash={id}#block", + "transactionExplorer": "http://explorer.ird.cash/?hash={id}#transaction", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_pico", + "cnVariant": 2, + "cnBlobType": 2, - "daemonType": "default", - "cnAlgorithm": "cryptonight_light", - "cnVariant": 1, - "cnBlobType": 2, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 5000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 100000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 10000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 5000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 100000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 10000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 20, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 20, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 13007 - }, + "daemon": { + "host": "127.0.0.1", + "port": 13007 + }, - "wallet": { - "host": "127.0.0.1", - "port": 13008 - }, + "wallet": { + "host": "127.0.0.1", + "port": 13008 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/italocoin.json b/config_examples/italocoin.json index 9c933668d..18c7d9f6f 100644 --- a/config_examples/italocoin.json +++ b/config_examples/italocoin.json @@ -1,318 +1,328 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "italoCoin", - "symbol": "ITA", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "italoCoin", + "symbol": "XTA", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "https://explorer.italo.network/block/{id}", + "transactionExplorer": "https://explorer.italo.network/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 13, + "cnBlobType": 0, + "includeHeight": true, + "includeAlgo": "cn/r", + "isRandomX": false, - "daemonType": "default", - "cnAlgorithm": "cryptonight_heavy", - "cnVariant": null, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 251, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": "251", + "subAddressPrefix": "252", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } + ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": true, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 20000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 3, + "transferFee": 2500000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 60, + "depth": 50, + "poolFee": 0, + "devDonation": 0, + "networkFee": 30 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, - "daemon": { - "host": "127.0.0.1", - "port": 13102 - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "wallet": { - "host": "127.0.0.1", - "port": 13103 - }, + "daemon": { + "host": "127.0.0.1", + "port": 13102 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "wallet": { + "host": "127.0.0.1", + "port": 13100 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/kepl.json b/config_examples/kepl.json new file mode 100644 index 000000000..1f9079355 --- /dev/null +++ b/config_examples/kepl.json @@ -0,0 +1,319 @@ +{ + "poolHost": "your.poolhost.tld", + + "coin": "kepl", + "symbol": "KEPL", + "coinUnits": 100000000, + "coinDecimalPlaces": 2, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 4, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "**Your Pool Wallet Address**", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + } + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 500000, + "targetTime": 10, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "+" + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 10000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 500000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 100, + "devDonation": 0.0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "host": "api", + "password": "CHANGE ME", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 8581 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8070, + "password": "**YOUR WALLET RPC PASSWORD - REQUIRES PR#305 **" + }, + + "redis": { + "host": "redis", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/krypton.json b/config_examples/krypton.json new file mode 100644 index 000000000..c78a4d281 --- /dev/null +++ b/config_examples/krypton.json @@ -0,0 +1,311 @@ +{ + "poolHost": "https://yourpool.example.com", + "coin": "krypton", + "symbol": "ZOD", + "coinUnits": 1000000, + "coinDecimalPlaces": 6, + "coinDifficultyTarget": 60, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 2, + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": 6, + "poolAddress": "your_KRYPTON_ADDRESS_to_receive_block_rewards", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 60, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "separators": [ + { + "value": "+", + "desc": "plus" + }, + { + "value": ".", + "desc": "dot" + } + ], + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 0, + "priority": 0, + "transferFee": 100, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000, + "maxTransactionAmount": 500000000, + "denomination": 100 + }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 20, + "poolFee": 1, + "devDonation": 0, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "set_custom_password_here", + "ssl": false, + "sslPort": 3109, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + "daemon": { + "host": "127.0.0.1", + "port": 12888 + }, + "wallet": { + "host": "127.0.0.1", + "port": 8070 + }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + "email": { + "enabled": false, + "fromAddress": "no-reply@example.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + "telegram": { + "enabled": false, + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/lethean.json b/config_examples/lethean.json new file mode 100644 index 000000000..8ef603c32 --- /dev/null +++ b/config_examples/lethean.json @@ -0,0 +1,327 @@ +{ + "poolHost": "your.pool.host", + + "coin": "Lethean", + "symbol": "LTHN", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "https://explorer.lethean.io/search?value={id}", + "transactionExplorer": "https://explorer.lethean.io/search?value={id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 13, + "cnBlobType": 0, + "includeHeight": true, + "includeAlgo": "cn/r", + "isRandomX": false, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": "129", + "subAddressPrefix": "", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": true, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 4, + "priority": 0, + "transferFee": 500000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 100000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 10000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 40, + "poolFee": 0, + "devDonation": 0, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 48782 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 48781 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/loki.json b/config_examples/loki.json index 1ff7267e4..20123a847 100644 --- a/config_examples/loki.json +++ b/config_examples/loki.json @@ -1,319 +1,324 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "loki", - "symbol": "LOKI", - "coinUnits": 1000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "loki", + "symbol": "LOKI", + "coinUnits": 1000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "randomx", + "isRandomX": true, + "cnVariant": 18, + "cnBlobType": 5, - "daemonType": "default", - "cnAlgorithm": "cryptonight_heavy", - "cnVariant": null, - "cnBlobType": 5, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 115, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 115, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 10, - "priority": 0, - "transferFee": 50000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 5000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 15, + "mixin": 10, + "priority": 0, + "transferFee": 50000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 5000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0, - "useFirstVout": true - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "useFirstVout": true + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 22023 - }, + "daemon": { + "host": "127.0.0.1", + "port": 22023 + }, - "wallet": { - "host": "127.0.0.1", - "port": 22024 - }, + "wallet": { + "host": "127.0.0.1", + "port": 22024 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/luka.json b/config_examples/luka.json index a639e43ca..469581710 100644 --- a/config_examples/luka.json +++ b/config_examples/luka.json @@ -1,318 +1,323 @@ { - "poolHost": "your ip", + "poolHost": "your ip", - "coin": "Luka", - "symbol": "LUK", - "coinUnits": 100000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 60, + "coin": "Luka", + "symbol": "LUK", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 60, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 2, - "daemonType": "bytecoin", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 2, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "**your pool wallet**", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "**your pool wallet**", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 1, - "priority": 0, - "transferFee": 100000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 25000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 1, + "priority": 0, + "transferFee": 100000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 25000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 10, - "poolFee": 0.0, - "devDonation": 0.0, - "networkFee": 0.0, - "fixBlockHeightRPC": true - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 0.0, + "fixBlockHeightRPC": true + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 52421 - }, + "daemon": { + "host": "127.0.0.1", + "port": 52421 + }, - "wallet": { - "host": "127.0.0.1", - "port": 52424 - }, + "wallet": { + "host": "127.0.0.1", + "port": 52424 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 5, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 5, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/mangocoin.json b/config_examples/mangocoin.json new file mode 100644 index 000000000..af6ad785f --- /dev/null +++ b/config_examples/mangocoin.json @@ -0,0 +1,320 @@ +{ + "poolHost": "your.pool.host", + "coin": "mangocoin", + "symbol": "MNG", + "coinUnits": 100000000, + "coinDecimalPlaces": 8, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://explorer.mangocoin.online/block/{id}", + "transactionExplorer": "http://explorer.mangocoin.online/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_pico", + "cnVariant": 2, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": 6, + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 30, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 200000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000, + "maxTransactionAmount": 100000000, + "denomination": 100000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 120, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "192.168.0.116", + "port": 3107, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": true, + "sslPort": 3109, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 11898 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8070 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/masari.json b/config_examples/masari.json index 67320aa4c..5ef137b8a 100644 --- a/config_examples/masari.json +++ b/config_examples/masari.json @@ -1,318 +1,325 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "masari", - "symbol": "MSR", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "masari", + "symbol": "MSR", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 60, + "blockchainExplorer": "http://msrchain.net/block/{id}", + "transactionExplorer": "http://msrchain.net/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 9, + "cnBlobType": 6, + "isRandomX": false, + "includeHeight": false, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 4, - "cnBlobType": 3, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": "29", + "subAddressPrefix": "52", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 120, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "." - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "+" - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": true, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 5000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 5000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 500000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 50, + "poolFee": 0, + "devDonation": 0, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 900, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 38081 - }, + "daemon": { + "host": "127.0.0.1", + "port": 38081 + }, - "wallet": { - "host": "127.0.0.1", - "port": 38083 - }, + "wallet": { + "host": "127.0.0.1", + "port": 38079 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "altex", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/masari/masari.json b/config_examples/masari/masari.json new file mode 100644 index 000000000..b1b626abb --- /dev/null +++ b/config_examples/masari/masari.json @@ -0,0 +1,322 @@ +{ + "poolHost": "your.pool.host", + + "coin": "masari", + "symbol": "MSR", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 60, + + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 9, + "cnBlobType": 6, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 120, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "+" + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 5000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 900, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 38081 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 38083 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/masari/notes.txt b/config_examples/masari/notes.txt new file mode 100644 index 000000000..25de4d954 --- /dev/null +++ b/config_examples/masari/notes.txt @@ -0,0 +1 @@ +after "npm update" in the node_modules renaming the cryptonote-util folder to "cryptoforknote-util". \ No newline at end of file diff --git a/config_examples/masari/package.json b/config_examples/masari/package.json new file mode 100644 index 000000000..d985acafe --- /dev/null +++ b/config_examples/masari/package.json @@ -0,0 +1,32 @@ +{ + "name": "cryptonote-nodejs-pool", + "version": "1.4.0", + "license": "GPL-2.0", + "Original author": "Daniel Vandal", + "Maintained by": "Musclesonvacation", + "repository": { + "type": "git", + "url": "https://github.com/muscleman/cryptonote-nodejs-pool.git" + }, + "dependencies": { + "async": "1", + "base58-native": "*", + "bignum": "*", + "cli-color": "*", + "cryptonote-util": "git://github.com/masari-project/node-cryptonote-util", + "cryptonight-hashing": "git://github.com/MoneroOcean/node-cryptonight-hashing.git", + "dateformat": "*", + "mailgun.js": "*", + "node-telegram-bot-api": "*", + "nodemailer": "2.7.2", + "nodemailer-sendmail-transport": "*", + "redis": "*", + "socket.io": "^2.1.1", + "time-ago": "*", + "request": "^2.88.0", + "request-promise-native": "^1.0.5" + }, + "engines": { + "node": ">=8.11.3" + } +} diff --git a/config_examples/monero.json b/config_examples/monero.json index 46b7fb116..f2bad7d90 100644 --- a/config_examples/monero.json +++ b/config_examples/monero.json @@ -1,321 +1,325 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "monero", - "symbol": "XMR", - "coinUnits": 1000000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "monero", + "symbol": "XMR", + "coinUnits": 1000000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "randomx", + "cnVariant": 0, + "cnBlobType": 0, + "includeHeight": false, + "isRandomX": true, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 13, - "cnBlobType": 0, - "includeHeight": true, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 19, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 19, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 120, + "retargetTime": 60, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 14000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000000 - }, + "payments": { + "enabled": true, + "interval": 180, + "maxAddresses": 50, + "mixin": 7, + "priority": 0, + "transferFee": 140000, + "dynamicTransferFee": true, + "minerPayFee" : true, + "minPayment": 10000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 18081 - }, + "daemon": { + "host": "127.0.0.1", + "port": 18081 + }, - "wallet": { - "host": "127.0.0.1", - "port": 18082, - "user": "", - "pass": "" - }, + "wallet": { + "host": "127.0.0.1", + "port": 18082 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/myztic.json b/config_examples/myztic.json index 94205b908..a78605706 100644 --- a/config_examples/myztic.json +++ b/config_examples/myztic.json @@ -1,317 +1,322 @@ { - "poolHost": "your.pool.host", - "coin": "myztic", - "symbol": "MZT", - "coinUnits": 1000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "poolHost": "your.pool.host", + "coin": "myztic", + "symbol": "MZT", + "coinUnits": 1000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 341434745, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 4444, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 341434745, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 4444, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "Mid range hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "Mid range hardware" }, - { - "port": 6666, - "difficulty": 100000, - "desc": "High end hardware" + { + "port": 6666, + "difficulty": 100000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 2000, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 30, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 2000, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 30, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 900, - "maxAddresses": 5, - "mixin": 4, - "priority": 0, - "transferFee": 1, - "dynamicTransferFee": false, - "minerPayFee" : true, - "minPayment": 5000000000, - "maxPayment": null, - "maxTransactionAmount": 200000000000, - "denomination": 10000000 - }, + "payments": { + "enabled": true, + "interval": 900, + "maxAddresses": 5, + "mixin": 4, + "priority": 0, + "transferFee": 1, + "dynamicTransferFee": false, + "minerPayFee": true, + "minPayment": 5000000000, + "maxPayment": null, + "maxTransactionAmount": 200000000000, + "denomination": 10000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 1, - "devDonation": 0.0, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 1, + "devDonation": 0.0, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 18191 - }, + "daemon": { + "host": "127.0.0.1", + "port": 18191 + }, - "wallet": { - "host": "127.0.0.1", - "port": 26969 - }, + "wallet": { + "host": "127.0.0.1", + "port": 26969 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, + "prices": { + "source": "cryptonator", + "currency": "USD" + }, - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/newton.json b/config_examples/newton.json index 7ec252a8f..3ff417f5c 100644 --- a/config_examples/newton.json +++ b/config_examples/newton.json @@ -1,190 +1,195 @@ { - "coin": "Newton", - "symbol": "NCP", - "coinUnits": 100000000, - "coinDifficultyTarget": 120, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **" uhXd9FrWpYidBFMuaHBkECaRnjRsKf5g7Ukr2mcCD6", - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "coin": "Newton", + "symbol": "NCP", + "coinUnits": 100000000, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 200000, - "targetTime": 100, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - } - }, - "payments": { - "enabled": true, - "interval": 600, - "maxAddresses": 50, - "mixin": 1, - "transferFee": 1000000, - "minPayment": 100000000000, - "maxTransactionAmount": 50000000000000, - "denomination": 10000000000 - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 200000, + "targetTime": 100, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": ".", + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + } + }, + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 50, + "mixin": 1, + "transferFee": 1000000, + "minPayment": 100000000000, + "maxTransactionAmount": 50000000000000, + "denomination": 10000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 2, - "devDonation": 0.0, - "coreDevDonation": 0.0, - "extraFeaturesDevDonation":0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 2, + "devDonation": 0.0, + "coreDevDonation": 0.0, + "extraFeaturesDevDonation": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password" - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password" + }, - "daemon": { - "host": "127.0.0.1", - "port": 21236 - }, + "daemon": { + "host": "127.0.0.1", + "port": 21236 + }, - "wallet": { - "host": "127.0.0.1", - "port": 22236 - }, + "wallet": { + "host": "127.0.0.1", + "port": 22236 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "db": 0 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "db": 0 + }, - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - } - } -} + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } + } diff --git a/config_examples/qwertycoin.json b/config_examples/qwertycoin.json index 2a4fed9af..308300198 100644 --- a/config_examples/qwertycoin.json +++ b/config_examples/qwertycoin.json @@ -1,312 +1,317 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "Qwertycoin", - "symbol": "QWC", - "coinUnits": 100000000, - "coinDecimalPlaces": 8, - "coinDifficultyTarget": 120, + "coin": "Qwertycoin", + "symbol": "QWC", + "coinUnits": 100000000, + "coinDecimalPlaces": 8, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 0, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 1344012, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 1344012, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 600, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 500000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 10000000000, - "maxTransactionAmount": 0, - "denomination": 100000000 - }, + "payments": { + "enabled": true, + "interval": 600, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 200000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 8197 - }, + "daemon": { + "host": "127.0.0.1", + "port": 8197 + }, - "wallet": { - "host": "127.0.0.1", - "port": 8198 - }, + "wallet": { + "host": "127.0.0.1", + "port": 8198 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "enable": "/enable", - "disable": "/disable" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, - "prices": { - "source": "crex24", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "crex24", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } } diff --git a/config_examples/safex/config.json b/config_examples/safex/config.json new file mode 100644 index 000000000..958f26f52 --- /dev/null +++ b/config_examples/safex/config.json @@ -0,0 +1,325 @@ +{ + "poolHost": "your.pool.host", + + "coin": "safex", + "symbol": "SAFEX", + "coinUnits": 10000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + + "daemonType": "default", + "cnAlgorithm": "randomx", + "cnVariant": 0, + "cnBlobType": 23, + "includeHeight": false, + "isRandomX": true, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 251, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 20000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 20000000000, + "maxPayment": 5000000000000, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 17402 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 17404 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/safex/package.json b/config_examples/safex/package.json new file mode 100644 index 000000000..b70c36df0 --- /dev/null +++ b/config_examples/safex/package.json @@ -0,0 +1,30 @@ +{ + "name": "cryptonote-nodejs-pool", + "version": "1.4.0", + "license": "GPL-2.0", + "Original author": "Daniel Vandal", + "Maintained by": "Musclesonvacation", + "repository": { + "type": "git", + "url": "https://github.com/muscleman/cryptonote-nodejs-pool.git" + }, + "dependencies": { + "async": "1", + "base58-native": "*", + "bignum": "*", + "cli-color": "*", + "cryptoforknote-util": "git://github.com/VanGrx/node-cryptoforknote-util.git", + "cryptonight-hashing": "git://github.com/VanGrx/node-cryptonight-hashing.git", + "dateformat": "*", + "mailgun.js": "*", + "node-telegram-bot-api": "*", + "nodemailer": "2.7.2", + "nodemailer-sendmail-transport": "*", + "redis": "*", + "socket.io": "^2.1.1", + "time-ago": "*" + }, + "engines": { + "node": ">=8.11.3" + } +} diff --git a/config_examples/stellite.json b/config_examples/stellite.json index 71c0c372f..97c0bef9d 100644 --- a/config_examples/stellite.json +++ b/config_examples/stellite.json @@ -1,318 +1,324 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "stellite", - "symbol": "XTL", - "coinUnits": 10000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 60, + "coin": "stellite", + "symbol": "XTC", + "coinUnits": 100, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 300, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 9, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 28822, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 28822, + "subAddressPrefix": 66, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 6, - "priority": 0, - "transferFee": 100, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 100000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 10000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 15, + "mixin": 1, + "priority": 0, + "transferFee": 15, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 18, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 20189 - }, + "daemon": { + "host": "127.0.0.1", + "port": 20189 + }, - "wallet": { - "host": "127.0.0.1", - "port": 20190 - }, + "wallet": { + "host": "127.0.0.1", + "port": 20190 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/sumokoin.json b/config_examples/sumokoin.json index 995df83c5..01a9126b8 100644 --- a/config_examples/sumokoin.json +++ b/config_examples/sumokoin.json @@ -1,318 +1,325 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "sumokoin", - "symbol": "SUMO", - "coinUnits": 1000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 240, + "coin": "sumokoin", + "symbol": "SUMO", + "coinUnits": 1000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 240, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 13, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight_heavy", - "cnVariant": null, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 2700186, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "includeHeight": true, + "includeAlgo": "cn/r", + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 536986, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 12, - "priority": 0, - "transferFee": 1000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 10000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 10000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 12, + "priority": 0, + "transferFee": 1000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 10000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 10000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 19734 - }, + "daemon": { + "host": "127.0.0.1", + "port": 19734 + }, - "wallet": { - "host": "127.0.0.1", - "port": 19735 - }, + "wallet": { + "host": "127.0.0.1", + "port": 19735 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/superior.json b/config_examples/superior.json index c77720e9b..f9fe64aec 100644 --- a/config_examples/superior.json +++ b/config_examples/superior.json @@ -1,317 +1,322 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "superior", - "symbol": "SUP", - "coinUnits": 100000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "superior", + "symbol": "SUP", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, - "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 50000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 50000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 16035 - }, + "daemon": { + "host": "127.0.0.1", + "port": 16035 + }, - "wallet": { - "host": "127.0.0.1", - "port": 16036 - }, + "wallet": { + "host": "127.0.0.1", + "port": 16036 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/talleo.json b/config_examples/talleo.json new file mode 100644 index 000000000..606bb1c04 --- /dev/null +++ b/config_examples/talleo.json @@ -0,0 +1,332 @@ +{ + "poolHost": "your.pool.host", + + "coin": "talleo", + "symbol": "TLO", + "coinUnits": 100, + "coinDecimalPlaces": 2, + "coinDifficultyTarget": 60, + "blockchainExplorer": "https://pool.talleo.org/?hash={id}#blockchain_block", + "transactionExplorer": "https://pool.talleo.org/?hash={id}#blockchain_transaction", + "daemonType": "bytecoin", + "previousOffset": null, + "cnAlgorithm": "cryptonight-turtle", + "isRandomX": false, + "cnVariant": 2, + "cnBlobType": 2, + "includeHeight": false, + "includeAlgo": "cn/ultra", + + "hashingUtil": true, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 10000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 100000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 1000000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 10000000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 1000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 100000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 30, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 50 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 300, + "maxAddresses": 50, + "mixin": 1, + "priority": 0, + "transferFee": 1, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 50, + "maxPayment": null, + "maxTransactionAmount": 200000, + "denomination": 1 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 0.0, + "devDonation": 0.0, + "networkFee": 0.0, + "fixBlockHeightRPC": false, + "useFirstVout": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 20, + "bindIp": "0.0.0.0", + "port": 8126, + "blocks": 30, + "payments": 30, + "password": "Password", + "ssl": true, + "sslPort": 8127, + "sslCert": "cert.pem", + "sslKey": "privkey.pem", + "sslCA": "chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 33888, + "alwaysPoll": true + }, + + "wallet": { + "host": "127.0.0.1", + "port": 33777 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 10, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/turtlecoin.json b/config_examples/turtlecoin.json new file mode 100644 index 000000000..7b3826d21 --- /dev/null +++ b/config_examples/turtlecoin.json @@ -0,0 +1,320 @@ +{ + "poolHost": "your.pool.host", + "coin": "turtlecoin", + "symbol": "TRTL", + "coinUnits": 100, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 30, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_pico", + "cnVariant": 2, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": 6, + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 30, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 200, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 100000, + "maxTransactionAmount": 100000000, + "denomination": 100 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 120, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "192.168.0.116", + "port": 3107, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": true, + "sslPort": 3109, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 11898 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8070 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/ultranote.json b/config_examples/ultranote.json index f33c20c13..888ebc183 100644 --- a/config_examples/ultranote.json +++ b/config_examples/ultranote.json @@ -1,299 +1,304 @@ { - "poolHost": "delta.ultranote.org", + "poolHost": "delta.ultranote.org", - "coin": "ultranote", - "symbol": "XUN", - "coinUnits": 1000000, - "coinDecimalPlaces": 6, - "coinDifficultyTarget": 120, + "coin": "ultranote", + "symbol": "XUN", + "coinUnits": 1000000, + "coinDecimalPlaces": 6, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight_light", - "cnVariant": 1, - "cnBlobType": 0, - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "Pool Wallet Address", - "intAddressPrefix": null, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "ports": [ - { - "port": 5555, - "difficulty": 10000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** your pool wallet **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "ports": [ + { + "port": 5555, + "difficulty": 10000, + "desc": "Low end hardware" }, - { - "port": 7777, - "difficulty": 50000, - "desc": "Mid range hardware" + { + "port": 7777, + "difficulty": 50000, + "desc": "Mid range hardware" }, - { - "port": 8888, - "difficulty": 100000, - "desc": "High end hardware", - "hidden": false + { + "port": 8888, + "difficulty": 100000, + "desc": "High end hardware", + "hidden": false } ], - "varDiff": { - "minDiff": 1000, - "maxDiff": 200000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 120, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 1000, + "maxDiff": 200000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 120, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 3, - "priority": 0, - "transferFee": 100000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 5000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 1000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 100000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 5000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 10, - "poolFee": 1, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 1, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "pass", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "pass", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 31000 - }, + "daemon": { + "host": "127.0.0.1", + "port": 31000 + }, - "wallet": { - "host": "127.0.0.1", - "port": 8082 - }, + "wallet": { + "host": "127.0.0.1", + "port": 8082 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/woolong.json b/config_examples/woolong.json new file mode 100644 index 000000000..1a5c142ed --- /dev/null +++ b/config_examples/woolong.json @@ -0,0 +1,320 @@ +{ + "poolHost": "your.pool.host", + "coin": "woolong", + "symbol": "WOO", + "coinUnits": 100000000, + "coinDecimalPlaces": 8, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://explorer.woolongs.com/block/{id}", + "transactionExplorer": "http://explorer.woolongs.com/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": 6, + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 30, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 3, + "priority": 0, + "transferFee": 200000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000, + "maxTransactionAmount": 100000000, + "denomination": 100000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 120, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "192.168.0.116", + "port": 3107, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": true, + "sslPort": 3109, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 11898 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8070 + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/worktips.json b/config_examples/worktips.json new file mode 100644 index 000000000..96820af1a --- /dev/null +++ b/config_examples/worktips.json @@ -0,0 +1,318 @@ +{ + "poolHost": "your.pool.host", + + "coin": "worktips", + "symbol": "WTIP", + "coinUnits": 100000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "bytecoin", + "cnAlgorithm": "cryptonight_light", + "cnVariant": 1, + "cnBlobType": 2, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": null, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" + }, + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" + }, + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" + }, + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" + }, + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 1, + "priority": 0, + "transferFee": 50000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 100000000000, + "maxTransactionAmount": 2000000000000000, + "denomination": 100000000 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 10, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0, + "fixBlockHeightRPC": false + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, + + "daemon": { + "host": "127.0.0.1", + "port": 18238 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 8082, + "password": "rpc_password" + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getBalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + } + } +} diff --git a/config_examples/wownero.json b/config_examples/wownero.json index fa8e1a335..f245eb2ef 100644 --- a/config_examples/wownero.json +++ b/config_examples/wownero.json @@ -1,309 +1,319 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "wownero", - "symbol": "WOW", - "coinUnits": 100000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 300, + "coin": "wownero", + "symbol": "WOW", + "coinUnits": 100000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 300, + "blockchainExplorer": "http://blockexplorer.arqma.com/block/{id}", + "transactionExplorer": "http://blockexplorer.arqma.com/tx/{id}", + "daemonType": "default", + "cnAlgorithm": "randomx", + "isRandomX": true, + "cnVariant": 17, + "cnBlobType": 0, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 12, - "cnBlobType": 0, - "includeHeight": true, - "includeAlgo": "cn/wow", - - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "Your pool wallet address", - "intAddressPrefix": 19, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 4333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "Your pool wallet address", + "intAddressPrefix": 19, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 4333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 4555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 4555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 4777, - "difficulty": 500000, - "hidden": true, - "desc": "Cloud-mining / NiceHash" + { + "port": 4777, + "difficulty": 500000, + "hidden": true, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 4888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 4888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 1000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 1000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 16, - "priority": 0, - "transferFee": 14000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 1000000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 100000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 16, + "priority": 0, + "transferFee": 14000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 1000000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 100000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 2.0, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 2.0, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 34558, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 34558, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 34568 - }, + "daemon": { + "host": "127.0.0.1", + "port": 34568 + }, - "wallet": { - "host": "127.0.0.1", - "port": 39578 - }, + "wallet": { + "host": "127.0.0.1", + "port": 39578 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "tradeogre", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 10, - "stepInterval": 600, - "maximumPeriod": 3600 - }, - "miners": { - "enabled": true, - "updateInterval": 10, - "stepInterval": 600, - "maximumPeriod": 3600 - }, - "workers": { - "enabled": true, - "updateInterval": 10, - "stepInterval": 6000, - "maximumPeriod": 3600 - }, - "difficulty": { - "enabled": true, - "updateInterval": 10, - "stepInterval": 600, - "maximumPeriod": 3600 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradeogre", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 10, + "stepInterval": 600, + "maximumPeriod": 3600 + }, + "miners": { + "enabled": true, + "updateInterval": 10, + "stepInterval": 600, + "maximumPeriod": 3600 + }, + "workers": { + "enabled": true, + "updateInterval": 10, + "stepInterval": 6000, + "maximumPeriod": 3600 + }, + "difficulty": { + "enabled": true, + "updateInterval": 10, + "stepInterval": 600, + "maximumPeriod": 3600 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } diff --git a/config_examples/x-cash/package.json b/config_examples/x-cash/package.json new file mode 100644 index 000000000..c30a12d6b --- /dev/null +++ b/config_examples/x-cash/package.json @@ -0,0 +1,32 @@ +{ + "name": "cryptonote-nodejs-pool", + "version": "1.4.0", + "license": "GPL-2.0", + "Original author": "Daniel Vandal", + "Maintained by": "Musclesonvacation", + "repository": { + "type": "git", + "url": "https://github.com/muscleman/cryptonote-nodejs-pool.git" + }, + "dependencies": { + "async": "1", + "base58-native": "*", + "bignum": "*", + "cli-color": "*", + "cryptonote-util": "git://github.com/masari-project/node-cryptonote-util", + "cryptonight-hashing": "git://github.com/X-CASH-official/node-cryptonight-hashing.git", + "dateformat": "*", + "mailgun.js": "*", + "node-telegram-bot-api": "*", + "nodemailer": "2.7.2", + "nodemailer-sendmail-transport": "*", + "redis": "*", + "socket.io": "^2.1.1", + "time-ago": "*", + "request": "^2.88.0", + "request-promise-native": "^1.0.5" + }, + "engines": { + "node": ">=8.11.3" + } +} diff --git a/config_examples/x-cash/x-cash.json b/config_examples/x-cash/x-cash.json new file mode 100644 index 000000000..c6be6b108 --- /dev/null +++ b/config_examples/x-cash/x-cash.json @@ -0,0 +1,323 @@ +{ + "poolHost": "your.pool.host", + + "coin": "xcash", + "symbol": "XCASH", + "coinUnits": 1000000, + "coinDecimalPlaces": 6, + "coinDifficultyTarget": 120, + + "cnAlgorithm": "cryptonight", + "cnVariant": 9, + "cnBlobType": 0, + + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "mergedMining": false, + "clusterForks": "auto", + "poolAddress": "YOUR_POOLS_XCASH_ADDRESS", + "intAddressPrefix": 4178228, + "blockRefreshInterval": 1000, + "minerTimeout": 600, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 10000, + "desc": "CPU Mining" + }, + { + "port": 5555, + "difficulty": 50000, + "desc": "Low end GPU mining" + }, + { + "port": 7777, + "difficulty": 100000, + "desc": "High end GPU mining" + }, + { + "port": 9000, + "difficulty": 50000, + "desc": "SSL", + "ssl": true + }, + { + "port": 9999, + "difficulty": 200000, + "desc": "Very high end GPU mining, Nicehash" + } + ], + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 30, + "retargetTime": 60, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": false, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, + + "payments": { + "enabled": true, + "interval": 3600, + "maxAddresses": 50, + "mixin": 20, + "priority": 1, + "transferFee": 5000, + "dynamicTransferFee": false, + "minerPayFee": true, + "minPayment": 10000000000, + "maxPayment": null, + "maxTransactionAmount": 10000000000000, + "denomination": 1000 + }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, + + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 10, + "bindIp": "YOUR_SERVERS_IP_ADDRESS", + "port": 8117, + "blocks": 20, + "payments": 20, + "password": "PASSWORD_FOR_THE_ADMIN_AREA", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": false + }, + + "daemon": { + "host": "127.0.0.1", + "port": 18281 + }, + + "wallet": { + "host": "127.0.0.1", + "port": YOUR_RPC_WALLET_PORT + }, + + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, + + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, + + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, + + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "enable": "/enable", + "disable": "/disable" + } + }, + + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "tradesatoshi", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } +} diff --git a/config_examples/xmv.json b/config_examples/xmv.json index cad40bfad..796190c03 100644 --- a/config_examples/xmv.json +++ b/config_examples/xmv.json @@ -1,318 +1,321 @@ { - "poolHost": "your.pool.host", + "poolHost": "your.pool.host", - "coin": "moneroV", - "symbol": "XMV", - "coinUnits": 100000000000, - "coinDecimalPlaces": 4, - "coinDifficultyTarget": 120, + "coin": "moneroV", + "symbol": "XMV", + "coinUnits": 100000000000, + "coinDecimalPlaces": 4, + "coinDifficultyTarget": 120, - "daemonType": "default", - "cnAlgorithm": "cryptonight", - "cnVariant": 1, - "cnBlobType": 0, + "daemonType": "default", + "cnAlgorithm": "cryptonight", + "cnVariant": 1, + "cnBlobType": 0, - "logging": { - "files": { - "level": "info", - "directory": "logs", - "flushInterval": 5 - }, - "console": { - "level": "info", - "colors": true - } - }, - - "poolServer": { - "enabled": true, - "clusterForks": "auto", - "poolAddress": "** Your pool wallet address **", - "intAddressPrefix": 19, - "blockRefreshInterval": 1000, - "minerTimeout": 900, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "ports": [ - { - "port": 3333, - "difficulty": 5000, - "desc": "Low end hardware" + "logging": { + "files": { + "level": "info", + "directory": "logs", + "flushInterval": 5 + }, + "console": { + "level": "info", + "colors": true + } + }, + "childPools": null, + "poolServer": { + "enabled": true, + "clusterForks": "auto", + "poolAddress": "** Your pool wallet address **", + "intAddressPrefix": 19, + "blockRefreshInterval": 1000, + "minerTimeout": 900, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "ports": [ + { + "port": 3333, + "difficulty": 5000, + "desc": "Low end hardware" }, - { - "port": 4444, - "difficulty": 15000, - "desc": "Mid range hardware" + { + "port": 4444, + "difficulty": 15000, + "desc": "Mid range hardware" }, - { - "port": 5555, - "difficulty": 25000, - "desc": "High end hardware" + { + "port": 5555, + "difficulty": 25000, + "desc": "High end hardware" }, - { - "port": 7777, - "difficulty": 500000, - "desc": "Cloud-mining / NiceHash" + { + "port": 7777, + "difficulty": 500000, + "desc": "Cloud-mining / NiceHash" }, - { - "port": 8888, - "difficulty": 25000, - "desc": "Hidden port", - "hidden": true + { + "port": 8888, + "difficulty": 25000, + "desc": "Hidden port", + "hidden": true }, - { - "port": 9999, - "difficulty": 20000, - "desc": "SSL connection", - "ssl": true + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], - "varDiff": { - "minDiff": 100, - "maxDiff": 100000000, - "targetTime": 60, - "retargetTime": 30, - "variancePercent": 30, - "maxJump": 100 - }, - "paymentId": { - "addressSeparator": "+" - }, - "fixedDiff": { - "enabled": true, - "addressSeparator": "." - }, - "shareTrust": { - "enabled": true, - "min": 10, - "stepDown": 3, - "threshold": 10, - "penalty": 30 - }, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 25, - "checkThreshold": 30 - }, - "slushMining": { - "enabled": false, - "weight": 300, - "blockTime": 60, - "lastBlockCheckRate": 1 - } - }, + "varDiff": { + "minDiff": 100, + "maxDiff": 100000000, + "targetTime": 60, + "retargetTime": 30, + "variancePercent": 30, + "maxJump": 100 + }, + "paymentId": { + "addressSeparator": ".", + "validation": true, + "validations": ["1,16", "64"], + "ban": true + }, + "fixedDiff": { + "enabled": true, + "addressSeparator": "." + }, + "shareTrust": { + "enabled": true, + "min": 10, + "stepDown": 3, + "threshold": 10, + "penalty": 30 + }, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 25, + "checkThreshold": 30 + }, + "slushMining": { + "enabled": false, + "weight": 300, + "blockTime": 60, + "lastBlockCheckRate": 1 + } + }, - "payments": { - "enabled": true, - "interval": 1800, - "maxAddresses": 50, - "mixin": 5, - "priority": 0, - "transferFee": 1000000000, - "dynamicTransferFee": true, - "minerPayFee" : true, - "minPayment": 100000000000, - "maxPayment": null, - "maxTransactionAmount": 0, - "denomination": 1000000000 - }, + "payments": { + "enabled": true, + "interval": 1800, + "maxAddresses": 50, + "mixin": 5, + "priority": 0, + "transferFee": 1000000000, + "dynamicTransferFee": true, + "minerPayFee": true, + "minPayment": 100000000000, + "maxPayment": null, + "maxTransactionAmount": 0, + "denomination": 1000000000 + }, - "blockUnlocker": { - "enabled": true, - "interval": 30, - "depth": 60, - "poolFee": 0.8, - "devDonation": 0.2, - "networkFee": 0.0 - }, + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 60, + "poolFee": 0.8, + "devDonation": 0.2, + "networkFee": 0.0 + }, - "api": { - "enabled": true, - "hashrateWindow": 600, - "updateInterval": 5, - "bindIp": "0.0.0.0", - "port": 8117, - "blocks": 30, - "payments": 30, - "password": "your_password", - "ssl": false, - "sslPort": 8119, - "sslCert": "./cert.pem", - "sslKey": "./privkey.pem", - "sslCA": "./chain.pem", - "trustProxyIP": true - }, + "api": { + "enabled": true, + "hashrateWindow": 600, + "updateInterval": 5, + "bindIp": "0.0.0.0", + "port": 8117, + "blocks": 30, + "payments": 30, + "password": "your_password", + "ssl": false, + "sslPort": 8119, + "sslCert": "./cert.pem", + "sslKey": "./privkey.pem", + "sslCA": "./chain.pem", + "trustProxyIP": true + }, - "daemon": { - "host": "127.0.0.1", - "port": 18081 - }, + "daemon": { + "host": "127.0.0.1", + "port": 19091 + }, - "wallet": { - "host": "127.0.0.1", - "port": 18082 - }, + "wallet": { + "host": "127.0.0.1", + "port": 19095 + }, - "redis": { - "host": "127.0.0.1", - "port": 6379, - "auth": null, - "db": 0, - "cleanupInterval": 15 - }, + "redis": { + "host": "127.0.0.1", + "port": 6379, + "auth": null, + "db": 0, + "cleanupInterval": 15 + }, - "notifications": { - "emailTemplate": "email_templates/default.txt", - "emailSubject": { - "emailAdded": "Your email was registered", - "workerConnected": "Worker %WORKER_NAME% connected", - "workerTimeout": "Worker %WORKER_NAME% stopped hashing", - "workerBanned": "Worker %WORKER_NAME% banned", - "blockFound": "Block %HEIGHT% found !", - "blockUnlocked": "Block %HEIGHT% unlocked !", - "blockOrphaned": "Block %HEIGHT% orphaned !", - "payment": "We sent you a payment !" - }, - "emailMessage": { - "emailAdded": "Your email has been registered to receive pool notifications.", - "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", - "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", - "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", - "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", - "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", - "blockOrphaned": "Block orphaned at height %HEIGHT% :(", - "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." - }, - "telegramMessage": { - "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", - "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", - "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", - "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", - "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", - "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", - "payment": "A payment of _%AMOUNT%_ has been sent." - } - }, + "notifications": { + "emailTemplate": "email_templates/default.txt", + "emailSubject": { + "emailAdded": "Your email was registered", + "workerConnected": "Worker %WORKER_NAME% connected", + "workerTimeout": "Worker %WORKER_NAME% stopped hashing", + "workerBanned": "Worker %WORKER_NAME% banned", + "blockFound": "Block %HEIGHT% found !", + "blockUnlocked": "Block %HEIGHT% unlocked !", + "blockOrphaned": "Block %HEIGHT% orphaned !", + "payment": "We sent you a payment !" + }, + "emailMessage": { + "emailAdded": "Your email has been registered to receive pool notifications.", + "workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.", + "workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.", + "workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.", + "blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.", + "blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.", + "blockOrphaned": "Block orphaned at height %HEIGHT% :(", + "payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet." + }, + "telegramMessage": { + "workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.", + "workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.", + "workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.", + "blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*", + "blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*", + "blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*", + "payment": "A payment of _%AMOUNT%_ has been sent." + } + }, - "email": { - "enabled": false, - "fromAddress": "your@email.com", - "transport": "sendmail", - "sendmail": { - "path": "/usr/sbin/sendmail" - }, - "smtp": { - "host": "smtp.example.com", - "port": 587, - "secure": false, - "auth": { - "user": "username", - "pass": "password" - }, - "tls": { - "rejectUnauthorized": false - } - }, - "mailgun": { - "key": "your-private-key", - "domain": "mg.yourdomain" - } - }, + "email": { + "enabled": false, + "fromAddress": "your@email.com", + "transport": "sendmail", + "sendmail": { + "path": "/usr/sbin/sendmail" + }, + "smtp": { + "host": "smtp.example.com", + "port": 587, + "secure": false, + "auth": { + "user": "username", + "pass": "password" + }, + "tls": { + "rejectUnauthorized": false + } + }, + "mailgun": { + "key": "your-private-key", + "domain": "mg.yourdomain" + } + }, - "telegram": { - "enabled": false, - "botName": "", - "token": "", - "channel": "", - "channelStats": { - "enabled": false, - "interval": 30 - }, - "botCommands": { - "stats": "/stats", - "report": "/report", - "notify": "/notify", - "blocks": "/blocks" - } - }, - - "monitoring": { - "daemon": { - "checkInterval": 60, - "rpcMethod": "getblockcount" - }, - "wallet": { - "checkInterval": 60, - "rpcMethod": "getbalance" - } - }, + "telegram": { + "enabled": false, + "botName": "", + "token": "", + "channel": "", + "channelStats": { + "enabled": false, + "interval": 30 + }, + "botCommands": { + "stats": "/stats", + "report": "/report", + "notify": "/notify", + "blocks": "/blocks" + } + }, - "prices": { - "source": "cryptonator", - "currency": "USD" - }, - - "charts": { - "pool": { - "hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "workers": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "difficulty": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "price": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - }, - "profit": { - "enabled": true, - "updateInterval": 1800, - "stepInterval": 10800, - "maximumPeriod": 604800 - } - }, - "user": { - "hashrate": { - "enabled": true, - "updateInterval": 180, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, - "worker_hashrate": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 60, - "maximumPeriod": 86400 - }, - "payments": { - "enabled": true - } - }, - "blocks": { - "enabled": true, - "days": 30 - } - } + "monitoring": { + "daemon": { + "checkInterval": 60, + "rpcMethod": "getblockcount" + }, + "wallet": { + "checkInterval": 60, + "rpcMethod": "getbalance" + } + }, + + "prices": { + "source": "cryptonator", + "currency": "USD" + }, + + "charts": { + "pool": { + "hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "miners": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "workers": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "difficulty": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "price": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + }, + "profit": { + "enabled": true, + "updateInterval": 1800, + "stepInterval": 10800, + "maximumPeriod": 604800 + } + }, + "user": { + "hashrate": { + "enabled": true, + "updateInterval": 180, + "stepInterval": 1800, + "maximumPeriod": 86400 + }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, + "payments": { + "enabled": true + } + }, + "blocks": { + "enabled": true, + "days": 30 + } + } } From 24be6bda86fe2d97a19a4ab09725c5d4cac133bd Mon Sep 17 00:00:00 2001 From: Cideg <34962099+Cideg@users.noreply.github.com> Date: Sun, 25 Oct 2020 20:06:39 +0100 Subject: [PATCH 98/98] Delete admin.html --- campurro-merged-website-tamplate/admin.html | 114 -------------------- 1 file changed, 114 deletions(-) delete mode 100644 campurro-merged-website-tamplate/admin.html diff --git a/campurro-merged-website-tamplate/admin.html b/campurro-merged-website-tamplate/admin.html deleted file mode 100644 index c2356aac0..000000000 --- a/campurro-merged-website-tamplate/admin.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - Mining Pool Admin Panel - - - - - - - - - - - - - - - - - - - - - -