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 5234880c0..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) @@ -94,14 +95,19 @@ 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) #### Pools Using This Software -* https://minercountry.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/mcnproject/mm-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", @@ -194,7 +213,9 @@ 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*/ "includeAlgo":"cn/wow", /*wownero specific change to include algo in job to miner*/ +"isRandomX": true, /* Logging */ "logging": { @@ -218,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, @@ -232,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, @@ -305,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 @@ -393,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) */ @@ -411,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 */ @@ -514,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 }, @@ -566,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 } @@ -594,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 @@ -606,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 : ``` @@ -614,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/campurro/cryptonote-nodejs-pool/blob/master/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. @@ -657,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/ + + + + + + (42) + (ZLS) Merged Mining Pool - MinerCountry + + + + + + + + + + + + + + + + + +
+ + + + + +
+
Network: N/A
+
Pool: N/A
+
You: N/A
+
Stats Updated  
+
+
+ + +
+
+

+
+ +
+ + + + + + + + + + + + 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": "开源于" +} 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
+
+
+ +

+ +

+ +
+ +
+
+ + + 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: "" + }; +})); 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 new file mode 100644 index 000000000..b5796bb6b --- /dev/null +++ b/config_examples/aeon.json @@ -0,0 +1,315 @@ +{ + "poolHost": "your.pool.host", + + "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, + + "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": 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": 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 + }, + + "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 + }, + "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/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 + } + } +} diff --git a/config_examples/bbscoin.json b/config_examples/bbscoin.json index d01f817a8..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", - "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": 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/bitcoinmono.json b/config_examples/bitcoinmono.json new file mode 100644 index 000000000..70ec273e4 --- /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": 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": 1800, + "maxAddresses": 50, + "mixin": 0, + "priority": 0, + "transferFee": 1000, + "dynamicTransferFee": false, + "minerPayFee" : true, + "minPayment": 10000, + "maxPayment": 1000000000000, + "maxTransactionAmount": 0, + "denomination": 10 + }, + + "blockUnlocker": { + "enabled": true, + "interval": 30, + "depth": 50, + "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": 11358 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 11359 + }, + + "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/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/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 + } + } +} 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/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 + } + } +} diff --git a/config_examples/electronero.json b/config_examples/electronero.json new file mode 100644 index 000000000..7dc2e8ca0 --- /dev/null +++ b/config_examples/electronero.json @@ -0,0 +1,317 @@ +{ + "poolHost": "POOL_URL", + + "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, + + "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": 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": ".", + "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 + }, + + "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 + } + } +} 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 63929bb6a..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": 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": 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 9c65a8a09..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", - "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": 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 bbc1d5f06..20123a847 100644 --- a/config_examples/loki.json +++ b/config_examples/loki.json @@ -1,318 +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": 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": 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": 5.0 - }, + "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 new file mode 100644 index 000000000..469581710 --- /dev/null +++ b/config_examples/luka.json @@ -0,0 +1,323 @@ +{ + "poolHost": "your ip", + + "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, + + "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": 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": 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 + }, + + "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 + } + } +} 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 a69a73bac..f2bad7d90 100644 --- a/config_examples/monero.json +++ b/config_examples/monero.json @@ -1,318 +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": 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": 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 - }, + "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_inf8-mcn.json b/config_examples/parsicoin.json similarity index 87% rename from config_inf8-mcn.json rename to config_examples/parsicoin.json index 1eefc45c8..1b0f876eb 100644 --- a/config_inf8-mcn.json +++ b/config_examples/parsicoin.json @@ -1,18 +1,15 @@ { - "poolHost": "infinium8.minercountry.com", + "poolHost": "pool.website.com", - "coin": "infinium8", - "symbol": "INF8", + "coin": "parsicoin", + "symbol": "PARS", "coinUnits": 1000000000000, "coinDecimalPlaces": 4, - "coinDifficultyTarget": 90, + "coinDifficultyTarget": 120, - "childCoin": "monetaverde", - - "daemonType": "default", "cnAlgorithm": "cryptonight", - "cnVariant": 0, - "cnBlobType": 0, + "cnVariant": 1, + "cnBlobType": 2, "logging": { "files": { @@ -28,18 +25,16 @@ "poolServer": { "enabled": true, - "mergedMining": true, - "clusterForks": 1, - "poolAddress": "**** YOUR INF8 WALLET ADDRESS *******", - "poolChildAddress": "**** YOUR MCN WALLET ADDRESS ********", - "intAddressPrefix": "", + "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" @@ -64,22 +59,28 @@ "difficulty": 25000, "desc": "Hidden port", "hidden": true + }, + { + "port": 9999, + "difficulty": 20000, + "desc": "SSL connection", + "ssl": true } ], "varDiff": { - "minDiff": 500, + "minDiff": 100, "maxDiff": 100000000, - "targetTime": 45, - "retargetTime": 40, + "targetTime": 120, + "retargetTime": 30, "variancePercent": 30, - "maxJump": 60 + "maxJump": 100 }, "paymentId": { - "addressSeparator": "+" + "addressSeparator": "." }, "fixedDiff": { "enabled": true, - "addressSeparator": "." + "addressSeparator": "+" }, "shareTrust": { "enabled": true, @@ -104,38 +105,38 @@ "payments": { "enabled": true, - "interval": 30, - "maxAddresses": 5, - "mixin": 3, + "interval": 120, + "maxAddresses": 50, + "mixin": 0, "priority": 0, - "transferFee": 10000000000, + "transferFee": 100000000000, "dynamicTransferFee": true, "minerPayFee" : true, - "minPayment": 10000000000000, + "minPayment": 1000000000000, "maxPayment": null, - "maxTransactionAmount": 1000000000000000, - "denomination": 10000000000 + "maxTransactionAmount": 100000000000000, + "denomination": 1000000000000 }, "blockUnlocker": { "enabled": true, "interval": 30, - "depth": 60, - "poolFee": 0.5, + "depth": 10, + "poolFee": 1.0, "devDonation": 0, "networkFee": 0.0 }, "api": { "enabled": true, - "hashrateWindow": 600, + "hashrateWindow": 900, "updateInterval": 5, "bindIp": "0.0.0.0", - "port": 8999, + "port": 8117, "blocks": 30, "payments": 30, "password": "your_password", - "ssl": false, + "ssl": true, "sslPort": 8119, "sslCert": "./cert.pem", "sslKey": "./privkey.pem", @@ -145,17 +146,12 @@ "daemon": { "host": "127.0.0.1", - "port": 27855 - }, - - "childDaemon": { - "host": "192.168.1.39", - "port": 26081 + "port": 18240 }, "wallet": { "host": "127.0.0.1", - "port": 27856 + "port": 18242 }, "redis": { @@ -163,7 +159,7 @@ "port": 6379, "auth": null, "db": 0, - "cleanupInterval": 15 + "cleanupInterval": 15 }, "notifications": { @@ -265,12 +261,6 @@ "stepInterval": 1800, "maximumPeriod": 86400 }, - "miners": { - "enabled": true, - "updateInterval": 60, - "stepInterval": 1800, - "maximumPeriod": 86400 - }, "workers": { "enabled": true, "updateInterval": 60, @@ -303,6 +293,12 @@ "stepInterval": 1800, "maximumPeriod": 86400 }, + "worker_hashrate": { + "enabled": true, + "updateInterval": 60, + "stepInterval": 60, + "maximumPeriod": 86400 + }, "payments": { "enabled": true } @@ -313,3 +309,4 @@ } } } + diff --git a/config_examples/qwertycoin.json b/config_examples/qwertycoin.json index fda67de4c..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_heavy", - "cnVariant": 0, - "cnBlobType": 4, - - "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/turtle.json b/config_examples/turtle.json new file mode 100644 index 000000000..ae61816bf --- /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.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/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 new file mode 100644 index 000000000..888ebc183 --- /dev/null +++ b/config_examples/ultranote.json @@ -0,0 +1,304 @@ +{ + "poolHost": "delta.ultranote.org", + + "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, + + "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": 8888, + "difficulty": 100000, + "desc": "High end hardware", + "hidden": false + } + ], + "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 + }, + + "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 + } + } +} 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 new file mode 100644 index 000000000..f245eb2ef --- /dev/null +++ b/config_examples/wownero.json @@ -0,0 +1,319 @@ +{ + "poolHost": "your.pool.host", + + "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, + + "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": 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": ".", + "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 + }, + + "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 + }, + "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 new file mode 100644 index 000000000..796190c03 --- /dev/null +++ b/config_examples/xmv.json @@ -0,0 +1,321 @@ +{ + "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 + } + }, + "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": 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": 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": 19091 + }, + + "wallet": { + "host": "127.0.0.1", + "port": 19095 + }, + + "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 + } + } +} diff --git a/init.js b/init.js index 2347d8312..331d07297 100644 --- a/init.js +++ b/init.js @@ -1,290 +1,408 @@ -/** - * 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 }); - -// 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/lib/api.js b/lib/api.js index 48de03f64..8f8cae5fc 100644 --- a/lib/api.js +++ b/lib/api.js @@ -6,1055 +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; - callback(null, { - difficulty: blockHeader.difficulty, - height: blockHeader.height, - timestamp: blockHeader.timestamp, - reward: blockHeader.reward, - hash: blockHeader.hash - }); - }); +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 + }); + }); +} + +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' + })); + }); + }); } /** @@ -1062,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' + })); + } + }); } /** @@ -1164,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' + })); } /** @@ -1662,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 @@ -1790,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 18f010058..7c09310e1 100644 --- a/lib/apiInterfaces.js +++ b/lib/apiInterfaces.js @@ -9,117 +9,112 @@ 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'; - 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 = JSON.parse(replyData); - } - catch(e){ - callback(e, {}); - return; - } - callback(null, replyJson); - }); - }); +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, {}); - }); + req.on('error', function (e) { + callback(e, {}); + }); - req.end(data); + req.end(data); } /** * Send RPC request **/ -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) - }); +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){ - 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); +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); - }, - 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 - } +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 f268040a3..889d4d3fb 100644 --- a/lib/blockUnlocker.js +++ b/lib/blockUnlocker.js @@ -6,259 +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; - apiInterfaces.rpcDaemon('getblockheaderbyheight', {height: blockHeight}, function(error, result){ - if (error){ - log('error', logSystem, 'Error with getblockheaderbyheight RPC request for block %s - %j', [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]); - 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; - 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 eec7eba07..65e39b237 100644 --- a/lib/configReader.js +++ b/lib/configReader.js @@ -6,49 +6,55 @@ **/ // Load required modules -var fs = require('fs'); +let fs = require('fs'); // Set pool software version -global.version = "v1.3.5"; +global.version = "v2.0.0"; /** * Load pool configuration **/ - + // Get configuration file path -var configFile = (function(){ - for (var 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! **/ - -var donationAddresses = { - BTC: '35kAuFPUS1REXQnGM2TxqMbwkKZ9hA4ZfW', - ETH: '0x1f6177295A6630858BFA25fD60effA048B307674', - LTC: 'MPoYzJjr7FhRqwgSNNYi6Ai9uQ9MeiR5Yy', - XMR: '49nCLA3KtAx5MTWidB3opHif7cwnXXHcn6occpXB8DTe6B4QjcAqG4SH7TrGPVuE4n1ygE6is5nER4ms1Yb1hnmYMkBwN4L', - MCN: 'VduTsfyVGBAA2CqdqEh4Vya7B1im7sf1PDbntv1GomLxQpXNxwucsww4ArmR6uLK7PQCY4kVnPGXr8evyXDFNmkk2aBv178S6' + +let donationAddresses = { + BTC: '18tytWtmoi3fzEdRFXfa96xHXUfBPvtMj5', + BCH: 'qpxcm3r90y6cedvazm4phwr82m3ywwn66gzwllq63l', + ETH: '0x745F2Bc9570B8C8DcD51249d7fdC2528f03efF1c', + LTC: 'LKF12Fi92zuxDhpHLe7gSWBtTdJbcULa85', + XMR: '44c7umSm7TyXxKch9q4R5QfoTAf663A8yEFfJbxmxUJ1JCWq2kFu33oAAydrgNDQA8619rSQhZaFV3ScpESWCfcQB3Fqc6w', + TRTL: 'TRTLv2tGkYa5W7UTH8q9mhhQdY8Ftbo3RNBfpupZYnCm7uHZp6jrxXV3pyPnzdftZj2rfCZsUkpJQWTbdbJ5wx84PB3rTojR7C9', + DOGE: 'DDrA5dZTjjnyYPxT23wmG5X5sxqt7XNMQe', + ARMS: 'guns91W8WhUT8mwBRByeh2erje1JcW7Vq8XP7VEL8CS2NPR7oqRK9mv2YN3Vvup9Hk98mpEQ9S9WeLMzgL52CKRY3xVd1WgTKL', + MNG: 'MEpLh1LswBqihtwVB7VuYAQP7E39SYSwVQwFVyAjpGd6fdALVvZk74YTq5jTo4DNnTdkw2wcWCzJ2EtVJ9k9DhioBWQ7GGq', + WOO: 'WwaFjmWQrNG1h1bSVcP8UjKYL1nHRHp5sGVVSX9pZmtFjfPFfBi4XoJCWewvjXU4gxGmuiNpZSBLd4sgRWnusrNS15x8facrx' }; global.donations = {}; -var percent = config.blockUnlocker.devDonation; -var 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 2f5688a27..c82a34255 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -6,1241 +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); - 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); - 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; +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 (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); - - if (loop) - setTimeout(function(){ - jobRefresh(true); - }, config.poolServer.blockRefreshInterval); - - //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); - } - 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 = new Buffer(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 = new Buffer(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 = new Buffer(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 = new Buffer(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(new Buffer(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, blockTemplate, 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 2d8a37def..73f9999d7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -6,141 +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(new Buffer(config.poolServer.poolAddress)).toString()); -var integratedAddressBase58Prefix = config.poolServer.intAddressPrefix ? parseInt(config.poolServer.intAddressPrefix) : addressBase58Prefix + 1; +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 = new Buffer(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; - 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; - } - }; -}; \ No newline at end of file +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/package.json b/package.json index e6e8fca89..0a2a0f314 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,31 @@ { - "name": "cryptonote-nodejs-pool", - "version": "1.3.5", - "license": "GPL-2.0", - "author": "Daniel Vandal", - "repository": { - "type": "git", - "url": "https://github.com/dvandal/cryptonote-nodejs-pool.git" - }, - "dependencies": { - "async": "1", - "base58-native": "*", - "bignum": "*", - "cli-color": "*", - "cryptoforknote-util": "git://github.com/campurro/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": "*" - }, - "engines": { - "node": ">=4.0" - } + "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" + } } 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 000000000..2f03f239a Binary files /dev/null and b/website_example/favicon.ico differ diff --git a/website_example/index.html b/website_example/index.html index 47cc13a1e..9e74180e4 100644 --- a/website_example/index.html +++ b/website_example/index.html @@ -1,211 +1,323 @@ - - - - - - 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 baf8af8bc..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,248 +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"
     },
 ],
-
-
-
+
+
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; + } + +}