├── .gitignore ├── README.md ├── config ├── bans.json ├── config.json └── poolRewards.json ├── dashboard ├── rewards.json ├── static │ ├── index.html │ ├── logo.png │ ├── script.js │ └── styles.css └── workers.json ├── package-lock.json ├── package.json ├── src ├── connectionHandler.js ├── dashboard.js ├── index.js ├── kolka.js ├── logging.js ├── mining.js └── sync.js └── utils └── createPool.js /.gitignore: -------------------------------------------------------------------------------- 1 | duinocoin/ 2 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Welcome to duino-coin-pools 👋

2 |

3 | 4 | Maintenance 5 | License: MIT 6 |

7 | 8 | Duino Pools are mining nodes for Duino-Coin written in node.js. 9 | 10 | * [Duino-Coin Github page](https://github.com/revoxhere/duino-coin) 11 | * [Duino-Coin Website](https://duinocoin.com) 12 | 13 | ## Show your support 14 | 15 | Give a ⭐️ if this project helped you! 16 | 17 | ## 📝 License 18 | 19 | Copyright © 2021-2022 [Bilaboz](https://github.com/Bilaboz) from the Duino-Coin team.
20 | Copyright © 2021-2022 [revox](https://piotrowsky.dev) from the Duino-Coin team.
21 | This project is [MIT](https://github.com/Bilaboz/duino-stats/blob/main/LICENSE) licensed. 22 | 23 | *** 24 | -------------------------------------------------------------------------------- /config/bans.json: -------------------------------------------------------------------------------- 1 | { 2 | "bannedUsernames": [ 3 | "bad_guy1337" 4 | ], 5 | "bannedIPs": [ 6 | 7 | ] 8 | } -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "poolName": "", 3 | "poolPassword": "", 4 | "poolID": "", 5 | "poolVersion": "0.1", 6 | 7 | "host": "0.0.0.0", 8 | "port": 6000, 9 | 10 | "serverIP": "server.duinocoin.com", 11 | "serverPort": 2810, 12 | "serverVersion": "2.5", 13 | 14 | "preGenJobCount": 1000, 15 | "jobGenerationDelay": 30000, 16 | 17 | "maxWorkers": 50, 18 | 19 | "motd": "Welcome to the first Duino-Coin pool!", 20 | 21 | "initialBlockHash": "05b7406b3909071b2d85b2d2958ad07a0d52bd12", 22 | "expectedSharetime": 12, 23 | "updateMinersStatsEvery": 2, 24 | "blockProbability": 1000000, 25 | "blockReward": 28.11 26 | } 27 | -------------------------------------------------------------------------------- /config/poolRewards.json: -------------------------------------------------------------------------------- 1 | { 2 | "EXTREME": { 3 | "difficulty": 950000, 4 | "reward": 0, 5 | "max_hashrate": 999999999, 6 | "kolka_decrease_perc": 80 7 | }, 8 | "XXHASH": { 9 | "difficulty": 100000, 10 | "reward": 0.0003, 11 | "max_hashrate": 900000, 12 | "kolka_decrease_perc": 80 13 | }, 14 | "NET": { 15 | "difficulty": 100000, 16 | "reward": 0.0015811, 17 | "max_hashrate": 1000000, 18 | "kolka_decrease_perc": 80 19 | }, 20 | "MEDIUM": { 21 | "difficulty": 75000, 22 | "reward": 0.0012811, 23 | "max_hashrate": 500000, 24 | "kolka_decrease_perc": 80 25 | }, 26 | "LOW": { 27 | "difficulty": 7500, 28 | "reward": 0.0022811, 29 | "max_hashrate": 250000, 30 | "kolka_decrease_perc": 80 31 | }, 32 | "ESP32": { 33 | "difficulty": 1500, 34 | "reward": 0.00375, 35 | "max_hashrate": 16000, 36 | "kolka_decrease_perc": 96 37 | }, 38 | "ESP8266": { 39 | "difficulty": 1000, 40 | "reward": 0.0045, 41 | "max_hashrate": 13000, 42 | "kolka_decrease_perc": 96 43 | }, 44 | "DUE": { 45 | "difficulty": 500, 46 | "reward": 0.003, 47 | "max_hashrate": 5000, 48 | "kolka_decrease_perc": 96 49 | }, 50 | "ARM": { 51 | "difficulty": 100, 52 | "reward": 0.003, 53 | "max_hashrate": 1000, 54 | "kolka_decrease_perc": 96 55 | }, 56 | "MEGA": { 57 | "difficulty": 16, 58 | "reward": 0.004, 59 | "max_hashrate": 400, 60 | "kolka_decrease_perc": 96 61 | }, 62 | "AVR": { 63 | "difficulty": 6, 64 | "reward": 0.005, 65 | "max_hashrate": 240, 66 | "kolka_decrease_perc": 96 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /dashboard/rewards.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /dashboard/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Pulse Pool Stats 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 |
22 |

Welcome to

23 | 27 |

28 | a 29 | 30 | Duino-Coin 31 | 32 | mining node 👋 33 |

34 |
35 |
36 |

37 | 38 | Node statistics 39 |

40 |
41 |
42 |

43 | 44 | Connections: 45 | 46 | 0 47 | 48 |

49 |
50 |
51 |

52 | 53 | CPU usage: 54 | 55 | 0% 56 | 57 |

58 |
59 |
60 |

61 | 62 | RAM usage: 63 | 64 | 0% 65 | 66 |

67 |
68 |
69 |

70 | 71 | Workers: 72 | 73 | workers.json 74 | 75 |

76 |
77 |
78 |

79 | ~ More data will be added here when Bila adds more api stuff ~ 80 |

81 |
82 |
83 |

84 | 85 | History 86 |

87 |

88 | Pulse pool (also named Kolka pool) is the first Duino-Coin mining node created by Bilaboz from the Duino team.
89 | It was launched at 6/07/2021 and helps in off-loading the master server from PC and ESP miners. 90 |

91 |
92 |
93 |
94 |
95 |

96 |

97 |

98 |

99 |

100 |

101 |

102 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /dashboard/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bilaboz/duino-coin-pools/6d309ed1ff216bfe049775e838589c11f66d138b/dashboard/static/logo.png -------------------------------------------------------------------------------- /dashboard/static/script.js: -------------------------------------------------------------------------------- 1 | const connections = document.getElementById("connections"); 2 | const cpu = document.getElementById("cpu"); 3 | const ram = document.getElementById("ram"); 4 | const workers = document.getElementById("workers"); 5 | 6 | function fetch_statistics() { 7 | fetch("/statistics") 8 | .then(response => response.json()) 9 | .then(data => { 10 | connections.innerHTML = data["connections"]; 11 | cpu.innerHTML = data["cpu"].toFixed(2) + "%"; 12 | ram.innerHTML = data["ram"].toFixed(2) + "%"; 13 | }) 14 | } 15 | 16 | fetch_statistics(); 17 | setInterval(function() { 18 | // run every 5s 19 | fetch_statistics(); 20 | }, 5000); -------------------------------------------------------------------------------- /dashboard/static/styles.css: -------------------------------------------------------------------------------- 1 | /* Some dracula colors from https://draculatheme.com/contribute */ 2 | .has-text-success { 3 | color: #50fa7b; 4 | } 5 | 6 | .has-text-warning { 7 | color: #ffb86c; 8 | } 9 | 10 | .gradient-logo { 11 | background: linear-gradient(to right, 12 | #FFF 10%, 13 | #6272a4 30%, 14 | #8be9fd 70%, 15 | #FFF 90%); 16 | -webkit-background-clip: text; 17 | background-clip: text; 18 | 19 | -webkit-text-fill-color: transparent; 20 | text-fill-color: transparent; 21 | 22 | background-size: 200% auto; 23 | animation: text_shine 2.5s ease infinite alternate; 24 | } 25 | 26 | @keyframes text_shine { 27 | to { 28 | background-position: 200%; 29 | } 30 | } -------------------------------------------------------------------------------- /dashboard/workers.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "duino-coin-pools", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "MIT", 10 | "dependencies": { 11 | "axios": "^0.21.1", 12 | "express": "^4.17.1", 13 | "node-os-utils": "^1.3.5", 14 | "xxhashjs": "^0.2.2" 15 | } 16 | }, 17 | "node_modules/accepts": { 18 | "version": "1.3.7", 19 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 20 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 21 | "dependencies": { 22 | "mime-types": "~2.1.24", 23 | "negotiator": "0.6.2" 24 | }, 25 | "engines": { 26 | "node": ">= 0.6" 27 | } 28 | }, 29 | "node_modules/array-flatten": { 30 | "version": "1.1.1", 31 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 32 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 33 | }, 34 | "node_modules/axios": { 35 | "version": "0.21.1", 36 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", 37 | "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", 38 | "dependencies": { 39 | "follow-redirects": "^1.10.0" 40 | } 41 | }, 42 | "node_modules/body-parser": { 43 | "version": "1.19.0", 44 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 45 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 46 | "dependencies": { 47 | "bytes": "3.1.0", 48 | "content-type": "~1.0.4", 49 | "debug": "2.6.9", 50 | "depd": "~1.1.2", 51 | "http-errors": "1.7.2", 52 | "iconv-lite": "0.4.24", 53 | "on-finished": "~2.3.0", 54 | "qs": "6.7.0", 55 | "raw-body": "2.4.0", 56 | "type-is": "~1.6.17" 57 | }, 58 | "engines": { 59 | "node": ">= 0.8" 60 | } 61 | }, 62 | "node_modules/bytes": { 63 | "version": "3.1.0", 64 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 65 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", 66 | "engines": { 67 | "node": ">= 0.8" 68 | } 69 | }, 70 | "node_modules/content-disposition": { 71 | "version": "0.5.3", 72 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 73 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 74 | "dependencies": { 75 | "safe-buffer": "5.1.2" 76 | }, 77 | "engines": { 78 | "node": ">= 0.6" 79 | } 80 | }, 81 | "node_modules/content-type": { 82 | "version": "1.0.4", 83 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 84 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 85 | "engines": { 86 | "node": ">= 0.6" 87 | } 88 | }, 89 | "node_modules/cookie": { 90 | "version": "0.4.0", 91 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 92 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 93 | "engines": { 94 | "node": ">= 0.6" 95 | } 96 | }, 97 | "node_modules/cookie-signature": { 98 | "version": "1.0.6", 99 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 100 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 101 | }, 102 | "node_modules/cuint": { 103 | "version": "0.2.2", 104 | "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", 105 | "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=" 106 | }, 107 | "node_modules/debug": { 108 | "version": "2.6.9", 109 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 110 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 111 | "dependencies": { 112 | "ms": "2.0.0" 113 | } 114 | }, 115 | "node_modules/depd": { 116 | "version": "1.1.2", 117 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 118 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 119 | "engines": { 120 | "node": ">= 0.6" 121 | } 122 | }, 123 | "node_modules/destroy": { 124 | "version": "1.0.4", 125 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 126 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 127 | }, 128 | "node_modules/ee-first": { 129 | "version": "1.1.1", 130 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 131 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 132 | }, 133 | "node_modules/encodeurl": { 134 | "version": "1.0.2", 135 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 136 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 137 | "engines": { 138 | "node": ">= 0.8" 139 | } 140 | }, 141 | "node_modules/escape-html": { 142 | "version": "1.0.3", 143 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 144 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 145 | }, 146 | "node_modules/etag": { 147 | "version": "1.8.1", 148 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 149 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 150 | "engines": { 151 | "node": ">= 0.6" 152 | } 153 | }, 154 | "node_modules/express": { 155 | "version": "4.17.1", 156 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 157 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 158 | "dependencies": { 159 | "accepts": "~1.3.7", 160 | "array-flatten": "1.1.1", 161 | "body-parser": "1.19.0", 162 | "content-disposition": "0.5.3", 163 | "content-type": "~1.0.4", 164 | "cookie": "0.4.0", 165 | "cookie-signature": "1.0.6", 166 | "debug": "2.6.9", 167 | "depd": "~1.1.2", 168 | "encodeurl": "~1.0.2", 169 | "escape-html": "~1.0.3", 170 | "etag": "~1.8.1", 171 | "finalhandler": "~1.1.2", 172 | "fresh": "0.5.2", 173 | "merge-descriptors": "1.0.1", 174 | "methods": "~1.1.2", 175 | "on-finished": "~2.3.0", 176 | "parseurl": "~1.3.3", 177 | "path-to-regexp": "0.1.7", 178 | "proxy-addr": "~2.0.5", 179 | "qs": "6.7.0", 180 | "range-parser": "~1.2.1", 181 | "safe-buffer": "5.1.2", 182 | "send": "0.17.1", 183 | "serve-static": "1.14.1", 184 | "setprototypeof": "1.1.1", 185 | "statuses": "~1.5.0", 186 | "type-is": "~1.6.18", 187 | "utils-merge": "1.0.1", 188 | "vary": "~1.1.2" 189 | }, 190 | "engines": { 191 | "node": ">= 0.10.0" 192 | } 193 | }, 194 | "node_modules/finalhandler": { 195 | "version": "1.1.2", 196 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 197 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 198 | "dependencies": { 199 | "debug": "2.6.9", 200 | "encodeurl": "~1.0.2", 201 | "escape-html": "~1.0.3", 202 | "on-finished": "~2.3.0", 203 | "parseurl": "~1.3.3", 204 | "statuses": "~1.5.0", 205 | "unpipe": "~1.0.0" 206 | }, 207 | "engines": { 208 | "node": ">= 0.8" 209 | } 210 | }, 211 | "node_modules/follow-redirects": { 212 | "version": "1.14.0", 213 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz", 214 | "integrity": "sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg==", 215 | "funding": [ 216 | { 217 | "type": "individual", 218 | "url": "https://github.com/sponsors/RubenVerborgh" 219 | } 220 | ], 221 | "engines": { 222 | "node": ">=4.0" 223 | }, 224 | "peerDependenciesMeta": { 225 | "debug": { 226 | "optional": true 227 | } 228 | } 229 | }, 230 | "node_modules/forwarded": { 231 | "version": "0.2.0", 232 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 233 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 234 | "engines": { 235 | "node": ">= 0.6" 236 | } 237 | }, 238 | "node_modules/fresh": { 239 | "version": "0.5.2", 240 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 241 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 242 | "engines": { 243 | "node": ">= 0.6" 244 | } 245 | }, 246 | "node_modules/http-errors": { 247 | "version": "1.7.2", 248 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 249 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 250 | "dependencies": { 251 | "depd": "~1.1.2", 252 | "inherits": "2.0.3", 253 | "setprototypeof": "1.1.1", 254 | "statuses": ">= 1.5.0 < 2", 255 | "toidentifier": "1.0.0" 256 | }, 257 | "engines": { 258 | "node": ">= 0.6" 259 | } 260 | }, 261 | "node_modules/iconv-lite": { 262 | "version": "0.4.24", 263 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 264 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 265 | "dependencies": { 266 | "safer-buffer": ">= 2.1.2 < 3" 267 | }, 268 | "engines": { 269 | "node": ">=0.10.0" 270 | } 271 | }, 272 | "node_modules/inherits": { 273 | "version": "2.0.3", 274 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 275 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 276 | }, 277 | "node_modules/ipaddr.js": { 278 | "version": "1.9.1", 279 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 280 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 281 | "engines": { 282 | "node": ">= 0.10" 283 | } 284 | }, 285 | "node_modules/media-typer": { 286 | "version": "0.3.0", 287 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 288 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 289 | "engines": { 290 | "node": ">= 0.6" 291 | } 292 | }, 293 | "node_modules/merge-descriptors": { 294 | "version": "1.0.1", 295 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 296 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 297 | }, 298 | "node_modules/methods": { 299 | "version": "1.1.2", 300 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 301 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 302 | "engines": { 303 | "node": ">= 0.6" 304 | } 305 | }, 306 | "node_modules/mime": { 307 | "version": "1.6.0", 308 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 309 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 310 | "bin": { 311 | "mime": "cli.js" 312 | }, 313 | "engines": { 314 | "node": ">=4" 315 | } 316 | }, 317 | "node_modules/mime-db": { 318 | "version": "1.48.0", 319 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", 320 | "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", 321 | "engines": { 322 | "node": ">= 0.6" 323 | } 324 | }, 325 | "node_modules/mime-types": { 326 | "version": "2.1.31", 327 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", 328 | "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", 329 | "dependencies": { 330 | "mime-db": "1.48.0" 331 | }, 332 | "engines": { 333 | "node": ">= 0.6" 334 | } 335 | }, 336 | "node_modules/ms": { 337 | "version": "2.0.0", 338 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 339 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 340 | }, 341 | "node_modules/negotiator": { 342 | "version": "0.6.2", 343 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 344 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 345 | "engines": { 346 | "node": ">= 0.6" 347 | } 348 | }, 349 | "node_modules/node-os-utils": { 350 | "version": "1.3.5", 351 | "resolved": "https://registry.npmjs.org/node-os-utils/-/node-os-utils-1.3.5.tgz", 352 | "integrity": "sha512-bIJIlk+hA+7/ATnu3sQMtF697iw9T/JksDhKMe9uENG0OhzIG7hLM6fbcyu18bOuajlYWnSlj0IhDo2q7k0ebg==" 353 | }, 354 | "node_modules/on-finished": { 355 | "version": "2.3.0", 356 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 357 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 358 | "dependencies": { 359 | "ee-first": "1.1.1" 360 | }, 361 | "engines": { 362 | "node": ">= 0.8" 363 | } 364 | }, 365 | "node_modules/parseurl": { 366 | "version": "1.3.3", 367 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 368 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 369 | "engines": { 370 | "node": ">= 0.8" 371 | } 372 | }, 373 | "node_modules/path-to-regexp": { 374 | "version": "0.1.7", 375 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 376 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 377 | }, 378 | "node_modules/proxy-addr": { 379 | "version": "2.0.7", 380 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 381 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 382 | "dependencies": { 383 | "forwarded": "0.2.0", 384 | "ipaddr.js": "1.9.1" 385 | }, 386 | "engines": { 387 | "node": ">= 0.10" 388 | } 389 | }, 390 | "node_modules/qs": { 391 | "version": "6.7.0", 392 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 393 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", 394 | "engines": { 395 | "node": ">=0.6" 396 | } 397 | }, 398 | "node_modules/range-parser": { 399 | "version": "1.2.1", 400 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 401 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 402 | "engines": { 403 | "node": ">= 0.6" 404 | } 405 | }, 406 | "node_modules/raw-body": { 407 | "version": "2.4.0", 408 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 409 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 410 | "dependencies": { 411 | "bytes": "3.1.0", 412 | "http-errors": "1.7.2", 413 | "iconv-lite": "0.4.24", 414 | "unpipe": "1.0.0" 415 | }, 416 | "engines": { 417 | "node": ">= 0.8" 418 | } 419 | }, 420 | "node_modules/safe-buffer": { 421 | "version": "5.1.2", 422 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 423 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 424 | }, 425 | "node_modules/safer-buffer": { 426 | "version": "2.1.2", 427 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 428 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 429 | }, 430 | "node_modules/send": { 431 | "version": "0.17.1", 432 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 433 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 434 | "dependencies": { 435 | "debug": "2.6.9", 436 | "depd": "~1.1.2", 437 | "destroy": "~1.0.4", 438 | "encodeurl": "~1.0.2", 439 | "escape-html": "~1.0.3", 440 | "etag": "~1.8.1", 441 | "fresh": "0.5.2", 442 | "http-errors": "~1.7.2", 443 | "mime": "1.6.0", 444 | "ms": "2.1.1", 445 | "on-finished": "~2.3.0", 446 | "range-parser": "~1.2.1", 447 | "statuses": "~1.5.0" 448 | }, 449 | "engines": { 450 | "node": ">= 0.8.0" 451 | } 452 | }, 453 | "node_modules/send/node_modules/ms": { 454 | "version": "2.1.1", 455 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 456 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 457 | }, 458 | "node_modules/serve-static": { 459 | "version": "1.14.1", 460 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 461 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 462 | "dependencies": { 463 | "encodeurl": "~1.0.2", 464 | "escape-html": "~1.0.3", 465 | "parseurl": "~1.3.3", 466 | "send": "0.17.1" 467 | }, 468 | "engines": { 469 | "node": ">= 0.8.0" 470 | } 471 | }, 472 | "node_modules/setprototypeof": { 473 | "version": "1.1.1", 474 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 475 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 476 | }, 477 | "node_modules/statuses": { 478 | "version": "1.5.0", 479 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 480 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 481 | "engines": { 482 | "node": ">= 0.6" 483 | } 484 | }, 485 | "node_modules/toidentifier": { 486 | "version": "1.0.0", 487 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 488 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", 489 | "engines": { 490 | "node": ">=0.6" 491 | } 492 | }, 493 | "node_modules/type-is": { 494 | "version": "1.6.18", 495 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 496 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 497 | "dependencies": { 498 | "media-typer": "0.3.0", 499 | "mime-types": "~2.1.24" 500 | }, 501 | "engines": { 502 | "node": ">= 0.6" 503 | } 504 | }, 505 | "node_modules/unpipe": { 506 | "version": "1.0.0", 507 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 508 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 509 | "engines": { 510 | "node": ">= 0.8" 511 | } 512 | }, 513 | "node_modules/utils-merge": { 514 | "version": "1.0.1", 515 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 516 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 517 | "engines": { 518 | "node": ">= 0.4.0" 519 | } 520 | }, 521 | "node_modules/vary": { 522 | "version": "1.1.2", 523 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 524 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 525 | "engines": { 526 | "node": ">= 0.8" 527 | } 528 | }, 529 | "node_modules/xxhashjs": { 530 | "version": "0.2.2", 531 | "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", 532 | "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", 533 | "dependencies": { 534 | "cuint": "^0.2.2" 535 | } 536 | } 537 | }, 538 | "dependencies": { 539 | "accepts": { 540 | "version": "1.3.7", 541 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 542 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 543 | "requires": { 544 | "mime-types": "~2.1.24", 545 | "negotiator": "0.6.2" 546 | } 547 | }, 548 | "array-flatten": { 549 | "version": "1.1.1", 550 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 551 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 552 | }, 553 | "axios": { 554 | "version": "0.21.1", 555 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", 556 | "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", 557 | "requires": { 558 | "follow-redirects": "^1.10.0" 559 | } 560 | }, 561 | "body-parser": { 562 | "version": "1.19.0", 563 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 564 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 565 | "requires": { 566 | "bytes": "3.1.0", 567 | "content-type": "~1.0.4", 568 | "debug": "2.6.9", 569 | "depd": "~1.1.2", 570 | "http-errors": "1.7.2", 571 | "iconv-lite": "0.4.24", 572 | "on-finished": "~2.3.0", 573 | "qs": "6.7.0", 574 | "raw-body": "2.4.0", 575 | "type-is": "~1.6.17" 576 | } 577 | }, 578 | "bytes": { 579 | "version": "3.1.0", 580 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 581 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 582 | }, 583 | "content-disposition": { 584 | "version": "0.5.3", 585 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 586 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 587 | "requires": { 588 | "safe-buffer": "5.1.2" 589 | } 590 | }, 591 | "content-type": { 592 | "version": "1.0.4", 593 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 594 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 595 | }, 596 | "cookie": { 597 | "version": "0.4.0", 598 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 599 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 600 | }, 601 | "cookie-signature": { 602 | "version": "1.0.6", 603 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 604 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 605 | }, 606 | "cuint": { 607 | "version": "0.2.2", 608 | "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", 609 | "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=" 610 | }, 611 | "debug": { 612 | "version": "2.6.9", 613 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 614 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 615 | "requires": { 616 | "ms": "2.0.0" 617 | } 618 | }, 619 | "depd": { 620 | "version": "1.1.2", 621 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 622 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 623 | }, 624 | "destroy": { 625 | "version": "1.0.4", 626 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 627 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 628 | }, 629 | "ee-first": { 630 | "version": "1.1.1", 631 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 632 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 633 | }, 634 | "encodeurl": { 635 | "version": "1.0.2", 636 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 637 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 638 | }, 639 | "escape-html": { 640 | "version": "1.0.3", 641 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 642 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 643 | }, 644 | "etag": { 645 | "version": "1.8.1", 646 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 647 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 648 | }, 649 | "express": { 650 | "version": "4.17.1", 651 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 652 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 653 | "requires": { 654 | "accepts": "~1.3.7", 655 | "array-flatten": "1.1.1", 656 | "body-parser": "1.19.0", 657 | "content-disposition": "0.5.3", 658 | "content-type": "~1.0.4", 659 | "cookie": "0.4.0", 660 | "cookie-signature": "1.0.6", 661 | "debug": "2.6.9", 662 | "depd": "~1.1.2", 663 | "encodeurl": "~1.0.2", 664 | "escape-html": "~1.0.3", 665 | "etag": "~1.8.1", 666 | "finalhandler": "~1.1.2", 667 | "fresh": "0.5.2", 668 | "merge-descriptors": "1.0.1", 669 | "methods": "~1.1.2", 670 | "on-finished": "~2.3.0", 671 | "parseurl": "~1.3.3", 672 | "path-to-regexp": "0.1.7", 673 | "proxy-addr": "~2.0.5", 674 | "qs": "6.7.0", 675 | "range-parser": "~1.2.1", 676 | "safe-buffer": "5.1.2", 677 | "send": "0.17.1", 678 | "serve-static": "1.14.1", 679 | "setprototypeof": "1.1.1", 680 | "statuses": "~1.5.0", 681 | "type-is": "~1.6.18", 682 | "utils-merge": "1.0.1", 683 | "vary": "~1.1.2" 684 | } 685 | }, 686 | "finalhandler": { 687 | "version": "1.1.2", 688 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 689 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 690 | "requires": { 691 | "debug": "2.6.9", 692 | "encodeurl": "~1.0.2", 693 | "escape-html": "~1.0.3", 694 | "on-finished": "~2.3.0", 695 | "parseurl": "~1.3.3", 696 | "statuses": "~1.5.0", 697 | "unpipe": "~1.0.0" 698 | } 699 | }, 700 | "follow-redirects": { 701 | "version": "1.14.0", 702 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz", 703 | "integrity": "sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg==" 704 | }, 705 | "forwarded": { 706 | "version": "0.2.0", 707 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 708 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 709 | }, 710 | "fresh": { 711 | "version": "0.5.2", 712 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 713 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 714 | }, 715 | "http-errors": { 716 | "version": "1.7.2", 717 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 718 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 719 | "requires": { 720 | "depd": "~1.1.2", 721 | "inherits": "2.0.3", 722 | "setprototypeof": "1.1.1", 723 | "statuses": ">= 1.5.0 < 2", 724 | "toidentifier": "1.0.0" 725 | } 726 | }, 727 | "iconv-lite": { 728 | "version": "0.4.24", 729 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 730 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 731 | "requires": { 732 | "safer-buffer": ">= 2.1.2 < 3" 733 | } 734 | }, 735 | "inherits": { 736 | "version": "2.0.3", 737 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 738 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 739 | }, 740 | "ipaddr.js": { 741 | "version": "1.9.1", 742 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 743 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 744 | }, 745 | "media-typer": { 746 | "version": "0.3.0", 747 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 748 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 749 | }, 750 | "merge-descriptors": { 751 | "version": "1.0.1", 752 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 753 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 754 | }, 755 | "methods": { 756 | "version": "1.1.2", 757 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 758 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 759 | }, 760 | "mime": { 761 | "version": "1.6.0", 762 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 763 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 764 | }, 765 | "mime-db": { 766 | "version": "1.48.0", 767 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", 768 | "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" 769 | }, 770 | "mime-types": { 771 | "version": "2.1.31", 772 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", 773 | "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", 774 | "requires": { 775 | "mime-db": "1.48.0" 776 | } 777 | }, 778 | "ms": { 779 | "version": "2.0.0", 780 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 781 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 782 | }, 783 | "negotiator": { 784 | "version": "0.6.2", 785 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 786 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 787 | }, 788 | "node-os-utils": { 789 | "version": "1.3.5", 790 | "resolved": "https://registry.npmjs.org/node-os-utils/-/node-os-utils-1.3.5.tgz", 791 | "integrity": "sha512-bIJIlk+hA+7/ATnu3sQMtF697iw9T/JksDhKMe9uENG0OhzIG7hLM6fbcyu18bOuajlYWnSlj0IhDo2q7k0ebg==" 792 | }, 793 | "on-finished": { 794 | "version": "2.3.0", 795 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 796 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 797 | "requires": { 798 | "ee-first": "1.1.1" 799 | } 800 | }, 801 | "parseurl": { 802 | "version": "1.3.3", 803 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 804 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 805 | }, 806 | "path-to-regexp": { 807 | "version": "0.1.7", 808 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 809 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 810 | }, 811 | "proxy-addr": { 812 | "version": "2.0.7", 813 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 814 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 815 | "requires": { 816 | "forwarded": "0.2.0", 817 | "ipaddr.js": "1.9.1" 818 | } 819 | }, 820 | "qs": { 821 | "version": "6.7.0", 822 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 823 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 824 | }, 825 | "range-parser": { 826 | "version": "1.2.1", 827 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 828 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 829 | }, 830 | "raw-body": { 831 | "version": "2.4.0", 832 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 833 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 834 | "requires": { 835 | "bytes": "3.1.0", 836 | "http-errors": "1.7.2", 837 | "iconv-lite": "0.4.24", 838 | "unpipe": "1.0.0" 839 | } 840 | }, 841 | "safe-buffer": { 842 | "version": "5.1.2", 843 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 844 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 845 | }, 846 | "safer-buffer": { 847 | "version": "2.1.2", 848 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 849 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 850 | }, 851 | "send": { 852 | "version": "0.17.1", 853 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 854 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 855 | "requires": { 856 | "debug": "2.6.9", 857 | "depd": "~1.1.2", 858 | "destroy": "~1.0.4", 859 | "encodeurl": "~1.0.2", 860 | "escape-html": "~1.0.3", 861 | "etag": "~1.8.1", 862 | "fresh": "0.5.2", 863 | "http-errors": "~1.7.2", 864 | "mime": "1.6.0", 865 | "ms": "2.1.1", 866 | "on-finished": "~2.3.0", 867 | "range-parser": "~1.2.1", 868 | "statuses": "~1.5.0" 869 | }, 870 | "dependencies": { 871 | "ms": { 872 | "version": "2.1.1", 873 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 874 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 875 | } 876 | } 877 | }, 878 | "serve-static": { 879 | "version": "1.14.1", 880 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 881 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 882 | "requires": { 883 | "encodeurl": "~1.0.2", 884 | "escape-html": "~1.0.3", 885 | "parseurl": "~1.3.3", 886 | "send": "0.17.1" 887 | } 888 | }, 889 | "setprototypeof": { 890 | "version": "1.1.1", 891 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 892 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 893 | }, 894 | "statuses": { 895 | "version": "1.5.0", 896 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 897 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 898 | }, 899 | "toidentifier": { 900 | "version": "1.0.0", 901 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 902 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 903 | }, 904 | "type-is": { 905 | "version": "1.6.18", 906 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 907 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 908 | "requires": { 909 | "media-typer": "0.3.0", 910 | "mime-types": "~2.1.24" 911 | } 912 | }, 913 | "unpipe": { 914 | "version": "1.0.0", 915 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 916 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 917 | }, 918 | "utils-merge": { 919 | "version": "1.0.1", 920 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 921 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 922 | }, 923 | "vary": { 924 | "version": "1.1.2", 925 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 926 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 927 | }, 928 | "xxhashjs": { 929 | "version": "0.2.2", 930 | "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", 931 | "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", 932 | "requires": { 933 | "cuint": "^0.2.2" 934 | } 935 | } 936 | } 937 | } 938 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "duino-coin-pools", 3 | "version": "3.3.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node src/index" 8 | }, 9 | "author": "Bilaboz", 10 | "license": "MIT", 11 | "dependencies": { 12 | "axios": "^0.21.1", 13 | "express": "^4.17.1", 14 | "node-os-utils": "^1.3.5", 15 | "xxhashjs": "^0.2.2", 16 | "chalk": "4.1.2", 17 | "sync-request": "^6.1.0", 18 | "form-data": "4.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/connectionHandler.js: -------------------------------------------------------------------------------- 1 | /* Duino-Coin Connection handler 2 | For documention about these functions see 3 | https://github.com/revoxhere/duino-coin/blob/useful-tools 4 | 2019-2022 Duino-Coin community */ 5 | 6 | const fs = require("fs"); 7 | const crypto = require("crypto"); 8 | const log = require("./logging"); 9 | const mining = require("./mining"); 10 | const { exec } = require("child_process"); 11 | const bans = require("../config/bans.json"); 12 | const { motd, serverVersion } = require("../config/config.json"); 13 | 14 | if (bans.bannedIPs.length > 10) { 15 | bans.bannedIPs = []; 16 | fs.writeFileSync( 17 | "./config/bans.json", 18 | JSON.stringify(bans, null, 0) 19 | ); 20 | log.info(`Cleared bans`); 21 | } 22 | 23 | const ban_ip = (ip) => { 24 | // uncomment the correct command for your firewall 25 | //const cmd = `csf -td ${ip}`; //csf 26 | //const cmd = `iptables -A INPUT -s ${ip} -j DROP`; //iptables 27 | //const cmd = `sudo ufw deny from ${ip} to any`; //ufw 28 | const cmd = '' //none 29 | 30 | if (cmd) { 31 | exec(cmd, (error) => { 32 | if (error) { 33 | log.warning(`Error banning ${ip}: ${error}`); 34 | return; 35 | } else { 36 | log.warning(`Banned ${ip}`); 37 | } 38 | }); 39 | } 40 | }; 41 | 42 | const handle = (conn) => { 43 | conn.id = crypto.randomBytes(8).toString("hex"); 44 | try { 45 | conn.setTimeout(30 * 1000); 46 | conn.setEncoding("ascii"); 47 | conn.write(serverVersion + "\n"); 48 | } catch (err) { 49 | return conn.destroy(); 50 | } 51 | 52 | conn.on("close", () => { 53 | try { 54 | delete mining.stats.minersStats[conn.id]; 55 | } catch (err) {} 56 | try { 57 | mining.stats.workers[conn.remoteAddress] -= 1; 58 | if (mining.stats.workers[conn.remoteAddress] <= 0) 59 | delete mining.stats.workers[conn.remoteAddress]; 60 | } catch (err) {} 61 | try { 62 | mining.stats.usrWorkers[conn.username] -= 1; 63 | if (mining.stats.usrWorkers[conn.username] <= 0) 64 | delete mining.stats.usrWorkers[conn.username]; 65 | } catch (err) {} 66 | }); 67 | 68 | conn.on("error", (err) => { 69 | if (err.code !== "ECONNRESET") {} 70 | }); 71 | 72 | conn.on("timeout", () => { 73 | conn.destroy(); 74 | }); 75 | 76 | conn.on("data", function mainListener(data) { 77 | data = data.trim().split(","); 78 | 79 | if (!conn.remoteAddress || data.length > 6) { 80 | conn.write("BAD,Incorrect data\n"); 81 | return conn.destroy(); 82 | } 83 | 84 | /* IP ban check */ 85 | if ( 86 | bans.bannedIPs.includes(conn.remoteAddress) && 87 | conn.remoteAddress != "127.0.0.1" 88 | ) { 89 | conn.write("BAD,User banned\n"); 90 | ban_ip(conn.remoteAddress); 91 | return conn.destroy(); 92 | } 93 | 94 | /* Username ban check */ 95 | if ( 96 | bans.bannedUsernames.includes(data[1]) && 97 | conn.remoteAddress != "127.0.0.1" && 98 | !bans.bannedIPs.includes(conn.remoteAddress) 99 | ) { 100 | bans.bannedIPs.push(conn.remoteAddress); 101 | try { 102 | fs.writeFileSync( 103 | "./config/bans.json", 104 | JSON.stringify(bans, null, 0) 105 | ); 106 | } catch (err) { 107 | console.log(err); 108 | } 109 | conn.write("BAD,User banned\n"); 110 | ban_ip(conn.remoteAddress); 111 | return conn.destroy(); 112 | } 113 | 114 | if (data[0] === "JOB") { 115 | if (!data[1]) { 116 | conn.write("BAD,No username specified\n"); 117 | return conn.destroy(); 118 | } else { 119 | if (!conn.username) conn.username = data[1]; 120 | else if (conn.username != data[1]) { 121 | return conn.destroy(); 122 | } 123 | mining.miningHandler(conn, data, mainListener, false); 124 | } 125 | } else if (data[0] === "MOTD") { 126 | conn.write(motd); 127 | } 128 | }); 129 | }; 130 | 131 | module.exports = handle; 132 | -------------------------------------------------------------------------------- /src/dashboard.js: -------------------------------------------------------------------------------- 1 | /* Duino-Coin Pool dashboard generator 2 | For documention about these functions see 3 | https://github.com/revoxhere/duino-coin/blob/useful-tools 4 | 2019-2022 Duino-Coin community */ 5 | 6 | const express = require("express"); 7 | const log = require("./logging"); 8 | const app = express(); 9 | 10 | app.get("/ping", async (req, res) => { 11 | require("./index"); 12 | res.json({ 13 | result: "Pong!", 14 | success: true, 15 | }); 16 | }); 17 | 18 | app.listen(8080).on("error", function (err) { 19 | log.warning(`Ping listener is probably already running (${err})`); 20 | }); 21 | log.info("Ping listener on port 8080 enabled"); 22 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* Duino-Coin Pool 2 | For documention about these functions see 3 | https://github.com/revoxhere/duino-coin/blob/useful-tools 4 | 2019-2022 Duino-Coin community */ 5 | 6 | const net = require("net"); 7 | const handle = require("./connectionHandler"); 8 | const sync = require("./sync"); 9 | const { spawn } = require("child_process"); 10 | const log = require("./logging"); 11 | const { use_ngrok, port, host, autoRestart } = require("../config/config.json"); 12 | 13 | connections = 0; 14 | 15 | if (use_ngrok) { 16 | ngrok = spawn(`./ngrok`, [`tcp`, `-region`, `eu`, `${port}`]); 17 | 18 | ngrok.stderr.on("data", (data) => { 19 | log.error(`Ngrok stderr: ${data}`); 20 | process.exit(-1); 21 | }); 22 | 23 | ngrok.on("error", (err) => { 24 | log.error(`Ngrok error: ${err}`); 25 | process.exit(-1); 26 | }); 27 | 28 | ngrok.on("close", (code) => { 29 | log.error(`Ngrok exited (code ${code})`); 30 | spawn(`sudo pkill ngrok`); 31 | process.exit(-1); 32 | }); 33 | } 34 | 35 | sync.updatePoolReward(); 36 | sync.login(); 37 | 38 | require("./dashboard"); 39 | 40 | const server = net.createServer(handle); 41 | server.listen(port, host, 0, () => { 42 | log.info(`Server listening on port ${port}\n`); 43 | }); 44 | 45 | process.once("SIGINT", async () => { 46 | log.warning( 47 | "SIGINT detected, closing the server and logging out the pool..." 48 | ); 49 | await sync.logout(); 50 | server.close(); 51 | process.exit(0); 52 | }); 53 | 54 | process.once("SIGTERM", async () => { 55 | log.warning( 56 | "SIGTERM detected, closing the server and logging out the pool..." 57 | ); 58 | await sync.logout(); 59 | server.close(); 60 | process.exit(0); 61 | }); 62 | 63 | setInterval(() => { 64 | server.getConnections((error, count) => { 65 | if (!error) { 66 | connections = count; 67 | log.info(`Connected clients: ${count}`); 68 | } 69 | }); 70 | }, 10000); 71 | 72 | if (autoRestart > 0) { 73 | log.info(`Autorestarter enabled (every ${autoRestart} minutes)`); 74 | setTimeout(function () { 75 | log.info(`Restarting`); 76 | if (use_ngrok) ngrok.kill(); 77 | process.exit(1); 78 | }, autoRestart * 1000 * 60); 79 | } 80 | -------------------------------------------------------------------------------- /src/kolka.js: -------------------------------------------------------------------------------- 1 | /* Duino-Coin Kolka algorithms and formulas 2 | For documention about these functions see 3 | https://github.com/revoxhere/duino-coin/blob/useful-tools 4 | 2019-2022 Duino-Coin community */ 5 | 6 | const poolRewards = require("../config/poolRewards.json"); 7 | const highestPCdiff = poolRewards["NET"]["difficulty"] * 5.19202; 8 | const highestESPdiff = poolRewards["ESP32"]["difficulty"]; 9 | const highestAVRdiff = poolRewards["DUE"]["difficulty"]; 10 | const gpuMiningPercentage = 11 | poolRewards["EXTREME"]["kolka_decrease_perc"] * 0.01; 12 | const pcMiningPercentage = poolRewards["NET"]["kolka_decrease_perc"] * 0.01; 13 | const espMiningPercentage = poolRewards["ESP32"]["kolka_decrease_perc"] * 0.01; 14 | const avrMiningPercentage = poolRewards["AVR"]["kolka_decrease_perc"] * 0.01; 15 | 16 | function V1(hashrate, difficulty, workers, reward_div) { 17 | let output; 18 | 19 | if (workers > 4) { 20 | workers = workers - 3; 21 | } else { 22 | workers = 1; 23 | } 24 | 25 | try { 26 | output = Math.log(hashrate) / reward_div; 27 | } catch (err) { 28 | return 0; 29 | } 30 | 31 | if (difficulty > highestPCdiff) { 32 | // GPU, Extreme PC 33 | output = 2 * (output * Math.pow(gpuMiningPercentage, workers - 1)); 34 | } else if (difficulty > highestESPdiff) { 35 | // PC 36 | output = 2 * (output * Math.pow(pcMiningPercentage, workers - 1)); 37 | } else if (difficulty > highestAVRdiff) { 38 | // ESP 39 | output = 2 * (output * Math.pow(espMiningPercentage, workers - 1)); 40 | } else { 41 | // AVR 42 | output = 2 * (output * Math.pow(avrMiningPercentage, workers - 1)); 43 | } 44 | 45 | return output; 46 | } 47 | 48 | function V2(currDiff) { 49 | switch (currDiff) { 50 | case "AVR": 51 | return "MEGA"; 52 | case "MEGA": 53 | return "ARM"; 54 | case "ARM": 55 | return "DUE"; 56 | case "DUE": 57 | return "ESP8266"; 58 | case "ESP8266": 59 | return "ESP8266H"; 60 | case "ESP8266H": 61 | return "ESP32"; 62 | 63 | case "ESP8266N": 64 | return "ESP8266NH"; 65 | case "ESP8266NH": 66 | return "ESP32"; 67 | 68 | case "ESP32": 69 | return "LOW"; 70 | case "LOW": 71 | return "MEDIUM"; 72 | case "MEDIUM": 73 | return "NET"; 74 | case "NET": 75 | return "EXTREME"; 76 | case "EXTREME": 77 | return "EXTREME"; 78 | } 79 | } 80 | 81 | function V2_REVERSE(currDiff) { 82 | switch (currDiff) { 83 | case "AVR": 84 | return "AVR"; 85 | case "MEGA": 86 | return "AVR"; 87 | case "ARM": 88 | return "MEGA"; 89 | case "DUE": 90 | return "ARM"; 91 | 92 | case "ESP8266NH": 93 | return "ESP8266N"; 94 | case "ESP8266N": 95 | return "ESP8266N"; 96 | 97 | case "ESP8266H": 98 | return "ESP8266"; 99 | case "ESP8266": 100 | return "ESP8266"; 101 | 102 | case "ESP32": 103 | return "ESP32"; 104 | case "LOW": 105 | return "LOW"; 106 | case "MEDIUM": 107 | return "LOW"; 108 | case "NET": 109 | return "MEDIUM"; 110 | case "EXTREME": 111 | return "NET"; 112 | } 113 | } 114 | 115 | function V3(sharetime, expectedSharetime, difficulty) { 116 | const p = 2 - sharetime / expectedSharetime; 117 | let newDifficulty = difficulty; 118 | 119 | if (p < 1 || p > 1.1) { 120 | newDifficulty = difficulty * p; 121 | 122 | if (newDifficulty < 0) { 123 | newDifficulty = 124 | Math.floor(parseInt(difficulty / (Math.abs(p) + 2)) * 0.9) + 1; 125 | } else if (newDifficulty === 0) { 126 | newDifficulty = difficulty * 0.5; 127 | } 128 | } 129 | 130 | if (newDifficulty <= 2500) newDifficulty = 2500; 131 | 132 | return parseInt(newDifficulty); 133 | } 134 | 135 | function V4(sharetime, expectedTestSharetime) { 136 | const p = sharetime / expectedTestSharetime; 137 | 138 | if (p > 1.5) { 139 | return { 140 | rejected: true, 141 | penalty: V1(0, sharetime, 0, 0, true), 142 | }; 143 | } else { 144 | return { 145 | rejected: false, 146 | }; 147 | } 148 | } 149 | 150 | module.exports = { 151 | V1, 152 | V2, 153 | V2_REVERSE, 154 | V3, 155 | V4, 156 | }; 157 | -------------------------------------------------------------------------------- /src/logging.js: -------------------------------------------------------------------------------- 1 | /* Duino-Coin Pool logging system 2 | For documention about these functions see 3 | https://github.com/revoxhere/duino-coin/blob/useful-tools 4 | 2019-2022 Duino-Coin community */ 5 | 6 | const chalk = require("chalk"); 7 | const blue = chalk.blue; 8 | const red = chalk.bold.red; 9 | const green = chalk.green; 10 | const orange = chalk.hex("#FFA500"); 11 | 12 | const { poolName } = require("../config/config.json"); 13 | 14 | const info = (msg) => { 15 | console.log(`${poolName}: ${new Date().toLocaleString()} ` + blue(msg)); 16 | }; 17 | 18 | const success = (msg) => { 19 | console.log(`${poolName}: ${new Date().toLocaleString()} ` + green(msg)); 20 | }; 21 | 22 | const warning = (msg) => { 23 | console.log(`${poolName}: ${new Date().toLocaleString()} ` + orange(msg)); 24 | }; 25 | 26 | const error = (msg) => { 27 | console.log(`${poolName}: ${new Date().toLocaleString()} ` + red(msg)); 28 | }; 29 | 30 | module.exports = { 31 | info, 32 | success, 33 | warning, 34 | error, 35 | }; 36 | -------------------------------------------------------------------------------- /src/mining.js: -------------------------------------------------------------------------------- 1 | /* Duino-Coin Mining handler 2 | For documention about these functions see 3 | https://github.com/revoxhere/duino-coin/blob/useful-tools 4 | 2019-2023 Duino-Coin community */ 5 | 6 | const crypto = require("crypto"); 7 | const request = require('sync-request'); 8 | const kolka = require("./kolka"); 9 | const log = require("./logging"); 10 | const { 11 | poolName, 12 | maxWorkers, 13 | blockReward, 14 | initialBlockHash, 15 | updateMinersStatsEvery, 16 | serverVersion, 17 | max_shares_per_minute, 18 | } = require("../config/config.json"); 19 | const poolRewards = require("../config/poolRewards.json"); 20 | const algo = "DUCO-S1"; 21 | 22 | let lastBlockhash = initialBlockHash; 23 | globalBlocks = []; 24 | let workers = {}; 25 | let usrWorkers = {}; 26 | let minersStats = {}; 27 | let cachedItems = {}; 28 | let balancesToUpdate = {}; 29 | let globalShares = { 30 | increase: 0, 31 | total: 0, 32 | }; 33 | if (!max_shares_per_minute) { 34 | let max_shares_per_minute = 80; 35 | } 36 | 37 | const getDiff = (poolRewards, textDiff) => { 38 | try { 39 | let { difficulty } = poolRewards[textDiff]; 40 | return difficulty; 41 | } catch (err) { 42 | console.log(err); 43 | } 44 | }; 45 | 46 | const checkWorkers = (ipWorkers, usrWorkers, serverMiners, username) => { 47 | if (maxWorkers <= 0) return false; 48 | 49 | if (Math.max(ipWorkers, usrWorkers, serverMiners) > maxWorkers) { 50 | /* Check if user has worker limit upgrades */ 51 | if (!cachedItems.hasOwnProperty(username) && getRand(50) != 10) { 52 | res = request('GET', `https://server.duinocoin.com/v2/users/${username}`) 53 | userItems = JSON.parse(res.getBody('utf8')).result.items; 54 | cachedItems[username] = userItems; 55 | } else { 56 | userItems = cachedItems[username]; 57 | } 58 | 59 | if (userItems.includes(10) && userItems.includes(11)) { 60 | if (Math.max(ipWorkers, usrWorkers, serverMiners) > 125) { 61 | return true 62 | } 63 | } else if (userItems.includes(11)) { 64 | if (Math.max(ipWorkers, usrWorkers, serverMiners) > 100) { 65 | return true 66 | } 67 | } else if (userItems.includes(10)) { 68 | if (Math.max(ipWorkers, usrWorkers, serverMiners) > 75) { 69 | return true 70 | } 71 | } else { 72 | return true 73 | } 74 | } 75 | return false; 76 | }; 77 | 78 | const receiveData = (conn) => { 79 | return new Promise((resolve) => { 80 | conn.on("data", function listener(data) { 81 | conn.removeListener("data", listener); 82 | resolve(data.trim()); 83 | }); 84 | }); 85 | }; 86 | 87 | const getRand = (max) => { 88 | try { 89 | return crypto.randomInt(max); 90 | } catch (err) { 91 | console.log(err); 92 | return Math.floor(Math.random() * max); 93 | } 94 | }; 95 | 96 | const percDiff = (a, b) => { 97 | return Math.floor(100 * Math.abs((a - b) / ((a + b) / 2))); 98 | }; 99 | 100 | const miningHandler = async (conn, data, mainListener, usingAVR) => { 101 | let random, newHash, reqDifficulty, miningKey; 102 | let this_miner_chipid, minerName, sharetime; 103 | let feedback_sent = 0; 104 | 105 | let isFirstShare = true; 106 | conn.acceptedShares = 0; 107 | conn.rejectedShares = 0; 108 | 109 | const username = data[1]; 110 | conn.username = username; 111 | conn.serverMiners = 0; 112 | conn.this_miner_id = 0; 113 | conn.lastminshares = 0; 114 | conn.lastsharereset = Math.floor(new Date() / 1000); 115 | conn.ping = 0; 116 | 117 | // remove the main listener to not re-trigger miningHandler() 118 | conn.removeListener("data", mainListener); 119 | while (true) { 120 | conn.reject_shares = false; 121 | conn.donate = false; 122 | 123 | if (isFirstShare) { 124 | reqDifficulty = data[2] ? data[2] : "NET"; 125 | 126 | if (reqDifficulty == "ESP32") worker_add = 0.5; 127 | else worker_add = 1; 128 | 129 | if (workers[conn.remoteAddress]) { 130 | workers[conn.remoteAddress] += worker_add; 131 | } else { 132 | workers[conn.remoteAddress] = worker_add; 133 | } 134 | 135 | if (usrWorkers[conn.username]) { 136 | usrWorkers[conn.username] += worker_add; 137 | } else { 138 | usrWorkers[conn.username] = worker_add; 139 | } 140 | 141 | if (conn.remoteAddress != "127.0.0.1") { 142 | conn.this_miner_id = Math.max( 143 | usrWorkers[conn.username], 144 | workers[conn.remoteAddress], 145 | conn.serverMiners 146 | ); 147 | } else { 148 | conn.this_miner_id = Math.max( 149 | usrWorkers[conn.username], 150 | conn.serverMiners 151 | ); 152 | } 153 | conn.setTimeout(90 * 1000); 154 | } else { 155 | data = await receiveData(conn); 156 | data = data.split(","); 157 | conn.ping = (new Date() - feedback_sent) / 1000; 158 | 159 | if (conn.overrideDifficulty) 160 | reqDifficulty = conn.overrideDifficulty; 161 | else if (usingAVR) reqDifficulty = "AVR"; 162 | else reqDifficulty = data[2] ? data[2] : "NET"; 163 | } 164 | 165 | if (data[3]) miningKey = Buffer.from(data[3]).toString("base64"); 166 | else miningKey = null; 167 | 168 | if (data[4]) { 169 | if (!data[4].match(/[A-Za-z0-9 .():@-]+/)) 170 | conn.iot_reading = "Error:incorrect data"; 171 | else if (data[4].length > 48) 172 | conn.iot_reading = "Error:data too long"; 173 | else conn.iot_reading = data[4]; 174 | } else conn.iot_reading = null; 175 | 176 | if (conn.remoteAddress != "127.0.0.1") { 177 | if ( 178 | await checkWorkers( 179 | workers[conn.remoteAddress], 180 | usrWorkers[conn.username], 181 | conn.serverMiners, 182 | conn.username 183 | ) 184 | ) { 185 | conn.reject_shares = "Too many workers"; 186 | } 187 | } else { 188 | if ( 189 | await checkWorkers( 190 | 0, 191 | usrWorkers[conn.username], 192 | conn.serverMiners, 193 | conn.username 194 | ) 195 | ) { 196 | conn.reject_shares = "Too many workers"; 197 | } 198 | } 199 | 200 | if ( 201 | conn.this_miner_id > 202 | Math.max( 203 | workers[conn.remoteAddress], 204 | usrWorkers[conn.username], 205 | conn.serverMiners 206 | ) 207 | ) { 208 | conn.this_miner_id = Math.max( 209 | workers[conn.remoteAddress], 210 | usrWorkers[conn.username], 211 | conn.serverMiners 212 | ); 213 | } 214 | 215 | if (!poolRewards.hasOwnProperty(reqDifficulty)) reqDifficulty = "NET"; 216 | 217 | let diff = getDiff(poolRewards, reqDifficulty); 218 | 219 | if (!isFirstShare && diff > getDiff(poolRewards, "ESP8266NH")) { 220 | diff = kolka.V3(sharetime, expectedSharetime, diff); 221 | } 222 | 223 | random = getRand(diff * 100) + 1; 224 | 225 | const shasum = crypto.createHash("sha1"); 226 | shasum.update(lastBlockhash + random); 227 | newHash = shasum.digest("hex"); 228 | let job = [lastBlockhash, newHash.toString(), diff]; 229 | 230 | conn.write(job.toString() + "\n"); 231 | let job_sent = new Date(); 232 | let answer = await receiveData(conn); 233 | let answer_received = new Date(); 234 | answer = answer.split(","); 235 | 236 | sharetime = (answer_received - job_sent) / 1000; 237 | if (sharetime > conn.ping) { 238 | sharetime -= conn.ping; 239 | } 240 | 241 | reportedHashrate = parseFloat(answer[1]); 242 | if (reportedHashrate <= 0) { 243 | conn.reject_shares = "Invalid data"; 244 | } 245 | 246 | if (sharetime >= 0.5) { 247 | hashrate_calc = random / sharetime; 248 | } else { 249 | hashrate_calc = reportedHashrate; 250 | } 251 | 252 | conn.lastminshares++; 253 | 254 | let miner_res = parseInt(answer[0]); 255 | if (usingAVR) { 256 | miner_res = parseInt(answer[0], 2); 257 | } 258 | 259 | /* try { 260 | if (diff <= getDiff(poolRewards, "ESP8266NH")) { 261 | const r = /[+-]?([0-9][.][0-9])+/; 262 | if ( 263 | parseFloat(answer[2].match(r)) && 264 | parseFloat(answer[2].match(r)[0]) < 265 | parseFloat(serverVersion) 266 | ) { 267 | conn.reject_shares = "Outdated miner"; 268 | } 269 | } 270 | } catch (err) { 271 | conn.reject_shares = "No miner name"; 272 | } */ 273 | 274 | if (conn.lastminshares > max_shares_per_minute) { 275 | conn.reject_shares = "Too low difficulty"; 276 | } 277 | 278 | hashrateIsEstimated = false; 279 | hashrate = hashrate_calc; 280 | if (!reportedHashrate) hashrateIsEstimated = true; 281 | else hashrate = reportedHashrate; 282 | 283 | if (isFirstShare) this_miner_chipid = answer[4]; 284 | 285 | if ( 286 | diff > getDiff(poolRewards, "DUE") && 287 | diff <= getDiff(poolRewards, "ESP8266NH") && 288 | percDiff(hashrate, hashrate_calc) > 15 && 289 | sharetime >= 1.5 290 | ) { 291 | conn.reject_shares = "Modified hashrate"; 292 | } 293 | 294 | reward_div = poolRewards[reqDifficulty]["reward"]; 295 | maxHashrate = poolRewards[reqDifficulty]["max_hashrate"]; 296 | minHashrate = poolRewards[reqDifficulty]["min_hashrate"]; 297 | expectedSharetime = poolRewards[reqDifficulty]["expected_sharetime"]; 298 | blockProbability = poolRewards[reqDifficulty]["block_chance"]; 299 | reward = 0; 300 | 301 | if (conn.reject_shares) { 302 | conn.write(`BAD,${conn.reject_shares}\n`); 303 | feedback_sent = new Date(); 304 | } else if (hashrate < minHashrate) { 305 | conn.overrideDifficulty = kolka.V2_REVERSE(reqDifficulty); 306 | conn.rejectedShares++; 307 | conn.write("BAD,Too high starting difficulty\n"); 308 | feedback_sent = new Date(); 309 | } else if (hashrate >= maxHashrate) { 310 | conn.overrideDifficulty = kolka.V2(reqDifficulty); 311 | conn.rejectedShares++; 312 | conn.write("BAD,Too low starting difficulty\n"); 313 | feedback_sent = new Date(); 314 | } else if (miner_res === random) { 315 | conn.acceptedShares++; 316 | if (conn.acceptedShares > 10) { 317 | if (diff <= getDiff(poolRewards, "ESP8266NH")) { 318 | if (!this_miner_chipid) { 319 | conn.rejectedShares++; 320 | } else if (answer[4] != this_miner_chipid) { 321 | conn.rejectedShares++; 322 | } else { 323 | reward = kolka.V1( 324 | hashrate_calc, 325 | diff, 326 | conn.this_miner_id, 327 | reward_div 328 | ); 329 | } 330 | } else { 331 | reward = kolka.V1( 332 | hashrate_calc, 333 | diff, 334 | conn.this_miner_id, 335 | reward_div 336 | ); 337 | } 338 | } 339 | 340 | if (Math.floor(Math.random() * blockProbability) === 1) { 341 | reward += blockReward; 342 | 343 | const blockInfos = { 344 | timestamp: Date.now(), 345 | finder: conn.username, 346 | amount: reward, 347 | algo: "DUCO-S1", 348 | hash: newHash.toString(), 349 | }; 350 | 351 | globalBlocks.push(blockInfos); 352 | log.info(`Block found by ${conn.username}`); 353 | conn.write("BLOCK\n"); 354 | feedback_sent = new Date(); 355 | } else { 356 | conn.write("GOOD\n"); 357 | feedback_sent = new Date(); 358 | } 359 | } else { 360 | conn.rejectedShares++; 361 | conn.write("BAD,Incorrect result\n"); 362 | feedback_sent = new Date(); 363 | } 364 | 365 | if ( 366 | conn.acceptedShares > 0 && 367 | conn.acceptedShares % updateMinersStatsEvery === 0 368 | ) { 369 | if (balancesToUpdate[conn.username]) 370 | balancesToUpdate[conn.username] += reward; 371 | else balancesToUpdate[conn.username] = reward; 372 | 373 | try { 374 | minerName = answer[2].match(/[A-Za-z0-9 .()-]+/g).join(" "); 375 | } catch (err) { 376 | miner_name = "Unknown miner"; 377 | } 378 | 379 | let wallet_id; 380 | try { 381 | wallet_id = parseInt(answer[5]); 382 | } catch (err) { 383 | wallet_id = "None"; 384 | } 385 | 386 | let rigIdentifier; 387 | try { 388 | const cut_rigid = answer[3].split(":"); 389 | 390 | rigIdentifier = cut_rigid[0] 391 | .match(/[A-Za-z0-9 .()-]+/g) 392 | .join(" "); 393 | 394 | if (cut_rigid[1]) 395 | // mining key backwards compatibility for old miners 396 | miningKey = Buffer.from(cut_rigid[1]).toString("base64"); 397 | } catch (err) { 398 | rigIdentifier = "None"; 399 | } 400 | 401 | if (Math.floor(new Date() / 1000) - conn.lastsharereset >= 60) { 402 | conn.lastsharereset = Math.floor(new Date() / 1000); 403 | conn.lastminshares = 0; 404 | } 405 | 406 | if (conn.this_miner_id > 4) { 407 | kolka_drop = conn.this_miner_id - 3; 408 | } else { 409 | kolka_drop = 1; 410 | } 411 | 412 | minersStats[conn.id] = { 413 | u: conn.username, 414 | h: hashrateIsEstimated ? hashrate : reportedHashrate, 415 | s: sharetime, 416 | a: conn.acceptedShares, 417 | r: conn.rejectedShares, 418 | c: kolka_drop, 419 | al: algo, 420 | d: diff, 421 | p: poolName, 422 | sft: minerName, 423 | id: rigIdentifier, 424 | t: Math.floor(new Date() / 1000), 425 | wd: wallet_id, 426 | k: this_miner_chipid, 427 | pw: miningKey, 428 | ls: conn.lastminshares, 429 | it: conn.iot_reading, 430 | ip: conn.remoteAddress, 431 | pg: conn.ping, 432 | rw: reward, 433 | }; 434 | lastBlockhash = newHash; 435 | globalShares.increase += updateMinersStatsEvery; 436 | globalShares.total += updateMinersStatsEvery; 437 | } 438 | 439 | isFirstShare = false; 440 | } 441 | }; 442 | 443 | module.exports = { 444 | miningHandler, 445 | }; 446 | 447 | module.exports.stats = { 448 | workers, 449 | usrWorkers, 450 | minersStats, 451 | balancesToUpdate, 452 | globalShares, 453 | globalBlocks, 454 | }; 455 | -------------------------------------------------------------------------------- /src/sync.js: -------------------------------------------------------------------------------- 1 | /* Duino-Coin Pool Sync handler 2 | For documention about these functions see 3 | https://github.com/revoxhere/duino-coin/blob/useful-tools 4 | 2019-2022 Duino-Coin community */ 5 | 6 | const net = require("net"); 7 | const axios = require("axios"); 8 | const fs = require("fs"); 9 | const osu = require("node-os-utils"); 10 | 11 | const dns = require("dns"); 12 | const log = require("./logging"); 13 | const FormData = require("form-data"); 14 | const { 15 | poolID, 16 | poolVersion, 17 | serverVersion, 18 | serverIP, 19 | poolName, 20 | serverPort, 21 | base_sync_folder, 22 | syncTime, 23 | server_sync_url, 24 | timeout, 25 | use_ngrok, 26 | } = require("../config/config.json"); 27 | 28 | const SYNC_TIME = syncTime * 1000; 29 | const TIMEOUT = timeout * 1000; 30 | 31 | let ip, 32 | port, 33 | sync_count = 0; 34 | let poolRewards = {}; 35 | let serverMiners = {}; 36 | 37 | const wait = (milliseconds) => { 38 | return new Promise((resolve) => setTimeout(resolve, milliseconds)); 39 | }; 40 | 41 | const dns_lookup = async (ip) => { 42 | return new Promise((resolve, reject) => { 43 | dns.lookup(ip, (err, address) => { 44 | if (err) reject(err); 45 | resolve(address); 46 | }); 47 | }); 48 | }; 49 | 50 | const get_ngrok_ip = async () => { 51 | while (true) { 52 | await wait(1000); 53 | try { 54 | let res = await axios.get( 55 | "http://127.0.0.1:4040/api/tunnels/command_line", 56 | { 57 | timeout: TIMEOUT, 58 | } 59 | ); 60 | 61 | content = res.data.public_url.split(":"); 62 | ip = await dns_lookup(content[1].replace("//", "")); 63 | port = content[2]; 64 | 65 | log.info(`Fetched ngrok IP: ${ip}:${port}`); 66 | break; 67 | } catch (err) { 68 | log.error(`Can't fetch ngrok IP: ${err}`); 69 | } 70 | } 71 | }; 72 | 73 | const get_pool_ip = async () => { 74 | while (true) { 75 | try { 76 | let res = await axios.get("https://api.ipify.org", { 77 | timeout: TIMEOUT, 78 | }); 79 | 80 | ip = res.data; 81 | port = require("../config/config.json").port; 82 | 83 | log.info(`Fetched pool IP: ${ip}:${port}`); 84 | break; 85 | } catch (err) { 86 | log.error(`Can't fetch pool IP: ${err}`); 87 | } 88 | await wait(3000); 89 | } 90 | }; 91 | 92 | const login = async () => { 93 | if (use_ngrok) await get_ngrok_ip(); 94 | else await get_pool_ip(); 95 | 96 | const loginInfos = { 97 | host: ip, 98 | port: port, 99 | version: poolVersion, 100 | identifier: poolID, 101 | name: poolName, 102 | }; 103 | 104 | const socket = new net.Socket(); 105 | try { 106 | socket.setEncoding("utf-8"); 107 | socket.setTimeout(TIMEOUT); 108 | socket.connect(serverPort, serverIP); 109 | } catch (err) { 110 | log.error(`Socket error at connect: ${err}`); 111 | } 112 | 113 | socket.on("error", (err) => { 114 | log.error(`Socket error at login: ${err}`); 115 | }); 116 | 117 | socket.on("timeout", () => { 118 | log.error("Socket timeout at login"); 119 | }); 120 | 121 | socket.on("data", (data) => { 122 | if (data.startsWith(serverVersion)) { 123 | socket.write(`PoolLogin,${JSON.stringify(loginInfos)}`); 124 | } else if (data === "LoginOK") { 125 | log.success("Successfully logged in"); 126 | socket.destroy(); 127 | } else { 128 | log.warning(`Unknown error, server returned ${data} in login`); 129 | process.exit(-1); 130 | } 131 | }); 132 | 133 | sync(); 134 | //updateMinerCount(); 135 | }; 136 | 137 | const logout = () => { 138 | return new Promise((resolve, reject) => { 139 | const socket = new net.Socket(); 140 | try { 141 | socket.setEncoding("utf-8"); 142 | socket.setTimeout(TIMEOUT); 143 | socket.connect(serverPort, serverIP); 144 | } catch (err) { 145 | log.error(`Socket error at connect: ${err}`); 146 | } 147 | 148 | socket.on("error", (err) => { 149 | log.error(`Socket error at logout: ${err}`); 150 | reject(1); 151 | }); 152 | 153 | socket.on("timeout", () => { 154 | log.error(`Socket timeout at logout`); 155 | reject(1); 156 | }); 157 | 158 | socket.on("data", (data) => { 159 | if (data.startsWith(serverVersion)) { 160 | socket.write(`PoolLogout,${poolID}`); 161 | } else if (data === "LogoutOK") { 162 | log.success("Successfully logged out"); 163 | resolve(); 164 | } else { 165 | log.warning(`Unknown error, server returned ${data} in logout`); 166 | reject(data); 167 | } 168 | }); 169 | }); 170 | }; 171 | 172 | const sync = async () => { 173 | const mining = require("./mining"); 174 | const blockIncrease = mining.stats.globalShares.increase; 175 | require("./index"); 176 | 177 | if (use_ngrok && sync_count > 0 && sync_count % 50 == 0) 178 | await get_ngrok_ip(); 179 | 180 | const cpuUsage = await osu.cpu.usage(); 181 | let ramUsage = await osu.mem.info(); 182 | ramUsage = 100 - ramUsage.freeMemPercentage; 183 | 184 | mining.stats.globalShares.increase = 0; 185 | const syncData = { 186 | blocks: { 187 | blockIncrease: blockIncrease, 188 | bigBlocks: globalBlocks, 189 | }, 190 | cpu: cpuUsage, 191 | ram: ramUsage, 192 | connections: connections, 193 | }; 194 | 195 | fs.writeFileSync( 196 | `${base_sync_folder}/workers_${poolName}.json`, 197 | JSON.stringify(mining.stats.minersStats, null, 0) 198 | ); 199 | fs.writeFileSync( 200 | `${base_sync_folder}/rewards_${poolName}.json`, 201 | JSON.stringify(mining.stats.balancesToUpdate, null, 0) 202 | ); 203 | // fs.writeFileSync(`${base_sync_folder}/statistics_${poolName}.json`, JSON.stringify(syncData, null, 0)); 204 | 205 | const request_url = 206 | `https://${serverIP}/pool_sync/` + 207 | `?host=${ip}&port=${port}&version=${poolVersion}` + 208 | `&identifier=${poolID}&name=${poolName}` + 209 | `&blockIncrease=${blockIncrease}&bigBlocks=${globalBlocks}` + 210 | `&cpu=${cpuUsage}&ram=${ramUsage}&connections=${connections}`; 211 | 212 | let form = new FormData(); 213 | form.append( 214 | "rewards", 215 | fs.readFileSync(`${base_sync_folder}/rewards_${poolName}.json`), 216 | `rewards_${poolName}.json` 217 | ); 218 | form.append( 219 | "workers", 220 | fs.readFileSync(`${base_sync_folder}/workers_${poolName}.json`), 221 | `workers_${poolName}.json` 222 | ); 223 | // form.append('statistics', fs.readFileSync(`${base_sync_folder}/statistics_${poolName}.json`), `rewards_${poolName}.json`); 224 | 225 | try { 226 | sync_count++; 227 | axios 228 | .post(request_url, form, { 229 | headers: { 230 | ...form.getHeaders(), 231 | "Content-Type": "multipart/form-data", 232 | }, 233 | }) 234 | .then((response) => { 235 | if (response && response.data.success) { 236 | Object.keys(mining.stats.balancesToUpdate).forEach((k) => { 237 | delete mining.stats.balancesToUpdate[k]; 238 | }); 239 | globalBlocks = []; 240 | log.success(`Successfull sync #${sync_count}`); 241 | } else { 242 | log.warning( 243 | `Server error at sync #${sync_count}: ${response.data.message}` 244 | ); 245 | } 246 | }) 247 | .catch((err) => { 248 | log.error(`Socket error while syncing: ${err}`); 249 | }); 250 | } catch (err) { 251 | log.error(`Error while syncing: ${err}`); 252 | } 253 | 254 | setTimeout(sync, SYNC_TIME); 255 | }; 256 | 257 | const updatePoolReward = () => { 258 | axios 259 | .get(`${server_sync_url}/poolrewards.json`, { 260 | timeout: SYNC_TIME, 261 | }) 262 | .then((response) => { 263 | poolRewards = response.data; 264 | fs.writeFileSync( 265 | "./config/poolRewards.json", 266 | JSON.stringify(poolRewards, null, 2) 267 | ); 268 | log.success("Updated pool rewards file"); 269 | }) 270 | .catch((err) => { 271 | log.error(`Error updating pool rewards file: ${err}`); 272 | }); 273 | 274 | let bans = JSON.parse(fs.readFileSync("./config/bans.json", "utf8")); 275 | axios 276 | .get(`${server_sync_url}/bans.json`, { 277 | timeout: TIMEOUT, 278 | }) 279 | .then((response) => { 280 | bans.bannedUsernames = response.data; 281 | fs.writeFileSync( 282 | "./config/bans.json", 283 | JSON.stringify(bans, null, 2) 284 | ); 285 | log.success("Updated bans file"); 286 | }) 287 | .catch((err) => { 288 | log.error(`Error updating bans file: ${err}`); 289 | }); 290 | }; 291 | 292 | const updateMinerCount = () => { 293 | axios 294 | .get("https://server.duinocoin.com/statistics_miners", { 295 | timeout: TIMEOUT, 296 | }) 297 | .then((response) => { 298 | serverMiners = response.data.result; 299 | fs.writeFileSync( 300 | "./config/serverMiners.json", 301 | JSON.stringify(response.data.result, null, 2) 302 | ); 303 | log.success("Updated miner count file"); 304 | }) 305 | .catch((err) => { 306 | log.error(`Error updating miner count file: ${err}`); 307 | }); 308 | 309 | //setTimeout(updateMinerCount, SYNC_TIME); 310 | }; 311 | 312 | module.exports = { 313 | login, 314 | logout, 315 | updatePoolReward, 316 | poolRewards, 317 | serverMiners, 318 | }; 319 | -------------------------------------------------------------------------------- /utils/createPool.js: -------------------------------------------------------------------------------- 1 | const net = require("net"); 2 | const axios = require("axios"); 3 | 4 | const { poolPassword, serverIP, serverPort, port } = require("../config/config.json"); 5 | 6 | async function createPool() { 7 | const res = await axios.get("https://api.ipify.org/"); 8 | if (!res.data) { 9 | console.log("Error: can't get the pool IP"); 10 | process.exit(1); 11 | } 12 | const ip = res.data; 13 | 14 | const loginInfos = { 15 | name: "", 16 | host: ip, 17 | port: port, // port defined in config.json 18 | identifier: "", // put something like your hwid, this is like a password 19 | hidden: "False" 20 | }; 21 | 22 | const socket = new net.Socket(); 23 | socket.setEncoding("utf-8"); 24 | socket.connect(serverPort, serverIP); 25 | 26 | socket.on("error", (err) => { 27 | console.log(`Socket error at createPool: ${err}`); 28 | process.exit(-1); 29 | }); 30 | 31 | socket.on("timeout", () => { 32 | console.log("Socket timeout at createPool"); 33 | process.exit(-1); 34 | }); 35 | 36 | socket.on("data", (data) => { 37 | console.log(data); 38 | if (data.startsWith("2")) { 39 | socket.write(`PoolLoginAdd,${poolPassword},${JSON.stringify(loginInfos)}`); 40 | } else if (data === "LoginOK") { 41 | console.log("Pool successfully added"); 42 | } else { 43 | console.log(`Unknown error, server returned ${data} in createPool`); 44 | process.exit(-1); 45 | } 46 | }); 47 | } 48 | 49 | createPool(); --------------------------------------------------------------------------------