Пример #1
0
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)

}
Пример #2
0
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, &currency_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
}
Пример #3
0
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)
}
Пример #4
0
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, &currency_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
}
Пример #5
0
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"], &currencyList)
	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(&currency_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
}
Пример #6
0
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
}
Пример #7
0
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")
	}
}
Пример #8
0
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
}