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';
+ for (var lang in langs) {
+ var selected = lang == langCode ? ' selected="selected"' : '';
+ html += '' + 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 += '';
+ for (var lang in langs) {
+ var selected = lang == langCode ? ' selected="selected"' : '';
+ html += '' + langs[lang] + ' ';
+ numLangs ++;
+ }
+ 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
+
+
+
+
+
+ Port
+ Starting Difficulty
+ Description
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mining Applications
+
+
+
+
Generate your custom configuration to mine on our pool
+
+
+
+
+
+
+
+
+
+ App Name
+ Architecture
+ Features
+ Download
+ Configuration
+
+
+
+
+
+
+ XMR Stak
+ CPU & GPU (AMD/NVIDIA)
+ Easy to use CPU + GPU Mining App
+ Download
+ See 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": " ",
+
+
+
+
+
+
+
+ XMRig
+ CPU
+ Ligthweight but powerful CPU Mining App
+ Download
+ See more
+
+
+
+
+
+
"pools": [
+ {
+ "url": "POOL_HOST :PORT ",
+ "user": "YOUR_WALLET_ADDRESS ",
+ "pass": "YOUR_WORKER_NAME ",
+ "keepalive": true,
+ "nicehash": false,
+ "variant": 1
+ }
+],
+
+
+
+
+
+
+
+ XMRig-AMD
+ OpenCL (AMD)
+ XMRIG version for AMD GPU
+ Download
+ See more
+
+
+
+
+
+
"pools": [
+ {
+ "url": "POOL_HOST :PORT ",
+ "user": "YOUR_WALLET_ADDRESS ",
+ "pass": "YOUR_WORKER_NAME "
+ "keepalive": true,
+ "nicehash": false,
+ "variant": 1
+ }
+],
+
+
+
+
+
+
+
+ XMRig-NVIDIA
+ Cuda (Nvidia)
+ XMRIG version for Nvidia GPU
+ Download
+ See more
+
+
+
+
+
+
"pools": [
+ {
+ "url": "POOL_HOST :PORT ",
+ "user": "YOUR_WALLET_ADDRESS ",
+ "pass": "YOUR_WORKER_NAME "
+ "keepalive": true,
+ "nicehash": false,
+ "variant": 1
+ }
+],
+
+
+
+
+
+
+
+ XMRigCC
+ CPU
+ XMRIG Fork, optimized with remote control
+ Download
+ See more
+
+
+
+
+
+
"pools": [
+ {
+ "url": "POOL_HOST :PORT ",
+ "user": "YOUR_WALLET_ADDRESS ",
+ "pass": "YOUR_WORKER_NAME ",
+ "keepalive": true,
+ "nicehash": false
+ },
+],
+
+
+
+
+
+
+
+ SRBMiner Cryptonight AMD GPU Miner
+ OpenCL (AMD)
+ XMRIG version for AMD GPU
+ Download
+ See more
+
+
+
+
+
+
+{
+/* Type can be : normal, lite, heavy */
+"cryptonight_type" : "heavy",
+
+/* Intensity 0-> auto intensity, or value from 1-200 */
+"intensity" : 0,
+
+/* To use 2 threads per card set double_threads to true */
+"double_threads" : false,
+
+/* Gpu target temperature, leave it on 0 if you don't want to use this option */
+"target_temperature" : 0,
+
+/* If you use a SSL/TLS encrypted pool set this to true*/
+"pool_use_tls" : false,
+
+/* Mining pool address WITHOUT the stratum+tcp:// or stratum+tls:// part */
+"pool" : "POOL_HOST :PORT ",
+
+/* Address of you wallet */
+"wallet" : "YOUR_WALLET_ADDRESS ",
+
+/* Password for your wallet, probably just x */
+"password" : "YOUR_WORKER_NAME ",
+
+/* Location for devfee servers, to get better latency */
+"location" : "europe",
+
+/* If you want to log console output, put a filename here */
+"log_file" : "",
+
+/* Charity mining, if you want to disable charity mining just delete these lines, or set values to "" (empty) */
+"charity_pool" : "POOL_HOST :PORT ",
+"charity_wallet" : "YOUR_WALLET_ADDRESS ",
+"charity_password" : "YOUR_WORKER_NAME ",
+
+/* Settings for each GPU manually */
+/* Put in devices that you want to use, if you for ex. don't want to use gpu 2, just don't insert it,like in this example */
+/* Id starts from 0 , not from 1 !! */
+/* To get a list of available devices with their id's, use --listdevices parameter */
+/* This is just an example, edit it and remove comment lines (the slash and star) !! */
+/*"gpu_conf" :
+[
+ { "id" : 0, "intensity" : 0, "double_threads" : true},
+ { "id" : 1, "intensity" : 60, "double_threads" : false},
+ { "id" : 3, "intensity" : 50, "double_threads" : true},
+ { "id" : 4, "intensity" : 0, "double_threads" : true},
+]*/
+}
+
+
+
+
+
+
+
+
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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Pool Hash Rate
+
N/A (0% )
+
+
+
+
+
+
+
+
+
+
+
+
Blocks Found
+
N/A (Never )
+
+
+
+
+
+
+
+
+
+
+
+
Blocks Found Every
+
N/A (estimated )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Network Hash Rate
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Blockchain Height
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Connected Miners
+
N/A (N/A workers )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Payment Interval
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 ...
+
+
+
+
+
+
+
+
Estimate Mining Profits
+
+
+
+
+
+
+
\ 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 )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 Sent
+ Transaction Hash
+ Amount
+ Fee
+ Mixin
+ Payees
+
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Time Found
+ Reward
+ Height
+ Difficulty
+ Block Hash
+ Effort
+ Status
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
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)
+
+
+
+
+
+
+
+
+
+
+ Port
+ Connected 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.
+
+
+
+
Miner IP address :
+
+
+
+
+
+
+
+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.
+
+
Minimum payout :
+
+
+
+
+
+ Set
+
+
+
+
+
+
+
+
Enable email notifications
+
+
This pool will send out email notification when a block is found and whenever a payout happens.
+
+
Email address :
+
+
+
+
+
+ Enable
+
+
+ Disable
+
+
+
+
+
+
+
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+
+
+
+
+
+
+
+
+
+ Send test
+
+
+
+
+
+
+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
+
+
+
+
+
+ #
+ Miner
+ Hash Rate
+ Last Share Submitted
+ Total 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)
+
+
+
+
+
+
+
+
+
+
+ Wallet
+ Hashrate
+ 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
+
+
+
+
+
+ Lookup
+ Searching...
+
+
+
+
+
+
+
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 Sent
+ Transaction Hash
+ Amount
+ Mixin
+
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
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
+
+
+
+
+
+ Port
+ Starting Difficulty
+ Description
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mining Applications
+
+
+
+
Generate your custom configuration to mine on our pool
+
+
+
+
+
+
+
+
+
+ App Name
+ Architecture
+ Features
+ Download
+ Configuration
+
+
+
+
+
+
+ XMR Stak
+ CPU & GPU (AMD/NVIDIA)
+ Easy to use CPU + GPU Mining App
+ Download
+ See 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": " ",
+
+
+
+
+
+
+
+ XMRig
+ CPU
+ Ligthweight but powerful CPU Mining App
+ Download
+ See more
+
+
+
+
+
+
"pools": [
+ {
+ "url": "POOL_HOST :PORT ",
+ "user": "YOUR_WALLET_ADDRESS ",
+ "pass": "YOUR_BSM_WALLET_ADDRESS "
+ "keepalive": true,
+ "nicehash": false,
+ "variant": 1
+ }
+],
+
+
+
+
+
+
+
+ XMRig-AMD
+ OpenCL (AMD)
+ XMRIG version for AMD GPU
+ Download
+ See more
+
+
+
+
+
+
"pools": [
+ {
+ "url": "POOL_HOST :PORT ",
+ "user": "YOUR_WALLET_ADDRESS ",
+ "pass": "YOUR_BSM_WALLET_ADDRESS "
+ "keepalive": true,
+ "nicehash": false,
+ "variant": 1
+ }
+],
+
+
+
+
+
+
+
+ XMRig-NVIDIA
+ Cuda (Nvidia)
+ XMRIG version for Nvidia GPU
+ Download
+ See more
+
+
+
+
+
+
"pools": [
+ {
+ "url": "POOL_HOST :PORT ",
+ "user": "YOUR_WALLET_ADDRESS ",
+ "pass": "YOUR_BSM_WALLET_ADDRESS "
+ "keepalive": true,
+ "nicehash": false,
+ "variant": 1
+ }
+],
+
+
+
+
+
+
+
+ XMRigCC
+ CPU
+ XMRIG Fork, optimized with remote control
+ Download
+ See 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/A 42
+ / N/A ZLS
+
+ (Never )
+
+
+
+
+
+
+
+
+
+
+
+
+
Blocks Found Every
+
N/A (estimated )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Network Hash Rate
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Blockchain Height
+
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 )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Payment Interval
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 ...
+
+
+
+
+
+
+
+
Estimate Mining Profits
+
+
+
+
+
+
+
\ 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
+
+
+
+
+
+
+
+
+
+
+ Lookup
+ Searching...
+
+
+
+
+
+
+
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 Sent
+ Transaction Hash
+ Amount
+ Mixin
+
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Lookup
+ Searching...
+
+
+
+
+
+
+
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 Sent
+ Transaction Hash
+ Amount
+ Mixin
+
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
+
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 )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Payment Interval
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
Denomination Unit
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+ Time Sent
+ Transaction Hash
+ Amount
+ Fee
+ Mixin
+ Payees
+
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Total Payments
+
N/A (N/A miners )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Payment Interval
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
Denomination Unit
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+ Time Sent
+ Transaction Hash
+ Amount
+ Fee
+ Mixin
+ Payees
+
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Time Found
+ Reward
+ Height
+ Difficulty
+ Block Hash
+ Effort
+ Status
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Total Blocks Mined
+
N/A (Never )
+
+
+
+
+
+
+
+
+
+
+
+
Maturity Requirement
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Time Found
+ Reward
+ Height
+ Difficulty
+ Block Hash
+ Effort
+ Status
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Time Found
+ Reward
+ Height
+ Difficulty
+ Block Hash
+ Effort
+ Status
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Total Blocks Mined
+
N/A (Never )
+
+
+
+
+
+
+
+
+
+
+
+
Maturity Requirement
+
N/A
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Time Found
+ Reward
+ Height
+ Difficulty
+ Block Hash
+ Effort
+ Status
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
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.
+
+
+
+
Miner IP address :
+
+
+
+
+
+
+
+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.
+
+
Minimum payout :
+
+
+
+
+
+ Set
+
+
+
+
+
+
+
+
Enable email notifications
+
+
This pool will send out email notification when a block is found and whenever a payout happens.
+
+
Email address :
+
+
+
+
+
+ Enable
+
+
+ Disable
+
+
+
+
+
+
+
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
+
+
+
+
+
+ #
+ Miner
+ Hash Rate
+ Last Share Submitted
+ Total 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
+
+
+
+
+
+
+
+
+
+
+ Lookup
+ Searching...
+
+
+
+
+
+
+
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 Sent
+ Transaction Hash
+ Amount
+ Mixin
+
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Lookup
+ Searching...
+
+
+
+
+
+
+
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 Sent
+ Transaction Hash
+ Amount
+ Mixin
+
+
+
+
+
+
+
+
+
+
+ Load More
+
+
+
+
+
+
+
+
+
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
+
+