コード例 #1
0
ファイル: new_photo.go プロジェクト: dzyk/dcoin-go
func (c *Controller) NewPhoto() (string, error) {

	c.r.ParseForm()

	userId := int64(utils.StrToFloat64(c.r.FormValue("user_id")))

	data, err := c.OneRow("SELECT photo_block_id, photo_max_miner_id, miners_keepers FROM miners_data WHERE user_id = ?", userId).String()
	if err != nil {
		return "", utils.ErrInfo(err)
	}

	// получим ID майнеров, у которых лежат фото нужного нам юзера
	minersIds := utils.GetMinersKeepers(data["photo_block_id"], data["photo_max_miner_id"], data["miners_keepers"], true)

	// берем 1 случайный из 10-и ID майнеров
	k := utils.RandInt(0, len(minersIds))
	minerId := minersIds[k]
	host, err := c.Single("SELECT http_host FROM miners_data WHERE miner_id  =  ?", minerId).String()
	if err != nil {
		return "", utils.ErrInfo(err)
	}

	result, err := json.Marshal(map[string]string{"face": host + "public/face_" + utils.Int64ToStr(userId) + ".jpg", "profile": host + "public/profile_" + utils.Int64ToStr(userId) + ".jpg"})
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	return string(result), nil
}
コード例 #2
0
ファイル: node_voting.go プロジェクト: dzyk/dcoin-go
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)

}
コード例 #3
0
ファイル: new_user.go プロジェクト: dzyk/dcoin-go
func (c *Controller) NewUser() (string, error) {

	txType := "NewUser"
	txTypeId := utils.TypeInt(txType)
	timeNow := utils.Time()

	param := utils.ParamType{X: 176, Y: 100, Width: 100, Bg_path: "static/img/k_bg.png"}

	refPhotos := make(map[int64][]string)
	myRefsKeys := make(map[int64]map[string]string)
	if c.SessRestricted == 0 {
		join := c.MyPrefix + `my_new_users.user_id`
		if c.ConfigIni["db_type"] == "sqlite" || c.ConfigIni["db_type"] == "postgresql" {
			join = `"` + c.MyPrefix + `my_new_users".user_id`
		}
		rows, err := c.Query(c.FormatQuery(`
				SELECT users.user_id,	private_key,  log_id
				FROM ` + c.MyPrefix + `my_new_users
				LEFT JOIN users ON users.user_id = ` + join + `
				WHERE status = 'approved'
				`))
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		defer rows.Close()
		for rows.Next() {
			var user_id, log_id int64
			var private_key string
			err = rows.Scan(&user_id, &private_key, &log_id)
			if err != nil {
				return "", utils.ErrInfo(err)
			}
			// проверим, не сменил ли уже юзер свой ключ
			StrUserId := utils.Int64ToStr(user_id)
			if log_id != 0 {
				myRefsKeys[user_id] = map[string]string{"user_id": StrUserId}
			} else {
				myRefsKeys[user_id] = map[string]string{"user_id": StrUserId, "private_key": private_key}
				md5 := string(utils.Md5(private_key))
				kPath := *utils.Dir + "/public/" + md5[0:16]
				kPathPng := kPath + ".png"
				kPathTxt := kPath + ".txt"
				if _, err := os.Stat(kPathPng); os.IsNotExist(err) {
					privKey := strings.Replace(private_key, "-----BEGIN RSA PRIVATE KEY-----", "", -1)
					privKey = strings.Replace(privKey, "-----END RSA PRIVATE KEY-----", "", -1)
					_, err = utils.KeyToImg(privKey, kPathPng, user_id, c.TimeFormat, param)
					if err != nil {
						return "", utils.ErrInfo(err)
					}
					err := ioutil.WriteFile(kPathTxt, []byte(privKey), 0644)
					if err != nil {
						return "", utils.ErrInfo(err)
					}
					/*$gd = key_to_img($private_key, $param, $row['user_id']);
					imagepng($gd, $k_path_png);
					file_put_contents($k_path_txt, trim($private_key));*/
				}
			}
		}
	}

	refs := make(map[int64]map[int64]float64)
	// инфа по рефам юзера
	rows, err := c.Query(c.FormatQuery(`
			SELECT referral, sum(amount) as amount, currency_id
			FROM referral_stats
			WHERE user_id = ?
			GROUP BY currency_id,  referral
			`), c.SessUserId)
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	defer rows.Close()
	for rows.Next() {
		var referral, currency_id int64
		var amount float64
		err = rows.Scan(&referral, &amount, &currency_id)
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		refs[referral] = map[int64]float64{currency_id: amount}
	}

	myRefsAmounts := make(map[int64]myRefsType)
	for refUserId, refData := range refs {
		data, err := c.OneRow("SELECT * FROM miners_data WHERE user_id  =  ?", refUserId).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		// получим ID майнеров, у которых лежат фото нужного нам юзера
		if len(data) == 0 {
			continue
		}
		minersIds := utils.GetMinersKeepers(data["photo_block_id"], data["photo_max_miner_id"], data["miners_keepers"], true)
		if len(minersIds) > 0 {
			hosts, err := c.GetList("SELECT http_host FROM miners_data WHERE miner_id  IN (" + utils.JoinInts(minersIds, ",") + ")").String()
			if err != nil {
				return "", utils.ErrInfo(err)
			}
			myRefsAmounts[refUserId] = myRefsType{Amounts: refData, Hosts: hosts}
			refPhotos[refUserId] = hosts
		}
	}
	myRefs := make(map[int64]myRefsType)
	for refUserId, refData := range myRefsAmounts {
		myRefs[refUserId] = refData
	}
	for refUserId, refData := range myRefsKeys {
		md5 := string(utils.Md5(refData["private_key"]))
		myRefs[refUserId] = myRefsType{Key: refData["private_key"], KeyUrl: c.NodeConfig["pool_url"] + "public/" + md5[0:16]}
	}

	/*
	 * Общая стата по рефам
	 */
	globalRefs := make(map[int64]globalRefsType)
	// берем лидеров по USD
	rows, err = c.Query(c.FormatQuery(`
			SELECT user_id, sum(amount) as amount
			FROM referral_stats
			WHERE currency_id = 72
			GROUP BY user_id
			ORDER BY amount DESC
			`))
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	defer rows.Close()
	for rows.Next() {
		var user_id int64
		var amount float64
		err = rows.Scan(&user_id, &amount)
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		// вся прибыль с рефов у данного юзера
		refAmounts, err := c.GetAll(`
				SELECT ROUND(sum(amount)) as amount,  currency_id
				FROM referral_stats
				WHERE user_id = ?
				GROUP BY currency_id
				`, -1, user_id)
		if err != nil {
			return "", utils.ErrInfo(err)
		}

		data, err := c.OneRow("SELECT * FROM miners_data WHERE user_id  =  ?", user_id).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		// получим ID майнеров, у которых лежат фото нужного нам юзера
		minersIds := utils.GetMinersKeepers(data["photo_block_id"], data["photo_max_miner_id"], data["miners_keepers"], true)
		hosts, err := c.GetList("SELECT http_host FROM miners_data WHERE miner_id  IN (" + utils.JoinInts(minersIds, ",") + ")").String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		globalRefs[user_id] = globalRefsType{Amounts: refAmounts, Hosts: hosts}
		refPhotos[user_id] = hosts
	}

	lastTx, err := c.GetLastTx(c.SessUserId, utils.TypesToIds([]string{"NewUser"}), 1, c.TimeFormat)
	lastTxFormatted := ""
	if len(lastTx) > 0 {
		lastTxFormatted, _ = utils.MakeLastTx(lastTx, c.Lang)
	}

	TemplateStr, err := makeTemplate("new_user", "newUser", &newUserPage{
		Alert:           c.Alert,
		Lang:            c.Lang,
		CountSignArr:    c.CountSignArr,
		ShowSignData:    c.ShowSignData,
		UserId:          c.SessUserId,
		TimeNow:         timeNow,
		TxType:          txType,
		TxTypeId:        txTypeId,
		SignData:        "",
		LastTxFormatted: lastTxFormatted,
		MyRefs:          myRefs,
		GlobalRefs:      globalRefs,
		CurrencyList:    c.CurrencyList,
		RefPhotos:       refPhotos,
		PoolUrl:         c.NodeConfig["pool_url"]})
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	return TemplateStr, nil
}
コード例 #4
0
ファイル: votes_node_new_miner.go プロジェクト: dzyk/dcoin-go
func (p *Parser) VotesNodeNewMiner() error {

	var votes [2]int64
	votesData, err := p.OneRow("SELECT user_id, votes_start_time, votes_0, votes_1 FROM votes_miners WHERE id = ?", p.TxMaps.Int64["vote_id"]).Int64()
	if err != nil {
		return p.ErrInfo(err)
	}
	log.Debug("votesData", votesData)
	log.Debug("votesData[user_id]", votesData["user_id"])
	minersData, err := p.OneRow("SELECT photo_block_id, photo_max_miner_id, miners_keepers, log_id FROM miners_data WHERE user_id = ?", votesData["user_id"]).String()
	log.Debug("minersData", minersData)
	// $votes_data['user_id'] - это юзер, за которого голосуют
	if err != nil {
		return p.ErrInfo(err)
	}

	votes[0] = votesData["votes_0"]
	votes[1] = votesData["votes_1"]
	// прибавим голос
	votes[p.TxMaps.Int64["result"]]++

	// обновляем голоса. При откате просто вычитаем
	err = p.ExecSql("UPDATE votes_miners SET votes_"+utils.Int64ToStr(p.TxMaps.Int64["result"])+" = ? WHERE id = ?", votes[p.TxMaps.Int64["result"]], p.TxMaps.Int64["vote_id"])
	if err != nil {
		return p.ErrInfo(err)
	}

	// логируем, чтобы юзер {$this->tx_data['user_id']} не смог повторно проголосовать
	err = p.ExecSql("INSERT INTO log_votes (user_id, voting_id, type) VALUES (?, ?, 'votes_miners')", p.TxMaps.Int64["user_id"], p.TxMaps.Int64["vote_id"])
	if err != nil {
		return p.ErrInfo(err)
	}

	// ID майнеров, у которых сохраняются фотки
	minersIds := utils.GetMinersKeepers(minersData["photo_block_id"], minersData["photo_max_miner_id"], minersData["miners_keepers"], true)

	log.Debug("minersIds", minersIds, len(minersIds))
	// данные для проверки окончания голосования

	minerData := new(MinerData)
	minerData.myMinersIds, err = p.getMyMinersIds()
	if err != nil {
		return p.ErrInfo(err)
	}
	minerData.adminUiserId, err = p.GetAdminUserId()
	if err != nil {
		return p.ErrInfo(err)
	}
	minerData.minersIds = minersIds
	minerData.votes0 = votes[0]
	minerData.votes1 = votes[1]
	minerData.minMinersKeepers = p.Variables.Int64["min_miners_keepers"]
	log.Debug("minerData.adminUiserId %v", minerData.adminUiserId)
	log.Debug("minerData.myMinersIds %v", minerData.myMinersIds)
	log.Debug("minerData.minersIds %v", minerData.minersIds)
	log.Debug("minerData.votes0 %v", minerData.votes0)
	log.Debug("minerData.votes1 %v", minerData.votes1)
	log.Debug("minerData.minMinersKeepers %v", minerData.minMinersKeepers)

	if p.minersCheckVotes1(minerData) || (minerData.votes0 > minerData.minMinersKeepers || int(minerData.votes0) == len(minerData.minersIds)) {
		// отмечаем, что голосование нодов закончено
		err = p.ExecSql("UPDATE votes_miners SET votes_end = 1, end_block_id = ? WHERE id = ?", p.BlockData.BlockId, p.TxMaps.Int64["vote_id"])
		if err != nil {
			return p.ErrInfo(err)
		}
	}
	if p.minersCheckVotes1(minerData) || p.minersCheckMyMinerIdAndVotes0(minerData) {
		// отметим del_block_id всем, кто голосовал за данного юзера,
		// чтобы через N блоков по крону удалить бесполезные записи
		err = p.ExecSql("UPDATE log_votes SET del_block_id = ? WHERE voting_id = ? AND type = 'votes_miners'", p.BlockData.BlockId, p.TxMaps.Int64["vote_id"])
		if err != nil {
			return p.ErrInfo(err)
		}
	}

	// если набрано >=X голосов "за", то пишем в БД, что юзер готов к проверке людьми
	// либо если набранное кол-во голосов= кол-ву майнеров (актуально в самом начале запуска проекта)
	if p.minersCheckVotes1(minerData) {
		err = p.ExecSql("INSERT INTO votes_miners ( user_id, type, votes_start_time ) VALUES ( ?, 'user_voting', ? )", votesData["user_id"], p.BlockData.Time)
		if err != nil {
			return p.ErrInfo(err)
		}

		// и отмечаем лицо как готовое участвовать в поиске дублей
		err = p.ExecSql("UPDATE faces SET status = 'used' WHERE user_id = ?", votesData["user_id"])
		if err != nil {
			return p.ErrInfo(err)
		}
	} else if p.minersCheckMyMinerIdAndVotes0(minerData) {
		// если набрано >5 голосов "против" и мы среди тех X майнеров, которые копировали фото к себе
		// либо если набранное кол-во голосов = кол-ву майнеров (актуально в самом начале запуска проекта)
		facePath := fmt.Sprintf(*utils.Dir+"/public/face_%v.jpg", votesData["user_id"])
		profilePath := fmt.Sprintf(*utils.Dir+"/public/profile_%v.jpg", votesData["user_id"])

		faceRandName := ""
		profileRandName := ""
		// возможно фото к нам не было скопировано, т.к. хост был недоступен.
		if _, err := os.Stat(profilePath); os.IsNotExist(err) {
			faceRandName = ""
			profileRandName = ""
		} else if _, err := os.Stat(facePath); os.IsNotExist(err) {
			faceRandName = ""
			profileRandName = ""
		} else {
			faceRandName = utils.RandSeq(30)
			profileRandName = utils.RandSeq(30)

			// перемещаем фото в корзину, откуда по крону будем удалять данные
			err = utils.CopyFileContents(facePath, faceRandName)
			if err != nil {
				return p.ErrInfo(err)
			}
			err = os.Remove(facePath)
			if err != nil {
				return p.ErrInfo(err)
			}
			err = utils.CopyFileContents(profilePath, profileRandName)
			if err != nil {
				return p.ErrInfo(err)
			}
			err = os.Remove(profilePath)
			if err != nil {
				return p.ErrInfo(err)
			}

			// если в корзине что-то есть, то логируем
			// отсутствие файлов также логируем, т.к. больше негде, а при откате эти данные очень важны.
			logData, err := p.OneRow("SELECT * FROM recycle_bin WHERE user_id = ?", votesData["user_id"]).String()
			if err != nil {
				return p.ErrInfo(err)
			}
			if len(logData) > 0 {
				logId, err := p.ExecSqlGetLastInsertId("INSERT INTO log_recycle_bin ( user_id, profile_file_name, face_file_name, block_id, prev_log_id ) VALUES ( ?, ?, ?, ?, ? )", "log_id", logData["user_id"], logData["profile_file_name"], logData["face_file_name"], p.BlockData.BlockId, logData["log_id"])
				if err != nil {
					return p.ErrInfo(err)
				}
				err = p.ExecSql("UPDATE recycle_bin SET profile_file_name = ?, face_file_name = ?, log_id = ? WHERE user_id = ?", profileRandName, faceRandName, logId, logData["user_id"])
				if err != nil {
					return p.ErrInfo(err)
				}
			} else {
				err = p.ExecSql("INSERT INTO recycle_bin ( user_id, profile_file_name, face_file_name ) VALUES ( ?, ?, ? )", votesData["user_id"], profileRandName, faceRandName)
				if err != nil {
					return p.ErrInfo(err)
				}
			}
		}
	}

	return nil
}
コード例 #5
0
ファイル: votes_node_new_miner.go プロジェクト: dzyk/dcoin-go
func (p *Parser) VotesNodeNewMinerRollback() error {

	votesData, err := p.OneRow("SELECT user_id, votes_start_time, votes_0, votes_1 FROM votes_miners WHERE id = ?", p.TxMaps.Int64["vote_id"]).Int64()
	if err != nil {
		return p.ErrInfo(err)
	}
	minersData, err := p.OneRow("SELECT photo_block_id, photo_max_miner_id, miners_keepers, log_id FROM miners_data WHERE user_id = ?", votesData["user_id"]).String()
	if err != nil {
		return p.ErrInfo(err)
	}

	minerData := new(MinerData)
	// запомним голоса- пригодится чуть ниже в minersCheckVotes1
	minerData.votes0 = votesData["votes_0"]
	minerData.votes1 = votesData["votes_1"]

	var votes [2]int64
	votes[0] = votesData["votes_0"]
	votes[1] = votesData["votes_1"]
	// вычтем голос
	votes[p.TxMaps.Int64["result"]]--

	// обновляем голоса
	err = p.ExecSql("UPDATE votes_miners SET votes_"+utils.Int64ToStr(p.TxMaps.Int64["result"])+" = ? WHERE id = ?", votes[p.TxMaps.Int64["result"]], p.TxMaps.Int64["vote_id"])
	if err != nil {
		return p.ErrInfo(err)
	}

	// удаляем нашу запись из log_votes
	err = p.ExecSql("DELETE FROM log_votes WHERE user_id = ? AND voting_id = ? AND type = 'votes_miners'", p.TxMaps.Int64["user_id"], p.TxMaps.Int64["vote_id"])
	if err != nil {
		return p.ErrInfo(err)
	}
	minersIds := utils.GetMinersKeepers(minersData["photo_block_id"], minersData["photo_max_miner_id"], minersData["miners_keepers"], true)
	minerData.myMinersIds, err = p.getMyMinersIds()
	if err != nil {
		return p.ErrInfo(err)
	}
	minerData.minersIds = minersIds
	minerData.minMinersKeepers = p.Variables.Int64["min_miners_keepers"]

	if p.minersCheckVotes1(minerData) || p.minersCheckMyMinerIdAndVotes0(minerData) {

		// отменяем отметку о том, что голосование нодов закончено
		err = p.ExecSql("UPDATE votes_miners SET votes_end = 0, end_block_id = 0 WHERE id = ?", p.TxMaps.Int64["vote_id"])
		if err != nil {
			return p.ErrInfo(err)
		}

		// всем, кому ставили del_block_id, его убираем, т.е. отменяем будущее удаление по крону
		err = p.ExecSql("UPDATE log_votes SET del_block_id = 0 WHERE voting_id = ? AND type = 'votes_miners' AND del_block_id = ? ", p.TxMaps.Int64["vote_id"], p.BlockData.BlockId)
		if err != nil {
			return p.ErrInfo(err)
		}
	}

	// если набрано >=5 голосов, то отменяем  в БД, что юзер готов к проверке людьми
	if p.minersCheckVotes1(minerData) {
		// отменяем созданное юзерское голосование
		err = p.ExecSql("DELETE FROM votes_miners WHERE user_id = ? AND votes_start_time = ? AND type = 'user_voting'", votesData["user_id"], p.BlockData.Time)
		if err != nil {
			return p.ErrInfo(err)
		}
		err = p.rollbackAI("votes_miners", 1)
		if err != nil {
			return p.ErrInfo(err)
		}

		// и отмечаем лицо как неучаствующее в поиске клонов
		err = p.ExecSql("UPDATE faces SET status = 'pending' WHERE user_id = ?", votesData["user_id"])
		if err != nil {
			return p.ErrInfo(err)
		}
	} else if p.minersCheckMyMinerIdAndVotes0(minerData) {
		// если фото плохое и мы среди тех 10 майнеров, которые копировали (или нет) фото к себе,
		// а затем переместили фото в корзину

		// получаем rand_name из логов
		data, err := p.OneRow("SELECT profile_file_name, face_file_name FROM recycle_bin WHERE user_id = ?", votesData["user_id"]).String()
		if err != nil {
			return p.ErrInfo(err)
		}

		// перемещаем фото из корзины, если есть, что перемещать
		if len(data["profile_file_name"]) > 0 && len(data["face_file_name"]) > 0 {
			utils.CopyFileContents("recycle_bin/"+data["face_file_name"], *utils.Dir+"/public/face_"+utils.Int64ToStr(votesData["user_id"])+".jpg")
			utils.CopyFileContents("recycle_bin/"+data["profile_file_name"], *utils.Dir+"/public/profile_"+utils.Int64ToStr(votesData["user_id"])+".jpg")
		}
		p.generalRollback("recycle_bin", votesData["user_id"], "", false)
	}

	return nil
}
コード例 #6
0
ファイル: statistic.go プロジェクト: dzyk/dcoin-go
func (c *Controller) Statistic() (string, error) {

	var err error

	sumWallets := make(map[int64]float64)
	// получаем кол-во DC на кошельках
	rows, err := c.Query(`
			SELECT currency_id,
					     sum(amount) as sum_amount
			FROM wallets
			GROUP BY currency_id
			`)
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	defer rows.Close()
	for rows.Next() {
		var currency_id int64
		var sum_amount float64
		err = rows.Scan(&currency_id, &sum_amount)
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		sumWallets[currency_id] = sum_amount
	}
	// получаем кол-во TDC на обещанных суммах
	rows, err = c.Query(`
			SELECT currency_id,
			  		     sum(tdc_amount) as sum_amount
			FROM promised_amount
			GROUP BY currency_id
			`)
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	defer rows.Close()
	for rows.Next() {
		var currency_id int64
		var sum_amount float64
		err = rows.Scan(&currency_id, &sum_amount)
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		if sumWallets[currency_id] > 0 {
			sumWallets[currency_id] += sum_amount
		} else {
			sumWallets[currency_id] = sum_amount
		}
	}

	// получаем суммы обещанных сумм
	sumPromisedAmount, err := c.GetMap(`
			SELECT currency_id,
						sum(amount) as sum_amount
			FROM promised_amount
			WHERE status = 'mining' AND
					     del_block_id = 0 AND
						(cash_request_out_time = 0 OR cash_request_out_time > ?)
			GROUP BY currency_id`, "currency_id", "sum_amount", utils.Time()-c.Variables.Int64["cash_request_time"])

	// получаем кол-во майнеров по валютам
	promisedAmountMiners, err := c.GetMap(`
			SELECT currency_id, count(user_id) as count
			FROM (
					SELECT currency_id, user_id
					FROM promised_amount
					WHERE  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")

	// получаем кол-во анонимных юзеров по валютам
	walletsUsers, err := c.GetMap(`
			SELECT currency_id, count(user_id) as count
			FROM wallets
			WHERE amount > 0
			GROUP BY  currency_id`, "currency_id", "count")

	refPhotos := make(map[int64][]string)
	// таблица обмена на наличные
	cashRequests, err := c.GetAll(`
			SELECT *
			FROM cash_requests
			ORDER BY id DESC
			LIMIT 5`, 5)
	for i := 0; i < len(cashRequests); i++ {
		if cashRequests[i]["del_block_id"] != "0" {
			cashRequests[i]["status"] = "reduction closed"
		} else if utils.Time()-utils.StrToInt64(cashRequests[i]["time"]) > c.Variables.Int64["cash_request_time"] && cashRequests[i]["status"] != "approved" {
			cashRequests[i]["status"] = "rejected"
		}
		t := time.Unix(utils.StrToInt64(cashRequests[i]["time"]), 0)
		cashRequests[i]["time"] = t.Format(c.TimeFormat)

		// ### from_user_id для фоток
		data, err := c.OneRow("SELECT * FROM miners_data WHERE user_id  =  ?", cashRequests[i]["from_user_id"]).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		// получим ID майнеров, у которых лежат фото нужного нам юзера
		minersIds := utils.GetMinersKeepers(data["photo_block_id"], data["photo_max_miner_id"], data["miners_keepers"], true)
		hosts, err := c.GetList("SELECT http_host FROM miners_data WHERE miner_id  IN (" + utils.JoinInts(minersIds, ",") + ")").String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		refPhotos[utils.StrToInt64(cashRequests[i]["from_user_id"])] = hosts

		// ### to_user_id для фоток
		data, err = c.OneRow("SELECT * FROM miners_data WHERE user_id  =  ?", cashRequests[i]["to_user_id"]).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		// получим ID майнеров, у которых лежат фото нужного нам юзера
		minersIds = utils.GetMinersKeepers(data["photo_block_id"], data["photo_max_miner_id"], data["miners_keepers"], true)
		hosts, err = c.GetList("SELECT http_host FROM miners_data WHERE miner_id  IN (" + utils.JoinInts(minersIds, ",") + ")").String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		refPhotos[utils.StrToInt64(cashRequests[i]["to_user_id"])] = hosts
	}

	var userInfoWallets []utils.DCAmounts
	var promisedAmountListAccepted []utils.PromisedAmounts
	var credits map[string]string
	// поиск инфы о юзере
	userInfoId := int64(utils.StrToFloat64(c.Parameters["user_info_id"]))
	if userInfoId > 0 {
		userInfoWallets, err = c.GetBalances(userInfoId)
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		// обещанные суммы юзера
		_, promisedAmountListAccepted, _, err = c.GetPromisedAmounts(userInfoId, c.Variables.Int64["cash_request_time"])
		// кредиты
		credits, err = c.GetMap(`
				SELECT sum(amount) as amount,
							 currency_id
				FROM credits
				WHERE from_user_id = ? AND
							 del_block_id = 0
				GROUP BY currency_id`, "amount", "currency_id", userInfoId)
	}

	/*
	 * Кол-во юзеров, сменивших ключ
	 * */
	countUsers, err := c.Single("SELECT count(user_id) FROM users WHERE log_id > 0").Int64()
	if err != nil {
		return "", utils.ErrInfo(err)
	}

	/*
	 * %/год
	 * */
	currencyPct := make(map[int64]map[string]string)
	for currencyId, name := range c.CurrencyList {
		pct, err := c.OneRow("SELECT * FROM pct WHERE currency_id  =  ? ORDER BY block_id DESC", currencyId).Float64()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		currencyPct[currencyId] = make(map[string]string)
		currencyPct[currencyId]["name"] = name
		currencyPct[currencyId]["miner"] = utils.Float64ToStr(utils.Round((math.Pow(1+pct["miner"], 120)-1)*100, 6))
		currencyPct[currencyId]["user"] = utils.Float64ToStr(utils.Round((math.Pow(1+pct["user"], 120)-1)*100, 6))
	}

	/*
	 * Произошедшие сокращения
	 * */
	reduction, err := c.GetAll(`
			SELECT *
			FROM reduction
			ORDER BY time DESC
			LIMIT 20`, 20)
	for i := 0; i < len(reduction); i++ {
		if reduction[i]["type"] != "auto" {
			reduction[i]["type"] = "voting"
		}

		t := time.Unix(utils.StrToInt64(reduction[i]["time"]), 0)
		reduction[i]["time"] = t.Format(c.TimeFormat)
	}

	TemplateStr, err := makeTemplate("statistic", "statistic", &StatisticPage{
		Lang:                       c.Lang,
		CurrencyList:               c.CurrencyListCf,
		UserInfoId:                 userInfoId,
		SumWallets:                 sumWallets,
		SumPromisedAmount:          sumPromisedAmount,
		PromisedAmountMiners:       promisedAmountMiners,
		WalletsUsers:               walletsUsers,
		CashRequests:               cashRequests,
		UserInfoWallets:            userInfoWallets,
		Credits:                    credits,
		PromisedAmountListAccepted: promisedAmountListAccepted,
		CountUsers:                 countUsers,
		CurrencyPct:                currencyPct,
		Reduction:                  reduction,
		RefPhotos:                  refPhotos,
		UserId:                     c.SessUserId})
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	return TemplateStr, nil
}
コード例 #7
0
ファイル: assignments.go プロジェクト: dzyk/dcoin-go
func (c *Controller) Assignments() (string, error) {

	var randArr []int64
	// Нельзя завершить голосование юзеров раньше чем через сутки, даже если набрано нужное кол-во голосов.
	// В голосовании нодов ждать сутки не требуется, т.к. там нельзя поставить поддельных нодов

	// Модерация новых майнеров
	// берем тех, кто прошел проверку нодов (type='node_voting')
	num, err := c.Single("SELECT count(id) FROM votes_miners WHERE votes_end  =  0 AND type  =  'user_voting'").Int64()
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	if num > 0 {
		randArr = append(randArr, 1)
	}

	// Модерация promised_amount
	// вначале получим ID валют, которые мы можем проверять.
	currency, err := c.GetList("SELECT currency_id FROM promised_amount WHERE status IN ('mining', 'repaid') AND user_id = ?", c.SessUserId).String()
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	addSql := ""
	currencyIds := strings.Join(currency, ",")
	if len(currencyIds) > 0 || c.SessUserId == 1 {
		if c.SessUserId != 1 {
			addSql = "AND currency_id IN (" + currencyIds + ")"
		}
		num, err := c.Single("SELECT count(id) FROM promised_amount WHERE status  =  'pending' AND del_block_id  =  0 " + addSql + "").Int64()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		if num > 0 {
			randArr = append(randArr, 2)
		}
	}

	log.Debug("randArr %v", randArr)

	var AssignType int64
	if len(randArr) > 0 {
		AssignType = randArr[utils.RandInt(0, len(randArr))]
	}

	cloneHosts := make(map[int64][]string)
	var photoHosts []string
	examplePoints := make(map[string]string)
	tplName := "assignments"
	tplTitle := "assignments"

	var txType string
	var txTypeId int64
	var timeNow int64
	var myRace, myCountry, mainQuestion, newPromiseAmount, videoHost string
	var promisedAmountData, userInfo map[string]string

	switch AssignType {
	case 1:

		// ***********************************
		// задания по модерации новых майнеров
		// ***********************************
		txType = "VotesMiner"
		txTypeId = utils.TypeInt(txType)
		timeNow = utils.Time()

		userInfo, err = c.OneRow(`
				SELECT miners_data.user_id,
							 votes_miners.id as vote_id,
							 face_coords,
							 profile_coords,
							 video_type,
							 video_url_id,
							 photo_block_id,
							 photo_max_miner_id,
							 miners_keepers,
							 http_host
				FROM votes_miners
				LEFT JOIN miners_data ON miners_data.user_id = votes_miners.user_id
				LEFT JOIN `+c.MyPrefix+`my_tasks ON `+c.MyPrefix+`my_tasks.id = votes_miners.id
				WHERE 	votes_end = 0 AND
						votes_miners.type = 'user_voting' AND
						(`+c.MyPrefix+`my_tasks.time IS NULL OR (`+c.MyPrefix+`my_tasks.time < ? AND `+c.MyPrefix+`my_tasks.type  =  'miner'))
				`, utils.Time()-consts.ASSIGN_TIME).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		if len(userInfo) == 0 {
			tplName = "assignments"
			break
		}

		examplePoints, err = c.GetPoints(c.Lang)
		if err != nil {
			return "", utils.ErrInfo(err)
		}

		// получим ID майнеров, у которых лежат фото нужного нам юзера
		minersIds := utils.GetMinersKeepers(userInfo["photo_block_id"], userInfo["photo_max_miner_id"], userInfo["miners_keepers"], true)
		if len(minersIds) > 0 {
			photoHosts, err = c.GetList("SELECT http_host FROM miners_data WHERE miner_id  IN (" + utils.JoinInts(minersIds, ",") + ")").String()
			if err != nil {
				return "", utils.ErrInfo(err)
			}
		}

		// отрезки майнера, которого проверяем
		relations, err := c.OneRow("SELECT * FROM faces WHERE user_id  =  ?", userInfo["user_id"]).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}

		// получим допустимые расхождения между точками и совместимость версий
		data_, err := c.OneRow("SELECT tolerances, compatibility FROM spots_compatibility").String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		tolerances := make(map[string]map[string]string)
		if err := json.Unmarshal([]byte(data_["tolerances"]), &tolerances); err != nil {
			return "", utils.ErrInfo(err)
		}
		var compatibility []int
		if err := json.Unmarshal([]byte(data_["compatibility"]), &compatibility); err != nil {
			return "", utils.ErrInfo(err)
		}

		// формируем кусок SQL-запроса для соотношений отрезков
		addSqlTolerances := ""
		typesArr := []string{"face", "profile"}
		for i := 0; i < len(typesArr); i++ {
			for j := 1; j <= len(tolerances[typesArr[i]]); j++ {
				currentRelations := utils.StrToFloat64(relations[typesArr[i][:1]+utils.IntToStr(j)])
				diff := utils.StrToFloat64(tolerances[typesArr[i]][utils.IntToStr(j)]) * currentRelations
				if diff == 0 {
					continue
				}
				min := currentRelations - diff
				max := currentRelations + diff
				addSqlTolerances += typesArr[i][:1] + utils.IntToStr(j) + ">" + utils.Float64ToStr(min) + " AND " + typesArr[i][:1] + utils.IntToStr(j) + " < " + utils.Float64ToStr(max) + " AND "
			}
		}
		addSqlTolerances = addSqlTolerances[:len(addSqlTolerances)-4]

		// формируем кусок SQL-запроса для совместимости версий
		addSqlCompatibility := ""
		for i := 0; i < len(compatibility); i++ {
			addSqlCompatibility += fmt.Sprintf(`%d,`, compatibility[i])
		}
		addSqlCompatibility = addSqlCompatibility[:len(addSqlCompatibility)-1]

		// получаем из БД похожие фото
		rows, err := c.Query(c.FormatQuery(`
				SELECT miners_data.user_id,
							 photo_block_id,
							 photo_max_miner_id,
							 miners_keepers
				FROM faces
				LEFT JOIN miners_data ON
						miners_data.user_id = faces.user_id
				WHERE `+addSqlTolerances+` AND
							version IN (`+addSqlCompatibility+`) AND
				             faces.status = 'used' AND
				             miners_data.user_id != ?
				LIMIT 100
				`), userInfo["user_id"])
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		defer rows.Close()
		for rows.Next() {
			var photo_block_id, photo_max_miner_id, miners_keepers string
			var user_id int64
			err = rows.Scan(&user_id, &photo_block_id, &photo_max_miner_id, &miners_keepers)
			if err != nil {
				return "", utils.ErrInfo(err)
			}
			// майнеры, у которых можно получить фото нужного нам юзера
			minersIds := utils.GetMinersKeepers(photo_block_id, photo_max_miner_id, miners_keepers, true)
			if len(minersIds) > 0 {
				photoHosts, err = c.GetList("SELECT http_host FROM miners_data WHERE miner_id  IN (" + utils.JoinInts(minersIds, ",") + ")").String()
				if err != nil {
					return "", utils.ErrInfo(err)
				}
			}
			cloneHosts[user_id] = photoHosts
		}

		data, err := c.OneRow("SELECT race, country FROM " + c.MyPrefix + "my_table").Int64()
		myRace = c.Races[data["race"]]
		myCountry = consts.Countries[int(data["country"])]

		tplName = "assignments_new_miner"
		tplTitle = "assignmentsNewMiner"

	case 2:
		promisedAmountData, err = c.OneRow(`
				SELECT id,
							 currency_id,
							 amount,
							 user_id,
							 video_type,
							 video_url_id
				FROM promised_amount
				WHERE status =  'pending' AND
							 del_block_id = 0
				` + addSql + `
		`).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		promisedAmountData["currency_name"] = c.CurrencyList[utils.StrToInt64(promisedAmountData["currency_id"])]

		// проверим, не голосовали ли мы за это в последние 30 минут
		repeated, err := c.Single("SELECT id FROM "+c.MyPrefix+"my_tasks WHERE type  =  'promised_amount' AND id  =  ? AND time > ?", promisedAmountData["id"], utils.Time()-consts.ASSIGN_TIME).Int64()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		if repeated > 0 {
			tplName = "assignments"
			tplTitle = "assignments"
			break
		}

		// если нету видео на ютубе, то получаем host юзера, где брать видео
		if promisedAmountData["video_url_id"] == "null" {
			videoHost, err = c.Single("SELECT http_host FROM miners_data WHERE user_id  =  ?", promisedAmountData["user_id"]).String()
			if err != nil {
				return "", utils.ErrInfo(err)
			}
		}

		// каждый раз обязательно проверяем, где находится юзер
		userInfo, err = c.OneRow(`
				SELECT latitude,
							 user_id,
							 longitude,
							 photo_block_id,
							 photo_max_miner_id,
							 miners_keepers,
							 http_host
				FROM miners_data
				WHERE user_id = ?
				`, promisedAmountData["user_id"]).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}

		// получим ID майнеров, у которых лежат фото нужного нам юзера
		minersIds := utils.GetMinersKeepers(userInfo["photo_block_id"], userInfo["photo_max_miner_id"], userInfo["miners_keepers"], true)
		if len(minersIds) > 0 {
			photoHosts, err = c.GetList("SELECT http_host FROM miners_data WHERE miner_id  IN (" + utils.JoinInts(minersIds, ",") + ")").String()
			if err != nil {
				return "", utils.ErrInfo(err)
			}
		}

		txType = "VotesPromisedAmount"
		txTypeId = utils.TypeInt(txType)
		timeNow = utils.Time()

		newPromiseAmount = strings.Replace(c.Lang["new_promise_amount"], "[amount]", promisedAmountData["amount"], -1)
		newPromiseAmount = strings.Replace(newPromiseAmount, "[currency]", promisedAmountData["currency_name"], -1)

		mainQuestion = strings.Replace(c.Lang["main_question"], "[amount]", promisedAmountData["amount"], -1)
		mainQuestion = strings.Replace(mainQuestion, "[currency]", promisedAmountData["currency_name"], -1)

		tplName = "assignments_promised_amount"
		tplTitle = "assignmentsPromisedAmount"

	default:
		tplName = "assignments"
		tplTitle = "assignments"
	}

	TemplateStr, err := makeTemplate(tplName, tplTitle, &AssignmentsPage{
		Alert:              c.Alert,
		Lang:               c.Lang,
		CountSignArr:       c.CountSignArr,
		ShowSignData:       c.ShowSignData,
		UserId:             c.SessUserId,
		TimeNow:            timeNow,
		TxType:             txType,
		TxTypeId:           txTypeId,
		SignData:           "",
		CurrencyList:       c.CurrencyList,
		MainQuestion:       mainQuestion,
		NewPromiseAmount:   newPromiseAmount,
		MyRace:             myRace,
		MyCountry:          myCountry,
		ExamplePoints:      examplePoints,
		VideoHost:          videoHost,
		PhotoHosts:         photoHosts,
		PromisedAmountData: promisedAmountData,
		UserInfo:           userInfo,
		CloneHosts:         cloneHosts})
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	return TemplateStr, nil
}
コード例 #8
0
ファイル: get_miner_data.go プロジェクト: dzyk/dcoin-go
func (c *Controller) GetMinerData() (string, error) {

	c.r.ParseForm()

	secs := float64(3600 * 24 * 365)

	userId := utils.StrToInt64(c.r.FormValue("userId"))
	if !utils.CheckInputData(userId, "int") {
		return `{"result":"incorrect userId"}`, nil
	}

	minersData, err := c.OneRow("SELECT * FROM miners_data WHERE user_id  =  ?", userId).String()
	if err != nil {
		return "", err
	}

	// получим ID майнеров, у которых лежат фото нужного нам юзера
	minersIds := utils.GetMinersKeepers(minersData["photo_block_id"], minersData["photo_max_miner_id"], minersData["miners_keepers"], false)
	hosts, err := c.GetList("SELECT http_host as host FROM miners_data WHERE miner_id IN (" + utils.JoinIntsK(minersIds, ",") + ")").String()
	if err != nil {
		return "", err
	}

	currencyList, err := c.GetCurrencyList(false)
	if err != nil {
		return "", err
	}

	_, _, promisedAmountListGen, err := c.GetPromisedAmounts(userId, c.Variables.Int64["cash_request_time"])
	log.Debug("promisedAmountListGen: %v", promisedAmountListGen)
	var data utils.DCAmounts
	if promisedAmountListGen[72].Amount > 0 {
		data = promisedAmountListGen[72]
	} else if promisedAmountListGen[23].Amount > 0 {
		data = promisedAmountListGen[23]
	} else {
		data = utils.DCAmounts{}
	}
	log.Debug("data: %v", data)

	promisedAmounts := ""
	prognosis := make(map[int64]float64)
	if data.Amount > 1 {
		promisedAmounts += RoundStr(utils.Float64ToStr(utils.Round(data.Amount, 0)), 0) + " " + currencyList[(data.CurrencyId)] + "<br>"
		prognosis[int64(data.CurrencyId)] += (math.Pow(1+data.PctSec, secs) - 1) * data.Amount
	}

	if len(promisedAmounts) > 0 {
		promisedAmounts = "<strong>" + promisedAmounts[:len(promisedAmounts)-4] + "</strong><br>" + c.Lang["promised"] + "<hr>"
	}

	/*
	 * На кошельках
	 * */

	balances, err := c.GetBalances(userId)
	if err != nil {
		return "", err
	}
	walletsByCurrency := make(map[int]utils.DCAmounts)
	for _, data := range balances {
		walletsByCurrency[int(data.CurrencyId)] = data
	}
	log.Debug("walletsByCurrency[72].Amount: %v", walletsByCurrency[72].Amount)
	if walletsByCurrency[72].Amount > 0 {
		data = walletsByCurrency[72]
	} else if walletsByCurrency[23].Amount > 0 {
		data = walletsByCurrency[23]
	} else {
		data = utils.DCAmounts{}
	}
	log.Debug("data: %v", data)

	wallets := ""
	var countersIds []string
	var pctSec float64
	if data.Amount > 0 {
		counterId := "map-" + utils.Int64ToStr(userId) + "-" + utils.Int64ToStr(data.CurrencyId)
		countersIds = append(countersIds, counterId)
		wallets = "<span class='dc_amount' id='" + counterId + "'>" + RoundStr(utils.Float64ToStr(data.Amount), 8) + "</span> d" + currencyList[(data.CurrencyId)] + "<br>"
		// прогноз
		prognosis[int64(data.CurrencyId)] += (math.Pow(1+data.PctSec, secs) - 1) * data.Amount
		pctSec = data.PctSec
	}

	if len(wallets) > 0 {
		wallets = wallets[:len(wallets)-4] + "<br>" + c.Lang["on_the_account"] + "<hr>"
	}

	/*
	 * Годовой прогноз
	 * */
	prognosisHtml := ""
	for currencyId, amount := range prognosis {
		if amount < 0.01 {
			continue
		} else if amount < 1 {
			amount = utils.Round(amount, 2)
		} else {
			amount = amount
		}
		prognosisHtml += "<span class='amount_1year'>" + RoundStr(utils.Float64ToStr(amount), 2) + " d" + currencyList[(currencyId)] + "</span><br>"
	}
	if len(prognosisHtml) > 0 {
		prognosisHtml = prognosisHtml[:len(prognosisHtml)-4] + "<br> " + c.Lang["profit_forecast"] + " " + c.Lang["after_1_year"]
	}

	prognosisHtml = ""

	result_ := minersDataType{Hosts: hosts, Lnglat: map[string]string{"lng": minersData["longitude"], "lat": minersData["latitude"]}, Html: promisedAmounts + wallets + "<div style=\"clear:both\"></div>" + prognosisHtml + "</p>", Counters: countersIds, PctSec: pctSec}
	log.Debug("result_", result_)
	result, err := json.Marshal(result_)
	if err != nil {
		return "", err
	}
	log.Debug(string(result))
	return string(result), nil
}