示例#1
0
文件: exchange.go 项目: dzyk/dcoin-go
func Exchange(chBreaker chan bool, chAnswer chan string) {

	defer func() {
		if r := recover(); r != nil {
			log.Error("daemon Recovered", r)
			panic(r)
		}
	}()

	const GoroutineName = "Exchange"
	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
	}

BEGIN:
	for {
		log.Info(GoroutineName)
		MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())}

		// проверим, не нужно ли нам выйти из цикла
		if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) {
			break BEGIN
		}

		blockId, err := d.GetConfirmedBlockId()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		var myPrefix string
		community, err := d.GetCommunityUsers()
		if len(community) > 0 {
			adminUserId, err := d.GetPoolAdminUserId()
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			myPrefix = utils.Int64ToStr(adminUserId) + "_"
		} else {
			myPrefix = ""
		}

		eConfig, err := d.GetMap(`SELECT * FROM e_config`, "name", "value")
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		confirmations := utils.StrToInt64(eConfig["confirmations"])
		mainDcAccount := utils.StrToInt64(eConfig["main_dc_account"])

		// все валюты, с которыми работаем
		currencyList, err := utils.EGetCurrencyList()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		// ++++++++++++ reduction ++++++++++++

		// максимальный номер блока для процентов. Чтобы брать только новые
		maxReductionBlock, err := d.Single(`SELECT max(block_id) FROM e_reduction`).Int64()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		// если есть свежая reduction, то нужно остановить торги
		reduction, err := d.Single(`SELECT block_id FROM reduction WHERE block_id > ? and pct > 0`, maxReductionBlock).Int64()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		if reduction > 0 {
			err = d.ExecSql(`INSERT INTO e_reduction_lock (time) VALUES (?)`, utils.Time())
			if err != nil {
				log.Error("%v", utils.ErrInfo(err))
			}
		}

		// если уже прошло 10 блоков с момента обнаружения reduction, то производим сокращение объема монет
		rows, err := d.Query(d.FormatQuery(`SELECT pct, currency_id, time, block_id FROM reduction WHERE block_id > ? AND
	block_id < ?`), maxReductionBlock, blockId-confirmations)
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		for rows.Next() {
			var pct float64
			var currencyId, rTime, blockId int64
			err = rows.Scan(&pct, &currencyId, &rTime, &blockId)
			if err != nil {
				rows.Close()
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			//	$k = (100-$row['pct'])/100;
			k := (100 - pct) / 100
			// уменьшаем все средства на счетах
			err = d.ExecSql(`UPDATE e_wallets SET amount = amount * ? WHERE currency_id = ?`, k, currencyId)

			// уменьшаем все средства на вывод
			err = d.ExecSql(`UPDATE e_withdraw SET amount = amount * ? WHERE currency_id = ? AND close_time = 0`, k, currencyId)

			// уменьшаем все ордеры на продажу
			err = d.ExecSql(`UPDATE e_orders SET amount = amount * ?, begin_amount = begin_amount * ? WHERE sell_currency_id = ?`, k, k, currencyId)

			err = d.ExecSql(`INSERT INTO e_reduction (
					time,
					block_id,
					currency_id,
					pct
				)
				VALUES (
					?,
					?,
					?,
					?
				)`, rTime, blockId, currencyId, pct)
			if err != nil {
				rows.Close()
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
		}
		rows.Close()

		// ++++++++++++ DC ++++++++++++
		/*
		 * Важно! отключать в кроне при обнулении данных в БД
		 *
		 * 1. Получаем инфу о входящих переводах и начисляем их на счета юзеров
		 * 2. Обновляем проценты
		 * 3. Чистим кол-во отправленных смс-ок
		 * */
		nodePrivateKey, err := d.GetNodePrivateKey(myPrefix)
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		/*
		 * Получаем инфу о входящих переводах и начисляем их на счета юзеров
		 * */

		// если всё остановлено из-за найденного блока с reduction, то входящие переводы не обрабатываем
		reductionLock, err := utils.EGetReductionLock()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		if reductionLock > 0 {
			if d.dPrintSleep(utils.ErrInfo("reductionLock"), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		rows, err = d.Query(d.FormatQuery(`
				SELECT amount, id, block_id, type_id, currency_id, to_user_id, time, comment, comment_status
				FROM my_dc_transactions
				WHERE type = 'from_user' AND
					  block_id < ? AND
					  exchange_checked = 0 AND
					  status = 'approved' AND
					  to_user_id = ?
				ORDER BY id DESC`), blockId-confirmations, mainDcAccount)
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		for rows.Next() {
			var amount float64
			var id, blockId, typeId, currencyId, toUserId, txTime int64
			var comment, commentStatus string
			err = rows.Scan(&amount, &id, &blockId, &typeId, &currencyId, &toUserId, &txTime, &comment, &commentStatus)
			if err != nil {
				rows.Close()
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			// отметим exchange_checked=1, чтобы больше не брать эту тр-ию
			err = d.ExecSql(`UPDATE my_dc_transactions SET exchange_checked = 1 WHERE id = ?`, id)
			if err != nil {
				rows.Close()
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}

			// вначале нужно проверить, точно ли есть такой перевод в блоке
			binaryData, err := d.Single(`SELECT data FROM block_chain WHERE id = ?`, blockId).Bytes()
			if err != nil {
				rows.Close()
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			p := new(dcparser.Parser)
			p.DCDB = d.DCDB
			p.BinaryData = binaryData
			p.ParseDataLite()
			for _, txMap := range p.TxMapArr {
				// пропускаем все ненужные тр-ии
				if utils.BytesToInt64(txMap["type"]) != utils.TypeInt("SendDc") {
					continue
				}
				log.Debug("md5hash %s", txMap["md5hash"])

				// если что-то случится с таблой my_dc_transactions, то все ввода на биржу будут зачислены по новой
				// поэтому нужно проверять e_adding_funds
				exists, err := d.Single(`SELECT id FROM e_adding_funds WHERE hex(tx_hash) = ?`, string(txMap["md5hash"])).Int64()
				if err != nil {
					rows.Close()
					if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				log.Debug("exists %d", exists)
				if exists != 0 {
					continue
				}

				log.Debug("user_id = %d / typeId = %d / currency_id = %d / currencyId = %d / amount = %f / amount = %f / comment = %s / comment = %s / to_user_id = %d / toUserId = %d ", utils.BytesToInt64(txMap["user_id"]), typeId, utils.BytesToInt64(txMap["currency_id"]), currencyId, utils.BytesToFloat64(txMap["amount"]), amount, string(utils.BinToHex(txMap["comment"])), comment, utils.BytesToInt64(txMap["to_user_id"]), toUserId)
				// сравнение данных из таблы my_dc_transactions с тем, что в блоке
				if utils.BytesToInt64(txMap["user_id"]) == typeId && utils.BytesToInt64(txMap["currency_id"]) == currencyId && utils.BytesToFloat64(txMap["amount"]) == amount && string(utils.BinToHex(txMap["comment"])) == comment && utils.BytesToInt64(txMap["to_user_id"]) == toUserId {

					decryptedComment := comment
					if commentStatus == "encrypted" {
						// расшифруем коммент
						block, _ := pem.Decode([]byte(nodePrivateKey))
						if block == nil || block.Type != "RSA PRIVATE KEY" {
							rows.Close()
							if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
								break BEGIN
							}
							continue BEGIN
						}
						private_key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
						if err != nil {
							rows.Close()
							if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
								break BEGIN
							}
							continue BEGIN
						}
						decryptedComment_, err := rsa.DecryptPKCS1v15(rand.Reader, private_key, utils.HexToBin(comment))
						if err != nil {
							rows.Close()
							if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
								break BEGIN
							}
							continue BEGIN
						}
						decryptedComment = string(decryptedComment_)
						// запишем расшифрованный коммент, чтобы потом можно было найти перевод в ручном режиме
						err = d.ExecSql("UPDATE "+myPrefix+"my_dc_transactions SET comment = ?, comment_status = 'decrypted' WHERE id = ?", decryptedComment, id)
						if err != nil {
							rows.Close()
							if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
								break BEGIN
							}
							continue BEGIN
						}
					}

					// возможно юзер сделал перевод в той валюте, которая у нас на бирже еще не торгуется
					if len(currencyList[currencyId]) == 0 {
						log.Error("currencyId %d not trading", currencyId)
						continue
					}

					// возможно, что чуть раньше было reduction, а это значит, что все тр-ии,
					// которые мы ещё не обработали и которые были До блока с reduction нужно принимать с учетом reduction
					// т.к. средства на нашем счете уже урезались, а  вот те, что после reduction - остались в том виде, в котором пришли
					lastReduction, err := d.OneRow("SELECT block_id, pct FROM reduction WHERE currency_id  = ? ORDER BY block_id", currencyId).Int64()
					if err != nil {
						rows.Close()
						if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
							break BEGIN
						}
						continue BEGIN
					}
					if blockId <= lastReduction["block_id"] {
						// сумму с учетом reduction
						k0 := (100 - lastReduction["pct"]) / 100
						amount = amount * float64(k0)
					}

					// начисляем средства на счет того, чей id указан в комменте
					r, _ := regexp.Compile(`(?i)\s*#\s*([0-9]+)\s*`)
					user := r.FindStringSubmatch(decryptedComment)
					if len(user) == 0 {
						log.Error("len(user) == 0")
						continue
					}
					log.Debug("user %s", user[1])
					// user_id с биржевой таблы
					uid := utils.StrToInt64(user[1])
					userAmountAndProfit := utils.EUserAmountAndProfit(uid, currencyId)
					newAmount_ := userAmountAndProfit + amount

					utils.UpdEWallet(uid, currencyId, utils.Time(), newAmount_, true)

					// для отчетности запишем в историю
					err = d.ExecSql(`
						INSERT INTO e_adding_funds (
							user_id,
							currency_id,
							time,
							amount,
							tx_hash
						)
						VALUES (
							?,
							?,
							?,
							?,
							?
						)`, uid, currencyId, txTime, amount, string(txMap["md5hash"]))
					if err != nil {
						rows.Close()
						if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
							break BEGIN
						}
						continue BEGIN
					}
				}
			}
		}
		rows.Close()

		/*
		 * Обновляем проценты
		 * */
		// максимальный номер блока для процентов. Чтобы брать только новые
		maxPctBlock, err := d.Single(`SELECT max(block_id) FROM e_user_pct`).Int64()

		log.Debug(`SELECT time, block_id, currency_id, user FROM pct WHERE  block_id < ` + utils.Int64ToStr(blockId-confirmations) + ` AND block_id > ` + utils.Int64ToStr(maxPctBlock))
		rows, err = d.Query(d.FormatQuery(`SELECT time, block_id, currency_id, user FROM pct WHERE  block_id < ? AND block_id > ?`), blockId-confirmations, maxPctBlock)
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		for rows.Next() {
			var pct float64
			var pTime, blockId, currencyId int64
			err = rows.Scan(&pTime, &blockId, &currencyId, &pct)
			if err != nil {
				rows.Close()
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			d.ExecSql(`
				INSERT INTO e_user_pct (
					time,
					block_id,
					currency_id,
					pct
				)
				VALUES (
					?,
					?,
					?,
					?
				)`, pTime, blockId, currencyId, pct)
		}
		rows.Close()

		if d.dSleep(d.sleepTime) {
			break BEGIN
		}
	}
	log.Debug("break BEGIN %v", GoroutineName)
}
示例#2
0
func BlocksCollection(chBreaker chan bool, chAnswer chan string) {
	defer func() {
		if r := recover(); r != nil {
			log.Error("daemon Recovered", r)
			panic(r)
		}
	}()

	const GoroutineName = "BlocksCollection"
	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 = 300
	} else {
		d.sleepTime = 60
	}
	if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) {
		return
	}
	d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName)
	if d.DCDB == nil {
		return
	}
	//var cur bool
BEGIN:
	for {
		log.Info(GoroutineName)
		MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())}

		// проверим, не нужно ли нам выйти из цикла
		if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) {
			break BEGIN
		}
		log.Debug("0")
		config, err := d.GetNodeConfig()
		if err != nil {
			if d.dPrintSleep(err, d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		log.Debug("1")

		err, restart := d.dbLock()
		if restart {
			log.Debug("restart true")
			break BEGIN
		}
		if err != nil {
			log.Debug("restart err %v", err)
			if d.dPrintSleep(err, d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		log.Debug("2")

		// если это первый запуск во время инсталяции
		currentBlockId, err := d.GetBlockId()
		if err != nil {
			if d.unlockPrintSleep(err, d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		log.Info("config", config)
		log.Info("currentBlockId", currentBlockId)

		// на время тестов
		/*if !cur {
		    currentBlockId = 0
		    cur = true
		}*/
		parser := new(dcparser.Parser)
		parser.DCDB = d.DCDB
		parser.GoroutineName = GoroutineName
		if currentBlockId == 0 || *utils.StartBlockId > 0 {
			/*
			   IsNotExistBlockChain := false
			   if _, err := os.Stat(*utils.Dir+"/public/blockchain"); os.IsNotExist(err) {
			       IsNotExistBlockChain = true
			   }*/
			if config["first_load_blockchain"] == "file" /* && IsNotExistBlockChain*/ {

				log.Info("first_load_blockchain=file")
				nodeConfig, err := d.GetNodeConfig()
				blockchain_url := nodeConfig["first_load_blockchain_url"]
				if len(blockchain_url) == 0 {
					blockchain_url = consts.BLOCKCHAIN_URL
				}
				log.Debug("blockchain_url: %s", blockchain_url)
				// возможно сервер отдаст блокчейн не с первой попытки
				var blockchainSize int64
				for i := 0; i < 10; i++ {
					log.Debug("blockchain_url: %s, i: %d", blockchain_url, i)
					blockchainSize, err = utils.DownloadToFile(blockchain_url, *utils.Dir+"/public/blockchain", 3600, chBreaker, chAnswer, GoroutineName)
					if err != nil {
						log.Error("%v", utils.ErrInfo(err))
					}
					if blockchainSize > consts.BLOCKCHAIN_SIZE {
						break
					}
				}
				log.Debug("blockchain dw ok")
				if err != nil || blockchainSize < consts.BLOCKCHAIN_SIZE {
					if err != nil {
						log.Error("%v", utils.ErrInfo(err))
					} else {
						log.Info(fmt.Sprintf("%v < %v", blockchainSize, consts.BLOCKCHAIN_SIZE))
					}
					if d.unlockPrintSleep(err, d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}

				first := true
				/*// блокчейн мог быть загружен ранее. проверим его размер


				  stat, err := file.Stat()
				  if err != nil {
				      if d.unlockPrintSleep(err, d.sleepTime) {	break BEGIN }
				      file.Close()
				      continue BEGIN
				  }
				  if stat.Size() < consts.BLOCKCHAIN_SIZE {
				      d.unlockPrintSleep(fmt.Errorf("%v < %v", stat.Size(), consts.BLOCKCHAIN_SIZE), 1)
				      file.Close()
				      continue BEGIN
				  }*/

				log.Debug("GO!")
				file, err := os.Open(*utils.Dir + "/public/blockchain")
				if err != nil {
					if d.unlockPrintSleep(err, d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				err = d.ExecSql(`UPDATE config SET current_load_blockchain = 'file'`)
				if err != nil {
					if d.unlockPrintSleep(err, d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}

				for {
					// проверим, не нужно ли нам выйти из цикла
					if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) {
						d.unlockPrintSleep(fmt.Errorf("DaemonsRestart"), 0)
						break BEGIN
					}
					b1 := make([]byte, 5)
					file.Read(b1)
					dataSize := utils.BinToDec(b1)
					log.Debug("dataSize", dataSize)
					if dataSize > 0 {

						data := make([]byte, dataSize)
						file.Read(data)
						//log.Debug("data %x\n", data)
						blockId := utils.BinToDec(data[0:5])
						if *utils.EndBlockId > 0 && blockId == *utils.EndBlockId {
							if d.dPrintSleep(err, d.sleepTime) {
								break BEGIN
							}
							file.Close()
							continue BEGIN
						}
						log.Info("blockId", blockId)
						data2 := data[5:]
						length := utils.DecodeLength(&data2)
						log.Debug("length", length)
						//log.Debug("data2 %x\n", data2)
						blockBin := utils.BytesShift(&data2, length)
						//log.Debug("blockBin %x\n", blockBin)

						if *utils.StartBlockId == 0 || (*utils.StartBlockId > 0 && blockId > *utils.StartBlockId) {

							// парсинг блока
							parser.BinaryData = blockBin

							if first {
								parser.CurrentVersion = consts.VERSION
								first = false
							}
							err = parser.ParseDataFull()
							if err != nil {
								if d.dPrintSleep(err, d.sleepTime) {
									break BEGIN
								}
								file.Close()
								continue BEGIN
							}
							err = parser.InsertIntoBlockchain()
							if err != nil {
								if d.dPrintSleep(err, d.sleepTime) {
									break BEGIN
								}
								file.Close()
								continue BEGIN
							}

							// отметимся, чтобы не спровоцировать очистку таблиц
							err = parser.UpdMainLock()
							if err != nil {
								if d.dPrintSleep(err, d.sleepTime) {
									break BEGIN
								}
								file.Close()
								continue BEGIN
							}
							if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) {
								if d.dPrintSleep(err, d.sleepTime) {
									break BEGIN
								}
								file.Close()
								continue BEGIN
							}
						}
						// ненужный тут размер в конце блока данных
						data = make([]byte, 5)
						file.Read(data)
					} else {
						if d.unlockPrintSleep(err, d.sleepTime) {
							break BEGIN
						}
						continue BEGIN
					}
					// utils.Sleep(1)
				}
				file.Close()
			} else {

				newBlock, err := static.Asset("static/1block.bin")
				if err != nil {
					if d.dPrintSleep(err, d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				parser.BinaryData = newBlock
				parser.CurrentVersion = consts.VERSION

				err = parser.ParseDataFull()
				if err != nil {
					if d.dPrintSleep(err, d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				err = parser.InsertIntoBlockchain()

				if err != nil {
					if d.dPrintSleep(err, d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}

			utils.Sleep(1)
			d.dbUnlock()
			continue BEGIN
		}
		d.dbUnlock()

		err = d.ExecSql(`UPDATE config SET current_load_blockchain = 'nodes'`)
		if err != nil {
			if d.unlockPrintSleep(err, d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		myConfig, err := d.OneRow("SELECT local_gate_ip, static_node_user_id FROM config").String()
		if err != nil {
			if d.dPrintSleep(err, d.sleepTime) {
				break BEGIN
			}
			continue
		}
		var hosts []map[string]string
		var nodeHost string
		var dataTypeMaxBlockId, dataTypeBlockBody int64
		if len(myConfig["local_gate_ip"]) > 0 {
			hosts = append(hosts, map[string]string{"host": myConfig["local_gate_ip"], "user_id": myConfig["static_node_user_id"]})
			nodeHost, err = d.Single("SELECT tcp_host FROM miners_data WHERE user_id  =  ?", myConfig["static_node_user_id"]).String()
			if err != nil {
				if d.dPrintSleep(err, d.sleepTime) {
					break BEGIN
				}
				continue
			}
			dataTypeMaxBlockId = 9
			dataTypeBlockBody = 8
			//getBlockScriptName = "ajax?controllerName=protectedGetBlock";
			//addNodeHost = "&nodeHost="+nodeHost;
		} else {
			// получим список нодов, с кем установлено рукопожатие
			hosts, err = d.GetAll("SELECT * FROM nodes_connection", -1)
			if err != nil {
				if d.dPrintSleep(err, d.sleepTime) {
					break BEGIN
				}
				continue
			}
			dataTypeMaxBlockId = 10
			dataTypeBlockBody = 7
			//getBlockScriptName = "ajax?controllerName=getBlock";
			//addNodeHost = "";
		}

		log.Info("%v", hosts)

		if len(hosts) == 0 {
			if d.dPrintSleep(err, 1) {
				break BEGIN
			}
			continue
		}

		maxBlockId := int64(1)
		maxBlockIdHost := ""
		var maxBlockIdUserId int64
		// получим максимальный номер блока
		for i := 0; i < len(hosts); i++ {
			if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) {
				break BEGIN
			}
			conn, err := utils.TcpConn(hosts[i]["host"])
			if err != nil {
				if d.dPrintSleep(err, 1) {
					break BEGIN
				}
				continue
			}
			// шлем тип данных
			_, err = conn.Write(utils.DecToBin(dataTypeMaxBlockId, 1))
			if err != nil {
				conn.Close()
				if d.dPrintSleep(err, 1) {
					break BEGIN
				}
				continue
			}
			if len(nodeHost) > 0 { // защищенный режим
				err = utils.WriteSizeAndData([]byte(nodeHost), conn)
				if err != nil {
					conn.Close()
					if d.dPrintSleep(err, d.sleepTime) {
						break BEGIN
					}
					continue
				}
			}
			// в ответ получаем номер блока
			blockIdBin := make([]byte, 4)
			_, err = conn.Read(blockIdBin)
			if err != nil {
				conn.Close()
				if d.dPrintSleep(err, 1) {
					break BEGIN
				}
				continue
			}
			conn.Close()
			id := utils.BinToDec(blockIdBin)
			if id > maxBlockId || i == 0 {
				maxBlockId = id
				maxBlockIdHost = hosts[i]["host"]
				maxBlockIdUserId = utils.StrToInt64(hosts[i]["user_id"])
			}
			if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) {
				utils.Sleep(1)
				break BEGIN
			}
		}

		// получим наш текущий имеющийся номер блока
		// ждем, пока разлочится и лочим сами, чтобы не попасть в тот момент, когда данные из блока уже занесены в БД, а info_block еще не успел обновиться
		err, restart = d.dbLock()
		if restart {
			break BEGIN
		}
		if err != nil {
			if d.dPrintSleep(err, d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		currentBlockId, err = d.Single("SELECT block_id FROM info_block").Int64()
		if err != nil {
			if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		log.Info("currentBlockId", currentBlockId, "maxBlockId", maxBlockId)
		if maxBlockId <= currentBlockId {
			if d.unlockPrintSleep(utils.ErrInfo(errors.New("maxBlockId <= currentBlockId")), d.sleepTime) {
				break BEGIN
			}
			continue
		}

		fmt.Printf("\nnode: %s\n", maxBlockIdHost)

		// в цикле собираем блоки, пока не дойдем до максимального
		for blockId := currentBlockId + 1; blockId < maxBlockId+1; blockId++ {
			d.UpdMainLock()
			if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) {
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				break BEGIN
			}
			variables, err := d.GetAllVariables()
			if err != nil {
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			// качаем тело блока с хоста maxBlockIdHost
			binaryBlock, err := utils.GetBlockBody(maxBlockIdHost, blockId, dataTypeBlockBody, nodeHost)

			if len(binaryBlock) == 0 {
				// баним на 1 час хост, который дал нам пустой блок, хотя должен был дать все до максимального
				// для тестов убрал, потом вставить.
				//nodes_ban ($db, $max_block_id_user_id, substr($binary_block, 0, 512)."\n".__FILE__.', '.__LINE__.', '. __FUNCTION__.', '.__CLASS__.', '. __METHOD__);
				//p.NodesBan(maxBlockIdUserId, "len(binaryBlock) == 0")
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			binaryBlockFull := binaryBlock
			utils.BytesShift(&binaryBlock, 1) // уберем 1-й байт - тип (блок/тр-я)
			// распарсим заголовок блока
			blockData := utils.ParseBlockHeader(&binaryBlock)
			log.Info("blockData: %v, blockId: %v", blockData, blockId)

			// если существуют глючная цепочка, тот тут мы её проигнорируем
			badBlocks_, err := d.Single("SELECT bad_blocks FROM config").Bytes()
			if err != nil {
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			badBlocks := make(map[int64]string)
			if len(badBlocks_) > 0 {
				err = json.Unmarshal(badBlocks_, &badBlocks)
				if err != nil {
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}
			if badBlocks[blockData.BlockId] == string(utils.BinToHex(blockData.Sign)) {
				d.NodesBan(maxBlockIdUserId, fmt.Sprintf("bad_block = %v => %v", blockData.BlockId, badBlocks[blockData.BlockId]))
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}

			// размер блока не может быть более чем max_block_size
			if currentBlockId > 1 {
				if int64(len(binaryBlock)) > variables.Int64["max_block_size"] {
					d.NodesBan(maxBlockIdUserId, fmt.Sprintf(`len(binaryBlock) > variables.Int64["max_block_size"]  %v > %v`, len(binaryBlock), variables.Int64["max_block_size"]))
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}

			if blockData.BlockId != blockId {
				d.NodesBan(maxBlockIdUserId, fmt.Sprintf(`blockData.BlockId != blockId  %v > %v`, blockData.BlockId, blockId))
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}

			// нам нужен хэш предыдущего блока, чтобы проверить подпись
			prevBlockHash := ""
			if blockId > 1 {
				prevBlockHash, err = d.Single("SELECT hash FROM block_chain WHERE id = ?", blockId-1).String()
				if err != nil {
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				prevBlockHash = string(utils.BinToHex([]byte(prevBlockHash)))
			} else {
				prevBlockHash = "0"
			}
			first := false
			if blockId == 1 {
				first = true
			}
			// нам нужен меркель-рут текущего блока
			mrklRoot, err := utils.GetMrklroot(binaryBlock, variables, first)
			if err != nil {
				d.NodesBan(maxBlockIdUserId, fmt.Sprintf(`%v`, err))
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}

			// публичный ключ того, кто этот блок сгенерил
			nodePublicKey, err := d.GetNodePublicKey(blockData.UserId)
			if err != nil {
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}

			// SIGN от 128 байта до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT
			forSign := fmt.Sprintf("0,%v,%v,%v,%v,%v,%s", blockData.BlockId, prevBlockHash, blockData.Time, blockData.UserId, blockData.Level, mrklRoot)

			// проверяем подпись
			if !first {
				_, err = utils.CheckSign([][]byte{nodePublicKey}, forSign, blockData.Sign, true)
			}

			// качаем предыдущие блоки до тех пор, пока отличается хэш предыдущего.
			// другими словами, пока подпись с prevBlockHash будет неверной, т.е. пока что-то есть в $error
			if err != nil {
				log.Error("%v", utils.ErrInfo(err))
				if blockId < 1 {
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				// нужно привести данные в нашей БД в соответствие с данными у того, у кого качаем более свежий блок
				//func (p *Parser) GetOldBlocks (userId, blockId int64, host string, hostUserId int64, goroutineName, getBlockScriptName, addNodeHost string) error {
				err := parser.GetOldBlocks(blockData.UserId, blockId-1, maxBlockIdHost, maxBlockIdUserId, GoroutineName, dataTypeBlockBody, nodeHost)
				if err != nil {
					log.Error("%v", err)
					d.NodesBan(maxBlockIdUserId, fmt.Sprintf(`blockId: %v / %v`, blockId, err))
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}

			} else {

				log.Info("plug found blockId=%v\n", blockId)

				// получим наши транзакции в 1 бинарнике, просто для удобства
				var transactions []byte
				utils.WriteSelectiveLog("SELECT data FROM transactions WHERE verified = 1 AND used = 0")
				rows, err := d.Query("SELECT data FROM transactions WHERE verified = 1 AND used = 0")
				if err != nil {
					utils.WriteSelectiveLog(err)
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				for rows.Next() {
					var data []byte
					err = rows.Scan(&data)
					utils.WriteSelectiveLog(utils.BinToHex(data))
					if err != nil {
						rows.Close()
						if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
							break BEGIN
						}
						continue BEGIN
					}
					transactions = append(transactions, utils.EncodeLengthPlusData(data)...)
				}
				rows.Close()
				if len(transactions) > 0 {
					// отмечаем, что эти тр-ии теперь нужно проверять по новой
					utils.WriteSelectiveLog("UPDATE transactions SET verified = 0 WHERE verified = 1 AND used = 0")
					affect, err := d.ExecSqlGetAffect("UPDATE transactions SET verified = 0 WHERE verified = 1 AND used = 0")
					if err != nil {
						utils.WriteSelectiveLog(err)
						if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
							break BEGIN
						}
						continue BEGIN
					}
					utils.WriteSelectiveLog("affect: " + utils.Int64ToStr(affect))
					// откатываем по фронту все свежие тр-ии
					parser.BinaryData = transactions
					err = parser.ParseDataRollbackFront(false)
					if err != nil {
						utils.Sleep(1)
						continue BEGIN
					}
				}

				err = parser.RollbackTransactionsTestblock(true)
				if err != nil {
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				err = d.ExecSql("DELETE FROM testblock")
				if err != nil {
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}

			// теперь у нас в таблицах всё тоже самое, что у нода, у которого качаем блок
			// и можем этот блок проверить и занести в нашу БД
			parser.BinaryData = binaryBlockFull
			err = parser.ParseDataFull()
			if err == nil {
				err = parser.InsertIntoBlockchain()
				if err != nil {
					if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}
			// начинаем всё с начала уже с другими нодами. Но у нас уже могут быть новые блоки до $block_id, взятые от нода, которого с в итоге мы баним
			if err != nil {
				d.NodesBan(maxBlockIdUserId, fmt.Sprintf(`blockId: %v / %v`, blockId, err))
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
		}

		d.dbUnlock()

		if d.dSleep(d.sleepTime) {
			break BEGIN
		}
	}

	log.Debug("break BEGIN %v", GoroutineName)
}
示例#3
0
func (c *Controller) BlockExplorer() (string, error) {

	var err error

	blockId := int64(utils.StrToFloat64(c.Parameters["blockId"]))
	start := int64(utils.StrToFloat64(c.Parameters["start"]))

	var data, sql string
	if start > 0 || (start == 0 && blockId == 0) {
		if start == 0 && blockId == 0 {
			data += "<h3>Latest Blocks</h3>"
			sql = `	SELECT data,  hash
						FROM block_chain
						ORDER BY id DESC
						LIMIT 15`
		} else {
			sql = `	SELECT data,  hash
						FROM block_chain
						ORDER BY id ASC
						LIMIT ` + utils.Int64ToStr(start-1) + `, 100`
		}
		data += `<table class="table"><tr><th>Block</th><th>Hash</th><th>Time</th><th><nobr>User id</nobr></th><th><nobr>Miner id</nobr></th><th>Level</th><th>Transactions</th></tr>`
		blocksChain, err := c.GetAll(sql, -1)
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		for _, blockData := range blocksChain {
			hash := utils.BinToHex([]byte(blockData["hash"]))
			binaryData := []byte(blockData["data"])
			parser := new(dcparser.Parser)
			parser.DCDB = c.DCDB
			parser.BinaryData = binaryData
			err = parser.ParseDataLite()
			parser.BlockData.Sign = utils.BinToHex(parser.BlockData.Sign)
			minerId, err := c.GetMinerId(parser.BlockData.UserId)
			if err != nil {
				return "", utils.ErrInfo(err)
			}
			data += fmt.Sprintf(`<tr><td><a href="#" onclick="dc_navigate('blockExplorer', {'blockId':%d})">%d</a></td><td>%s</td><td><nobr><span class='unixtime'>%d</span></nobr></td><td>%d</td><td>%d</td><td>%d</td><td>`, parser.BlockData.BlockId, parser.BlockData.BlockId, hash, parser.BlockData.Time, parser.BlockData.UserId, minerId, parser.BlockData.Level)
			data += utils.IntToStr(len(parser.TxMapArr))
			data += "</td></tr>"
		}
		data += "</table>"
	} else if blockId > 0 {
		data += `<table class="table">`
		blockChain, err := c.OneRow("SELECT data, hash, cur_0l_miner_id, max_miner_id FROM block_chain WHERE id = ?", blockId).String()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		binToHexArray := []string{"sign", "public_key", "encrypted_message", "comment", "bin_public_keys"}
		hash := utils.BinToHex([]byte(blockChain["hash"]))
		binaryData := blockChain["data"]
		parser := new(dcparser.Parser)
		parser.DCDB = c.DCDB
		parser.BinaryData = []byte(binaryData)
		err = parser.ParseDataLite()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		parser.BlockData.Sign = utils.BinToHex(parser.BlockData.Sign)
		previous := parser.BlockData.BlockId - 1
		next := parser.BlockData.BlockId + 1
		levelsRange := utils.GetBlockGeneratorMinerIdRange(utils.StrToInt64(blockChain["cur_0l_miner_id"]), utils.StrToInt64(blockChain["max_miner_id"]))
		minerId, err := c.GetMinerId(parser.BlockData.UserId)
		if err != nil {
			return "", utils.ErrInfo(err)
		}

		_, _, _, CurrentUserId, _, _, _ := c.TestBlock()
		maxMinerId, err := c.Single("SELECT max(miner_id) FROM miners").Int64()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		currentMinerId, err := c.Single("SELECT miner_id FROM miners_data WHERE user_id = ?", CurrentUserId).Int64()
		if err != nil {
			return "", utils.ErrInfo(err)
		}
		NextBlockLevelsRange := utils.GetBlockGeneratorMinerIdRange(currentMinerId, maxMinerId)

		data += fmt.Sprintf(`<tr><td><strong>Raw&nbsp;data</strong></td><td><a href='ajax?controllerName=getBlock&id=%d&download=1' target='_blank'>Download</a></td></tr>`, parser.BlockData.BlockId)
		data += fmt.Sprintf(`<tr><td><strong>Block_id</strong></td><td>%d (<a href="#" onclick="dc_navigate('blockExplorer', {'blockId':%d})">Previous</a> / <a href="#" onclick="dc_navigate('blockExplorer', {'blockId':%d})">Next</a> )</td></tr>`, parser.BlockData.BlockId, previous, next)
		data += fmt.Sprintf(`<tr><td><strong>Hash</strong></td><td>%s</td></tr>`, hash)
		data += fmt.Sprintf(`<tr><td><strong>Time</strong></td><td><span class='unixtime'>%d</span> / %d</td></tr>`, parser.BlockData.Time, parser.BlockData.Time)
		data += fmt.Sprintf(`<tr><td><strong>User_id</strong></td><td>%d</td></tr>`, parser.BlockData.UserId)
		data += fmt.Sprintf(`<tr><td><strong>Miner_Id</strong></td><td>%d</td></tr>`, minerId)
		data += fmt.Sprintf(`<tr><td><strong>Level</strong></td><td>%d (%v) next: %v</td></tr>`, parser.BlockData.Level, levelsRange, NextBlockLevelsRange)
		data += fmt.Sprintf(`<tr><td><strong>Sign</strong></td><td>%s</td></tr>`, parser.BlockData.Sign)
		if len(parser.TxMapArr) > 0 {
			data += `<tr><td><strong>Transactions</strong></td><td><div><pre style='width: 700px'>`
			for i := 0; i < len(parser.TxMapArr); i++ {
				for k, data_ := range parser.TxMapArr[i] {
					if utils.InSliceString(k, binToHexArray) {
						parser.TxMapArr[i][k] = utils.BinToHex(data_)
					}
					if k == "file" {
						parser.TxMapArr[i][k] = []byte("file size: " + utils.IntToStr(len(data_)))
					} else if k == "code" {
						parser.TxMapArr[i][k] = utils.DSha256(data_)
					} else if k == "secret" {
						parser.TxMapArr[i][k] = utils.BinToHex(data_)
					}
					data += fmt.Sprintf("%v : %s\n", k, parser.TxMapArr[i][k])
				}
				data += "\n\n"
			}

			data += "</pre></div></td></tr>"
		}
		data += "</table>"
	}

	// пока панель тут
	myNotice := make(map[string]string)
	if c.SessUserId > 0 {
		myNotice, err = c.GetMyNoticeData(c.SessUserId, c.SessUserId, c.MyPrefix, c.Lang)
		if err != nil {
			return "", utils.ErrInfo(err)
		}
	}

	TemplateStr, err := makeTemplate("block_explorer", "blockExplorer", &BlockExplorerPage{
		Lang:           c.Lang,
		CurrencyList:   c.CurrencyListCf,
		MyNotice:       myNotice,
		Data:           data,
		Start:          start,
		BlockId:        blockId,
		PoolAdmin:      c.PoolAdmin,
		SessRestricted: c.SessRestricted,
		UserId:         c.SessUserId})
	if err != nil {
		return "", utils.ErrInfo(err)
	}
	return TemplateStr, nil
}
示例#4
0
文件: shop.go 项目: dzyk/dcoin-go
func Shop(chBreaker chan bool, chAnswer chan string) {
	defer func() {
		if r := recover(); r != nil {
			log.Error("daemon Recovered", r)
			panic(r)
		}
	}()

	const GoroutineName = "Shop"
	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 = 120
	}
	if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) {
		return
	}
	d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName)
	if d.DCDB == nil {
		return
	}

BEGIN:
	for {
		log.Info(GoroutineName)
		MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())}

		// проверим, не нужно ли нам выйти из цикла
		if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) {
			break BEGIN
		}

		myBlockId, err := d.GetMyBlockId()
		blockId, err := d.GetBlockId()
		if myBlockId > blockId {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		currencyList, err := d.GetCurrencyList(false)
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		// нужно знать текущий блок, который есть у большинства нодов
		blockId, err = d.GetConfirmedBlockId()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}

		// сколько должно быть подтверждений, т.е. кол-во блоков сверху
		confirmations := int64(5)

		// берем всех юзеров по порядку
		community, err := d.GetCommunityUsers()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		for _, userId := range community {
			privateKey := ""
			myPrefix := utils.Int64ToStr(userId) + "_"
			allTables, err := d.GetAllTables()
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			if !utils.InSliceString(myPrefix+"my_keys", allTables) {
				continue
			}
			// проверим, майнер ли
			minerId, err := d.GetMinerId(userId)
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			if minerId > 0 {
				// наш приватный ключ нода, которым будем расшифровывать комменты
				privateKey, err = d.GetNodePrivateKey(myPrefix)
				if err != nil {
					if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}
			// возможно, что комменты будут зашифрованы юзерским ключем
			if len(privateKey) == 0 {
				privateKey, err = d.GetMyPrivateKey(myPrefix)
				if err != nil {
					if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}
			// если это еще не майнер и админ ноды не указал его приватный ключ в табле my_keys, то $private_key будет пуст
			if len(privateKey) == 0 {
				continue
			}
			myData, err := d.OneRow("SELECT shop_secret_key, shop_callback_url FROM " + myPrefix + "my_table").String()
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}

			// Получаем инфу о входящих переводах и начисляем их на счета юзеров
			dq := d.GetQuotes()
			rows, err := d.Query(d.FormatQuery(`
					SELECT id, block_id, type_id, currency_id, amount, to_user_id, comment_status, comment
					FROM `+dq+myPrefix+`my_dc_transactions`+dq+`
					WHERE type = 'from_user' AND
								 block_id < ? AND
								 merchant_checked = 0 AND
								 status = 'approved'
					ORDER BY id DESC
					`), blockId-confirmations)
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			for rows.Next() {
				var id, block_id, type_id, currency_id, to_user_id int64
				var comment_status, comment string
				var amount float64
				err = rows.Scan(&id, &block_id, &type_id, &currency_id, &amount, &to_user_id, &comment_status, &comment)
				if err != nil {
					rows.Close()
					if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				if len(myData["shop_callback_url"]) == 0 {
					// отметим merchant_checked=1, чтобы больше не брать эту тр-ию
					err = d.ExecSql("UPDATE "+myPrefix+"my_dc_transactions SET merchant_checked = 1 WHERE id = ?", id)
					if err != nil {
						rows.Close()
						if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
							break BEGIN
						}
						continue BEGIN
					}
					continue
				}

				// вначале нужно проверить, точно ли есть такой перевод в блоке
				binaryData, err := d.Single("SELECT data FROM block_chain WHERE id  =  ?", blockId).Bytes()
				if err != nil {
					rows.Close()
					if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
				p := new(dcparser.Parser)
				p.DCDB = d.DCDB
				p.BinaryData = binaryData
				p.ParseDataLite()
				for _, txMap := range p.TxMapArr {

					// пропускаем все ненужные тр-ии
					if utils.BytesToInt64(txMap["type"]) != utils.TypeInt("SendDc") {
						continue
					}

					// сравнение данных из таблы my_dc_transactions с тем, что в блоке
					if utils.BytesToInt64(txMap["user_id"]) == userId && utils.BytesToInt64(txMap["currency_id"]) == currency_id && utils.BytesToFloat64(txMap["amount"]) == amount && string(utils.BinToHex(txMap["comment"])) == comment && utils.BytesToInt64(txMap["to_user_id"]) == to_user_id {
						decryptedComment := comment
						// расшифруем коммент
						if comment_status == "encrypted" {
							block, _ := pem.Decode([]byte(privateKey))
							if block == nil || block.Type != "RSA PRIVATE KEY" {
								rows.Close()
								if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
									break BEGIN
								}
								continue BEGIN
							}
							private_key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
							if err != nil {
								rows.Close()
								if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
									break BEGIN
								}
								continue BEGIN
							}
							decryptedComment_, err := rsa.DecryptPKCS1v15(rand.Reader, private_key, []byte(comment))
							if err != nil {
								rows.Close()
								if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
									break BEGIN
								}
								continue BEGIN
							}
							decryptedComment = string(decryptedComment_)
							// запишем расшифрованный коммент, чтобы потом можно было найти перевод в ручном режиме
							err = d.ExecSql("UPDATE "+myPrefix+"my_dc_transactions SET comment = ?, comment_status = 'decrypted' WHERE id = ?", decryptedComment, id)
							if err != nil {
								rows.Close()
								if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
									break BEGIN
								}
								continue BEGIN
							}
						}

						// возможно, что чуть раньше было reduction, а это значит, что все тр-ии,
						// которые мы ещё не обработали и которые были До блока с reduction нужно принимать с учетом reduction
						// т.к. средства на нашем счете уже урезались, а  вот те, что после reduction - остались в том виде, в котором пришли
						lastReduction, err := d.OneRow("SELECT block_id, pct FROM reduction WHERE currency_id  = ? ORDER BY block_id", currency_id).Int64()
						if err != nil {
							rows.Close()
							if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
								break BEGIN
							}
							continue BEGIN
						}
						if blockId <= lastReduction["block_id"] {
							// сумму с учетом reduction
							k0 := (100 - lastReduction["pct"]) / 100
							amount = amount * float64(k0)
						}

						// делаем запрос к callback скрипту
						r, _ := regexp.Compile(`(?i)\s*#\s*([0-9]+)\s*`)
						order := r.FindStringSubmatch(decryptedComment)
						orderId := 0
						if len(order) > 0 {
							orderId = utils.StrToInt(order[1])
						}
						txId := id
						sign := fmt.Sprintf("%v:%v:%v:%v:%v:%v:%v:%v", amount, currencyList[currency_id], orderId, decryptedComment, txMap["user_id"], blockId, txId, myData["shop_secret_key"])
						data := url.Values{}
						data.Add("amount", utils.Float64ToStrPct(amount))
						data.Add("currency", currencyList[currency_id])
						data.Add("order_id", utils.IntToStr(orderId))
						data.Add("message", decryptedComment)
						data.Add("user_id", string(txMap["user_id"]))
						data.Add("block_id", string(txMap["block_id"]))
						data.Add("tx_id", utils.Int64ToStr(txId))
						data.Add("sign", sign)

						client := &http.Client{}
						req, err := http.NewRequest("POST", myData["shop_callback_url"], bytes.NewBufferString(data.Encode()))
						if err != nil {
							rows.Close()
							if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
								break BEGIN
							}
							continue BEGIN
						}
						req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
						req.Header.Add("Content-Length", utils.IntToStr(len(data.Encode())))

						resp, err := client.Do(req)
						if err != nil {
							rows.Close()
							if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
								break BEGIN
							}
							continue BEGIN
						}
						//contents, _ := ioutil.ReadAll(resp.Body)
						if resp.StatusCode == 200 {
							// отметим merchant_checked=1, чтобы больше не брать эту тр-ию
							err = d.ExecSql("UPDATE "+myPrefix+"my_dc_transactions SET merchant_checked = 1 WHERE id = ?", id)
							if err != nil {
								rows.Close()
								if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
									break BEGIN
								}
								continue BEGIN
							}
						}
					}
				}
			}
			rows.Close()
		}

		if d.dSleep(d.sleepTime) {
			break BEGIN
		}
	}
	log.Debug("break BEGIN %v", GoroutineName)
}
示例#5
0
func TestblockIsReady(chBreaker chan bool, chAnswer chan string) {
	defer func() {
		if r := recover(); r != nil {
			log.Error("daemon Recovered", r)
			panic(r)
		}
	}()

	const GoroutineName = "TestblockIsReady"
	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 = 1
	}
	if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) {
		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
		}

		LocalGateIp, err := d.GetMyLocalGateIp()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		if len(LocalGateIp) > 0 {
			if d.dPrintSleep(utils.ErrInfo(errors.New("len(LocalGateIp) > 0")), d.sleepTime) {
				break BEGIN
			}
			continue
		}

		// сколько нужно спать
		prevBlock, myUserId, myMinerId, currentUserId, level, levelsRange, err := d.TestBlock()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		log.Info("%v", prevBlock, myUserId, myMinerId, currentUserId, level, levelsRange)

		if myMinerId == 0 {
			log.Debug("myMinerId == 0")
			if d.dSleep(d.sleepTime) {
				break BEGIN
			}
			continue
		}

		sleepData, err := d.GetSleepData()
		sleep := d.GetIsReadySleep(prevBlock.Level, sleepData["is_ready"])
		prevHeadHash := prevBlock.HeadHash

		// Если случится откат или придет новый блок, то testblock станет неактуален
		startSleep := utils.Time()
		for i := 0; i < int(sleep); i++ {
			err, restart := d.dbLock()
			if restart {
				break BEGIN
			}
			if err != nil {
				if d.dPrintSleep(err, d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}

			newHeadHash, err := d.Single("SELECT head_hash FROM info_block").String()
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue
			}
			d.dbUnlock()
			newHeadHash = string(utils.BinToHex([]byte(newHeadHash)))
			if newHeadHash != prevHeadHash {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			log.Info("%v", "i", i, "time", utils.Time())
			if utils.Time()-startSleep > sleep {
				break
			}
			utils.Sleep(1) // спим 1 сек. общее время = $sleep
		}

		/*
			Заголовок
			TYPE (0-блок, 1-тр-я)       FF (256)
			BLOCK_ID   				       FF FF FF FF (4 294 967 295)
			TIME       					       FF FF FF FF (4 294 967 295)
			USER_ID                          FF FF FF FF FF (1 099 511 627 775)
			LEVEL                              FF (256)
			SIGN                               от 128 байта до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT
			Далее - тело блока (Тр-ии)
		*/

		// блокируем изменения данных в тестблоке
		// также, нужно блокировать main, т.к. изменение в info_block и block_chain ведут к изменению подписи в testblock
		err, restart := d.dbLock()
		if restart {
			break BEGIN
		}
		if err != nil {
			if d.dPrintSleep(err, d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		// за промежуток в main_unlock и main_lock мог прийти новый блок
		prevBlock, myUserId, myMinerId, currentUserId, level, levelsRange, err = d.TestBlock()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		log.Info("%v", prevBlock, myUserId, myMinerId, currentUserId, level, levelsRange)

		// на всякий случай убедимся, что блок не изменился
		if prevBlock.HeadHash != prevHeadHash {
			if d.unlockPrintSleep(utils.ErrInfo(errors.New("prevBlock.HeadHash != prevHeadHash")), d.sleepTime) {
				break BEGIN
			}
			continue
		}

		// составим блок. заголовок + тело + подпись
		testBlockData, err := d.OneRow("SELECT * FROM testblock WHERE status  =  'active'").String()
		if err != nil {
			if d.unlockPrintSleep(utils.ErrInfo(errors.New("prevBlock.HeadHash != prevHeadHash")), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		log.Debug("testBlockData: %v", testBlockData)
		if len(testBlockData) == 0 {
			if d.unlockPrintSleep(utils.ErrInfo(errors.New("null $testblock_data")), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		// получим транзакции
		var testBlockDataTx []byte
		transactionsTestBlock, err := d.GetList("SELECT data FROM transactions_testblock ORDER BY id ASC").String()
		if err != nil {
			if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		for _, data := range transactionsTestBlock {
			testBlockDataTx = append(testBlockDataTx, utils.EncodeLengthPlusData([]byte(data))...)
		}

		// в промежутке межде тем, как блок был сгенерирован и запуском данного скрипта может измениться текущий блок
		// поэтому нужно проверять подпись блока из тестблока
		prevBlockHash, err := d.Single("SELECT hash FROM info_block").Bytes()
		if err != nil {
			if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		prevBlockHash = utils.BinToHex(prevBlockHash)
		nodePublicKey, err := d.GetNodePublicKey(utils.StrToInt64(testBlockData["user_id"]))
		if err != nil {
			if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		forSign := fmt.Sprintf("0,%v,%s,%v,%v,%v,%s", testBlockData["block_id"], prevBlockHash, testBlockData["time"], testBlockData["user_id"], testBlockData["level"], utils.BinToHex([]byte(testBlockData["mrkl_root"])))
		log.Debug("forSign %v", forSign)
		log.Debug("signature %x", testBlockData["signature"])

		p := new(dcparser.Parser)
		p.DCDB = d.DCDB
		// проверяем подпись
		_, err = utils.CheckSign([][]byte{nodePublicKey}, forSign, []byte(testBlockData["signature"]), true)
		if err != nil {
			log.Error("incorrect signature %v")
			p.RollbackTransactionsTestblock(true)
			err = d.ExecSql("DELETE FROM testblock")
			if err != nil {
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		// БАГ
		if utils.StrToInt64(testBlockData["block_id"]) == prevBlock.BlockId {
			log.Error("testBlockData block_id =  prevBlock.BlockId (%v=%v)", testBlockData["block_id"], prevBlock.BlockId)

			err = p.RollbackTransactionsTestblock(true)
			if err != nil {
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			err = d.ExecSql("DELETE FROM testblock")
			if err != nil {
				if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		// готовим заголовок
		newBlockIdBinary := utils.DecToBin(utils.StrToInt64(testBlockData["block_id"]), 4)
		timeBinary := utils.DecToBin(utils.StrToInt64(testBlockData["time"]), 4)
		userIdBinary := utils.DecToBin(utils.StrToInt64(testBlockData["user_id"]), 5)
		levelBinary := utils.DecToBin(utils.StrToInt64(testBlockData["level"]), 1)
		//prevBlockHashBinary := prevBlock.Hash
		//merkleRootBinary := testBlockData["mrklRoot"];

		// заголовок
		blockHeader := utils.DecToBin(0, 1)
		blockHeader = append(blockHeader, newBlockIdBinary...)
		blockHeader = append(blockHeader, timeBinary...)
		blockHeader = append(blockHeader, userIdBinary...)
		blockHeader = append(blockHeader, levelBinary...)
		blockHeader = append(blockHeader, utils.EncodeLengthPlusData([]byte(testBlockData["signature"]))...)

		// сам блок
		block := append(blockHeader, testBlockDataTx...)
		log.Debug("block %x", block)

		// теперь нужно разнести блок по таблицам и после этого мы будем его слать всем нодам скриптом disseminator.php
		p.BinaryData = block
		err = p.ParseDataFront()
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		// и можно удалять данные о тестблоке, т.к. они перешел в нормальный блок
		err = d.ExecSql("DELETE FROM transactions_testblock")
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}
		err = d.ExecSql("DELETE FROM testblock")
		if err != nil {
			if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue BEGIN
		}

		// между testblock_generator и testbock_is_ready
		p.RollbackTransactionsTestblock(false)

		d.dbUnlock()

		if d.dSleep(d.sleepTime) {
			break BEGIN
		}
	}
	log.Debug("break BEGIN %v", GoroutineName)
}
示例#6
0
func QueueParserTestblock(chBreaker chan bool, chAnswer chan string) {
	defer func() {
		if r := recover(); r != nil {
			log.Error("daemon Recovered", r)
			panic(r)
		}
	}()

	const GoroutineName = "QueueParserTestblock"
	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 = 1800
	} else {
		d.sleepTime = 1
	}
	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
		}

		data, err := d.OneRow("SELECT * FROM queue_testblock ORDER BY head_hash ASC").String()
		if err != nil {
			if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) {
				break BEGIN
			}
			continue
		}
		if len(data) == 0 {
			if d.unlockPrintSleepInfo(utils.ErrInfo(errors.New("len(data) == 0")), d.sleepTime) {
				break BEGIN
			}
			continue
		}

		newBlock := []byte(data["data"])
		newHeaderHash := utils.BinToHex([]byte(data["head_hash"]))
		tx := utils.DeleteHeader(newBlock)

		// сразу можно удалять данные из таблы-очереди
		err = d.ExecSql("DELETE FROM queue_testblock WHERE hex(head_hash) = ?", newHeaderHash)
		if err != nil {
			if d.unlockPrintSleep(utils.ErrInfo(errors.New("len(data) == 0")), d.sleepTime) {
				break BEGIN
			}
			continue
		}

		// прежде всего нужно проверить, а нет ли в этом блоке ошибок с несовметимыми тр-ми
		// при полной проверке, а не только фронтальной проблем с несовместимыми тр-ми не будет, т.к. там даные сразу пишутся в таблицы
		// а тут у нас данные пишутся только в log_time_
		// и сами тр-ии пишем в отдельную таблу
		p := new(dcparser.Parser)
		p.DCDB = d.DCDB
		if len(tx) > 0 {
			log.Debug("len(tx): %d", len(tx))
			for {
				log.Debug("tx: %x", tx)
				txSize := utils.DecodeLength(&tx)
				log.Debug("txSize: %d", txSize)
				// отделим одну транзакцию от списка транзакций
				txBinaryData := utils.BytesShift(&tx, txSize)
				log.Debug("txBinaryData: %x", txBinaryData)
				// проверим, нет ли несовместимых тр-ий
				fatalError, waitError, _, _, _, _ := p.ClearIncompatibleTx(txBinaryData, false)
				if len(fatalError) > 0 || len(waitError) > 0 {
					if d.unlockPrintSleep(utils.ErrInfo(errors.New(" len(fatalError) > 0 || len(waitError) > 0")), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}

				if len(tx) == 0 {
					break
				}
			}
		}
		// откатим тр-ии тестблока, но не удаляя их, т.к. далее еще можем их вернуть
		p.RollbackTransactionsTestblock(false)

		// проверим блок, который получился с данными, которые прислал другой нод
		p.BinaryData = newBlock
		err = p.ParseDataGate(false)
		if err != nil {

			log.Error("%v", err)

			// т.к. мы откатили наши тр-ии из transactions_testblock, то теперь нужно обработать их по новой
			// получим наши транзакции в 1 бинарнике, просто для удобства

			var myTestBlockBody []byte
			transactionsTestblock, err := d.GetAll("SELECT data FROM transactions_testblock ORDER BY id ASC", -1)
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			for _, data := range transactionsTestblock {
				myTestBlockBody = append(myTestBlockBody, []byte(data["data"])...)
			}

			if len(myTestBlockBody) > 0 {
				p.BinaryData = append(utils.DecToBin(0, 1), myTestBlockBody...)
				err = p.ParseDataGate(true)
				if err != nil {
					if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}
		} else {
			// наши тр-ии уже не актуальны, т.к. мы их откатили
			err = d.ExecSql("DELETE FROM transactions_testblock")
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
			exists, err := d.Single(`SELECT block_id FROM testblock`).Int64()
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				return
			}
			if exists > 0 {
				// если всё нормально, то пишем в таблу testblock новые тр-ии и новые данные по юзеру их сгенерившему
				err = d.ExecSql(`
				UPDATE testblock
				SET  time = ?,
						user_id = ?,
						header_hash = [hex],
						signature = [hex],
						mrkl_root = [hex]
				`, p.BlockData.Time, p.BlockData.UserId, newHeaderHash, utils.BinToHex(p.BlockData.Sign), p.MrklRoot)
				if err != nil {
					if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
						break BEGIN
					}
					continue BEGIN
				}
			}

			// и сами тр-ии пишем в отдельную таблу
			if len(tx) > 0 {
				for {
					txSize := utils.DecodeLength(&tx)
					// отчекрыжим одну транзакцию от списка транзакций
					txBinaryData := utils.BytesShift(&tx, txSize)
					// получим тип тр-ии и юзера
					txType, userId, toUserId := utils.GetTxTypeAndUserId(txBinaryData)
					md5 := utils.Md5(txBinaryData)
					dataHex := utils.BinToHex(txBinaryData)

					err = d.ExecSql("DELETE FROM transactions_testblock")
					if err != nil {
						if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
							break BEGIN
						}
						continue BEGIN
					}

					err = d.ExecSql("INSERT INTO transactions_testblock (hash, data, type, user_id, third_var) VALUES ([hex], [hex], ?, ?, ?)", md5, dataHex, txType, userId, toUserId)
					if err != nil {
						if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
							break BEGIN
						}
						continue BEGIN
					}

					if len(tx) == 0 {
						break
					}
				}
			}

			// удаляем всё, где хэш больше нашего
			err = d.ExecSql("DELETE FROM queue_testblock WHERE hex(head_hash) > ?", newHeaderHash)
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}

			// возможно нужно откатить и тр-ии с verified=1 и used=0 из transactions
			// т.к. в transactions может быть тр-ия на удаление банкноты
			// и в transactions_testblock только что была залита такая же тр-ия
			// выходит, что блок, который будет сгенерен на основе transactions будет ошибочным
			// или при откате transactions будет сделан вычет из log_time_....
			// и выйдет что попавшая в блок тр-я из transactions_testblock попала минуя запись  log_time_....
			err = p.RollbackTransactions()
			if err != nil {
				if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) {
					break BEGIN
				}
				continue BEGIN
			}
		}

		d.dbUnlock()

		if d.dSleep(d.sleepTime) {
			break BEGIN
		}
	}
	log.Debug("break BEGIN %v", GoroutineName)

}