1- import hashlib/ rhash/ sha1
2- import net, httpclient
3- import json
4- import strutils, strformat
5- import threadpool
6- import os
7-
8- proc recvAll (s: Socket ): string = # Function for receiving an arbitrary amount of data from the socket
9- var res = " "
10- res = res & s.recv (1 , timeout= 45000 )
11- while s.hasDataBuffered ():
12- res = res & s.recv (1 , timeout= 45000 )
13- return res
1+ import std / [
2+ net,
3+ strutils, strformat, strscans,
4+ threadpool, atomics,
5+ os, times,
6+ json, httpclient
7+ ]
8+
9+ import nimcrypto/ sha
10+
11+ const
12+ monitorInterval = 7500
13+
14+ var
15+ startTime: Time
16+ acceptedCnt, rejectedCnt, hashesCnt, currentDifficulty: Atomic [int ]
1417
15- proc mine (username: string , pool_ip: string , pool_port: Port , difficulty: string , miner_name: string ) {.thread .} = # Mining functions executed in multiple threads
16- var soc: Socket = newSocket () # Creating a new TCP socket
17- soc.connect (pool_ip, pool_port) # Connecting to the mining server
18- discard soc.recv (3 , timeout= 45000 ) # Receiving the server version and voiding it
18+ # Function for receiving an arbitrary amount of data from the socket
19+ proc recvAll (s: Socket ): string =
20+ result = s.recv (1 , timeout= 30000 )
21+ while s.hasDataBuffered ():
22+ result &= s.recv (1 , timeout= 30000 )
1923
20- echo fmt" Thread #{ getThreadId ()} connected to { pool_ip} :{ pool_port} "
24+ proc minerThread (username: string , pool_ip: string , pool_port: Port , difficulty: string , miner_name: string ) {.thread .} = # Mining functions executed in multiple threads
25+ # Connecting to the server and discarding the server verison
26+ var soc: Socket = newSocket ()
27+ soc.connect (pool_ip, pool_port)
28+ discard soc.recvAll ()
2129
22- var job: seq [string ]
23- var feedback: string
30+ # echo fmt"Thread #{getThreadId()} connected to {pool_ip}:{pool_port}"
2431
25- while true : # An infinite loop of requesting and solving jobs
26- if difficulty == " NORMAL" : # Checking if the difficulty is set to "NORMAL" and sending a job request to the server
32+ while true :
33+ # Checking if the difficulty is set to "NORMAL" and sending a job request to the server
34+ if difficulty == " NORMAL" :
2735 soc.send (fmt" JOB,{ username} " )
2836 else :
2937 soc.send (fmt" JOB,{ username} ,{ difficulty} " )
30- job = soc.recvAll ().split (" ," ) # Receiving a job from the server that is comma-separated
31- for result in 0 .. 100 * parseInt (job[2 ]): # A loop for solving the job
32- if $ count [RHASH_SHA1 ](job[0 ] & $ (result )) == job[1 ]: # Checking if the hashes of the job matches our hash
33- soc.send ($ (result ) & " ,," & miner_name) # Sending the result to the server
34- feedback = soc.recvAll () # Receiving feedback from the server
38+
39+ # Receiving and parsing the job from the server
40+ var job = soc.recvAll ()
41+ var
42+ prefix, target: string
43+ diff: int
44+ if not scanf (job, " $+,$+,$i" , prefix, target, diff):
45+ quit (" Error: couldn't parse job from the server!" )
46+ target = target.toUpper ()
47+ currentDifficulty.store (diff)
48+
49+ # Initialize the sha1 context and add prefix
50+ var ctx: sha1
51+ ctx.init ()
52+ ctx.update (prefix)
53+
54+ # A loop for solving the job
55+ for res in 0 .. 100 * diff:
56+ # Copy the initialized context and add the value
57+ var ctxCopy = ctx
58+ ctxCopy.update ($ res)
59+
60+ # Checking if the hash of the job matches our hash
61+ if $ ctxCopy.finish () == target:
62+ hashesCnt.atomicInc (res)
63+ soc.send (fmt" { $ res} ,,{ miner_name} " )
64+
65+ # Receiving and checking the feedback from the server
66+ let feedback = soc.recvAll ()
3567 if feedback == " GOOD" : # Checking the server feedback
36- echo fmt " Accepted share { result } with a difficulty of { parseInt (job[ 2 ]) } "
68+ acceptedCnt. atomicInc ()
3769 elif feedback == " BAD" :
38- echo fmt" Rejected share { result } with a difficulty of { parseInt (job[2 ])} "
39- break # Breaking from the loop, as the job was solved
70+ rejectedCnt.atomicInc ()
71+
72+ # Breaking from the loop, because the job was solved
73+ break
74+
75+
76+ proc monitorThread () {.thread .} =
77+ startTime = getTime ()
78+ echo fmt" Statistics update interval: { monitorInterval / 1000 } seconds "
79+ while true :
80+ sleep (monitorInterval)
81+ # Get time diff in milliseconds
82+ let mils = (getTime () - startTime).inMilliseconds.float
83+
84+ # Calculate amount of hashes per second
85+ let hashesSec = (hashesCnt.load ().float / mils) * 1000
86+ let khsec = hashesSec / 1000
87+ let mhsec = khsec / 1000
88+ let toShow = if mhsec >= 1 :
89+ mhsec.formatFloat (ffDecimal, 2 ) & " MH/s"
90+ elif khsec >= 1 :
91+ khsec.formatFloat (ffDecimal, 2 ) & " KH/s"
92+ else :
93+ hashesSec.formatFloat (ffDecimal, 2 ) & " H/s"
94+
95+ startTime = getTime ()
96+ let strTime = startTime.format (" HH:mm:ss" )
97+ echo fmt" { strTime} Hashrate: { toShow} , Accepted: { acceptedCnt.load ()} , Rejected: { rejectedCnt.load ()} , Difficulty: { currentDifficulty.load ()} "
98+
99+ # Resetting hash count
100+ hashesCnt.store (0 )
101+
40102
41103var config: JsonNode
42104if paramCount () < 1 :
@@ -54,16 +116,22 @@ else:
54116
55117let client: HttpClient = newHttpClient () # Creating a new HTTP client
56118
57- var pool_address: string = client.getContent (config[" ip_url" ].getStr ()) # Making a request to the URL specified in the config for getting mining server details
119+ var pool_address: seq [string ] = client.getContent (config[" ip_url" ].getStr ()).split (" \n " ) # Making a request to the URL specified in the config for getting mining server details
120+
121+ var pool_ip: string = pool_address[0 ] # Parsing the server IP
122+ var pool_port: Port = Port (parseInt (pool_address[1 ])) # Parsing the server port
58123
59- var pool_ip: string = pool_address.split (" \n " )[0 ] # Parsing the server IP
60- var pool_port: Port = Port (parseInt (pool_address.split (" \n " )[1 ])) # Parsing the server port
124+ var username = config[" username" ].getStr ()
125+ var difficulty = config[" difficulty" ].getStr ()
126+ var miner_name = config[" miner_name" ].getStr ()
127+ var thread_count = config[" thread_count" ].getInt ()
61128
62- var username = config[" username" ].getStr (default = " 5Q" )
63- var difficulty = config[" difficulty" ].getStr (default = " NORMAL" )
64- var miner_name = config[" miner_name" ].getStr (default = " DUCOMiner-Nim" )
65- var thread_count = config[" thread_count" ].getInt (default = 16 )
129+ # Starting mining threads and the monitor thread
130+ for i in 0 ..< thread_count:
131+ spawn minerThread (username, pool_ip, pool_port, difficulty, miner_name)
132+ sleep (300 )
133+ echo " Started all mining threads"
134+ spawn monitorThread ()
66135
67- for i in countup (0 , thread_count - 1 ): # A loop that spawns new threads executing the mine() function
68- spawn mine (username, pool_ip, pool_port, difficulty, miner_name)
69- sync () # Synchronizing the threads so the program doesn't exit until Ctrl+C is pressed or an exception is raised
136+ # Synchronizing the threads so the program doesn't exit until Ctrl+C is pressed or an exception is raised
137+ sync ()
0 commit comments