gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/miningpool/database.go (about) 1 package pool 2 3 import ( 4 "bytes" 5 "database/sql" 6 "errors" 7 "fmt" 8 "time" 9 10 "gitlab.com/SiaPrime/SiaPrime/types" 11 ) 12 13 var ( 14 // ErrDuplicateUserInDifferentCoin is an error when a address used in 15 // different coin 16 ErrDuplicateUserInDifferentCoin = errors.New("duplicate user in different coin, you need use a different address") 17 // ErrNoUsernameInDatabase is an error when can't find a username in db 18 ErrNoUsernameInDatabase = errors.New("user is not found in db") 19 // ErrCreateClient is an error when can't create a new client 20 ErrCreateClient = errors.New("Error when creating a new client") 21 ) 22 23 const ( 24 sqlReconnectRetry = 6 25 sqlRetryDelay = 10 26 confirmedButUnpaid = "Confirmed but unpaid" 27 ) 28 29 func (p *Pool) newDbConnection() error { 30 dbc := p.InternalSettings().PoolDBConnection 31 p.dbConnectionMu.Lock() 32 defer p.dbConnectionMu.Unlock() 33 var err error 34 35 // to prevent other goroutine reconnect 36 if p.sqldb != nil { 37 err = p.sqldb.Ping() 38 if err == nil { 39 return nil 40 } 41 } 42 43 for i := 0; i < sqlReconnectRetry; i++ { 44 fmt.Printf("try to connect mysql: %d\n", i) 45 p.sqldb, err = sql.Open("mysql", dbc) 46 if err != nil { 47 time.Sleep(sqlRetryDelay * time.Second) 48 continue 49 } 50 51 err = p.sqldb.Ping() 52 if err != nil { 53 time.Sleep(sqlRetryDelay * time.Second) 54 continue 55 } 56 fmt.Printf("success\n") 57 return nil 58 } 59 60 return fmt.Errorf("sql reconnect retry time exceeded: %d", sqlReconnectRetry) 61 } 62 63 // AddClientDB add user into accounts 64 func (p *Pool) AddClientDB(c *Client) error { 65 p.mu.Lock() 66 defer func() { 67 p.mu.Unlock() 68 }() 69 70 p.yiilog.Printf("Adding user %s to yiimp account\n", c.Name()) 71 tx, err := p.sqldb.Begin() 72 if err != nil { 73 return err 74 } 75 defer tx.Rollback() 76 77 stmt, err := tx.Prepare(` 78 INSERT INTO accounts (coinid, username, coinsymbol) 79 VALUES (?, ?, ?); 80 `) 81 if err != nil { 82 return err 83 } 84 defer stmt.Close() 85 86 rs, err := stmt.Exec(SiaCoinID, c.cr.name, SiaCoinSymbol) 87 if err != nil { 88 return err 89 } 90 err = tx.Commit() 91 if err != nil { 92 return err 93 } 94 id, err := rs.LastInsertId() 95 p.yiilog.Printf("User %s account id is %d\n", c.Name(), id) 96 c.cr.clientID = id 97 98 return nil 99 } 100 101 // FindClientDB find user in accounts 102 func (p *Pool) FindClientDB(name string) (*Client, error) { 103 var clientID int64 104 var Name, Wallet string 105 var coinid int 106 107 p.yiilog.Debugf("Searching for %s in existing accounts\n", name) 108 err := p.sqldb.QueryRow("SELECT id, username, username, coinid FROM accounts WHERE username = ?", name).Scan(&clientID, &Name, &Wallet, &coinid) 109 if err != nil { 110 p.yiilog.Debugf("Search failed: %s\n", err) 111 return nil, ErrNoUsernameInDatabase 112 } 113 p.yiilog.Debugf("Account %s found: %d \n", Name, clientID) 114 if coinid != SiaCoinID { 115 p.yiilog.Debugf(ErrDuplicateUserInDifferentCoin.Error()) 116 return nil, ErrDuplicateUserInDifferentCoin 117 } 118 // if we're here, we found the client in the database 119 // try looking for the client in memory 120 c := p.Client(Name) 121 // if it's in memory, just return a pointer to the copy in memory 122 if c != nil { 123 return c, nil 124 } 125 // client was in database but not in memory - 126 // find workers and connect them to the in memory copy 127 c, err = newClient(p, name) 128 if err != nil { 129 p.log.Printf("Error when creating a new client %s: %s\n", name, err) 130 return nil, ErrCreateClient 131 } 132 var wallet types.UnlockHash 133 wallet.LoadString(Wallet) 134 c.SetWallet(wallet) 135 c.cr.clientID = clientID 136 137 return c, nil 138 } 139 140 func (w *Worker) deleteWorkerRecord() error { 141 stmt, err := w.Parent().pool.sqldb.Prepare(` 142 DELETE FROM workers 143 WHERE id = ? 144 `) 145 if err != nil { 146 w.log.Printf("Error preparing to update worker: %s\n", err) 147 return err 148 } 149 defer stmt.Close() 150 _, err = stmt.Exec(w.wr.workerID) 151 if err != nil { 152 w.log.Printf("Error deleting record: %s\n", err) 153 return err 154 } 155 return nil 156 } 157 158 // DeleteAllWorkerRecords deletes all worker records associated with a pool. 159 // This should be used on pool startup and shutdown to ensure the database 160 // is clean and isn't storing any worker records for non-connected workers. 161 func (p *Pool) DeleteAllWorkerRecords() error { 162 stmt, err := p.sqldb.Prepare(` 163 DELETE FROM workers 164 WHERE pid = ? 165 `) 166 if err != nil { 167 p.log.Printf("Error preparing to delete all workers: %s\n", err) 168 return err 169 } 170 defer stmt.Close() 171 _, err = stmt.Exec(p.InternalSettings().PoolID) 172 if err != nil { 173 p.log.Printf("Error deleting records: %s\n", err) 174 return err 175 } 176 return nil 177 } 178 179 // addFoundBlock add founded block to yiimp blocks table 180 func (w *Worker) addFoundBlock(b *types.Block) error { 181 pool := w.Parent().Pool() 182 tx, err := pool.sqldb.Begin() 183 if err != nil { 184 return err 185 } 186 defer tx.Rollback() 187 188 bh := pool.persist.GetBlockHeight() 189 w.log.Printf("New block to mine on %d\n", uint64(bh)+1) 190 // reward := b.CalculateSubsidy(bh).String() 191 pool.mu.Lock() 192 defer pool.mu.Unlock() 193 timeStamp := time.Now().Unix() 194 // TODO: maybe add difficulty_user 195 stmt, err := tx.Prepare(` 196 INSERT INTO blocks 197 (height, blockhash, coin_id, userid, workerid, category, difficulty, time, algo) 198 VALUES 199 (?, ?, ?, ?, ?, ?, ?, ?, ?) 200 `) 201 if err != nil { 202 return err 203 } 204 defer stmt.Close() 205 206 currentTarget, _ := pool.cs.ChildTarget(b.ID()) 207 difficulty, _ := currentTarget.Difficulty().Uint64() // TODO: maybe should use parent ChildTarget 208 // TODO: figure out right difficulty_user 209 _, err = stmt.Exec(bh, b.ID().String(), SiaCoinID, w.Parent().cr.clientID, 210 w.wr.workerID, "new", difficulty, timeStamp, SiaCoinAlgo) 211 if err != nil { 212 return err 213 } 214 err = tx.Commit() 215 if err != nil { 216 return err 217 } 218 219 return nil 220 } 221 222 // SaveShift periodically saves the shares for a given worker to the db 223 func (s *Shift) SaveShift() error { 224 if len(s.Shares()) == 0 { 225 return nil 226 } 227 228 worker := s.worker 229 client := worker.Parent() 230 pool := client.Pool() 231 var buffer bytes.Buffer 232 buffer.WriteString("INSERT INTO shares(userid, workerid, coinid, valid, difficulty, time, algo, reward, block_difficulty, status, height, share_reward, share_diff) VALUES ") 233 for i, share := range s.Shares() { 234 if i != 0 { 235 buffer.WriteString(",") 236 } 237 buffer.WriteString(fmt.Sprintf("(%d, %d, %d, %t, %f, %d, '%s', %f, %d, %d, %d, %f, %f)", 238 share.userid, share.workerid, SiaCoinID, share.valid, share.difficulty, share.time.Unix(), 239 SiaCoinAlgo, share.reward, share.blockDifficulty, 0, share.height, share.shareReward, share.shareDifficulty)) 240 } 241 buffer.WriteString(";") 242 243 rows, err := pool.sqldb.Query(buffer.String()) 244 if rows != nil { 245 rows.Close() 246 } 247 if err != nil { 248 worker.log.Println(buffer.String()) 249 worker.log.Printf("Error saving shares: %s\n, Try to reconnect", err) 250 fmt.Println(err) 251 err = pool.newDbConnection() 252 if err != nil { 253 worker.log.Println(buffer.String()) 254 worker.log.Printf("Error saving shares: %s\n, Try to reconnect", err) 255 fmt.Println(err) 256 return err 257 } 258 rows2, err2 := pool.sqldb.Query(buffer.String()) 259 if rows2 != nil { 260 rows2.Close() 261 } 262 if err2 != nil { 263 worker.log.Println(buffer.String()) 264 worker.log.Printf("Error adding record of last shift: %s\n", err2) 265 return err2 266 } 267 } 268 269 return nil 270 } 271 272 // addWorkerDB inserts info to workers 273 func (c *Client) addWorkerDB(w *Worker) error { 274 c.mu.Lock() 275 defer c.mu.Unlock() 276 277 c.log.Printf("Adding client %s worker %s to database\n", c.cr.name, w.Name()) 278 tx, err := c.pool.sqldb.Begin() 279 if err != nil { 280 return err 281 } 282 defer tx.Rollback() 283 // TODO: add ip etc info 284 stmt, err := tx.Prepare(` 285 INSERT INTO workers (userid, name, worker, algo, time, pid, version, ip) 286 VALUES (?, ?, ?, ?, ?, ?, ?, ?); 287 `) 288 if err != nil { 289 return err 290 } 291 defer stmt.Close() 292 293 rs, err := stmt.Exec(c.cr.clientID, c.cr.name, w.wr.name, SiaCoinAlgo, time.Now().Unix(), 294 c.pool.InternalSettings().PoolID, w.s.clientVersion, w.s.remoteAddr) 295 if err != nil { 296 return err 297 } 298 299 affectedRows, err := rs.RowsAffected() 300 if err != nil { 301 return err 302 } 303 w.log.Printf("Rows affected insert workers %d", affectedRows) 304 305 id, err := rs.LastInsertId() 306 if err != nil { 307 return err 308 } 309 310 w.wr.workerID = id 311 312 err = tx.Commit() 313 if err != nil { 314 return err 315 } 316 317 return nil 318 }