func (t *TcpServer) Type7() { /* Выдаем тело указанного блока * запрос шлет демон blocksCollection и queue_parser_blocks через p.GetBlocks() */ buf := make([]byte, 4) _, err := t.Conn.Read(buf) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } blockId := utils.BinToDec(buf) block, err := t.Single("SELECT data FROM block_chain WHERE id = ?", blockId).Bytes() if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("blockId %x", blockId) log.Debug("block %x", block) err = utils.WriteSizeAndData(block, t.Conn) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } }
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 (t *TcpServer) Type6() { /** - проверяем, находится ли отправитель на одном с нами уровне - получаем block_id, user_id, mrkl_root, signature - если хэш блока меньше того, что есть у нас в табле testblock, то смотртим, есть ли такой же хэш тр-ий, - если отличается, то загружаем блок от отправителя - если не отличается, то просто обновляем хэш блока у себя данные присылает демон testblockDisseminator */ currentBlockId, err := t.GetBlockId() if err != nil { log.Error("%v", utils.ErrInfo(err)) return } if currentBlockId == 0 { log.Debug("%v", utils.ErrInfo("currentBlockId == 0")) return } buf := make([]byte, 4) _, err = t.Conn.Read(buf) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } size := utils.BinToDec(buf) log.Debug("size: %v", size) if size < 10485760 { binaryData := make([]byte, size) //binaryData, err = ioutil.ReadAll(t.Conn) _, err = io.ReadFull(t.Conn, binaryData) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("binaryData: %x", binaryData) newTestblockBlockId := utils.BinToDecBytesShift(&binaryData, 4) newTestblockTime := utils.BinToDecBytesShift(&binaryData, 4) newTestblockUserId := utils.BinToDecBytesShift(&binaryData, 4) newTestblockMrklRoot := utils.BinToHex(utils.BytesShift(&binaryData, 32)) newTestblockSignatureHex := utils.BinToHex(utils.BytesShift(&binaryData, utils.DecodeLength(&binaryData))) log.Debug("newTestblockBlockId: %v", newTestblockBlockId) log.Debug("newTestblockTime: %v", newTestblockTime) log.Debug("newTestblockUserId: %v", newTestblockUserId) log.Debug("newTestblockMrklRoot: %s", newTestblockMrklRoot) log.Debug("newTestblockSignatureHex: %s", newTestblockSignatureHex) if !utils.CheckInputData(newTestblockBlockId, "int") { log.Debug("%v", utils.ErrInfo("incorrect newTestblockBlockId")) return } if !utils.CheckInputData(newTestblockTime, "int") { log.Debug("%v", utils.ErrInfo("incorrect newTestblockTime")) return } if !utils.CheckInputData(newTestblockUserId, "int") { log.Debug("%v", utils.ErrInfo("incorrect newTestblockUserId")) return } if !utils.CheckInputData(newTestblockMrklRoot, "sha256") { log.Debug("%v", utils.ErrInfo("incorrect newTestblockMrklRoot")) return } /* * Проблема одновременных попыток локнуть. Надо попробовать без локов * */ //t.DbLockGate("6") exists, err := t.Single(` SELECT block_id FROM testblock WHERE status = 'active' `).Int64() if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } if exists == 0 { t.PrintSleep(utils.ErrInfo("null testblock"), 0) return } //prevBlock, myUserId, myMinerId, currentUserId, level, levelsRange, err := t.TestBlock() prevBlock, _, _, _, level, levelsRange, err := t.TestBlock() if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } nodesIds := utils.GetOurLevelNodes(level, levelsRange) log.Debug("nodesIds: %v ", nodesIds) log.Debug("prevBlock: %v ", prevBlock) log.Debug("level: %v ", level) log.Debug("levelsRange: %v ", levelsRange) log.Debug("newTestblockBlockId: %v ", newTestblockBlockId) // проверим, верный ли ID блока if newTestblockBlockId != prevBlock.BlockId+1 { t.PrintSleep(utils.ErrInfo(fmt.Sprintf("newTestblockBlockId != prevBlock.BlockId+1 %d!=%d+1", newTestblockBlockId, prevBlock.BlockId)), 1) return } // проверим, есть ли такой майнер minerId, err := t.Single("SELECT miner_id FROM miners_data WHERE user_id = ?", newTestblockUserId).Int64() if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } if minerId == 0 { t.PrintSleep(utils.ErrInfo("minerId == 0"), 0) return } log.Debug("minerId: %v ", minerId) // проверим, точно ли отправитель с нашего уровня if !utils.InSliceInt64(minerId, nodesIds) { t.PrintSleep(utils.ErrInfo("!InSliceInt64(minerId, nodesIds)"), 0) return } // допустимая погрешность во времени генерации блока maxErrorTime := t.variables.Int64["error_time"] // получим значения для сна sleep, err := t.GetGenSleep(prevBlock, level) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } // исключим тех, кто сгенерил блок слишком рано if prevBlock.Time+sleep-newTestblockTime > maxErrorTime { t.PrintSleep(utils.ErrInfo("prevBlock.Time + sleep - newTestblockTime > maxErrorTime"), 0) return } // исключим тех, кто сгенерил блок с бегущими часами if newTestblockTime > utils.Time() { t.PrintSleep(utils.ErrInfo("newTestblockTime > Time()"), 0) return } // получим хэш заголовка newHeaderHash := utils.DSha256(fmt.Sprintf("%v,%v,%v", newTestblockUserId, newTestblockBlockId, prevBlock.HeadHash)) myTestblock, err := t.OneRow(` SELECT block_id, user_id, hex(mrkl_root) as mrkl_root, hex(signature) as signature FROM testblock WHERE status = 'active' `).String() if len(myTestblock) > 0 { if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } // получим хэш заголовка myHeaderHash := utils.DSha256(fmt.Sprintf("%v,%v,%v", myTestblock["user_id"], myTestblock["block_id"], prevBlock.HeadHash)) // у кого меньше хэш, тот и круче hash1 := big.NewInt(0) hash1.SetString(string(newHeaderHash), 16) hash2 := big.NewInt(0) hash2.SetString(string(myHeaderHash), 16) log.Debug("%v", hash1.Cmp(hash2)) //if HexToDecBig(newHeaderHash) > string(myHeaderHash) { if hash1.Cmp(hash2) == 1 { t.PrintSleep(utils.ErrInfo(fmt.Sprintf("newHeaderHash > myHeaderHash (%s > %s)", newHeaderHash, myHeaderHash)), 0) return } /* т.к. на данном этапе в большинстве случаев наш текущий блок будет заменен, * то нужно парсить его, рассылать другим нодам и дождаться окончания проверки */ err = t.ExecSql("UPDATE testblock SET status = 'pending'") if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } } // если отличается, то загружаем недостающии тр-ии от отправителя if string(newTestblockMrklRoot) != myTestblock["mrkl_root"] { log.Debug("download new tx") sendData := "" // получим все имеющиеся у нас тр-ии, которые еще не попали в блоки txArray, err := t.GetMap(`SELECT hex(hash) as hash, data FROM transactions`, "hash", "data") if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } for hash, _ := range txArray { sendData += hash } err = utils.WriteSizeAndData([]byte(sendData), t.Conn) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } /* в ответ получаем: BLOCK_ID 4 TIME 4 USER_ID 5 SIGN от 128 до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT Размер всех тр-ий, размер 1 тр-ии, тело тр-ии. Хэши три-ий (порядок тр-ий) */ buf := make([]byte, 4) _, err = t.Conn.Read(buf) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } dataSize := utils.BinToDec(buf) log.Debug("dataSize %d", dataSize) // и если данных менее 10мб, то получаем их if dataSize < 10485760 { binaryData := make([]byte, dataSize) //binaryData, err = ioutil.ReadAll(t.Conn) _, err = io.ReadFull(t.Conn, binaryData) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } // Разбираем полученные бинарные данные newTestblockBlockId := utils.BinToDecBytesShift(&binaryData, 4) newTestblockTime := utils.BinToDecBytesShift(&binaryData, 4) newTestblockUserId := utils.BinToDecBytesShift(&binaryData, 5) newTestblockSignature := utils.BytesShift(&binaryData, utils.DecodeLength(&binaryData)) log.Debug("newTestblockBlockId %v", newTestblockBlockId) log.Debug("newTestblockTime %v", newTestblockTime) log.Debug("newTestblockUserId %v", newTestblockUserId) log.Debug("newTestblockSignature %x", newTestblockSignature) // недостающие тр-ии length := utils.DecodeLength(&binaryData) // размер всех тр-ий txBinary := utils.BytesShift(&binaryData, length) for { // берем по одной тр-ии length := utils.DecodeLength(&txBinary) // размер всех тр-ий if length == 0 { break } log.Debug("length %d", length) tx := utils.BytesShift(&txBinary, length) log.Debug("tx %x", tx) txArray[string(utils.Md5(tx))] = string(tx) } // порядок тр-ий var orderHashArray []string for { orderHashArray = append(orderHashArray, string(utils.BinToHex(utils.BytesShift(&binaryData, 16)))) if len(binaryData) == 0 { break } } // сортируем и наши и полученные транзакции var transactions []byte for _, txMd5 := range orderHashArray { transactions = append(transactions, utils.EncodeLengthPlusData([]byte(txArray[txMd5]))...) } // формируем блок, который далее будем тщательно проверять /* Заголовок (от 143 до 527 байт ) TYPE (0-блок, 1-тр-я) 1 BLOCK_ID 4 TIME 4 USER_ID 5 LEVEL 1 SIGN от 128 до 512 байт. Подпись от TYPE, BLOCK_ID, PREV_BLOCK_HASH, TIME, USER_ID, LEVEL, MRKL_ROOT Далее - тело блока (Тр-ии) */ newBlockIdBinary := utils.DecToBin(newTestblockBlockId, 4) timeBinary := utils.DecToBin(newTestblockTime, 4) userIdBinary := utils.DecToBin(newTestblockUserId, 5) levelBinary := utils.DecToBin(level, 1) newBlockHeader := utils.DecToBin(0, 1) // 0 - это блок newBlockHeader = append(newBlockHeader, newBlockIdBinary...) newBlockHeader = append(newBlockHeader, timeBinary...) newBlockHeader = append(newBlockHeader, userIdBinary...) newBlockHeader = append(newBlockHeader, levelBinary...) // $level пишем, чтобы при расчете времени ожидания в следующем блоке не пришлось узнавать, какой был max_miner_id newBlockHeader = append(newBlockHeader, utils.EncodeLengthPlusData(newTestblockSignature)...) newBlockHex := utils.BinToHex(append(newBlockHeader, transactions...)) // и передаем блок для обратотки через демон queue_parser_testblock // т.к. есть запросы к log_time_, а их можно выполнять только по очереди err = t.ExecSql(`DELETE FROM queue_testblock WHERE hex(head_hash) = ?`, newHeaderHash) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } log.Debug("INSERT INTO queue_testblock (head_hash, data) VALUES (%s, %s)", newHeaderHash, newBlockHex) err = t.ExecSql(`INSERT INTO queue_testblock (head_hash, data) VALUES ([hex], [hex])`, newHeaderHash, newBlockHex) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } } } else { // если всё нормально, то пишем в таблу testblock новые данные exists, err := t.Single(`SELECT block_id FROM testblock`).Int64() if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } if exists == 0 { err = t.ExecSql(`INSERT INTO testblock (block_id, time, level, user_id, header_hash, signature, mrkl_root) VALUES (?, ?, ?, ?, [hex], [hex], [hex])`, newTestblockBlockId, newTestblockTime, level, newTestblockUserId, string(newHeaderHash), newTestblockSignatureHex, string(newTestblockMrklRoot)) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } } else { err = t.ExecSql(` UPDATE testblock SET time = ?, user_id = ?, header_hash = [hex], signature = [hex] `, newTestblockTime, newTestblockUserId, string(newHeaderHash), string(newTestblockSignatureHex)) if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } } } err = t.ExecSql("UPDATE testblock SET status = 'active'") if err != nil { t.PrintSleep(utils.ErrInfo(err), 0) return } //t.DbUnlockGate("6") } }