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, ¤cyId, &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, ¤cyId, &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, ¤cyId, &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) }
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) }
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 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 }
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, ¤cy_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) }
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) }
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) }