├── .gitignore ├── .gitmodules ├── HandyMiner.desktop ├── LICENSE ├── README.md ├── bower.json ├── css └── main.css ├── fonts ├── IBMPlexMono-Regular.ttf ├── IBMPlexSans-Italic.ttf ├── IBMPlexSans-Regular.ttf ├── IBMPlexSans-SemiBold.ttf ├── nitti-light-v500.woff └── nitti-medium-v500.woff ├── glsl ├── handshake.json └── handshake_complex.json ├── icons ├── app.icns ├── app.ico ├── app.png ├── app_128x128x32.png ├── app_16x16x32.png ├── app_256x256x32.png ├── app_32x32x32.png ├── app_48x48x32.png ├── app_png.png ├── document.icns ├── logo_min.svg └── qr.png ├── img ├── asic.jpg ├── cn.png ├── en.png ├── gpu.png └── gpu.svg ├── js ├── detectScreensaver.ps1 ├── enableThemes.js ├── l10n.csv └── main.js ├── miner.html └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | node_modules/ 3 | submodules/ 4 | externals/ 5 | package.json 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/HandyMiner-Goldshell-CLI"] 2 | path = submodules/HandyMiner-Goldshell-CLI 3 | url = https://github.com/HandyMiner/HandyMiner-Goldshell-CLI.git 4 | -------------------------------------------------------------------------------- /HandyMiner.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | # The type as listed above 3 | Type=Application 4 | 5 | # The version of the desktop entry specification to which this file complies 6 | Version=1.0 7 | 8 | # The name of the application 9 | Name=HandyMiner 10 | 11 | # A comment which can/will be used as a tooltip 12 | Comment="HandyMiner Goldshell GUI" 13 | 14 | # The path to the folder in which the executable is run 15 | Path=./ 16 | 17 | # The executable of the application, possibly with arguments. 18 | Exec=bash -c '"$(dirname "$1")"/HandyMiner' dummy %k 19 | 20 | # The name of the icon that will be used to display this entry 21 | Icon=app_256x256x32.png 22 | 23 | # Describes whether this application needs to be run in a terminal or not 24 | Terminal=false 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 HandyMiner 2 | Copyright 2020 Alex Smith - alex.smith@earthlab.tech 3 | Copyright 2020 Steven McKie - mckie@amentum.org 4 | Copyright 2020 Thomas Costanzo - stanzo89@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## HandyMiner-Goldshell GUI 4 | 5 | ### Download Latest Prebuilt HandyMiner-Goldshell-GUI in [Releases](https://github.com/HandyMiner/HandyMiner-Goldshell-GUI/releases) [Skips all the steps below] 6 | 7 | **[Note for Ubuntu users permissions on first run](#ubuntuFirstRun)** 8 | 9 | # Easy Installation 10 | Simple setup will have mining in **seconds** with an incredibly streamlined UX. 11 | 0. Paste your HNS address and create a worker name. 12 | 1. Select your pool (HNSPool/DXPool/F2Pool support by default) and press Play. 13 | 2. Your miner will immediate connect to the pool and start mining. 14 | 3. Note: Make sure your device is set to not sleep automatically, otherwise your miner will stop mining. 15 | 16 | 17 | ### HandyMiner GUI running with the Goldshell HS1 and multiple ASIC devices connected to a USB hub. 18 | ![imgur](https://i.imgur.com/WaWiwWT.jpg) 19 | 20 | ### HandyBrowser Support Telegram/Handshake Discussion: 21 | [🤝 HandshakeTalk Telegram](http://t.me/HandshakeTalk) 22 | 23 | ### Donate to the HandyMiner Team: 24 | 25 | ![alt text](./icons/qr.png) 26 | 27 | **HandyMiner Team Donation Address (HNS): ```hs1qwfpd5ukdwdew7tn7vdgtk0luglgckp3klj44f8```** 28 | 29 | **HandyMiner Team Donation Address (BTC): ```bc1qk3rk4kgek0hzpgs8qj4yej9j5fs7kcnjk7kuvt```** 30 | 31 | ### Building from Source 32 | 33 | First we will checkout HandyMiner-Goldshell-CLI into the project 34 | ```git submodule init``` 35 | ```git submodule update``` 36 | 37 | #### Install GUI/CLI-Miner Dependencies: 38 | 39 | Node.js is REQUIRED to build AND distribute. **Make sure to get the same version of Node.js as the NW.js version you download contains.** [Download Node.js](https://nodejs.org/) 40 | 41 | 0. ```npm install -g bower``` 42 | 1. ```bower install``` 43 | 2. ```npm install -g node-gyp``` will allow us to compile the native module ```serialport``` 44 | 3. ```npm install``` in this folder 45 | 4. ```mkdir externals```, then copy the node (or nodejs on linux) binary into the externals folder. Make sure this node is the same version as nw.js is running. [Download Node.js binaries here](https://nodejs.org/) 46 | 47 | #### Building for Mac 48 | 49 | 0. [Download NW.js](https://nwjs.io/) 50 | 1. Copy this repo to folder ```./nwjs.app/Contents/Resources/app.nw``` 51 | 2. Copy ./icons/app.icns, ./icons/document.icns to ```./nwjs.app/Contents/Resources/``` 52 | 3. To change the app display name, follow directions [here](https://nwjs.readthedocs.io/en/latest/For%20Users/Package%20and%20Distribute/) 53 | 54 | #### Building for Windows / Linux 55 | 56 | 0. [Download NW.js](https://nwjs.io/) 57 | 1. Checkout this repo into the NW.js directory next to nwjs.exe as a folder named ```package.nw``` 58 | 2. To change icons, use ResourceHacker to modify nw.exe icons to ./icons/app.ico 59 | 3. Further options to build/distribute [here](https://nwjs.readthedocs.io/en/latest/For%20Users/Package%20and%20Distribute/) 60 | 61 | ### Building native modules 62 | 0. (Windows) Install Python 2.7 63 | 1. **Make sure your system has the same node.js version as nw.js distributable contains**. This way we can ensure the distributable can run the pre-build native serialport module. 64 | 2. npm install in this directory, it will build module ```serialport``` that can run with NW.js 65 | 66 | 67 | 68 | ### Ubuntu Users First Run Permissions Step: 69 | 70 | ##### To add your user to the device group for non-sudo device access: 71 | ##### The easy way: ```sudo ./linux_grant_serial_permissions.sh``` in the release root, and then logout/login to the linux machine. 72 | 73 | ##### Or the less easier way: 74 | 75 | 0. Plug in the USB and run the command with the device ID listed in the error, like: 76 | ```ls -la /dev/ttyACM0``` 77 | It will output something like: 78 | ```crw-rw---- 1 root dialout 166, 0 Jul 18 18:06 /dev/ttyACM0``` 79 | Which in our case, the group is ```dialout``` 80 | 1. To add your username to the dialout group: 81 | ```sudo useradd -G dialout $USER``` (OR ON SOME SYSTEMS) ```sudo adduser $USER dialout``` 82 | 2. Now logout/login to the computer and voila, you can now mine without sudo! 83 | 84 | ##### [LICENSE](https://github.com/HandyMiner/HandyMiner-Goldshell-GUI/blob/master/LICENSE) 85 | 86 | HandyMiner-Goldshell-GUI - A Handshake Mining GUI for the Goldshell HS1 ASIC 87 | 88 | Copyright (C) 2020 HandyMiner 89 | - Alex Smith - alex.smith@earthlab.tech 90 | - Steven McKie - mckie@amentum.org 91 | - Thomas Costanzo - stanzo89@gmail.com 92 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HandyGUI", 3 | "description": "HandyMiner GUI", 4 | "main": "index.html", 5 | "authors": [ 6 | "Alex Smith " 7 | ], 8 | "license": "MIT", 9 | "homepage": "", 10 | "private": true, 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "test", 16 | "tests" 17 | ], 18 | "dependencies": { 19 | "numeral": "^2.0.6", 20 | "d3": "^5.9.2", 21 | "moment": "^2.24.0", 22 | "jquery": "^3.3.1", 23 | "three.js": "^0.93.0", 24 | "threejs-trackball-controls": "*", 25 | "Sortable": "sortablejs#^1.10.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Nitti Light"; 3 | font-weight: normal; 4 | src: url('../fonts/nitti-light-v500.eot') format('embedded-opentype'), 5 | url('../fonts/nitti-light-v500.woff') format('woff'); 6 | } 7 | 8 | @font-face { 9 | font-family: "Nitti Light"; 10 | font-weight: bold; 11 | src: url('../fonts/nitti-medium-v500.eot') format('embedded-opentype'), 12 | url('../fonts/nitti-medium-v500.woff') format('woff'), 13 | url('../fonts/nitti-medium-v500.woff2') format('woff2'); 14 | } 15 | 16 | @font-face { 17 | font-family: "IBM Plex Mono"; 18 | font-weight: 400; 19 | src: url('../fonts/IBMPlexMono-Regular.ttf') format('truetype'); 20 | } 21 | 22 | @font-face { 23 | font-family: "IBM Plex Mono"; 24 | font-style: italic; 25 | font-weight: 400; 26 | src: url('../fonts/IBMPlexMono-Italic.ttf'); 27 | } 28 | 29 | 30 | 31 | @font-face { 32 | font-family: "IBM Plex Mono"; 33 | font-style: italic; 34 | font-weight: 600; 35 | src: url('../fonts/IBMPlexMono-SemiBoldItalic.ttf'); 36 | } 37 | 38 | @font-face { 39 | font-family: "IBM Plex Sans"; 40 | font-weight: 400; 41 | src: url('../fonts/IBMPlexSans-Regular.ttf'); 42 | } 43 | 44 | @font-face { 45 | font-family: "IBM Plex Sans"; 46 | font-style: italic; 47 | font-weight: 400; 48 | src: url('../fonts/IBMPlexSans-Italic.ttf'); 49 | } 50 | 51 | @font-face { 52 | font-family: "IBM Plex Sans"; 53 | font-weight: 600; 54 | src: url('../fonts/IBMPlexSans-SemiBold.ttf'); 55 | } 56 | 57 | @font-face { 58 | font-family: "IBM Plex Sans"; 59 | font-style: italic; 60 | font-weight: 600; 61 | src: url('../fonts/IBMPlexSans-SemiBoldItalic.ttf'); 62 | } 63 | 64 | html,body{ 65 | margin:0; 66 | padding:0; 67 | background:#eee; 68 | color:#111; 69 | font-family: 'IBM Plex Mono', sans-serif; 70 | transition: color 0.4s linear; 71 | } 72 | html::-webkit-scrollbar { 73 | display: none; 74 | } 75 | html#dark, #dark body{ 76 | color:#ddd; 77 | background:#222; 78 | } 79 | html.inverted{ 80 | filter:invert(1.0); 81 | } 82 | html.inverted h3, html.inverted .minerMain ul li:first-child{ 83 | filter:invert(1.0); 84 | } 85 | 86 | h3{ 87 | font-size:24px; 88 | font-weight:300; 89 | margin:5px 0; 90 | color:#88b04b; 91 | font-weight:700; 92 | font-family:'IBM Plex Sans'; 93 | } 94 | #dark .minerForm{ 95 | background:#222; 96 | color:#ddd; 97 | } 98 | .minerForm{ 99 | position:absolute; 100 | width:calc(100% - 20px); 101 | height:calc(100% - 10px); 102 | padding: 0px 10px 10px 10px; 103 | position:absolute; 104 | top:0; 105 | left:0; 106 | transition: left 0.2s linear; 107 | background:#eee; 108 | z-index:1; 109 | transition: background 0.4s linear; 110 | } 111 | .minerForm#cyoa{ 112 | z-index:6; 113 | } 114 | .minerForm#cyoa2{ 115 | z-index:5; 116 | } 117 | .minerForm#poolUI{ 118 | z-index:4; 119 | } 120 | .minerForm#stratum{ 121 | z-index:3; 122 | } 123 | .minerForm#gpus{ 124 | display:none; 125 | z-index:2; 126 | } 127 | .minerForm#hsd{ 128 | z-index:1; 129 | } 130 | .minerForm.minerMain{ 131 | z-index:0; 132 | } 133 | #dark .escape{ 134 | color:#999; 135 | } 136 | .minerForm .escape{ 137 | font-size:12px; 138 | color:#444; 139 | right:5px; 140 | bottom: 5px; 141 | position:absolute; 142 | } 143 | 144 | #cyoa .option, #cyoa2 .option{ 145 | float:left; 146 | width:90%; 147 | padding: 12% 10px; 148 | margin: 5% calc(5% - 10px); 149 | background:#fff; 150 | border-radius:4px; 151 | text-align:center; 152 | font-family:'IBM Plex Sans'; 153 | font-size:18px; 154 | cursor:pointer; 155 | } 156 | #cyoa .option:hover, #cyoa2 .option:hover{ 157 | background:#ccc; 158 | } 159 | .infoElement#hsdAPI, .infoElement#wallet{ 160 | display:none; 161 | } 162 | .minerForm.hidden{ 163 | left:-100%; 164 | } 165 | #dark .infoElement{ 166 | background: linear-gradient(to bottom, rgba(255,255,255,0.09) 0%,rgba(255,255,255,0.02) 1%,rgba(255,255,255,0) 100%); 167 | } 168 | .infoElement{ 169 | float:left; 170 | width:calc(100% - 40px); 171 | padding: 10px; 172 | background: linear-gradient(to bottom, rgba(0,0,0,0.09) 0%,rgba(0,0,0,0.01) 1%,rgba(0,0,0,0) 100%); 173 | transition: background 0.4s linear; 174 | position:relative; 175 | } 176 | .infoElement.gpuIcon{ 177 | padding: 2px 10px 5px 10px; 178 | width: auto !important; 179 | margin-right: 10px; 180 | } 181 | .gpuIcon ul{ 182 | width: auto !important; 183 | } 184 | .gpuIcon.disconnected{ 185 | opacity:0.5; 186 | } 187 | .gpuIcon.disconnected li:first-child{ 188 | color:#aaa; 189 | } 190 | .gpuIcon.disconnected small:after{ 191 | content: ' [disconnected]'; 192 | color:#aaa; 193 | } 194 | .gpuIcon{ 195 | cursor:pointer; 196 | transition: background 0.2s linear; 197 | } 198 | #dark .gpuIcon:hover, #dark .gpuIcon.clicked{ 199 | background:rgba(0,180,0,0.1); 200 | } 201 | .gpuIcon:hover, .gpuIcon.clicked{ 202 | background:rgba(0,120,0,0.1); 203 | } 204 | #right .asic{ 205 | overflow:hidden; 206 | height:auto; 207 | transition: height linear 0.1s; 208 | } 209 | .asic.hidden{ 210 | height:0px !important; 211 | } 212 | .asic.disconnected{ 213 | opacity:0.5; 214 | } 215 | .asic.disconnected .name:after{ 216 | content: ' [disconnected]'; 217 | color:#aaa; 218 | font-size:10px; 219 | } 220 | .asic .name{ 221 | font-weight: 600; 222 | } 223 | .infoElement label{ 224 | display: block; 225 | margin-bottom: 5px; 226 | font-family:'IBM Plex Sans'; 227 | } 228 | .infoElement small{ 229 | font-weight:300; 230 | font-size:12px; 231 | display:block; 232 | color:#666; 233 | margin: 5px 0; 234 | } 235 | .gpuIcon small{ 236 | margin: 0px; 237 | font-size:10px; 238 | margin-top: -4px; 239 | line-height:0px; 240 | margin-left: 1px; 241 | } 242 | .stratumSubElement{ 243 | float:left; 244 | width:50%; 245 | } 246 | .stratumSubElement.hidden{ 247 | display:none; 248 | } 249 | .stratumSubElement input{ 250 | width: 95%; 251 | } 252 | #stratumUserPool.superwide{ 253 | width: 200%; 254 | } 255 | select#poolProvider{ 256 | margin: 2px 5px; 257 | padding: 2px 5px; 258 | float:left; 259 | } 260 | #dark #advancedPoolSettings{ 261 | color:#88b04b; 262 | } 263 | #advancedPoolSettings{ 264 | float:left; 265 | clear:both; 266 | color:#0066f0; 267 | font-size:11px; 268 | margin-top: 5px; 269 | } 270 | #gpuTemplate{ 271 | display:none; 272 | } 273 | .queryGPUs{ 274 | background: rgba(0,0,0,0.07); 275 | padding: 5px 6px; 276 | font-size: 16px; 277 | cursor: pointer; 278 | border: 1px solid #444; 279 | line-height: 20px; 280 | color: #333; 281 | position: absolute; 282 | right: 5px; 283 | bottom: 5px; 284 | } 285 | 286 | .queryGPUs:hover{ 287 | background:rgba(0,0,0,0.15); 288 | border: 1px solid #777; 289 | color:#555; 290 | } 291 | .queryGPUs span{ 292 | width: 20px; 293 | height: 20px; 294 | line-height: 20px; 295 | vertical-align: middle; 296 | text-align: center; 297 | background: rgba(0,0,120,0.5); 298 | border-radius: 30px; 299 | float: left; 300 | margin-right: 3px; 301 | font-weight:700; 302 | color:#ccc; 303 | } 304 | .queryGPUs:hover span{ 305 | background:rgba(0,0,180,0.5); 306 | color:#eee; 307 | } 308 | #dark input{ 309 | background: rgba(0,0,0,0.25); 310 | color:#ccc; 311 | } 312 | input{ 313 | background: rgba(0,0,0,0.15); 314 | border-radius: 3px; 315 | padding: 5px 10px; 316 | font-size: 20px; 317 | outline: none; 318 | color:#111; 319 | border:none; 320 | } 321 | #dark select{ 322 | background: rgba(0,0,0,0.25); 323 | color:#ccc; 324 | } 325 | #dark select option{ 326 | background-color:#111; 327 | } 328 | select { 329 | /*-webkit-appearance: none; 330 | -moz-appearance: none; 331 | appearance: none;*/ 332 | background: rgba(0,0,0,0.15); 333 | border: none; 334 | border-radius: 3px; 335 | padding: 5px 10px; 336 | font-size: 20px; 337 | outline: none; 338 | color:#111; 339 | font-family:'IBM Plex Sans'; 340 | } 341 | #dark .saveButton, #dark #logs .close, .nukeDocker{ 342 | background:rgba(0,0,0,0.25); 343 | color:#ccc; 344 | } 345 | .saveButton, #logs .close, .nukeDocker{ 346 | float:left; 347 | padding: 5px 10px; 348 | cursor:pointer; 349 | background:rgba(0,0,0,0.15); 350 | position:absolute; 351 | bottom:10px; 352 | left:10px; 353 | } 354 | .nukeDocker{ 355 | left: 10px; 356 | bottom: 75px; 357 | } 358 | .doRemoveThisTime, .doRemoveThisTime:hover{ 359 | background:rgba(200,200,0,1.0); 360 | } 361 | .saveButton:hover, #logs .close:hover, .nukeDocker:hover{ 362 | background:rgba(0,0,0,0.4); 363 | } 364 | #dark .minerMain .infoElement img{ 365 | filter:invert(0.8); 366 | } 367 | .minerMain .infoElement img{ 368 | width: 47px; 369 | filter:invert(0.08); 370 | float:left; 371 | margin-top:14px; 372 | transition: filter 0.4s linear; 373 | } 374 | .minerMain ul{ 375 | float:left; 376 | width:calc(100% - 50px); 377 | padding: 0; 378 | margin:0; 379 | list-style-type: none; 380 | line-height:40px; 381 | } 382 | .minerMain .infoElement{ 383 | width:calc(50% - 20px); 384 | margin:0; 385 | } 386 | .minerMain ul li{ 387 | float:left; 388 | padding:2px 3px; 389 | margin:0; 390 | font-size:20px; 391 | 392 | } 393 | .minerMain li.hashrate{ 394 | line-height:20px; 395 | clear:both; 396 | display:block; 397 | } 398 | .minerMain ul li:first-child{ 399 | color:#88b04b; 400 | padding-left:2px; 401 | font-weight:700; 402 | font-family:'IBM Plex Sans'; 403 | clear:right; 404 | display:block; 405 | } 406 | #dark .blocksAllTime, #dark .blocksToday{ 407 | background: linear-gradient(to bottom, rgba(255,255,255,0.09) 0%,rgba(255,255,255,0.02) 1%,rgba(255,255,255,0) 100%); 408 | } 409 | .blocksAllTime, .blocksToday{ 410 | float:left; 411 | width:50%; 412 | text-align: center; 413 | background:linear-gradient(to bottom, rgba(0,0,0,0.09) 0%,rgba(0,0,0,0.01) 1%,rgba(0,0,0,0) 100%); 414 | transition: background 0.4s linear; 415 | } 416 | .title{ 417 | float:right; 418 | margin-top: 31px; 419 | font-size:12px; 420 | color:#777; 421 | position:absolute; 422 | right: 15px; 423 | bottom: 15px; 424 | } 425 | .title a{ 426 | color:#0066f0; 427 | } 428 | a:visited{ 429 | color:#0066f0; 430 | } 431 | 432 | .syncedButton{ 433 | display:none; 434 | float: left; 435 | position: absolute; 436 | right: 10px; 437 | bottom: 11px; 438 | font-size: 12px; 439 | color: #333; 440 | background: #ccc; 441 | padding: 0px 5px 0 0; 442 | line-height:16px; 443 | vertical-align: middle; 444 | overflow:hidden; 445 | z-index:1; 446 | } 447 | 448 | .syncedButton .syncedIcon{ 449 | 450 | width: 15px; 451 | height: 15px; 452 | display: inline; 453 | padding: 0 4px; 454 | background: #ccc; 455 | color: #000; 456 | font-size:16px; 457 | transition: all 0.3s linear; 458 | } 459 | .syncedIcon.alert{ 460 | background: #eaa400; 461 | color: #ff0; 462 | } 463 | 464 | .syncedIcon.success{ 465 | width: 15px; 466 | height: 15px; 467 | display: inline; 468 | padding: 0 4px; 469 | background: #060; 470 | color: #0f0; 471 | line-height:21px; 472 | vertical-align: middle; 473 | } 474 | 475 | .blocksToday .number, .blocksAllTime .number{ 476 | font-size:86px; 477 | display:block; 478 | } 479 | #dark .blocksToday .label, #dark .blocksAllTime .label{ 480 | color:#777; 481 | } 482 | .blocksToday .label, .blocksAllTime .label{ 483 | color:#555; 484 | margin-top:-5px; 485 | font-size:12px; 486 | } 487 | .gpuStatus{ 488 | float:left; 489 | width:100%; 490 | } 491 | #modal{ 492 | float:left; 493 | position:absolute; 494 | z-index:100; 495 | background:rgba(255,255,255,0.7); 496 | width:100%; 497 | height:100%; 498 | display: none; 499 | } 500 | #modal .modalBody{ 501 | float:left; 502 | width:calc(80% - 20px); 503 | height:calc(80% - 20px); 504 | margin: 7% 10%; 505 | padding: 10px; 506 | border: 1px solid #ccc; 507 | background:#aaa; 508 | color:#eee; 509 | } 510 | .modalContent::-webkit-scrollbar { 511 | display: none; 512 | } 513 | .modalContent{ 514 | height:88%; 515 | border: 1px solid #777; 516 | overflow-y:scroll; 517 | background: #eee; 518 | color: #111; 519 | } 520 | .modalBody ul{ 521 | list-style-type:none; 522 | padding:0; 523 | margin:0; 524 | } 525 | .modalBody li{ 526 | margin: 0 3%; 527 | padding: 5px; 528 | background: linear-gradient(to bottom, rgba(255,255,255,0.3) 0%,rgba(255,255,255,0.07) 1%,rgba(0,0,0,0) 100%); 529 | border-bottom: 1px solid #ccc; 530 | width:94%; 531 | float:left; 532 | } 533 | .modalBody li .checkbox-label .checkbox-custom{ 534 | background:#ccc; 535 | } 536 | .modalBody .tLabel{ 537 | margin-left:5px; 538 | padding-top:2px; 539 | float:left; 540 | cursor:pointer; 541 | } 542 | .tLabel.isIntel{ 543 | color:#c00; 544 | } 545 | .modalBody small.isIntel{ 546 | color:#c00; 547 | float:left; 548 | margin-left:35px; 549 | font-size:12px; 550 | } 551 | .modalBody .button{ 552 | padding: 5px 10px; 553 | cursor:pointer; 554 | background:rgba(0,0,0,0.35); 555 | margin: 5px; 556 | float:left; 557 | } 558 | .modalBody .save{ 559 | background:rgba(0,0,0,0.35); 560 | } 561 | .modalBody .close{ 562 | 563 | } 564 | #dark .settings, #dark .startStop, #dark .logs{ 565 | background:rgba(51,51,51,0.8); 566 | color:#ccc; 567 | } 568 | .settings,.startStop,.logs{ 569 | position:absolute; 570 | top:0; 571 | right:0; 572 | padding:5px; 573 | font-size:14px; 574 | color:#555; 575 | text-align: center; 576 | /*line-height:20px;*/ 577 | padding-left:7px; 578 | cursor:pointer; 579 | background:rgba(255,255,255,0.8); 580 | } 581 | .logs.mac{ 582 | overflow:hidden; 583 | height:20px; 584 | padding-top: 3px; 585 | padding-bottom: 6px; 586 | } 587 | .logs.alerted{ 588 | background:#c00; 589 | } 590 | .settings{ 591 | right: 30px; 592 | padding-top:4px; 593 | padding-bottom: 7px; 594 | } 595 | .startStop{ 596 | top: 0px; 597 | right: 54px; 598 | padding: 4px 5px 18px 5px; 599 | /*padding-bottom: 0px;*/ 600 | color: #555; 601 | line-height: 19px; 602 | padding-left: 9px; 603 | } 604 | .startStop.playing{ 605 | font-size:12px; 606 | } 607 | #globalSettings{ 608 | float:right; 609 | top:0px; 610 | right:0px; 611 | width: 80px; 612 | height: 29px; 613 | overflow:hidden; 614 | position:absolute; 615 | } 616 | 617 | #dark .stats{ 618 | background: linear-gradient(to bottom, rgba(0,0,0,0.09) 0%,rgba(255,255,255,0.02) 1%,rgba(255,255,255,0) 100%); 619 | } 620 | .stats{ 621 | float:left; 622 | height:31%; 623 | width:100%; 624 | margin-top:10px; 625 | background:linear-gradient(to bottom, rgba(0,0,0,0.09) 0%,rgba(0,0,0,0.01) 1%,rgba(0,0,0,0) 100%); 626 | transition: background 0.4s linear; 627 | padding-top:5px; 628 | } 629 | .stats .halfChart{ 630 | float:left; 631 | width:50%; 632 | height:100%; 633 | 634 | } 635 | .stats .halfChart#left{ 636 | width:30%; 637 | } 638 | #right::-webkit-scrollbar { 639 | display: none; 640 | } 641 | .stats .halfChart#right{ 642 | width:70%; 643 | overflow-y:scroll; 644 | } 645 | 646 | .stats .halfChart#left .leftInfo{ 647 | text-align:center; 648 | margin: 14px 0px; 649 | } 650 | .stats .halfChart#left .leftInfo .value{ 651 | font-size:24px; 652 | font-weight:600; 653 | } 654 | /*#dark .stats .halfChart .asic{ 655 | background: linear-gradient(to bottom, rgba(255,255,255,0.09) 0%,rgba(255,255,255,0.02) 1%,rgba(255,255,255,0) 100%); 656 | } 657 | .stats .halfChart .asic{ 658 | background:linear-gradient(to bottom, rgba(0,0,0,0.09) 0%,rgba(0,0,0,0.01) 1%,rgba(0,0,0,0) 100%); 659 | transition: background 0.4s linear; 660 | }*/ 661 | .stats .halfChart svg{ 662 | float:left; 663 | width:100%; 664 | height:100%; 665 | } 666 | #right svg text.first{ 667 | /*fill:#fff;*/ 668 | } 669 | #dark #right svg text.front{ 670 | fill:#fff; 671 | } 672 | #dark #right svg text.back{ 673 | stroke:#333; 674 | } 675 | #right svg text.back{ 676 | stroke:#fff; 677 | } 678 | #right svg.temp path{ 679 | opacity:0.35; 680 | transition: opacity 0.25s linear; 681 | } 682 | /*#right svg.fans path, #right svg.hashrate path.first{*/ 683 | #right svg path{ 684 | opacity:0.45; 685 | transition: opacity 0.25s linear; 686 | } 687 | /*#right svg.temp:hover path, #right svg.fans:hover path, #right svg.hashrate:hover path.first{*/ 688 | #right g:hover path, #right rect:hover path{ 689 | opacity:1.0; 690 | } 691 | } 692 | path.line{ 693 | stroke:#666; 694 | fill:#fff; 695 | } 696 | path.localHR{ 697 | stroke:#88b04b; 698 | stroke-width:1.5px; 699 | fill:none; 700 | } 701 | html.inverted path.localHR{ 702 | filter:invert(1.0); 703 | } 704 | #dark .stats .label{ 705 | color:#888; 706 | } 707 | .stats .label{ 708 | color:#555; 709 | margin-bottom: 5px; 710 | padding-left:5px; 711 | } 712 | .stats svg text, .stats svg line, .stats svg .tick{ 713 | fill:#555; 714 | } 715 | .stats svg line{ 716 | stroke:#555; 717 | } 718 | html.inverted g.locaHRAxis text, html.inverted g.localHRAxis line{ 719 | filter:invert(1.0); 720 | } 721 | g.localHRAxis text, g.localHRAxis line{ 722 | fill:#00aa00; 723 | } 724 | g.localHRAxis line{ 725 | stroke:#88b04b; 726 | } 727 | .label small{ 728 | font-size: 10px; 729 | } 730 | input#hsdMinerWallet, input#hsdMinerWalletPool{ 731 | width:100%; 732 | } 733 | 734 | #dark #logs{ 735 | background:rgba(21,21,21,0.95); 736 | } 737 | #logs{ 738 | float:left; 739 | width:100%; 740 | height:100%; 741 | background:rgba(255,255,255,0.85); 742 | z-index:10; 743 | position:absolute; 744 | left:0; 745 | top:0; 746 | transition: all 0.1s linear; 747 | } 748 | #logs.hidden{ 749 | left:-110%; 750 | } 751 | #logs pre{ 752 | float:left; 753 | width: 94%; 754 | color:#0c0; 755 | margin: 1% 2%; 756 | padding: 10px; 757 | background:rgba(0,0,0,0.88); 758 | height:calc(100% - 120px); 759 | overflow-y:scroll; 760 | overflow-x: auto; 761 | white-space: pre-wrap; 762 | white-space: -moz-pre-wrap; 763 | white-space: -pre-wrap; 764 | white-space: -o-pre-wrap; 765 | word-wrap: break-word; 766 | } 767 | #dark #logs .label{ 768 | color:#999; 769 | } 770 | #logs .label{ 771 | float:left; 772 | width:calc(100% - 40px); 773 | padding: 0px 20px; 774 | margin-top: 5px; 775 | } 776 | #dark #logs .close{ 777 | background:rgba(140,140,140,0.15); 778 | } 779 | #logs .close{ 780 | right: 20px; 781 | bottom: 10px; 782 | left: auto; 783 | } 784 | #logs.required .close{ 785 | display:none; 786 | } 787 | .infoElement#difficulty small{ 788 | font-size: 14px; 789 | color: #555; 790 | } 791 | .checkboxWrapper{ 792 | float:left; 793 | width:100%; 794 | background:#ccc; 795 | border-radius: 4px; 796 | border: 1px solid #aaa; 797 | padding: 5px 12px; 798 | } 799 | .checkboxWrapper label{ 800 | cursor:pointer; 801 | } 802 | 803 | 804 | .infoElement .checkbox-container, .modalContent .checkbox-container { 805 | float: left; 806 | width: auto; 807 | box-sizing: border-box; 808 | text-align:center; 809 | padding:10px 0px 6px 0px; 810 | } 811 | .infoElement .circular-container, .modalContent .circular-container { 812 | background-color:#0067FF; 813 | } 814 | 815 | .input-title { 816 | clear: both; 817 | padding: 22px 0px 0px 0px; 818 | font-size: 16px; 819 | color: rgba(255,255,255,.6); 820 | font-weight: 300; 821 | } 822 | 823 | 824 | 825 | 826 | /* Styling Checkbox Starts */ 827 | .checkbox-label { 828 | display: block; 829 | position: relative; 830 | margin: 0px 5px 0px 0px; 831 | float:left; 832 | cursor: pointer; 833 | font-size: 22px; 834 | line-height: 24px; 835 | height: 24px; 836 | width: 24px; 837 | } 838 | #fflabel{ 839 | float: left; 840 | margin-left: 11px; 841 | font-size: 20px; 842 | } 843 | 844 | .checkbox-label input { 845 | position: absolute; 846 | opacity: 0; 847 | cursor: pointer; 848 | } 849 | 850 | .checkbox-label .checkbox-custom { 851 | position: absolute; 852 | top: 0px; 853 | left: 0px; 854 | height: 24px; 855 | width: 24px; 856 | background-color: transparent; 857 | border-radius: 5px; 858 | transition: all 0.3s ease-out; 859 | -webkit-transition: all 0.3s ease-out; 860 | -moz-transition: all 0.3s ease-out; 861 | -ms-transition: all 0.3s ease-out; 862 | -o-transition: all 0.3s ease-out; 863 | border: 2px solid #FFFFFF; 864 | background:#999; 865 | } 866 | 867 | 868 | .checkbox-label input:checked ~ .checkbox-custom { 869 | background-color: #FFFFFF; 870 | border-radius: 5px; 871 | -webkit-transform: rotate(0deg) scale(1); 872 | -ms-transform: rotate(0deg) scale(1); 873 | transform: rotate(0deg) scale(1); 874 | opacity:1; 875 | border: 2px solid #FFFFFF; 876 | } 877 | 878 | 879 | .checkbox-label .checkbox-custom::after { 880 | position: absolute; 881 | content: ""; 882 | left: 12px; 883 | top: 12px; 884 | height: 0px; 885 | width: 0px; 886 | border-radius: 5px; 887 | border: solid #009BFF; 888 | border-width: 0 3px 3px 0; 889 | -webkit-transform: rotate(0deg) scale(0); 890 | -ms-transform: rotate(0deg) scale(0); 891 | transform: rotate(0deg) scale(0); 892 | opacity:1; 893 | transition: all 0.3s ease-out; 894 | -webkit-transition: all 0.3s ease-out; 895 | -moz-transition: all 0.3s ease-out; 896 | -ms-transition: all 0.3s ease-out; 897 | -o-transition: all 0.3s ease-out; 898 | } 899 | 900 | 901 | .checkbox-label input:checked ~ .checkbox-custom::after { 902 | -webkit-transform: rotate(45deg) scale(1); 903 | -ms-transform: rotate(45deg) scale(1); 904 | transform: rotate(45deg) scale(1); 905 | opacity:1; 906 | left: 8px; 907 | top: 3px; 908 | width: 6px; 909 | height: 12px; 910 | border: solid #009BFF; 911 | border-width: 0 2px 2px 0; 912 | background-color: transparent; 913 | border-radius: 0; 914 | } 915 | 916 | 917 | 918 | /* For Ripple Effect */ 919 | .checkbox-label .checkbox-custom::before { 920 | position: absolute; 921 | content: ""; 922 | left: 10px; 923 | top: 10px; 924 | width: 0px; 925 | height: 0px; 926 | border-radius: 5px; 927 | border: 2px solid #FFFFFF; 928 | -webkit-transform: scale(0); 929 | -ms-transform: scale(0); 930 | transform: scale(0); 931 | } 932 | 933 | .checkbox-label input:checked ~ .checkbox-custom::before { 934 | left: -3px; 935 | top: -3px; 936 | width: 24px; 937 | height: 24px; 938 | border-radius: 5px; 939 | -webkit-transform: scale(3); 940 | -ms-transform: scale(3); 941 | transform: scale(3); 942 | opacity:0; 943 | z-index: 999; 944 | transition: all 0.3s ease-out; 945 | -webkit-transition: all 0.3s ease-out; 946 | -moz-transition: all 0.3s ease-out; 947 | -ms-transition: all 0.3s ease-out; 948 | -o-transition: all 0.3s ease-out; 949 | } 950 | 951 | 952 | 953 | 954 | /* Style for Circular Checkbox */ 955 | .checkbox-label .checkbox-custom.circular { 956 | border-radius: 50%; 957 | border: 2px solid #FFFFFF; 958 | } 959 | 960 | .checkbox-label input:checked ~ .checkbox-custom.circular { 961 | background-color: #FFFFFF; 962 | border-radius: 50%; 963 | border: 2px solid #FFFFFF; 964 | } 965 | .checkbox-label input:checked ~ .checkbox-custom.circular::after { 966 | border: solid #0067FF; 967 | border-width: 0 2px 2px 0; 968 | } 969 | .checkbox-label .checkbox-custom.circular::after { 970 | border-radius: 50%; 971 | } 972 | 973 | .checkbox-label .checkbox-custom.circular::before { 974 | border-radius: 50%; 975 | border: 2px solid #FFFFFF; 976 | } 977 | 978 | .checkbox-label input:checked ~ .checkbox-custom.circular::before { 979 | border-radius: 50%; 980 | } 981 | 982 | #loading{ 983 | position:absolute; 984 | width:64px; 985 | height:64px; 986 | padding:16px; 987 | border: 1px solid #ccc; 988 | background:rgba(50,50,50,0.7); 989 | left: calc( 50% - 40px); 990 | top: calc(50% - 40px); 991 | z-index:50; 992 | } 993 | 994 | .lds-grid { 995 | display: inline-block; 996 | position: relative; 997 | width: 64px; 998 | height: 64px; 999 | } 1000 | .lds-grid div { 1001 | position: absolute; 1002 | width: 13px; 1003 | height: 13px; 1004 | border-radius: 50%; 1005 | background: #fff; 1006 | animation: lds-grid 1.2s linear infinite; 1007 | } 1008 | .lds-grid div:nth-child(1) { 1009 | top: 6px; 1010 | left: 6px; 1011 | animation-delay: 0s; 1012 | } 1013 | .lds-grid div:nth-child(2) { 1014 | top: 6px; 1015 | left: 26px; 1016 | animation-delay: -0.4s; 1017 | } 1018 | .lds-grid div:nth-child(3) { 1019 | top: 6px; 1020 | left: 45px; 1021 | animation-delay: -0.8s; 1022 | } 1023 | .lds-grid div:nth-child(4) { 1024 | top: 26px; 1025 | left: 6px; 1026 | animation-delay: -0.4s; 1027 | } 1028 | .lds-grid div:nth-child(5) { 1029 | top: 26px; 1030 | left: 26px; 1031 | animation-delay: -0.8s; 1032 | } 1033 | .lds-grid div:nth-child(6) { 1034 | top: 26px; 1035 | left: 45px; 1036 | animation-delay: -1.2s; 1037 | } 1038 | .lds-grid div:nth-child(7) { 1039 | top: 45px; 1040 | left: 6px; 1041 | animation-delay: -0.8s; 1042 | } 1043 | .lds-grid div:nth-child(8) { 1044 | top: 45px; 1045 | left: 26px; 1046 | animation-delay: -1.2s; 1047 | } 1048 | .lds-grid div:nth-child(9) { 1049 | top: 45px; 1050 | left: 45px; 1051 | animation-delay: -1.6s; 1052 | } 1053 | @keyframes lds-grid { 1054 | 0%, 100% { 1055 | opacity: 1; 1056 | } 1057 | 50% { 1058 | opacity: 0.5; 1059 | } 1060 | } 1061 | 1062 | #dark #settingsOptions{ 1063 | background:rgba(51,51,51,0.85); 1064 | box-shadow:-1px 2px 7px #000; 1065 | } 1066 | #dark #settingsOptions li{ 1067 | border-bottom: 1px solid #555; 1068 | } 1069 | #settingsOptions{ 1070 | position:absolute; 1071 | top:30px; 1072 | right:31px; 1073 | width: 150px; 1074 | text-align:center; 1075 | background:rgba(200,200,200,0.8); 1076 | box-shadow:-1px 2px 3px #ccc; 1077 | display:none; 1078 | } 1079 | #settingsOptions ul{ 1080 | padding:0; 1081 | list-style-type:none; 1082 | width:100%; 1083 | float:left; 1084 | margin:0; 1085 | } 1086 | #settingsOptions ul li{ 1087 | float:left; 1088 | padding:3px 0; 1089 | margin:0; 1090 | border-bottom: 1px solid #888; 1091 | width:100%; 1092 | cursor:pointer; 1093 | } 1094 | #settingsOptions li:hover{ 1095 | background:#666; 1096 | color:#fff; 1097 | } 1098 | #settingsOptions li#gpuSettings{ 1099 | display:none; 1100 | } 1101 | .settings.hovered{ 1102 | background:rgba(200,200,200,0.8); 1103 | } 1104 | #settingsOptions ul li:last-child{ 1105 | border-bottom:none !important; 1106 | } 1107 | #dark #introLogo{ 1108 | background:#222; 1109 | } 1110 | #introLogo{ 1111 | position:absolute; 1112 | z-index:100; 1113 | background:#eee; 1114 | width:100%; 1115 | height:100%; 1116 | top:0; 1117 | left:0; 1118 | overflow:hidden; 1119 | } 1120 | #introLogo.hidden{ 1121 | display:none; 1122 | } 1123 | 1124 | .gpuPlatformNote.err{ 1125 | color:#aa0000; 1126 | } 1127 | .languageIcon{ 1128 | float: left; 1129 | width: 50%; 1130 | cursor:pointer; 1131 | background-position:center center; 1132 | background-size: 24px 24px; 1133 | background-repeat:no-repeat; 1134 | height:24px; 1135 | 1136 | } 1137 | .languageIcon#cn{ 1138 | background-image:url('../img/cn.png'); 1139 | } 1140 | .languageIcon#en{ 1141 | background-image:url('../img/en.png'); 1142 | } 1143 | #dark .languageIcon:hover, #dark .languageIcon.selected{ 1144 | background-color:rgba(255,255,255,0.25); 1145 | } 1146 | .languageIcon:hover, .languageIcon.selected{ 1147 | background-color:rgba(0,0,0,0.7) 1148 | } 1149 | 1150 | 1151 | -------------------------------------------------------------------------------- /fonts/IBMPlexMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/fonts/IBMPlexMono-Regular.ttf -------------------------------------------------------------------------------- /fonts/IBMPlexSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/fonts/IBMPlexSans-Italic.ttf -------------------------------------------------------------------------------- /fonts/IBMPlexSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/fonts/IBMPlexSans-Regular.ttf -------------------------------------------------------------------------------- /fonts/IBMPlexSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/fonts/IBMPlexSans-SemiBold.ttf -------------------------------------------------------------------------------- /fonts/nitti-light-v500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/fonts/nitti-light-v500.woff -------------------------------------------------------------------------------- /fonts/nitti-medium-v500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/fonts/nitti-medium-v500.woff -------------------------------------------------------------------------------- /icons/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app.icns -------------------------------------------------------------------------------- /icons/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app.ico -------------------------------------------------------------------------------- /icons/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app.png -------------------------------------------------------------------------------- /icons/app_128x128x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app_128x128x32.png -------------------------------------------------------------------------------- /icons/app_16x16x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app_16x16x32.png -------------------------------------------------------------------------------- /icons/app_256x256x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app_256x256x32.png -------------------------------------------------------------------------------- /icons/app_32x32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app_32x32x32.png -------------------------------------------------------------------------------- /icons/app_48x48x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app_48x48x32.png -------------------------------------------------------------------------------- /icons/app_png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/app_png.png -------------------------------------------------------------------------------- /icons/document.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/document.icns -------------------------------------------------------------------------------- /icons/logo_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /icons/qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/icons/qr.png -------------------------------------------------------------------------------- /img/asic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/img/asic.jpg -------------------------------------------------------------------------------- /img/cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/img/cn.png -------------------------------------------------------------------------------- /img/en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/img/en.png -------------------------------------------------------------------------------- /img/gpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HandyOSS/HandyMiner-Goldshell-GUI/8d317f8505054739f5877ec6c5c7d2d1a4c2f8ab/img/gpu.png -------------------------------------------------------------------------------- /img/gpu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/detectScreensaver.ps1: -------------------------------------------------------------------------------- 1 | Get-Process | Where {$_.ProcessName -Like "*scr"} -------------------------------------------------------------------------------- /js/enableThemes.js: -------------------------------------------------------------------------------- 1 | let isDarkMode = localStorage.getItem('isDarkMode') == 'true' ? true : false; 2 | if(isDarkMode){ 3 | document.getElementsByTagName('html')[0].setAttribute('id','dark'); 4 | } 5 | else{ 6 | document.getElementsByTagName('html')[0].setAttribute('id','light'); 7 | } 8 | 9 | let l10n = localStorage.getItem('l10n') != null ? localStorage.getItem('l10n') : 'en'; 10 | 11 | if(localStorage.getItem('l10n') == null){ 12 | //try to set from environment 13 | let lang = process.env.LANG; 14 | if(typeof lang != "undefined"){ 15 | if(lang.indexOf('en') == 0){ 16 | l10n = 'en'; 17 | } 18 | if(lang.indexOf('zh') == 0){ 19 | l10n = 'cn'; 20 | } 21 | } 22 | localStorage.setItem('l10n',l10n); 23 | } 24 | 25 | let ifs = require('fs'); 26 | 27 | const l10n_csv = ifs.readFileSync(nw.__dirname+'/js/l10n.csv','utf8'); 28 | 29 | let l10nData = {}; 30 | let csvData = l10n_csv.split('\n'); 31 | let keys = csvData[0].split(',').map(key=>{ 32 | return key.replace('\r','').trim(); 33 | }); 34 | 35 | csvData.slice(1).map(line=>{ 36 | let cells = line.split(','); 37 | let elemKey = cells[0]; 38 | l10nData[elemKey] = {}; 39 | cells.slice(1).map((val,i)=>{ 40 | l10nData[elemKey][keys[i+1]] = val.replace(/"/gi,""); 41 | }) 42 | }) 43 | localStorage.setItem('l10nData',JSON.stringify(l10nData)); 44 | -------------------------------------------------------------------------------- /js/l10n.csv: -------------------------------------------------------------------------------- 1 | element_id,en,cn 2 | label_sessionSolution,"Session Shares","提交" 3 | label_allTimeSolution,"Shares All Time","全部提交数" 4 | label_totalHR,"Total Hashrate","总算力" 5 | label_avgHR,"Avg Hashrate","平均算力" 6 | label_networkDiff,"Network Diff","网络难度" 7 | label_shareDiff,"Share Diff","矿池难度" 8 | miningMode,"Mining Settings","矿池设置" 9 | darkLightMode,"Dark Mode|Light Mode","夜间模式|亮模式" 10 | label_poolDetails,"Enter Mining Pool Details","矿池配置信息" 11 | label_poolProvider,"Mining Pool","矿池" 12 | label_other,"other","其他" 13 | advancedPoolSettings,"advanced|custom pool settings","高級|自定义设置" 14 | label_stratumHost,"Pool Stratum Custom Host","主机" 15 | label_stratumPort,"Pool Stratum Custom Port","端口" 16 | label_miningUser,"Mining User","用户名" 17 | label_miningPass,"Mining Password","密码" 18 | label_savePool,"Save Mining Pool Details","保存矿池详细信息" 19 | label_escHide,"[ESC to hide]","[按ESC键隐藏]" 20 | label_rawLogs,"Raw Miner Logs:","原始矿机日志" 21 | label_hideLogs,"Hide","隐藏" 22 | text.hashrate,"Hashrate","算力" 23 | text.workers,"Worker","旷工" 24 | text.temp,"Temperature","温度" 25 | text.fan,"Fan RPM","风扇转速" -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | let FE; 2 | const fs = require('fs'); 3 | const spawn = require('child_process').spawn; 4 | 5 | delete process.env.OPENSSL_CONF; 6 | 7 | $(document).ready(function(){ 8 | FE = new feApp(); 9 | }); 10 | class feApp{ 11 | constructor(){ 12 | /* 13 | note: leaving a lot of the docker cruft for integration with handybrowser later 14 | */ 15 | let appDirPath = process.env.HOME+'/Library/Application\ Support/HandyMiner/'; 16 | if(process.platform == 'win32'){ 17 | appDirPath = process.env.HOMEDRIVE+process.env.HOMEPATH+'/AppData/Local/HandyMiner/User\ Data/Default/'; 18 | } 19 | if(process.platform == 'linux'){ 20 | appDirPath = process.env.HOME+'/.config/HandyMiner/Default/'; 21 | } 22 | this.appDirPath = appDirPath; 23 | this.blocksSolvedLast24 = 0; 24 | this.allTimeBlocks = 0; 25 | let alltime = 0; 26 | try{ 27 | alltime = fs.readFileSync(this.appDirPath+'HandyMinerConfigs/allTimeBlocks.json','utf8'); 28 | let j = JSON.parse(alltime); 29 | alltime = parseFloat(j.count); 30 | } 31 | catch(e){ 32 | alltime = 0; 33 | } 34 | if(isNaN(alltime)){ 35 | alltime = 0; 36 | } 37 | this.l10nData = {} 38 | this.initl10n(); 39 | this.allTimeBlocks = alltime; 40 | let formatSmallSession = this.blocksSolvedLast24 > 1000 ? '0.0a' : '0a'; 41 | let formatSmallAllTime = this.allTimeBlocks > 1000 ? '0.0a' : '0a'; 42 | $('.blocksToday .number').html(numeral(this.blocksSolvedLast24).format(formatSmallSession).toUpperCase()) 43 | $('.blocksAllTime .number').html(numeral(this.allTimeBlocks).format(formatSmallAllTime).toUpperCase()); 44 | this.showMiningConsole(); 45 | //},4000) 46 | this.resizeStatsPanel(); 47 | nw.Window.get().on('resize',()=>{ 48 | this.resizeStatsPanel(); 49 | this.renderHashrate(); 50 | }) 51 | 52 | 53 | this.writeLinuxDesktopRunner(); 54 | //this.enableDarkLightOption(); 55 | this.isDisplaySleeping = false; 56 | this.detectDisplayIsSleeping(); 57 | } 58 | initl10n(){ 59 | if(localStorage.getItem('l10nData') != null){ 60 | let l10nData = JSON.parse(localStorage.getItem('l10nData')); 61 | this.l10nData = l10nData; 62 | } 63 | const _this = this; 64 | $('.languageIcon').off('click').on('click',function(){ 65 | let val = $(this).attr('id'); 66 | localStorage.setItem('l10n',val); 67 | $('.languageIcon').removeClass('selected'); 68 | $(this).addClass('selected'); 69 | _this.renderl10n(); 70 | _this.renderHashrate(); 71 | //console.log('set l10n',val) 72 | }) 73 | _this.renderl10n(); 74 | } 75 | renderl10n(){ 76 | let l10n = localStorage.getItem('l10n'); 77 | let darkLabel = 'Dark Mode'; 78 | let lightLabel = 'Light Mode'; 79 | $('.languageIcon').removeClass('selected') 80 | $('.languageIcon#'+l10n).removeClass('selected').addClass('selected') 81 | Object.keys(this.l10nData).map(id=>{ 82 | if(id.indexOf('text.') == -1 && id != 'darkLightMode'){ 83 | //console.log('l10n setting',id,this.l10nData[id][l10n]) 84 | $('#'+id).html(this.l10nData[id][l10n]); 85 | } 86 | if(id == 'darkLightMode'){ 87 | let modes = this.l10nData[id][l10n].split('|'); 88 | darkLabel = modes[0]; 89 | lightLabel = modes[1]; 90 | } 91 | 92 | 93 | }); 94 | this.enableDarkLightOption(darkLabel,lightLabel) 95 | } 96 | detectDisplayIsSleeping(){ 97 | return false; //deprecate in favor of eliminate d3 transitions 98 | if(process.platform.indexOf('darwin') >= 0){ 99 | this.detectDisplayInterval = setInterval(()=>{ 100 | let ioreg = spawn('ioreg',['-n', /*'AppleBacklightDisplay'*/ 'IODisplayWrangler']); 101 | let grep = spawn('grep',['-i', /*'brightness'*/ 'IOPowerManagement']) 102 | //, 'grep','-i', 'IOPowerManagement' 103 | ioreg.stdout.on('data',d=>{ 104 | grep.stdin.write(d.toString('utf8')); 105 | //fs.appendFileSync(nw.__dirname+'/screenStats.jsonl',d.toString('utf8')+'\n\n'); 106 | }) 107 | ioreg.on('close', (code) => { 108 | grep.stdin.end(); 109 | }); 110 | 111 | grep.stdout.on('data',d=>{ 112 | let data = d.toString('utf8'); 113 | data = data.split('"CurrentPowerState"')[1].split(',')[0].replace('=',''); 114 | data = parseInt(data); 115 | 116 | /*fs.appendFileSync(nw.__dirname+'/screenStats.jsonl',JSON.stringify({powerState:data})+'\n'); 117 | fs.appendFileSync(nw.__dirname+'/screenStats.jsonl',d.toString('utf8')+'\n');*/ 118 | if(data < 3){ 119 | //screen is asleep 120 | this.isDisplaySleeping = true; 121 | } 122 | else{ 123 | this.isDisplaySleeping = false; 124 | } 125 | }) 126 | },5000) 127 | } 128 | if(process.platform.indexOf('win32') >= 0){ 129 | //windows 130 | this.detectDisplayInterval = setInterval(()=>{ 131 | let resp = ''; 132 | let ps1 = spawn('powershell.exe',[nw.__dirname+'/js/detectScreensaver.ps1']); 133 | ps1.stdout.on('data',d=>{ 134 | resp += d.toString('utf8').trim(); 135 | 136 | }) 137 | ps1.on('close',d=>{ 138 | if(resp.length == 0){ 139 | //not going 140 | this.isDisplaySleeping = false; 141 | } 142 | else{ 143 | this.isDisplaySleeping = true; 144 | } 145 | }) 146 | },5000); 147 | } 148 | } 149 | enableDarkLightOption(darkLabel,lightLabel){ 150 | let isDarkMode = localStorage.getItem('isDarkMode') == 'true' ? true : false; 151 | let darkModeLabel = darkLabel; 152 | if(isDarkMode){ 153 | darkModeLabel = lightLabel; 154 | } 155 | 156 | $('#darkLightMode').html(darkModeLabel); 157 | $('#darkLightMode').off('click').on('click',()=>{ 158 | isDarkMode = localStorage.getItem('isDarkMode') == 'true' ? true : false; 159 | if(isDarkMode){ 160 | //is dark, toggle light 161 | $('html').attr('id','light'); 162 | $('#darkLightMode').html(darkLabel); 163 | localStorage.setItem('isDarkMode','false'); 164 | } 165 | else{ 166 | $('html').attr('id','dark'); 167 | $('#darkLightMode').html(lightLabel); 168 | localStorage.setItem('isDarkMode','true'); 169 | } 170 | }) 171 | } 172 | writeLinuxDesktopRunner(){ 173 | if(process.platform.indexOf('linux') == -1){ 174 | return; //not linux 175 | } 176 | let runnerPath = nw.App.getStartPath()+'/HandyMiner.desktop'; 177 | let execPath = nw.App.getStartPath(); 178 | if(global.__dirname.indexOf('package.nw') >= 0){ 179 | execPath = global.__dirname.split('/').slice(0,-1).join('/'); 180 | runnerPath = execPath+'/HandyMiner.desktop'; 181 | } 182 | let runnerText = fs.readFileSync(runnerPath,'utf8'); 183 | let lines = runnerText.split('\n').map(line=>{ 184 | if(line.indexOf('Icon=') == 0){ 185 | //target line, update icon w abs path 186 | return `Icon=${global.__dirname}/icons/app.png`; 187 | } 188 | else if(line.indexOf('Path=') == 0){ 189 | //update path 190 | return `Path=${execPath}`; 191 | } 192 | else if(line.indexOf('Exec=') == 0){ 193 | return `Exec=${execPath}/HandyMiner`; 194 | } 195 | else return line; 196 | }) 197 | fs.writeFileSync(runnerPath,lines.join('\n'),'utf8'); 198 | } 199 | resizeStatsPanel(){ 200 | let offset = Math.floor($('.stats').offset().top); 201 | $('.stats').css('height','calc(100% - '+offset+'px)'); 202 | } 203 | showMiningConsole(){ 204 | //nw.Window.get().focus(); 205 | if(typeof process.env.HOME == "undefined"){ 206 | if(typeof process.env.HOMEDRIVE && process.env.HOMEPATH){ 207 | process.env.HOME = process.env.HOMEDRIVE+process.env.HOMEPATH; 208 | } 209 | } 210 | 211 | this.network = 'main'; 212 | this.prodPort = '12937'; 213 | this.peersPort = '12938'; 214 | 215 | 216 | if(typeof process.env.HANDY_IS_MAINNET != "undefined"){ 217 | this.network = 'main'; 218 | this.prodPort = '12937'; 219 | this.peersPort = '12938'; 220 | } 221 | 222 | /* 223 | this.prodWallet = 'hs1qwfpd5ukdwdew7tn7vdgtk0luglgckp3klj44f8'; //placeholder/defaults 224 | */ 225 | this.macUseDocker = true; //tell mac users to start docker machines and whatnot.. 226 | /***********PRETTY PLS ******/ 227 | 228 | 229 | process.env.PATH += ':/usr/local/bin'; 230 | process.env.HANDYRAW = true; 231 | this.isFirstRunEver = false; //is this the first time we ran this? 232 | this.hashRates = {}; 233 | this.difficulty = {}; 234 | this.asicNames = {}; 235 | this.asicDisconnected = {}; 236 | this.minerLogs = ''; 237 | this.hsdLogs = '######################################\r\n'; 238 | this.hsdLogs +='############# HANDY MINER ############\r\n'; 239 | this.hsdLogs +='######################################\r\n'; 240 | this.logsVisible = false; 241 | this.miningMode = 'pool';//solo|pool 242 | this.soloStratumOption = 'local'; //local || remote 243 | this.awaitingConfirmation = false; 244 | this.hsdURL = '127.0.0.1:'+this.prodPort; 245 | this.hsdUser = 'earthlab'; 246 | /* 247 | this.hsdWallet = 'hs1qwfpd5ukdwdew7tn7vdgtk0luglgckp3klj44f8'; //placeholder/defaults 248 | */ 249 | this.defaultWallet = this.hsdWallet; 250 | this.hsdWalletPlaceHolder = this.hsdWallet; 251 | this.last100Blocks = {}; 252 | this.canFetchNetworkTimeline = false; 253 | 254 | this.initEvents(); 255 | //let config = fs.readFileSync('./HandyMiner/config.json','utf8'); 256 | let config; 257 | if(!fs.existsSync(this.appDirPath+'HandyMinerConfigs')){ 258 | fs.mkdirSync(this.appDirPath+'HandyMinerConfigs/'); 259 | this.isFirstRunEver = true; 260 | console.log('created ~/.HandyMiner dir'); 261 | $('#stratum.minerForm .saveButton').html('Save Stratum Details and Build Docker Node') 262 | //is new-ish 263 | } 264 | if(!fs.existsSync(this.appDirPath+'HandyMinerConfigs/config.json')){ 265 | config = { 266 | "asics":"-1", 267 | "mode":"pool", 268 | "host":"", 269 | "port":"", 270 | "stratum_user":"", 271 | "stratum_pass":"", 272 | "poolDifficulty":"-1", 273 | "muteFanfareSong":true, 274 | "network":this.network 275 | } 276 | $('.minerForm#poolUI').removeClass('hidden'); 277 | } 278 | else{ 279 | config = JSON.parse(fs.readFileSync(this.appDirPath+'HandyMinerConfigs/config.json','utf8')) 280 | //$('#gpuList').val(config.asics); 281 | //$('#gpuMfg option').removeAttr('selected'); 282 | //$('#gpuMfg option[value="'+config.gpu_mfg.toLowerCase()+'"]').attr('selected','selected'); 283 | $('#stratumHost').val(config.host); 284 | $('#stratumPort').val(config.port); 285 | $('#stratumHostPool').val(config.host); 286 | $('#stratumPortPool').val(config.port); 287 | $('#poolProvider option').removeAttr('selected'); 288 | $('#poolProvider option[value="'+config.host+'"]').attr('selected','selected'); 289 | let vals = [] 290 | $('#poolProvider option').each(function(){ 291 | vals.push($(this).val()) 292 | }) 293 | if(vals.indexOf(config.host) == -1){ 294 | $('#poolProvider option[value="other"]').attr('selected','selected'); 295 | $('.advancedStratumSetting').removeClass('hidden'); 296 | } 297 | $('#stratumUserPool').val(config.stratum_user); 298 | $('#stratumPassPool').val(config.stratum_pass); 299 | $('#stratumUser').val(config.stratum_user); 300 | $('#stratumPass').val(config.stratum_pass); 301 | $('#network option[value="'+config.network+'"]').attr('selected','selected'); 302 | $('#gpuPlatform option').removeAttr('selected'); 303 | //$('#gpuPlatform option[value="'+config.gpu_platform+'"]').attr('selected','selected'); 304 | //$('#intensity option[value="'+config.intensity+'"]').attr('selected','selected'); 305 | /*if(config.poolDifficulty){ 306 | $('#minerDifficulty').val(config.poolDifficulty); 307 | }*/ 308 | if(config.muteFanfareSong){ 309 | $('#muteFanfare').attr('checked','checked'); 310 | } 311 | /*config.gpus.split(',').map(function(item){ 312 | let $tmpl = $('#gpuTemplate').clone(); 313 | $tmpl.removeAttr('id'); 314 | $tmpl.attr('data-id',item); 315 | $tmpl.addClass('gpuIcon') 316 | $('li',$tmpl).eq(0).html('GPU'+item); 317 | $('li',$tmpl).eq(1).html('0MH'); 318 | $('.gpuStatus').append($tmpl); 319 | })*/ 320 | this.miningMode = config.mode || 'pool' 321 | } 322 | this.config = config; 323 | 324 | /*if(this.config.mode == 'pool'){ 325 | $('.blocksAllTime .label').html('Shares All Time'); 326 | $('.blocksToday .label').html('Session Shares') 327 | }*/ 328 | 329 | this.initLogo(); 330 | setTimeout(()=>{ 331 | 332 | this.hideLoading(); 333 | },3000); 334 | 335 | } 336 | initEvents(){ 337 | const _this = this; 338 | $('.nukeDocker').off('click').on('click',function(){ 339 | if(!$(this).hasClass('doRemoveThisTime')){ 340 | $('.nukeDocker').addClass('doRemoveThisTime'); 341 | $('.nukeDocker').html('Click again to confirm nuke. It will take 2-5 mins to reconstruct FYI.') 342 | } 343 | else{ 344 | //we actually nuke then. 345 | _this.nukeDockerContainer(); 346 | $('.nukeDocker').removeClass('doRemoveThisTime'); 347 | $('.nukeDocker').html('DONE! Docker will rebuild now.') 348 | setTimeout(function(){ 349 | $('.minerForm:not(#main)').addClass('hidden'); 350 | $('#logs').removeClass('hidden').removeClass('required'); 351 | _this.logsVisible = true; 352 | _this.pushToLogs('####DOCKER:: NUKING DOCKER CONTAINER...','stdout','hsd') 353 | },1000) 354 | setTimeout(function(){ 355 | $('.nukeDocker').html('Nuke and Rebuild Docker Machine') 356 | },5000) 357 | 358 | } 359 | 360 | }) 361 | $('#poolProvider').off('change').on('change',()=>{ 362 | let val = $('#poolProvider option:selected').val(); 363 | $('#stratumHostPool').val(val); 364 | //set ports automagically 365 | switch(val){ 366 | case 'hns.f2pool.com': 367 | $('#stratumPortPool').val('6000'); 368 | hidePass(); 369 | break; 370 | case 'stratum+tcp://handshake.6block.com': 371 | $('#stratumPortPool').val('7701'); 372 | hidePass(); 373 | break; 374 | case 'hns-us.ss.poolflare.com': 375 | $('#stratumPortPool').val('3355'); 376 | hidePass(); 377 | break; 378 | case 'stratum-us.hnspool.com': 379 | $('#stratumPortPool').val('3001'); 380 | $('#stratumUserPool').attr('placeholder','Account Username'); 381 | $('#stratumPassPool').attr('placeholder','Account Password'); 382 | $('#poolPass').removeClass('hidden'); 383 | $('#stratumUserPool').removeClass('superwide'); 384 | break; 385 | case 'hns.ss.dxpool.com': 386 | $('#stratumPortPool').val('3008'); 387 | $('#stratumUserPool').attr('placeholder','Account Username.WorkerName'); 388 | 389 | break; 390 | case 'other': 391 | $('#stratumPortPool').val('').attr('placeholder','Port'); 392 | $('#stratumHostPool').val('').attr('placeholder','127.0.0.1 or hns.somepool.com'); 393 | $('.advancedStratumSetting').removeClass('hidden'); 394 | break; 395 | } 396 | function hidePass(){ 397 | $('#stratumUserPool').attr('placeholder','HNS Address.[Insert Worker Name]'); 398 | $('#stratumPassPool').attr('placeholder','Anything'); 399 | $('#poolPass').addClass('hidden'); 400 | $('#stratumUserPool').addClass('superwide'); 401 | } 402 | }) 403 | let _useStratumAdvancedSettings = false; 404 | $('#advancedPoolSettings').off('click').on('click',()=>{ 405 | $('.stratumSubElement.advancedStratumSetting').toggleClass('hidden'); 406 | }); 407 | $('.saveButton').off('click').on('click',function(){ 408 | let host,port,stratumUser,stratumPass,hsdApiKey,hsdMinerWallet; 409 | switch($(this).parents('.minerForm').attr('id')){ 410 | case 'gpus': 411 | console.log('save gpus info'); 412 | let mfg = $('#gpuMfg option:selected').val(); 413 | let platformID = $('#gpuPlatform option:selected').val(); 414 | let gpus = $('#gpuList').val(); 415 | let intensity = $('#intensity option:selected').val(); 416 | let muteFanfare = $('#muteFanfare').is(':checked'); 417 | console.log('gpus isset',gpus); 418 | _this.config.gpu_mfg = mfg; 419 | _this.config.gpus = gpus; 420 | _this.config.gpu_platform = platformID; 421 | _this.config.intensity = intensity; 422 | _this.config.muteFanfareSong = muteFanfare; 423 | /* 424 | let $tmpl = $('#gpuTemplate').clone(); 425 | $tmpl.removeAttr('id'); 426 | let t = $tmpl.clone(); 427 | t.attr('data-id',v); 428 | t.addClass('gpuIcon'); 429 | $('li',t).eq(0).html('GPU'+v); 430 | $('li',t).eq(1).html('0MH'); 431 | $('.gpuStatus').append(t); 432 | */ 433 | let $tmpl = $('#gpuTemplate').clone(); 434 | $tmpl.removeAttr('id'); 435 | let gpuArr = gpus.split(','); 436 | $('.gpuStatus .gpuIcon').remove(); 437 | gpuArr.map(function(gpuID){ 438 | let t = $tmpl.clone(); 439 | t.attr('data-id',gpuID); 440 | t.addClass('gpuIcon'); 441 | $('li',t).eq(0).html('GPU'+gpuID); 442 | $('li',t).eq(1).html('--GH'); 443 | $('.gpuStatus').append(t); 444 | }) 445 | fs.writeFileSync(this.appDirPath+'HandyMinerConfigs/config.json',JSON.stringify(_this.config,null,2)); 446 | console.log('mfg',mfg,'plat',platformID,'gpus',gpus); 447 | break; 448 | 449 | case 'stratum': 450 | //console.log('save stratum info') 451 | console.log('save stratum info solo strat and mining mode',_this.soloStratumOption,_this.miningMode); 452 | console.log('yaaaaaaaaaaaaaaaaa') 453 | //check miningmode here 454 | //this.soloStratumOption 455 | if(_this.soloStratumOption == 'local' && _this.miningMode == 'solo'){ 456 | host = '127.0.0.1'; 457 | port = '3008'; 458 | stratumUser = 'earthlab'; 459 | stratumPass = 'earthlab'; 460 | } 461 | else{ 462 | host = $('#stratumHost').val() || '127.0.0.1'; 463 | port = $('#stratumPort').val() || '3008'; 464 | stratumUser = $('#stratumUser').val() || 'earthlab'; 465 | stratumPass = $('#stratumPass').val() || 'earthlab'; 466 | } 467 | _this.config.network = $('#network option:selected').val() || 'testnet'; 468 | 469 | let portO; 470 | let portP; 471 | let midChar = '0'; 472 | if(host == '127.0.0.1' || host.indexOf('192.168') >= 0){ 473 | midChar = '9'; 474 | } 475 | switch(_this.config.network){ 476 | case 'main': 477 | portO = '12'+midChar+'37'; 478 | portP = '12'+midChar+'38'; 479 | break; 480 | case 'testnet': 481 | portO = '13'+midChar+'37'; 482 | portP = '13'+midChar+'38'; 483 | break; 484 | case 'simnet': 485 | portO = '15'+midChar+'37'; 486 | portP = '15'+midChar+'38'; 487 | break; 488 | case 'regtest': 489 | portO = '14'+midChar+'37'; 490 | portP = '14'+midChar+'38'; 491 | break; 492 | } 493 | 494 | _this.peersPort = portP; 495 | _this.config.host = host; 496 | _this.config.port = port; 497 | _this.config.stratum_user = stratumUser; 498 | _this.config.stratum_pass = stratumPass; 499 | 500 | _this.hsdUser = _this.config.stratum_user; 501 | _this.hsdURL = $('#hsdAPIUrl').val() || _this.hsdURL; 502 | _this.config.hsdURL = _this.hsdURL; 503 | console.log('hsdrul???',_this.hsdURL); 504 | let parts = _this.hsdURL.split(':'); 505 | console.log('parts',parts); 506 | let pbase = parts.slice(0,-1); 507 | if(pbase.length == 0){ 508 | pbase = ['127.0.0.1']; 509 | } 510 | pbase = pbase.concat(portO).join(':') 511 | _this.hsdURL = pbase; 512 | 513 | hsdApiKey = $('#hsdAPIPass').val() || $('#stratumPass').val() || 'earthlab'; 514 | if(_this.soloStratumOption == 'local' && _this.miningMode == 'solo'){ 515 | hsdApiKey = 'earthlab'; 516 | } 517 | hsdMinerWallet = '';//$('#hsdMinerWallet').val().trim() || 'hs1qwfpd5ukdwdew7tn7vdgtk0luglgckp3klj44f8'; 518 | _this.hsdWallet = hsdMinerWallet; 519 | if(typeof hsdApiKey != "undefined" && typeof hsdMinerWallet != "undefined"){ 520 | _this.hsdConfig = {wallet:hsdMinerWallet,apiKey:hsdApiKey,url:_this.hsdURL}; 521 | fs.writeFileSync(this.appDirPath+'HandyMinerConfigs/hsdConfig.json',JSON.stringify(_this.hsdConfig)) 522 | } 523 | console.log('we want to restart docker now',_this.macUseDocker); 524 | if(_this.macUseDocker || process.platform.toLowerCase().indexOf('win') == 0){ 525 | console.log('should now restart docker container') 526 | _this.restartDockerContainer(true) 527 | } 528 | else{ 529 | _this.launchHSD(true); 530 | } 531 | 532 | $('.nukeDocker').removeClass('doRemoveThisTime'); 533 | $('.nukeDocker').html('Nuke and Rebuild Docker Machine') 534 | 535 | console.log('vals',host,port,stratumUser,stratumPass); 536 | break; 537 | 538 | case 'poolUI': 539 | console.log('save stratum info') 540 | host = $('#stratumHostPool').val() || '127.0.0.1'; 541 | port = $('#stratumPortPool').val() || '3008'; 542 | stratumUser = $('#stratumUserPool').val() || 'earthlab'; 543 | stratumPass = $('#stratumPassPool').val() || 'earthlab'; 544 | _this.config.host = host; 545 | _this.config.port = port; 546 | _this.config.stratum_user = stratumUser; 547 | _this.config.stratum_pass = stratumPass; 548 | _this.hsdUser = _this.config.stratum_user; 549 | _this.hsdURL = $('#hsdAPIUrlPool').val() || _this.hsdURL; 550 | _this.config.hsdURL = _this.hsdURL; 551 | 552 | hsdApiKey = $('#hsdAPIPassPool').val() || $('#stratumPassPool').val() || 'earthlab'; 553 | hsdMinerWallet = ''; 554 | //_this.hsdWallet = hsdMinerWallet; 555 | let poolDifficulty = '-1';//$('#minerDifficulty').val() || '-1'; 556 | _this.config.poolDifficulty = poolDifficulty; 557 | if(typeof hsdApiKey != "undefined" && typeof hsdMinerWallet != "undefined"){ 558 | _this.hsdConfig = {wallet:hsdMinerWallet,apiKey:hsdApiKey,url:_this.hsdURL}; 559 | fs.writeFileSync(_this.appDirPath+'HandyMinerConfigs/hsdConfig.json',JSON.stringify(_this.hsdConfig)) 560 | } 561 | 562 | fs.writeFileSync(_this.appDirPath+'HandyMinerConfigs/config.json',JSON.stringify(_this.config,null,2)); 563 | _this.launchHSD(); 564 | console.log('vals',host,port,stratumUser,stratumPass); 565 | break; 566 | default: 567 | 568 | break; 569 | } 570 | 571 | $(this).parents('.minerForm').addClass('hidden'); 572 | }); 573 | 574 | /*$('.settings').off('click').on('click',function(){ 575 | $('.minerForm').removeClass('hidden'); 576 | });*/ 577 | $('.settings').off('mouseenter').on('mouseenter',function(){ 578 | $('#settingsOptions').show(); 579 | $(this).addClass('hovered'); 580 | }); 581 | if(process.platform.toLowerCase().indexOf('darwin') >= 0){ 582 | $('.logs').addClass('mac'); 583 | } 584 | $('.startStop, .logs').off('mouseenter').on('mouseenter',function(){ 585 | $('#settingsOptions').hide(); 586 | $('.settings').removeClass('hovered'); 587 | }); 588 | $('#settingsOptions').off('mouseleave').on('mouseleave',function(){ 589 | $('#settingsOptions').hide(); 590 | $('.settings').removeClass('hovered'); 591 | }); 592 | $('#settingsOptions li#miningMode').off('click').on('click',function(){ 593 | var id = $(this).attr('id'); 594 | _this.hideLoading(); 595 | switch(id){ 596 | case 'miningMode': 597 | $('#poolUI').removeClass('hidden'); 598 | break; 599 | case 'gpuSettings': 600 | $('.minerForm#gpus').removeClass('hidden'); 601 | break; 602 | } 603 | }) 604 | $('.logs').off('click').on('click',function(){ 605 | _this.logsVisible = true; 606 | _this.hideLoading(); 607 | $('.logs').removeClass('alerted'); 608 | //$('#logs pre#logOutput').html(_this.hsdLogs); 609 | //$('#logs pre#logOutput')[0].scrollTop = $('#logs pre#logOutput')[0].scrollHeight; 610 | $('#logs').removeClass('hidden').removeClass('required'); 611 | }) 612 | $('.startStop').off('click').on('click',function(){ 613 | if($(this).hasClass('paused')){ 614 | //should play 615 | $(this).html('▌▌'); 616 | $(this).addClass('playing'); 617 | $(this).attr('title','pause miner'); 618 | $(this).removeClass('paused'); 619 | $('.gpuStatus .gpuIcon').remove(); 620 | _this.startMiner(); 621 | 622 | } 623 | else{ 624 | $(this).html('▶') 625 | $(this).addClass('paused'); 626 | $(this).attr('title','start miner'); 627 | $(this).removeClass('playing'); 628 | _this.stopMiner(); 629 | } 630 | }) 631 | this.startTimer(); 632 | nw.Window.get().on('close',function(){ 633 | if(typeof _this.hsdProcess != "undefined"){ 634 | _this.hsdProcess.kill(); 635 | } 636 | if(typeof _this.minerProcess != "undefined"){ 637 | try{ 638 | _this.minerProcess.stdin.write('dashboard sigint'); 639 | } 640 | catch(e){ 641 | 642 | } 643 | } 644 | if(typeof _this.detectDisplayInterval != "undefined"){ 645 | clearInterval(_this.detectDisplayInterval); 646 | } 647 | if(process.platform.toLowerCase().indexOf('win') == 0 || _this.macUseDocker){ 648 | 649 | _this.stopDockerMachine(); 650 | } 651 | this.close(true); 652 | }); 653 | 654 | $('#cyoa .option').off('click').on('click',function(){ 655 | let v = $(this).attr('id'); 656 | _this.miningMode = v; 657 | _this.config.mode = v; 658 | $('#cyoa').addClass('hidden'); 659 | switch(v){ 660 | case 'solo': 661 | //$('#poolUI').addClass('hidden') 662 | testHSD('solo'); 663 | break; 664 | case 'pool': 665 | testHSD('pool'); 666 | $('#cyoa2').addClass('hidden'); 667 | $('#stratum').addClass('hidden'); 668 | break; 669 | default: 670 | 671 | break; 672 | } 673 | }); 674 | function testHSD(mode){ 675 | let apiKey = 'earthlab'; 676 | apiKey = _this.hsdConfig ? _this.hsdConfig.apiKey : apiKey; 677 | $.post('http://x:'+apiKey+'@'+_this.hsdURL,JSON.stringify({params:[],method:'getmininginfo'}),function(d){ 678 | console.log('got mining info?',d); 679 | if(d == '[]' || d == [] || d == ''){ 680 | //empty, means were still not in yet 681 | $('#hsdAPI .notes').show(); 682 | } 683 | else{ 684 | //TODO show their IP to them? not needed yet.. 685 | } 686 | 687 | //success, we can just leave things hidden 688 | }).fail(function(){ 689 | let selector; 690 | /*switch(mode){ 691 | case 'pool': 692 | selector = '#poolUI'; 693 | break; 694 | case 'solo': 695 | selector = '#stratum'; 696 | break; 697 | default: 698 | selector = '#stratum'; 699 | break; 700 | }*/ 701 | $('#hsdAPI .notes').hide(); 702 | //try using solo server then.. 703 | 704 | }) 705 | } 706 | $('#cyoa2 .option').off('click').on('click',function(){ 707 | let v = $(this).attr('id'); 708 | $('#cyoa2').addClass('hidden'); 709 | $('#poolUI').addClass('hidden'); 710 | $('#stratum').removeClass('hidden'); 711 | switch(v){ 712 | case 'local': 713 | //hide all the form elems 714 | _this.soloStratumOption = 'local'; 715 | $('#stratum #hsdAPI').hide(); 716 | $('#stratum #userPass').hide(); 717 | $('#stratum #serverPort').hide(); 718 | 719 | break; 720 | case 'remote': 721 | _this.soloStratumOption = 'remote'; 722 | //show all the form elems 723 | //$('#stratum #hsdAPI').show(); 724 | $('#stratum #userPass').show(); 725 | $('#stratum #serverPort').show(); 726 | break; 727 | } 728 | }); 729 | $('#logs .close').off('click').on('click',function(){ 730 | $('#logs').addClass('hidden'); 731 | _this.logsVisible = false; 732 | }) 733 | $(window).on('keyup',function(e){ 734 | if(e.key == 'Escape'){ 735 | $('.minerForm:not(#main)').addClass('hidden'); 736 | $('#logs').addClass('hidden'); 737 | } 738 | }) 739 | } 740 | launchHSD(isFirstTimeLaunch){ 741 | return false; 742 | //deprecating from goldshell so we dont have to depend on docker/hsd for pool mining 743 | const _this = this; 744 | let walletTarget = this.prodWallet; 745 | if(typeof this.hsdConfig.wallet != "undefined"){ 746 | if(this.hsdConfig.wallet != this.hsdWalletPlaceHolder){ 747 | walletTarget = this.hsdConfig.wallet; 748 | } 749 | } 750 | this.hsdParams = [ 751 | '--network='+(this.config.network || 'main'), 752 | '--cors=true', 753 | '--api-key='+this.hsdConfig.apiKey, 754 | '--http-host=0.0.0.0', 755 | '--coinbase-address='+walletTarget, 756 | '--index-address=true', 757 | '--index-tx=true', 758 | '--listen', 759 | '--plugins', 760 | 'hstratum', 761 | '--stratum-host', 762 | '0.0.0.0', 763 | '--stratum-port', 764 | this.config.port, 765 | '--stratum-public-host', 766 | '0.0.0.0', 767 | '--stratum-public-port', 768 | this.config.port, 769 | '--stratum-max-inbound', 770 | '1000', 771 | '--stratum-difficulty', 772 | '8', 773 | '--stratum-dynamic', 774 | '--stratum-password='+this.config.stratum_pass 775 | ]; 776 | if(typeof this.hsdProcess != "undefined"){ 777 | this.killHSD(); 778 | this.hideLoading(); 779 | }; 780 | let apiKey = this.hsdConfig.apiKey; 781 | let wallet = this.hsdConfig.wallet; 782 | if(wallet == ''){ 783 | wallet = this.prodWallet; 784 | } 785 | 786 | 787 | if(process.platform.toLowerCase().indexOf('win') == 0 || _this.macUseDocker){ 788 | 789 | _this.checkDockerSupport(isFirstTimeLaunch); 790 | //if it's windows we'll launch HSD in docker, yas 791 | } 792 | if(isFirstTimeLaunch){ 793 | this.hideLoading(); 794 | $('#logs').addClass('required').removeClass('hidden'); 795 | $('#logs pre#logOutput').html('Initializing HSD Installation...\r\n'); 796 | $('#logs pre#logOutput').append('#################################\r\n'); 797 | setTimeout(function(){ 798 | $('#logs').removeClass('required'); 799 | },5000) 800 | } 801 | //} 802 | 803 | if(!_this.macUseDocker){ 804 | //launch hsd locally then 805 | let hsdParams = this.hsdParams; 806 | console.log('hsd params isset',process.env); 807 | 808 | 809 | if(!envParams.env.NODE_BACKEND){ 810 | envParams.env.NODE_BACKEND = 'native'; 811 | } 812 | else{ 813 | console.log("HEYYYYYYYYYY NODE BACKEND WAS HERE",envParams.env.NODE_BACKEND); 814 | } 815 | let executable = process.platform.toLowerCase().indexOf('darwin') == 0 ? process.execPath : nw.global.__dirname+'/externals/node.exe'; 816 | 817 | let hsdProcess = spawn(executable,newParams,envParams) 818 | let hsdLogs = ''; 819 | 820 | console.log('should cwd to ',nw.__dirname+'/submodules/HandyMiner-CLI/node_modules/hsd') 821 | this.hsdProcess = hsdProcess; 822 | 823 | this.hsdProcess.stderr.on('data',function(d){ 824 | console.log('sdterr',d.toString('utf8')); 825 | //console.log('hsd stderr',d.toString('utf8')) 826 | if(this.isFirstTimeLaunch){ 827 | $('#logOutput').append(d.toString('utf8')); 828 | $('#logs pre#logOutput')[0].scrollTop = $('#logs pre#logOutput')[0].scrollHeight; 829 | $('#logs').removeClass('required'); 830 | } 831 | else{ 832 | 833 | _this.pushToLogs(d.toString('utf8'),'error','hsd') 834 | } 835 | _this.hideLoading(); 836 | }); 837 | this.hasLogged = false; 838 | this.hsdProcess.stdout.on('data',(d)=>{ 839 | //console.log('data??',d.toString('utf8')); 840 | return false; 841 | //deprecating for now 842 | if(!this.hasLogged){ 843 | this.hsdIsRunningLocally = true; 844 | this.hasLogged = true; 845 | this.hideLoading(); 846 | setTimeout(function(){ 847 | _this.addPeers(); 848 | },1000); 849 | } 850 | //console.log('hsd stdout',d.toString('utf8')); 851 | hsdLogs += d.toString('utf8')+'\r\n'; 852 | if(hsdLogs.length >= 1000000){ 853 | hsdLogs = hsdLogs.slice(-10000); 854 | } 855 | if(isFirstTimeLaunch){ 856 | $('#logOutput').html(hsdLogs); 857 | $('#logs pre#logOutput')[0].scrollTop = $('#logs pre#logOutput')[0].scrollHeight; 858 | 859 | } 860 | else{ 861 | console.log("HEYYYYYYYYYY NODE BACKEND WAS HERE",envParams.env.NODE_BACKEND); 862 | } 863 | 864 | }); 865 | this.hsdProcess.on('close',function(d){ 866 | //console.log('ps closed!?',d.toString('utf8')) 867 | this.hsdIsRunningLocally = false; 868 | /*$('.syncedIcon').addClass('alert'); 869 | $('.syncedButton .statusLabel').html('Local HSD Closed');*/ 870 | }) 871 | 872 | console.log('run test start'); 873 | 874 | 875 | /*let t = spawn('which',['node'],{env:process.env}) 876 | t.stdout.on('data',function(d){ 877 | console.log('test stdout',d.toString('utf8')) 878 | }); 879 | t.stderr.on('data',function(d){ 880 | console.log('test stderr',d.toString('utf8')) 881 | }); 882 | t.on('close',function(d){ 883 | console.log('test is closed'); 884 | })*/ 885 | //if(process.platform.toLowerCase().indexOf('darwin') >= 0){ 886 | 887 | } 888 | //} 889 | //_this.initLogo(); 890 | //setTimeout(function(){ 891 | _this.getHSDNetworkInfo(); 892 | //_this.startTimer(); 893 | //},3000) 894 | } 895 | killHSD(){ 896 | this.hsdProcess.kill(); 897 | } 898 | addPeers(){ 899 | return false; //deprecating 900 | //manually add some peers 901 | let peersPort = this.peersPort || '13038'; 902 | let hsdPort = '13037'; 903 | if(['testnet','simnet'].indexOf(this.config.network) == -1){ 904 | return; 905 | } 906 | 907 | 908 | switch(this.config.network){ 909 | case 'main': 910 | hsdPort = '12037'; 911 | peersPort = '12038'; 912 | break; 913 | case 'testnet': 914 | hsdPort = '13037'; 915 | peersPort = '13038'; 916 | break; 917 | case 'simnet': 918 | hsdPort = '15037'; 919 | peersPort = '15038'; 920 | 921 | break; 922 | } 923 | let nodeaddr = 'aorsxa4ylaacshipyjkfbvzfkh3jhh4yowtoqdt64nzemqtiw2whk@3.14.224.108:'+peersPort; 924 | $.ajax('http://x:'+this.hsdConfig.apiKey+'@127.0.0.1:'+(hsdPort.replace('0','9'))+'/',{ 925 | type:'POST', 926 | contentType:'application/json', 927 | data:JSON.stringify({method:'addnode',params:[nodeaddr,'add']}) 928 | }) 929 | //$.post('http://x:'+this.hsdConfig.apiKey+'@127.0.0.1:13037/',{method:'addnode',params:[nodeaddr,'add']}); 930 | } 931 | 932 | 933 | getHSDNetworkInfo(isAttempt2){ 934 | return false; //deprecating for goldshell gui 935 | const _this = this; 936 | let info = {}; 937 | let qL = 4; 938 | let qC = 0; 939 | let hasFired = false; 940 | $.post('http://x:'+this.hsdConfig.apiKey+'@'+this.hsdURL,JSON.stringify({params:[],method:'getmininginfo'}),function(d){ 941 | console.log('got mining info?',d); 942 | info['mining'] = d.result; 943 | _this.pushToLogs('MINING INFO:: '+JSON.stringify(d.result)+'\r\n','stdout','hsd') 944 | qC++; 945 | if(qC == qL && !hasFired){ 946 | hasFired = true; 947 | done(); 948 | } 949 | }).fail(function(){ 950 | console.log('something failed????') 951 | 952 | 953 | //try stratum host if solo mode then 954 | _this.hideLoading(); 955 | console.log('and modes info',_this.miningMode,_this.config.host+':'+_this.config.port,_this.hsdURL); 956 | if(_this.miningMode == 'solo' && _this.config.host+':'+_this.config.port != _this.hsdURL && _this.config.host != ""){ 957 | _this.hsdConfig.url = _this.config.host+':'+_this.hsdURL.split(':')[1]; 958 | _this.hsdConfig.apiKey = _this.config.stratum_pass; 959 | _this.hsdURL = _this.hsdConfig.url; 960 | console.log('set garbage params',_this.hsdURL); 961 | $('.statusPrefix').html('Remote Node '); 962 | if(!isAttempt2){ 963 | _this.getHSDNetworkInfo(true); 964 | } 965 | else{ 966 | $('.syncedButton .statusLabel').html('Failed to initialize remote HSD'); 967 | if(_this.macUseDocker && process.platform.indexOf('darwin') >= 0){ 968 | $('.statusPrefix').html('Local Node ') 969 | $('.syncedButton .statusLabel').html('Trying to Start Local Docker...'); 970 | } 971 | } 972 | return; 973 | } 974 | }) 975 | let respCount = 0; 976 | let respTarget = 2; 977 | $.post('http://x:'+this.hsdConfig.apiKey+'@'+this.hsdURL,JSON.stringify({params:[],method:'getblockchaininfo'}),function(d){ 978 | console.log('got chain info?',d); 979 | info['chain'] = d.result; 980 | _this.pushToLogs('CHAIN INFO:: '+JSON.stringify(d.result)+'\r\n','stdout','hsd') 981 | qC++; 982 | if(qC == qL && !hasFired){ 983 | hasFired = true; 984 | respCount++; 985 | done(); 986 | } 987 | }); 988 | $.post('http://x:'+this.hsdConfig.apiKey+'@'+this.hsdURL,JSON.stringify({params:[],method:'getnetworkinfo'}),function(d){ 989 | console.log('got mining info?',d); 990 | info['network'] = d.result; 991 | _this.pushToLogs('NETWORK INFO:: '+JSON.stringify(d.result)+'\r\n','stdout','hsd') 992 | qC++; 993 | if(qC == qL && !hasFired){ 994 | hasFired = true; 995 | respCount++; 996 | done(); 997 | } 998 | }); 999 | 1000 | $.post('http://x:'+this.hsdConfig.apiKey+'@'+this.hsdURL,JSON.stringify({params:[],method:'getpeerinfo'}),function(d){ 1001 | console.log('peers are set',d); 1002 | let greatestHeight = 0; 1003 | info['peers'] = d.result; 1004 | d.result.map(peer=>{ 1005 | if(peer.startingheight > greatestHeight){ 1006 | greatestHeight = peer.startingheight; 1007 | } 1008 | }); 1009 | _this.peersGreatestHeight = greatestHeight; 1010 | _this.pushToLogs('PEERS INFO:: '+JSON.stringify(d.result)+'\r\n','stdout','hsd'); 1011 | qC++; 1012 | if(qC == qL && !hasFired){ 1013 | hasFired = true; 1014 | respCount++; 1015 | done(); 1016 | } 1017 | 1018 | }); 1019 | 1020 | function done(){ 1021 | 1022 | console.log('got network info',info); 1023 | _this.hsdInfo = info; 1024 | 1025 | if(info.mining.blocks == 0 /*&& info.mining.chain == 'test'*/){ 1026 | console.log('garbage time'); 1027 | //assume this is bogus unsynced garbage and try to sync w solo pool host 1028 | if(isAttempt2){ 1029 | $('.syncedIcon').addClass('alert'); 1030 | $('.syncedButton .statusLabel').html('Dockerized HSD Started, but Block Height is 0'); 1031 | _this.hideLoading(); 1032 | } 1033 | if(_this.miningMode == 'solo' && _this.config.host+':'+_this.config.port != _this.hsdURL && _this.config.host != ""){ 1034 | _this.hsdConfig.url = _this.config.host+':'+(_this.hsdURL.split(':')[_this.hsdURL.split(':').length-1]); 1035 | _this.hsdConfig.apiKey = _this.config.stratum_pass; 1036 | _this.hsdURL = _this.hsdConfig.url; 1037 | console.log('set garbage params',_this.hsdURL); 1038 | if(!isAttempt2){ 1039 | _this.getHSDNetworkInfo(true); 1040 | } 1041 | if(info.peers.length == 0 && (info.chain.chain == 'simnet' || info.chain.chain == 'regtest') && info.chain.blocks == 0){ 1042 | //dont return, it's simnet 1043 | } 1044 | else{ 1045 | return; 1046 | } 1047 | 1048 | } 1049 | } 1050 | let heightNow = info.mining.blocks; 1051 | if(typeof _this.lastKnownHeight == "undefined"){ 1052 | _this.lastKnownHeight = _this.peersGreatestHeight || 0; 1053 | } 1054 | $('.title a').attr('href','http://hnscan.com/block/'+heightNow).html(heightNow) 1055 | let bI = 0; 1056 | let bC = 100; 1057 | let blocks = {} 1058 | //setTimeout(()=>{ 1059 | //if(_this.canFetchNetworkTimeline){ 1060 | //},1000) 1061 | if(Math.abs(_this.lastKnownHeight - heightNow) < 5 && Math.abs(heightNow - _this.peersGreatestHeight) < 10){ 1062 | 1063 | console.log("FETCHING SOME TOOOONS OF TX") 1064 | let hnStart = heightNow-100 < 0 ? 0 : heightNow-100; 1065 | if(hnStart == 0){ 1066 | bC = _this.lastKnownHeight; 1067 | console.log('last known height is small',bC); 1068 | } 1069 | 1070 | for(let i=hnStart;i<=heightNow;i++){ 1071 | $.post('http://x:'+_this.hsdConfig.apiKey+'@'+_this.hsdURL,JSON.stringify({params:[i,true,false],method:'getblockbyheight'}),function(d){ 1072 | //console.log('got block info?',d); 1073 | blocks[i] = d.result; 1074 | setTimeout(function(){ 1075 | _this.hideLoading(); 1076 | },1000) 1077 | let tx = d.result.tx[0]; 1078 | $.get('http://x:'+_this.hsdConfig.apiKey+'@'+_this.hsdURL+'/tx/'+tx,function(d){ 1079 | blocks[i]._txDetail = d; 1080 | if(d.outputs[0].address == _this.hsdWallet){ 1081 | blocks[i].isMyBlock = true; 1082 | } 1083 | bI++; 1084 | if(bI == bC){ 1085 | blocksDone(blocks,heightNow); 1086 | } 1087 | }); 1088 | 1089 | 1090 | }); 1091 | if(i == 0 && bC == 0){ 1092 | blocksDone(blocks,heightNow); 1093 | } 1094 | } 1095 | } 1096 | else{ 1097 | console.log('last known height is fuckin huge',_this.lastKnownHeight,heightNow,Math.abs(_this.lastKnownHeight - heightNow)) 1098 | } 1099 | //} 1100 | _this.lastKnownHeight = heightNow; 1101 | } 1102 | function blocksDone(blocks,heightNow){ 1103 | 1104 | console.log('loaded 100 blocks',blocks); 1105 | _this.hideLoading(); 1106 | _this.last100Blocks = blocks; 1107 | let timeMin = new Date().getTime() - (24 * 60 * 60 * 1000); 1108 | let blocksTotal = 0; 1109 | Object.keys(_this.last100Blocks).map((blockKey)=>{ 1110 | let blockD = _this.last100Blocks[blockKey]; 1111 | if(blockD.time*1000 >= timeMin && blockD.isMyBlock){ 1112 | console.log('blocks are mine!'); 1113 | blocksTotal++; 1114 | } 1115 | }) 1116 | _this.blocksSolvedLast24 = blocksTotal; 1117 | let alltime = 0; 1118 | try{ 1119 | alltime = fs.readFileSync(this.appDirPath+'HandyMinerConfigs/allTimeBlocks.txt','utf8'); 1120 | alltime = parseFloat(alltime); 1121 | } 1122 | catch(e){ 1123 | alltime = 0; 1124 | } 1125 | if(isNaN(alltime)){ 1126 | alltime = 0; 1127 | } 1128 | if(parseFloat(_this.blocksSolvedLast24) > parseFloat(alltime)){ 1129 | alltime = _this.blocksSolvedLast24; 1130 | } 1131 | console.log('alltime and not',alltime,_this.blocksSolvedLast24) 1132 | _this.allTimeBlocks = alltime; 1133 | $('.blocksToday .number').html(numeral(_this.blocksSolvedLast24).format('0a').toUpperCase()) 1134 | $('.blocksAllTime .number').html(numeral(_this.allTimeBlocks).format('0a').toUpperCase()); 1135 | _this.canFetchNetworkTimeline = true; 1136 | //_this.getHSDNetworkInfo(); 1137 | setTimeout(function(){ 1138 | $('.syncedIcon').removeClass('alert').addClass('success'); 1139 | $('.syncedButton .statusLabel').html('synced at block: '+heightNow); 1140 | $('.syncedButton').fadeOut(5000); 1141 | },500) 1142 | _this.renderLastBlocks(blocks); 1143 | _this.renderHashrate(); 1144 | 1145 | 1146 | } 1147 | } 1148 | startHashrateChartTicks(){ 1149 | console.log('hashrate chart tick init') 1150 | if(typeof this.hashrateDisplayInterval != "undefined"){ 1151 | return false; 1152 | } 1153 | setTimeout(()=>{ 1154 | this.renderHashrate(); 1155 | },1000); 1156 | //this.renderHashrate(); 1157 | this.hashrateDisplayInterval = setInterval(()=>{ 1158 | //console.log('render hashrate chart called'); 1159 | this.renderHashrate(); 1160 | //console.log('render hashrate chart') 1161 | },10500) 1162 | } 1163 | renderHashrate(){ 1164 | const _this = this; 1165 | //let globalFn = this.appDirPath+'HandyMinerConfigs/globalHashrate.csv'; 1166 | let diffFn = this.appDirPath+'/HandyMinerConfigs/difficulty.jsonl'; 1167 | let localFn = this.appDirPath+'HandyMinerConfigs/localHashrate.jsonl'; 1168 | 1169 | let lines; 1170 | let localLines; 1171 | try{ 1172 | localLines = fs.readFileSync(localFn,'utf8'); 1173 | } 1174 | catch(e){ 1175 | console.log('caught error in locallines'); 1176 | localLines = ''; 1177 | } 1178 | try{ 1179 | lines = fs.readFileSync(diffFn,'utf8'); 1180 | } 1181 | catch(e){ 1182 | lines = ''; 1183 | } 1184 | 1185 | localLines = localLines.split('\n'); 1186 | //console.log('hashrateLines',localLines); 1187 | lines = lines.split('\n'); 1188 | if(lines.length > 300){ 1189 | lines = lines.slice(-200); 1190 | fs.writeFileSync(diffFn,lines.join('\n'),'utf8'); 1191 | } 1192 | if(localLines.length > 300){ 1193 | localLines = localLines.slice(-200); 1194 | fs.writeFileSync(localFn,localLines.join('\n'),'utf8'); 1195 | }; 1196 | 1197 | let difficultyPerAsic = {}; 1198 | let hashratePerAsic = {}; 1199 | let fansPerAsic = {}; 1200 | let tempPerAsic = {}; 1201 | let totalHashrate = []; 1202 | let allTimes = []; 1203 | 1204 | let dataByAsic = {}; 1205 | 1206 | let hashrateJSONs = localLines.filter(function(d){ 1207 | return d.length > 0; 1208 | }).map(function(d){ 1209 | var json = JSON.parse(d); 1210 | return json; 1211 | }); 1212 | let diffJSONs = lines.filter(function(d){ 1213 | return d.length > 0; 1214 | }).map(d=>{ 1215 | return JSON.parse(d); 1216 | }); 1217 | let lastTotalHashrate = 0; 1218 | let lastAvgHashrate = 0; 1219 | let lastNetworkDiff = 0; 1220 | let lastLocalDiff = 0; 1221 | let targetTime = moment().subtract(2,'hours').format('x'); 1222 | //console.log('targetTime',targetTime); 1223 | let filteredJSONs = /*hashrateJSONs;= */hashrateJSONs.filter(line=>{ 1224 | if(line.time >= targetTime){ 1225 | return true; 1226 | } 1227 | return false; 1228 | }) //get rid of really old data 1229 | /*hashrateJSONs*/ 1230 | //console.log('tl data',filteredJSONs); 1231 | filteredJSONs.map((hrLine,i)=>{ 1232 | let totalSum = 0; 1233 | let avgSum = 0; 1234 | let timeForLine; 1235 | let timestamp = hrLine.time; 1236 | timeForLine = timestamp; 1237 | allTimes.push(timestamp); 1238 | //console.log('hrline',hrLine); 1239 | Object.keys(hrLine.rates).filter(asicID=>{ 1240 | return typeof this.asicNames[asicID] != "undefined" && typeof this.asicDisconnected[asicID] == "undefined" 1241 | }).map(asicID=>{ 1242 | //console.log('asicID',asicID,this.asicNames[asicID],this.asicDisconnected[asicID]) 1243 | if(typeof dataByAsic[asicID] == "undefined"){ 1244 | createLineObj(asicID,hrLine.rates[asicID],this.asicNames[asicID]); 1245 | } 1246 | let hashrateData = hrLine.rates[asicID]; 1247 | hashratePerAsic[asicID].realtime.push([hashrateData.hashrateNow,timestamp]) 1248 | fansPerAsic[asicID].realtime.push([hashrateData.fan,timestamp]); 1249 | tempPerAsic[asicID].realtime.push([hashrateData.temp,timestamp]); 1250 | 1251 | Object.keys(hashrateData.workerHashrates).map(workerID=>{ 1252 | //console.log('workerID',workerID,hashrateData); 1253 | hashratePerAsic[asicID].workers[workerID].realtime.push([hashrateData.workerHashrates[workerID].hashrateNow,timestamp]) 1254 | }) 1255 | totalSum += hashrateData.hashrateNow; 1256 | avgSum += hashrateData.hashrateAvg; 1257 | /* 1258 | _this.hashRates[asicID] = { 1259 | hashrateNow:lineD.data.hashrateNow, 1260 | hashrateAvg:lineD.data.hashrateAvg, 1261 | workerHashrates: lineD.data.workerHashrates, 1262 | temp:lineD.data.temp, 1263 | fan:lineD.data.fanRpm 1264 | }; 1265 | */ 1266 | }); 1267 | //console.log('last avg',lastAvgHashrate); 1268 | lastAvgHashrate = avgSum; 1269 | lastTotalHashrate = totalSum; 1270 | totalHashrate.push([totalSum,timeForLine]); 1271 | 1272 | /*if(typeof diffJSONs[i] != "undefined"){ 1273 | //add diff 1274 | let diffLine = diffJSONs[i]; 1275 | if(typeof diffLine.rates != "undefined" && Object.keys(diffLine.rates).length > 0){ 1276 | Object.keys(diffLine.rates).map(asicID=>{ 1277 | console.log('DIFFLINE',diffLine,diffLine.rates[asicID],asicID) 1278 | let diffData = diffLine.rates[asicID]; 1279 | let timestamp = diffLine.time; 1280 | allTimes.push(timestamp); 1281 | difficultyPerAsic[asicID].network.push([diffData.network,timestamp]); 1282 | difficultyPerAsic[asicID].local.push([diffData.local,timestamp]); 1283 | lastNetworkDiff = diffData.network; 1284 | lastLocalDiff = diffData.local; 1285 | }) 1286 | } 1287 | }*/ 1288 | }); 1289 | diffJSONs.map(diffLine=>{ 1290 | if(Object.keys(diffLine.rates).length > 0){ 1291 | let r0 = diffLine.rates[Object.keys(diffLine.rates)[0]]; 1292 | lastNetworkDiff = r0.network; 1293 | lastLocalDiff = r0.local; 1294 | } 1295 | }) 1296 | 1297 | 1298 | this.renderLeftStats(lastNetworkDiff,lastLocalDiff,lastTotalHashrate,lastAvgHashrate); 1299 | 1300 | 1301 | let timeExtent = [targetTime,moment().format('x')]//d3.extent(allTimes); 1302 | let totalHashrateSeries = []; 1303 | let allDates = totalHashrate.map(d=>{ 1304 | totalHashrateSeries.push(d[0]); 1305 | return d[1]; 1306 | }); 1307 | if(this.isDisplaySleeping){ 1308 | return; //skip chart render if the app does not have focus? 1309 | } 1310 | Object.keys(dataByAsic).map(asicID=>{ 1311 | if(typeof this.asicNames[asicID] == "undefined"){ 1312 | return; 1313 | } 1314 | /* 1315 | hashratePerAsic[asicID].realtime.push([hashrateData.hashrateNow,timestamp]) 1316 | fansPerAsic[asicID].realtime.push([hashrateData.fan,timestamp]); 1317 | tempPerAsic[asicID].realtime.push([hashrateData.temp,timestamp]); 1318 | difficultyPerAsic[asicID].network.push([diffData.network,timestamp]); 1319 | difficultyPerAsic[asicID].local.push([diffData.local,timestamp]); 1320 | */ 1321 | let hashrateSeries = []; 1322 | let fanSeries = []; 1323 | let tempSeries = []; 1324 | let diffNetworkSeries = []; 1325 | let diffLocalSeries = []; 1326 | let dates = []; 1327 | let workersSeries = []; 1328 | 1329 | hashratePerAsic[asicID].realtime.map(d=>{ 1330 | dates.push(d[1]) 1331 | hashrateSeries.push(d[0]) 1332 | }); 1333 | Object.keys(hashratePerAsic[asicID].workers).map(workerID=>{ 1334 | let d = hashratePerAsic[asicID].workers[workerID]; 1335 | let workerseries = d.realtime.map(d=>{ 1336 | return d[0]; 1337 | }) 1338 | workersSeries.push({name:'Worker '+workerID,values:workerseries}); 1339 | }); 1340 | fansPerAsic[asicID].realtime.map(d=>{ 1341 | fanSeries.push(d[0]) 1342 | }); 1343 | tempPerAsic[asicID].realtime.map(d=>{ 1344 | tempSeries.push(d[0]); 1345 | }); 1346 | difficultyPerAsic[asicID].network.map(d=>{ 1347 | diffNetworkSeries.push(d[0]); 1348 | }); 1349 | difficultyPerAsic[asicID].local.map(d=>{ 1350 | diffLocalSeries.push(d[0]); 1351 | }); 1352 | 1353 | let hashrateSeriesData = []; 1354 | hashrateSeriesData.push({name:'Hashrate',values:hashrateSeries}); 1355 | hashrateSeriesData = hashrateSeriesData.concat(workersSeries); 1356 | 1357 | let tempSeriesData = []; 1358 | tempSeriesData.push({name:'Temperature',values:tempSeries}); 1359 | 1360 | let fanSeriesData = []; 1361 | fanSeriesData.push({name: 'Fan RPM',values:fanSeries}); 1362 | 1363 | let diffSeriesData = []; 1364 | diffSeriesData.push({name:'Network Diff',values:diffNetworkSeries}); 1365 | diffSeriesData.push({name:'Share Diff',values:diffLocalSeries}); 1366 | 1367 | dataByAsic[asicID] = { 1368 | dates:dates, 1369 | difficulty:diffSeriesData, 1370 | fans: fanSeriesData, 1371 | temp: tempSeriesData, 1372 | hashrate:hashrateSeriesData 1373 | }; 1374 | 1375 | renderChart(dataByAsic[asicID],asicID,'hashrate'); 1376 | renderChart(dataByAsic[asicID],asicID,'temp'); 1377 | renderChart(dataByAsic[asicID],asicID,'fans'); 1378 | if(!$('#right .asic[data-id="'+asicID+'"]').hasClass('hidden')){ 1379 | $('#right .asic[data-id="'+asicID+'"]').css({'height':'auto'}); 1380 | setTimeout(()=>{ 1381 | let h = $('#right .asic[data-id="'+asicID+'"]').height(); 1382 | $('#right .asic[data-id="'+asicID+'"]').attr('data-height',h); 1383 | $('#right .asic[data-id="'+asicID+'"]').css({'height':h}); 1384 | },150) 1385 | } 1386 | 1387 | }); 1388 | 1389 | //ok now draw some charts 1390 | let horizonElCount = 0; 1391 | 1392 | function DOM(name,id){ 1393 | return new Id("O-" + (name == null ? "" : name + "-" ) + id);// + (++horizonElCount)); 1394 | } 1395 | function Id(id) { 1396 | this.id = id; 1397 | this.href = new URL(`#${id}`, location) + ""; 1398 | } 1399 | 1400 | Id.prototype.toString = function() { 1401 | return "url(" + this.href + ")"; 1402 | }; 1403 | 1404 | 1405 | function renderChart(data,asicID,type){ 1406 | let asicName = _this.asicNames[asicID] ? asicID+'.'+_this.asicNames[asicID] : asicID; 1407 | 1408 | //console.log('chartd',data); 1409 | const step = 23; 1410 | let $el; 1411 | if($('#right .asic[data-id="'+asicID+'"]').length == 0){ 1412 | $el = $('
'+asicName+'
') 1413 | $('#right').append($el); 1414 | } 1415 | else{ 1416 | $el = $('#right .asic[data-id="'+asicID+'"]'); 1417 | } 1418 | if($('svg.'+type,$el).length == 0){ 1419 | $el.append('') 1420 | } 1421 | 1422 | //horizonElCount = 4;// data.hashrate.length; 1423 | const svg = d3.select($el[0]).select('svg[data-id="'+asicID+'"].'+type); 1424 | $('svg[data-id="'+asicID+'"].'+type,$el).css('height',(step+1)*data[type].length) 1425 | 1426 | let width = $('#right.halfChart').width(); 1427 | let height = $('#right.halfChart').height(); 1428 | let margin = ({top: 0, right: 0, bottom: 0, left: 0}) 1429 | let overlap = 6; 1430 | let colorScheme; 1431 | let labelSuffix = ''; 1432 | switch(type){ 1433 | case 'hashrate': 1434 | colorScheme = 'schemePurples'; 1435 | labelSuffix = 'GH'; 1436 | break; 1437 | case 'temp': 1438 | colorScheme = 'schemeYlOrRd'; 1439 | labelSuffix = '°C'; 1440 | overlap = 6; 1441 | break; 1442 | case 'fans': 1443 | colorScheme = 'schemePuBu'; 1444 | labelSuffix = 'RPM' 1445 | overlap = 6; 1446 | break; 1447 | } 1448 | let color = (i) => d3[colorScheme][Math.max(3, overlap)][i + Math.max(0, 3 - overlap)] 1449 | let greyscale = (i) => d3[colorScheme][Math.max(3, overlap)][i + Math.max(0, 3 - overlap)] 1450 | let ylGn = (i) => d3['schemeYlGn'][Math.max(3, overlap)][i + Math.max(0, 3 - overlap)] 1451 | let dateExtent = d3.extent(data.dates); 1452 | let dateDomain = [moment().subtract(15,'minutes').format('x'),moment().format('x')]; 1453 | if(dateExtent[0] > dateDomain[0]){ 1454 | dateDomain = dateExtent; 1455 | } 1456 | let x = d3.scaleUtc() 1457 | .domain(dateDomain/*d3.extent(data.dates)*/) 1458 | .range([0, width]) 1459 | .clamp(true); 1460 | let y = d3.scaleLinear() 1461 | .domain([0, d3.max(data[type], d => d3.max(d.values))]) 1462 | .range([0, -overlap * step]) 1463 | 1464 | let area = d3.area() 1465 | .curve(d3.curveBasis) 1466 | .defined(d => !isNaN(d)) 1467 | .x((d, i) => x(data.dates[i])) 1468 | .y0(0) 1469 | .y1(d => y(d)); 1470 | 1471 | let xAxis = g => g 1472 | .attr("transform", `translate(0,${margin.top})`) 1473 | .call(d3.axisTop(x).ticks(width / 80).tickSizeOuter(0)) 1474 | .call(g => g.selectAll(".tick").filter(d => x(d) < margin.left || x(d) >= width - margin.right).remove()) 1475 | .call(g => g.select(".domain").remove()); 1476 | let chartData = data[type].map((d,i) => Object.assign({ 1477 | clipId: DOM("clip"+type+asicID,i), 1478 | pathId: DOM("path"+type+asicID,i) 1479 | }, d)) 1480 | const g = svg 1481 | .selectAll('g.main') 1482 | .data(chartData) 1483 | .join('g') 1484 | //.append("g") 1485 | .classed('main',true) 1486 | //.classed(asicID,true) 1487 | .attr("transform", (d, i) => `translate(0,${i * (step + 1)})`); 1488 | let cPath = g.selectAll('clipPath') 1489 | .data(d=>{return [d];}) 1490 | .join('clipPath') 1491 | //.append("clipPath") 1492 | .attr("id", d => { return d.clipId.id;}); 1493 | 1494 | cPath.selectAll('rect') 1495 | .data(d=>{return [d];}) 1496 | //.append("rect") 1497 | .join('rect') 1498 | .attr("width", width) 1499 | .attr("height", step);; 1500 | 1501 | let defs = g.selectAll('defs') 1502 | .data(d=>{return [d];}) 1503 | .join('defs'); 1504 | defs.selectAll('path') 1505 | .data(d=>{return [d];}) 1506 | .join('path') 1507 | //.append("path") 1508 | .attr("id", d => d.pathId.id) 1509 | /*.transition() 1510 | .duration(300)*/ 1511 | .attr("d", d => area(d.values)); 1512 | let gClipPath = g.selectAll('g') 1513 | .data(d=>{return [d];}) 1514 | //append("g") 1515 | .join('g') 1516 | .attr("clip-path", d => 'url('+d.clipId.href+')') 1517 | 1518 | gClipPath.selectAll("use") 1519 | .data(d => new Array(overlap).fill(d)) 1520 | .join("use") 1521 | .attr("fill", (d, i) => { 1522 | let rgb; 1523 | // grey::: //let rgb = d3.rgb(greyscale(i)); 1524 | if((d.name == 'Hashrate') || type != 'hashrate') { 1525 | 1526 | rgb = d3.rgb(color(i)); 1527 | } 1528 | else{ 1529 | rgb = d3.rgb(ylGn(i)); 1530 | } 1531 | //console.log('fill color',rgb,d); 1532 | return rgb 1533 | }) 1534 | .on('mouseenter',function(d,i){ 1535 | d3.select(this.parentNode).selectAll('use') 1536 | .transition() 1537 | .duration(100) 1538 | .attr("fill", (d, i) => { 1539 | let rgb; 1540 | if((d.name == 'Hashrate') || type != 'hashrate') { 1541 | 1542 | rgb = d3.rgb(color(i)); 1543 | } 1544 | else{ 1545 | rgb = d3.rgb(ylGn(i)); 1546 | } 1547 | //console.log('fill color',rgb,d); 1548 | return rgb 1549 | }) 1550 | return; 1551 | }) 1552 | .on('mouseleave',function(d,i){ 1553 | d3.select(this.parentNode).selectAll('use') 1554 | .transition() 1555 | .duration(100) 1556 | .attr("fill", (d, i) => { 1557 | //GREY //let rgb = d3.rgb(greyscale(i)); 1558 | let rgb; 1559 | // grey::: //let rgb = d3.rgb(greyscale(i)); 1560 | if((d.name == 'Hashrate') || type != 'hashrate') { 1561 | 1562 | rgb = d3.rgb(color(i)); 1563 | } 1564 | else{ 1565 | rgb = d3.rgb(ylGn(i)); 1566 | } 1567 | //console.log('fill color',rgb,d); 1568 | return rgb 1569 | }) 1570 | return; 1571 | }) 1572 | .attr('stroke','rgba(200,200,200,0.6') 1573 | .attr("transform", (d, i) => `translate(0,${(i + 1) * step})`) 1574 | .attr("xlink:href", d => d.pathId.href); 1575 | //TODO add translation 1576 | let l10n = localStorage.getItem('l10n'); 1577 | let texts = g.selectAll('text') 1578 | .data(d=>{return [d];}) 1579 | .join('text') 1580 | //.append("text") 1581 | .classed('front',true) 1582 | .classed('back',false) 1583 | .attr("x", 4) 1584 | .attr("y", step / 2) 1585 | .attr("dy", "0.35em") 1586 | .text(d => { 1587 | if(d.name == 'Hashrate'){ 1588 | return _this.l10nData['text.hashrate'][l10n].trim()+': '+d.values[d.values.length-1]+labelSuffix; 1589 | } 1590 | if(d.name.indexOf('Worker') >= 0){ 1591 | let wID = d.name.split('Worker')[1]; 1592 | return _this.l10nData['text.workers'][l10n].trim()+' '+wID+': '+d.values[d.values.length-1]+labelSuffix; 1593 | } 1594 | if(d.name == 'Temperature'){ 1595 | return _this.l10nData['text.temp'][l10n].trim()+': '+d.values[d.values.length-1]+labelSuffix; 1596 | } 1597 | if(d.name == 'Fan RPM'){ 1598 | return _this.l10nData['text.fan'][l10n].trim()+': '+d.values[d.values.length-1]+labelSuffix; 1599 | 1600 | } 1601 | 1602 | }) 1603 | .on('mouseenter',function(d,i){ 1604 | d3.select(this.parentNode).selectAll('use') 1605 | .transition() 1606 | .duration(100) 1607 | .attr("fill", (d, i) => { 1608 | let rgb; 1609 | if((d.name == 'Hashrate') || type != 'hashrate') { 1610 | 1611 | rgb = d3.rgb(color(i)); 1612 | } 1613 | else{ 1614 | rgb = d3.rgb(ylGn(i)); 1615 | } 1616 | //console.log('fill color',rgb,d); 1617 | return rgb 1618 | }) 1619 | return; 1620 | }) 1621 | .on('mouseleave',function(d,i){ 1622 | d3.select(this.parentNode).selectAll('use') 1623 | .transition() 1624 | .duration(100) 1625 | .attr("fill", (d, i) => { 1626 | let rgb = d3.rgb(greyscale(i)); 1627 | //console.log('fill color',rgb,d); 1628 | return rgb 1629 | }) 1630 | return; 1631 | }); 1632 | texts.clone(true) 1633 | .classed('front',false) 1634 | .classed('back',true) 1635 | .attr("stroke-linejoin", "round") 1636 | .attr("stroke-width", 3); 1637 | g.selectAll('.front').raise() 1638 | $('text.front','svg[data-id="'+asicID+'"].'+type).eq(0).addClass('first'); 1639 | $('svg[data-id="'+asicID+'"].hashrate path').eq(0).addClass('first'); 1640 | 1641 | /*svg.append("g") 1642 | .call(xAxis);*/ 1643 | 1644 | } 1645 | function createLineObj(asicID,hrLast,asicName){ 1646 | //create obj if doesnt exist 1647 | dataByAsic[asicID] = { 1648 | dates:[], 1649 | series:[] 1650 | } 1651 | if(typeof difficultyPerAsic[asicID] == "undefined"){ 1652 | difficultyPerAsic[asicID] = { 1653 | network:[], 1654 | local:[] 1655 | }; 1656 | } 1657 | 1658 | let workerHRObj = {}; 1659 | if(Object.keys(hrLast.workerHashrates).length == 0){ 1660 | //create placeholder 1661 | for(let i=1;i<=4;i++){ 1662 | workerHRObj[i] = { 1663 | realtime:[] 1664 | } 1665 | } 1666 | } 1667 | else{ 1668 | Object.keys(hrLast.workerHashrates).map(workerID=>{ 1669 | workerHRObj[workerID] = { 1670 | realtime:[] 1671 | } 1672 | }); 1673 | } 1674 | if(Object.keys(workerHRObj).length == 4 && asicName.indexOf('Plus') >= 0){ 1675 | //recycling port ID, lets add workers to historic data then bc this is a plus.. 1676 | for(let i=5;i<=8;i++){ 1677 | workerHRObj[i] = { 1678 | realtime:[] 1679 | } 1680 | } 1681 | } 1682 | else if(Object.keys(workerHRObj).length == 8 && asicName.indexOf('Plus') == -1){ 1683 | //recycling port id from a plus to a non-plus.. 1684 | for(let i=5;i<=8;i++){ 1685 | delete workerHRObj[i]; 1686 | } 1687 | 1688 | } 1689 | //console.log("workerHRObj isset",workerHRObj,asicName); 1690 | if(typeof hashratePerAsic[asicID] == "undefined"){ 1691 | hashratePerAsic[asicID] = { 1692 | realtime:[], 1693 | workers:workerHRObj 1694 | }; 1695 | fansPerAsic[asicID] = { 1696 | realtime:[] 1697 | }; 1698 | tempPerAsic[asicID] = { 1699 | realtime:[] 1700 | } 1701 | } 1702 | } 1703 | 1704 | } 1705 | renderLeftStats(lastNetworkDiff,lastLocalDiff,lastTotalHashrate,lastAvgHashrate){ 1706 | let $el = $('#left'); 1707 | $('.totalHashrate .value',$el).html(numeral(lastTotalHashrate).format('0.00')+'GH'); 1708 | $('.avgHashrate .value',$el).html(numeral(lastAvgHashrate).format('0.00')+'GH'); 1709 | $('.networkDiff .value',$el).html(numeral(lastNetworkDiff).format('0a').toUpperCase()); 1710 | $('.localDiff .value',$el).html(numeral(lastLocalDiff).format('0a').toUpperCase()); 1711 | /* 1712 |
1713 |
---GH
1714 |
Total Hashrate
1715 |
1716 |
1717 |
---GH
1718 |
Avg Hashrate
1719 |
1720 |
1721 |
---
1722 |
Network Diff
1723 |
1724 |
1725 |
---
1726 |
Share Diff
1727 |
1728 | */ 1729 | } 1730 | renderLastBlocks(blocks){ 1731 | let tempBlocks = {}; 1732 | let filtered = Object.keys(blocks).filter(k=>{ 1733 | return blocks[k] != null; 1734 | }).map(k=>{ 1735 | tempBlocks[k] = blocks[k]; 1736 | }); 1737 | blocks = tempBlocks; 1738 | 1739 | 1740 | //render chart of the last 100 blocks 1741 | var svg = d3.select('#left.halfChart svg'); 1742 | svg.selectAll('g').remove(); 1743 | let bData = Object.keys(blocks); 1744 | bData = bData.map(function(k){ 1745 | return blocks[k]; 1746 | }); 1747 | let w = $('#left.halfChart').width(); 1748 | let h = $('#left.halfChart').height(); 1749 | let padding = {l:36,t:5,r:5,b:20}; 1750 | let newblocks = {}; 1751 | Object.keys(blocks).map(k=>{ 1752 | if(typeof blocks[k] != "undefined"){ 1753 | newblocks[k] = blocks[k]; 1754 | } 1755 | }); 1756 | blocks = newblocks; 1757 | let xExtent = d3.extent(Object.keys(blocks),function(d){ 1758 | return blocks[d].height; 1759 | }); 1760 | 1761 | let diffExtent = d3.extent(Object.keys(blocks),function(d){ 1762 | return blocks[d].difficulty; 1763 | }); 1764 | bData = [{height:xExtent[0],difficulty:diffExtent[0]}].concat(bData); 1765 | bData = bData.concat([{height:xExtent[1],difficulty:diffExtent[0]}]); 1766 | let path = svg.selectAll('path').data([bData]); 1767 | 1768 | 1769 | let xScale = d3.scaleLinear() 1770 | .range([padding.l,w-padding.r]) 1771 | .domain(xExtent); 1772 | let yScale = d3.scaleLinear() 1773 | .range([h-padding.b,padding.t]) 1774 | .domain(diffExtent); 1775 | let axisL = d3.axisLeft(yScale).ticks(5).tickFormat(d=>numeral(d).format('0.0a')); 1776 | let axisB = d3.axisBottom(xScale).ticks(5); 1777 | 1778 | let line = d3.line() 1779 | .x(function(d){ 1780 | return xScale(d.height); 1781 | }) 1782 | .y(function(d){ 1783 | return yScale(d.difficulty); 1784 | }); 1785 | svg.append('g') 1786 | .attr('transform','translate(35,0)') 1787 | .call(axisL); 1788 | svg.append('g') 1789 | .attr('transform','translate(0,'+(h-20)+')') 1790 | .call(axisB); 1791 | 1792 | path.transition() 1793 | .attr('d',line); 1794 | path.enter() 1795 | .append('path') 1796 | .classed('line',true) 1797 | .transition() 1798 | .attr('d',line); 1799 | path.exit().remove(); 1800 | } 1801 | startTimer(){ 1802 | return false; //deprecating for goldshell 1803 | const _this = this; 1804 | if(typeof this.timer != "undefined"){ 1805 | clearTimeout(this.timer); 1806 | } 1807 | this.timer = setTimeout(function(){ 1808 | 1809 | if(_this.canFetchNetworkTimeline){ 1810 | $.post('http://x:'+_this.hsdConfig.apiKey+'@'+_this.hsdURL,JSON.stringify({params:[],method:'getmininginfo'}),function(d){ 1811 | //console.log('got mining info?',d); 1812 | let hr = d.result.networkhashps; 1813 | let time = new Date().getTime(); 1814 | let line = [hr,time].join(',')+'\n' 1815 | fs.appendFile(process.env.HOME+'/.HandyMiner/globalHashrate.csv',line,function(err,d){}); 1816 | 1817 | _this.syncBlocks(d.result.blocks); 1818 | let heightNow = d.result.blocks; 1819 | 1820 | $('.title a').attr('href','http://hnscan.com/block/'+heightNow).html(heightNow); 1821 | 1822 | _this.renderHashrate(); 1823 | $('#logs.required').hide(); //means we can hide install logs 1824 | 1825 | }); 1826 | if(Object.keys(_this.hashRates).length > 0){ 1827 | let hrLine = JSON.stringify({rates:_this.hashRates,time:new Date().getTime()})+'\n'; 1828 | fs.appendFile(process.env.HOME+'/.HandyMiner/localHashrate.json',hrLine,function(err,d){}) 1829 | } 1830 | } 1831 | else{ 1832 | $.post('http://x:'+_this.hsdConfig.apiKey+'@'+_this.hsdURL,JSON.stringify({params:[],method:'getmininginfo'}),function(d){ 1833 | //console.log('got mining info?',d); 1834 | console.log('mining infos?',d); 1835 | let heightNow = d.result.blocks; 1836 | if(typeof _this.syncHeight == "undefined"){ 1837 | _this.syncHeight = 0; 1838 | } 1839 | if(_this.syncHeight == heightNow && heightNow != 0 && (Math.abs(heightNow - _this.peersGreatestHeight) < 10) || (_this.peersGreatestHeight == 0 && _this.hsdInfo.peers.length == 0)){ 1840 | console.log('might be done syncing me thinks..'); 1841 | 1842 | _this.lastKnownHeight = heightNow; 1843 | _this.getHSDNetworkInfo(); 1844 | 1845 | } 1846 | else{ 1847 | console.log('syncheight ',_this.syncHeight,heightNow); 1848 | } 1849 | _this.syncHeight = heightNow; 1850 | 1851 | }); 1852 | 1853 | } 1854 | _this.startTimer(); 1855 | 1856 | },20000); 1857 | 1858 | 1859 | } 1860 | showLoading(){ 1861 | $('#loading').show(); 1862 | } 1863 | hideLoading(){ 1864 | $('#loading').hide(); 1865 | } 1866 | syncBlocks(startAtBlock){ 1867 | const _this = this; 1868 | let blocks = this.last100Blocks; 1869 | console.log('syncBlock startat',startAtBlock); 1870 | $.post('http://x:'+this.hsdConfig.apiKey+'@'+this.hsdURL,JSON.stringify({params:[startAtBlock,true,false],method:'getblockbyheight'}),function(d){ 1871 | //console.log('got block info?',d); 1872 | _this.last100Blocks[startAtBlock] = d.result; 1873 | 1874 | let tx = d.result.tx[0]; 1875 | $.get('http://x:'+_this.hsdConfig.apiKey+'@'+_this.hsdURL+'/tx/'+tx,function(d){ 1876 | _this.last100Blocks[startAtBlock]._txDetail = d; 1877 | if(d.outputs[0].address == _this.hsdWallet){ 1878 | _this.last100Blocks[startAtBlock].isMyBlock = true; 1879 | } 1880 | console.log('startat block is defined?',parseInt(startAtBlock)-1,blocks[parseInt(startAtBlock)-1]); 1881 | if(typeof blocks[parseInt(startAtBlock)-1] == "undefined"){ 1882 | _this.syncBlocks(parseInt(startAtBlock)-1); 1883 | } 1884 | else{ 1885 | //done! 1886 | _this.renderLastBlocks(_this.last100Blocks); 1887 | } 1888 | }); 1889 | 1890 | }); 1891 | } 1892 | startMiner(){ 1893 | this.isMining = true; 1894 | process.env.HANDYRAW = 'true'; 1895 | if(typeof this.minerProcess != "undefined"){ 1896 | this.minerProcess.kill(); 1897 | this.hashRates = {}; 1898 | } 1899 | 1900 | if(process.platform != 'darwin'){ 1901 | //mac cant write the config, make sure distro has a goldshell.json dummy in the cli main dir 1902 | fs.writeFileSync(nw.__dirname+'/submodules/HandyMiner-Goldshell-CLI/goldshell.json',JSON.stringify(this.config,null,2),'utf8'); //make a default config 1903 | } 1904 | 1905 | const handyMinerParams = [ 1906 | './mine.js', 1907 | this.config.asics, 1908 | '',//this.config.gpu_platform, 1909 | '',//this.config.gpu_mfg, 1910 | 'authorize', 1911 | '',//this.hsdConfig.wallet, 1912 | this.config.stratum_user, 1913 | this.config.stratum_pass, 1914 | this.config.host, 1915 | this.config.port, 1916 | this.config.intensity || "10", 1917 | this.config.mode || "pool", 1918 | this.config.poolDifficulty || -1, 1919 | (this.config.muteFanfareSong ? "1" : "0") 1920 | ]; 1921 | let executable = process.platform.toLowerCase().indexOf('darwin') == 0 ? process.execPath : nw.global.__dirname+'/externals/node.exe'; 1922 | if(process.platform.indexOf('darwin') >= 0){ 1923 | executable = nw.global.__dirname+'/externals/node'; 1924 | } 1925 | if(process.platform.indexOf('linux') >= 0){ 1926 | executable = nw.global.__dirname+'/externals/nodejs'; 1927 | } 1928 | process.env.HANDYMINER_GUI_NODE_EXEC = executable; 1929 | console.log('exec',executable); 1930 | let minerProcess = spawn(executable, 1931 | handyMinerParams, 1932 | { 1933 | cwd:nw.__dirname+'/submodules/HandyMiner-Goldshell-CLI/', 1934 | env:process.env 1935 | }); 1936 | this.minerProcess = minerProcess; 1937 | //console.log('miner process isset???',minerProcess); 1938 | 1939 | minerProcess.stderr.on('data',(d)=>{ 1940 | console.log('stderr',d.toString('utf8')); 1941 | this.pushToLogs(d.toString('utf8'),'error','miner'); 1942 | console.log('data err',d.toString('utf8')); 1943 | }) 1944 | 1945 | minerProcess.stdout.on('data',(d)=>{ 1946 | //console.log('miner stdout',d.toString('utf8')); 1947 | let lines = d.toString('utf8'); 1948 | lines = lines.split('\n').filter(l=>{ 1949 | return l.length > 0; 1950 | }); 1951 | lines.map(line=>{ 1952 | let t = 'stdout'; 1953 | 1954 | if(line.indexOf('"type":"error"') >= 0){ 1955 | t = 'error'; 1956 | } 1957 | //console.log('logs here',d.toString('utf8'),t); 1958 | this.pushToLogs(line,t,'miner'); 1959 | }) 1960 | 1961 | 1962 | this.parseMinerOut(d.toString('utf8')); 1963 | 1964 | }); 1965 | minerProcess.on('close',function(){ 1966 | //done 1967 | this.isMining = false; 1968 | console.log('miner process closed?'); 1969 | }); 1970 | } 1971 | stopMiner(){ 1972 | 1973 | if(typeof this.minerProcess != "undefined"){ 1974 | try{ 1975 | this.minerProcess.stdin.write('dashboard sigint'); 1976 | } 1977 | catch(e){ 1978 | 1979 | } 1980 | //this.minerProcess.kill(); 1981 | this.isMining = false; 1982 | this.hashRates = {}; 1983 | $('.gpuIcon').each(function(){ 1984 | $('li',this).eq(1).html('--GH') 1985 | }); 1986 | $('.leftInfo.totalHashrate .value').html('--GH') 1987 | $('.leftInfo.avgHashrate .value').html('--GH'); 1988 | d3.selectAll('#right svg text').each(function(d){ 1989 | if(d.name == 'Hashrate' || d.name.indexOf('Worker') >= 0){ 1990 | d3.select(this).text(d.name+': --GH') 1991 | } 1992 | if(d.name == "Temperature"){ 1993 | d3.select(this).text(d.name+': --°C') 1994 | } 1995 | if(d.name == 'Fan RPM'){ 1996 | d3.select(this).text(d.name+': ----RPM') 1997 | } 1998 | }) 1999 | 2000 | } 2001 | } 2002 | parseMinerOut(text){ 2003 | const _this = this; 2004 | let lines = text.split('\n').filter(l=>{ 2005 | return l.length > 0; 2006 | }); 2007 | lines.map(function(line){ 2008 | let lineD; 2009 | 2010 | try{ 2011 | lineD = JSON.parse(line.trim()); 2012 | } 2013 | catch(e){ 2014 | //console.log('error parsing json',e); 2015 | } 2016 | if(lineD){ 2017 | //console.log('lineD isset',lineD); 2018 | let asicID; 2019 | switch(lineD.type){ 2020 | case 'job': 2021 | //got a new job 2022 | //should query/update charts? 2023 | break; 2024 | case 'solution': 2025 | //epic, get ready for confirmation 2026 | _this.awaitingConfirmation = true; 2027 | break; 2028 | case 'registration': 2029 | //add gpus to main 2030 | console.log('registration event',lineD); 2031 | if(typeof lineD.data.length != "undefined"){ 2032 | //empty registration passes back empty array vs object per machine 2033 | //alert user 2034 | $('.syncedIcon').addClass('alert').addClass('noAsicAlert'); 2035 | $('.statusPrefix').html(''); 2036 | $('.syncedButton .statusLabel').html('No ASICs detected'); 2037 | $('.syncedButton').show(); 2038 | 2039 | } 2040 | else{ 2041 | if($('.syncedIcon').hasClass('noAsicAlert')){ 2042 | $('.syncedIcon').removeClass('alert').removeClass('noAsicAlert').addClass('success') 2043 | $('.statusPrefix').html(''); 2044 | $('.syncedButton .statusLabel').html('Detected ASIC: '+lineD.data.serialPort); 2045 | $('.syncedButton').fadeOut(1000); 2046 | } 2047 | } 2048 | let $tmpl = $('#gpuTemplate').clone(); 2049 | $tmpl.removeAttr('id'); 2050 | //let gpuArr = gpus.split(','); 2051 | //$('.gpuStatus .gpuIcon').remove(); 2052 | /* 2053 | .data 2054 | { 2055 | serialPort:serialPath, 2056 | modelName, 2057 | firmwareVersion:fwVersion, 2058 | serial, 2059 | hashRate, 2060 | workDepth 2061 | }; 2062 | */ 2063 | //console.log('new machine registered',lineD); 2064 | _this.asicNames[lineD.data.serialPort] = lineD.data.modelName; 2065 | if(typeof _this.asicDisconnected[lineD.data.serialPort] != "undefined"){ 2066 | delete _this.asicDisconnected[lineD.data.serialPort]; 2067 | } 2068 | if($('.gpuIcon[data-id="'+lineD.data.serialPort+'"]').length == 0){ 2069 | let t = $tmpl.clone(); 2070 | t.attr('data-id',lineD.data.serialPort); 2071 | t.addClass('gpuIcon'); 2072 | let shortName = lineD.data.serialPort; 2073 | if(shortName.length > 4){ 2074 | shortName = '...'+shortName.slice(-4); 2075 | } 2076 | $('li',t).eq(0).html(shortName+'.'+lineD.data.modelName+''+lineD.data.serial.slice(1)+''); 2077 | $('li',t).eq(1).html('--GH'); 2078 | $('.gpuStatus').append(t); 2079 | } 2080 | else{ 2081 | $('.gpuIcon[data-id="'+lineD.data.serialPort+'"]').removeClass('disconnected'); 2082 | $('.halfChart .asic[data-id="'+lineD.data.serialPort+'"]').removeClass('disconnected') 2083 | } 2084 | $('.gpuIcon[data-id="'+lineD.data.serialPort+'"]').off('mouseenter').on('mouseenter',()=>{ 2085 | if($('.gpuIcon.clicked').length == 0){ 2086 | $('.halfChart#right .asic').addClass('hidden'); 2087 | $('.halfChart#right .asic[data-id="'+lineD.data.serialPort+'"]').removeClass('hidden'); 2088 | } 2089 | }).off('mouseleave').on('mouseleave',function(){ 2090 | if($('.gpuIcon.clicked').length == 0){ 2091 | $('.halfChart#right .asic').removeClass('hidden'); 2092 | } 2093 | }).off('click').on('click',function(){ 2094 | console.log("clicked"); 2095 | let isClicked = $(this).hasClass('clicked'); 2096 | if(isClicked){ 2097 | $('.gpuIcon').removeClass('clicked'); 2098 | $('#right .asic').removeClass('hidden'); 2099 | } 2100 | else{ 2101 | $('.gpuIcon').not($(this)).removeClass('clicked'); 2102 | $(this).addClass('clicked'); 2103 | $('.halfChart#right .asic').addClass('hidden'); 2104 | $('.halfChart#right .asic[data-id="'+lineD.data.serialPort+'"]').removeClass('hidden'); 2105 | if($('.halfChart#right .asic[data-id="'+lineD.data.serialPort+'"]').attr('data-height')){ 2106 | $('.halfChart#right .asic[data-id="'+lineD.data.serialPort+'"]').css('height',$('.halfChart#right .asic[data-id="'+lineD.data.serialPort+'"]').attr('data-height')) 2107 | } } 2108 | }) 2109 | _this.resizeStatsPanel(); 2110 | //_this.startHashrateChartTicks(); 2111 | break; 2112 | case 'difficulty': 2113 | //console.log('difficulty data recv',lineD); 2114 | /* 2115 | case 'difficulty': 2116 | this.statsData.difficulty = json.difficulty; 2117 | this.statsData.networkDifficulty = json.networkDifficulty; 2118 | this.statsData.target = json.target; 2119 | this.updateStats(this.statsData.target,'target'); 2120 | break; 2121 | */ 2122 | asicID = lineD.asic; 2123 | let localDifficulty = lineD.difficulty; 2124 | let networkDifficulty = lineD.networkDifficulty; 2125 | 2126 | _this.difficulty[asicID] = { 2127 | local:localDifficulty, 2128 | network:networkDifficulty 2129 | }; 2130 | 2131 | 2132 | break; 2133 | case 'status': 2134 | case 'asicStats': 2135 | /* 2136 | data:{ 2137 | temp:this.asicStats[asicID].temp, 2138 | fanRpm: this.asicStats[asicID].fanRpm, 2139 | voltage: this.asicStats[asicID].voltage, 2140 | frequency: this.asicStats[asicID].frequency, 2141 | asicID: asicID, 2142 | name: this.asicNames[asicID].modelName, 2143 | serial: this.asicNames[asicID].serial, 2144 | hashrateNow: Math.round(perAsicRateNow[asicID]*100)/100, 2145 | hashrateAvg: Math.round(perAsicRateAvg[asicID]*100)/100, 2146 | workerHashrates:workerHashrates, 2147 | solutions: this.asicShares[asicID], 2148 | lastNonce: this.asicJobHashrates[asicID+'_'+1].last 2149 | } 2150 | */ 2151 | //gpu status 2152 | let hashRate = lineD.data.hashrateNow; 2153 | asicID = lineD.data.asicID; 2154 | 2155 | /*let gpuID = lineD.data[0].gpuID; 2156 | let hashRate = lineD.data[0].hashRate;*/ 2157 | // 2158 | _this.hashRates[asicID] = { 2159 | hashrateNow:lineD.data.hashrateNow, 2160 | hashrateAvg:lineD.data.hashrateAvg, 2161 | workerHashrates: lineD.data.workerHashrates, 2162 | temp:lineD.data.temp, 2163 | fan:lineD.data.fanRpm 2164 | }; 2165 | if(Object.keys(_this.asicDisconnected).length > 0){ 2166 | let blankWorkers = {}; 2167 | for(let i=1;i<=4;i++){ 2168 | blankWorkers[i] = { 2169 | hashrateNow:0, 2170 | hashrateAvg:0 2171 | } 2172 | } 2173 | Object.keys(_this.asicDisconnected).map(id=>{ 2174 | _this.hashRates[id] = { 2175 | hashrateNow:0, 2176 | hashrateAvg:0, 2177 | workerHashrates:blankWorkers, 2178 | temp:0, 2179 | fan:0 2180 | } 2181 | }) 2182 | } 2183 | let hrLine = JSON.stringify({rates:_this.hashRates,time:new Date().getTime()})+'\n'; 2184 | fs.appendFileSync(_this.appDirPath+'HandyMinerConfigs/localHashrate.jsonl',hrLine,'utf8'); 2185 | //write difficulty here because it's more regular 2186 | 2187 | let diffLine = JSON.stringify({rates:_this.difficulty,time:new Date().getTime()})+'\n'; 2188 | fs.appendFileSync(_this.appDirPath+'HandyMinerConfigs/difficulty.jsonl',diffLine,'utf8') 2189 | 2190 | //console.log('hashrates recvd',lineD); 2191 | $('.gpuIcon[data-id="'+asicID+'"] li').eq(1).html(numeral(hashRate).format('0.00')+'GH') 2192 | if(parseFloat(hashRate) > 0){ 2193 | _this.renderHashrate(); 2194 | } 2195 | /*if(hashRate > 0 && hashRate < 1000000000){ 2196 | _this.hashRates[gpuID] = hashRate; 2197 | //console.log('hashrate: ',hashRate); 2198 | $('.gpuIcon[data-id="'+gpuID+'"] li').eq(1).html(numeral(hashRate).format('0.000b').replace('B','H')) 2199 | }*/ 2200 | break; 2201 | 2202 | case 'stratumLog': 2203 | //just logs 2204 | break; 2205 | case 'confirmation': 2206 | //console.log('confirmation???',lineD); 2207 | //won a block/share 2208 | //if(_this.awaitingConfirmation){ 2209 | //if(typeof lineD.granule != "undefined"){ 2210 | //it sends out 2 messages with confirmation 2211 | //we just want to fire this 1x 2212 | _this.tickBlockConfirmation(); 2213 | _this.awaitingConfirmation = false; 2214 | //} 2215 | /*} 2216 | else{ 2217 | $.post('http://x:'+apiKey+'@'+_this.hsdURL,JSON.stringify({params:[],method:'getmininginfo'}),function(d){ 2218 | console.log('got mining info?',d); 2219 | //do nothing 2220 | 2221 | //success, we can just leave things hidden 2222 | }).fail(function(){ 2223 | _this.tickBlockConfirmation(); 2224 | _this.awaitingConfirmation = false; 2225 | }) 2226 | }*/ 2227 | break; 2228 | } 2229 | } 2230 | }) 2231 | } 2232 | tickBlockConfirmation(){ 2233 | 2234 | this.blocksSolvedLast24++; 2235 | this.allTimeBlocks++; 2236 | let formatSmallSession = this.blocksSolvedLast24 > 1000 ? '0.0a' : '0a'; 2237 | let formatSmallAllTime = this.allTimeBlocks > 1000 ? '0.0a' : '0a'; 2238 | $('.blocksToday .number').html(numeral(this.blocksSolvedLast24).format(formatSmallSession).toUpperCase()) 2239 | $('.blocksAllTime .number').html(numeral(this.allTimeBlocks).format(formatSmallAllTime).toUpperCase()); 2240 | //let bc = parseInt($('.title a').html())+1; 2241 | //$('.title a').attr('href','http://hnscan.com/block/'+bc).html(bc) 2242 | //console.log('write confirm',this.appDirPath+'HandyMinerConfigs/allTimeBlocks.json') 2243 | fs.writeFileSync(this.appDirPath+'HandyMinerConfigs/allTimeBlocks.json',JSON.stringify({count:this.allTimeBlocks}),'utf8'); 2244 | 2245 | } 2246 | checkDockerSupport(isFirstTimeLaunch){ 2247 | //check if we have docker support 2248 | const _this = this; 2249 | let checker = spawn('docker',['ps','-a']); 2250 | let resp = ''; 2251 | /* 2252 | let hsdLogs = ''; 2253 | console.log('should cwd to ',nw.__dirname+'/submodules/HandyMiner-CLI/node_modules/hsd') 2254 | this.hsdProcess = hsdProcess; 2255 | this.hsdProcess.stderr.on('data',function(d){ 2256 | console.log('hsd stderr',d.toString('utf8')) 2257 | $('#logOutput').append(d.toString('utf8')); 2258 | $('#logs').removeClass('required'); 2259 | }) 2260 | this.hsdProcess.stdout.on('data',function(d){ 2261 | console.log('hsd stdout',d.toString('utf8')); 2262 | hsdLogs += d.toString('utf8')+'\r\n'; 2263 | if(hsdLogs.length >= 15000){ 2264 | hsdLogs = hsdLogs.slice(-10000); 2265 | } 2266 | if(isFirstTimeLaunch){ 2267 | $('#logOutput').html(hsdLogs); 2268 | } 2269 | }); 2270 | */ 2271 | let hsdLogs = ''; 2272 | checker.stdout.on('data',d=>{ 2273 | //console.log('checker data',d.toString('utf8')); 2274 | resp += d.toString('utf8'); 2275 | hsdLogs += d.toString('utf8')+'\r\n'; 2276 | if(hsdLogs.length >= 15000){ 2277 | hsdLogs = hsdLogs.slice(-10000); 2278 | } 2279 | if(isFirstTimeLaunch){ 2280 | $('#logOutput').html(hsdLogs); 2281 | } 2282 | }); 2283 | checker.stderr.on('data',d=>{ 2284 | $('#logOutput').append(d.toString('utf8')); 2285 | $('#logs').removeClass('required'); 2286 | console.log('checker error',d.toString('utf8')); 2287 | }) 2288 | checker.on('close',d=>{ 2289 | //checker is done, lets look around 2290 | console.log('checker is done'); 2291 | let lines = resp.split('\n'); 2292 | let earthLine = lines.filter(l=>{ 2293 | return l.indexOf('earthlabHSD') >=0; 2294 | }); 2295 | console.log('earthLine len',earthLine.length) 2296 | if(earthLine.length > 0){ 2297 | if(earthLine[0].indexOf('Exited') >= 0){ 2298 | //this machine was exited 2299 | console.log('machine is down'); 2300 | _this.pushToLogs('#### DOCKER:: BOOTING HSD DOCKER CONTAINER #####','stdout','hsd') 2301 | $('.syncedIcon').removeClass('alert'); 2302 | $('.syncedButton .statusLabel').html('Booting Docker Container') 2303 | this.startExistingDockerMachine(); 2304 | } 2305 | else{ 2306 | //heyo machine is running 2307 | console.log('docker machine is already running'); 2308 | _this.pushToLogs('#### DOCKER:: STARTING HSD ON DOCKER CONTAINER #####','stdout','hsd') 2309 | $('.syncedIcon').removeClass('alert'); 2310 | $('.syncedButton .statusLabel').html('Starting HSD in Docker') 2311 | this.startDockerizedHSD(); 2312 | } 2313 | } 2314 | else{ 2315 | //no line = no docker box 2316 | console.log('no docker machine was present, lets make one'); 2317 | _this.pushToLogs('#### DOCKER:: STARTING NEW HSD DOCKER MACHINE, THIS WILL TAKE 2-5 MINUTES #####','stdout','hsd'); 2318 | $('.syncedIcon').removeClass('alert'); 2319 | $('.syncedButton .statusLabel').html('Building Brand New Docker Machine. This will take 2-5 minutes only once..') 2320 | this.startNewDockerMachine(isFirstTimeLaunch); 2321 | } 2322 | }) 2323 | } 2324 | pushToLogs(line,type,context){ 2325 | //context = miner | hsd 2326 | if(context == 'hsd'){ 2327 | if(type == 'stdout' && line.indexOf('[debug]') >= 0){ 2328 | return; 2329 | } 2330 | this.hsdLogs += line; 2331 | if(line.indexOf('chain/LOCK:') >= 0 && line.indexOf('Resource temporarily unavailable') >= 0){ 2332 | ///left off here 2333 | this.hsdLogs += '\r\n #############################################################'; 2334 | this.hsdLogs += '\r\n ########## WARNING HSD IS ALREADY RUNNING LOCALLY ###########'; 2335 | this.hsdLogs += '\r\n #############################################################\r\n'; 2336 | this.hsdIsRunningLocally = true; 2337 | $('.syncedIcon').removeClass('alert'); 2338 | $('.syncedButton .statusLabel').html('Found Running HSD Instance Locally'); 2339 | } 2340 | else{ 2341 | if(!this.hsdIsRunningLocally && (process.platform.indexOf('darwin') >= 0 && !this.macUseDocker)){ 2342 | $('.syncedIcon').addClass('alert'); 2343 | $('.syncedButton .statusLabel').html('Failed to start HSD'); 2344 | } 2345 | } 2346 | if(this.hsdLogs.length >= 100000){ 2347 | this.hsdLogs = this.hsdLogs.slice(-5000); 2348 | } 2349 | if(this.logsVisible){ 2350 | $('#logs pre#logOutput').html(this.hsdLogs); 2351 | $('#logs pre#logOutput')[0].scrollTop = $('#logs pre#logOutput')[0].scrollHeight; 2352 | } 2353 | } 2354 | if(context == 'miner'){ 2355 | this.minerLogs += line; 2356 | 2357 | if(this.minerLogs.length >= 20000){ 2358 | this.minerLogs = this.minerLogs.slice(-10000); 2359 | } 2360 | //if(this.logsVisible){ 2361 | //console.log('logs vis?????') 2362 | $('#logs pre#minerOutput').html(this.minerLogs); 2363 | $('#logs pre#minerOutput')[0].scrollTop = $('#logs pre#minerOutput')[0].scrollHeight; 2364 | //} 2365 | } 2366 | 2367 | let j = JSON.parse(line); 2368 | //console.log('line data',j,j.type,j.data); 2369 | if(j.type == 'stratumLog' && $('.syncedIcon').hasClass('alert') && !$('.syncedIcon').hasClass('noAsicAlert') && j.data == 'Successfully Registered With Stratum'){ 2370 | //reconnected then 2371 | $('.syncedIcon').removeClass('alert').addClass('success'); 2372 | $('.syncedButton .statusLabel').html('Connected To Pool'); 2373 | $('.syncedButton').fadeOut(1000); 2374 | } 2375 | if(type == 'error'){ 2376 | if(j.disconnected){ 2377 | //asic was disconnected 2378 | let asicID = j.data.asicID; 2379 | this.asicDisconnected[asicID] = true; 2380 | let allDisconnected = true; 2381 | Object.keys(this.asicNames).filter(id=>{ 2382 | return id != "undefined"; 2383 | }).map(id=>{ 2384 | if(!this.asicDisconnected[id]){ 2385 | allDisconnected = false; 2386 | } 2387 | }) 2388 | if(allDisconnected){ 2389 | $('.totalHashrate .value').html('--GH') 2390 | $('.avgHashrate .value').html('--GH') 2391 | } 2392 | 2393 | console.log('asic id discon',asicID) 2394 | $('.gpuIcon[data-id="'+asicID+'"]').addClass('disconnected'); 2395 | $('.logs').addClass('alerted'); 2396 | $('.halfChart .asic[data-id="'+asicID+'"]').addClass('disconnected') 2397 | } 2398 | if(j.message.indexOf('STRATUM CONNECTION WAS CLOSED') == -1){ 2399 | $('.logs').addClass('alerted'); 2400 | } 2401 | if(j.message.indexOf('RECONNECTING NOW') >= 0 && !$('.syncedIcon').hasClass('noAsicAlert')){ 2402 | $('.syncedIcon').addClass('alert'); 2403 | $('.statusPrefix').html(''); 2404 | $('.syncedButton .statusLabel').html('Connecting to Pool...'); 2405 | $('.syncedButton').show(); 2406 | 2407 | } 2408 | 2409 | } 2410 | 2411 | 2412 | } 2413 | startDockerizedHSD(){ 2414 | this.hasLogged = false; 2415 | let envVars = process.env; 2416 | //envVars.PATH = "C:\\Program\ Files\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\bin"+';'+process.env.PATH; 2417 | //let hsdProcess = spawn('docker',['exec','earthlab','sh','-c','"./bin/hsd '+(this.hsdParams.join(' '))+'"']/*,{env:envVars}*/); 2418 | let wallet = this.hsdConfig.wallet || this.hsdWallet; 2419 | console.log('docker is set to mine to wallet:: ',wallet,this.config.network); 2420 | //if no wallet fill in with one 2421 | if(wallet == ''){ 2422 | wallet = this.defaultWallet 2423 | } 2424 | let hsdProcess = spawn('docker',['exec','-i','earthlabHSD','sh','-c','"./run.sh\ '+wallet+'\ '+(this.config.network || 'main')+'"'],{shell:true}) 2425 | //let hsdLogs = ''; 2426 | hsdProcess.stdout.on('data',d=>{ 2427 | //console.log('hsd data',d.toString('utf8')); 2428 | if(!this.hasLogged){ 2429 | this.hsdIsRunningLocally = true; 2430 | this.hasLogged = true; 2431 | this.hideLoading(); 2432 | setTimeout(()=>{ 2433 | this.addPeers(); 2434 | },1000); 2435 | } 2436 | 2437 | 2438 | 2439 | this.pushToLogs(d.toString('utf8'),'stdout','hsd'); 2440 | 2441 | 2442 | }) 2443 | hsdProcess.stderr.on('data',d=>{ 2444 | //console.log('hsd error',d.toString('utf8')); 2445 | //hsdLogs += d.toString('utf8'); 2446 | this.pushToLogs(d.toString('utf8'),'error','hsd'); 2447 | }) 2448 | hsdProcess.on('close',d=>{ 2449 | console.log('hsd process closed'); 2450 | }); 2451 | this.hsdProcess = hsdProcess; 2452 | this.initLogo(); 2453 | setTimeout(()=>{ 2454 | this.getHSDNetworkInfo(); 2455 | 2456 | //_this.startTimer(); 2457 | },3000) 2458 | } 2459 | stopDockerizedHSD(){ 2460 | this.restartDockerContainer(false); 2461 | this.hsdProcess.kill(); 2462 | } 2463 | stopDockerMachine(){ 2464 | this.restartDockerContainer(false); 2465 | let fin = spawn('docker',['stop','earthlabHSD']); 2466 | fin.on('close',d=>{ 2467 | return; 2468 | }); 2469 | } 2470 | startExistingDockerMachine(){ 2471 | let startD = spawn('docker',['start','earthlabHSD']); 2472 | startD.stdout.on('data',d=>{ 2473 | console.log('startD data',d.toString('utf8')); 2474 | this.pushToLogs('## DOCKER:: '+d.toString('utf8'),'stdout','hsd'); 2475 | }) 2476 | startD.stderr.on('data',d=>{ 2477 | console.log('startD error',d.toString('utf8')); 2478 | this.pushToLogs('## DOCKER:: '+d.toString('utf8'),'error','hsd'); 2479 | }) 2480 | startD.on('close',d=>{ 2481 | this.startDockerizedHSD(); 2482 | console.log('started existing docker box!') 2483 | }) 2484 | } 2485 | startNewDockerMachine(){ 2486 | //first we'll create the docker machine 2487 | //first lets check if docker ls lists any eartlabs... 2488 | let hsdLogs = ''; 2489 | let listData = ''; 2490 | let listD = spawn('docker',['image','ls']); 2491 | listD.stdout.on('data',d=>{ 2492 | listData += d.toString('utf8'); 2493 | hsdLogs += d.toString('utf8')+'\r\n'; 2494 | if(hsdLogs.length >= 15000){ 2495 | hsdLogs = hsdLogs.slice(-10000); 2496 | } 2497 | //$('#logOutput').html(hsdLogs); 2498 | $('.syncedButton .statusLabel').html('Building Brand New Docker Machine. This will take 2-5 minutes only once..') 2499 | this.pushToLogs('## DOCKER:: '+d.toString('utf8'),'stdout','hsd'); 2500 | }); 2501 | /*listD.stderr.on('dtaa',d=>{ 2502 | 2503 | })*/ 2504 | listD.on('close',d=>{ 2505 | $('.syncedButton .statusLabel').html('Built Docker Container!') 2506 | console.log('start listD stdout',listData,listData.indexOf('earthlab')); 2507 | if(listData.indexOf('earthlab') == -1){ 2508 | //we dont need to build image 2509 | this.createDockerImage(); 2510 | } 2511 | else{ 2512 | this.createDockerContainer(); 2513 | } 2514 | }); 2515 | 2516 | 2517 | } 2518 | 2519 | createDockerImage(){ 2520 | let wasError = false; 2521 | let createD = spawn('docker',['build', '-t', 'earthlab', '.'],{cwd:nw.__dirname+'/submodules/HandyMiner-CLI/windows_utils'}); 2522 | console.log('create image was called'); 2523 | let hsdLogs = ''; 2524 | createD.stdout.on('data',d=>{ 2525 | console.log('creating docker machine',d.toString('utf8')); 2526 | //here 2527 | /*hsdLogs += d.toString('utf8')+'\r\n'; 2528 | if(hsdLogs.length >= 15000){ 2529 | hsdLogs = hsdLogs.slice(-10000); 2530 | }*/ 2531 | //$('#logOutput').html(hsdLogs); 2532 | $('.syncedIcon').removeClass('alert'); 2533 | $('.syncedButton .statusLabel').html('Creating Docker Image. This may take 2-5 minutes only once..'); 2534 | this.pushToLogs('## DOCKER:: '+d.toString('utf8'),'stdout','hsd'); 2535 | }) 2536 | createD.stderr.on('data',d=>{ 2537 | wasError = true; 2538 | /*hsdLogs += d.toString('utf8')+'\r\n'; 2539 | if(hsdLogs.length >= 15000){ 2540 | hsdLogs = hsdLogs.slice(-10000); 2541 | } 2542 | $('#logOutput').html(hsdLogs);*/ 2543 | this.pushToLogs('## DOCKER:: '+d.toString('utf8'),'error','hsd'); 2544 | $('.syncedIcon').addClass('alert'); 2545 | $('.syncedButton .statusLabel').html('ERROR CREATING DOCKER IMAGE') 2546 | $('#logs').removeClass('required'); 2547 | console.log('err create docker machine',d.toString('utf8')); 2548 | }) 2549 | createD.on('close',d=>{ 2550 | //we should now make our container 2551 | console.log('createD is closed now',wasError); 2552 | if(!wasError){ 2553 | this.createDockerContainer(); 2554 | } 2555 | }) 2556 | } 2557 | createDockerContainer(){ 2558 | let wasContainerError = false; 2559 | let hsdLogs = ''; 2560 | console.log('create docker container called'); 2561 | let containerD = spawn('docker', ['run', '-p', '13937:13037', '-p', '13938:13038', '-p', '14937:14037','-p','14938:14038', '-p', '12937:12037', '-p', '12938:12038', '-p', '3008:3008', '-p', '15937:15037', '-p', '15938:15038', '-p', '5301:5301', '-p', '13038:13038', '-p', '15359:15359', '--expose', '3008', '--name', 'earthlabHSD', '-td', 'earthlab'],{cwd:nw.__dirname+'/submodules/HandyMiner-CLI/windows_utils'}); 2562 | containerD.stdout.on('data',d=>{ 2563 | /*hsdLogs += d.toString('utf8')+'\r\n'; 2564 | if(hsdLogs.length >= 15000){ 2565 | hsdLogs = hsdLogs.slice(-10000); 2566 | } 2567 | $('#logOutput').html(hsdLogs);*/ 2568 | $('.syncedIcon').removeClass('alert'); 2569 | $('.syncedButton .statusLabel').html('Creating Docker Container. This may take 2-5 minutes only once..') 2570 | this.pushToLogs('## DOCKER:: '+d.toString('utf8'),'stdout','hsd'); 2571 | console.log('container creation data',d.toString('utf8')); 2572 | }) 2573 | containerD.stderr.on('data',d=>{ 2574 | wasContainerError = true; 2575 | /*hsdLogs += d.toString('utf8'); 2576 | $('#logOutput').html(hsdLogs); 2577 | $('#logs').removeClass('required');*/ 2578 | this.pushToLogs('## DOCKER:: '+d.toString('utf8'),'error','hsd'); 2579 | $('.syncedIcon').addClass('alert'); 2580 | $('.syncedButton .statusLabel').html('Error creating Docker container') 2581 | console.log('container creation ERROR',d.toString('utf8')); 2582 | }); 2583 | containerD.on('close',d=>{ 2584 | console.log('containerD was closed'); 2585 | if(!wasContainerError){ 2586 | //lets start 2587 | this.startDockerizedHSD(); 2588 | } 2589 | }) 2590 | } 2591 | restartDockerContainer(doRestart){ 2592 | const _this = this; 2593 | let containerD = spawn('docker',['exec','-i','earthlabHSD','sh','-c','"./stop.sh"'],{shell:true}); 2594 | console.log('stop docker is called then') 2595 | containerD.on('close',d=>{ 2596 | //console.log('finished docker close cmd',d.toString('utf8')); 2597 | 2598 | }); 2599 | containerD.stderr.on('data',d=>{ 2600 | console.log('docker close error??',d.toString('utf8')); 2601 | if(d.toString('utf8').indexOf('No such container') >= 0){ 2602 | //just initializing then 2603 | console.log('starting container') 2604 | _this.checkDockerSupport(true); 2605 | } 2606 | }) 2607 | containerD.stdout.on('data',d=>{ 2608 | // 2609 | if(doRestart){ 2610 | console.log('docker close data',d.toString('utf8')) 2611 | console.log('should do docker stopping process') 2612 | setTimeout(function(){ 2613 | _this.startDockerizedHSD(); 2614 | },1000); 2615 | } 2616 | }) 2617 | 2618 | } 2619 | nukeDockerContainer(){ 2620 | const _this = this; 2621 | console.log('nuke docker container was called'); 2622 | let containerD = spawn('docker',['stop','earthlabHSD','&&','docker','rm','earthlabHSD','&&','docker','image','rm','earthlab'],{shell:true}); 2623 | containerD.on('close',d=>{ 2624 | console.log('removed docker container, now make it again') 2625 | _this.checkDockerSupport(true); 2626 | }) 2627 | containerD.stdout.on('data',d=>{ 2628 | console.log('nuke docker continer msg',d.toString('utf8')) 2629 | }) 2630 | containerD.stderr.on('data',d=>{ 2631 | console.log('cant nuke docker continer',d.toString('utf8')) 2632 | }) 2633 | } 2634 | animateLogo(){ 2635 | window.requestAnimationFrame(()=>{ 2636 | if(this.shouldRenderLogo){ 2637 | this.animateLogo(); 2638 | } 2639 | 2640 | }); 2641 | //this.controls.update(); 2642 | this.renderer.render(this.scene,this.camera); 2643 | } 2644 | initLogo(){ 2645 | this.shouldRenderLogo = true; 2646 | this.scene = new THREE.Scene(); 2647 | let color = 0xeeeeee; 2648 | if($('html').attr('id') == 'dark'){ 2649 | color = 0x222222 2650 | } 2651 | this.scene.background = new THREE.Color(color) 2652 | this.timeGroup = new THREE.Object3D(); 2653 | this.scene.add(this.timeGroup); 2654 | this.highlightLinesGroup = new THREE.Object3D(); 2655 | this.timeGroup.add(this.highlightLinesGroup); 2656 | this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 100000 ); 2657 | //this.camera.position.z = -100; 2658 | //this.controls = new THREE.TrackballControls(this.camera,$('#introLogo')[0]); 2659 | this.renderer = new THREE.WebGLRenderer({antialias:true}); 2660 | this.clock = new THREE.Clock(); 2661 | this.camera.position.x = -window.innerWidth * 2; 2662 | //this.controls.target = new THREE.Vector3(0,0,0) 2663 | this.camera.lookAt(new THREE.Vector3(0,0,0)); 2664 | //this.controls.target0 = this.controls.target.clone(); 2665 | //this.controls.update(); 2666 | this.toggle = 0; 2667 | this.renderer.setSize( Math.floor(window.innerWidth-1), Math.floor(window.innerHeight-1) ); 2668 | this.$el = $('#introLogo'); 2669 | $('#introLogo').append( this.renderer.domElement ); 2670 | this.animateLogo(); 2671 | var geometry = new THREE.Geometry();//new createGeom(complex) 2672 | //var bufferGeometry = new THREE.BufferGeometry(); 2673 | geometry.dynamic = true; 2674 | for(var i = 0;i<577;i++){ 2675 | var f0 = i *3 +0; 2676 | var f1 = i*3 + 1; 2677 | var f2 = i*3 + 2; 2678 | var face = new THREE.Face3(f0,f1,f2) 2679 | geometry.faces.push(face); 2680 | } 2681 | var bufferGeometry; 2682 | var _this = this; 2683 | 2684 | $.getJSON('./glsl/handshake.json',function(d){ 2685 | 2686 | var directions = new Float32Array(d.direction.value.length*3); 2687 | var centroids = new Float32Array(d.centroid.value.length*3); 2688 | var vertices = d.vertices; 2689 | 2690 | d.direction.value.map(function(v,i){ 2691 | directions[i*3+0] = v.x; 2692 | directions[i*3+1] = v.y; 2693 | directions[i*3+2] = v.z; 2694 | geometry.vertices.push(new THREE.Vector3(vertices[i*3+0],vertices[i*3+1],vertices[i*3+2])) 2695 | }); 2696 | d.centroid.value.map(function(c,i){ 2697 | centroids[i*3+0] = c.x; 2698 | centroids[i*3+1] = c.y; 2699 | centroids[i*3+2] = c.z; 2700 | }) 2701 | 2702 | //console.log('three geometry isset',geometry); 2703 | //console.log('centroids',centroids,directions); 2704 | bufferGeometry = new THREE.BufferGeometry().fromGeometry(geometry) 2705 | bufferGeometry.addAttribute( 'direction', new THREE.BufferAttribute( directions, 3 ) ); 2706 | bufferGeometry.addAttribute( 'centroid', new THREE.BufferAttribute( centroids, 3 ) ); 2707 | 2708 | let color = new THREE.Vector3(0,0,0); 2709 | if($('html').attr('id') == 'dark'){ 2710 | color = new THREE.Vector3(180/255,180/255,180/255) 2711 | } 2712 | const material = new THREE.ShaderMaterial({ 2713 | vertexShader: document.getElementById('logoVertexShader').textContent, 2714 | fragmentShader: document.getElementById('logoFragmentShader').textContent, 2715 | wireframe: false, 2716 | transparent: true, 2717 | opacity:0.5, 2718 | side:THREE.DoubleSide, 2719 | //attributes: bufferGeometry, 2720 | uniforms: { 2721 | opacity: { type: 'f', value: 1 }, 2722 | scale: { type: 'f', value: 0 }, 2723 | animate: { type: 'f', value: 0 }, 2724 | color: { type: 'v3', value: color} 2725 | } 2726 | }) 2727 | bufferGeometry.computeBoundingSphere(); 2728 | const mesh = new THREE.Mesh(bufferGeometry, material) 2729 | mesh.rotation.setFromVector3(new THREE.Vector3(0,-Math.PI/2,0)) 2730 | _this.scene.add(mesh); 2731 | setTimeout(function(){ 2732 | addLogoTransition(mesh); 2733 | },100) 2734 | 2735 | function addLogoTransition(mesh){ 2736 | var i = 0; 2737 | var si = setInterval(function(){ 2738 | if(i >= 1.0){ 2739 | i = 0; 2740 | mesh.material.uniforms.animate.value = 1.0; 2741 | mesh.material.uniforms.scale.value = 1.0; 2742 | 2743 | clearInterval(si); 2744 | setTimeout(function(){ 2745 | removeLogo(mesh); 2746 | },500) 2747 | return false; 2748 | } 2749 | i+= 0.04; 2750 | mesh.material.uniforms.animate.value = i; 2751 | mesh.material.uniforms.scale.value = i; 2752 | mesh.position.set(_this.camera.position.x+2.15,_this.camera.position.y + 0.1,_this.camera.position.z); 2753 | //mesh.lookAt(_this.camera.position); 2754 | },21) 2755 | } 2756 | 2757 | _this.logoMesh = mesh; 2758 | //console.log('logo mesh',mesh) 2759 | }) 2760 | function removeLogo(mesh){ 2761 | var i = 0; 2762 | mesh.material.wireframe = true; 2763 | //$('#nameList').addClass('showing'); 2764 | 2765 | var si2 = setInterval(function(){ 2766 | if(i >= 1.0){ 2767 | mesh.material.uniforms.animate.value = 1.0 - i; 2768 | mesh.material.uniforms.scale.value = 1.0 - i; 2769 | 2770 | clearInterval(si2); 2771 | _this.cameraUnlocked = true; 2772 | _this.shouldRenderLogo = false; 2773 | $('#introLogo').addClass('hidden'); 2774 | /*$('#nameList li').eq(1).trigger('mouseenter'); 2775 | $('#instructions').fadeIn(); 2776 | $('#modes').fadeIn(); 2777 | setTimeout(function(){ 2778 | $('#nameList li').eq(1).trigger('mouseenter'); 2779 | },250)*/ 2780 | //ok hide logo and stop rendering three here 2781 | return false; 2782 | } 2783 | i += 0.03; 2784 | 2785 | mesh.material.uniforms.animate.value = 1.0-i; 2786 | mesh.material.uniforms.scale.value = 1.0-i; 2787 | mesh.position.set(_this.camera.position.x+2.15,_this.camera.position.y + 0.1,_this.camera.position.z); 2788 | //mesh.lookAt(_this.camera.position); 2789 | },21) 2790 | } 2791 | } 2792 | } 2793 | -------------------------------------------------------------------------------- /miner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 49 | 50 | 51 | 52 | 53 | 110 | 120 | 130 | 131 |
201 | 263 | 280 | 295 |
296 |
297 | 298 | 302 |
303 |
304 | 305 |
306 |
307 |
--Session Shares
308 |
--Shares All Time
309 |
310 |
311 |
312 |
313 |
---GH
314 |
Total Hashrate
315 |
316 |
317 |
---GH
318 |
Avg Hashrate
319 |
320 |
321 |
---
322 |
Network Diff
323 |
324 |
325 |
---
326 |
Share Diff
327 |
328 |
329 | 332 |
333 |
334 | 335 | Local Node 336 | Starting Synchronization... 337 |
338 | 339 |
340 |
341 |
342 |
📜
343 |
344 | 345 |
346 | 356 |
357 |
358 |
359 |
360 | 369 |
370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HandyMiner", 3 | "main": "miner.html", 4 | "nodejs": true, 5 | "window": { 6 | "width": 840, 7 | "frame": true, 8 | "resizable": true, 9 | "position": "center", 10 | "height": 580, 11 | "icon": "icons/app_256x256x32.png" 12 | }, 13 | "inject_js_start":"js/enableThemes.js", 14 | "node-remote": { 15 | "scheme": "file" 16 | }, 17 | "dependencies": { 18 | "async-exit-hook": "^2.0.1", 19 | "bheep": "^0.1.4", 20 | "blessed": "^0.1.81", 21 | "blessed-contrib": "^4.8.16", 22 | "bmutex": "^0.1.5", 23 | "bn.js": "^4.11.8", 24 | "bsert": "0.0.8", 25 | "buffer-map": "0.0.5", 26 | "bufio": "^1.0.6", 27 | "inquirer": "^7.0.0", 28 | "moment": "^2.24.0", 29 | "numeral": "^2.0.6", 30 | "play-sound": "^1.1.3", 31 | "serialport": "^9.0.0" 32 | } 33 | } 34 | --------------------------------------------------------------------------------