Skip to content

Commit

Permalink
Change hashing libary, implement midstate caching, hashrate monitoring
Browse files Browse the repository at this point in the history
  • Loading branch information
its5Q committed Feb 2, 2021
1 parent 4f3fa4f commit 509e703
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 44 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
nimcache/
nimblecache/
htmldocs/
compile*
compile*
ducominer.exe
ducominer
148 changes: 108 additions & 40 deletions ducominer.nim
Original file line number Diff line number Diff line change
@@ -1,42 +1,104 @@
import hashlib/rhash/sha1
import net, httpclient
import json
import strutils, strformat
import threadpool
import os

proc recvAll(s: Socket): string = # Function for receiving an arbitrary amount of data from the socket
var res = ""
res = res & s.recv(1, timeout=45000)
while s.hasDataBuffered():
res = res & s.recv(1, timeout=45000)
return res
import std / [
net,
strutils, strformat, strscans,
threadpool, atomics,
os, times,
json, httpclient
]

import nimcrypto/sha

const
monitorInterval = 7500

var
startTime: Time
acceptedCnt, rejectedCnt, hashesCnt, currentDifficulty: Atomic[int]

proc mine(username: string, pool_ip: string, pool_port: Port, difficulty: string, miner_name: string) {.thread.} = # Mining functions executed in multiple threads
var soc: Socket = newSocket() # Creating a new TCP socket
soc.connect(pool_ip, pool_port) # Connecting to the mining server
discard soc.recv(3, timeout=45000) # Receiving the server version and voiding it
# Function for receiving an arbitrary amount of data from the socket
proc recvAll(s: Socket): string =
result = s.recv(1, timeout=30000)
while s.hasDataBuffered():
result &= s.recv(1, timeout=30000)

echo fmt"Thread #{getThreadId()} connected to {pool_ip}:{pool_port}"
proc minerThread(username: string, pool_ip: string, pool_port: Port, difficulty: string, miner_name: string) {.thread.} = # Mining functions executed in multiple threads
# Connecting to the server and discarding the server verison
var soc: Socket = newSocket()
soc.connect(pool_ip, pool_port)
discard soc.recvAll()

var job: seq[string]
var feedback: string
# echo fmt"Thread #{getThreadId()} connected to {pool_ip}:{pool_port}"

while true: # An infinite loop of requesting and solving jobs
if difficulty == "NORMAL": # Checking if the difficulty is set to "NORMAL" and sending a job request to the server
while true:
# Checking if the difficulty is set to "NORMAL" and sending a job request to the server
if difficulty == "NORMAL":
soc.send(fmt"JOB,{username}")
else:
soc.send(fmt"JOB,{username},{difficulty}")
job = soc.recvAll().split(",") # Receiving a job from the server that is comma-separated
for result in 0..100 * parseInt(job[2]): # A loop for solving the job
if $count[RHASH_SHA1](job[0] & $(result)) == job[1]: # Checking if the hashes of the job matches our hash
soc.send($(result) & ",," & miner_name) # Sending the result to the server
feedback = soc.recvAll() # Receiving feedback from the server

# Receiving and parsing the job from the server
var job = soc.recvAll()
var
prefix, target: string
diff: int
if not scanf(job, "$+,$+,$i", prefix, target, diff):
quit("Error: couldn't parse job from the server!")
target = target.toUpper()
currentDifficulty.store(diff)

# Initialize the sha1 context and add prefix
var ctx: sha1
ctx.init()
ctx.update(prefix)

# A loop for solving the job
for res in 0 .. 100 * diff:
# Copy the initialized context and add the value
var ctxCopy = ctx
ctxCopy.update($res)

# Checking if the hash of the job matches our hash
if $ctxCopy.finish() == target:
hashesCnt.atomicInc(res)
soc.send(fmt"{$res},,{miner_name}")

# Receiving and checking the feedback from the server
let feedback = soc.recvAll()
if feedback == "GOOD": # Checking the server feedback
echo fmt"Accepted share {result} with a difficulty of {parseInt(job[2])}"
acceptedCnt.atomicInc()
elif feedback == "BAD":
echo fmt"Rejected share {result} with a difficulty of {parseInt(job[2])}"
break # Breaking from the loop, as the job was solved
rejectedCnt.atomicInc()

# Breaking from the loop, because the job was solved
break


proc monitorThread() {.thread.} =
startTime = getTime()
echo fmt"Statistics update interval: {monitorInterval / 1000} seconds"
while true:
sleep(monitorInterval)
# Get time diff in milliseconds
let mils = (getTime() - startTime).inMilliseconds.float

# Calculate amount of hashes per second
let hashesSec = (hashesCnt.load().float / mils) * 1000
let khsec = hashesSec / 1000
let mhsec = khsec / 1000
let toShow = if mhsec >= 1:
mhsec.formatFloat(ffDecimal, 2) & " MH/s"
elif khsec >= 1:
khsec.formatFloat(ffDecimal, 2) & " KH/s"
else:
hashesSec.formatFloat(ffDecimal, 2) & " H/s"

startTime = getTime()
let strTime = startTime.format("HH:mm:ss")
echo fmt"{strTime} Hashrate: {toShow}, Accepted: {acceptedCnt.load()}, Rejected: {rejectedCnt.load()}, Difficulty: {currentDifficulty.load()}"

# Resetting hash count
hashesCnt.store(0)


var config: JsonNode
if paramCount() < 1:
Expand All @@ -54,16 +116,22 @@ else:

let client: HttpClient = newHttpClient() # Creating a new HTTP client

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
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

var pool_ip: string = pool_address[0] # Parsing the server IP
var pool_port: Port = Port(parseInt(pool_address[1])) # Parsing the server port

var pool_ip: string = pool_address.split("\n")[0] # Parsing the server IP
var pool_port: Port = Port(parseInt(pool_address.split("\n")[1])) # Parsing the server port
var username = config["username"].getStr()
var difficulty = config["difficulty"].getStr()
var miner_name = config["miner_name"].getStr()
var thread_count = config["thread_count"].getInt()

var username = config["username"].getStr(default = "5Q")
var difficulty = config["difficulty"].getStr(default = "NORMAL")
var miner_name = config["miner_name"].getStr(default = "DUCOMiner-Nim")
var thread_count = config["thread_count"].getInt(default = 16)
# Starting mining threads and the monitor thread
for i in 0 ..< thread_count:
spawn minerThread(username, pool_ip, pool_port, difficulty, miner_name)
sleep(300)
echo "Started all mining threads"
spawn monitorThread()

for i in countup(0, thread_count - 1): # A loop that spawns new threads executing the mine() function
spawn mine(username, pool_ip, pool_port, difficulty, miner_name)
sync() # Synchronizing the threads so the program doesn't exit until Ctrl+C is pressed or an exception is raised
# Synchronizing the threads so the program doesn't exit until Ctrl+C is pressed or an exception is raised
sync()
2 changes: 1 addition & 1 deletion ducominer.nim.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
-d:ssl
--threads:on
--threads:on
4 changes: 2 additions & 2 deletions ducominer.nimble
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Package Information
version = "1.0.0"
version = "1.2.0"
author = "its5Q"
description = "A multithreaded miner for DuinoCoin written in Nim."
license = "MIT"

bin = @["ducominer"]

# Dependencies
requires "hashlib"
requires "nimcrypto"

0 comments on commit 509e703

Please sign in to comment.