func NodeVoting(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "NodeVoting" d := new(daemon) d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } d.goRoutineName = GoroutineName d.chAnswer = chAnswer d.chBreaker = chBreaker if utils.Mobile() { d.sleepTime = 3600 } else { d.sleepTime = 60 } if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) { return } d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } err = d.notMinerSetSleepTime(1800) if err != nil { log.Error("%v", err) return } BEGIN: for { log.Info(GoroutineName) MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())} // проверим, не нужно ли нам выйти из цикла if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } err, restart := d.dbLock() if restart { break BEGIN } if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } // берем данные, которые находятся на голосовании нодов rows, err := d.Query(d.FormatQuery(` SELECT miners_data.user_id, http_host as host, face_hash, profile_hash, photo_block_id, photo_max_miner_id, miners_keepers, id as vote_id, miner_id FROM votes_miners LEFT JOIN miners_data ON votes_miners.user_id = miners_data.user_id WHERE cron_checked_time < ? AND votes_end = 0 AND type = 'node_voting' `), utils.Time()-consts.CRON_CHECKED_TIME_SEC) if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if ok := rows.Next(); ok { var vote_id, miner_id int64 var user_id, host, row_face_hash, row_profile_hash, photo_block_id, photo_max_miner_id, miners_keepers string err = rows.Scan(&user_id, &host, &row_face_hash, &row_profile_hash, &photo_block_id, &photo_max_miner_id, &miners_keepers, &vote_id, &miner_id) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // проверим, не нужно нам выйти, т.к. обновилась версия софта if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { rows.Close() utils.Sleep(1) break } minersIds := utils.GetMinersKeepers(photo_block_id, photo_max_miner_id, miners_keepers, true) myUsersIds, err := d.GetMyUsersIds(true, true) myMinersIds, err := d.GetMyMinersIds(myUsersIds) // нет ли нас среди тех, кто должен скачать фото к себе и проголосовать var intersectMyMiners []int64 for _, id := range minersIds { if utils.InSliceInt64(int64(id), myMinersIds) { intersectMyMiners = append(intersectMyMiners, int64(id)) } } var vote int64 if len(intersectMyMiners) > 0 { var downloadError bool var faceHash, profileHash string var faceFile, profileFile []byte // копируем фото к себе profilePath := *utils.Dir + "/public/profile_" + user_id + ".jpg" _, err = utils.DownloadToFile(host+"/public/"+user_id+"_user_profile.jpg", profilePath, 60, chBreaker, chAnswer, GoroutineName) if err != nil { log.Error("%s", utils.ErrInfo(err)) downloadError = true } facePath := *utils.Dir + "/public/face_" + user_id + ".jpg" _, err = utils.DownloadToFile(host+"/public/"+user_id+"_user_face.jpg", facePath, 60, chBreaker, chAnswer, GoroutineName) if err != nil { log.Error("%s", utils.ErrInfo(err)) downloadError = true } if !downloadError { // хэши скопированных фото profileFile, err = ioutil.ReadFile(profilePath) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } profileHash = string(utils.DSha256(profileFile)) log.Info("%v", "profileHash", profileHash) faceFile, err = ioutil.ReadFile(facePath) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } faceHash = string(utils.DSha256(faceFile)) log.Info("%v", "faceHash", faceHash) } // проверяем хэш. Если сходится, то голосуем за, если нет - против и размер не должен быть более 200 Kb. if profileHash == row_profile_hash && faceHash == row_face_hash && len(profileFile) < 204800 && len(faceFile) < 204800 { vote = 1 } else { log.Error("%s %s %s %s %d %d", profileHash, row_face_hash, faceHash, row_profile_hash, len(profileFile), len(faceFile)) vote = 0 // если хэш не сходится, то удаляем только что скаченное фото os.Remove(profilePath) os.Remove(facePath) } // проходимся по всем нашим майнерам, если это пул и по одному, если это сингл-мод for _, myMinerId := range intersectMyMiners { myUserId, err := d.Single("SELECT user_id FROM miners_data WHERE miner_id = ?", myMinerId).Int64() if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } curTime := utils.Time() forSign := fmt.Sprintf("%v,%v,%v,%v,%v", utils.TypeInt("VotesNodeNewMiner"), curTime, myUserId, vote_id, vote) binSign, err := d.GetBinSign(forSign, myUserId) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } data := utils.DecToBin(utils.TypeInt("VotesNodeNewMiner"), 1) data = append(data, utils.DecToBin(curTime, 4)...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(myUserId))...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(vote_id))...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(vote))...) data = append(data, utils.EncodeLengthPlusData([]byte(binSign))...) err = d.InsertReplaceTxInQueue(data) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } } // отмечаем, чтобы больше не брать эту строку err = d.ExecSql("UPDATE votes_miners SET cron_checked_time = ? WHERE id = ?", utils.Time(), vote_id) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } rows.Close() d.dbUnlock() if d.dSleep(d.sleepTime) { break BEGIN } } log.Debug("break BEGIN %v", GoroutineName) }
func (p *Parser) CashRequestInRollback() error { err := p.updPromisedAmountsRollback(p.TxUserID, true) if err != nil { return p.ErrInfo(err) } err = p.ExecSql("UPDATE cash_requests SET for_repaid_del_block_id = 0 WHERE to_user_id = ? AND for_repaid_del_block_id = ?", p.TxUserID, p.BlockData.BlockId) if err != nil { return p.ErrInfo(err) } var to_user_id, from_user_id, cTime int64 var status, currency_id string var hash_code []byte var amount float64 err = p.QueryRow(p.FormatQuery("SELECT from_user_id, to_user_id, currency_id, status, hash_code, time, amount FROM cash_requests WHERE id = ?"), p.TxMaps.Int64["cash_request_id"]).Scan(&from_user_id, &to_user_id, ¤cy_id, &status, &hash_code, &cTime, &amount) if err != nil && err != sql.ErrNoRows { return p.ErrInfo(err) } err = p.pointsUpdateRollbackMain(from_user_id) if err != nil { return p.ErrInfo(err) } // откатим cash_requests err = p.ExecSql("UPDATE cash_requests SET status = 'pending' WHERE id = ?", p.TxMaps.Int64["cash_request_id"]) if err != nil { return p.ErrInfo(err) } // откатим DC, списанные с кошелька отправителя DC err = p.generalRollback("wallets", from_user_id, "AND currency_id = "+currency_id, false) if err != nil { return p.ErrInfo(err) } // откатываем обещанные суммы, у которых было затронуто amount rows, err := p.Query(p.FormatQuery("SELECT id, log_id FROM promised_amount WHERE user_id = ? AND currency_id = ? AND cash_request_in_block_id = ? AND del_block_id = 0 AND del_mining_block_id = 0 ORDER BY log_id DESC"), p.TxUserID, currency_id, p.BlockData.BlockId) if err != nil { return p.ErrInfo(err) } defer rows.Close() for rows.Next() { var id, log_id int64 err = rows.Scan(&id, &log_id) if err != nil { return p.ErrInfo(err) } if log_id > 0 { logData, err := p.OneRow("SELECT amount, tdc_amount, tdc_amount_update, cash_request_in_block_id, prev_log_id FROM log_promised_amount WHERE log_id = ?", log_id).String() if err != nil { return p.ErrInfo(err) } err = p.ExecSql("UPDATE promised_amount SET amount = ?, tdc_amount = ?, tdc_amount_update = ?, cash_request_in_block_id = ?, log_id = ? WHERE id = ?", logData["amount"], logData["tdc_amount"], logData["tdc_amount_update"], logData["cash_request_in_block_id"], logData["prev_log_id"], id) if err != nil { return p.ErrInfo(err) } err = p.ExecSql("DELETE FROM log_promised_amount WHERE log_id = ?", log_id) if err != nil { return p.ErrInfo(err) } err = p.rollbackAI("log_promised_amount", 1) if err != nil { return p.ErrInfo(err) } } else { err = p.ExecSql("DELETE FROM promised_amount WHERE id = ?", id) if err != nil { return p.ErrInfo(err) } err = p.rollbackAI("promised_amount", 1) if err != nil { return p.ErrInfo(err) } } } cashRequestsFromUserId, err := p.Single("SELECT from_user_id FROM cash_requests WHERE id = ?", p.TxMaps.Int64["cash_request_id"]).Int64() if err != nil { return p.ErrInfo(err) } // проверим, не наш ли это user_id _, _, myPrefix, myUserIds, err := p.GetMyUserId(p.TxUserID) if err != nil { return err } if utils.InSliceInt64(p.TxUserID, myUserIds) || utils.InSliceInt64(cashRequestsFromUserId, myUserIds) { collective, err := p.GetCommunityUsers() if err != nil { return err } if len(collective) > 0 && utils.InSliceInt64(cashRequestsFromUserId, myUserIds) { // наш юзер - это отправитель _out myPrefix = utils.Int64ToStr(cashRequestsFromUserId) + "_" } else if len(collective) > 0 && utils.InSliceInt64(p.TxUserID, myUserIds) { // наш юзер - это отправитель _in myPrefix = utils.Int64ToStr(p.TxUserID) + "_" } else { myPrefix = "" } // обновим таблу, отметив, что мы отдали деньги err = p.ExecSql("UPDATE "+myPrefix+"my_cash_requests SET status = 'approved' WHERE cash_request_id = ?", p.TxMaps.Int64["cash_request_id"]) if err != nil { return p.ErrInfo(err) } if utils.InSliceInt64(cashRequestsFromUserId, myUserIds) { err = p.ExecSql("DELETE FROM "+myPrefix+"my_dc_transactions WHERE status = 'approved' AND type = 'cash_request' AND amount = ? AND block_id = ? AND currency_id = ?", amount, p.BlockData.BlockId, currency_id) if err != nil { return p.ErrInfo(err) } } } err = p.mydctxRollback() if err != nil { return p.ErrInfo(err) } return nil }
func Connector(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() if _, err := os.Stat(*utils.Dir + "/nodes.inc"); os.IsNotExist(err) { data, err := static.Asset("static/nodes.inc") if err != nil { log.Error("%v", err) } err = ioutil.WriteFile(*utils.Dir+"/nodes.inc", []byte(data), 0644) if err != nil { log.Error("%v", err) } } GoroutineName := "Connector" d := new(daemon) d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } d.goRoutineName = GoroutineName d.chAnswer = chAnswer d.chBreaker = chBreaker if utils.Mobile() { d.sleepTime = 600 } else { d.sleepTime = 30 } if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) { return } d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } // соединения для чата иногда отваливаются, поэтому в цикле мониторим состояние go func() { for { if myUserIdForChat == 0 { utils.Sleep(1) continue } if len(utils.ChatOutConnections) < 5 || len(utils.ChatInConnections) < 5 { go d.chatConnector() } utils.Sleep(30) } }() BEGIN: for { log.Info(GoroutineName) MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())} // проверим, не нужно ли нам выйти из цикла if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } nodeConfig, err := d.GetNodeConfig() if len(nodeConfig["local_gate_ip"]) > 0 { utils.Sleep(2) continue } var delMiners []string var hosts []map[string]string var nodeCount int64 idArray := make(map[int]int64) nodesInc := make(map[string]string) // ровно стольким нодам мы будем слать хэши блоков и тр-ий var maxHosts = consts.OUT_CONNECTIONS if utils.StrToInt64(nodeConfig["out_connections"]) > 0 { maxHosts = utils.StrToInt(nodeConfig["out_connections"]) } log.Info("%v", maxHosts) collective, err := d.GetCommunityUsers() if err != nil { log.Error("%v", err) return } if len(collective) == 0 { myUserId, err := d.GetMyUserId("") if err != nil { log.Error("%v", err) return } collective = append(collective, myUserId) myUserIdForChat = myUserId } else { myUserIdForChat, err = d.Single(`SELECT pool_admin_user_id FROM config`).Int64() if err != nil { log.Error("%v", err) return } } // в сингл-моде будет только $my_miners_ids[0] myMinersIds, err := d.GetMyMinersIds(collective) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } log.Info("%v", myMinersIds) nodesBan, err := d.GetMap(` SELECT tcp_host, ban_start FROM nodes_ban LEFT JOIN miners_data ON miners_data.user_id = nodes_ban.user_id `, "tcp_host", "ban_start") log.Info("%v", nodesBan) nodesConnections, err := d.GetAll(` SELECT nodes_connection.host, nodes_connection.user_id, ban_start, miner_id FROM nodes_connection LEFT JOIN nodes_ban ON nodes_ban.user_id = nodes_connection.user_id LEFT JOIN miners_data ON miners_data.user_id = nodes_connection.user_id `, -1) //fmt.Println("nodesConnections", nodesConnections) log.Debug("nodesConnections: %v", nodesConnections) for _, data := range nodesConnections { // проверим, не нужно нам выйти, т.к. обновилась версия софта if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } /*// проверим соотвествие хоста и user_id ok, err := d.Single("SELECT user_id FROM miners_data WHERE user_id = ? AND tcp_host = ?", data["user_id"], data["host"]).Int64() if err != nil { utils.Sleep(1) continue BEGIN } if ok == 0 { err = d.ExecSql("DELETE FROM nodes_connection WHERE host = ? OR user_id = ?", data["host"], data["user_id"]) if err != nil { utils.Sleep(1) continue BEGIN } }*/ // если нода забанена недавно if utils.StrToInt64(data["ban_start"]) > utils.Time()-consts.NODE_BAN_TIME { delMiners = append(delMiners, data["miner_id"]) err = d.ExecSql("DELETE FROM nodes_connection WHERE host = ? OR user_id = ?", data["host"], data["user_id"]) if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } continue } hosts = append(hosts, map[string]string{"host": data["host"], "user_id": data["user_id"]}) nodesInc[data["host"]] = data["user_id"] nodeCount++ } log.Debug("hosts: %v", hosts) /* ch := make(chan *answerType) for _, host := range hosts { userId := utils.StrToInt64(host["user_id"]) go func(userId int64, host string) { ch_ := make(chan *answerType, 1) go func() { log.Debug("host: %v / userId: %v", host, userId) ch_ <- check(host, userId) }() select { case reachable := <-ch_: ch <- reachable case <-time.After(consts.WAIT_CONFIRMED_NODES * time.Second): ch <- &answerType{userId: userId, answer: 0} } }(userId, host["host"]) } log.Debug("%v", "hosts", hosts) var newHosts []map[string]string var countOk int // если нода не отвечает, то удалем её из таблы nodes_connection for i := 0; i < len(hosts); i++ { result := <-ch if result.answer == 0 { log.Info("delete %v", result.userId) err = d.ExecSql("DELETE FROM nodes_connection WHERE user_id = ?", result.userId) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } } for _, data := range hosts { if utils.StrToInt64(data["user_id"]) != result.userId { newHosts = append(newHosts, data) } } } else { countOk++ } log.Info("answer: %v", result) } */ var countOk int hosts = checkHosts(hosts, &countOk) log.Debug("countOk: %d / hosts: %v", countOk, hosts) // проверим, не нужно нам выйти, т.к. обновилась версия софта if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } // добьем недостающие хосты до $max_hosts var newHostsForCheck []map[string]string if len(hosts) < maxHosts { need := maxHosts - len(hosts) max, err := d.Single("SELECT max(miner_id) FROM miners").Int() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } i0 := 0 for { rand := 1 if max > 1 { rand = utils.RandInt(1, max+1) } idArray[rand] = 1 i0++ if i0 > 30 || len(idArray) >= need || len(idArray) >= max { break } } log.Info("%v", "idArray", idArray) // удалим себя for _, id := range myMinersIds { delete(idArray, int(id)) } // Удалим забаннные хосты for _, id := range delMiners { delete(idArray, utils.StrToInt(id)) } log.Info("%v", "idArray", idArray) ids := "" if len(idArray) > 0 { for id, _ := range idArray { ids += utils.IntToStr(id) + "," } ids = ids[:len(ids)-1] minersHosts, err := d.GetMap(` SELECT tcp_host, user_id FROM miners_data WHERE miner_id IN (`+ids+`)`, "tcp_host", "user_id") if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } for host, userId := range minersHosts { if len(nodesBan[host]) > 0 { if utils.StrToInt64(nodesBan[host]) > utils.Time()-consts.NODE_BAN_TIME { continue } } //hosts = append(hosts, map[string]string{"host": host, "user_id": userId}) /*err = d.ExecSql("DELETE FROM nodes_connection WHERE host = ?", host) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } log.Debug(host)*/ newHostsForCheck = append(newHostsForCheck, map[string]string{"user_id": userId, "host": host}) /*err = d.ExecSql("INSERT INTO nodes_connection ( host, user_id ) VALUES ( ?, ? )", host, userId) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN }*/ } } } hosts = checkHosts(newHostsForCheck, &countOk) log.Debug("countOk: %d / hosts: %v", countOk, hosts) // проверим, не нужно нам выйти, т.к. обновилась версия софта if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } log.Debug("%v", "hosts", hosts) // если хосты не набрались из miner_data, то берем из файла if len(hosts) < 10 { hostsData_, err := ioutil.ReadFile(*utils.Dir + "/nodes.inc") if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } hostsData := strings.Split(string(hostsData_), "\n") log.Debug("%v", "hostsData_", hostsData_) log.Debug("%v", "hostsData", hostsData) max := 0 log.Debug("maxHosts: %v", maxHosts) if len(hosts) > maxHosts-1 { max = maxHosts } else { max = len(hostsData) } log.Debug("max: %v", max) for i := 0; i < max; i++ { r := utils.RandInt(0, max) if len(hostsData) <= r { continue } hostUserId := strings.Split(hostsData[r], ";") if len(hostUserId) == 1 { continue } host, userId := hostUserId[0], hostUserId[1] if utils.InSliceInt64(utils.StrToInt64(userId), collective) { continue } if len(nodesBan[host]) > 0 { if utils.StrToInt64(nodesBan[host]) > utils.Time()-consts.NODE_BAN_TIME { continue } } /* err = d.ExecSql("DELETE FROM nodes_connection WHERE host = ?", host) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } log.Debug(host) /*err = d.ExecSql("INSERT INTO nodes_connection ( host, user_id ) VALUES ( ?, ? )", host, userId) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN }*/ newHostsForCheck = append(newHostsForCheck, map[string]string{"user_id": userId, "host": host}) nodesInc[host] = userId } } hosts = checkHosts(newHostsForCheck, &countOk) log.Debug("countOk: %d / hosts: %v", countOk, hosts) // проверим, не нужно нам выйти, т.к. обновилась версия софта if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } for _, host := range hosts { err = d.ExecSql("DELETE FROM nodes_connection WHERE host = ?", host["host"]) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } err = d.ExecSql("INSERT INTO nodes_connection ( host, user_id ) VALUES ( ?, ? )", host["host"], host["user_id"]) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } } if nodeCount > 5 { nodesFile := "" for k, v := range nodesInc { nodesFile += k + ";" + v + "\n" } nodesFile = nodesFile[:len(nodesFile)-1] err := ioutil.WriteFile(*utils.Dir+"/nodes.inc", []byte(nodesFile), 0644) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } } var sleepTime int if countOk < 2 { sleepTime = 5 } else { sleepTime = d.sleepTime } if d.dSleep(sleepTime) { break BEGIN } } log.Debug("break BEGIN %v", GoroutineName) }
func (p *Parser) CashRequestIn() error { var to_user_id, from_user_id, currency_id, cTime int64 var status string var hash_code []byte var amount float64 err := p.QueryRow(p.FormatQuery("SELECT from_user_id, to_user_id, currency_id, status, hash_code, time, amount FROM cash_requests WHERE id = ?"), p.TxMaps.Int64["cash_request_id"]).Scan(&from_user_id, &to_user_id, ¤cy_id, &status, &hash_code, &cTime, &amount) if err != nil && err != sql.ErrNoRows { return p.ErrInfo(err) } // возможно нужно обновить таблицу points_status err = p.pointsUpdateMain(from_user_id) if err != nil { return p.ErrInfo(err) } promisedAmountStatus := "repaid" // есть вероятность того, что после попадания в Dc-сеть cash_request_out придет admin_ban_miner, а после попадения в сеть cash_request_in придет admin_unban_miner. В admin_unban_miner смена статуса suspended на repaid у нового promised_amount учтено userStatus, err := p.Single("SELECT status FROM miners_data WHERE user_id = ?", p.TxUserID).String() if err != nil { return p.ErrInfo(err) } var repaidPromisedAmountId int64 if userStatus == "suspended_miner" { promisedAmountStatus = "suspended" // нужно понять, какой promised_amount ранее имел статус repaid repaidPromisedAmountId, err = p.Single("SELECT id FROM promised_amount WHERE user_id = ? AND currency_id = ? AND status_backup = 'repaid' AND del_block_id = 0 AND del_mining_block_id = 0", p.TxUserID, currency_id).Int64() if err != nil { return p.ErrInfo(err) } } else { // ну а если майнер не забанен админом, то всё просто repaidPromisedAmountId, err = p.Single("SELECT id FROM promised_amount WHERE user_id = ? AND currency_id = ? AND status = 'repaid' AND del_block_id = 0 AND del_mining_block_id = 0", p.TxUserID, currency_id).Int64() if err != nil { return p.ErrInfo(err) } } // если уже есть repaid для данной валюты, то просто приплюсуем к сумме if repaidPromisedAmountId > 0 { data, err := p.OneRow("SELECT * FROM promised_amount WHERE id = ?", repaidPromisedAmountId).String() if err != nil { return p.ErrInfo(err) } logId, err := p.ExecSqlGetLastInsertId("INSERT INTO log_promised_amount ( amount, tdc_amount, tdc_amount_update, cash_request_in_block_id, block_id, prev_log_id ) VALUES ( ?, ?, ?, ?, ?, ? )", "log_id", data["amount"], data["tdc_amount"], data["tdc_amount_update"], data["cash_request_in_block_id"], p.BlockData.BlockId, data["log_id"]) if err != nil { return p.ErrInfo(err) } // tdc_amount не пересчитываются, т.к. пока есть cash_requests с pending, они не растут err = p.ExecSql("UPDATE promised_amount SET amount = amount + ?, tdc_amount = ?, tdc_amount_update = ?, cash_request_in_block_id = ?, log_id = ? WHERE id = ?", amount, (utils.StrToFloat64(data["tdc_amount"]) + amount), p.BlockData.Time, p.BlockData.BlockId, logId, repaidPromisedAmountId) if err != nil { return p.ErrInfo(err) } } else { err = p.ExecSql("INSERT INTO promised_amount ( user_id, amount, currency_id, start_time, status, tdc_amount, tdc_amount_update, cash_request_in_block_id ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )", p.TxUserID, amount, currency_id, p.BlockData.Time, promisedAmountStatus, amount, p.BlockData.Time, p.BlockData.BlockId) if err != nil { return p.ErrInfo(err) } } // теперь нужно вычесть зачисленную сумму на repaid из mining data, err := p.OneRow("SELECT * FROM promised_amount WHERE user_id = ? AND currency_id = ? AND status = 'mining' AND del_block_id = 0 AND del_mining_block_id = 0", p.TxUserID, currency_id).String() if err != nil { return p.ErrInfo(err) } logId, err := p.ExecSqlGetLastInsertId("INSERT INTO log_promised_amount ( amount, tdc_amount, tdc_amount_update, cash_request_in_block_id, block_id, prev_log_id ) VALUES ( ?, ?, ?, ?, ?, ? )", "log_id", data["amount"], data["tdc_amount"], data["tdc_amount_update"], data["cash_request_in_block_id"], p.BlockData.BlockId, data["log_id"]) if err != nil { return p.ErrInfo(err) } // вычитаем из mining то, что начислили выше на repaid // tdc_amount не пересчитываются, т.к. пока есть cash_requests с pending, они не растут err = p.ExecSql("UPDATE promised_amount SET amount = amount - ?, tdc_amount = ?, tdc_amount_update = ?, cash_request_in_block_id = ?, log_id = ? WHERE id = ?", amount, data["tdc_amount"], p.BlockData.Time, p.BlockData.BlockId, logId, data["id"]) if err != nil { return p.ErrInfo(err) } // обновим сумму на кошельке отправителя, вычтя amount и залогировав предыдущее значение err = p.updateSenderWallet(from_user_id, currency_id, amount, 0, "cash_request", p.TxMaps.Int64["cash_request_id"], p.TxUserID, "cash_request", "decrypted") if err != nil { return p.ErrInfo(err) } // Отмечаем, что данный cash_requests погашен. err = p.ExecSql("UPDATE cash_requests SET status = 'approved' WHERE id = ?", p.TxMaps.Int64["cash_request_id"]) if err != nil { return p.ErrInfo(err) } // возможно, больше нет mining ни по одной валюте (кроме WOC) у данного юзера forRepaidCurrencyIds, err := p.GetList("SELECT currency_id FROM promised_amount WHERE status = 'mining' AND user_id = ? AND amount > 0 AND currency_id > 1 AND del_block_id = 0 AND del_mining_block_id = 0", p.TxUserID).Int64() if err != nil { return p.ErrInfo(err) } var forRepaidCurrencyIdsNew []int64 for _, currencyId := range forRepaidCurrencyIds { // либо сумма погашенных стала >= максимальной обещанной, т.к. в этом случае прислать этому юзеру cash_request_out будет невозможно maxPromisedAmount, err := p.GetMaxPromisedAmount(currencyId) if err != nil { return p.ErrInfo(err) } repaidAmount, err := p.GetRepaidAmount(currencyId, p.TxUserID) if err != nil { return p.ErrInfo(err) } if repaidAmount < maxPromisedAmount { forRepaidCurrencyIdsNew = append(forRepaidCurrencyIdsNew, currencyId) } } if len(forRepaidCurrencyIdsNew) == 0 { // просроченным cash_requests ставим for_repaid_del_block_id, чтобы было ясно, что юзер не имеет долгов, и его TDC должны расти err = p.ExecSql("UPDATE cash_requests SET for_repaid_del_block_id = ? WHERE to_user_id = ? AND time < ? AND for_repaid_del_block_id = 0", p.BlockData.BlockId, p.TxUserID, (p.BlockData.Time - p.Variables.Int64["cash_request_time"])) if err != nil { return p.ErrInfo(err) } } existsRequests := p.CheckCashRequests(p.TxUserID) // возможно, что данный cash_requests с approved был единственный, и последующий вызов метода mining начислит новые TDC в соответствии с имеющимся % роста,. значит необходимо обновить tdc_amount и tdc_amount_update if len(forRepaidCurrencyIdsNew) == 0 || existsRequests == nil { // у юзера нет долгов, нужно ставить ему cash_request_out_time=0 err = p.updPromisedAmounts(p.TxUserID, false, true, 0) if err != nil { return p.ErrInfo(err) } } else { // для того, чтобы было проще делать rollback пишем время cash_request_out_time, хотя по сути cash_request_out_time будет таким же каким и был cashRequestOutTime, err := p.Single("SELECT cash_request_out_time FROM promised_amount WHERE user_id = ? AND cash_request_out_time > 0", p.TxUserID).Int64() if err != nil { return p.ErrInfo(err) } err = p.updPromisedAmounts(p.TxUserID, false, true, cashRequestOutTime) if err != nil { return p.ErrInfo(err) } } cashRequestsDataFromUserId, err := p.Single("SELECT from_user_id FROM cash_requests WHERE id = ?", p.TxMaps.Int64["cash_request_id"]).Int64() if err != nil { return p.ErrInfo(err) } // проверим, не наш ли это user_id _, myBlockId, myPrefix, myUserIds, err := p.GetMyUserId(p.TxMaps.Int64["to_user_id"]) if err != nil { return err } if (utils.InSliceInt64(p.TxUserID, myUserIds) || utils.InSliceInt64(cashRequestsDataFromUserId, myUserIds)) && myBlockId <= p.BlockData.BlockId { collective, err := p.GetCommunityUsers() if err != nil { return err } if len(collective) > 0 && utils.InSliceInt64(cashRequestsDataFromUserId, myUserIds) { // наш юзер - это отправитель _out myPrefix = utils.Int64ToStr(cashRequestsDataFromUserId) + "_" } else if len(collective) > 0 && utils.InSliceInt64(p.TxUserID, myUserIds) { // наш юзер - это отправитель _in myPrefix = utils.Int64ToStr(p.TxUserID) + "_" } else { myPrefix = "" } // обновим таблу, отметив, что мы отдали деньги err = p.ExecSql("UPDATE "+myPrefix+"my_cash_requests SET status = 'approved' WHERE cash_request_id = ?", p.TxMaps.Int64["cash_request_id"]) if err != nil { return p.ErrInfo(err) } } return nil }
func (p *Parser) NewMaxPromisedAmountsFront() error { err := p.generalCheck() if err != nil { return p.ErrInfo(err) } // является ли данный юзер майнером err = p.checkMiner(p.TxUserID) if err != nil { return p.ErrInfo(err) } nodePublicKey, err := p.GetNodePublicKey(p.TxUserID) if err != nil { return p.ErrInfo(err) } if len(nodePublicKey) == 0 { return p.ErrInfo("incorrect user_id") } allMaxAmounts := utils.GetAllMaxPromisedAmount() if err != nil { return p.ErrInfo(err) } totalCountCurrencies, err := p.GetCountCurrencies() if err != nil { return p.ErrInfo(err) } // проверим, верно ли указаны ID валют currencyList := make(map[string]int64) err = json.Unmarshal(p.TxMap["new_max_promised_amounts"], ¤cyList) if err != nil { return p.ErrInfo(err) } currencyIdsSql := "" var countCurrency int64 for currencyId, amount := range currencyList { if !utils.CheckInputData(currencyId, "int") { return p.ErrInfo("currencyId") } currencyIdsSql += currencyId + "," countCurrency++ if !utils.InSliceInt64(amount, allMaxAmounts) { return p.ErrInfo("incorrect amount") } } currencyIdsSql = currencyIdsSql[0 : len(currencyIdsSql)-1] if countCurrency == 0 { return p.ErrInfo("countCurrency") } count, err := p.Single("SELECT count(id) FROM currency WHERE id IN (" + currencyIdsSql + ")").Int64() if err != nil { return p.ErrInfo(err) } if count != countCurrency { return p.ErrInfo("count != countCurrency") } forSign := fmt.Sprintf("%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["new_max_promised_amounts"]) CheckSignResult, err := utils.CheckSign([][]byte{nodePublicKey}, forSign, p.TxMap["sign"], true) if err != nil { return p.ErrInfo(err) } if !CheckSignResult { return p.ErrInfo("incorrect sign") } // проверим, прошло ли 2 недели с момента последнего обновления pctTime, err := p.Single("SELECT max(time) FROM max_promised_amounts").Int64() if err != nil { return p.ErrInfo(err) } if p.TxTime-pctTime <= p.Variables.Int64["new_max_promised_amount"] { return p.ErrInfo("14 day error") } // берем все голоса maxPromisedAmountVotes := make(map[int64][]map[int64]int64) rows, err := p.Query("SELECT currency_id, amount, count(user_id) as votes FROM votes_max_promised_amount GROUP BY currency_id, amount ORDER BY currency_id, amount ASC") if err != nil { return p.ErrInfo(err) } defer rows.Close() for rows.Next() { var currency_id, amount, votes int64 err = rows.Scan(¤cy_id, &amount, &votes) if err != nil { return p.ErrInfo(err) } maxPromisedAmountVotes[currency_id] = append(maxPromisedAmountVotes[currency_id], map[int64]int64{amount: votes}) //fmt.Println("currency_id", currency_id) } NewMaxPromisedAmountsVotes := make(map[string]int64) for currencyId, amountsAndVotes := range maxPromisedAmountVotes { NewMaxPromisedAmountsVotes[utils.Int64ToStr(currencyId)] = utils.GetMaxVote(amountsAndVotes, 0, totalCountCurrencies, 10) } jsonData, err := json.Marshal(NewMaxPromisedAmountsVotes) if err != nil { return p.ErrInfo(err) } if string(p.TxMap["new_max_promised_amounts"]) != string(jsonData) { return p.ErrInfo("p.TxMap[new_max_promised_amounts] != jsonData " + string(p.TxMap["new_max_promised_amounts"]) + "!=" + string(jsonData)) } return nil }
func (p *Parser) NewReductionFront() error { err := p.generalCheck() if err != nil { return p.ErrInfo(err) } // является ли данный юзер майнером err = p.checkMiner(p.TxUserID) if err != nil { return p.ErrInfo(err) } verifyData := map[string]string{"currency_id": "int", "pct": "int"} err = p.CheckInputData(verifyData) if err != nil { return p.ErrInfo(err) } if !utils.InSliceInt64(utils.BytesToInt64(p.TxMap["pct"]), consts.ReductionDC) { return p.ErrInfo("incorrect pct") } if p.BlockData != nil && p.BlockData.BlockId < 85849 { // для всех тр-ий из старых блоков просто присваем manual, т.к. там не было других типов p.TxMaps.String["reduction_type"] = "manual" } else { verifyData := map[string]string{"reduction_type": "reduction_type"} err = p.CheckInputData(verifyData) if err != nil { return p.ErrInfo(err) } } nodePublicKey, err := p.GetNodePublicKey(p.TxUserID) if err != nil { return p.ErrInfo(err) } if len(nodePublicKey) == 0 { return p.ErrInfo("incorrect user_id") } currencyId, err := p.CheckCurrencyId(p.TxMaps.Int64["currency_id"]) if err != nil { return p.ErrInfo(err) } if currencyId == 0 { return p.ErrInfo("incorrect currency_id") } forSign := "" if p.BlockData != nil && p.BlockData.BlockId < 85849 { forSign = fmt.Sprintf("%s,%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["currency_id"], p.TxMap["pct"]) } else { forSign = fmt.Sprintf("%s,%s,%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["currency_id"], p.TxMap["pct"], p.TxMap["reduction_type"]) } CheckSignResult, err := utils.CheckSign([][]byte{nodePublicKey}, forSign, p.TxMap["sign"], true) if err != nil { return p.ErrInfo(err) } if !CheckSignResult { return p.ErrInfo("incorrect sign") } if p.TxMaps.String["reduction_type"] == "manual" { // проверим, прошло ли 2 недели с момента последнего reduction reductionTime, err := p.Single("SELECT max(time) FROM reduction WHERE currency_id = ? AND type = 'manual'", p.TxMaps.Int64["currency_id"]).Int64() if err != nil { return p.ErrInfo(err) } if p.TxTime-reductionTime <= p.Variables.Int64["reduction_period"] { return p.ErrInfo("reduction_period error") } } else { reductionTime, err := p.Single("SELECT max(time) FROM reduction WHERE currency_id = ? AND type = 'auto'", p.TxMaps.Int64["currency_id"]).Int64() if err != nil { return p.ErrInfo(err) } // или 48 часов, если это авто-урезание if p.TxTime-reductionTime <= consts.AUTO_REDUCTION_PERIOD { return p.ErrInfo("reduction_period error") } } if p.TxMaps.String["reduction_type"] == "manual" { // получаем кол-во обещанных сумм у разных юзеров по каждой валюте. start_time есть только у тех, у кого статус mining/repaid promisedAmount, err := p.DCDB.GetMap(` SELECT currency_id, count(user_id) as count FROM ( SELECT currency_id, user_id FROM promised_amount WHERE start_time < ? AND del_block_id = 0 AND del_mining_block_id = 0 AND status IN ('mining', 'repaid') GROUP BY user_id, currency_id ) as t1 GROUP BY currency_id`, "currency_id", "count", (p.TxTime - p.Variables.Int64["min_hold_time_promise_amount"])) if err != nil { return p.ErrInfo(err) } if len(promisedAmount[utils.Int64ToStr(p.TxMaps.Int64["currency_id"])]) == 0 { return p.ErrInfo("empty promised_amount") } // берем все голоса юзеров по данной валюте countVotes, err := p.Single("SELECT count(currency_id) as votes FROM votes_reduction WHERE time > ? AND currency_id = ? AND pct = ?", (p.TxTime - p.Variables.Int64["reduction_period"]), p.TxMaps.Int64["currency_id"], p.TxMaps.String["pct"]).Int64() if err != nil { return p.ErrInfo(err) } if countVotes < utils.StrToInt64(promisedAmount[utils.Int64ToStr(p.TxMaps.Int64["currency_id"])])/2 { return p.ErrInfo("incorrect count_votes") } } else if p.TxMaps.String["reduction_type"] == "promised_amount" { // и недопустимо для WOC if p.TxMaps.Int64["currency_id"] == 1 { return p.ErrInfo("WOC AUTO_REDUCTION_CASHs") } // проверим, есть ли хотябы 1000 юзеров, у которых на кошелках есть или была данная валюты countUsers, err := p.Single("SELECT count(user_id) FROM wallets WHERE currency_id = ?", p.TxMaps.Int64["currency_id"]).Int64() if err != nil { return p.ErrInfo(err) } if countUsers < consts.AUTO_REDUCTION_PROMISED_AMOUNT_MIN { return p.ErrInfo(fmt.Sprintf("AUTO_REDUCTION_PROMISED_AMOUNT_MIN %v < %v", countUsers, consts.AUTO_REDUCTION_PROMISED_AMOUNT_MIN)) } // получаем кол-во DC на кошельках sumWallets, err := p.Single("SELECT sum(amount) FROM wallets WHERE currency_id = ?", p.TxMaps.Int64["currency_id"]).Float64() if err != nil { return p.ErrInfo(err) } // получаем кол-во TDC на обещанных суммах sumPromisedAmountTdc, err := p.Single("SELECT sum(tdc_amount) FROM promised_amount WHERE currency_id = ?", p.TxMaps.Int64["currency_id"]).Float64() if err != nil { return p.ErrInfo(err) } sumWallets += sumPromisedAmountTdc // получаем суммы обещанных сумм. при этом не берем те, что имеют просроченные cash_request_out sumPromisedAmount, err := p.Single("SELECT sum(amount) FROM promised_amount WHERE status = 'mining' AND del_block_id = 0 AND del_mining_block_id = 0 AND currency_id = ? AND (cash_request_out_time = 0 OR cash_request_out_time > ?)", p.TxMaps.Int64["currency_id"], (p.TxTime - p.Variables.Int64["cash_request_time"])).Float64() if err != nil { return p.ErrInfo(err) } log.Debug("sumPromisedAmount", sumPromisedAmount) // если обещанных сумм менее чем 100% от объема DC на кошельках, то всё норм, если нет - ошибка if sumPromisedAmount >= sumWallets*float64(consts.AUTO_REDUCTION_PROMISED_AMOUNT_PCT) { return p.ErrInfo(fmt.Sprintf("error reduction $sum_promised_amount %v >= %v * %v", sumPromisedAmount, sumWallets, consts.AUTO_REDUCTION_PROMISED_AMOUNT_PCT)) } } return nil }
func (t *TcpServer) Type6() { /** - проверяем, находится ли отправитель на одном с нами уровне - получаем block_id, user_id, mrkl_root, signature - если хэш блока меньше того, что есть у нас в табле testblock, то смотртим, есть ли такой же хэш тр-ий, - если отличается, то загружаем блок от отправителя - если не отличается, то просто обновляем хэш блока у себя данные присылает демон testblockDisseminator */ currentBlockId, err := t.GetBlockId() if err != nil { log.Error("%v", utils.ErrInfo(err)) return } if currentBlockId == 0 { log.Debug("%v", utils.ErrInfo("currentBlockId == 0")) return } buf := make([]byte, 4) _, err = t.Conn.Read(buf) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } size := utils.BinToDec(buf) log.Debug("size: %v", size) if size < 10485760 { binaryData := make([]byte, size) //binaryData, err = ioutil.ReadAll(t.Conn) _, err = io.ReadFull(t.Conn, binaryData) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("binaryData: %x", binaryData) newTestblockBlockId := utils.BinToDecBytesShift(&binaryData, 4) newTestblockTime := utils.BinToDecBytesShift(&binaryData, 4) newTestblockUserId := utils.BinToDecBytesShift(&binaryData, 4) newTestblockMrklRoot := utils.BinToHex(utils.BytesShift(&binaryData, 32)) newTestblockSignatureHex := utils.BinToHex(utils.BytesShift(&binaryData, utils.DecodeLength(&binaryData))) log.Debug("newTestblockBlockId: %v", newTestblockBlockId) log.Debug("newTestblockTime: %v", newTestblockTime) log.Debug("newTestblockUserId: %v", newTestblockUserId) log.Debug("newTestblockMrklRoot: %s", newTestblockMrklRoot) log.Debug("newTestblockSignatureHex: %s", newTestblockSignatureHex) if !utils.CheckInputData(newTestblockBlockId, "int") { log.Debug("%v", utils.ErrInfo("incorrect newTestblockBlockId")) return } if !utils.CheckInputData(newTestblockTime, "int") { log.Debug("%v", utils.ErrInfo("incorrect newTestblockTime")) return } if !utils.CheckInputData(newTestblockUserId, "int") { log.Debug("%v", utils.ErrInfo("incorrect newTestblockUserId")) return } if !utils.CheckInputData(newTestblockMrklRoot, "sha256") { log.Debug("%v", utils.ErrInfo("incorrect newTestblockMrklRoot")) return } /* * Проблема одновременных попыток локнуть. Надо попробовать без локов * */ //t.DbLockGate("6") exists, err := t.Single(` SELECT block_id FROM testblock WHERE status = 'active' `).Int64() if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } if exists == 0 { t.PrintSleep(utils.ErrInfo("null testblock"), 0) return } //prevBlock, myUserId, myMinerId, currentUserId, level, levelsRange, err := t.TestBlock() prevBlock, _, _, _, level, levelsRange, err := t.TestBlock() if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } nodesIds := utils.GetOurLevelNodes(level, levelsRange) log.Debug("nodesIds: %v ", nodesIds) log.Debug("prevBlock: %v ", prevBlock) log.Debug("level: %v ", level) log.Debug("levelsRange: %v ", levelsRange) log.Debug("newTestblockBlockId: %v ", newTestblockBlockId) // проверим, верный ли ID блока if newTestblockBlockId != prevBlock.BlockId+1 { t.PrintSleep(utils.ErrInfo(fmt.Sprintf("newTestblockBlockId != prevBlock.BlockId+1 %d!=%d+1", newTestblockBlockId, prevBlock.BlockId)), 1) return } // проверим, есть ли такой майнер minerId, err := t.Single("SELECT miner_id FROM miners_data WHERE user_id = ?", newTestblockUserId).Int64() if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } if minerId == 0 { t.PrintSleep(utils.ErrInfo("minerId == 0"), 0) return } log.Debug("minerId: %v ", minerId) // проверим, точно ли отправитель с нашего уровня if !utils.InSliceInt64(minerId, nodesIds) { t.PrintSleep(utils.ErrInfo("!InSliceInt64(minerId, nodesIds)"), 0) return } // допустимая погрешность во времени генерации блока maxErrorTime := t.variables.Int64["error_time"] // получим значения для сна sleep, err := t.GetGenSleep(prevBlock, level) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } // исключим тех, кто сгенерил блок слишком рано if prevBlock.Time+sleep-newTestblockTime > maxErrorTime { t.PrintSleep(utils.ErrInfo("prevBlock.Time + sleep - newTestblockTime > maxErrorTime"), 0) return } // исключим тех, кто сгенерил блок с бегущими часами if newTestblockTime > utils.Time() { t.PrintSleep(utils.ErrInfo("newTestblockTime > Time()"), 0) return } // получим хэш заголовка newHeaderHash := utils.DSha256(fmt.Sprintf("%v,%v,%v", newTestblockUserId, newTestblockBlockId, prevBlock.HeadHash)) myTestblock, err := t.OneRow(` SELECT block_id, user_id, hex(mrkl_root) as mrkl_root, hex(signature) as signature FROM testblock WHERE status = 'active' `).String() if len(myTestblock) > 0 { if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } // получим хэш заголовка myHeaderHash := utils.DSha256(fmt.Sprintf("%v,%v,%v", myTestblock["user_id"], myTestblock["block_id"], prevBlock.HeadHash)) // у кого меньше хэш, тот и круче hash1 := big.NewInt(0) hash1.SetString(string(newHeaderHash), 16) hash2 := big.NewInt(0) hash2.SetString(string(myHeaderHash), 16) log.Debug("%v", hash1.Cmp(hash2)) //if HexToDecBig(newHeaderHash) > string(myHeaderHash) { if hash1.Cmp(hash2) == 1 { t.PrintSleep(utils.ErrInfo(fmt.Sprintf("newHeaderHash > myHeaderHash (%s > %s)", newHeaderHash, myHeaderHash)), 0) return } /* т.к. на данном этапе в большинстве случаев наш текущий блок будет заменен, * то нужно парсить его, рассылать другим нодам и дождаться окончания проверки */ err = t.ExecSql("UPDATE testblock SET status = 'pending'") if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } } // если отличается, то загружаем недостающии тр-ии от отправителя if string(newTestblockMrklRoot) != myTestblock["mrkl_root"] { log.Debug("download new tx") sendData := "" // получим все имеющиеся у нас тр-ии, которые еще не попали в блоки txArray, err := t.GetMap(`SELECT hex(hash) as hash, data FROM transactions`, "hash", "data") if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } for hash, _ := range txArray { sendData += hash } err = utils.WriteSizeAndData([]byte(sendData), t.Conn) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } /* в ответ получаем: BLOCK_ID 4 TIME 4 USER_ID 5 SIGN от 128 до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT Размер всех тр-ий, размер 1 тр-ии, тело тр-ии. Хэши три-ий (порядок тр-ий) */ buf := make([]byte, 4) _, err = t.Conn.Read(buf) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } dataSize := utils.BinToDec(buf) log.Debug("dataSize %d", dataSize) // и если данных менее 10мб, то получаем их if dataSize < 10485760 { binaryData := make([]byte, dataSize) //binaryData, err = ioutil.ReadAll(t.Conn) _, err = io.ReadFull(t.Conn, binaryData) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } // Разбираем полученные бинарные данные newTestblockBlockId := utils.BinToDecBytesShift(&binaryData, 4) newTestblockTime := utils.BinToDecBytesShift(&binaryData, 4) newTestblockUserId := utils.BinToDecBytesShift(&binaryData, 5) newTestblockSignature := utils.BytesShift(&binaryData, utils.DecodeLength(&binaryData)) log.Debug("newTestblockBlockId %v", newTestblockBlockId) log.Debug("newTestblockTime %v", newTestblockTime) log.Debug("newTestblockUserId %v", newTestblockUserId) log.Debug("newTestblockSignature %x", newTestblockSignature) // недостающие тр-ии length := utils.DecodeLength(&binaryData) // размер всех тр-ий txBinary := utils.BytesShift(&binaryData, length) for { // берем по одной тр-ии length := utils.DecodeLength(&txBinary) // размер всех тр-ий if length == 0 { break } log.Debug("length %d", length) tx := utils.BytesShift(&txBinary, length) log.Debug("tx %x", tx) txArray[string(utils.Md5(tx))] = string(tx) } // порядок тр-ий var orderHashArray []string for { orderHashArray = append(orderHashArray, string(utils.BinToHex(utils.BytesShift(&binaryData, 16)))) if len(binaryData) == 0 { break } } // сортируем и наши и полученные транзакции var transactions []byte for _, txMd5 := range orderHashArray { transactions = append(transactions, utils.EncodeLengthPlusData([]byte(txArray[txMd5]))...) } // формируем блок, который далее будем тщательно проверять /* Заголовок (от 143 до 527 байт ) TYPE (0-блок, 1-тр-я) 1 BLOCK_ID 4 TIME 4 USER_ID 5 LEVEL 1 SIGN от 128 до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT Далее - тело блока (Тр-ии) */ newBlockIdBinary := utils.DecToBin(newTestblockBlockId, 4) timeBinary := utils.DecToBin(newTestblockTime, 4) userIdBinary := utils.DecToBin(newTestblockUserId, 5) levelBinary := utils.DecToBin(level, 1) newBlockHeader := utils.DecToBin(0, 1) // 0 - это блок newBlockHeader = append(newBlockHeader, newBlockIdBinary...) newBlockHeader = append(newBlockHeader, timeBinary...) newBlockHeader = append(newBlockHeader, userIdBinary...) newBlockHeader = append(newBlockHeader, levelBinary...) // $level пишем, чтобы при расчете времени ожидания в следующем блоке не пришлось узнавать, какой был max_miner_id newBlockHeader = append(newBlockHeader, utils.EncodeLengthPlusData(newTestblockSignature)...) newBlockHex := utils.BinToHex(append(newBlockHeader, transactions...)) // и передаем блок для обратотки через демон queue_parser_testblock // т.к. есть запросы к log_time_, а их можно выполнять только по очереди err = t.ExecSql(`DELETE FROM queue_testblock WHERE hex(head_hash) = ?`, newHeaderHash) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } log.Debug("INSERT INTO queue_testblock (head_hash, data) VALUES (%s, %s)", newHeaderHash, newBlockHex) err = t.ExecSql(`INSERT INTO queue_testblock (head_hash, data) VALUES ([hex], [hex])`, newHeaderHash, newBlockHex) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } } } else { // если всё нормально, то пишем в таблу testblock новые данные exists, err := t.Single(`SELECT block_id FROM testblock`).Int64() if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } if exists == 0 { err = t.ExecSql(`INSERT INTO testblock (block_id, time, level, user_id, header_hash, signature, mrkl_root) VALUES (?, ?, ?, ?, [hex], [hex], [hex])`, newTestblockBlockId, newTestblockTime, level, newTestblockUserId, string(newHeaderHash), newTestblockSignatureHex, string(newTestblockMrklRoot)) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } } else { err = t.ExecSql(` UPDATE testblock SET time = ?, user_id = ?, header_hash = [hex], signature = [hex] `, newTestblockTime, newTestblockUserId, string(newHeaderHash), string(newTestblockSignatureHex)) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } } } err = t.ExecSql("UPDATE testblock SET status = 'active'") if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } //t.DbUnlockGate("6") } }
func (p *Parser) VotesComplexFront() error { err := p.generalCheck() if err != nil { return p.ErrInfo(err) } // является ли данный юзер майнером err = p.checkMiner(p.TxUserID) if err != nil { return p.ErrInfo(err) } var txTime int64 if p.BlockData != nil { txTime = p.BlockData.Time } else { txTime = time.Now().Unix() - 30 } // прошло ли 30 дней с момента регистрации майнера err = p.checkMinerNewbie() if err != nil { return p.ErrInfo(err) } forSign := fmt.Sprintf("%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["json_data"]) CheckSignResult, err := utils.CheckSign(p.PublicKeys, forSign, p.TxMap["sign"], false) if err != nil { return p.ErrInfo(err) } if !CheckSignResult { return p.ErrInfo("incorrect sign") } currencyVotes := make(map[string][]float64) var doubleCheck []int64 // раньше не было рефских if p.BlockData == nil || p.BlockData.BlockId > 77951 { vComplex, err := makeVcomplex(p.TxMap["json_data"]) if err != nil { return p.ErrInfo(err) } if vComplex.Referral == nil { return p.ErrInfo("!Referral") } if vComplex.Currency == nil { return p.ErrInfo("!Currency") } if p.BlockData == nil || p.BlockData.BlockId > 153750 { if vComplex.Admin > 0 { adminUserId, err := p.Single("SELECT user_id FROM users WHERE user_id = ?", vComplex.Admin).Int64() if err != nil { return p.ErrInfo(err) } if adminUserId == 0 { return p.ErrInfo("incorrect admin user_id") } } } if !utils.CheckInputData(vComplex.Referral["first"], "referral") || !utils.CheckInputData(vComplex.Referral["second"], "referral") || !utils.CheckInputData(vComplex.Referral["third"], "referral") { return p.ErrInfo("incorrect referral") } currencyVotes = vComplex.Currency } else { vComplex := make(map[string][]float64) err = json.Unmarshal(p.TxMap["json_data"], &vComplex) if err != nil { return p.ErrInfo(err) } currencyVotes = vComplex } for currencyId, data := range currencyVotes { if !utils.CheckInputData(currencyId, "int") { return p.ErrInfo("incorrect currencyId") } // проверим, что нет дублей if utils.InSliceInt64(utils.StrToInt64(currencyId), doubleCheck) { return p.ErrInfo("double currencyId") } doubleCheck = append(doubleCheck, utils.StrToInt64(currencyId)) // есть ли такая валюта currencyId_, err := p.Single("SELECT id FROM currency WHERE id = ?", currencyId).Int64() if err != nil { return p.ErrInfo(err) } if currencyId_ == 0 { return p.ErrInfo("incorrect currencyId") } // у юзера по данной валюте должна быть обещанная сумма, которая имеет статус mining/repaid и находится с таким статусом >90 дней id, err := p.Single("SELECT id FROM promised_amount WHERE currency_id = ? AND user_id = ? AND status IN ('mining', 'repaid') AND start_time < ? AND start_time > 0 AND del_block_id = 0 AND del_mining_block_id = 0", currencyId, p.TxUserID, (txTime - p.Variables.Int64["min_hold_time_promise_amount"])).Int64() if err != nil { return p.ErrInfo(err) } if id == 0 { return p.ErrInfo("incorrect currencyId") } // если по данной валюте еще не набралось >1000 майнеров, то за неё голосовать нельзя. countMiners, err := p.Single(` SELECT count(*) FROM (SELECT user_id FROM promised_amount WHERE start_time < ? AND del_block_id = 0 AND status IN ('mining', 'repaid') AND currency_id = ? AND del_block_id = 0 AND del_mining_block_id = 0 GROUP BY user_id) as t1`, (txTime - p.Variables.Int64["min_hold_time_promise_amount"]), currencyId).Int64() if err != nil { return p.ErrInfo(err) } if countMiners < p.Variables.Int64["min_miners_of_voting"] { return p.ErrInfo("countMiners") } if len(data) != 5 { return p.ErrInfo("incorrect data") } if !utils.CheckPct(data[0]) { return p.ErrInfo("incorrect miner_pct") } if !utils.CheckPct(data[1]) { return p.ErrInfo("incorrect user_pct") } // max promise amount if !utils.InSliceInt64(int64(data[2]), utils.GetAllMaxPromisedAmount()) { log.Debug("%v", int64(data[2])) log.Debug("%v", utils.GetAllMaxPromisedAmount()) return p.ErrInfo("incorrect max promised amount") } totalCountCurrencies, err := p.Single("SELECT count(id) FROM currency").Int64() if err != nil { return p.ErrInfo(err) } // max other currency 0/1/2/3/.../76 if !utils.CheckInputData(int(data[3]), "int") || int64(data[3]) > totalCountCurrencies { return p.ErrInfo(fmt.Sprintf("incorrect max other currency %d > %d", data[3], totalCountCurrencies)) } currencyCount, err := p.Single("SELECT count(id) FROM currency").Int64() if err != nil { return p.ErrInfo(err) } if int64(data[3]) > (currencyCount - 1) { return p.ErrInfo("incorrect max other currency") } // reduction 10/25/50/90 if !utils.InSliceInt64(int64(data[4]), consts.ReductionDC) { return p.ErrInfo("incorrect reduction") } } err = p.limitRequest(p.Variables.Int64["limit_votes_complex"], "votes_complex", p.Variables.Int64["limit_votes_complex_period"]) if err != nil { return p.ErrInfo(err) } return nil }