/** * Демон, который мониторит таблу testblock и если видит status=active, * то шлет блок строго тем, кто находятся на одном с нами уровне. Если пошлет * тем, кто не на одном уровне, то блок просто проигнорируется * */ func TestblockDisseminator(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "TestblockDisseminator" 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 } 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 } nodeConfig, err := d.GetNodeConfig() if len(nodeConfig["local_gate_ip"]) != 0 { if d.dPrintSleep("local_gate_ip", d.sleepTime) { break BEGIN } continue } _, _, _, _, level, levelsRange, err := d.TestBlock() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } log.Debug("level: %v", level) log.Debug("levelsRange: %v", levelsRange) // получим id майнеров, которые на нашем уровне nodesIds := utils.GetOurLevelNodes(level, levelsRange) if len(nodesIds) == 0 { log.Debug("len(nodesIds) == 0") if d.dSleep(d.sleepTime) { break BEGIN } continue } log.Debug("nodesIds: %v", nodesIds) // получим хосты майнеров, которые на нашем уровне hosts_, err := d.GetList("SELECT tcp_host FROM miners_data WHERE miner_id IN (" + strings.Join(utils.SliceInt64ToString(nodesIds), `,`) + ")").String() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } hosts := []string{} for _, host := range hosts_ { if !stringInSlice(host, hosts) { hosts = append(hosts, host) } } log.Debug("hosts: %v", hosts) // шлем block_id, user_id, mrkl_root, signature data, err := d.OneRow("SELECT block_id, time, user_id, mrkl_root, signature FROM testblock WHERE status = 'active' AND sent=0").String() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } if len(data) > 0 { err = d.ExecSql("UPDATE testblock SET sent=1") if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } dataToBeSent := utils.DecToBin(utils.StrToInt64(data["block_id"]), 4) dataToBeSent = append(dataToBeSent, utils.DecToBin(data["time"], 4)...) dataToBeSent = append(dataToBeSent, utils.DecToBin(data["user_id"], 4)...) dataToBeSent = append(dataToBeSent, []byte(data["mrkl_root"])...) dataToBeSent = append(dataToBeSent, utils.EncodeLengthPlusData(data["signature"])...) for _, host := range hosts { go func(host string) { log.Debug("host: %v", host) conn, err := utils.TcpConn(host) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } defer conn.Close() // вначале шлем тип данных _, err = conn.Write(utils.DecToBin(6, 1)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // в 4-х байтах пишем размер данных, которые пошлем далее _, err = conn.Write(utils.DecToBin(len(dataToBeSent), 4)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // далее шлем сами данные log.Debug("dataToBeSent: %x", dataToBeSent) _, err = conn.Write(dataToBeSent) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } /* * Получаем тр-ии, которые есть у юзера, в ответ выдаем те, что недостают и * их порядок следования, чтобы получить валидный блок */ buf := make([]byte, 4) _, err = conn.Read(buf) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } dataSize := utils.BinToDec(buf) // и если данных менее 10мб, то получаем их if dataSize < 10485760 { data, err := d.OneRow("SELECT * FROM testblock").String() if err != nil { log.Error("%v", utils.ErrInfo(err)) return } responseBinaryData := utils.DecToBin(utils.StrToInt64(data["block_id"]), 4) responseBinaryData = append(responseBinaryData, utils.DecToBin(utils.StrToInt64(data["time"]), 4)...) responseBinaryData = append(responseBinaryData, utils.DecToBin(utils.StrToInt64(data["user_id"]), 5)...) responseBinaryData = append(responseBinaryData, utils.EncodeLengthPlusData(data["signature"])...) addSql := "" if dataSize > 0 { binaryData := make([]byte, dataSize) _, err := conn.Read(binaryData) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // разбираем присланные данные // получим хэши тр-ий, которые надо исключить for { if len(binaryData) < 16 { break } txHex := utils.BinToHex(utils.BytesShift(&binaryData, 16)) // проверим addSql += string(txHex) + "," if len(binaryData) == 0 { break } } addSql = addSql[:len(addSql)-1] addSql = "WHERE id NOT IN (" + addSql + ")" } // сами тр-ии var transactions []byte transactions_testblock, err := d.GetList(`SELECT data FROM transactions_testblock ` + addSql).String() if err != nil { log.Error("%v", utils.ErrInfo(err)) return } for _, txData := range transactions_testblock { transactions = append(transactions, utils.EncodeLengthPlusData(txData)...) } responseBinaryData = append(responseBinaryData, utils.EncodeLengthPlusData(transactions)...) // порядок тр-ий transactions_testblock, err = d.GetList(`SELECT hash FROM transactions_testblock ORDER BY id ASC`).String() if err != nil { log.Error("%v", utils.ErrInfo(err)) return } for _, txHash := range transactions_testblock { responseBinaryData = append(responseBinaryData, []byte(txHash)...) } // в 4-х байтах пишем размер данных, которые пошлем далее _, err = conn.Write(utils.DecToBin(len(responseBinaryData), 4)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // далее шлем сами данные _, err = conn.Write(responseBinaryData) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } } }(host) } } 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") } }