Kevacoin stratum server for solo-mining
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
3.7 KiB

10 years ago
package storage
import (
"log"
"strconv"
"strings"
"time"
"gopkg.in/redis.v3"
"../pool"
)
type RedisClient struct {
client *redis.Client
prefix string
}
func NewRedisClient(cfg *pool.Redis, prefix string) *RedisClient {
client := redis.NewClient(&redis.Options{
Addr: cfg.Endpoint,
Password: cfg.Password,
DB: cfg.Database,
PoolSize: cfg.PoolSize,
})
return &RedisClient{client: client, prefix: prefix}
}
func (r *RedisClient) Check() {
pong, err := r.client.Ping().Result()
if err != nil {
log.Fatalf("Can't establish Redis connection: %v", err)
}
log.Printf("Redis PING command reply: %v", pong)
}
// Always returns list of addresses. If Redis fails it will return empty list.
func (r *RedisClient) GetBlacklist() []string {
cmd := r.client.SMembers(r.formatKey("blacklist"))
if cmd.Err() != nil {
log.Printf("Failed to get blacklist from Redis: %v", cmd.Err())
return []string{}
}
return cmd.Val()
}
// Always returns list of IPs. If Redis fails it will return empty list.
func (r *RedisClient) GetWhitelist() []string {
cmd := r.client.SMembers(r.formatKey("whitelist"))
if cmd.Err() != nil {
log.Printf("Failed to get blacklist from Redis: %v", cmd.Err())
return []string{}
}
return cmd.Val()
}
func (r *RedisClient) WriteShare(login string, diff int64) {
tx := r.client.Multi()
defer tx.Close()
ms := time.Now().UnixNano() / 1000000
ts := ms / 1000
_, err := tx.Exec(func() error {
r.writeShare(tx, ms, ts, login, diff)
return nil
})
if err != nil {
log.Printf("Failed to insert share data into Redis: %v", err)
}
}
func (r *RedisClient) WriteBlock(login string, diff, roundDiff, height int64, hashHex string) {
tx := r.client.Multi()
defer tx.Close()
ms := time.Now().UnixNano() / 1000000
ts := ms / 1000
cmds, err := tx.Exec(func() error {
r.writeShare(tx, ms, ts, login, diff)
tx.HSet(r.formatKey("stats"), "lastBlockFound", strconv.FormatInt(ms, 10))
tx.ZIncrBy(r.formatKey("finders"), 1, login)
tx.Rename(r.formatKey("shares", "roundCurrent"), r.formatKey("shares", formatRound(height)))
tx.HGetAllMap(r.formatKey("shares", formatRound(height)))
return nil
})
if err != nil {
log.Printf("Failed to insert block candidate into Redis: %v", err)
} else {
sharesMap, _ := cmds[7].(*redis.StringStringMapCmd).Result()
totalShares := int64(0)
for _, v := range sharesMap {
n, _ := strconv.ParseInt(v, 10, 64)
totalShares += n
}
s := join(hashHex, ts, roundDiff, totalShares)
cmd := r.client.ZAdd(r.formatKey("blocks", "candidates"), redis.Z{Score: float64(height), Member: s})
if cmd.Err() != nil {
log.Printf("Failed to insert block candidate shares into Redis: %v", cmd.Err())
} else {
log.Printf("Inserted block to Redis, height: %v, variance: %v/%v, %v", height, totalShares, roundDiff, cmd.Val())
}
}
}
func (r *RedisClient) writeShare(tx *redis.Multi, ms, ts int64, login string, diff int64) {
tx.HIncrBy(r.formatKey("shares", "roundCurrent"), login, diff)
tx.ZAdd(r.formatKey("hashrate"), redis.Z{Score: float64(ts), Member: join(diff, login, ms)})
tx.HIncrBy(r.formatKey("workers", login), "hashes", diff)
tx.HSet(r.formatKey("workers", login), "lastShare", strconv.FormatInt(ts, 10))
}
func (r *RedisClient) formatKey(args ...interface{}) string {
return join(r.prefix, join(args...))
}
func formatRound(height int64) string {
return "round" + strconv.FormatInt(height, 10)
}
func join(args ...interface{}) string {
s := make([]string, len(args))
for i, v := range args {
switch v.(type) {
case string:
s[i] = v.(string)
case int64:
s[i] = strconv.FormatInt(v.(int64), 10)
default:
panic("Invalid type specified for conversion")
}
}
return strings.Join(s, ":")
}