Browse Source

PPLNS / SOLO

master
yuriy0803 2 years ago
parent
commit
a9f3430422
  1. 365
      api/server.go
  2. 33
      exchange/exchange_test.go
  3. 8
      main.go
  4. 53
      payouts/payer.go
  5. 185
      payouts/unlocker.go
  6. 158
      payouts/unlocker_test.go
  7. 23
      proxy/blocks.go
  8. 7
      proxy/config.go
  9. 4
      proxy/handlers.go
  10. 69
      proxy/miner.go
  11. 2
      proxy/proto.go
  12. 73
      proxy/proxy.go
  13. 21
      rpc/rpc.go
  14. 1787
      storage/redis.go
  15. 591
      storage/redis_test.go
  16. 1
      util/util.go

365
api/server.go

@ -4,18 +4,20 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
"net"
"net/http" "net/http"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"math/big"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/robfig/cron"
"github.com/robfig/cron"
"github.com/yuriy0803/open-etc-pool-friends/storage" "github.com/yuriy0803/open-etc-pool-friends/storage"
"github.com/yuriy0803/open-etc-pool-friends/util" "github.com/yuriy0803/open-etc-pool-friends/util"
) )
@ -23,14 +25,6 @@ import (
type ApiConfig struct { type ApiConfig struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
Listen string `json:"listen"` Listen string `json:"listen"`
PoolCharts string `json:"poolCharts"`
PoolChartsNum int64 `json:"poolChartsNum"`
NetCharts string `json:"netCharts"`
NetChartsNum int64 `json:"netChartsNum"`
MinerChartsNum int64 `json:"minerChartsNum"`
MinerCharts string `json:"minerCharts"`
ShareCharts string `json:"shareCharts"`
ShareChartsNum int64 `json:"shareChartsNum"`
StatsCollectInterval string `json:"statsCollectInterval"` StatsCollectInterval string `json:"statsCollectInterval"`
HashrateWindow string `json:"hashrateWindow"` HashrateWindow string `json:"hashrateWindow"`
HashrateLargeWindow string `json:"hashrateLargeWindow"` HashrateLargeWindow string `json:"hashrateLargeWindow"`
@ -39,6 +33,14 @@ type ApiConfig struct {
Blocks int64 `json:"blocks"` Blocks int64 `json:"blocks"`
PurgeOnly bool `json:"purgeOnly"` PurgeOnly bool `json:"purgeOnly"`
PurgeInterval string `json:"purgeInterval"` PurgeInterval string `json:"purgeInterval"`
PoolCharts string `json:"poolCharts"`
PoolChartsNum int64 `json:"poolChartsNum"`
MinerChartsNum int64 `json:"minerChartsNum"`
NetCharts string `json:"netCharts"`
NetChartsNum int64 `json:"netChartsNum"`
MinerCharts string `json:"minerCharts"`
ShareCharts string `json:"shareCharts"`
ShareChartsNum int64 `json:"shareChartsNum"`
} }
type ApiServer struct { type ApiServer struct {
@ -57,6 +59,8 @@ type Entry struct {
updatedAt int64 updatedAt int64
} }
const diff = 4000000000
func NewApiServer(cfg *ApiConfig, backend *storage.RedisClient) *ApiServer { func NewApiServer(cfg *ApiConfig, backend *storage.RedisClient) *ApiServer {
hashrateWindow := util.MustParseDuration(cfg.HashrateWindow) hashrateWindow := util.MustParseDuration(cfg.HashrateWindow)
hashrateLargeWindow := util.MustParseDuration(cfg.HashrateLargeWindow) hashrateLargeWindow := util.MustParseDuration(cfg.HashrateLargeWindow)
@ -110,7 +114,6 @@ func (s *ApiServer) Start() {
go func() { go func() {
c := cron.New() c := cron.New()
poolCharts := s.config.PoolCharts poolCharts := s.config.PoolCharts
log.Printf("Pool charts config is :%v", poolCharts) log.Printf("Pool charts config is :%v", poolCharts)
c.AddFunc(poolCharts, func() { c.AddFunc(poolCharts, func() {
@ -126,14 +129,16 @@ func (s *ApiServer) Start() {
minerCharts := s.config.MinerCharts minerCharts := s.config.MinerCharts
log.Printf("Miner charts config is :%v", minerCharts) log.Printf("Miner charts config is :%v", minerCharts)
c.AddFunc(minerCharts, func() { c.AddFunc(minerCharts, func() {
miners, err := s.backend.GetAllMinerAccount() miners, err := s.backend.GetAllMinerAccount()
if err != nil { if err != nil {
log.Println("Get all miners account error: ", err) log.Println("Get all miners account error: ", err)
} }
for _, login := range miners { for _, login := range miners {
miner, _ := s.backend.CollectWorkersStats(s.hashrateWindow, s.hashrateLargeWindow, login) miner, _ := s.backend.CollectWorkersStats(s.hashrateWindow, s.hashrateLargeWindow, login)
s.collectMinerCharts(login, miner["currentHashrate"].(int64), miner["hashrate"].(int64), miner["workersOnline"].(int64)) s.collectMinerCharts(login, miner["currentHashrate"].(int64), miner["hashrate"].(int64), miner["workersOnline"].(int64))
} }
}) })
@ -176,120 +181,155 @@ func (s *ApiServer) Start() {
} }
} }
func (s *ApiServer) collectPoolCharts() { func (s *ApiServer) listen() {
ts := util.MakeTimestamp() / 1000 r := mux.NewRouter()
now := time.Now() r.HandleFunc("/api/stats", s.StatsIndex)
year, month, day := now.Date() r.HandleFunc("/api/finders", s.FindersIndex)
hour, min, _ := now.Clock() r.HandleFunc("/api/miners", s.MinersIndex)
t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min) r.HandleFunc("/api/blocks", s.BlocksIndex)
r.HandleFunc("/api/payments", s.PaymentsIndex)
r.HandleFunc("/api/accounts/{login:0x[0-9a-fA-F]{40}}", s.AccountIndex)
r.HandleFunc("/api/settings", s.SubscribeHandler).Methods("POST")
r.HandleFunc("/api/mining", s.MiningHandler).Methods("POST")
r.NotFoundHandler = http.HandlerFunc(notFound)
err := http.ListenAndServe(s.config.Listen, r)
if err != nil {
log.Fatalf("Failed to start API: %v", err)
}
}
func (s *ApiServer) FindersIndex(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Cache-Control", "no-cache")
w.WriteHeader(http.StatusOK)
reply := make(map[string]interface{})
stats := s.getStats() stats := s.getStats()
hash := fmt.Sprint(stats["hashrate"]) if stats != nil {
log.Println("Pool Hash is ", ts, t2, hash) reply["now"] = util.MakeTimestamp()
err := s.backend.WritePoolCharts(ts, t2, hash) reply["finders"] = stats["finders"]
}
err := json.NewEncoder(w).Encode(reply)
if err != nil { if err != nil {
log.Printf("Failed to fetch pool charts from backend: %v", err) log.Println("Error serializing API response: ", err)
return
} }
} }
func (s *ApiServer) collectnetCharts() { func (s *ApiServer) collectPoolCharts() {
ts := util.MakeTimestamp() / 1000 ts := util.MakeTimestamp() / 1000
now := time.Now() now := time.Now()
year, month, day := now.Date() year, month, day := now.Date()
hour, min, _ := now.Clock() hour, min, _ := now.Clock()
t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min) t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min)
//stats := s.getStats() stats := s.getStats()
//diff := fmt.Sprint(stats["difficulty"]) hash := fmt.Sprint(stats["hashrate"])
nodes, erro := s.backend.GetNodeStates() err := s.backend.WritePoolCharts(ts, t2, hash)
if erro != nil {
log.Printf("Failed to fetch Diff charts from backend: %v", erro)
return
}
diff := fmt.Sprint(nodes[0]["difficulty"])
log.Println("Difficulty Hash is ", ts, t2, diff)
err := s.backend.WriteDiffCharts(ts, t2, diff)
if err != nil { if err != nil {
log.Printf("Failed to fetch Diff charts from backend: %v", err) log.Printf("Failed to fetch pool charts from backend: %v", err)
return return
} }
} }
func (s *ApiServer) collectMinerCharts(login string, hash int64, largeHash int64, workerOnline int64) { func (s *ApiServer) collectMinerCharts(login string, hash int64, largeHash int64, workerOnline int64) {
ts := util.MakeTimestamp() / 1000 ts := util.MakeTimestamp() / 1000
now := time.Now() now := time.Now()
year, month, day := now.Date() year, month, day := now.Date()
hour, min, _ := now.Clock() hour, min, _ := now.Clock()
t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min) t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min)
//log.Println("Miner "+login+" Hash is", ts, t2, hash, largeHash)
log.Println("Miner "+login+" Hash is", ts, t2, hash, largeHash)
err := s.backend.WriteMinerCharts(ts, t2, login, hash, largeHash, workerOnline) err := s.backend.WriteMinerCharts(ts, t2, login, hash, largeHash, workerOnline)
if err != nil { if err != nil {
log.Printf("Failed to fetch miner %v charts from backend: %v", login, err) log.Printf("Failed to fetch miner %v charts from backend: %v", login, err)
} }
} }
func (s *ApiServer) collectshareCharts(login string, workerOnline int64) { func (s *ApiServer) SubscribeHandler(w http.ResponseWriter, r *http.Request) {
ts := util.MakeTimestamp() / 1000 w.Header().Set("Content-Type", "application/json; charset=UTF-8")
now := time.Now() w.Header().Set("Access-Control-Allow-Origin", "*")
year, month, day := now.Date() w.Header().Set("Cache-Control", "no-cache")
hour, min, _ := now.Clock() w.WriteHeader(http.StatusOK)
t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min) reply := make(map[string]interface{})
log.Println("Share chart is created", ts, t2) reply["result"] = "IP address doesn`t match"
var email = r.FormValue("email")
//var threshold = r.FormValue("threshold")
var ipAddress = r.FormValue("ip_address")
var login = r.FormValue("login")
var threshold = r.FormValue("threshold")
if threshold == "" {
threshold = "0.5"
}
err := s.backend.WriteShareCharts(ts, t2, login, 0, 0, workerOnline) alert := "off"
if err != nil { if r.FormValue("alertCheck") != "" {
log.Printf("Failed to fetch miner %v charts from backend: %v", login, err) alert = r.FormValue("alertCheck")
} }
ip_address := s.backend.GetIP(login)
password := s.backend.GetPassword(login)
if ip_address == ipAddress || password == ipAddress {
s.backend.SetIP(login, ipAddress)
number, err := strconv.ParseFloat(threshold, 64)
if err != nil {
log.Println("Error Parsing threshold response: ", err)
} }
func (s *ApiServer) listen() { shannon := float64(1000000000)
r := mux.NewRouter() s.backend.SetThreshold(login, int64(number*shannon))
// Add middleware to log the real client IP address s.backend.SetMailAddress(login, email)
r.Use(func(next http.Handler) http.Handler { s.backend.SetAlert(login, alert)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get the real client IP address using the getClientIP function
clientIP := getClientIP(r)
// Log the request details including IP address, method, and path reply["result"] = "success"
log.Printf("[Diagnostics] Request from IP: %s - Method: %s, Path: %s", clientIP, r.Method, r.URL.Path) }
// Call the next handler in the middleware chain err := json.NewEncoder(w).Encode(reply)
next.ServeHTTP(w, r)
})
})
r.HandleFunc("/api/finders", s.FindersIndex)
r.HandleFunc("/api/stats", s.StatsIndex)
r.HandleFunc("/api/miners", s.MinersIndex)
r.HandleFunc("/api/blocks", s.BlocksIndex)
r.HandleFunc("/api/payments", s.PaymentsIndex)
r.HandleFunc("/api/accounts/{login:0x[0-9a-fA-F]{40}}", s.AccountIndex)
r.HandleFunc("/api/settings", s.SubscribeHandler).Methods("POST")
r.NotFoundHandler = http.HandlerFunc(notFound)
err := http.ListenAndServe(s.config.Listen, r)
if err != nil { if err != nil {
log.Fatalf("Failed to start API: %v", err) log.Println("Error serializing API response: ", err)
} }
} }
// getClientIP returns the real client IP address from the http.Request object func dot2comma(r rune) rune {
func getClientIP(r *http.Request) string { if r == '.' {
// Try to use the "X-Forwarded-For" header, if available return ','
forwardedFor := r.Header.Get("X-Forwarded-For") }
if forwardedFor != "" { return r
// Extract the first IP address from the list (if multiple are present)
return strings.TrimSpace(strings.Split(forwardedFor, ",")[0])
} }
// Try to use the "X-Real-IP" header, if available func (s *ApiServer) MiningHandler(w http.ResponseWriter, r *http.Request) {
realIP := r.Header.Get("X-Real-IP") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
if realIP != "" { w.Header().Set("Access-Control-Allow-Origin", "*")
return realIP w.Header().Set("Cache-Control", "no-cache")
w.WriteHeader(http.StatusOK)
reply := make(map[string]interface{})
reply["result"] = "IP address doesn`t match"
var miningType = r.FormValue("radio")
var login = r.FormValue("login")
var ipAddress = r.FormValue("ip_address")
ip_address := s.backend.GetIP(login)
password := s.backend.GetPassword(login)
if ip_address == ipAddress || password == ipAddress {
s.backend.SetMiningType(login, miningType)
s.backend.SetIP(login, ipAddress)
reply["result"] = "success"
}
err := json.NewEncoder(w).Encode(reply)
if err != nil {
log.Println("Error serializing API response: ", err)
} }
// If neither X-Forwarded-For nor X-Real-IP is set, use the remote address
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
return ip
} }
func notFound(w http.ResponseWriter, r *http.Request) { func notFound(w http.ResponseWriter, r *http.Request) {
@ -319,36 +359,17 @@ func (s *ApiServer) collectStats() {
if len(s.config.LuckWindow) > 0 { if len(s.config.LuckWindow) > 0 {
stats["luck"], err = s.backend.CollectLuckStats(s.config.LuckWindow) stats["luck"], err = s.backend.CollectLuckStats(s.config.LuckWindow)
stats["luckCharts"], err = s.backend.CollectLuckCharts(s.config.LuckWindow[0]) stats["luckCharts"], err = s.backend.CollectLuckCharts(s.config.LuckWindow[0])
stats["netCharts"], err = s.backend.GetNetCharts(s.config.NetChartsNum)
if err != nil { if err != nil {
log.Printf("Failed to fetch luck stats from backend: %v", err) log.Printf("Failed to fetch luck stats from backend: %v", err)
return return
} }
} }
stats["netCharts"], err = s.backend.GetNetCharts(s.config.NetChartsNum)
stats["poolCharts"], err = s.backend.GetPoolCharts(s.config.PoolChartsNum) stats["poolCharts"], err = s.backend.GetPoolCharts(s.config.PoolChartsNum)
s.stats.Store(stats) s.stats.Store(stats)
log.Printf("Stats collection finished %s", time.Since(start)) log.Printf("Stats collection finished %s", time.Since(start))
} }
func (s *ApiServer) FindersIndex(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Cache-Control", "no-cache")
w.WriteHeader(http.StatusOK)
reply := make(map[string]interface{})
stats := s.getStats()
if stats != nil {
reply["now"] = util.MakeTimestamp()
reply["finders"] = stats["finders"]
}
err := json.NewEncoder(w).Encode(reply)
if err != nil {
log.Println("Error serializing API response: ", err)
}
}
func (s *ApiServer) StatsIndex(w http.ResponseWriter, r *http.Request) { func (s *ApiServer) StatsIndex(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Origin", "*")
@ -366,15 +387,16 @@ func (s *ApiServer) StatsIndex(w http.ResponseWriter, r *http.Request) {
if stats != nil { if stats != nil {
reply["now"] = util.MakeTimestamp() reply["now"] = util.MakeTimestamp()
reply["stats"] = stats["stats"] reply["stats"] = stats["stats"]
reply["poolCharts"] = stats["poolCharts"]
reply["hashrate"] = stats["hashrate"] reply["hashrate"] = stats["hashrate"]
reply["minersTotal"] = stats["minersTotal"] reply["minersTotal"] = stats["minersTotal"]
reply["maturedTotal"] = stats["maturedTotal"] reply["maturedTotal"] = stats["maturedTotal"]
reply["immatureTotal"] = stats["immatureTotal"] reply["immatureTotal"] = stats["immatureTotal"]
reply["candidatesTotal"] = stats["candidatesTotal"] reply["candidatesTotal"] = stats["candidatesTotal"]
reply["exchangedata"] = stats["exchangedata"] reply["exchangedata"] = stats["exchangedata"]
reply["poolCharts"] = stats["poolCharts"]
reply["netCharts"] = stats["netCharts"] reply["netCharts"] = stats["netCharts"]
reply["workersTotal"] = stats["workersTotal"] reply["workersTotal"] = stats["workersTotal"]
//reply["nShares"] = stats["nShares"]
} }
err = json.NewEncoder(w).Encode(reply) err = json.NewEncoder(w).Encode(reply)
@ -396,6 +418,7 @@ func (s *ApiServer) MinersIndex(w http.ResponseWriter, r *http.Request) {
reply["miners"] = stats["miners"] reply["miners"] = stats["miners"]
reply["hashrate"] = stats["hashrate"] reply["hashrate"] = stats["hashrate"]
reply["minersTotal"] = stats["minersTotal"] reply["minersTotal"] = stats["minersTotal"]
reply["workersTotal"] = stats["workersTotal"]
} }
err := json.NewEncoder(w).Encode(reply) err := json.NewEncoder(w).Encode(reply)
@ -421,6 +444,41 @@ func (s *ApiServer) BlocksIndex(w http.ResponseWriter, r *http.Request) {
reply["candidatesTotal"] = stats["candidatesTotal"] reply["candidatesTotal"] = stats["candidatesTotal"]
reply["luck"] = stats["luck"] reply["luck"] = stats["luck"]
reply["luckCharts"] = stats["luckCharts"] reply["luckCharts"] = stats["luckCharts"]
mt, _ := strconv.Atoi(fmt.Sprintf("%d", stats["maturedTotal"]))
it, _ := strconv.Atoi(fmt.Sprintf("%d", stats["immatureTotal"]))
ct, _ := strconv.Atoi(fmt.Sprintf("%d", stats["candidatesTotal"]))
reply["blocksTotal"] = mt + it + ct
tmp := stats["stats"].(map[string]interface{})
crs := fmt.Sprintf("%d", tmp["currentRoundShares"])
crsInt, _ := strconv.Atoi(crs)
networkDiff, _ := s.backend.GetNetworkDifficulty()
multiple := crsInt * diff
multipleFloat := float64(multiple)
networkDiffFloat := new(big.Float).SetInt(networkDiff)
x := big.NewFloat(multipleFloat)
variance := new(big.Float).Quo(x, networkDiffFloat)
reply["variance"] = variance
nodes, err := s.backend.GetNodeStates()
if err != nil {
log.Printf("Failed to get nodes stats from backend: %v", err)
}
reply["nodes"] = nodes
reply["lastBlockFound"] = tmp["lastBlockFound"]
currentHeight := "0"
for _, value := range nodes {
currentHeight = value["height"].(string)
}
reply["currentHeight"] = currentHeight
} }
err := json.NewEncoder(w).Encode(reply) err := json.NewEncoder(w).Encode(reply)
@ -437,9 +495,11 @@ func (s *ApiServer) PaymentsIndex(w http.ResponseWriter, r *http.Request) {
reply := make(map[string]interface{}) reply := make(map[string]interface{})
stats := s.getStats() stats := s.getStats()
if stats != nil { if stats != nil {
reply["payments"] = stats["payments"] reply["payments"] = stats["payments"]
reply["paymentsTotal"] = stats["paymentsTotal"] reply["paymentsTotal"] = stats["paymentsTotal"]
reply["paymentsSum"] = stats["paymentsSum"]
reply["exchangedata"] = stats["exchangedata"] reply["exchangedata"] = stats["exchangedata"]
} }
@ -458,8 +518,6 @@ func (s *ApiServer) AccountIndex(w http.ResponseWriter, r *http.Request) {
s.minersMu.Lock() s.minersMu.Lock()
defer s.minersMu.Unlock() defer s.minersMu.Unlock()
generalstats := s.getStats()
reply, ok := s.miners[login] reply, ok := s.miners[login]
now := util.MakeTimestamp() now := util.MakeTimestamp()
cacheIntv := int64(s.statsIntv / time.Millisecond) cacheIntv := int64(s.statsIntv / time.Millisecond)
@ -476,7 +534,14 @@ func (s *ApiServer) AccountIndex(w http.ResponseWriter, r *http.Request) {
return return
} }
stats, err := s.backend.GetMinerStats(login, s.config.Payments) miningType := s.backend.GetMiningType(login)
var stats map[string]interface{}
if miningType == "solo" {
stats, err = s.backend.GetMinerStatsSolo(login, s.config.Payments)
} else {
stats, err = s.backend.GetMinerStats(login, s.config.Payments)
}
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
log.Printf("Failed to fetch stats from backend: %v", err) log.Printf("Failed to fetch stats from backend: %v", err)
@ -492,11 +557,22 @@ func (s *ApiServer) AccountIndex(w http.ResponseWriter, r *http.Request) {
stats[key] = value stats[key] = value
} }
stats["pageSize"] = s.config.Payments stats["pageSize"] = s.config.Payments
stats["exchangedata"] = generalstats["exchangedata"]
stats["minerCharts"], err = s.backend.GetMinerCharts(s.config.MinerChartsNum, login) stats["minerCharts"], err = s.backend.GetMinerCharts(s.config.MinerChartsNum, login)
stats["shareCharts"], err = s.backend.GetShareCharts(s.config.ShareChartsNum, login) stats["shareCharts"], err = s.backend.GetShareCharts(s.config.ShareChartsNum, login)
stats["paymentCharts"], err = s.backend.GetPaymentCharts(login) stats["paymentCharts"], err = s.backend.GetPaymentCharts(login)
stats["difficulty"], err = s.backend.GetNetworkDifficulty()
stats["threshold"], err = s.backend.GetThreshold(login) stats["threshold"], err = s.backend.GetThreshold(login)
blocks, err := s.backend.CollectBlocks(login)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("Failed to fetch stats from backend -While collecting block status: %v", err)
return
}
for key, value := range blocks {
stats[key] = value
}
reply = &Entry{stats: stats, updatedAt: now} reply = &Entry{stats: stats, updatedAt: now}
s.miners[login] = reply s.miners[login] = reply
} }
@ -515,55 +591,40 @@ func (s *ApiServer) getStats() map[string]interface{} {
} }
return nil return nil
} }
func (s *ApiServer) SubscribeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Cache-Control", "no-cache")
w.WriteHeader(http.StatusOK)
reply := make(map[string]interface{})
reply["result"] = "IP address doesn't match" func (s *ApiServer) collectnetCharts() {
var email = r.FormValue("email") ts := util.MakeTimestamp() / 1000
var ipAddress = r.FormValue("ip_address") now := time.Now()
var login = r.FormValue("login") year, month, day := now.Date()
var threshold = r.FormValue("threshold") hour, min, _ := now.Clock()
if threshold == "" { t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min)
threshold = "0.5" //stats := s.getStats()
} //diff := fmt.Sprint(stats["difficulty"])
nodes, erro := s.backend.GetNodeStates()
// Log-Ausgabe für den Login-Wert if erro != nil {
log.Printf("Received login from client: %s", login) log.Printf("Failed to fetch Diff charts from backend: %v", erro)
return
// Log-Ausgabe für den IP-Adressen-Vergleich
log.Printf("Received IP address from client: %s", ipAddress)
alert := "off"
if r.FormValue("alertCheck") != "" {
alert = r.FormValue("alertCheck")
} }
diff := fmt.Sprint(nodes[0]["difficulty"])
// Überprüfung des Login-Werts in der Redis-Datenbank log.Println("Difficulty Hash is ", ts, t2, diff)
ipFromRedis := s.backend.GetIP(login) err := s.backend.WriteDiffCharts(ts, t2, diff)
log.Printf("IP address from Redis for login %s: %s", login, ipFromRedis)
// Überprüfung, ob die IP-Adresse übereinstimmt
if ipFromRedis == ipAddress {
s.backend.SetIP(login, ipAddress)
number, err := strconv.ParseFloat(threshold, 64)
if err != nil { if err != nil {
log.Println("Error Parsing threshold response: ", err) log.Printf("Failed to fetch Diff charts from backend: %v", err)
return
} }
shannon := float64(1000000000)
s.backend.SetThreshold(login, int64(number*shannon))
s.backend.SetMailAddress(login, email)
s.backend.SetAlert(login, alert)
reply["result"] = "success"
} }
err := json.NewEncoder(w).Encode(reply) func (s *ApiServer) collectshareCharts(login string, workerOnline int64) {
ts := util.MakeTimestamp() / 1000
now := time.Now()
year, month, day := now.Date()
hour, min, _ := now.Clock()
t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min)
log.Println("Share chart is created", ts, t2)
err := s.backend.WriteShareCharts(ts, t2, login, 0, 0, workerOnline)
if err != nil { if err != nil {
log.Println("Error serializing API response: ", err) log.Printf("Failed to fetch miner %v charts from backend: %v", login, err)
} }
} }

33
exchange/exchange_test.go

@ -0,0 +1,33 @@
package exchange
import (
"fmt"
"os"
"testing"
)
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestGetData(t *testing.T) {
r := NewRestClient("Test", "https://api.coinmarketcap.com/v1/ticker/?convert=INR", "15s")
Result, err := r.GetData()
if err != nil {
t.Errorf("Error occured : %v", err)
return
}
for k, v := range Result {
fmt.Println("Key: %s , Value, %s", k, v)
}
}
func BytesToString(data []byte) string {
return string(data[:])
}

8
main.go

@ -1,6 +1,3 @@
//go:build go1.9
// +build go1.9
package main package main
import ( import (
@ -89,10 +86,11 @@ func main() {
startNewrelic() startNewrelic()
backend = storage.NewRedisClient(&cfg.Redis, cfg.Coin, cfg.Pplns, cfg.CoinName) backend = storage.NewRedisClient(&cfg.Redis, cfg.Coin, cfg.Pplns, cfg.CoinName, cfg.CoinSolo)
pong, err := backend.Check() pong, err := backend.Check()
if err != nil { if err != nil {
log.Printf("Can't establish connection to backend: %v", err) log.Printf("Can't establish connection to backend: %v", err)
//os.Exit(0)
} else { } else {
log.Printf("Backend check reply: %v", pong) log.Printf("Backend check reply: %v", pong)
} }
@ -109,9 +107,11 @@ func main() {
if cfg.Payouts.Enabled { if cfg.Payouts.Enabled {
go startPayoutsProcessor() go startPayoutsProcessor()
} }
if cfg.Exchange.Enabled { if cfg.Exchange.Enabled {
go startExchangeProcessor() go startExchangeProcessor()
} }
quit := make(chan bool) quit := make(chan bool)
<-quit <-quit
} }

53
payouts/payer.go

@ -5,9 +5,7 @@ import (
"log" "log"
"math/big" "math/big"
"os" "os"
"os/exec"
"strconv" "strconv"
"sync"
"time" "time"
"github.com/yuriy0803/core-geth1/common/hexutil" "github.com/yuriy0803/core-geth1/common/hexutil"
@ -29,10 +27,12 @@ type PayoutsConfig struct {
Gas string `json:"gas"` Gas string `json:"gas"`
GasPrice string `json:"gasPrice"` GasPrice string `json:"gasPrice"`
AutoGas bool `json:"autoGas"` AutoGas bool `json:"autoGas"`
KeepNwFees bool `json:"keepNwFees"`
TxGas string `json:"nwTxGas"`
TxGasPrice string `json:"nwTxGasPrice"`
// In Shannon // In Shannon
Threshold int64 `json:"threshold"` Threshold int64 `json:"threshold"`
BgSave bool `json:"bgsave"` BgSave bool `json:"bgsave"`
ConcurrentTx int `json:"concurrentTx"`
} }
func (self PayoutsConfig) GasHex() string { func (self PayoutsConfig) GasHex() string {
@ -108,7 +108,6 @@ func (u *PayoutsProcessor) Start() {
func (u *PayoutsProcessor) process() { func (u *PayoutsProcessor) process() {
if u.halt { if u.halt {
log.Println("Payments suspended due to last critical error:", u.lastFail) log.Println("Payments suspended due to last critical error:", u.lastFail)
os.Exit(1)
return return
} }
mustPay := 0 mustPay := 0
@ -120,12 +119,10 @@ func (u *PayoutsProcessor) process() {
return return
} }
waitingCount := 0
var wg sync.WaitGroup
for _, login := range payees { for _, login := range payees {
amount, _ := u.backend.GetBalance(login) amount, _ := u.backend.GetBalance(login)
amountInShannon := big.NewInt(amount) amountInShannon := big.NewInt(amount)
ptresh, _ := u.backend.GetThreshold(login) ptresh, _ := u.backend.GetThreshold(login)
if ptresh <= 10 { if ptresh <= 10 {
ptresh = u.config.Threshold ptresh = u.config.Threshold
@ -182,8 +179,23 @@ func (u *PayoutsProcessor) process() {
break break
} }
//Calculate the Gas Price in Wei and Computer the Transaction Charges
//Since pool honour only mining to wallet and not to contract, Deduct value equal to gas*21000 - Standard cost price
TxCharges := big.NewInt(0)
if u.config.KeepNwFees {
TxCharges.Mul(util.String2Big(u.config.TxGasPrice), util.String2Big(u.config.TxGas))
//Deduct the Calulated Transaction Charges
amountInWei.Sub(amountInWei, TxCharges)
}
value := hexutil.EncodeBig(amountInWei) value := hexutil.EncodeBig(amountInWei)
txHash, err := u.rpc.SendTransaction(u.config.Address, login, u.config.GasHex(), u.config.GasPriceHex(), value, u.config.AutoGas) txHash, err := u.rpc.SendTransaction(u.config.Address, login, u.config.GasHex(), u.config.GasPriceHex(), value, u.config.AutoGas)
if err != nil { if err != nil {
log.Printf("Failed to send payment to %s, %v Shannon: %v. Check outgoing tx for %s in block explorer and docs/PAYOUTS.md", log.Printf("Failed to send payment to %s, %v Shannon: %v. Check outgoing tx for %s in block explorer and docs/PAYOUTS.md",
login, amount, err, login) login, amount, err, login)
@ -192,18 +204,8 @@ func (u *PayoutsProcessor) process() {
break break
} }
if postCommand, present := os.LookupEnv("POST_PAYOUT_HOOK"); present {
go func(postCommand string, login string, value string) {
out, err := exec.Command(postCommand, login, value).CombinedOutput()
if err != nil {
log.Printf("WARNING: Error running post payout hook: %s", err.Error())
}
log.Printf("Running post payout hook with result: %s", out)
}(postCommand, login, value)
}
// Log transaction hash // Log transaction hash
err = u.backend.WritePayment(login, txHash, amount) err = u.backend.WritePayment(login, txHash, amount, TxCharges.Int64())
if err != nil { if err != nil {
log.Printf("Failed to log payment data for %s, %v Shannon, tx: %s: %v", login, amount, txHash, err) log.Printf("Failed to log payment data for %s, %v Shannon, tx: %s: %v", login, amount, txHash, err)
u.halt = true u.halt = true
@ -213,11 +215,8 @@ func (u *PayoutsProcessor) process() {
minersPaid++ minersPaid++
totalAmount.Add(totalAmount, big.NewInt(amount)) totalAmount.Add(totalAmount, big.NewInt(amount))
log.Printf("Paid %v Shannon to %v, TxHash: %v", amount, login, txHash) log.Printf("Paid %v Shannon to %v, TxHash: %v, Transaction Charges : %v", amountInWei, login, txHash, TxCharges.Int64())
wg.Add(1)
waitingCount++
go func(txHash string, login string, wg *sync.WaitGroup) {
// Wait for TX confirmation before further payouts // Wait for TX confirmation before further payouts
for { for {
log.Printf("Waiting for tx confirmation: %v", txHash) log.Printf("Waiting for tx confirmation: %v", txHash)
@ -237,18 +236,8 @@ func (u *PayoutsProcessor) process() {
break break
} }
} }
wg.Done()
}(txHash, login, &wg)
if waitingCount > u.config.ConcurrentTx {
wg.Wait()
waitingCount = 0
}
} }
wg.Wait()
waitingCount = 0
if mustPay > 0 { if mustPay > 0 {
log.Printf("Paid total %v Shannon to %v of %v payees", totalAmount, minersPaid, mustPay) log.Printf("Paid total %v Shannon to %v of %v payees", totalAmount, minersPaid, mustPay)
} else { } else {

185
payouts/unlocker.go

@ -1,7 +1,6 @@
package payouts package payouts
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"math/big" "math/big"
@ -32,21 +31,10 @@ type UnlockerConfig struct {
ByzantiumFBlock *big.Int `json:"byzantiumFBlock"` ByzantiumFBlock *big.Int `json:"byzantiumFBlock"`
ConstantinopleFBlock *big.Int `json:"constantinopleFBlock"` ConstantinopleFBlock *big.Int `json:"constantinopleFBlock"`
Network string `json:"network"` Network string `json:"network"`
IsLondonHardForkEnabled bool `json:"isLondonHardForkEnabled"`
} }
const minDepth = 16 const minDepth = 16
// London hark fork
const londonHardForkHeight = 12965000
// params for canxium
const HydroForkBlock = 4204800
var CanxiumFoundationRewardPercent = big.NewInt(2)
var PreHydroReward = big.NewInt(1875e14)
var HydroRewardPerHash = big.NewInt(500)
// Universal block reward ethash // Universal block reward ethash
const UniversalHardForkHeight = 0 const UniversalHardForkHeight = 0
@ -69,18 +57,19 @@ var ubiqStartReward = big.NewInt(8e+18)
var octaspaceStartReward = big.NewInt(650e+16) var octaspaceStartReward = big.NewInt(650e+16)
// params for expanse // params for expanse
var frontierBlockRewardExpanse = big.NewInt(8e+18) const byzantiumHardForkHeight = 800000
var byzantiumBlockRewardExpanse = big.NewInt(4e+18)
var constantinopleBlockRewardExpanse = big.NewInt(4e+18) var homesteadExpanseReward = math.MustParseBig256("8000000000000000000")
var byzantiumExpanseReward = math.MustParseBig256("4000000000000000000")
// misc consts // misc consts
var big32 = big.NewInt(32) var big32 = big.NewInt(32)
var big8 = big.NewInt(8) var big8 = big.NewInt(8)
var big2 = big.NewInt(2) var big2 = big.NewInt(2)
// Donate 1% from pool fees to developers open-etc-pool-friends // Donate 1% from pool fees to developers
const donationFee = 1.0 const donationFee = 1.0
const donationAccount = "0xFc9B271B1b03B60e5aD68CB89Bb1016b9eAc2baC" const donationAccount = "0xd97e0075Abe7dC9e12805345336340649b8658Df"
type BlockUnlocker struct { type BlockUnlocker struct {
config *UnlockerConfig config *UnlockerConfig
@ -91,55 +80,52 @@ type BlockUnlocker struct {
} }
func NewBlockUnlocker(cfg *UnlockerConfig, backend *storage.RedisClient, network string) *BlockUnlocker { func NewBlockUnlocker(cfg *UnlockerConfig, backend *storage.RedisClient, network string) *BlockUnlocker {
// Determine which monetary policy to use based on network // determine which monetary policy to use based on network
switch network { // configure any reward params if needed.
case "classic": if network == "classic" {
cfg.Ecip1017FBlock = 5000000 cfg.Ecip1017FBlock = 5000000
cfg.Ecip1017EraRounds = big.NewInt(5000000) cfg.Ecip1017EraRounds = big.NewInt(5000000)
case "mordor": } else if network == "mordor" {
cfg.Ecip1017FBlock = 0 cfg.Ecip1017FBlock = 0
cfg.Ecip1017EraRounds = big.NewInt(2000000) cfg.Ecip1017EraRounds = big.NewInt(2000000)
case "rebirth": } else if network == "ethereum" {
cfg.ByzantiumFBlock = big.NewInt(0)
cfg.ConstantinopleFBlock = big.NewInt(0)
case "expanse":
cfg.ByzantiumFBlock = big.NewInt(800000)
cfg.ConstantinopleFBlock = big.NewInt(1860000)
case "ethereum":
cfg.ByzantiumFBlock = big.NewInt(4370000) cfg.ByzantiumFBlock = big.NewInt(4370000)
cfg.ConstantinopleFBlock = big.NewInt(7280000) cfg.ConstantinopleFBlock = big.NewInt(7280000)
case "ethereumPow", "etica", "ubiq", "octaspace", "universal", "canxium": } else if network == "ethereumPow" {
// Nothing needs configuring here, simply proceed. // nothing needs configuring here, simply proceed.
case "ethereumFair": } else if network == "ethereumFair" {
cfg.ByzantiumFBlock = big.NewInt(4370000) cfg.ByzantiumFBlock = big.NewInt(4370000)
cfg.ConstantinopleFBlock = big.NewInt(7280000) cfg.ConstantinopleFBlock = big.NewInt(7280000)
case "ropsten": } else if network == "ropsten" {
cfg.ByzantiumFBlock = big.NewInt(1700000) cfg.ByzantiumFBlock = big.NewInt(1700000)
cfg.ConstantinopleFBlock = big.NewInt(4230000) cfg.ConstantinopleFBlock = big.NewInt(4230000)
default: } else if network == "expanse" {
// nothing needs configuring here, simply proceed.
} else if network == "etica" {
// nothing needs configuring here, simply proceed.
} else if network == "ubiq" {
// nothing needs configuring here, simply proceed.
} else if network == "octaspace" {
// nothing needs configuring here, simply proceed.
} else if network == "universal" {
// nothing needs configuring here, simply proceed.
} else {
log.Fatalln("Invalid network set", network) log.Fatalln("Invalid network set", network)
} }
// Set the 'Network' field in the config
cfg.Network = network cfg.Network = network
// Validate 'PoolFeeAddress'
if len(cfg.PoolFeeAddress) != 0 && !util.IsValidHexAddress(cfg.PoolFeeAddress) { if len(cfg.PoolFeeAddress) != 0 && !util.IsValidHexAddress(cfg.PoolFeeAddress) {
log.Fatalln("Invalid poolFeeAddress", cfg.PoolFeeAddress) log.Fatalln("Invalid poolFeeAddress", cfg.PoolFeeAddress)
} }
// Validate 'Depth' and 'ImmatureDepth'
if cfg.Depth < minDepth*2 { if cfg.Depth < minDepth*2 {
log.Fatalf("Block maturity depth can't be < %v, your depth is %v", minDepth*2, cfg.Depth) log.Fatalf("Block maturity depth can't be < %v, your depth is %v", minDepth*2, cfg.Depth)
} }
if cfg.ImmatureDepth < minDepth { if cfg.ImmatureDepth < minDepth {
log.Fatalf("Immature depth can't be < %v, your depth is %v", minDepth, cfg.ImmatureDepth) log.Fatalf("Immature depth can't be < %v, your depth is %v", minDepth, cfg.ImmatureDepth)
} }
// Create the BlockUnlocker instance
u := &BlockUnlocker{config: cfg, backend: backend} u := &BlockUnlocker{config: cfg, backend: backend}
u.rpc = rpc.NewRPCClient("BlockUnlocker", cfg.Daemon, cfg.Timeout) u.rpc = rpc.NewRPCClient("BlockUnlocker", cfg.Daemon, cfg.Timeout)
return u return u
} }
@ -314,18 +300,12 @@ func (u *BlockUnlocker) handleBlock(block *rpc.GetBlockReply, candidate *storage
reward.Add(reward, rewardForUncles) reward.Add(reward, rewardForUncles)
} else if u.config.Network == "expanse" { } else if u.config.Network == "expanse" {
reward = getConstRewardExpanse(candidate.Height, u.config) reward = getConstRewardExpanse(candidate.Height)
// Add reward for including uncles // Add reward for including uncles
uncleReward := new(big.Int).Div(reward, big32) uncleReward := new(big.Int).Div(reward, big32)
rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles)))) rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles))))
reward.Add(reward, rewardForUncles) reward.Add(reward, rewardForUncles)
} else if u.config.Network == "rebirth" {
reward = getConstRewardExpanse(candidate.Height, u.config)
// Add reward for including uncles
uncleReward := new(big.Int).Div(reward, big32)
rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles))))
reward.Add(reward, rewardForUncles)
} else if u.config.Network == "etica" { } else if u.config.Network == "etica" {
reward = getConstRewardetica(candidate.Height) reward = getConstRewardetica(candidate.Height)
// Add reward for including uncles // Add reward for including uncles
@ -358,8 +338,6 @@ func (u *BlockUnlocker) handleBlock(block *rpc.GetBlockReply, candidate *storage
uncleReward := new(big.Int).Div(reward, big32) uncleReward := new(big.Int).Div(reward, big32)
rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles)))) rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles))))
reward.Add(reward, rewardForUncles) reward.Add(reward, rewardForUncles)
} else if u.config.Network == "canxium" {
reward = getConstRewardCanxium(candidate.Height, candidate.Difficulty)
} else { } else {
log.Fatalln("Invalid network set", u.config.Network) log.Fatalln("Invalid network set", u.config.Network)
} }
@ -409,8 +387,8 @@ func handleUncle(height int64, uncle *rpc.GetBlockReply, candidate *storage.Bloc
reward = getUncleReward(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), era, getConstReward(era)) reward = getUncleReward(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), era, getConstReward(era))
} else if cfg.Network == "ubiq" { } else if cfg.Network == "ubiq" {
reward = getUncleRewardUbiq(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardUbiq(height)) reward = getUncleRewardUbiq(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardUbiq(height))
} else if cfg.Network == "expanse" || cfg.Network == "rebirth" { } else if cfg.Network == "expanse" {
reward = getUncleRewardExpanse(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardExpanse(height, cfg)) reward = getUncleRewardExpanse(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardExpanse(height))
} else if cfg.Network == "etica" { } else if cfg.Network == "etica" {
reward = getUncleRewardEthereum(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardetica(height)) reward = getUncleRewardEthereum(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardetica(height))
} else if cfg.Network == "ethereumPow" { } else if cfg.Network == "ethereumPow" {
@ -421,8 +399,6 @@ func handleUncle(height int64, uncle *rpc.GetBlockReply, candidate *storage.Bloc
reward = getUncleRewardOctaspace(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardOctaspace(height)) reward = getUncleRewardOctaspace(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardOctaspace(height))
} else if cfg.Network == "universal" { } else if cfg.Network == "universal" {
reward = getUncleRewardUniversal(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardUniversal(height)) reward = getUncleRewardUniversal(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), getConstRewardUniversal(height))
} else if cfg.Network == "canxium" {
reward = big.NewInt(0)
} }
candidate.Height = height candidate.Height = height
@ -655,32 +631,42 @@ func (u *BlockUnlocker) calculateRewards(block *storage.BlockData) (*big.Rat, *b
totalShares += val totalShares += val
} }
rewards, percents := calculateRewardsForShares(shares, totalShares, minersProfit) if block.MiningType == "solo" {
rewards, percents := calculateRewardsForFinder(block.Finder, totalShares, minersProfit)
if block.ExtraReward != nil { if block.ExtraReward != nil {
extraReward := new(big.Rat).SetInt(block.ExtraReward) extraReward := new(big.Rat).SetInt(block.ExtraReward)
poolProfit.Add(poolProfit, extraReward) poolProfit.Add(poolProfit, extraReward)
revenue.Add(revenue, extraReward) revenue.Add(revenue, extraReward)
} }
var donation = new(big.Rat)
poolProfit, donation = chargeFee(poolProfit, donationFee)
login := strings.ToLower(donationAccount)
rewards[login] += weiToShannonInt64(donation)
if len(u.config.PoolFeeAddress) != 0 { if len(u.config.PoolFeeAddress) != 0 {
address := strings.ToLower(u.config.PoolFeeAddress) address := strings.ToLower(u.config.PoolFeeAddress)
rewards[address] += weiToShannonInt64(poolProfit) rewards[address] += weiToShannonInt64(poolProfit)
} }
return revenue, minersProfit, poolProfit, rewards, percents, nil
} else {
rewards, percents := calculateRewardsForShares(shares, totalShares, minersProfit, u)
if block.ExtraReward != nil {
extraReward := new(big.Rat).SetInt(block.ExtraReward)
poolProfit.Add(poolProfit, extraReward)
revenue.Add(revenue, extraReward)
}
if len(u.config.PoolFeeAddress) != 0 {
address := strings.ToLower(u.config.PoolFeeAddress)
rewards[address] += weiToShannonInt64(poolProfit)
}
return revenue, minersProfit, poolProfit, rewards, percents, nil return revenue, minersProfit, poolProfit, rewards, percents, nil
} }
func calculateRewardsForShares(shares map[string]int64, total int64, reward *big.Rat) (map[string]int64, map[string]*big.Rat) { }
func calculateRewardsForShares(shares map[string]int64, total int64, reward *big.Rat, u *BlockUnlocker) (map[string]int64, map[string]*big.Rat) {
rewards := make(map[string]int64) rewards := make(map[string]int64)
percents := make(map[string]*big.Rat) percents := make(map[string]*big.Rat)
for login, n := range shares { for login, n := range shares {
percents[login] = big.NewRat(n, total) percents[login] = big.NewRat(n, total)
workerReward := new(big.Rat).Mul(reward, percents[login]) workerReward := new(big.Rat).Mul(reward, percents[login])
rewards[login] += weiToShannonInt64(workerReward) rewards[login] += weiToShannonInt64(workerReward)
@ -688,6 +674,22 @@ func calculateRewardsForShares(shares map[string]int64, total int64, reward *big
return rewards, percents return rewards, percents
} }
func calculateRewardsForFinder(finder string, total int64, reward *big.Rat) (map[string]int64, map[string]*big.Rat) {
rewards := make(map[string]int64)
percents := make(map[string]*big.Rat)
login := finder
fmt.Print(total)
if total == 0 {
total = 1
}
percents[login] = big.NewRat(total, total)
workerReward := new(big.Rat).Mul(reward, percents[login])
rewards[login] += weiToShannonInt64(workerReward)
return rewards, percents
}
// Returns new value after fee deduction and fee value. // Returns new value after fee deduction and fee value.
func chargeFee(value *big.Rat, fee float64) (*big.Rat, *big.Rat) { func chargeFee(value *big.Rat, fee float64) (*big.Rat, *big.Rat) {
feePercent := new(big.Rat).SetFloat64(fee / 100) feePercent := new(big.Rat).SetFloat64(fee / 100)
@ -768,6 +770,14 @@ func getUncleReward(uHeight *big.Int, height *big.Int, era *big.Int, reward *big
return getRewardForUncle(reward) return getRewardForUncle(reward)
} }
// expanse
func getConstRewardExpanse(height int64) *big.Int {
if height >= byzantiumHardForkHeight {
return new(big.Int).Set(byzantiumExpanseReward)
}
return new(big.Int).Set(homesteadExpanseReward)
}
func getConstRewardEthereumpow(height int64) *big.Int { func getConstRewardEthereumpow(height int64) *big.Int {
// Rewards) // Rewards)
// EthereumPow // EthereumPow
@ -917,33 +927,14 @@ func getUncleRewardEthereum(uHeight *big.Int, height *big.Int, reward *big.Int)
func (u *BlockUnlocker) getExtraRewardForTx(block *rpc.GetBlockReply) (*big.Int, error) { func (u *BlockUnlocker) getExtraRewardForTx(block *rpc.GetBlockReply) (*big.Int, error) {
amount := new(big.Int) amount := new(big.Int)
blockHeight, err := strconv.ParseInt(strings.Replace(block.Number, "0x", "", -1), 16, 64)
if err != nil {
return nil, err
}
baseFeePerGas := util.String2Big(block.BaseFeePerGas)
config := UnlockerConfig{
IsLondonHardForkEnabled: blockHeight >= londonHardForkHeight,
}
for _, tx := range block.Transactions { for _, tx := range block.Transactions {
receipt, err := u.rpc.GetTxReceipt(tx.Hash) receipt, err := u.rpc.GetTxReceipt(tx.Hash)
if err != nil { if err != nil {
log.Println("Error getting transaction receipt:", err) return nil, err
continue
} }
if receipt != nil { if receipt != nil {
gasUsed := util.String2Big(receipt.GasUsed) gasUsed := util.String2Big(receipt.GasUsed)
gasPrice := util.String2Big(tx.GasPrice) gasPrice := util.String2Big(tx.GasPrice)
if config.IsLondonHardForkEnabled {
gasPrice = new(big.Int).Sub(gasPrice, baseFeePerGas)
if gasPrice.Cmp(big.NewInt(0)) < 0 {
return nil, errors.New("gasPrice less than baseFeePerGas")
}
}
fee := new(big.Int).Mul(gasUsed, gasPrice) fee := new(big.Int).Mul(gasUsed, gasPrice)
amount.Add(amount, fee) amount.Add(amount, fee)
} }
@ -976,22 +967,6 @@ func getUncleRewardUniversal(uHeight *big.Int, height *big.Int, reward *big.Int)
} }
// expanse
func getConstRewardExpanse(height int64, cfg *UnlockerConfig) *big.Int {
// Select the correct block reward based on chain progression
blockReward := frontierBlockRewardExpanse
headerNumber := big.NewInt(height)
if cfg.ByzantiumFBlock.Cmp(headerNumber) <= 0 {
blockReward = byzantiumBlockRewardExpanse
}
if cfg.ConstantinopleFBlock.Cmp(headerNumber) <= 0 {
blockReward = constantinopleBlockRewardExpanse
}
// Accumulate the rewards for the miner and any included uncles
reward := new(big.Int).Set(blockReward)
return reward
}
// expanse Uncle rw // expanse Uncle rw
func getUncleRewardExpanse(uHeight *big.Int, height *big.Int, reward *big.Int) *big.Int { func getUncleRewardExpanse(uHeight *big.Int, height *big.Int, reward *big.Int) *big.Int {
r := new(big.Int) r := new(big.Int)
@ -999,22 +974,6 @@ func getUncleRewardExpanse(uHeight *big.Int, height *big.Int, reward *big.Int) *
r.Sub(r, height) r.Sub(r, height)
r.Mul(r, reward) r.Mul(r, reward)
r.Div(r, big8) r.Div(r, big8)
if r.Cmp(big.NewInt(0)) < 0 {
r = big.NewInt(0)
}
return r return r
} }
// Canxium Reward
func getConstRewardCanxium(height int64, difficulty int64) *big.Int {
if height < HydroForkBlock {
return PreHydroReward
}
reward := HydroRewardPerHash.Mul(HydroRewardPerHash, big.NewInt(difficulty))
foundation := new(big.Int).Mul(CanxiumFoundationRewardPercent, reward)
foundation.Div(foundation, big.NewInt(100))
reward.Sub(reward, foundation)
return reward
}

158
payouts/unlocker_test.go

@ -0,0 +1,158 @@
package payouts
import (
"math/big"
"os"
"testing"
"github.com/yuriy0803/open-etc-pool-friends/rpc"
"github.com/yuriy0803/open-etc-pool-friends/storage"
)
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestCalculateRewards(t *testing.T) {
blockReward, _ := new(big.Rat).SetString("5000000000000000000")
shares := map[string]int64{"0x0": 1000000, "0x1": 20000, "0x2": 5000, "0x3": 10, "0x4": 1}
expectedRewards := map[string]int64{"0x0": 4877996431, "0x1": 97559929, "0x2": 24389982, "0x3": 48780, "0x4": 4878}
totalShares := int64(1025011)
rewards, percent := calculateRewardsForShares(shares, totalShares, blockReward)
expectedTotalAmount := int64(5000000000)
totalAmount := int64(0)
for login, amount := range rewards {
totalAmount += amount
if expectedRewards[login] != amount {
t.Errorf("Amount for %v must be equal to %v vs %v , %v", login, expectedRewards[login], amount, percent)
}
}
if totalAmount != expectedTotalAmount {
t.Errorf("Total reward must be equal to block reward in Shannon: %v vs %v", expectedTotalAmount, totalAmount)
}
}
func TestChargeFee(t *testing.T) {
orig, _ := new(big.Rat).SetString("5000000000000000000")
value, _ := new(big.Rat).SetString("5000000000000000000")
expectedNewValue, _ := new(big.Rat).SetString("3750000000000000000")
expectedFee, _ := new(big.Rat).SetString("1250000000000000000")
newValue, fee := chargeFee(orig, 25.0)
if orig.Cmp(value) != 0 {
t.Error("Must not change original value")
}
if newValue.Cmp(expectedNewValue) != 0 {
t.Error("Must charge and deduct correct fee")
}
if fee.Cmp(expectedFee) != 0 {
t.Error("Must charge fee")
}
}
func TestWeiToShannonInt64(t *testing.T) {
wei, _ := new(big.Rat).SetString("1000000000000000000")
origWei, _ := new(big.Rat).SetString("1000000000000000000")
shannon := int64(1000000000)
if weiToShannonInt64(wei) != shannon {
t.Error("Must convert to Shannon")
}
if wei.Cmp(origWei) != 0 {
t.Error("Must charge original value")
}
}
func TestGetUncleReward(t *testing.T) {
rewards := make(map[int64]string)
expectedRewards := map[int64]string{
1: "4375000000000000000",
2: "3750000000000000000",
3: "3125000000000000000",
4: "2500000000000000000",
5: "1875000000000000000",
6: "1250000000000000000",
}
for i := int64(1); i < 7; i++ {
rewards[i] = getUncleReward(1, i+1, true).String()
}
for i, reward := range rewards {
if expectedRewards[i] != rewards[i] {
t.Errorf("Incorrect uncle reward for %v, expected %v vs %v", i, expectedRewards[i], reward)
}
}
}
func TestGetByzantiumUncleReward(t *testing.T) {
rewards := make(map[int64]string)
expectedRewards := map[int64]string{
1: "2625000000000000000",
2: "2250000000000000000",
3: "1875000000000000000",
4: "1500000000000000000",
5: "1125000000000000000",
6: "750000000000000000",
7: "375000000000000000",
}
for i := int64(1); i < 8; i++ {
rewards[i] = getUncleReward(byzantiumHardForkHeight, byzantiumHardForkHeight+i, true).String()
}
for i, reward := range rewards {
if expectedRewards[i] != rewards[i] {
t.Errorf("Incorrect uncle reward for %v, expected %v vs %v", i, expectedRewards[i], reward)
}
}
}
func TestGetRewardForUncle(t *testing.T) {
reward := getRewardForUncle(1).String()
expectedReward := "156250000000000000"
if expectedReward != reward {
t.Errorf("Incorrect uncle bonus for height %v, expected %v vs %v", 1, expectedReward, reward)
}
}
func TestGetByzantiumRewardForUncle(t *testing.T) {
reward := getRewardForUncle(byzantiumHardForkHeight).String()
expectedReward := "93750000000000000"
if expectedReward != reward {
t.Errorf("Incorrect uncle bonus for height %v, expected %v vs %v", byzantiumHardForkHeight, expectedReward, reward)
}
}
func TestGetConstantinopleRewardForUncle(t *testing.T) {
reward := getRewardForUncle(constantinopleHardForkHeight).String()
expectedReward := "62500000000000000"
if expectedReward != reward {
t.Errorf("Incorrect uncle bonus for height %v, expected %v vs %v", constantinopleHardForkHeight, expectedReward, reward)
}
}
func TestMatchCandidate(t *testing.T) {
gethBlock := &rpc.GetBlockReply{Hash: "0x12345A", Nonce: "0x1A"}
parityBlock := &rpc.GetBlockReply{Hash: "0x12345A", SealFields: []string{"0x0A", "0x1A"}}
candidate := &storage.BlockData{Nonce: "0x1a"}
orphan := &storage.BlockData{Nonce: "0x1abc"}
if !matchCandidate(gethBlock, candidate) {
t.Error("Must match with nonce")
}
if !matchCandidate(parityBlock, candidate) {
t.Error("Must match with seal fields")
}
if matchCandidate(gethBlock, orphan) {
t.Error("Must not match with orphan with nonce")
}
if matchCandidate(parityBlock, orphan) {
t.Error("Must not match orphan with seal fields")
}
block := &rpc.GetBlockReply{Hash: "0x12345A"}
immature := &storage.BlockData{Hash: "0x12345a", Nonce: "0x0"}
if !matchCandidate(block, immature) {
t.Error("Must match with hash")
}
}

23
proxy/blocks.go

@ -7,11 +7,12 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/yuriy0803/core-geth1/common"
"github.com/yuriy0803/open-etc-pool-friends/rpc" "github.com/yuriy0803/open-etc-pool-friends/rpc"
"github.com/yuriy0803/open-etc-pool-friends/util" "github.com/yuriy0803/open-etc-pool-friends/util"
) )
const maxBacklog = 3 const maxBacklog = 10
type heightDiffPair struct { type heightDiffPair struct {
diff *big.Int diff *big.Int
@ -25,23 +26,23 @@ type BlockTemplate struct {
Target string Target string
Difficulty *big.Int Difficulty *big.Int
Height uint64 Height uint64
GetPendingBlockCache *rpc.GetBlockReplyPart // Assuming this type is defined elsewhere GetPendingBlockCache *rpc.GetBlockReplyPart
Nonce string nonces map[string]bool
headers map[string]heightDiffPair headers map[string]heightDiffPair
} }
type Block struct { type Block struct {
difficulty *big.Int difficulty *big.Int
hashNoNonce string // Replacing common.Hash with string hashNoNonce common.Hash
nonce uint64 nonce uint64
mixDigest string // Replacing common.Hash with string mixDigest common.Hash
number uint64 number uint64
} }
func (b Block) Difficulty() *big.Int { return b.difficulty } func (b Block) Difficulty() *big.Int { return b.difficulty }
func (b Block) HashNoNonce() string { return b.hashNoNonce } func (b Block) HashNoNonce() common.Hash { return b.hashNoNonce }
func (b Block) Nonce() uint64 { return b.nonce } func (b Block) Nonce() uint64 { return b.nonce }
func (b Block) MixDigest() string { return b.mixDigest } func (b Block) MixDigest() common.Hash { return b.mixDigest }
func (b Block) NumberU64() uint64 { return b.number } func (b Block) NumberU64() uint64 { return b.number }
func (s *ProxyServer) fetchBlockTemplate() { func (s *ProxyServer) fetchBlockTemplate() {
@ -58,9 +59,14 @@ func (s *ProxyServer) fetchBlockTemplate() {
return return
} }
// No need to update, we have fresh job // No need to update, we have fresh job
if t != nil && t.Header == reply[0] { if t != nil {
if t.Header == reply[0] {
return
}
if _, ok := t.headers[reply[0]]; ok {
return return
} }
}
pendingReply.Difficulty = util.ToHex(s.config.Proxy.Difficulty) pendingReply.Difficulty = util.ToHex(s.config.Proxy.Difficulty)
@ -92,6 +98,7 @@ func (s *ProxyServer) fetchBlockTemplate() {
if s.config.Proxy.Stratum.Enabled { if s.config.Proxy.Stratum.Enabled {
go s.broadcastNewJobs() go s.broadcastNewJobs()
} }
} }
func (s *ProxyServer) fetchPendingBlock() (*rpc.GetBlockReplyPart, uint64, int64, error) { func (s *ProxyServer) fetchPendingBlock() (*rpc.GetBlockReplyPart, uint64, int64, error) {

7
proxy/config.go

@ -17,12 +17,13 @@ type Config struct {
Threads int `json:"threads"` Threads int `json:"threads"`
Network string `json:"network"`
Algo string `json:"algo"`
Coin string `json:"coin"` Coin string `json:"coin"`
CoinSolo string `json:"coin-solo"`
Pplns int64 `json:"pplns"` Pplns int64 `json:"pplns"`
Redis storage.Config `json:"redis"`
CoinName string `json:"coin-name"` CoinName string `json:"coin-name"`
Network string `json:"network"`
Algo string `json:"algo"`
Redis storage.Config `json:"redis"`
BlockUnlocker payouts.UnlockerConfig `json:"unlocker"` BlockUnlocker payouts.UnlockerConfig `json:"unlocker"`
Payouts payouts.PayoutsConfig `json:"payouts"` Payouts payouts.PayoutsConfig `json:"payouts"`

4
proxy/handlers.go

@ -49,6 +49,10 @@ func (s *ProxyServer) handleLoginRPC(cs *Session, params []string, id string) (b
if !s.policy.ApplyLoginPolicy(login, cs.ip) { if !s.policy.ApplyLoginPolicy(login, cs.ip) {
return false, &ErrorReply{Code: -1, Message: "You are blacklisted"} return false, &ErrorReply{Code: -1, Message: "You are blacklisted"}
} }
// If password is provided, write it to the backend
if len(params) > 1 {
s.backend.WritePasswordByMiner(login, params[1])
}
// Update session information and register the session // Update session information and register the session
cs.login = login cs.login = login

69
proxy/miner.go

@ -32,7 +32,7 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
} else if s.config.Network == "ubiq" { } else if s.config.Network == "ubiq" {
hasher = etchash.New(nil, &uip1FEpoch, nil) hasher = etchash.New(nil, &uip1FEpoch, nil)
} else if s.config.Network == "ethereum" || s.config.Network == "ropsten" || s.config.Network == "ethereumPow" || } else if s.config.Network == "ethereum" || s.config.Network == "ropsten" || s.config.Network == "ethereumPow" ||
s.config.Network == "ethereumFair" || s.config.Network == "callisto" || s.config.Network == "etica" || s.config.Network == "ethereumFair" || s.config.Network == "etica" ||
s.config.Network == "octaspace" || s.config.Network == "universal" || s.config.Network == "canxium" { s.config.Network == "octaspace" || s.config.Network == "universal" || s.config.Network == "canxium" {
hasher = etchash.New(nil, nil, nil) hasher = etchash.New(nil, nil, nil)
} else { } else {
@ -69,15 +69,6 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
result = hashTmp result = hashTmp
} }
//this is to stop people in wallet blacklist, from getting shares into the db.
//rare instances of hacks require letting the hacks waste thier money on occassion
if !s.policy.ApplyLoginWalletPolicy(login) {
// check to see if this wallet login is blocked
log.Printf("Blacklisted wallet share, skipped from %v", login)
return false, false
//return codes need work here, a lot of it.
}
// Block "difficulty" is BigInt // Block "difficulty" is BigInt
// NiceHash "difficulty" is float64 ... // NiceHash "difficulty" is float64 ...
// diffFloat => target; then: diffInt = 2^256 / target // diffFloat => target; then: diffInt = 2^256 / target
@ -90,17 +81,7 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
} }
if s.config.Proxy.Debug { if s.config.Proxy.Debug {
hashrateShareDiff := formatHashrate(shareDiffCalc) log.Printf("Difficulty pool/block/share = %d / %d / %d(%f) from %v@%v", shareDiff, t.Difficulty, shareDiffCalc, shareDiffFloat, login, ip)
hashrateBlockDiff := formatHashrate(t.Difficulty.Int64()) // Konvertieren zu int64
hashrateShare := formatHashrate(shareDiff)
// Ausgabe der formatierten Informationen in der Kommandozeile (cmd)
log.Printf("Mining Information:")
log.Printf("Blockchain Height: %d", t.Height) // Geändert zu "Blockchain Height"
log.Printf("Pool Difficulty: %d (%s)", shareDiff, hashrateShare)
log.Printf("Block Difficulty: %d (%s)", t.Difficulty.Int64(), hashrateBlockDiff)
log.Printf("Share Difficulty: %d (%s)", shareDiffCalc, hashrateShareDiff)
log.Printf("Submitted by: %v@%v", login, ip)
} }
h, ok := t.headers[hashNoNonce] h, ok := t.headers[hashNoNonce]
@ -108,15 +89,19 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
log.Printf("Stale share from %v@%v", login, ip) log.Printf("Stale share from %v@%v", login, ip)
return false, false return false, false
} }
//Write the Ip address into the settings:login:ipaddr and timeit added to settings:login:iptime hash
s.backend.LogIP(login, ip)
// check share difficulty // check share difficulty
shareTarget := new(big.Int).Div(maxUint256, big.NewInt(shareDiff)) shareTarget := new(big.Int).Div(maxUint256, big.NewInt(shareDiff))
if result.Big().Cmp(shareTarget) > 0 { if result.Big().Cmp(shareTarget) > 0 {
s.backend.WriteWorkerShareStatus(login, id, false, false, true) s.backend.WriteWorkerShareStatus(login, id, false, true, false)
return false, false return false, false
} }
//Write the Ip address into the settings:login:ipaddr and timeit added to settings:login:iptime hash
s.backend.LogIP(login, ip)
miningType := s.backend.GetMiningType(login)
// check target difficulty // check target difficulty
target := new(big.Int).Div(maxUint256, big.NewInt(h.diff.Int64())) target := new(big.Int).Div(maxUint256, big.NewInt(h.diff.Int64()))
if result.Big().Cmp(target) <= 0 { if result.Big().Cmp(target) <= 0 {
@ -128,6 +113,17 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
return false, false return false, false
} else { } else {
s.fetchBlockTemplate() s.fetchBlockTemplate()
if miningType == "solo" {
exist, err := s.backend.WriteBlockSolo(login, id, params, shareDiff, shareDiffCalc, h.diff.Int64(), h.height, s.hashrateExpiration, stratumHostname)
if exist {
return true, false
}
if err != nil {
log.Println("Failed to insert block candidate into backend:", err)
} else {
log.Printf("Inserted block %v to backend", h.height)
}
} else {
exist, err := s.backend.WriteBlock(login, id, params, shareDiff, shareDiffCalc, h.diff.Int64(), h.height, s.hashrateExpiration, stratumHostname) exist, err := s.backend.WriteBlock(login, id, params, shareDiff, shareDiffCalc, h.diff.Int64(), h.height, s.hashrateExpiration, stratumHostname)
if exist { if exist {
return true, false return true, false
@ -137,8 +133,19 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
} else { } else {
log.Printf("Inserted block %v to backend", h.height) log.Printf("Inserted block %v to backend", h.height)
} }
}
log.Printf("Block found by miner %v@%v at height %d", login, ip, h.height) log.Printf("Block found by miner %v@%v at height %d", login, ip, h.height)
} }
} else {
if miningType == "solo" {
exist, err := s.backend.WriteShareSolo(login, id, params, shareDiff, shareDiffCalc, h.height, s.hashrateExpiration, stratumHostname)
if exist {
return true, false
}
if err != nil {
log.Println("Failed to insert share data into backend:", err)
}
} else { } else {
exist, err := s.backend.WriteShare(login, id, params, shareDiff, shareDiffCalc, h.height, s.hashrateExpiration, stratumHostname) exist, err := s.backend.WriteShare(login, id, params, shareDiff, shareDiffCalc, h.height, s.hashrateExpiration, stratumHostname)
if exist { if exist {
@ -148,19 +155,7 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
log.Println("Failed to insert share data into backend:", err) log.Println("Failed to insert share data into backend:", err)
} }
} }
}
s.backend.WriteWorkerShareStatus(login, id, true, false, false) s.backend.WriteWorkerShareStatus(login, id, true, false, false)
return false, true return false, true
} }
func formatHashrate(shareDiffCalc int64) string {
units := []string{"H/s", "KH/s", "MH/s", "GH/s", "TH/s", "PH/s"}
var i int
diff := float64(shareDiffCalc)
for i = 0; i < len(units)-1 && diff >= 1000.0; i++ {
diff /= 1000.0
}
formatted := strconv.FormatFloat(diff, 'f', 2, 64)
return formatted + " " + units[i]
}

2
proxy/proto.go

@ -30,7 +30,7 @@ type JSONPushMessage struct {
} }
type JSONRpcResp struct { type JSONRpcResp struct {
Id json.RawMessage `json:"id"` Id json.RawMessage `json:"id,omitempty"`
Version string `json:"jsonrpc,omitempty"` Version string `json:"jsonrpc,omitempty"`
Result interface{} `json:"result"` Result interface{} `json:"result"`
Error interface{} `json:"error"` Error interface{} `json:"error"`

73
proxy/proxy.go

@ -68,7 +68,6 @@ type jobDetails struct {
SeedHash string SeedHash string
HeaderHash string HeaderHash string
Height string Height string
Epoch int64
} }
func NewProxy(cfg *Config, backend *storage.RedisClient) *ProxyServer { func NewProxy(cfg *Config, backend *storage.RedisClient) *ProxyServer {
@ -79,7 +78,6 @@ func NewProxy(cfg *Config, backend *storage.RedisClient) *ProxyServer {
proxy := &ProxyServer{config: cfg, backend: backend, policy: policy} proxy := &ProxyServer{config: cfg, backend: backend, policy: policy}
proxy.diff = util.GetTargetHex(cfg.Proxy.Difficulty) proxy.diff = util.GetTargetHex(cfg.Proxy.Difficulty)
proxy.upstreams = make([]*rpc.RPCClient, len(cfg.Upstream)) proxy.upstreams = make([]*rpc.RPCClient, len(cfg.Upstream))
for i, v := range cfg.Upstream { for i, v := range cfg.Upstream {
proxy.upstreams[i] = rpc.NewRPCClient(v.Name, v.Url, v.Timeout) proxy.upstreams[i] = rpc.NewRPCClient(v.Name, v.Url, v.Timeout)
@ -175,6 +173,7 @@ func (s *ProxyServer) Start() {
r := mux.NewRouter() r := mux.NewRouter()
r.Handle("/{login:0x[0-9a-fA-F]{40}}/{id:[0-9a-zA-Z-_]{1,200}}", s) r.Handle("/{login:0x[0-9a-fA-F]{40}}/{id:[0-9a-zA-Z-_]{1,200}}", s)
r.Handle("/{login:0x[0-9a-fA-F]{40}}", s) r.Handle("/{login:0x[0-9a-fA-F]{40}}", s)
r.HandleFunc("/ethw", s.MiningNotify)
srv := &http.Server{ srv := &http.Server{
Addr: s.config.Proxy.Listen, Addr: s.config.Proxy.Listen,
Handler: r, Handler: r,
@ -361,3 +360,73 @@ func (s *ProxyServer) isSick() bool {
func (s *ProxyServer) markOk() { func (s *ProxyServer) markOk() {
atomic.StoreInt64(&s.failsCount, 0) atomic.StoreInt64(&s.failsCount, 0)
} }
func (s *ProxyServer) MiningNotify(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "405 method not allowed.", http.StatusMethodNotAllowed)
return
}
body := make([]byte, r.ContentLength)
r.Body.Read(body)
var reply []string
err := json.Unmarshal(body, &reply)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.WriteHeader(http.StatusOK)
t := s.currentBlockTemplate()
// No need to update, we have fresh job
if t != nil {
if t.Header == reply[0] {
return
}
if _, ok := t.headers[reply[0]]; ok {
return
}
}
diff := util.TargetHexToDiff(reply[2])
height, err := strconv.ParseUint(strings.Replace(reply[3], "0x", "", -1), 16, 64)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
pendingReply := &rpc.GetBlockReplyPart{
Difficulty: util.ToHex(s.config.Proxy.Difficulty),
Number: reply[3],
}
newTemplate := BlockTemplate{
Header: reply[0],
Seed: reply[1],
Target: reply[2],
Height: height,
Difficulty: diff,
GetPendingBlockCache: pendingReply,
headers: make(map[string]heightDiffPair),
}
// Copy job backlog and add current one
newTemplate.headers[reply[0]] = heightDiffPair{
diff: diff,
height: height,
}
if t != nil {
for k, v := range t.headers {
if v.height > height-maxBacklog {
newTemplate.headers[k] = v
}
}
}
s.blockTemplate.Store(&newTemplate)
log.Printf("New block notified at height %d / %s / %d", height, reply[0][0:10], diff)
// Stratum
if s.config.Proxy.Stratum.Enabled {
go s.broadcastNewJobs()
}
}

21
rpc/rpc.go

@ -16,24 +16,6 @@ import (
"github.com/yuriy0803/open-etc-pool-friends/util" "github.com/yuriy0803/open-etc-pool-friends/util"
) )
const (
// RPC method "eth_getWork"
RPCEthGetWork = "eth_getWork"
// RPC method "eth_getBlockByNumber"
RPCEthGetBlockByNumber = "eth_getBlockByNumber"
// RPC method "eth_getBlockByHash"
RPCEthGetBlockByHash = "eth_getBlockByHash"
// Additional RPC method constants can be added here
RPCMethodFoo = "foo"
RPCMethodBar = "bar"
RPCMethodBaz = "baz"
RPCMethodQux = "qux"
)
type RPCClient struct { type RPCClient struct {
sync.RWMutex sync.RWMutex
Url string Url string
@ -52,13 +34,12 @@ type GetBlockReply struct {
Difficulty string `json:"difficulty"` Difficulty string `json:"difficulty"`
GasLimit string `json:"gasLimit"` GasLimit string `json:"gasLimit"`
GasUsed string `json:"gasUsed"` GasUsed string `json:"gasUsed"`
BaseFeePerGas string `json:"baseFeePerGas"`
Timestamp string `json:"timestamp"` Timestamp string `json:"timestamp"`
Transactions []Tx `json:"transactions"` Transactions []Tx `json:"transactions"`
Uncles []string `json:"uncles"` Uncles []string `json:"uncles"`
// https://github.com/ethereum/EIPs/issues/95 // https://github.com/ethereum/EIPs/issues/95
SealFields []string `json:"sealFields"` SealFields []string `json:"sealFields"`
// london hard fork
BaseFeePerGas string `json:"baseFeePerGas"`
} }
type GetBlockReplyPart struct { type GetBlockReplyPart struct {

1787
storage/redis.go

File diff suppressed because it is too large Load Diff

591
storage/redis_test.go

@ -0,0 +1,591 @@
package storage
import (
"os"
"reflect"
"strconv"
"testing"
"gopkg.in/redis.v3"
"log"
)
var r *RedisClient
const prefix = "test"
func TestMain(m *testing.M) {
r = NewRedisClient(&Config{Endpoint: "35.187.240.179:6379", Database: 10}, prefix, 1000000, "UBIQ")
reset()
c := m.Run()
reset()
os.Exit(c)
}
func TestWriteShareCheckExist(t *testing.T) {
reset()
exist, _ := r.WriteShare("x", "x", []string{"0x0", "0x0", "0x0"}, 10, 1008, 0)
if exist {
t.Error("PoW must not exist")
}
exist, _ = r.WriteShare("x", "x", []string{"0x0", "0x1", "0x0"}, 10, 1008, 0)
if exist {
t.Error("PoW must not exist")
}
exist, _ = r.WriteShare("x", "x", []string{"0x0", "0x0", "0x1"}, 100, 1010, 0)
if exist {
t.Error("PoW must not exist")
}
exist, _ = r.WriteShare("z", "x", []string{"0x0", "0x0", "0x1"}, 100, 1016, 0)
if !exist {
t.Error("PoW must exist")
}
exist, _ = r.WriteShare("x", "x", []string{"0x0", "0x0", "0x1"}, 100, 1025, 0)
if exist {
t.Error("PoW must not exist")
}
}
func TestGetPayees(t *testing.T) {
reset()
n := 256
for i := 0; i < n; i++ {
r.client.HSet(r.formatKey("miners", strconv.Itoa(i)), "balance", strconv.Itoa(i))
}
var payees []string
payees, _ = r.GetPayees()
if len(payees) != n {
t.Error("Must return all payees")
}
m := make(map[string]struct{})
for _, v := range payees {
m[v] = struct{}{}
}
if len(m) != n {
t.Error("Must be unique list")
}
}
func TestGetBalance(t *testing.T) {
reset()
r.client.HSet(r.formatKey("miners:x"), "balance", "750")
v, _ := r.GetBalance("x")
if v != 750 {
t.Error("Must return balance")
}
v, err := r.GetBalance("z")
if v != 0 {
t.Error("Must return 0 if account does not exist")
}
if err != nil {
t.Error("Must not return error if account does not exist")
}
}
func TestLockPayouts(t *testing.T) {
reset()
r.LockPayouts("x", 1000)
v := r.client.Get("test:payments:lock").Val()
if v != "x:1000" {
t.Errorf("Invalid lock amount: %v", v)
}
err := r.LockPayouts("x", 100)
if err == nil {
t.Errorf("Must not overwrite lock")
}
}
func TestUnlockPayouts(t *testing.T) {
reset()
r.client.Set(r.formatKey("payments:lock"), "x:1000", 0)
r.UnlockPayouts()
err := r.client.Get(r.formatKey("payments:lock")).Err()
if err != redis.Nil {
t.Errorf("Must release lock")
}
}
func TestIsPayoutsLocked(t *testing.T) {
reset()
r.LockPayouts("x", 1000)
if locked, _ := r.IsPayoutsLocked(); !locked {
t.Errorf("Payouts must be locked")
}
}
func TestUpdateBalance(t *testing.T) {
reset()
r.client.HMSetMap(
r.formatKey("miners:x"),
map[string]string{"paid": "50", "balance": "1000"},
)
r.client.HMSetMap(
r.formatKey("finances"),
map[string]string{"paid": "500", "balance": "10000"},
)
amount := int64(250)
r.UpdateBalance("x", amount)
result := r.client.HGetAllMap(r.formatKey("miners:x")).Val()
if result["pending"] != "250" {
t.Error("Must set pending amount")
}
if result["balance"] != "750" {
t.Error("Must deduct balance")
}
if result["paid"] != "50" {
t.Error("Must not touch paid")
}
result = r.client.HGetAllMap(r.formatKey("finances")).Val()
if result["pending"] != "250" {
t.Error("Must set pool pending amount")
}
if result["balance"] != "9750" {
t.Error("Must deduct pool balance")
}
if result["paid"] != "500" {
t.Error("Must not touch pool paid")
}
rank := r.client.ZRank(r.formatKey("payments:pending"), join("x", amount)).Val()
if rank != 0 {
t.Error("Must add pending payment")
}
}
func TestRollbackBalance(t *testing.T) {
reset()
r.client.HMSetMap(
r.formatKey("miners:x"),
map[string]string{"paid": "100", "balance": "750", "pending": "250"},
)
r.client.HMSetMap(
r.formatKey("finances"),
map[string]string{"paid": "500", "balance": "10000", "pending": "250"},
)
r.client.ZAdd(r.formatKey("payments:pending"), redis.Z{Score: 1, Member: "xx"})
amount := int64(250)
r.RollbackBalance("x", amount)
result := r.client.HGetAllMap(r.formatKey("miners:x")).Val()
if result["paid"] != "100" {
t.Error("Must not touch paid")
}
if result["balance"] != "1000" {
t.Error("Must increase balance")
}
if result["pending"] != "0" {
t.Error("Must deduct pending")
}
result = r.client.HGetAllMap(r.formatKey("finances")).Val()
if result["paid"] != "500" {
t.Error("Must not touch pool paid")
}
if result["balance"] != "10250" {
t.Error("Must increase pool balance")
}
if result["pending"] != "0" {
t.Error("Must deduct pool pending")
}
err := r.client.ZRank(r.formatKey("payments:pending"), join("x", amount)).Err()
if err != redis.Nil {
t.Errorf("Must remove pending payment")
}
}
func TestWritePayment(t *testing.T) {
reset()
r.client.HMSetMap(
r.formatKey("miners:x"),
map[string]string{"paid": "50", "balance": "1000", "pending": "250"},
)
r.client.HMSetMap(
r.formatKey("finances"),
map[string]string{"paid": "500", "balance": "10000", "pending": "250"},
)
amount := int64(250)
tx := int64(2)
r.WritePayment("x", "0x0", amount, tx)
result := r.client.HGetAllMap(r.formatKey("miners:x")).Val()
if result["pending"] != "0" {
t.Error("Must unset pending amount")
}
if result["balance"] != "1000" {
t.Error("Must not touch balance")
}
if result["paid"] != "300" {
t.Error("Must increase paid")
}
result = r.client.HGetAllMap(r.formatKey("finances")).Val()
if result["pending"] != "0" {
t.Error("Must deduct pool pending amount")
}
if result["balance"] != "10000" {
t.Error("Must not touch pool balance")
}
if result["paid"] != "750" {
t.Error("Must increase pool paid")
}
err := r.client.Get(r.formatKey("payments:lock")).Err()
if err != redis.Nil {
t.Errorf("Must release lock")
}
err = r.client.ZRank(r.formatKey("payments:pending"), join("x", amount)).Err()
if err != redis.Nil {
t.Error("Must remove pending payment")
}
err = r.client.ZRank(r.formatKey("payments:all"), join("0x0", "x", amount)).Err()
if err == redis.Nil {
t.Error("Must add payment to set")
}
err = r.client.ZRank(r.formatKey("payments:x"), join("0x0", amount)).Err()
if err == redis.Nil {
t.Error("Must add payment to set")
}
}
func TestGetPendingPayments(t *testing.T) {
reset()
r.client.HMSetMap(
r.formatKey("miners:x"),
map[string]string{"paid": "100", "balance": "750", "pending": "250"},
)
amount := int64(1000)
r.UpdateBalance("x", amount)
pending := r.GetPendingPayments()
if len(pending) != 1 {
t.Error("Must return pending payment")
}
if pending[0].Amount != amount {
t.Error("Must have corrent amount")
}
if pending[0].Address != "x" {
t.Error("Must have corrent account")
}
if pending[0].Timestamp <= 0 {
t.Error("Must have timestamp")
}
}
func TestCollectLuckStats(t *testing.T) {
reset()
members := []redis.Z{
redis.Z{Score: 0, Member: "1:0:0x0:0x0:0:100:100:0"},
}
r.client.ZAdd(r.formatKey("blocks:immature"), members...)
members = []redis.Z{
redis.Z{Score: 1, Member: "1:0:0x2:0x0:0:50:100:0"},
redis.Z{Score: 2, Member: "0:1:0x1:0x0:0:100:100:0"},
redis.Z{Score: 3, Member: "0:0:0x3:0x0:0:200:100:0"},
}
r.client.ZAdd(r.formatKey("blocks:matured"), members...)
stats, _ := r.CollectLuckStats([]int{1, 2, 5, 10})
expectedStats := map[string]interface{}{
"1": map[string]float64{
"luck": 1, "uncleRate": 1, "orphanRate": 0,
},
"2": map[string]float64{
"luck": 0.75, "uncleRate": 0.5, "orphanRate": 0,
},
"4": map[string]float64{
"luck": 1.125, "uncleRate": 0.5, "orphanRate": 0.25,
},
}
if !reflect.DeepEqual(stats, expectedStats) {
t.Error("Stats != expected stats")
}
}
func TestCollectStats(t *testing.T) {
stat, err := r.CollectStats(500000, 100, 100)
if err != nil {
t.Errorf("Result : %v, Err : %v", stat, err)
}
t.Logf("Result : %v", stat)
}
func TestGetMinerStats(t *testing.T) {
stats := make(map[string]interface{})
login := "0x5ca87a9e8e132be404a1efb6516665252a74a4e2"
tx := r.client.Multi()
defer tx.Close()
cmds, err := tx.Exec(func() error {
tx.HGetAllMap(r.formatKey("miners", login))
tx.ZRevRangeWithScores(r.formatKey("payments", login), 0, 100)
tx.ZCard(r.formatKey("payments", login))
tx.HGet(r.formatKey("shares", "roundCurrent"), login)
return nil
})
if err != nil && err != redis.Nil {
t.Errorf("Error :", err)
} else {
result, _ := cmds[0].(*redis.StringStringMapCmd).Result()
stats["stats"] = convertStringMap(result)
payments := convertPaymentsResults(cmds[1].(*redis.ZSliceCmd))
stats["payments"] = payments
stats["paymentsTotal"] = cmds[2].(*redis.IntCmd).Val()
roundShares, _ := cmds[3].(*redis.StringCmd).Int64()
if roundShares < 0 {
roundShares = 0
}
stats["roundShares"] = roundShares
log.Printf("Inner Result : %v ", result)
}
log.Printf("Result : %v ", stats)
if err != nil {
t.Errorf("Error :", err)
}
}
func TestStoreExchangeData(t *testing.T) {
m := map[string]string{
"id": "ethereum",
"name": "Ethereum",
"symbol": "ETH",
"rank": "2",
"price_usd": "311.984",
"price_btc": "0.0823755",
"24h_volume_usd": "1161280000.0",
"market_cap_usd": "29309660622.0",
"available_supply": "93946038.0",
"total_supply": "93946038.0",
"percent_change_1h": "0.47",
"percent_change_24h": "4.12",
"percent_change_7d": "30.36",
"last_updated": "1502540048",
"price_inr": "19995.366544",
"24h_volume_inr": "74427596480.0",
"market_cap_inr": "1878485458898",
}
m1 := map[string]string{
"id": "bitcoin",
"name": "Bitcoin",
"symbol": "BTC",
"rank": "1",
"price_usd": "3836.67",
"price_btc": "1.0",
"24h_volume_usd": "2080280000.0",
"market_cap_usd": "63315651883.0",
"available_supply": "16502762.0",
"total_supply": "16502762.0",
"percent_change_1h": "1.26",
"percent_change_24h": "8.93",
"percent_change_7d": "19.58",
"last_updated": "1502551754",
"price_inr": "245896.01697",
"24h_volume_inr": "133327225479.9999847412",
"market_cap_inr": "4057963444804",
}
data := []map[string]string{
m1,
m,
}
tx := r.client.Multi()
defer tx.Close()
for _, v := range data {
for k1, v1 := range v {
tx.HSet(r.formatKey("exchange", v["symbol"]), k1, v1)
}
}
log.Print("Writing Exchange Data : %v", data)
}
func TestGetExchangeData(t *testing.T) {
cmd := r.client.HGetAllMap(r.formatKey("exchange", "ETH"))
result, err := cmd.Result()
log.Printf("Writing Exchange Data : %v ", result)
if err != nil {
t.Errorf("Error at GetExchangeData:", err)
}
}
func TestCreateNewNValue(t *testing.T) {
result, err := r.CreateNewNValue(4000000000)
if err != nil {
t.Errorf("Result : %v, Err : %v", result, err)
}
t.Logf("Result : %v", result)
}
func TestGetNetworkDifficultyForCurrentShareDifficulty(t *testing.T) {
//m ,err := r.GetNodeStates()
result, err := r.GetNetworkDifficultyForCurrentShareDifficulty(4000000000)
if err != nil {
t.Errorf("Result : %v, Err : %v", result, err)
}
t.Logf("Result : %v", result)
}
func TestGetNetworkDifficulty(t *testing.T) {
result, err := r.GetNetworkDifficulty()
if err != nil {
t.Errorf("Result : %v, Err :%v", result, err)
}
t.Logf("Result : %v", result)
}
func TestGetThreshold(t *testing.T) {
result, err := r.SetThreshold("0xfacb288273969c68e9ad1eeeb81f08ab92cf57ad", 5000000)
t.Logf("Result : %v", result)
if err != nil {
t.Errorf("Error , %v", err)
}
}
func TestSetThreshold(t *testing.T) {
r.SetThreshold("0xfacb288273969c68e9ad1eeeb81f08ab92cf57ad", 5000000)
result, err := r.GetThreshold("0xfacb288273969c68e9ad1eeeb81f08ab92cf57ad")
t.Logf("Result : %v", result)
if err != nil {
t.Errorf("Error , %v", err)
}
}
func TestLogIP(t *testing.T) {
r.LogIP("0xb9cf2da90bdff1bc014720cc84f5ab99d7974eba", "192.168.00.100")
}
func TestAdjustCurrentNShares(t *testing.T) {
result, err := r.AdjustCurrentNShares(4000000000)
t.Logf("Result : %v", result)
if err != nil {
t.Errorf("Error , %v", err)
}
/*currentNShare := 1010
lastN := 1000
tx := r.client.Multi()
defer tx.Close()
if currentNShare > lastN{
shareHash := make([]string, currentNShare-lastN)
cmd, err := tx.Exec(func() error {
//Keep removing the shares from the List by RPOP and while removing adjust the correcponding miner share value and the stat:roundCurrent Share value
//count :=0
for loopIndex := currentNShare; loopIndex > lastN; loopIndex--{
//Generate all the poped value of the ShareHash on the Array
//tx.LIndex(r.formatKey("lastshares"),-1)
tx.RPop(r.formatKey("lastshares"))
//tx.HIncrBy(r.formatKey("shares", "roundCurrent"), str, -1)
//t.Logf("List index value : %v", str)
//count++
}
return nil
})
if err != nil {
t.Logf("Error while Reducing the share count , %v", err)
} else {
tx2 := r.client.Multi()
defer tx2.Close()
//Decrement the corresponding share value
_, err := tx2.Exec(func() error {
for key , _ := range shareHash {
poppedValue, err := cmd[key].(*redis.StringCmd).Result()
//poppedValue1, err := cmd[1].(*redis.StringCmd).Result()
if err==nil{
tx2.HIncrBy(r.formatKey("stats"), "roundShares", -1)
tx2.HIncrBy(r.formatKey("shares", "roundCurrent"),poppedValue, -1)
return errors.New("TEST RETURN")
}
log.Print(poppedValue)
log.Print(key)
//log.Print(poppedValue1)
}
return nil
})
if err!=nil{
t.Errorf("Error while adjusting the last share window count , %v", err)
}
}
} else {
//No adjustment is required for the Window
t.Logf("No formatting required")
}
*/
}
func TestWriteBlock(t *testing.T) {
}
func TestWriteShare(t *testing.T) {
}
func reset() {
keys := r.client.Keys(r.prefix + ":*").Val()
for _, k := range keys {
r.client.Del(k)
}
}

1
util/util.go

@ -26,7 +26,6 @@ func StringToBig(h string) *big.Int {
n.SetString(h, 0) n.SetString(h, 0)
return n return n
} }
func IsValidHexAddress(s string) bool { func IsValidHexAddress(s string) bool {
if IsZeroHash(s) || !addressPattern.MatchString(s) { if IsZeroHash(s) || !addressPattern.MatchString(s) {
return false return false

Loading…
Cancel
Save