· 8 years ago · Mar 30, 2018, 11:28 PM
1# files, directories
2$ConfigFile = [io.path]::combine($PSScriptRoot, "rudehash.json")
3$MinersDir = [io.path]::combine($PSScriptRoot, "miners")
4$ToolsDir = [io.path]::combine($PSScriptRoot, "tools")
5$TempDir = [io.path]::combine($PSScriptRoot, "temp")
6
7# make sure math functions work regardless of regional settings
8[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture
9
10# globals
11$Version = "8.0-dev"
12[System.Collections.Hashtable]$Config = @{}
13[System.Collections.Hashtable]$FileConfig = @{}
14# current keys: Api CoinMode DevMining Port Rates
15[System.Collections.Hashtable]$SessionConfig = @{}
16[System.Collections.Hashtable]$Profile = @{}
17$FirstRun = $false
18$FirstLoop = $true
19# hsrminer uses fixed port, so let's change it for all other miners
20$MinerPort = 4001
21$BtcDigits = 6
22$FiatDigits = 2
23$BlockchainUrl = "https://blockchain.info/ticker"
24$MonitoringUrl = "https://rudehash.org/monitor/miner.php"
25$MphStatsUrl = "https://miningpoolhubstats.com/api/worker/"
26$ZergpoolCoinsUrl = "http://api.zergpool.com:8080/api/currencies"
27$MinerApiErrorStr = "Malformed miner API response."
28
29function Exit-RudeHash ()
30{
31 Read-Host -Prompt "Press Enter to exit..."
32 Exit
33}
34
35function Write-Pretty ($BgColor, $String)
36{
37 $WindowWidth = $Host.UI.RawUI.MaxWindowSize.Width
38
39 ForEach ($Line in $($String -split "`r`n"))
40 {
41 $SpaceCount = $WindowWidth - ($Line.length % $WindowWidth)
42 $Line += " " * $SpaceCount
43
44 Write-Host -ForegroundColor White -BackgroundColor $BgColor $Line
45 }
46}
47
48function Write-PrettyDots ()
49{
50 $WindowWidth = $Host.UI.RawUI.MaxWindowSize.Width - 1
51 $String += "`u{2219}" * $WindowWidth
52
53 Write-Pretty DarkCyan $String
54}
55
56function Write-PrettyHeader ()
57{
58 Write-PrettyDots
59 Write-Pretty DarkCyan "RudeHash $($Version) NVIDIA Miner `u{00a9} gradinkov"
60 Write-PrettyDots
61}
62
63function Write-PrettyError ($String)
64{
65 Write-Pretty Red $String
66}
67
68function Write-PrettyDebug ($String)
69{
70 Write-Pretty Magenta $String
71}
72
73function Write-PrettyInfo ($String)
74{
75 Write-Pretty DarkBlue $String
76}
77
78function Write-PrettyEarnings ($String)
79{
80 Write-Pretty DarkGreen $String
81}
82
83$Pools =
84@{
85 "alttank" =
86 @{
87 Name = "AltTank"
88 PoolFee = 0.25
89 Authless = $true
90 Regions = $false
91 StratumProto = 0
92 Coins =
93 @{
94 "pgn" = @{ Server = "alttank.ca"; Port = 4646 }
95 }
96 }
97
98 "blockcruncher" =
99 @{
100 Name = "BlockCruncher"
101 PoolFee = 0.25
102 Authless = $true
103 Regions = $false
104 StratumProto = 0
105 Coins =
106 @{
107 "pgn" = @{ Server = "blockcruncher.com"; Port = 3636 }
108 }
109 }
110
111 "bsod" =
112 @{
113 Name = "TheBSODPool"
114 PoolFee = 0.9
115 Authless = $true
116 Regions = $true
117 StratumProto = 0
118 Coins =
119 @{
120 "pgn" = @{ Server = "%REGION%bsod.pw"; Port = 2196 }
121 }
122 }
123
124 "coinminers" =
125 @{
126 Name = "CoinMiners"
127 PoolFee = 0.1
128 Authless = $true
129 Regions = $false
130 StratumProto = 0
131 Coins =
132 @{
133 "pgn" = @{ Server = "stratum-eu.coin-miners.info"; Port = 3663 }
134 }
135 }
136
137 "corpopool" =
138 @{
139 Name = "CorpoPool"
140 PoolFee = 0.5
141 Authless = $true
142 Regions = $false
143 StratumProto = 0
144 Coins =
145 @{
146 "pgn" = @{ Server = "pool.corpopool.com"; Port = 194 }
147 }
148 }
149
150 "cryptopool" =
151 @{
152 Name = "Crypto Pool Party"
153 PoolFee = 0.1
154 Authless = $true
155 Regions = $false
156 StratumProto = 0
157 Coins =
158 @{
159 "pgn" = @{ Server = "cryptopool.party"; Port = 3737 }
160 }
161 }
162
163 "faster" =
164 @{
165 Name = "//FASTER"
166 PoolFee = 0.0
167 Authless = $true
168 Regions = $false
169 StratumProto = 0
170 Coins =
171 @{
172 "pgn" = @{ Server = "fasterpool.com"; Port = 3663 }
173 }
174 }
175
176 "huopool" =
177 @{
178 Name = "HuoPool"
179 PoolFee = 2.0
180 Authless = $true
181 Regions = $false
182 StratumProto = 0
183 Coins =
184 @{
185 "pgn" = @{ Server = "x16s.huopool.com"; Port = 3663 }
186 }
187 }
188
189 "kwchmining" =
190 @{
191 Name = "KWCHMining"
192 PoolFee = 0.6
193 Authless = $true
194 Regions = $false
195 StratumProto = 0
196 Coins =
197 @{
198 "pgn" = @{ Server = "kwchmining.com"; Port = 3333 }
199 }
200 }
201
202 "lycheebit" =
203 @{
204 Name = "LycheeBit"
205 PoolFee = 1.0
206 Authless = $true
207 Regions = $false
208 StratumProto = 0
209 Coins =
210 @{
211 "pgn" = @{ Server = "lycheebit.com"; Port = 3637 }
212 }
213 }
214
215 "robocop" =
216 @{
217 Name = "Robocop.Online"
218 PoolFee = 0.5
219 Authless = $true
220 Regions = $false
221 StratumProto = 0
222 Coins =
223 @{
224 "pgn" = @{ Server = "robocop.online"; Port = 3663 }
225 }
226 }
227
228 "slinging" =
229 @{
230 Name = "Slinging"
231 PoolFee = 1.0
232 Authless = $true
233 Regions = $false
234 StratumProto = 0
235 Coins =
236 @{
237 "pgn" = @{ Server = "pgn.slinging.io"; Port = 3333 }
238 }
239 }
240
241 "suprnova" =
242 @{
243 Name = "Suprnova"
244 PoolFee = 1.0
245 Authless = $false
246 Regions = $false
247 StratumProto = 2
248 Coins =
249 @{
250 "pgn" = @{ Server = "pign.suprnova.cc"; Port = 4096 }
251 }
252 }
253
254 "xitech" =
255 @{
256 Name = "XiTech"
257 PoolFee = 1.0
258 Authless = $true
259 Regions = $false
260 StratumProto = 0
261 Coins =
262 @{
263 "pgn" = @{ Server = "x-i-tech.com"; Port = 3638 }
264 }
265 }
266
267 "zergpool" =
268 @{
269 Name = "Zergpool"
270 PoolFee = 0.5
271 Authless = $true
272 Regions = $false
273 StratumProto = 0
274 ApiUrl = "http://api.zergpool.com:8080/api/status"
275 Coins =
276 @{
277 "pgn" = @{ Server = "x16s.mine.zergpool.com"; Port = 3663 }
278 }
279 }
280}
281
282$Coins =
283@{
284 "pgn" = @{ Name = "Pigeoncoin"; Algo = "x16s" }
285}
286
287$Miners =
288@{
289 "nevermore-x16s" = @{ Url = "https://github.com/brian112358/nevermore-miner-x16s/releases/download/v0.1.1/nevermore-x16s-v0.1.1-win64.zip"; ArchiveFile = "nevermore-z16s.zip"; ExeFile = "ccminer.exe"; FilesInRoot = $false; Algos = @("x16s"); Api = $true }
290 "suprminer" = @{ Url = "https://github.com/ocminer/suprminer/releases/download/1.2/suprminer-1.2.7z"; ArchiveFile = "suprminer.7z"; ExeFile = "ccminer.exe"; FilesInRoot = $false; Algos = @("x16s"); Api = $true }
291}
292
293$ExcavatorAlgos =
294@{
295 "ethash" = "daggerhashimoto"
296 "equihash" = "equihash"
297 "lyra2v2" = "lyra2rev2"
298 "neoscrypt" = "neoscrypt"
299 "nist5" = "nist5"
300}
301
302$Tools =
303@{
304 "7zip" = @{ Url = "http://7-zip.org/a/7za920.zip"; ArchiveFile = "7zip.zip"; ExeFile = "7za.exe"; FilesInRoot = $true }
305}
306
307$Regions =
308@{
309 "bsod" = @("eu1", "eu2", "pool")
310 "miningpoolhub" = @("asia", "europe", "us-east")
311 "nicehash" = @("br", "eu", "hk", "in", "jp", "usa")
312}
313
314# MPH returns all hashrates in kH/s but WTM uses different magnitudes for different algos
315$WtmModifiers =
316@{
317 "bitcore" = 1000000
318 "ethash" = 1000000
319 "equihash" = 1
320 "keccakc" = 1000000
321 "lyra2v2" = 1000
322 "lyra2z" = 1000
323 "neoscrypt" = 1000
324 "nist5" = 1000000
325 "phi" = 1000000
326 "tribus" = 1000000
327 "xevan" = 1000000
328}
329
330$AlgoNames =
331@{
332 "allium" = "Allium"
333 "bitcore" = "TimeTravel10"
334 "ethash" = "Ethash"
335 "equihash" = "Equihash"
336 "hsr" = "HSR"
337 "keccakc" = "Keccak-C"
338 "lyra2v2" = "Lyra2REv2"
339 "lyra2z" = "Lyra2Z"
340 "neoscrypt" = "NeoScrypt"
341 "nist5" = "Nist5"
342 "phi" = "PHI1612"
343 "polytimos" = "Polytimos"
344 "tribus" = "Tribus"
345 "x16r" = "X16R"
346 "x16s" = "X16S"
347 "xevan" = "Xevan"
348}
349
350$Remarks =
351@{
352 "Debug" = "If true, RudeHash will print diagnostic information along with the rest. Useful for troubleshooting."
353 "Watchdog" = "If true, RudeHash will restart the miner if it reports 0 hashrate for 5 consecutive minutes."
354 "MonitoringKey" = "Allows for monitoring your rigs at rudehash.org/monitor. A newly generated random key for you:`r`n$(New-Guid)"
355 "MphApiKey" = "Allows for monitoring at miningpoolhubstats.com using your MPH API key."
356 #"ActiveProfile" = "The profile currently in use, starting at 1."
357 # Pool
358 "Worker" = "Your rig's nickname."
359 "Wallet" = "Your Pigeoncoin wallet. Ignored on pools that use a site balance."
360 "WalletIsAltcoin" = "If true, your wallet's currency is the same as the coin you're mining, and you're paid directly to this wallet for mining a coin. If false, your wallet is Bitcoin, and the pool auto-exchanges the mined coins to BTC. Ignored on pools that use a site balance."
361 "User" = "Pool user name. Ignored on pools that use a wallet address."
362 # Region
363 # Coin
364 # Miner
365 # Algo
366 "Currency" = "Fiat currency used for calculating your earnings."
367 "ElectricityCost" = "The price you pay for electricity, <your currency>/kWh, decimal separator is '.'"
368 "ExtraArgs" = "Any additional command line arguments you want to pass to the miner."
369}
370
371# we build these dynamically
372[System.Collections.Hashtable]$BtcRates = @{}
373[System.Collections.Hashtable]$ZergpoolCoins = @{}
374
375function Set-Property ($Name, $Value, $Permanent, $ProfileItem)
376{
377 if ($ProfileItem)
378 {
379 if (-Not ($Config.Profiles))
380 {
381 [System.Collections.ArrayList]$Config.Profiles = @()
382 $Config.Profiles.Add([System.Collections.Hashtable] @{}) | Out-Null
383 }
384
385 # we never call this before ActiveProfile is set
386 $Config.Profiles[$Config.ActiveProfile - 1].$Name = $Value
387 $Profile.$Name = $Value
388 }
389 else
390 {
391 $Config.$Name = $Value
392 }
393
394 if ($Permanent)
395 {
396 try
397 {
398 $Config | ConvertTo-Json | Out-File -FilePath $ConfigFile -Encoding utf8NoBOM
399 }
400 catch
401 {
402 Write-PrettyError "Error writing '$ConfigFile'!"
403
404 if ($Config.Debug)
405 {
406 Write-PrettyDebug $_.Exception
407 }
408 }
409 }
410}
411
412function Get-CoinSupport ()
413{
414 $Table = New-Object System.Data.DataTable
415 $Table.Columns.Add("Coin", "string") | Out-Null
416 $Table.Columns.Add("Algo", "string") | Out-Null
417 $Table.DefaultView.Sort = "Coin ASC"
418
419 $Support = "Supported coins and their algos:"
420 foreach ($Key in $Coins.Keys)
421 {
422 $Row = $Table.NewRow()
423 $Row.Coin = $Key.ToUpper()
424 $Algos = ""
425 $Algos += $Coins[$Key].Algo
426 $Row.Algo = $Algos
427 $Table.Rows.Add($Row)
428 }
429
430 $Table = $Table.DefaultView.ToTable()
431 # use Format-Table to force flushing to screen immediately
432 $Support += Out-String -InputObject ($Table | Format-Table)
433 $Table.Dispose()
434
435 return $Support
436}
437
438function Get-MinerSupport ()
439{
440 $Table = New-Object System.Data.DataTable
441 $Table.Columns.Add("Miner", "string") | Out-Null
442 $Table.Columns.Add("Algo", "string") | Out-Null
443 $Table.DefaultView.Sort = "Miner ASC"
444
445 $Support += "Supported miners and algos:"
446 foreach ($Key in $Miners.Keys)
447 {
448 $Row = $Table.NewRow()
449 $Row.Miner = $Key
450 $Algos = ""
451 $Algos += foreach ($Algo in $Miners[$Key].Algos) { $Algo }
452 $Row.Algo = $Algos
453 $Table.Rows.Add($Row)
454 }
455
456 $Table = $Table.DefaultView.ToTable()
457 # use Format-Table to force flushing to screen immediately
458 $Support += Out-String -InputObject ($Table | Format-Table)
459 $Table.Dispose()
460
461 return $Support
462}
463
464function Get-AlgoSupport ()
465{
466 return Get-MinerSupport
467}
468
469function Get-PoolSupport ()
470{
471 $Table = New-Object System.Data.DataTable
472 $Table.Columns.Add("Pool", "string") | Out-Null
473 $Table.Columns.Add("Payout to", "string") | Out-Null
474 $Table.DefaultView.Sort = "Pool ASC"
475
476 $Support += "Supported pools and payout methods:"
477 foreach ($Key in $Pools.Keys)
478 {
479 $Row = $Table.NewRow()
480 $Row.Pool = $Key
481
482 if ($Pools[$Key].Authless)
483 {
484 $Row."Payout to" = "Own PGN wallet"
485 }
486 else
487 {
488 $Row."Payout to" = "Site balance"
489 }
490
491 $Table.Rows.Add($Row)
492 }
493
494 $Table = $Table.DefaultView.ToTable()
495 # use Format-Table to force flushing to screen immediately
496 $Support += Out-String -InputObject ($Table | Format-Table)
497 $Table.Dispose()
498
499 return $Support
500}
501
502function Get-RegionSupport ()
503{
504 $Table = New-Object System.Data.DataTable
505 $Table.Columns.Add("Pool", "string") | Out-Null
506 $Table.Columns.Add("Regions", "string") | Out-Null
507 $Table.DefaultView.Sort = "Pool ASC"
508
509 $Support += "Pools with regions:"
510 foreach ($Key in $Regions.Keys)
511 {
512 $Row = $Table.NewRow()
513 $Row.Pool = $Key
514 $Regs = ""
515 $Regs += foreach ($Region in $Regions[$Key] | Sort-Object) { $Region }
516 $Row.Regions = $Regs
517 $Table.Rows.Add($Row)
518 }
519
520 $Table = $Table.DefaultView.ToTable()
521 # use Format-Table to force flushing to screen immediately
522 $Support += Out-String -InputObject ($Table | Format-Table)
523 $Table.Dispose()
524
525 return $Support
526}
527
528function Get-ActiveProfileSupport ()
529{
530 $Table = New-Object System.Data.DataTable
531 $Table.Columns.Add("Profile", "string") | Out-Null
532 $Table.Columns.Add("Pool", "string") | Out-Null
533 $Table.Columns.Add("Coin", "string") | Out-Null
534 $Table.Columns.Add("Algo", "string") | Out-Null
535 $Table.Columns.Add("Miner", "string") | Out-Null
536 $Table.DefaultView.Sort = "Profile ASC"
537
538 $Support += "Available profiles:"
539 for ($i = 0; $i -lt $Config.Profiles.Length; $i++)
540 {
541 $Row = $Table.NewRow()
542 $Row.Profile = $i + 1
543
544 # gotta check each of these, as activeprofile is checked before profile options, they may or may not exist
545 if ($Config.Profiles[$i].Pool)
546 {
547 $Row.Pool = $Config.Profiles[$i].Pool
548 }
549 else
550 {
551 $Row.Pool = "N/A"
552 }
553
554 if ($Config.Profiles[$i].Coin)
555 {
556 $Row.Coin = $Config.Profiles[$i].Coin
557 $Row.Algo = $Coins[$Config.Profiles[$i].Coin].Algo
558 }
559 else
560 {
561 $Row.Coin = "N/A"
562
563 # don't print algo if coin is set, because in that case we ignore it anyway
564 if ($Config.Profiles[$i].Algo)
565 {
566 $Row.Algo = $Config.Profiles[$i].Algo
567 }
568 else
569 {
570 $Row.Algo = "N/A"
571 }
572 }
573
574 if ($Config.Profiles[$i].Miner)
575 {
576 $Row.Miner = $Config.Profiles[$i].Miner
577 }
578 else
579 {
580 $Row.Miner = "N/A"
581 }
582
583 $Table.Rows.Add($Row)
584 }
585
586 $Table = $Table.DefaultView.ToTable()
587 # use Format-Table to force flushing to screen immediately
588 $Support += Out-String -InputObject ($Table | Format-Table)
589 $Table.Dispose()
590
591 return $Support
592}
593
594function Test-DebugProperty ()
595{
596 try
597 {
598 $Config.Debug = [System.Convert]::ToBoolean($Config.Debug)
599 return $true
600 }
601 catch
602 {
603 Write-PrettyError ("'Debug' property is in incorrect format, it must be 'true' or 'false'!")
604 return $false
605 }
606}
607
608function Test-WatchdogProperty ()
609{
610 try
611 {
612 $Profile.Watchdog = [System.Convert]::ToBoolean($Profile.Watchdog)
613 return $true
614 }
615 catch
616 {
617 Write-PrettyError ("'Watchdog' property is in incorrect format, it must be 'true' or 'false'!")
618
619 if ($Config.Debug)
620 {
621 Write-PrettyDebug $_.Exception
622 }
623
624 return $false
625 }
626}
627
628function Test-MonitoringKeyProperty ()
629{
630 # if the user presses enter, the key will be deleted altogether
631 # we can't do much here without making the key mandatory
632 if ($Config.MonitoringKey)
633 {
634 $Uuid = New-Guid
635 $Res = [Guid]::TryParse($Config.MonitoringKey, [ref]$Uuid)
636
637 if (-Not $Res)
638 {
639 Write-PrettyError ("Monitoring key is in incorrect format!")
640
641 return $false
642 }
643 else
644 {
645 return $true
646 }
647 }
648 else
649 {
650 return $true
651 }
652}
653
654function Test-MphApiKeyProperty ()
655{
656 if ($Config.MphApiKey)
657 {
658 if ($Config.MphApiKey.length -ne 64)
659 {
660 Write-PrettyError ("MPH API key is in incorrect format, check it here: https://miningpoolhub.com/?page=account&action=edit")
661 return $false
662 }
663 else
664 {
665 return $true
666 }
667 }
668 else
669 {
670 return $true
671 }
672}
673
674function Test-ActiveProfileProperty ()
675{
676 # if first run, we won't have any profiles, force selecting that first one we'll create
677 if ($FirstRun)
678 {
679 Set-Property "ActiveProfile" 1 $true $false
680 return $true
681 }
682 # we have at least one profile
683 elseif ($Config.Profiles.Length -gt 0)
684 {
685 # profiles exist, but we haven't selected one
686 if (-Not ($Config.ActiveProfile))
687 {
688 Write-PrettyError "Active profile must be selected!"
689 return $false
690 }
691
692 # we have profiles, we have activeprofile, check format
693 try
694 {
695 $Config.ActiveProfile = [System.Convert]::ToInt16($Config.ActiveProfile)
696 }
697 catch
698 {
699 Write-PrettyError "Active profile is in invalid format, make sure to enter a positive integer!"
700 return $false
701 }
702
703 # alles gut, selected profile exists
704 # ActiveProfile starts at 1!
705 if (($Config.ActiveProfile -le $Config.Profiles.Length) -And ($Config.ActiveProfile -gt 0))
706 {
707 return $true
708 }
709 # profile is selected but doesn't exist
710 else
711 {
712 Write-PrettyError "Profile $($Config.ActiveProfile) does not exist!"
713 return $false
714 }
715 }
716 # profiles are missing, but that's okay, we'll force creating one
717 # don't ask for number because it's always 1
718 else
719 {
720 Set-Property "ActiveProfile" 1 $true $false
721 return $true
722 }
723}
724
725function Test-PoolProperty ()
726{
727 if (-Not ($Profile.Pool))
728 {
729 Write-PrettyError ("Pool must be set!")
730 return $false
731 }
732 elseif (-Not ($Pools.ContainsKey($Profile.Pool)))
733 {
734 Write-PrettyError ("The """ + $Profile.Pool + """ pool is not supported!")
735
736 return $false
737 }
738 else
739 {
740 return $true
741 }
742}
743
744function Test-WorkerProperty ()
745{
746 $Pattern = "^[a-zA-Z0-9]{1,15}$"
747
748 if (-Not ($Config.Worker))
749 {
750 Write-PrettyError ("Worker must be set!")
751 return $false
752 }
753 elseif (-Not ($Config.Worker -match $Pattern))
754 {
755 Write-PrettyError ("Worker name is in invalid format! Use a maximum of 15 letters and numbers!")
756 return $false
757 }
758 else
759 {
760 return $true
761 }
762}
763
764function Test-Wallet ($Address, $Symbol)
765{
766 switch ($Symbol)
767 {
768 "BTC"
769 {
770 if ((-Not ($Address.length -lt 26)) -And (-Not ($Address.length -gt 34)) -And ($Address.StartsWith("1") -Or $Address.StartsWith("3") -Or $Address.StartsWith("bc1")))
771 {
772 return $true
773 }
774 else
775 {
776 return $false
777 }
778 }
779 Default
780 {
781 return $true
782 }
783 }
784}
785
786function Test-WalletProperty ()
787{
788 # we don't care about wallet format here, later when we know it's BTC, we'll check it
789 return $true
790}
791
792function Test-WalletIsAltcoinProperty ()
793{
794 if ($Profile.WalletIsAltcoin)
795 {
796 try
797 {
798 $Profile.WalletIsAltcoin = [System.Convert]::ToBoolean($Profile.WalletIsAltcoin)
799 return $true
800 }
801 catch
802 {
803 Write-PrettyError ("'WalletIsAltcoin' property is in incorrect format, it must be 'true' or 'false'!")
804
805 if ($Config.Debug)
806 {
807 Write-PrettyDebug $_.Exception
808 }
809
810 return $false
811 }
812 }
813 else
814 {
815 return $true
816 }
817}
818
819function Test-UserProperty ()
820{
821 if ($Profile.User)
822 {
823 $Pattern = "^[a-zA-Z0-9]{1,20}$"
824
825 if ($Profile.User -match $Pattern)
826 {
827 return $true
828 }
829 else
830 {
831 Write-PrettyError ("User name is in invalid format! Use a maximum of 20 letters and numbers!")
832 return $false
833 }
834 }
835 else
836 {
837 return $true
838 }
839}
840
841function Test-RegionProperty ()
842{
843 if ($Profile.Region)
844 {
845 # make the array of dynamic size
846 [System.Collections.ArrayList]$ValidRegions = @()
847
848 # build a list of available regions
849 foreach ($Pool in $Regions.Keys)
850 {
851 foreach ($Region in $Regions[$Pool])
852 {
853 if (-Not ($ValidRegions.Contains($Region)))
854 {
855 $ValidRegions.Add($Region) | Out-Null
856 }
857 }
858 }
859
860 if ($ValidRegions.Contains($Profile.Region))
861 {
862 return $true
863 }
864 else
865 {
866 Write-PrettyError ("The """ + $Profile.Region + """ region does not exist!")
867
868 return $false
869 }
870 }
871 else
872 {
873 return $true
874 }
875}
876
877function Test-CoinProperty ()
878{
879 if ($Profile.Coin)
880 {
881 $Profile.Coin = $Profile.Coin.ToLower()
882
883 if (-Not ($Coins.ContainsKey($Profile.Coin)))
884 {
885 Write-PrettyError ("The """ + $Profile.Coin.ToUpper() + """ coin is not supported!")
886
887 return $false
888 }
889 else
890 {
891 return $true
892 }
893 }
894 else
895 {
896 return $true
897 }
898}
899
900function Test-MinerProperty ()
901{
902 if (-Not ($Profile.Miner))
903 {
904 Write-PrettyError ("Miner must be set!")
905 return $false
906 }
907 elseif (-Not ($Miners.ContainsKey($Profile.Miner)))
908 {
909 Write-PrettyError ("The """ + $Profile.Miner + """ miner is not supported!")
910
911 return $false
912 }
913 else
914 {
915 return $true
916 }
917}
918
919function Test-AlgoProperty ()
920{
921 if ($Profile.Algo)
922 {
923 # make the array of dynamic size
924 [System.Collections.ArrayList]$Algos = @()
925 #$Coll = {$Algos}.Invoke()
926
927 # build a list of available algos
928 foreach ($Miner in $Miners.Keys)
929 {
930 foreach ($Algo in $Miners[$Miner].Algos)
931 {
932 if (-Not ($Algos.Contains($Algo)))
933 {
934 $Algos.Add($Algo) | Out-Null
935 }
936 }
937 }
938
939 if (-Not ($Algos.Contains($Profile.Algo)))
940 {
941 Write-PrettyError ("The """ + $Profile.Algo + """ algo is not supported!")
942
943 return $false
944 }
945 else
946 {
947 return $true
948 }
949 }
950 else
951 {
952 return $true
953 }
954}
955
956function Test-CoinExchangeSupport ()
957{
958 if ($Profile.Pool -eq "zergpool")
959 {
960 if ($ZergpoolCoins.Count -eq 0)
961 {
962 if ($Config.Debug)
963 {
964 Write-PrettyDebug "Checking coin's exchange support..."
965 }
966
967 try
968 {
969 $Response = Invoke-RestMethod -Uri $ZergpoolCoinsUrl -UseBasicParsing -TimeoutSec 10 -ErrorAction SilentlyContinue
970
971 if ($Config.Debug)
972 {
973 Write-PrettyDebug ("Server response: $($Response.($Profile.Coin))")
974 }
975
976 foreach ($Coin in $Pools["zergpool"].Coins.Keys)
977 {
978 $ZergpoolCoins.Add($Coin, $Response.$Coin.noautotrade)
979 }
980 }
981 catch
982 {
983 Write-PrettyError "Error determining if the selected coin can be exchanged! RudeHash cannot continue."
984
985 if ($Config.Debug)
986 {
987 Write-PrettyDebug $_.Exception
988 }
989
990 Exit-RudeHash
991 }
992 }
993
994 # noautotrade = 0
995 if ($ZergpoolCoins.($Profile.Coin) -eq 0)
996 {
997 return $true
998 }
999 else
1000 {
1001 return $false
1002 }
1003 }
1004 else
1005 {
1006 return $true
1007 }
1008}
1009
1010function Receive-Choice ($A, $B)
1011{
1012 $KeyPress = ""
1013 Write-PrettyInfo "Press '1' to change '$($A)' or '2' to change '$($B)')"
1014
1015 while (-Not (($KeyPress.Character -eq "1") -Or ($KeyPress.Character -eq "2")))
1016 {
1017 $KeyPress = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
1018 }
1019
1020 if ($KeyPress.Character -eq "1")
1021 {
1022 return $A
1023 }
1024 elseif ($KeyPress.Character -eq "2")
1025 {
1026 return $B
1027 }
1028}
1029
1030function Receive-Property ($Name, $Mandatory)
1031{
1032 # if we ask for a property, it sure helps to print remarks
1033 Write-Help $Name
1034
1035 if ($Mandatory)
1036 {
1037 return Read-Host "Enter value for ""$($Name)"""
1038 }
1039 else
1040 {
1041 if ($Name -eq "Wallet")
1042 {
1043 return Read-Host "Enter value for ""$($Name)"" (or press Return to donate to the Development Fund)"
1044 }
1045 else
1046 {
1047 return Read-Host "Enter value for ""$($Name)"" (or press Return to delete)"
1048 }
1049 }
1050}
1051
1052function Write-Help ($Property)
1053{
1054 # print help text, if any
1055 try
1056 {
1057 Write-PrettyInfo( & (Get-ChildItem "Function:Get-$($Property)Support" -ErrorAction Ignore))
1058 }
1059 catch
1060 {
1061 if ($Remarks.ContainsKey($Property))
1062 {
1063 Write-PrettyInfo($Property + ": " + $Remarks[$Property])
1064 }
1065 }
1066}
1067
1068function Initialize-Property ($Name, $Mandatory, $Force, $ProfileItem)
1069{
1070 if ($Config.Debug)
1071 {
1072 Write-PrettyDebug "Evaluating $Name property..."
1073 }
1074
1075 if ($Force)
1076 {
1077 $Ret = Receive-Property $Name $Mandatory
1078
1079 if (($Name -eq "Wallet") -And ($Ret -eq ""))
1080 {
1081 Set-Property "Wallet" "PDiZ89gk5HMqwrcGp6Hs9WdgFALzLbD4HG" $true $true
1082 }
1083 else
1084 {
1085 Set-Property $Name $Ret $true $ProfileItem
1086 }
1087 }
1088
1089 # make sure $Profile is populated even if no config changes are made after start
1090 # shouldn't cause indexing errors coz we never use profile options before ActiveProfile
1091 if ($ProfileItem -And $Config.Profiles -And $Config.Profiles[$Config.ActiveProfile - 1].$Name)
1092 {
1093 $Profile.$Name = $Config.Profiles[$Config.ActiveProfile - 1].$Name
1094 }
1095
1096 # awesome trick from https://wprogramming.wordpress.com/2011/07/18/dynamic-function-and-variable-access-in-powershell/
1097 while (-Not (& (Get-ChildItem "Function:Test-$($Name)Property")))
1098 {
1099 $Ret = Receive-Property $Name $Mandatory
1100 Set-Property $Name $Ret $true $ProfileItem
1101 }
1102}
1103
1104function Select-Profile ()
1105{
1106 $Name = "ActiveProfile"
1107 if ($Config.Debug)
1108 {
1109 Write-PrettyDebug "Evaluating $($Name) property..."
1110 }
1111
1112 while (-Not (Test-ActiveProfileProperty))
1113 {
1114 $Ret = Receive-Property $Name $true
1115 Set-Property $Name $Ret $true $false
1116 }
1117}
1118
1119function Test-Compatibility ()
1120{
1121 if ($Pools[$Profile.Pool].Authless)
1122 {
1123 if (-Not ($Profile.Wallet))
1124 {
1125 Write-PrettyError ("""" + $Profile.Pool + """ is anonymous, wallet address must be set!")
1126 $Choice = Receive-Choice "Wallet" "Pool"
1127 $Profile.$Choice = ""
1128 Initialize-Property $Choice $true $true $true
1129 Test-Compatibility
1130 }
1131 }
1132 elseif (-Not ($Profile.User))
1133 {
1134 Write-PrettyError ("""" + $Profile.Pool + """ implements authentication, user name must be set!")
1135 $Choice = Receive-Choice "User" "Pool"
1136 $Profile.$Choice = ""
1137 Initialize-Property $Choice $true $true $true
1138 Test-Compatibility
1139 }
1140
1141 if ($Pools[$Profile.Pool].Regions)
1142 {
1143 if (-Not ($Profile.Region))
1144 {
1145 Write-PrettyError ("Region must be set for the """ + $Profile.Pool + """ pool!")
1146 $Choice = Receive-Choice "Region" "Pool"
1147 $Profile.$Choice = ""
1148 Initialize-Property $Choice $true $true $true
1149 Test-Compatibility
1150 }
1151 if (-Not ($Regions[$Profile.Pool].Contains($Profile.Region)))
1152 {
1153 Write-PrettyError ("The """ + $Profile.Region + """ region is not supported on the """ + $Profile.Pool + """ pool!")
1154 $Choice = Receive-Choice "Region" "Pool"
1155 $Profile.$Choice = ""
1156 Initialize-Property $Choice $true $true $true
1157 Test-Compatibility
1158 }
1159 }
1160
1161 $SessionConfig.CoinMode = $false
1162
1163 if ($Profile.Coin)
1164 {
1165 if (-Not ($Pools[$Profile.Pool].Coins))
1166 {
1167 Write-PrettyError ("Coin mining is not supported on """ + $Profile.Pool + """!")
1168 $Choice = Receive-Choice "Coin" "Pool"
1169 $Profile.$Choice = ""
1170 Initialize-Property $Choice $true $true $true
1171 Test-Compatibility
1172 }
1173 elseif (-Not ($Pools[$Profile.Pool].Coins.ContainsKey($Profile.Coin)))
1174 {
1175 Write-PrettyError ("The """ + $Profile.Coin + """ coin is not supported on """ + $Profile.Pool + """!")
1176 $Choice = Receive-Choice "Coin" "Pool"
1177 $Profile.$Choice = ""
1178 Initialize-Property $Choice $true $true $true
1179 Test-Compatibility
1180 }
1181 elseif ((-Not $Profile.WalletIsAltcoin) -And (-Not (Test-CoinExchangeSupport)))
1182 {
1183 Write-PrettyError ("The """ + $Profile.Coin + """ coin cannot be exchanged on """ + $Profile.Pool + """!")
1184 $Choice = Receive-Choice "Coin" "Pool"
1185 $Profile.$Choice = ""
1186 Initialize-Property $Choice $true $true $true
1187 Test-Compatibility
1188 }
1189 else
1190 {
1191 # use coin algo if coin is specified
1192 $Profile.Algo = $Coins[$Profile.Coin].Algo
1193 $SessionConfig.CoinMode = $true
1194 }
1195 }
1196 elseif (-Not $Profile.Algo)
1197 {
1198 Write-PrettyError ("You specified neither a coin nor an algo!")
1199 $Choice = Receive-Choice "Coin" "Algo"
1200 $Profile.$Choice = ""
1201 Initialize-Property $Choice $true $true $true
1202 Test-Compatibility
1203 }
1204 elseif (-Not ($Pools[$Profile.Pool].Algos))
1205 {
1206 Write-PrettyError ("Algo mining is not supported on """ + $Profile.Pool + """!")
1207 $Choice = Receive-Choice "Coin" "Pool"
1208 $Profile.$Choice = ""
1209 Initialize-Property $Choice $true $true $true
1210 Test-Compatibility
1211 }
1212 # reason for elseif: if the coin is supported on the pool, its algo doesn't need to be checked
1213 elseif (-Not ($Pools[$Profile.Pool].Algos.ContainsKey($Profile.Algo)))
1214 {
1215 Write-PrettyError ("Incompatible configuration! """ + $Profile.Algo + """ cannot be mined on """ + $Profile.Pool + """.")
1216 $Choice = Receive-Choice "Algo" "Pool"
1217 $Profile.$Choice = ""
1218 Initialize-Property $Choice $true $true $true
1219 Test-Compatibility
1220 }
1221
1222 if (-Not ($Miners[$Profile.Miner].Algos.Contains($Profile.Algo)))
1223 {
1224 if ($Profile.Coin)
1225 {
1226 Write-PrettyError ("Incompatible configuration! The """ + $Profile.Coin.ToUpper() + """ coin cannot be mined with """ + $Profile.Miner + """.")
1227 $Choice = Receive-Choice "Coin" "Miner"
1228 $Profile.$Choice = ""
1229 Initialize-Property $Choice $true $true $true
1230 Test-Compatibility
1231 }
1232 else
1233 {
1234 Write-PrettyError ("Incompatible configuration! The """ + $Profile.Algo + """ algo cannot be mined with """ + $Profile.Miner + """.")
1235 $Choice = Receive-Choice "Algo" "Miner"
1236 $Profile.$Choice = ""
1237 Initialize-Property $Choice $true $true $true
1238 Test-Compatibility
1239 }
1240 }
1241
1242 # wallet checks, only relevant on wallet pools
1243 if ($Pools[$Profile.Pool].Authless)
1244 {
1245 # altcoin payouts are only possible if the pool has coins
1246 if ($Profile.WalletIsAltcoin -And (-Not ($Pools[$Profile.Pool].Coins)))
1247 {
1248 Write-PrettyError ("""" + $Profile.Pool + """ doesn't support Altcoin payouts!")
1249 $Choice = Receive-Choice "WalletIsAltcoin" "Pool"
1250 $Profile.$Choice = ""
1251 Initialize-Property $Choice $true $true $true
1252 Test-Compatibility
1253 }
1254
1255 # bitcoin payouts are only possible if the pool has algos (at least ATM)
1256 if ((-Not ($Profile.WalletIsAltcoin)) -And (-Not ($Pools[$Profile.Pool].Algos)))
1257 {
1258 Write-PrettyError ("""" + $Profile.Pool + """ doesn't support Bitcoin payouts!")
1259 $Choice = Receive-Choice "WalletIsAltcoin" "Pool"
1260 $Profile.$Choice = ""
1261 Initialize-Property $Choice $true $true $true
1262 Test-Compatibility
1263 }
1264
1265 # altcoin payouts are only possible if mining a coin
1266 if ($Profile.WalletIsAltcoin -And (-Not ($SessionConfig.CoinMode)))
1267 {
1268 Write-PrettyError ("Altcoin payouts are only possible if mining a coin!")
1269 $Choice = Receive-Choice "WalletIsAltcoin" "Coin"
1270 $Profile.$Choice = ""
1271 Initialize-Property $Choice $true $true $true
1272 Test-Compatibility
1273 }
1274
1275 # if getting paid in BTC, check its format, let's accept altcoin wallets as-is
1276 if ((-Not ($Profile.WalletIsAltcoin)) -And (-Not (Test-Wallet $Profile.Wallet "BTC")))
1277 {
1278 Write-PrettyError ("Wallet is not a valid BTC address!")
1279 $Choice = Receive-Choice "WalletIsAltcoin" "Wallet"
1280 $Profile.$Choice = ""
1281 Initialize-Property $Choice $true $true $true
1282 Test-Compatibility
1283 }
1284 }
1285
1286 # configuration is good, let's set up globals
1287 if ($SessionConfig.CoinMode)
1288 {
1289 $SessionConfig.Server = $Pools[$Profile.Pool].Coins[$Profile.Coin].Server
1290 $SessionConfig.Port = $Pools[$Profile.Pool].Coins[$Profile.Coin].Port
1291 }
1292 else
1293 {
1294 $SessionConfig.Server = $Pools[$Profile.Pool].Algos[$Profile.Algo].Server
1295 $SessionConfig.Port = $Pools[$Profile.Pool].Algos[$Profile.Algo].Port
1296 }
1297
1298 if ($Pools[$Profile.Pool].Regions)
1299 {
1300 $RegionStr = $Profile.Region + "."
1301 $SessionConfig.Server = $SessionConfig.Server -Replace "%REGION%",$RegionStr
1302 }
1303
1304 if ($Miners[$Profile.Miner].Api)
1305 {
1306 $SessionConfig.Api = $true
1307 }
1308}
1309
1310function Get-CurrencySupport ()
1311{
1312 if (-Not ($SessionConfig.Rates))
1313 {
1314 try
1315 {
1316 $ResponseRaw = Invoke-WebRequest -Uri $BlockchainUrl -UseBasicParsing
1317 $Response = $ResponseRaw | ConvertFrom-Json -AsHashtable
1318
1319 foreach ($Currency in $Response.Keys)
1320 {
1321 $BtcRates.Add($Currency, $Response[$Currency].buy)
1322 }
1323
1324 $SessionConfig.Rates = $true
1325 }
1326 catch
1327 {
1328 Write-PrettyError "Error obtaining BTC exchange rates! BTC to Fiat conversion is disabled."
1329
1330 if ($Config.Debug)
1331 {
1332 Write-PrettyDebug $_.Exception
1333 }
1334
1335 $SessionConfig.Rates = $false
1336 }
1337 }
1338
1339 if ($SessionConfig.Rates)
1340 {
1341 $CurrencyStr = "Supported currencies:`r`n"
1342 $CurrencyStr += foreach ($Currency in $BtcRates.GetEnumerator() | Sort-Object -Property Name) { $Currency.Name }
1343 return $CurrencyStr
1344 }
1345}
1346
1347function Test-CurrencyProperty ()
1348{
1349 if (-Not $SessionConfig.Rates)
1350 {
1351 # Out-Null this, coz it's printed by Receive-Property
1352 # also, if this would return, the currency wouldn't be asked for
1353 Get-CurrencySupport | Out-Null
1354 }
1355
1356 if ($SessionConfig.Api -And $SessionConfig.Rates)
1357 {
1358 if (-Not ($Config.Currency))
1359 {
1360 Write-PrettyError ("Currency must be set!")
1361 return $false
1362 }
1363
1364 $Config.Currency = $Config.Currency.ToUpper()
1365
1366 if (-Not ($BtcRates.Contains($Config.Currency)))
1367 {
1368 Write-PrettyError ("The """ + $Config.Currency + """ currency is not supported!")
1369
1370 return $false
1371 }
1372 else
1373 {
1374 return $true
1375 }
1376 }
1377 else
1378 {
1379 return $true
1380 }
1381}
1382
1383function Test-ElectricityCostProperty ()
1384{
1385 if ($SessionConfig.Api -And $SessionConfig.Rates)
1386 {
1387 if (-Not ($Config.ElectricityCost))
1388 {
1389 Write-PrettyError ("Electricity cost must be set!")
1390 return $false
1391 }
1392
1393 # make sure we don't miscalculate profits, e.g. don't convert 0,18 to 18
1394 if ($Config.ElectricityCost.ToString().Contains(","))
1395 {
1396 Write-PrettyError ("Invalid electricity cost, make sure to use '.' as decimal separator!")
1397 return $false
1398 }
1399
1400 try
1401 {
1402 $Config.ElectricityCost = [System.Convert]::ToDouble($Config.ElectricityCost)
1403 return $true
1404 }
1405 catch
1406 {
1407 Write-PrettyError ("Invalid electricity cost, """ + $Config.ElectricityCost + """ is not a number!")
1408 return $false
1409 }
1410 }
1411 else
1412 {
1413 return $true
1414 }
1415}
1416
1417function Test-ExtraArgsProperty ()
1418{
1419 # we don't care what the user enters, impossible to check
1420 return $true
1421}
1422
1423function Initialize-Properties ()
1424{
1425 if ($FirstRun)
1426 {
1427 Write-PrettyInfo ("Welcome to RudeHash Pigeon Edition! Let's set up your configuration.")
1428 Write-PrettyInfo ("We'll ask your input for all config options. Some are optional, you can skip")
1429 Write-PrettyInfo ("those by pressing 'Enter'. Don't worry, if you try to specify an incompatible")
1430 Write-PrettyInfo ("setup, we will tell you and ask you to modify it. Not all options are used in")
1431 Write-PrettyInfo ("all scenarios, e.g. wallet address is unused on pools with their own balances.")
1432 Write-PrettyDebug ("If you want to obtain a full version of RudeHash, please visit rudehash.org!")
1433 Write-PrettyDots
1434 Set-Property "WalletIsAltcoin" $true $true $true
1435 Set-Property "Coin" "pgn" $true $true
1436 Set-Property "Algo" "x16s" $true $true
1437 }
1438 else
1439 {
1440 Set-Property "WalletIsAltcoin" $true $false $true
1441 Set-Property "Coin" "pgn" $false $true
1442 Set-Property "Algo" "x16s" $false $true
1443 }
1444
1445 Initialize-Property "Debug" $true $FirstRun
1446 Initialize-Property "Worker" $true $FirstRun
1447 Initialize-Property "MonitoringKey" $false $FirstRun
1448 Initialize-Property "MphApiKey" $false $FirstRun
1449
1450 Select-Profile
1451
1452 # profile items
1453 Initialize-Property "Watchdog" $true $FirstRun $true
1454 Initialize-Property "Pool" $true $FirstRun $true $true
1455 Initialize-Property "Wallet" $false $FirstRun $true
1456 # Initialize-Property "WalletIsAltcoin" $false $FirstRun $true
1457 Initialize-Property "User" $false $FirstRun $true
1458 Initialize-Property "Region" $false $FirstRun $true
1459 # Initialize-Property "Coin" $false $FirstRun $true
1460 Initialize-Property "Miner" $true $FirstRun $true
1461 # Initialize-Property "Algo" $false $FirstRun $true
1462 Initialize-Property "ExtraArgs" $false $FirstRun $true
1463
1464 Test-Compatibility
1465
1466 Initialize-Property "Currency" $true $FirstRun
1467 Initialize-Property "ElectricityCost" $true $FirstRun
1468}
1469
1470$RigStats =
1471@{
1472 GpuCount = 0;
1473 HashRate = 0.0;
1474 PowerUsage = 0.0;
1475 EarningsBtc = 0.0;
1476 EarningsFiat = 0.0;
1477 Profit = 0.0;
1478 ExchangeRate = 0.0;
1479 Price = 0.0;
1480 FailedChecks = 0;
1481 DevMinutes = 0;
1482 Pid = 0;
1483 Uptime = New-TimeSpan;
1484}
1485
1486function Initialize-Temp ()
1487{
1488 try
1489 {
1490 if (Test-Path $TempDir -ErrorAction Stop)
1491 {
1492 Remove-Item -Recurse -Force -Path $TempDir -ErrorAction Stop
1493 }
1494
1495 New-Item -ItemType Directory -Path $TempDir -Force -ErrorAction Stop | Out-Null
1496 }
1497 catch
1498 {
1499 Write-PrettyError "Error setting up temporary directory! Do we have write access?"
1500
1501 if ($Config.Debug)
1502 {
1503 Write-PrettyDebug $_.Exception
1504 }
1505
1506 Exit-RudeHash
1507 }
1508}
1509
1510function Read-MinerApi ($Request, $Critical)
1511{
1512 $Timeout = 10
1513
1514 try
1515 {
1516 $Client = New-Object System.Net.Sockets.TcpClient "127.0.0.1", $MinerPort
1517 $Stream = $Client.GetStream()
1518 $Writer = New-Object System.IO.StreamWriter $Stream
1519 $Reader = New-Object System.IO.StreamReader $Stream
1520 $Client.SendTimeout = $Timeout * 1000
1521 $Client.ReceiveTimeout = $Timeout * 1000
1522 $Writer.AutoFlush = $true
1523
1524 $Writer.WriteLine($Request)
1525 $Response = $Reader.ReadLine()
1526 }
1527 catch
1528 {
1529 Write-PrettyError "Error connecting to miner API!"
1530
1531 if ($Config.Debug)
1532 {
1533 Write-PrettyDebug $_.Exception
1534 }
1535
1536 if ($Critical -eq "true")
1537 {
1538 Write-PrettyError "Critical error, RudeHash cannot continue."
1539 Exit-RudeHash
1540 }
1541 }
1542 finally
1543 {
1544 if ($Reader) { $Reader.Close() }
1545 if ($Writer) { $Writer.Close() }
1546 if ($Stream) { $Stream.Close() }
1547 if ($Client) { $Client.Close() }
1548 }
1549
1550 return $Response
1551}
1552
1553function Resolve-PoolIp ()
1554{
1555 try
1556 {
1557 # make sure to flush the DNS cache first
1558 Start-Process "ipconfig" -ArgumentList "/flushdns" -Wait -NoNewWindow -RedirectStandardOutput nul -ErrorAction SilentlyContinue
1559
1560 # wonderful workaround for PowerShell *and* .Net both lacking proper built-in DNS resolution
1561 #$Ip = ([System.Net.DNS]::GetHostEntry($SessionConfig.Server).AddressList[0].IPAddressToString)
1562 $Res = (Get-ProcessOutput "nslookup" "-type=A -retry=3 $($SessionConfig.Server) 9.9.9.9").Split("`r`n")
1563 $Ip = ($Res.Split("`r`n") | Select-String -Pattern "^Address")[1].ToString().Split(":")[1].Trim()
1564
1565 # validate the IP address by casting it to IPAddress
1566 $Ip -match [ipaddress]$Ip | Out-Null
1567
1568 if ($Config.Debug)
1569 {
1570 Write-PrettyDebug ("Stratum server $($SessionConfig.Server) resolves to: $($Ip)")
1571 }
1572 }
1573 catch
1574 {
1575 Write-PrettyError "Error resolving pool IP addess, falling back to URL! Is your network connection working?"
1576
1577 if ($Config.Debug)
1578 {
1579 Write-PrettyDebug $_.Exception
1580 }
1581
1582 # it's better than nothing, it might start working during stratum connection
1583 $Ip = $SessionConfig.Server
1584 }
1585
1586 return $Ip
1587}
1588
1589function Get-GpuCount ()
1590{
1591 switch ($Profile.Miner)
1592 {
1593 {$_ -in "ccminer-alexis-hsr", "ccminer-allium", "ccminer-klaust", "ccminer-palginmod", "ccminer-phi", "ccminer-polytimos", "ccminer-rvn", "ccminer-tpruvot", "ccminer-xevan", "hsrminer-hsr", "hsrminer-neoscrypt", "nevermore-x16s", "suprminer", "vertminer"}
1594 {
1595 $Response = Read-MinerApi 'summary' $false
1596
1597 try
1598 {
1599 [System.Collections.Hashtable]$Summary = @{}
1600
1601 foreach ($Item in $Response.Split(";"))
1602 {
1603 $Summary.Add($Item.Split("=")[0], $Item.Split("=")[1])
1604 }
1605
1606 $Count = $Summary["GPUS"]
1607 }
1608 catch
1609 {
1610 Write-PrettyError $MinerApiErrorStr
1611
1612 if ($Config.Debug)
1613 {
1614 Write-PrettyDebug $_.Exception
1615 }
1616 }
1617 }
1618
1619 "dstm"
1620 {
1621 # dstm accepts any string as request, let's use the same as ccminer
1622 $ResponseRaw = Read-MinerApi 'summary' $false
1623
1624 try
1625 {
1626 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
1627 $Count = $Response.result.length
1628 }
1629 catch
1630 {
1631 Write-PrettyError $MinerApiErrorStr
1632
1633 if ($Config.Debug)
1634 {
1635 Write-PrettyDebug $_.Exception
1636 }
1637 }
1638 }
1639
1640 # api: https://github.com/ethereum-mining/ethminer/issues/295#issuecomment-353755310
1641 "ethminer"
1642 {
1643 $ResponseRaw = Read-MinerApi '{"id":0,"jsonrpc":"2.0","method":"miner_getstat1"}' $false
1644
1645 try
1646 {
1647 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
1648 $Count = $Response.result[3].Split(";").length
1649 }
1650 catch
1651 {
1652 Write-PrettyError $MinerApiErrorStr
1653
1654 if ($Config.Debug)
1655 {
1656 Write-PrettyDebug $_.Exception
1657 }
1658 }
1659 }
1660
1661 "excavator"
1662 {
1663 $ResponseRaw = Read-MinerApi '{"id":1,"method":"device.list","params":[]}' $true
1664 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
1665 $Count = $Response.devices.length
1666 }
1667
1668 "zecminer"
1669 {
1670 $ResponseRaw = Read-MinerApi '{"id":"0", "method":"getstat"}' $false
1671
1672 try
1673 {
1674 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
1675 $Count = $Response.result.length
1676 }
1677 catch
1678 {
1679 Write-PrettyError $MinerApiErrorStr
1680
1681 if ($Config.Debug)
1682 {
1683 Write-PrettyDebug $_.Exception
1684 }
1685 }
1686 }
1687 }
1688
1689 # miner might return empty string, then it's "not zero", thus we don't attempt to obtain count again
1690 # and then we're stuck restarting the miner every 5 minutes, forever
1691 # that's bad, very bad, let's check if we actually got a number at all
1692 try
1693 {
1694 $Count = [System.Convert]::ToInt16($Count)
1695
1696 if ($Count -gt 0)
1697 {
1698 $RigStats.GpuCount = $Count
1699 }
1700 }
1701 catch
1702 {
1703 # this should only happen when the miner's API has not started *yet*, so it's not an error
1704 # best example is when hsrminer gets stuck at devfee check for too long
1705 if ($Config.Debug)
1706 {
1707 Write-PrettyDebug $_.Exception
1708 }
1709 }
1710}
1711
1712function Start-Excavator ()
1713{
1714 $Excavator = [io.path]::combine($MinersDir, "excavator", "excavator.exe")
1715 $Args = "-p $MinerPort"
1716
1717 if ($Config.Debug)
1718 {
1719 Write-PrettyDebug ("$Excavator $Args")
1720 }
1721
1722 $Proc = Start-Process -FilePath $Excavator -ArgumentList $Args -PassThru -NoNewWindow -RedirectStandardOutput nul
1723 Write-PrettyInfo "Determining the number of GPUs..."
1724 Start-Sleep -Seconds 5
1725 return $Proc
1726}
1727
1728function Initialize-Json ($User, $Pass)
1729{
1730 $Count = $RigStats.GpuCount
1731 $ExcavatorJson = @"
1732[
1733 {"time":0,"commands":[
1734 {"id":1,"method":"algorithm.add","params":["$($ExcavatorAlgos[$Profile.Algo])","$(Resolve-PoolIp):$($SessionConfig.Port)","$($User):$($Pass)"]}
1735 ]},
1736 {"time":3,"commands":[
1737 $(for ($i = 0; $i -lt $Count; $i++)
1738 {
1739 $Line = "{""id"":1,""method"":""worker.add"",""params"":[""0"",""$i""]}"
1740 if (($Count - $i) -gt 1)
1741 {
1742 $Line += ",`r`n"
1743 }
1744 Write-Output $Line
1745 })
1746 ]},
1747 {"time":10,"loop":20,"commands":[
1748 $(for ($i = 0; $i -lt $Count; $i++)
1749 {
1750 $Line = "{""id"":1,""method"":""worker.print.speed"",""params"":[""$i""]}"
1751 if (($Count - $i) -gt 1)
1752 {
1753 $Line += ",`r`n"
1754 }
1755 Write-Output $Line
1756 })
1757 ]}
1758]
1759"@
1760 #{"id":1,"method":"algorithm.print.speeds","params":[]}
1761 return $ExcavatorJson
1762}
1763
1764function Initialize-Excavator ($User, $Pass)
1765{
1766 $Proc = Start-Excavator
1767 Get-GpuCount
1768 Stop-Process $Proc
1769
1770 if ($RigStats.GpuCount -gt 1)
1771 {
1772 $Suffix = "s"
1773 }
1774
1775 Write-PrettyInfo ($RigStats.GpuCount.ToString() + " GPU" + $Suffix + " detected.")
1776
1777 $Json = Initialize-Json $User $Pass
1778 $JsonFile = [io.path]::combine($TempDir, "excavator.json")
1779
1780 try
1781 {
1782 Set-Content -LiteralPath $JsonFile -Value $Json -ErrorAction Stop
1783 }
1784 catch
1785 {
1786 Write-PrettyError "Error writing Excavator JSON file! Make sure the file is not locked by another process!"
1787
1788 if ($Config.Debug)
1789 {
1790 Write-PrettyDebug $_.Exception
1791 }
1792
1793 Exit-RudeHash
1794 }
1795}
1796
1797function Initialize-MinerArgs ()
1798{
1799 switch ($Profile.Pool)
1800 {
1801 {$_ -in "alttank", "blockcruncher", "bsod", "coinminers", "corpopool", "cryptopool", "faster", "huopool", "kwchmining", "lycheebit", "robocop", "xitech" }
1802 {
1803 $PoolUser = $Profile.Wallet + "." + $Config.Worker
1804 $PoolPass = "c=" + $Profile.Coin.ToUpper()
1805 }
1806 "masterhash"
1807 {
1808 $PoolUser = $Profile.Wallet
1809 $PoolPass = "c=" + $Profile.Coin.ToUpper() + ",ID=" + $Config.Worker
1810 }
1811 {$_ -in "miningpoolhub", "suprnova" }
1812 {
1813 $PoolUser = $Profile.User + "." + $Config.Worker
1814 $PoolPass = "x"
1815 }
1816 {$_ -in "elitehash", "nicehash", "slinging" }
1817 {
1818 # https://www.nicehash.com/help/how-to-create-a-worker
1819 $PoolUser = $Profile.Wallet + "." + $Config.Worker
1820 $PoolPass = "x"
1821 }
1822 "poolr"
1823 {
1824 $PoolUser = $Profile.Wallet + "." + $Config.Worker
1825 $PoolPass = "c=" + $Profile.Coin.ToUpper()
1826 }
1827 "zergpool"
1828 {
1829 $PoolUser = $Profile.Wallet
1830
1831 if ($SessionConfig.CoinMode)
1832 {
1833 # zergpool only accepts the coin in uppercase
1834 if ($Profile.WalletIsAltcoin)
1835 {
1836 $PoolPass = "c=" + $Profile.Coin.ToUpper() + ",mc="+ $Profile.Coin.ToUpper() + ",ID=" + $Config.Worker
1837 }
1838 else
1839 {
1840 $PoolPass = "c=BTC,mc="+ $Profile.Coin.ToUpper() + ",ID=" + $Config.Worker
1841 }
1842 }
1843 else
1844 {
1845 $PoolPass = "c=BTC,ID=" + $Config.Worker
1846 }
1847 }
1848 "zpool"
1849 {
1850 $PoolUser = $Profile.Wallet
1851 # zpool only guarantees BTC payouts, so we enforce it, potentially suboptimal coin is better than completely lost mining
1852 $PoolPass = "c=BTC,ID=" + $Config.Worker
1853 }
1854 }
1855
1856 # always update the IP, the miner could've crashed because of an IP change to begin with
1857 $PoolIp = Resolve-PoolIp
1858
1859 switch ($Profile.Miner)
1860 {
1861 {$_ -in "ccminer-alexis-hsr", "ccminer-allium", "ccminer-klaust", "ccminer-palginmod", "ccminer-phi", "ccminer-rvn", "ccminer-tpruvot", "ccminer-xevan", "nevermore-x16s", "suprminer" } { $Args = "--algo=" + $Profile.Algo + " --url=stratum+tcp://" + $PoolIp + ":" + $SessionConfig.Port + " --user=" + $PoolUser + " --pass " + $PoolPass + " --api-bind 127.0.0.1:" + $MinerPort }
1862 "ccminer-polytimos" { $Args = "--algo=poly --url=stratum+tcp://" + $PoolIp + ":" + $SessionConfig.Port + " --user=" + $PoolUser + " --pass " + $PoolPass + " --api-bind 127.0.0.1:" + $MinerPort }
1863 "dstm" { $Args = "--server " + $PoolIp + " --user " + $PoolUser + " --pass " + $PoolPass + " --port " + $SessionConfig.Port + " --telemetry=127.0.0.1:" + $MinerPort + " --noreconnect" }
1864 "ethminer" { $Args = "--cuda --stratum " + $PoolIp + ":" + $SessionConfig.Port + " --userpass " + $PoolUser + ":" + $PoolPass + " --api-port " + $MinerPort + " --stratum-protocol " + $Pools[$Profile.Pool].StratumProto }
1865 "excavator"
1866 {
1867 Initialize-Excavator $PoolUser $PoolPass
1868 $Args = "-c " + [io.path]::combine($TempDir, "excavator.json") + " -p " + $MinerPort
1869 }
1870 {$_ -in "hsrminer-hsr", "hsrminer-neoscrypt" } { $Args = "--url=stratum+tcp://" + $PoolIp + ":" + $SessionConfig.Port + " --userpass=" + $PoolUser + ":" + $PoolPass }
1871 "vertminer" { $Args = "-o stratum+tcp://" + $PoolIp + ":" + $SessionConfig.Port + " -u " + $PoolUser + " -p " + $PoolPass + " --api-bind 127.0.0.1:" + $MinerPort }
1872 "zecminer" { $Args = "--server " + $PoolIp + " --user " + $PoolUser + " --pass " + $PoolPass + " --port " + $SessionConfig.Port + " --api 127.0.0.1:" + $MinerPort }
1873 }
1874
1875 if ($Profile.ExtraArgs)
1876 {
1877 $Args += " " + $Profile.ExtraArgs
1878 }
1879
1880 return $Args
1881}
1882
1883function Get-HashRate ()
1884{
1885 $HashRate = 0
1886
1887 switch ($Profile.Miner)
1888 {
1889 {$_ -in "ccminer-alexis-hsr", "ccminer-allium", "ccminer-klaust", "ccminer-palginmod", "ccminer-phi", "ccminer-polytimos", "ccminer-rvn", "ccminer-tpruvot", "ccminer-xevan", "hsrminer-hsr", "hsrminer-neoscrypt", "nevermore-x16s", "suprminer", "vertminer"}
1890 {
1891 $Response = Read-MinerApi 'threads' $false
1892
1893 try
1894 {
1895 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
1896 {
1897 $GpuStr = $Response.Split("|")[$i]
1898 [System.Collections.Hashtable]$GpuStats = @{}
1899
1900 foreach ($Item in $GpuStr.Split(";"))
1901 {
1902 $GpuStats.Add($Item.Split("=")[0], $Item.Split("=")[1])
1903 }
1904
1905 if ($Profile.Miner.StartsWith("hsrminer"))
1906 {
1907 $Key = "SPEED"
1908 }
1909 else
1910 {
1911 $Key = "KHS"
1912 }
1913
1914 $HashRate += $GpuStats[$Key]
1915 $GpuStats.Clear()
1916 }
1917
1918 # ccminer and hsrminer returns KH/s
1919 $HashRate *= 1000
1920 }
1921 catch
1922 {
1923 Write-PrettyError $MinerApiErrorStr
1924
1925 if ($Config.Debug)
1926 {
1927 Write-PrettyDebug $_.Exception
1928 }
1929 }
1930 }
1931
1932 "dstm"
1933 {
1934 # dstm accepts any string as request, let's use the same as ccminer
1935 $ResponseRaw = Read-MinerApi 'summary' $false
1936
1937 try
1938 {
1939 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
1940
1941 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
1942 {
1943 $HashRate += $Response.result[$i].sol_ps
1944 }
1945 }
1946 catch
1947 {
1948 Write-PrettyError $MinerApiErrorStr
1949
1950 if ($Config.Debug)
1951 {
1952 Write-PrettyDebug $_.Exception
1953 }
1954 }
1955 }
1956
1957 "ethminer"
1958 {
1959 $ResponseRaw = Read-MinerApi '{"id":0,"jsonrpc":"2.0","method":"miner_getstathr"}' $false
1960
1961 try
1962 {
1963 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
1964 $HashRate = $Response.result.ethhashrate
1965 }
1966 catch
1967 {
1968 Write-PrettyError $MinerApiErrorStr
1969
1970 if ($Config.Debug)
1971 {
1972 Write-PrettyDebug $_.Exception
1973 }
1974 }
1975 }
1976
1977 "excavator"
1978 {
1979 $ResponseRaw = Read-MinerApi '{"id":1,"method":"algorithm.list","params":[]}' $false
1980
1981 try
1982 {
1983 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
1984
1985 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
1986 {
1987 $HashRate += $Response.algorithms.workers[$i].speed[0]
1988 }
1989 }
1990 catch
1991 {
1992 if ($Config.Debug)
1993 {
1994 Write-PrettyDebug $_.Exception
1995 }
1996 }
1997 }
1998
1999 "zecminer"
2000 {
2001 $ResponseRaw = Read-MinerApi '{"id":"0", "method":"getstat"}' $false
2002
2003 try
2004 {
2005 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
2006
2007 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
2008 {
2009 $HashRate += $Response.result[$i].speed_sps
2010 }
2011 }
2012 catch
2013 {
2014 Write-PrettyError $MinerApiErrorStr
2015
2016 if ($Config.Debug)
2017 {
2018 Write-PrettyDebug $_.Exception
2019 }
2020 }
2021 }
2022 }
2023
2024 $RigStats.HashRate = ([math]::Round($HashRate, 0))
2025}
2026
2027function Get-PrettyHashRate ($HashRate)
2028{
2029 if ($HashRate -ge 1000000)
2030 {
2031 return (([math]::Round(($HashRate / 1000000), 2)).ToString() + " MH/s")
2032 }
2033 elseif ($HashRate -ge 1000)
2034 {
2035 return (([math]::Round(($HashRate / 1000), 2)).ToString() + " kH/s")
2036 }
2037 else
2038 {
2039 return ([math]::Round($HashRate, 2).ToString() + " H/s")
2040 }
2041}
2042
2043function Get-PowerUsage ()
2044{
2045 $PowerUsage = 0
2046
2047 switch ($Profile.Miner)
2048 {
2049 {$_ -in "ccminer-alexis-hsr", "ccminer-allium", "ccminer-klaust", "ccminer-palginmod", "ccminer-phi", "ccminer-polytimos", "ccminer-rvn", "ccminer-tpruvot", "ccminer-xevan", "hsrminer-hsr", "hsrminer-neoscrypt", "nevermore-x16s", "suprminer", "vertminer"}
2050 {
2051 $Response = Read-MinerApi 'threads' $false
2052
2053 try
2054 {
2055 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
2056 {
2057 $GpuStr = $Response.Split("|")[$i]
2058 [System.Collections.Hashtable]$GpuStats = @{}
2059
2060 foreach ($Item in $GpuStr.Split(";"))
2061 {
2062 $GpuStats.Add($Item.Split("=")[0], $Item.Split("=")[1])
2063 }
2064
2065 if ($Profile.Miner.StartsWith("hsrminer"))
2066 {
2067 $Key = "POWER_CONSUMPTION"
2068 }
2069 else
2070 {
2071 $Key = "POWER"
2072 }
2073
2074 $PowerUsage += $GpuStats[$Key]
2075 $GpuStats.Clear()
2076 }
2077
2078 # everything except these returns mW instead of W, because reasons
2079 if (-Not (($Profile.Miner -eq "suprminer") -Or ($Profile.Miner -eq "vertminer")))
2080 {
2081 $PowerUsage /= 1000
2082 }
2083 }
2084 catch
2085 {
2086 Write-PrettyError $MinerApiErrorStr
2087
2088 if ($Config.Debug)
2089 {
2090 Write-PrettyDebug $_.Exception
2091 }
2092 }
2093 }
2094
2095 "dstm"
2096 {
2097 # dstm accepts any string as request, let's use the same as ccminer
2098 $ResponseRaw = Read-MinerApi 'summary' $false
2099
2100 try
2101 {
2102 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
2103
2104 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
2105 {
2106 $PowerUsage += $Response.result[$i].power_usage
2107 }
2108 }
2109 catch
2110 {
2111 Write-PrettyError $MinerApiErrorStr
2112
2113 if ($Config.Debug)
2114 {
2115 Write-PrettyDebug $_.Exception
2116 }
2117 }
2118 }
2119
2120 "ethminer"
2121 {
2122 $ResponseRaw = Read-MinerApi '{"id":0,"jsonrpc":"2.0","method":"miner_getstathr"}' $false
2123
2124 try
2125 {
2126 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
2127
2128 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
2129 {
2130 $PowerUsage += [math]::Round($Response.result.powerusages[$i], 0)
2131 }
2132 }
2133 catch
2134 {
2135 Write-PrettyError $MinerApiErrorStr
2136
2137 if ($Config.Debug)
2138 {
2139 Write-PrettyDebug $_.Exception
2140 }
2141 }
2142 }
2143
2144 "excavator"
2145 {
2146 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
2147 {
2148 $ResponseRaw = Read-MinerApi '{"id":1,"method":"device.get","params":["0"]}' $false
2149
2150 try
2151 {
2152 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
2153 $PowerUsage += ([math]::Round($Response.gpu_power_usage, 0))
2154 }
2155 catch
2156 {
2157 if ($Config.Debug)
2158 {
2159 Write-PrettyDebug $_.Exception
2160 }
2161 }
2162 }
2163 }
2164
2165 "zecminer"
2166 {
2167 $ResponseRaw = Read-MinerApi '{"id":"0", "method":"getstat"}' $false
2168
2169 try
2170 {
2171 $Response = $ResponseRaw | ConvertFrom-Json -ErrorAction SilentlyContinue
2172
2173 for ($i = 0; $i -lt $RigStats.GpuCount; $i++)
2174 {
2175 $PowerUsage += $Response.result[$i].gpu_power_usage
2176 }
2177 }
2178 catch
2179 {
2180 Write-PrettyError $MinerApiErrorStr
2181
2182 if ($Config.Debug)
2183 {
2184 Write-PrettyDebug $_.Exception
2185 }
2186 }
2187 }
2188 }
2189
2190 $RigStats.PowerUsage = $PowerUsage
2191}
2192
2193function Measure-Earnings ()
2194{
2195 if ($SessionConfig.CoinMode)
2196 {
2197 if ($Coins[$Profile.Coin].WtmId)
2198 {
2199 try
2200 {
2201 [double]$HashRate = $RigStats.HashRate / $WtmModifiers[$Profile.Algo]
2202 $WtmUrl = "https://whattomine.com/coins/" + $Coins[$Profile.Coin].WtmId + ".json?hr=" + $HashRate + "&p=0&cost=0&fee=" + $Pools[$Profile.Pool].PoolFee + "&commit=Calculate"
2203 $Response = Invoke-RestMethod -Uri $WtmUrl -UseBasicParsing -ErrorAction SilentlyContinue
2204
2205 if ($Response.btc_revenue)
2206 {
2207 $RigStats.Price = [double]$Response.btc_revenue
2208 }
2209 elseif ($Config.Debug)
2210 {
2211 Write-PrettyDebug "WhatToMine API request failed!"
2212 }
2213
2214 $RigStats.EarningsBtc = [math]::Round($RigStats.Price, $BtcDigits)
2215
2216 if ($SessionConfig.Rates)
2217 {
2218 $RigStats.EarningsFiat = [math]::Round(($RigStats.EarningsBtc * $BtcRates[$Config.Currency]), $FiatDigits)
2219 }
2220 }
2221 catch
2222 {
2223 Write-PrettyError "WhatToMine API request failed! Earnings estimations are not updated."
2224
2225 if ($Config.Debug)
2226 {
2227 Write-PrettyDebug $_.Exception
2228 }
2229 }
2230 }
2231 }
2232 else
2233 {
2234 if ($Pools[$Profile.Pool].ApiUrl)
2235 {
2236 try
2237 {
2238 [double]$HashRate = $RigStats.HashRate / $Pools[$Profile.Pool].Algos[$Profile.Algo].Modifier
2239 [double]$Multiplier = 1 - ($Pools[$Profile.Pool].PoolFee / 100)
2240 $Response = Invoke-RestMethod -Uri $Pools[$Profile.Pool].ApiUrl -UseBasicParsing -ErrorAction SilentlyContinue
2241
2242 switch ($Profile.Pool)
2243 {
2244 "nicehash"
2245 {
2246 if ($Response.result.stats[$Pools[$Profile.Pool].Algos[$Profile.Algo].Id].price)
2247 {
2248 $RigStats.Price = [double]$Response.result.stats[$Pools[$Profile.Pool].Algos[$Profile.Algo].Id].price
2249 }
2250 elseif ($Config.Debug)
2251 {
2252 Write-PrettyDebug "$($Pools[$Profile.Pool].Name) API request failed!"
2253 }
2254 }
2255 {$_ -in "zergpool", "zpool"}
2256 {
2257 if ($Response.($Profile.Algo).estimate_current)
2258 {
2259 $RigStats.Price = [double]$Response.($Profile.Algo).estimate_current
2260 }
2261 elseif ($Config.Debug)
2262 {
2263 Write-PrettyDebug "$($Pools[$Profile.Pool].Name) API request failed!"
2264 }
2265 }
2266 }
2267
2268 $RigStats.EarningsBtc = [math]::Round(($HashRate * $RigStats.Price * $Multiplier), $BtcDigits)
2269
2270 if ($SessionConfig.Rates)
2271 {
2272 $RigStats.EarningsFiat = [math]::Round(($RigStats.EarningsBtc * $BtcRates[$Config.Currency]), $FiatDigits)
2273 }
2274 }
2275 catch
2276 {
2277 Write-PrettyError "$($Pools[$Profile.Pool].Name) API request failed! Earnings estimations are not updated."
2278
2279 if ($Config.Debug)
2280 {
2281 Write-PrettyDebug $_.Exception
2282 }
2283 }
2284 }
2285 }
2286}
2287
2288function Update-ExchangeRates ()
2289{
2290 try
2291 {
2292 $ResponseRaw = Invoke-WebRequest -Uri $BlockchainUrl -UseBasicParsing
2293 $Response = $ResponseRaw | ConvertFrom-Json -AsHashtable
2294
2295 foreach ($Currency in $Response.Keys)
2296 {
2297 $BtcRates[$Currency] = $Response[$Currency].buy
2298 }
2299 }
2300 catch
2301 {
2302 Write-PrettyError "Error updating BTC exchange rates! Is your network connection working?"
2303
2304 if ($Config.Debug)
2305 {
2306 Write-PrettyDebug $_.Exception
2307 }
2308 }
2309}
2310
2311function Measure-Profit ()
2312{
2313 Update-ExchangeRates
2314 $RigStats.Profit = [math]::Round($RigStats.EarningsFiat - ($Config.ElectricityCost * $RigStats.PowerUsage * 24 / 1000), $FiatDigits)
2315}
2316
2317function Get-Archive ($Url, $FileName)
2318{
2319 $DestFile = [io.path]::combine($TempDir, $FileName)
2320 $Client = New-Object System.Net.WebClient
2321
2322 try
2323 {
2324 $Client.DownloadFile($Url, $DestFile)
2325 }
2326 catch
2327 {
2328 Write-PrettyError "Error downloading package! Is your network connection working?"
2329
2330 if ($Config.Debug)
2331 {
2332 Write-PrettyDebug $_.Exception
2333 }
2334
2335 Exit-RudeHash
2336 }
2337
2338 return $TempDir
2339}
2340
2341function Expand-Stuff ($File, $DestDir)
2342{
2343 if ($File.EndsWith(".7z"))
2344 {
2345 Start-Process -FilePath ([io.path]::combine($ToolsDir, "7zip", $Tools["7zip"].ExeFile)) -ArgumentList ("x -o$DestDir $File") -NoNewWindow -Wait
2346 }
2347 elseif ($File.EndsWith(".exe"))
2348 {
2349 New-Item -ItemType Directory -Path $DestDir | Out-Null
2350 Move-Item -Path $File -Destination $DestDir
2351 }
2352 else
2353 {
2354 Expand-Archive -LiteralPath $File -DestinationPath $DestDir
2355 }
2356}
2357
2358function Test-Tool ($Name)
2359{
2360 $ToolDir = [io.path]::combine($ToolsDir, $Name)
2361 $ToolExe = [io.path]::combine($ToolDir, $Tools[$Name].ExeFile)
2362
2363 if (-Not (Test-Path -LiteralPath $ToolsDir))
2364 {
2365 New-Item -ItemType Directory -Path $ToolsDir | Out-Null
2366 }
2367
2368 if (-Not (Test-Path -LiteralPath $ToolExe))
2369 {
2370 if (Test-Path -LiteralPath $ToolDir)
2371 {
2372 Remove-Item -Recurse -Force -Path $ToolDir
2373 }
2374
2375 Write-PrettyInfo ("Downloading " + $Name + "...")
2376 $ArchiveDir = (Get-Archive ($Tools[$Name].Url) ($Tools[$Name].ArchiveFile))
2377
2378 if ($Tools[$Name].FilesInRoot)
2379 {
2380 $DestPath = ([io.path]::combine($ArchiveDir, $Name))
2381 }
2382 else
2383 {
2384 $DestPath = $ArchiveDir
2385 }
2386
2387 Expand-Stuff ([io.path]::combine($ArchiveDir, $Tools[$Name].ArchiveFile)) $DestPath
2388
2389 if (-Not ($Tools[$Name].FilesInRoot))
2390 {
2391 # don't try to rename tool dir to itself
2392 if (((Get-ChildItem -Directory $ArchiveDir | Select-Object -First 1).FullName).Split('\')[-1] -eq $Name)
2393 {
2394 $DestPath = [io.path]::combine($ArchiveDir, $Name)
2395 }
2396 else
2397 {
2398 $DestPath = (Rename-Item -Path (Get-ChildItem -Directory $ArchiveDir | Select-Object -First 1).FullName -NewName $Name -PassThru).FullName
2399 }
2400 }
2401
2402 Move-Item -Force $DestPath $ToolsDir
2403 }
2404}
2405
2406function Test-Tools ()
2407{
2408 foreach ($Tool in $Tools.Keys)
2409 {
2410 Test-Tool $Tool
2411 }
2412}
2413
2414function Get-ProcessOutput ($Exe, $Argus)
2415{
2416 $ProcInfo = New-Object System.Diagnostics.ProcessStartInfo
2417 $ProcInfo.FileName = $Exe
2418 # stupid PowerShell, $Args is a reserved word
2419 $ProcInfo.Arguments = $Argus
2420 $ProcInfo.RedirectStandardOutput = $true
2421 # nslookup prints "non-authoritative answer" line to stderr because reasons
2422 $ProcInfo.RedirectStandardError = $true
2423 $ProcInfo.UseShellExecute = $false
2424
2425 $Proc = New-Object System.Diagnostics.Process
2426 $Proc.StartInfo = $ProcInfo
2427 $Proc.Start() | Out-Null
2428 $Proc.WaitForExit()
2429 $Str = $Proc.StandardOutput.ReadToEnd()
2430 #$Str += $Proc.StandardError.ReadToEnd()
2431
2432 return $Str
2433}
2434
2435function Get-MinerVersion ($Name)
2436{
2437 $MinerDir = [io.path]::combine($MinersDir, $Name)
2438 $MinerExe = [io.path]::combine($MinerDir, $Miners[$Name].ExeFile)
2439
2440 try {
2441 switch ($Name)
2442 {
2443 # klaust messes up stdio, can't determine version reliably
2444 #"ccminer-klaust" { $VersionStr = (Get-ProcessOutput $MinerExe "-V").Split("`r`n")[0].Split(" ")[1].Split("-")[0] }
2445 "ccminer-phi" { $VersionStr = (Get-ProcessOutput $MinerExe "-V").Split("`r`n")[0].Split("-")[1] }
2446 {$_ -in "ccminer-allium", "ccminer-palginmod", "ccminer-rvn", "ccminer-tpruvot", "nevermore-x16s"} { $VersionStr = (Get-ProcessOutput $MinerExe "-V").Split("`r`n")[0].Split(" ")[2] }
2447 "dstm" { $VersionStr = (Get-ProcessOutput $MinerExe "").Split("`r`n")[0].Split(" ")[1].Split(",")[0] }
2448 "ethminer" { $VersionStr = (Get-ProcessOutput $MinerExe "-V").Split("`r`n")[0].Split(" ")[2].Split("+")[0] }
2449 "excavator" { $VersionStr = (Get-ProcessOutput $MinerExe "-h").Split("`r`n")[2].Trim().Split(" ")[1].Substring(1) }
2450 {$_ -in "hsrminer-hsr", "hsrminer-neoscrypt" } { $VersionStr = (Get-ProcessOutput $MinerExe "-v").Split("`r`n")[17].Trim().Split(" ")[3] }
2451 "vertminer" { $VersionStr = (Get-ProcessOutput $MinerExe "-V").Split("`r`n")[0].Split(" ")[2] }
2452 "zecminer" { $VersionStr = (Get-ProcessOutput $MinerExe "-V").Split("`r`n")[1].Split("|")[1].Trim().Split(" ")[4] }
2453 }
2454 }
2455 catch
2456 {
2457 $VersionStr = "UNKNOWN"
2458 }
2459
2460 return $VersionStr
2461}
2462
2463function Test-Miner ($Name)
2464{
2465 $MinerDir = [io.path]::combine($MinersDir, $Name)
2466 $MinerExe = [io.path]::combine($MinerDir, $Miners[$Name].ExeFile)
2467
2468 # create main miners dir if missing
2469 if (-Not (Test-Path -LiteralPath $MinersDir))
2470 {
2471 New-Item -ItemType Directory -Path $MinersDir | Out-Null
2472 }
2473
2474 $MinerExists = Test-Path -LiteralPath $MinerExe
2475
2476 # update check
2477 if ($Miners[$Name].Version)
2478 {
2479 $LatestVer = $Miners[$Name].Version
2480 $VersionStr = " v" + $LatestVer
2481
2482 if ($MinerExists)
2483 {
2484 $CurrentVer = Get-MinerVersion $Name
2485
2486 if (-Not ($LatestVer -eq $CurrentVer))
2487 {
2488 if ($CurrentVer -eq "UNKNOWN")
2489 {
2490 Write-PrettyInfo ("Unknown " + $Name + " version found, it will be replaced with v" + $LatestVer + ".")
2491 }
2492 else
2493 {
2494 Write-PrettyInfo ($Name + " v" + $CurrentVer + " found, it will be updated to v" + $LatestVer + ".")
2495 }
2496
2497 try
2498 {
2499 Remove-Item -Recurse -Force -Path $MinerDir
2500 }
2501 catch
2502 {
2503 Write-PrettyError ("Error removing " + $Name + " v" + $CurrentVer + "!")
2504 Exit-RudeHash
2505 }
2506
2507 $MinerExists = $false
2508 }
2509 elseif ($Config.Debug)
2510 {
2511 Write-PrettyDebug ($Name + " v" + $CurrentVer + " found, it is the latest version.")
2512 }
2513 }
2514 }
2515
2516 if (-Not $MinerExists)
2517 {
2518 if (Test-Path -LiteralPath $MinerDir)
2519 {
2520 Remove-Item -Recurse -Force -Path $MinerDir
2521 }
2522
2523 Write-PrettyInfo ("Downloading " + $Name + $VersionStr + "...")
2524 $ArchiveDir = (Get-Archive ($Miners[$Name].Url) ($Miners[$Name].ArchiveFile))
2525
2526 if ($Miners[$Name].FilesInRoot)
2527 {
2528 $DestPath = ([io.path]::combine($ArchiveDir, $Name))
2529 }
2530 else
2531 {
2532 $DestPath = $ArchiveDir
2533 }
2534
2535 Expand-Stuff ([io.path]::combine($ArchiveDir, $Miners[$Name].ArchiveFile)) $DestPath
2536
2537 if (-Not ($Miners[$Name].FilesInRoot))
2538 {
2539 # don't try to rename miner dir to itself
2540 if (((Get-ChildItem -Directory $ArchiveDir | Select-Object -First 1).FullName).Split('\')[-1] -eq $Name)
2541 {
2542 $DestPath = [io.path]::combine($ArchiveDir, $Name)
2543 }
2544 else
2545 {
2546 $DestPath = (Rename-Item -Path (Get-ChildItem -Directory $ArchiveDir | Select-Object -First 1).FullName -NewName $Name -PassThru).FullName
2547 }
2548 }
2549
2550 Move-Item -Force $DestPath $MinersDir
2551 }
2552}
2553
2554function Set-WindowTitle ()
2555{
2556 # $Sep = " `u{25a0 | 25bc} "
2557 $Sep = " `u{2219} "
2558
2559 if ($SessionConfig.CoinMode)
2560 {
2561 $CoinStr = ("Coin: " + $Coins[$Profile.Coin].Name + $Sep)
2562 }
2563 else
2564 {
2565 $CoinStr = ""
2566 }
2567
2568 if ($Pools[$Profile.Pool].Authless)
2569 {
2570 $WalletStr = $Sep + "Wallet: " + $Profile.Wallet
2571 $WorkerStr = "Worker: " + $Config.Worker + $Sep
2572 }
2573 else
2574 {
2575 $WalletStr = ""
2576 $WorkerStr = "Worker: " + $Profile.User + "." + $Config.Worker + $Sep
2577 }
2578
2579 $Host.UI.RawUI.WindowTitle = "RudeHash Pigeon Edition " + $Version + $Sep + "Pool: " + $Pools[$Profile.Pool].Name + $Sep + $WorkerStr + $CoinStr + "Algo: " + $AlgoNames[$Profile.Algo] + $Sep + "Miner: " + $Profile.Miner + $WalletStr
2580}
2581
2582function Update-MinerUptime ()
2583{
2584 try
2585 {
2586 $Proc = Get-Process -Id $RigStats.Pid -ErrorVariable Err -ErrorAction SilentlyContinue
2587 $RigStats.Uptime = New-TimeSpan -Start $Proc.StartTime -End (Get-Date)
2588 }
2589 catch
2590 {
2591 Write-PrettyError "Error while checking the miner's uptime!"
2592
2593 if ($Config.Debug)
2594 {
2595 Write-PrettyDebug $Err
2596 }
2597 }
2598}
2599
2600function Get-PrettyUptime ()
2601{
2602 return "$($RigStats.Uptime.Days)d $($RigStats.Uptime.Hours)h $($RigStats.Uptime.Minutes)m"
2603}
2604
2605function Read-Stats ()
2606{
2607 Update-MinerUptime
2608
2609 if ($SessionConfig.DevMining)
2610 {
2611 $RigStats.HashRate = 0;
2612 $RigStats.PowerUsage = 0;
2613 $RigStats.EarningsBtc = 0;
2614 }
2615 elseif ($SessionConfig.Api)
2616 {
2617 if ($RigStats.GpuCount -eq 0)
2618 {
2619 Get-GpuCount
2620 }
2621 else
2622 {
2623 Get-HashRate
2624 Get-PowerUsage
2625
2626 if (($SessionConfig.CoinMode -And $Coins[$Profile.Coin].WtmId) -Or (-Not ($SessionConfig.CoinMode) -And $Pools[$Profile.Pool].ApiUrl))
2627 {
2628 Measure-Earnings
2629
2630 if ($SessionConfig.Rates)
2631 {
2632 if ($RigStats.PowerUsage -gt 0)
2633 {
2634 Measure-Profit
2635 }
2636 }
2637 }
2638 }
2639 }
2640}
2641
2642function Write-Stats ()
2643{
2644 if ($SessionConfig.DevMining)
2645 {
2646 Write-PrettyInfo ("Dev mining minutes " + $RigStats.DevMinutes + "/10")
2647 }
2648 elseif ($SessionConfig.Api)
2649 {
2650 if (-Not ($RigStats.GpuCount -eq 0))
2651 {
2652 $Sep = " `u{2219} "
2653
2654 # ccminer-phi seems to always report 0 watts
2655 if ($RigStats.PowerUsage -gt 0)
2656 {
2657 $PowerUsageStr = $Sep + "Power Usage: " + $RigStats.PowerUsage + " W"
2658 }
2659
2660 Write-PrettyInfo ("Uptime: " + (Get-PrettyUptime) + $Sep + "Number of GPUs: " + $RigStats.GpuCount + $Sep + "Hash Rate: " + (Get-PrettyHashRate $RigStats.HashRate) + $PowerUsageStr)
2661
2662 # use WTM for coins, NH for algos
2663 if (($SessionConfig.CoinMode -And $Coins[$Profile.Coin].WtmId) -Or (-Not ($SessionConfig.CoinMode) -And $Pools[$Profile.Pool].ApiUrl))
2664 {
2665 # we could keep trying to obtain exchange rates, but if it would eventually succeed and the
2666 # list didn't contain the currency specified in the config, it'd result in indexing errors
2667 # or we could re-check the property, but then it'd cause mining to stop; neither is desirable
2668 if ($SessionConfig.Rates)
2669 {
2670 $FiatStr = " / " + $RigStats.EarningsFiat + " " + $Config.Currency
2671
2672 if ($RigStats.PowerUsage -gt 0)
2673 {
2674 $ProfitStr = $Sep + "Daily profit: " + $RigStats.Profit + " " + $Config.Currency
2675 }
2676 }
2677
2678 Write-PrettyEarnings ("Daily earnings: " + ("{0:f$BtcDigits}" -f $RigStats.EarningsBtc) + " BTC" + $FiatStr + $ProfitStr)
2679 }
2680 }
2681 }
2682}
2683
2684function Start-Miner ()
2685{
2686 # in the extremely rare case of AV deleting the miner, or even 7-Zip, try to re-download
2687 Test-Tools
2688 Test-Miner $Profile.Miner
2689
2690 $Exe = [io.path]::combine($MinersDir, $Profile.Miner, $Miners[$Profile.Miner].ExeFile)
2691 $Args = Initialize-MinerArgs
2692
2693 if ($Config.Debug)
2694 {
2695 Write-PrettyDebug ("Stratum address: " + $SessionConfig.Server + ":" + $SessionConfig.Port)
2696 Write-PrettyDebug ("Miner command line: $Exe $Args")
2697 }
2698
2699 $Proc = Start-Process -FilePath $Exe -ArgumentList $Args -PassThru -NoNewWindow
2700 $RigStats.Pid = $Proc.Id
2701 return $Proc
2702}
2703
2704function Enable-DevMining ()
2705{
2706 # we could just re-read the config file but that might cause a file access error in the middle of the day
2707 # let's just make sure we don't do anything risky
2708 $FileConfig.Pool = $Profile.Pool
2709 $Profile.Pool = "zpool"
2710
2711 $FileConfig.Algo = $Profile.Algo
2712 $Profile.Algo = "hsr"
2713
2714 $FileConfig.Miner = $Profile.Miner
2715 $Profile.Miner = "ccminer-alexis-hsr"
2716
2717 $FileConfig.Coin = $Profile.Coin
2718 $Profile.Coin = ""
2719
2720 $FileConfig.Wallet = $Profile.Wallet
2721 $Profile.Wallet = "1HFapEBFTyaJ74SULTJ5oN5BK3C5AYHWzk"
2722
2723 $FileConfig.ExtraArgs = $Profile.ExtraArgs
2724 $Profile.ExtraArgs = ""
2725
2726 # update server, port, etc
2727 Test-Compatibility
2728}
2729
2730function Disable-DevMining ()
2731{
2732 $Profile.Pool = $FileConfig.Pool
2733 $Profile.Algo = $FileConfig.Algo
2734 $Profile.Miner = $FileConfig.Miner
2735 $Profile.Coin = $FileConfig.Coin
2736 $Profile.Wallet = $FileConfig.Wallet
2737 $Profile.ExtraArgs = $FileConfig.ExtraArgs
2738
2739 # update server, port, etc
2740 Test-Compatibility
2741}
2742
2743function Test-DevMining ($Proc)
2744{
2745 if ($SessionConfig.DevMining)
2746 {
2747 $RigStats.DevMinutes += 1
2748 }
2749 # only start devfee processing if user's miner has been running for 24 hours straight
2750 elseif ($RigStats.Uptime.Hours -ge 24)
2751 {
2752 Write-PrettyInfo ("Starting dev mining...")
2753 Enable-DevMining
2754 Stop-Process $Proc
2755 Start-Sleep 5
2756 $Proc = Start-Miner
2757 $SessionConfig.DevMining = $true
2758 }
2759
2760 if ($RigStats.DevMinutes -ge 10)
2761 {
2762 Write-PrettyInfo ("Stopping dev mining...")
2763 Disable-DevMining
2764 Stop-Process $Proc
2765 Start-Sleep 5
2766 $Proc = Start-Miner
2767 $RigStats.DevMinutes = 0
2768 $SessionConfig.DevMining = $false
2769 }
2770
2771 return $Proc
2772}
2773
2774function Ping-Miner ($Proc)
2775{
2776 if ($SessionConfig.Api -And $Profile.Watchdog -And (-Not ($SessionConfig.DevMining)))
2777 {
2778 if ($RigStats.HashRate -eq 0)
2779 {
2780 $RigStats.FailedChecks += 1
2781
2782 if ($RigStats.FailedChecks -ge 2)
2783 {
2784 $Suffix = "s"
2785 }
2786
2787 if ($Config.Debug)
2788 {
2789 Write-PrettyDebug ("Watchdog detected zero hash rate " + $RigStats.FailedChecks + " time" + $Suffix + ".")
2790 }
2791 }
2792
2793 if ($RigStats.FailedChecks -ge 5)
2794 {
2795 Write-PrettyError "Watchdog detected zero hash rate, restarting miner..."
2796 Stop-Process $Proc
2797 # let the dust settle
2798 Start-Sleep 5
2799 $Proc = Start-Miner
2800 $RigStats.FailedChecks = 0
2801 }
2802 }
2803
2804 return $Proc
2805}
2806
2807function Ping-Monitoring ()
2808{
2809 if ($Config.MonitoringKey -Or $Config.MphApiKey)
2810 {
2811 if ($SessionConfig.DevMining)
2812 {
2813 $Name = "dev fee"
2814 $Active = ($RigStats.DevMinutes * 10).ToString() + " %"
2815 }
2816 else
2817 {
2818 $Name = $Profile.Miner
2819 $Active = Get-PrettyUptime
2820 }
2821
2822 $PrettyEarningsBtc = ("{0:f$BtcDigits}" -f $RigStats.EarningsBtc)
2823
2824 $MinerStats= @{
2825 Name = $Name
2826 Path = $Miners[$Profile.Miner].ExeFile
2827 Type = @()
2828 PID = $RigStats.Pid
2829 Version = $Version
2830 Active = $Active
2831 Algorithm = @($AlgoNames[$Profile.Algo])
2832 Pool = @($Pools[$Profile.Pool].Name)
2833 'BTC/day' = $PrettyEarningsBtc
2834 }
2835
2836 # if sent as array, MPM Monitoring displays it as H/s regardless of suffix
2837 # if not sent as array, MPH Stats errors out completely
2838 # because reasons.
2839 $HashRate = Get-PrettyHashRate $RigStats.HashRate
2840
2841 if ($Config.MonitoringKey)
2842 {
2843 try
2844 {
2845 $MinerStats.Add("CurrentSpeed", $HashRate)
2846 $MinerStats.Add("EstimatedSpeed", $HashRate)
2847 $MinerJson = ConvertTo-Json @($MinerStats)
2848 $MinerStats.Remove("CurrentSpeed")
2849 $MinerStats.Remove("EstimatedSpeed")
2850
2851 $Response = Invoke-RestMethod -Uri $MonitoringUrl -Method Post -Body @{ address = $Config.MonitoringKey; workername = $Config.Worker; miners = $MinerJson; profit = $PrettyEarningsBtc } -UseBasicParsing -TimeoutSec 10 -ErrorAction SilentlyContinue
2852
2853 if ($Config.Debug)
2854 {
2855 #Write-PrettyDebug $MinerJson
2856 Write-PrettyDebug ("Monitoring server response: $Response")
2857 }
2858 }
2859 catch
2860 {
2861 Write-PrettyError "Error while pinging the monitoring server!"
2862
2863 if ($Config.Debug)
2864 {
2865 Write-PrettyDebug $_.Exception
2866 }
2867 }
2868 }
2869
2870 if ($Config.MphApiKey)
2871 {
2872 try
2873 {
2874 $MinerStats.Add("CurrentSpeed", @($HashRate))
2875 $MinerStats.Add("EstimatedSpeed", @($HashRate))
2876 $MinerJson = ConvertTo-Json @($MinerStats)
2877
2878 $Response = Invoke-RestMethod -Uri ($MphStatsUrl + $Config.MphApiKey) -Method Post -Body @{ workername = $Config.Worker; miners = $MinerJson; profit = $RigStats.EarningsBtc } -UseBasicParsing -TimeoutSec 10 -ErrorAction SilentlyContinue
2879 }
2880 catch
2881 {
2882 Write-PrettyError "Error while pinging the MiningPoolHubStats server!"
2883
2884 if ($Config.Debug)
2885 {
2886 Write-PrettyDebug $_.Exception
2887 }
2888 }
2889 }
2890 }
2891}
2892
2893function Start-RudeHash ()
2894{
2895 # restart automatically if the miner crashes
2896 while (1)
2897 {
2898 # get GPU count quickly, but not on excavator, it knows the GPU count already
2899 if ($FirstLoop -And $SessionConfig.Api -And (-Not($Profile.Miner -eq "excavator")))
2900 {
2901 $Delay = 15
2902 }
2903 else
2904 {
2905 $Delay = 60
2906 }
2907
2908 if ($FirstLoop -or $Proc.HasExited)
2909 {
2910 if ($Proc.HasExited)
2911 {
2912 Write-PrettyError ("Miner has crashed, restarting...")
2913 }
2914
2915 $Proc = Start-Miner
2916 }
2917 else
2918 {
2919 Read-Stats
2920 $Proc = Ping-Miner $Proc
2921 Write-Stats
2922 Ping-Monitoring
2923 }
2924
2925 Start-Sleep -Seconds $Delay
2926
2927 $FirstLoop = $false
2928 }
2929}
2930
2931if (Test-Path $ConfigFile)
2932{
2933 try
2934 {
2935 $Config = Get-Content -Path $ConfigFile -Raw | ConvertFrom-Json -AsHashtable
2936
2937 # make sure we don't fail if the file is empty
2938 if (-Not ($Config))
2939 {
2940 [System.Collections.Hashtable]$Config = @{}
2941 $FirstRun = $true
2942 }
2943 }
2944 catch [System.Management.Automation.PSInvalidOperationException]
2945 {
2946 Write-PrettyError "Error parsing '$ConfigFile'! Do you have an option set multiple times?"
2947 Exit-RudeHash
2948 }
2949 catch
2950 {
2951 Write-PrettyError "Error accessing '$ConfigFile'! Check it with the config editor!"
2952 Exit-RudeHash
2953 }
2954}
2955else
2956{
2957 $FirstRun = $true
2958
2959 try
2960 {
2961 New-Item $ConfigFile -ItemType File | Out-Null
2962 }
2963 catch
2964 {
2965 Write-PrettyError "Error creating '$ConfigFile'!"
2966 Exit-RudeHash
2967 }
2968}
2969
2970Clear-Host
2971Write-PrettyHeader
2972Initialize-Temp
2973Initialize-Properties
2974Set-WindowTitle
2975Start-RudeHash