github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/miningpool/database.go (about)

     1  package pool
     2  
     3  import (
     4  	//"database/sql"
     5      "github.com/go-redis/redis"
     6  	//"context"
     7  	"errors"
     8  	"fmt"
     9      "strconv"
    10  	"time"
    11  
    12  	"SiaPrime/types"
    13  )
    14  
    15  var (
    16  	// ErrDuplicateUserInDifferentCoin is an error when a address used in
    17  	// different coin
    18  	//ErrDuplicateUserInDifferentCoin = errors.New("duplicate user in different coin, you need use a different address")
    19  	// ErrNoUsernameInDatabase is an error when can't find a username in db
    20  	ErrNoUsernameInDatabase = errors.New("user is not found in db")
    21  	// ErrCreateClient is an error when can't create a new client
    22  	ErrCreateClient = errors.New("Error when creating a new client")
    23  	ErrQueryTimeout = errors.New("DB query timeout")
    24      ErrConnectDB = errors.New("error in connecting redis")
    25      DB = []string{"accounts", "workers", "shares", "blocks"}
    26  )
    27  
    28  const (
    29  	sqlReconnectRetry  = 6
    30  	sqlRetryDelay      = 10
    31  	sqlQueryTimeout    = 5
    32      redisExpireTime    = 48 * 60 * 60 * time.Second
    33  )
    34  
    35  func (p *Pool) newDbConnection() error {
    36  	dbc := *p.InternalSettings().PoolRedisConnection
    37  	p.dbConnectionMu.Lock()
    38  	defer p.dbConnectionMu.Unlock()
    39  	var err error
    40  
    41      i := 0
    42      for _, conn := range p.redisdb {
    43          _, err = conn.Ping().Result()
    44          if err == nil {
    45              i++
    46          }
    47      }
    48  
    49      if i == len(DB) {
    50          return nil
    51      }
    52  
    53      for _, s := range DB {
    54          hosts := []string{}
    55          for _, host := range dbc["hosts"].([]string) {
    56              hosts = append(hosts, fmt.Sprintf("%s:%d", host, dbc["tables"].(map[string]interface{})[s].(int)))
    57          }
    58  
    59          fmt.Printf("try to connect redis: %s\n", s)
    60          p.redisdb[s] = redis.NewClusterClient(&redis.ClusterOptions{
    61              Addrs:      hosts,
    62              Password:   dbc["pass"].(string),
    63          })
    64          if err != nil {
    65              fmt.Println(err)
    66              return err
    67          }
    68          _, err = p.redisdb[s].Ping().Result()
    69          if err != nil {
    70              fmt.Println(err)
    71              return err
    72          }
    73          fmt.Println("Connection successful.")
    74      }
    75  
    76      i = 0
    77      for _, conn := range p.redisdb {
    78          _, err = conn.Ping().Result()
    79          if err == nil {
    80              i++
    81          }
    82      }
    83  
    84      if i == len(DB) {
    85          return nil
    86      }
    87  
    88      return ErrConnectDB
    89  }
    90  
    91  func (p *Pool) closeAllDB() {
    92      for _, conn := range p.redisdb {
    93          conn.Close()
    94      }
    95  }
    96  
    97  // AddClientDB add user into accounts
    98  func (p *Pool) AddClientDB(c *Client) error {
    99      id := p.newStratumID()()
   100      err := p.redisdb["accounts"].Set(c.Name(), id, 0).Err()
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	p.dblog.Printf("User %s account id is %d\n", c.Name(), id)
   106  	c.SetID(id)
   107  
   108  	return nil
   109  }
   110  
   111  // addWorkerDB inserts info to workers
   112  func (c *Client) addWorkerDB(w *Worker) error {
   113      id := c.Pool().newStratumID()()
   114      err := c.Pool().redisdb["workers"].HMSet(
   115          fmt.Sprintf("%d.%d", c.GetID(), id),
   116          map[string]interface{} {
   117              "wallet":   c.Name(),
   118              "worker":   w.Name(),
   119              "time":     time.Now().Unix(),
   120              "pid":      c.pool.InternalSettings().PoolID,
   121              "version":  w.Session().clientVersion,
   122              "ip":       w.Session().remoteAddr,
   123          }).Err()
   124          err = c.Pool().redisdb["workers"].Persist(
   125              fmt.Sprintf("%d.%d", c.GetID(), id)).Err()
   126  	if err != nil {
   127  		return err
   128  	}
   129  	w.SetID(id)
   130  	return nil
   131  }
   132  
   133  // FindClientDB find user in accounts
   134  func (p *Pool) FindClientDB(name string) (*Client, error) {
   135  	var clientID uint64
   136      var err error
   137  	c := p.Client(name)
   138  	// if it's in memory, just return a pointer to the copy in memory
   139  	if c != nil {
   140  		return c, nil
   141  	}
   142  
   143      id, err := p.redisdb["accounts"].Get(name).Result()
   144      if err == redis.Nil {
   145          return nil, ErrNoUsernameInDatabase
   146      } else if err != nil {
   147          fmt.Println(err)
   148          return nil, err
   149      }
   150      
   151      clientID, err = strconv.ParseUint(id, 10, 64)
   152      if err != nil {
   153          return nil, err
   154      }
   155  	// client was in database but not in memory -
   156  	// find workers and connect them to the in memory copy
   157  	c, err = newClient(p, name)
   158  	if err != nil {
   159  		p.dblog.Printf("Error when creating a new client %s: %s\n", name, err)
   160  		return nil, ErrCreateClient
   161  	}
   162  	var wallet types.UnlockHash
   163  	wallet.LoadString(name)
   164  	c.SetWallet(wallet)
   165  	c.SetID(clientID)
   166  
   167  	return c, nil
   168  }
   169  
   170  func (w *Worker) deleteWorkerRecord() error {
   171      err := w.Parent().Pool().redisdb["workers"].Expire(
   172          fmt.Sprintf("%d.%d", w.Parent().GetID(), w.GetID()),
   173          redisExpireTime).Err()
   174  	if err != nil {
   175  		w.Parent().Pool().dblog.Printf("Error setting redis worker expire: %s\n", err)
   176  		return err
   177  	}
   178  	return nil
   179  }
   180  
   181  func (p *Pool) DeleteAllWorkerRecords() error {
   182      conn := p.redisdb["workers"]
   183      var cursor uint64
   184      match := "*"
   185      count := int64(10)
   186      for {
   187          var keys []string
   188          var err error
   189          keys, cursor, err = conn.Scan(cursor, match, count).Result()
   190          if err != nil {
   191              fmt.Println(err)
   192              return err
   193          }
   194          for _, key := range keys {
   195              if conn.TTL(key).Val() < 0 {
   196                  err = conn.Expire(key, redisExpireTime).Err()
   197                  if err != nil {
   198                      return err
   199                  }
   200              }
   201          }
   202          if cursor == 0 {
   203              break
   204          }
   205      }
   206      return nil
   207  }
   208  // DeleteAllWorkerRecords deletes all worker records associated with a pool.
   209  // This should be used on pool startup and shutdown to ensure the database
   210  // is clean and isn't storing any worker records for non-connected workers.
   211  /*
   212  func (p *Pool) DeleteAllWorkerRecords() error {
   213      err := p.redisdb["workers"].FlushDB().Err()
   214  	if err != nil {
   215  		p.dblog.Printf("Error deleting records: %s\n", err)
   216  		return err
   217  	}
   218  	return nil
   219  }*/
   220  
   221  // addFoundBlock add founded block to yiimp blocks table
   222  func (w *Worker) addFoundBlock(b *types.Block) error {
   223  	pool := w.Parent().Pool()
   224  	bh := pool.persist.GetBlockHeight()
   225  	//w.log.Printf("New block to mine on %d\n", uint64(bh)+1)
   226  	// reward := b.CalculateSubsidy(bh).String()
   227  	pool.blockFoundMu.Lock()
   228  	defer pool.blockFoundMu.Unlock()
   229  	timeStamp := time.Now().Unix()
   230  	currentTarget, _ := pool.cs.ChildTarget(b.ID())
   231  	difficulty, _ := currentTarget.Difficulty().Uint64() // TODO: maybe should use parent ChildTarget
   232      exists := pool.redisdb["blocks"].Exists(strconv.FormatUint(uint64(bh), 10)).Val()
   233      if exists == 1 {
   234          pool.dblog.Printf("orphan block %s found, at height %d\n", b.ID().String(), uint64(bh))
   235          return nil
   236      }
   237      err := pool.redisdb["blocks"].HMSet(
   238          strconv.FormatUint(uint64(bh), 10),
   239          map[string]interface{} {
   240              "blockhash":    b.ID().String(),
   241              "user":         w.Parent().Name(),
   242              "worker":       w.Name(),
   243              "category":     "new",
   244              "difficulty":   difficulty,
   245              "time":         timeStamp,
   246          }).Err()
   247  	if err != nil {
   248  		return err
   249  	}
   250  	return nil
   251  }
   252  
   253  // SaveShift periodically saves the shares for a given worker to the db
   254  func (s *Shift) SaveShift() error {
   255      if len(s.Shares()) == 0 {
   256          return nil
   257      }
   258  
   259      worker := s.worker
   260      client := worker.Parent()
   261      redisdb := client.Pool().redisdb["shares"]
   262      for _, share := range s.Shares() {
   263          err := redisdb.HMSet(
   264              fmt.Sprintf("%d.%d.%d.%d", client.GetID(), worker.GetID(), share.height, share.time.Unix()),
   265              map[string]interface{} {
   266                  "valid":            share.valid,
   267                  "difficulty":       share.difficulty,
   268                  "reward":           share.reward,
   269                  "block_difficulty": share.blockDifficulty,
   270                  "share_reward":     share.shareReward,
   271                  "share_diff":       share.shareDifficulty,
   272              }).Err()
   273          if err != nil {
   274              client.Pool().dblog.Println(err)
   275              return err
   276          }
   277      }
   278      return nil
   279  }