func main() { f := tests_utils.InitLog() defer f.Close() txType := "ChangePrimaryKey" txTime := "1427383713" userId := []byte("2") var blockId int64 = 128008 var bin_public_key_pack []byte bin_public_key_pack = append(bin_public_key_pack, utils.EncodeLengthPlusData(utils.HexToBin([]byte("111111111")))...) bin_public_key_pack = append(bin_public_key_pack, utils.EncodeLengthPlusData(utils.HexToBin(([]byte(""))))...) bin_public_key_pack = append(bin_public_key_pack, utils.EncodeLengthPlusData(utils.HexToBin([]byte("")))...) var txSlice [][]byte // hash txSlice = append(txSlice, []byte("1111111111")) // type txSlice = append(txSlice, utils.Int64ToByte(utils.TypeInt(txType))) // time txSlice = append(txSlice, []byte(txTime)) // user_id txSlice = append(txSlice, []byte("1")) // public_keys txSlice = append(txSlice, []byte(bin_public_key_pack)) // sign txSlice = append(txSlice, []byte("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")) blockData := new(utils.BlockData) blockData.BlockId = blockId blockData.Time = utils.StrToInt64(txTime) blockData.UserId = utils.BytesToInt64(userId) err := tests_utils.MakeTest(txSlice, blockData, txType, "work_and_rollback") if err != nil { fmt.Println(err) } }
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 NodeVoting(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "NodeVoting" d := new(daemon) d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } d.goRoutineName = GoroutineName d.chAnswer = chAnswer d.chBreaker = chBreaker if utils.Mobile() { d.sleepTime = 3600 } else { d.sleepTime = 60 } if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) { return } d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } err = d.notMinerSetSleepTime(1800) if err != nil { log.Error("%v", err) return } BEGIN: for { log.Info(GoroutineName) MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())} // проверим, не нужно ли нам выйти из цикла if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } err, restart := d.dbLock() if restart { break BEGIN } if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } // берем данные, которые находятся на голосовании нодов rows, err := d.Query(d.FormatQuery(` SELECT miners_data.user_id, http_host as host, face_hash, profile_hash, photo_block_id, photo_max_miner_id, miners_keepers, id as vote_id, miner_id FROM votes_miners LEFT JOIN miners_data ON votes_miners.user_id = miners_data.user_id WHERE cron_checked_time < ? AND votes_end = 0 AND type = 'node_voting' `), utils.Time()-consts.CRON_CHECKED_TIME_SEC) if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if ok := rows.Next(); ok { var vote_id, miner_id int64 var user_id, host, row_face_hash, row_profile_hash, photo_block_id, photo_max_miner_id, miners_keepers string err = rows.Scan(&user_id, &host, &row_face_hash, &row_profile_hash, &photo_block_id, &photo_max_miner_id, &miners_keepers, &vote_id, &miner_id) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // проверим, не нужно нам выйти, т.к. обновилась версия софта if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { rows.Close() utils.Sleep(1) break } minersIds := utils.GetMinersKeepers(photo_block_id, photo_max_miner_id, miners_keepers, true) myUsersIds, err := d.GetMyUsersIds(true, true) myMinersIds, err := d.GetMyMinersIds(myUsersIds) // нет ли нас среди тех, кто должен скачать фото к себе и проголосовать var intersectMyMiners []int64 for _, id := range minersIds { if utils.InSliceInt64(int64(id), myMinersIds) { intersectMyMiners = append(intersectMyMiners, int64(id)) } } var vote int64 if len(intersectMyMiners) > 0 { var downloadError bool var faceHash, profileHash string var faceFile, profileFile []byte // копируем фото к себе profilePath := *utils.Dir + "/public/profile_" + user_id + ".jpg" _, err = utils.DownloadToFile(host+"/public/"+user_id+"_user_profile.jpg", profilePath, 60, chBreaker, chAnswer, GoroutineName) if err != nil { log.Error("%s", utils.ErrInfo(err)) downloadError = true } facePath := *utils.Dir + "/public/face_" + user_id + ".jpg" _, err = utils.DownloadToFile(host+"/public/"+user_id+"_user_face.jpg", facePath, 60, chBreaker, chAnswer, GoroutineName) if err != nil { log.Error("%s", utils.ErrInfo(err)) downloadError = true } if !downloadError { // хэши скопированных фото profileFile, err = ioutil.ReadFile(profilePath) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } profileHash = string(utils.DSha256(profileFile)) log.Info("%v", "profileHash", profileHash) faceFile, err = ioutil.ReadFile(facePath) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } faceHash = string(utils.DSha256(faceFile)) log.Info("%v", "faceHash", faceHash) } // проверяем хэш. Если сходится, то голосуем за, если нет - против и размер не должен быть более 200 Kb. if profileHash == row_profile_hash && faceHash == row_face_hash && len(profileFile) < 204800 && len(faceFile) < 204800 { vote = 1 } else { log.Error("%s %s %s %s %d %d", profileHash, row_face_hash, faceHash, row_profile_hash, len(profileFile), len(faceFile)) vote = 0 // если хэш не сходится, то удаляем только что скаченное фото os.Remove(profilePath) os.Remove(facePath) } // проходимся по всем нашим майнерам, если это пул и по одному, если это сингл-мод for _, myMinerId := range intersectMyMiners { myUserId, err := d.Single("SELECT user_id FROM miners_data WHERE miner_id = ?", myMinerId).Int64() if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } curTime := utils.Time() forSign := fmt.Sprintf("%v,%v,%v,%v,%v", utils.TypeInt("VotesNodeNewMiner"), curTime, myUserId, vote_id, vote) binSign, err := d.GetBinSign(forSign, myUserId) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } data := utils.DecToBin(utils.TypeInt("VotesNodeNewMiner"), 1) data = append(data, utils.DecToBin(curTime, 4)...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(myUserId))...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(vote_id))...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(vote))...) data = append(data, utils.EncodeLengthPlusData([]byte(binSign))...) err = d.InsertReplaceTxInQueue(data) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } } // отмечаем, чтобы больше не брать эту строку err = d.ExecSql("UPDATE votes_miners SET cron_checked_time = ? WHERE id = ?", utils.Time(), vote_id) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } rows.Close() d.dbUnlock() if d.dSleep(d.sleepTime) { break BEGIN } } log.Debug("break BEGIN %v", GoroutineName) }
func ElectionsAdmin(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "ElectionsAdmin" d := new(daemon) d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } d.goRoutineName = GoroutineName d.chAnswer = chAnswer d.chBreaker = chBreaker if utils.Mobile() { d.sleepTime = 3600 } else { d.sleepTime = 60 } if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) { return } d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } err = d.notMinerSetSleepTime(1800) if err != nil { log.Error("%v", err) return } BEGIN: for { log.Info(GoroutineName) MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())} // проверим, не нужно ли нам выйти из цикла if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } err, restart := d.dbLock() if restart { break BEGIN } if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } blockId, err := d.GetBlockId() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if blockId == 0 { if d.unlockPrintSleep(utils.ErrInfo("blockId == 0"), d.sleepTime) { break BEGIN } continue BEGIN } _, _, myMinerId, _, _, _, err := d.TestBlock() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // а майнер ли я ? if myMinerId == 0 { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } variables, err := d.GetAllVariables() curTime := utils.Time() // проверим, прошло ли 2 недели с момента последнего обновления adminTime, err := d.Single("SELECT time FROM admin").Int64() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if curTime-adminTime <= variables.Int64["new_pct_period"] { if d.unlockPrintSleep(utils.ErrInfo("14 day error"), d.sleepTime) { break BEGIN } continue BEGIN } // сколько всего майнеров countMiners, err := d.Single("SELECT count(miner_id) FROM miners WHERE active = 1").Int64() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if countMiners < 1000 { if d.unlockPrintSleep(utils.ErrInfo("countMiners < 1000"), d.sleepTime) { break BEGIN } continue BEGIN } // берем все голоса var newAdmin int64 votes_admin, err := d.GetMap(` SELECT admin_user_id, count(user_id) as votes FROM votes_admin WHERE time > ? GROUP BY admin_user_id `, "admin_user_id", "votes", curTime-variables.Int64["new_pct_period"]) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for admin_user_id, votes := range votes_admin { // если более 50% майнеров проголосовали if utils.StrToInt64(votes) > countMiners/2 { newAdmin = utils.StrToInt64(admin_user_id) } } if newAdmin == 0 { if d.unlockPrintSleep(utils.ErrInfo("newAdmin == 0"), d.sleepTime) { break BEGIN } continue BEGIN } _, myUserId, _, _, _, _, err := d.TestBlock() forSign := fmt.Sprintf("%v,%v,%v,%v", utils.TypeInt("NewAdmin"), curTime, myUserId, newAdmin) binSign, err := d.GetBinSign(forSign, myUserId) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } data := utils.DecToBin(utils.TypeInt("NewAdmin"), 1) data = append(data, utils.DecToBin(curTime, 4)...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(myUserId))...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(newAdmin))...) data = append(data, utils.EncodeLengthPlusData([]byte(binSign))...) err = d.InsertReplaceTxInQueue(data) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } p := new(dcparser.Parser) p.DCDB = d.DCDB err = p.TxParser(utils.HexToBin(utils.Md5(data)), data, true) if err != nil { if d.unlockPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } d.dbUnlock() if d.dSleep(d.sleepTime) { break BEGIN } } log.Debug("break BEGIN %v", GoroutineName) }
func (d *daemon) DisseminatorType1(host string, userId int64, node_public_key string, toBeSent []byte, dataType int64) { log.Debug("host %v / userId %v", host, userId) // шлем данные указанному хосту conn, err := utils.TcpConn(host) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } defer conn.Close() randTestblockHash, err := d.Single("SELECT head_hash FROM queue_testblock").String() if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // получаем IV + ключ + зашифрованный текст dataToBeSent, key, iv, err := utils.EncryptData(toBeSent, []byte(node_public_key), randTestblockHash) log.Debug("key: %s", key) log.Debug("iv: %s", iv) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // вначале шлем тип данных, чтобы принимающая сторона могла понять, как именно надо обрабатывать присланные данные n, err := conn.Write(utils.DecToBin(dataType, 1)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("n: %x", n) // т.к. на приеме может быть пул, то нужно дописать в начало user_id, чьим нодовским ключем шифруем dataToBeSent = append(utils.DecToBin(userId, 5), dataToBeSent...) log.Debug("dataToBeSent: %x", dataToBeSent) // в 4-х байтах пишем размер данных, которые пошлем далее size := utils.DecToBin(len(dataToBeSent), 4) n, err = conn.Write(size) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("n: %x", n) n, err = conn.Write(dataToBeSent) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("n: %d / size: %v / len: %d", n, utils.BinToDec(size), len(dataToBeSent)) // в ответ получаем размер данных, которые нам хочет передать сервер buf := make([]byte, 4) n, err = conn.Read(buf) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("n: %x", n) dataSize := utils.BinToDec(buf) log.Debug("dataSize %d", dataSize) // и если данных менее 1мб, то получаем их if dataSize < 1048576 { encBinaryTxHashes := make([]byte, dataSize) _, err = io.ReadFull(conn, encBinaryTxHashes) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // разбираем полученные данные binaryTxHashes, err := utils.DecryptCFB(iv, encBinaryTxHashes, key) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("binaryTxHashes %x", binaryTxHashes) var binaryTx []byte for { // Разбираем список транзакций txHash := make([]byte, 16) if len(binaryTxHashes) >= 16 { txHash = utils.BytesShift(&binaryTxHashes, 16) } txHash = utils.BinToHex(txHash) log.Debug("txHash %s", txHash) utils.WriteSelectiveLog("SELECT data FROM transactions WHERE hex(hash) = " + string(txHash)) tx, err := d.Single("SELECT data FROM transactions WHERE hex(hash) = ?", txHash).Bytes() log.Debug("tx %x", tx) if err != nil { utils.WriteSelectiveLog(err) log.Error("%v", utils.ErrInfo(err)) return } utils.WriteSelectiveLog("tx: " + string(utils.BinToHex(tx))) if len(tx) > 0 { binaryTx = append(binaryTx, utils.EncodeLengthPlusData(tx)...) } if len(binaryTxHashes) == 0 { break } } log.Debug("binaryTx %x", binaryTx) // шифруем тр-ии. Вначале encData добавляется IV encData, _, err := utils.EncryptCFB(binaryTx, key, iv) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("encData %x", encData) // шлем серверу // в первых 4-х байтах пишем размер данных, которые пошлем далее size := utils.DecToBin(len(encData), 4) _, err = conn.Write(size) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // далее шлем сами данные _, err = conn.Write(encData) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } } }
/** * Демон, который мониторит таблу 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 (c *Controller) SendToPool() (string, error) { if c.SessRestricted != 0 { return "", utils.ErrInfo(errors.New("Permission denied")) } filesSign := c.r.FormValue("filesSign") poolUid := utils.StrToInt64(c.r.FormValue("poolUid")) data_, err := c.OneRow(`SELECT tcp_host, http_host FROM miners_data WHERE user_id = ?`, poolUid).String() tcpHost := data_["tcp_host"] httpHost := data_["http_host"] if err != nil { return "", utils.ErrInfo(err) } conn, err := net.DialTimeout("tcp", tcpHost, 5*time.Second) if err != nil { return "", utils.ErrInfo(err) } defer conn.Close() conn.SetReadDeadline(time.Now().Add(240 * time.Second)) conn.SetWriteDeadline(time.Now().Add(240 * time.Second)) var data []byte data = append(data, utils.DecToBin(c.SessUserId, 5)...) data = append(data, utils.EncodeLengthPlusData(filesSign)...) if _, err := os.Stat(*utils.Dir + "/public/" + utils.Int64ToStr(c.SessUserId) + "_user_face.jpg"); err == nil { file, err := ioutil.ReadFile(*utils.Dir + "/public/" + utils.Int64ToStr(c.SessUserId) + "_user_face.jpg") if err != nil { return "", utils.ErrInfo(err) } data = append(data, utils.EncodeLengthPlusData(append(utils.DecToBin(0, 1), file...))...) } if _, err := os.Stat(*utils.Dir + "/public/" + utils.Int64ToStr(c.SessUserId) + "_user_profile.jpg"); err == nil { file, err := ioutil.ReadFile(*utils.Dir + "/public/" + utils.Int64ToStr(c.SessUserId) + "_user_profile.jpg") if err != nil { return "", utils.ErrInfo(err) } data = append(data, utils.EncodeLengthPlusData(append(utils.DecToBin(1, 1), file...))...) } if _, err := os.Stat(*utils.Dir + "/public/" + utils.Int64ToStr(c.SessUserId) + "_user_video.mp4"); err == nil { file, err := ioutil.ReadFile(*utils.Dir + "/public/" + utils.Int64ToStr(c.SessUserId) + "_user_video.mp4") if err != nil { return "", utils.ErrInfo(err) } data = append(data, utils.EncodeLengthPlusData(append(utils.DecToBin(2, 1), file...))...) } // тип данных _, err = conn.Write(utils.DecToBin(11, 1)) if err != nil { return "", utils.ErrInfo(err) } // в 4-х байтах пишем размер данных, которые пошлем далее size := utils.DecToBin(len(data), 4) _, err = conn.Write(size) if err != nil { return "", utils.ErrInfo(err) } // далее шлем сами данные _, err = conn.Write([]byte(data)) if err != nil { return "", utils.ErrInfo(err) } // в ответ получаем статус buf := make([]byte, 1) _, err = conn.Read(buf) if err != nil { return "", utils.ErrInfo(err) } status := utils.BinToDec(buf) result := "" if status == 1 { result = utils.JsonAnswer("1", "success").String() c.ExecSql(`UPDATE `+c.MyPrefix+`my_table SET tcp_host = ?, http_host = ?`, tcpHost, httpHost) } else { result = utils.JsonAnswer("error", "error").String() } return result, nil }
func (c *Controller) SendPromisedAmountToPool() (string, error) { if c.SessRestricted != 0 { return "", utils.ErrInfo(errors.New("Permission denied")) } filesSign := c.r.FormValue("filesSign") currencyId := utils.StrToInt64(c.r.FormValue("currencyId")) tcpHost, err := c.Single(`SELECT tcp_host FROM miners_data WHERE user_id = ?`, c.SessUserId).String() if err != nil { return "", utils.ErrInfo(err) } conn, err := net.DialTimeout("tcp", tcpHost, 5*time.Second) if err != nil { return "", utils.ErrInfo(err) } defer conn.Close() conn.SetReadDeadline(time.Now().Add(240 * time.Second)) conn.SetWriteDeadline(time.Now().Add(240 * time.Second)) var data []byte data = append(data, utils.DecToBin(c.SessUserId, 5)...) data = append(data, utils.DecToBin(currencyId, 1)...) data = append(data, utils.EncodeLengthPlusData(filesSign)...) if _, err := os.Stat(*utils.Dir + "/public/" + utils.Int64ToStr(c.SessUserId) + "_promised_amount_" + utils.Int64ToStr(currencyId) + ".mp4"); err == nil { file, err := ioutil.ReadFile(*utils.Dir + "/public/" + utils.Int64ToStr(c.SessUserId) + "_promised_amount_" + utils.Int64ToStr(currencyId) + ".mp4") if err != nil { return "", utils.ErrInfo(err) } data = append(data, utils.EncodeLengthPlusData(append(utils.DecToBin(2, 1), file...))...) } // тип данных _, err = conn.Write(utils.DecToBin(12, 1)) if err != nil { return "", utils.ErrInfo(err) } // в 4-х байтах пишем размер данных, которые пошлем далее size := utils.DecToBin(len(data), 4) _, err = conn.Write(size) if err != nil { return "", utils.ErrInfo(err) } // далее шлем сами данные _, err = conn.Write([]byte(data)) if err != nil { return "", utils.ErrInfo(err) } // в ответ получаем статус buf := make([]byte, 1) _, err = conn.Read(buf) if err != nil { return "", utils.ErrInfo(err) } status := utils.BinToDec(buf) result := "" if status == 1 { result = utils.JsonAnswer("1", "success").String() } else { result = utils.JsonAnswer("error", "error").String() } return result, nil }
/* * Каждые 2 недели собираем инфу о голосах за % и создаем тр-ию, которая * попадет в DC сеть только, если мы окажемся генератором блока * */ func PctGenerator(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "PctGenerator" d := new(daemon) d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } d.goRoutineName = GoroutineName d.chAnswer = chAnswer d.chBreaker = chBreaker if utils.Mobile() { d.sleepTime = 3600 } else { d.sleepTime = 60 } if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) { return } d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } err = d.notMinerSetSleepTime(1800) if err != nil { log.Error("%v", err) return } BEGIN: for { log.Info(GoroutineName) MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())} // проверим, не нужно ли нам выйти из цикла if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } err, restart := d.dbLock() if restart { break BEGIN } if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } blockId, err := d.GetBlockId() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if blockId == 0 { if d.unlockPrintSleep(utils.ErrInfo("blockId == 0"), d.sleepTime) { break BEGIN } continue BEGIN } _, _, myMinerId, _, _, _, err := d.TestBlock() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // а майнер ли я ? if myMinerId == 0 { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } variables, err := d.GetAllVariables() curTime := utils.Time() // проверим, прошло ли 2 недели с момента последнего обновления pct pctTime, err := d.Single("SELECT max(time) FROM pct").Int64() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if curTime-pctTime > variables.Int64["new_pct_period"] { // берем все голоса miner_pct pctVotes := make(map[int64]map[string]map[string]int64) rows, err := d.Query("SELECT currency_id, pct, count(user_id) as votes FROM votes_miner_pct GROUP BY currency_id, pct ORDER BY currency_id, pct ASC") if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var currency_id, votes int64 var pct string err = rows.Scan(¤cy_id, &pct, &votes) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } log.Info("%v", "newpctcurrency_id", currency_id, "pct", pct, "votes", votes) if len(pctVotes[currency_id]) == 0 { pctVotes[currency_id] = make(map[string]map[string]int64) } if len(pctVotes[currency_id]["miner_pct"]) == 0 { pctVotes[currency_id]["miner_pct"] = make(map[string]int64) } pctVotes[currency_id]["miner_pct"][pct] = votes } rows.Close() // берем все голоса user_pct rows, err = d.Query("SELECT currency_id, pct, count(user_id) as votes FROM votes_user_pct GROUP BY currency_id, pct ORDER BY currency_id, pct ASC") if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var currency_id, votes int64 var pct string err = rows.Scan(¤cy_id, &pct, &votes) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } log.Info("%v", "currency_id", currency_id, "pct", pct, "votes", votes) if len(pctVotes[currency_id]) == 0 { pctVotes[currency_id] = make(map[string]map[string]int64) } if len(pctVotes[currency_id]["user_pct"]) == 0 { pctVotes[currency_id]["user_pct"] = make(map[string]int64) } pctVotes[currency_id]["user_pct"][pct] = votes } rows.Close() newPct := make(map[string]map[string]map[string]string) newPct["currency"] = make(map[string]map[string]string) var userMaxKey int64 PctArray := utils.GetPctArray() log.Info("%v", "pctVotes", pctVotes) for currencyId, data := range pctVotes { currencyIdStr := utils.Int64ToStr(currencyId) // определяем % для майнеров pctArr := utils.MakePctArray(data["miner_pct"]) log.Info("%v", "pctArrminer_pct", pctArr, currencyId) key := utils.GetMaxVote(pctArr, 0, 390, 100) log.Info("%v", "key", key) if len(newPct["currency"][currencyIdStr]) == 0 { newPct["currency"][currencyIdStr] = make(map[string]string) } newPct["currency"][currencyIdStr]["miner_pct"] = utils.GetPctValue(key) // определяем % для юзеров pctArr = utils.MakePctArray(data["user_pct"]) log.Info("%v", "pctArruser_pct", pctArr, currencyId) log.Info("%v", "newPct", newPct) pctY := utils.ArraySearch(newPct["currency"][currencyIdStr]["miner_pct"], PctArray) log.Info("%v", "newPct[currency][currencyIdStr][miner_pct]", newPct["currency"][currencyIdStr]["miner_pct"]) log.Info("%v", "PctArray", PctArray) log.Info("%v", "miner_pct $pct_y=", pctY) maxUserPctY := utils.Round(utils.StrToFloat64(pctY)/2, 2) userMaxKey = utils.FindUserPct(int(maxUserPctY)) log.Info("%v", "maxUserPctY", maxUserPctY, "userMaxKey", userMaxKey, "currencyIdStr", currencyIdStr) // отрезаем лишнее, т.к. поиск идет ровно до макимального возможного, т.е. до miner_pct/2 pctArr = utils.DelUserPct(pctArr, userMaxKey) log.Info("%v", "pctArr", pctArr) key = utils.GetMaxVote(pctArr, 0, userMaxKey, 100) log.Info("%v", "data[user_pct]", data["user_pct"]) log.Info("%v", "pctArr", pctArr) log.Info("%v", "userMaxKey", userMaxKey) log.Info("%v", "key", key) newPct["currency"][currencyIdStr]["user_pct"] = utils.GetPctValue(key) log.Info("%v", "user_pct", newPct["currency"][currencyIdStr]["user_pct"]) } newPct_ := new(newPctType) newPct_.Currency = make(map[string]map[string]string) newPct_.Currency = newPct["currency"] newPct_.Referral = make(map[string]int64) refLevels := []string{"first", "second", "third"} for i := 0; i < len(refLevels); i++ { level := refLevels[i] var votesReferral []map[int64]int64 // берем все голоса rows, err := d.Query("SELECT " + level + ", count(user_id) as votes FROM votes_referral GROUP BY " + level + " ORDER BY " + level + " ASC ") if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var level_, votes int64 err = rows.Scan(&level_, &votes) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } votesReferral = append(votesReferral, map[int64]int64{level_: votes}) } rows.Close() newPct_.Referral[level] = (utils.GetMaxVote(votesReferral, 0, 30, 10)) } jsonData, err := json.Marshal(newPct_) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } _, myUserId, _, _, _, _, err := d.TestBlock() forSign := fmt.Sprintf("%v,%v,%v,%s", utils.TypeInt("NewPct"), curTime, myUserId, jsonData) log.Debug("forSign = %v", forSign) binSign, err := d.GetBinSign(forSign, myUserId) log.Debug("binSign = %x", binSign) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } data := utils.DecToBin(utils.TypeInt("NewPct"), 1) data = append(data, utils.DecToBin(curTime, 4)...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(myUserId))...) data = append(data, utils.EncodeLengthPlusData(jsonData)...) data = append(data, utils.EncodeLengthPlusData([]byte(binSign))...) err = d.InsertReplaceTxInQueue(data) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // и не закрывая main_lock переводим нашу тр-ию в verified=1, откатив все несовместимые тр-ии // таким образом у нас будут в блоке только актуальные голоса. // а если придет другой блок и станет verified=0, то эта тр-ия просто удалится. p := new(dcparser.Parser) p.DCDB = d.DCDB err = p.TxParser(utils.HexToBin(utils.Md5(data)), data, true) if err != nil { 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 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) }
/** фронт. проверка + занесение данных из блока в таблицы и info_block */ func (p *Parser) ParseDataFull() error { p.TxIds = []string{} p.dataPre() if p.dataType != 0 { // парсим только блоки return utils.ErrInfo(fmt.Errorf("incorrect dataType")) } var err error p.Variables, err = p.GetAllVariables() if err != nil { return utils.ErrInfo(err) } //if len(p.BinaryData) > 500000 { // ioutil.WriteFile("block-"+string(utils.DSha256(p.BinaryData)), p.BinaryData, 0644) //} err = p.ParseBlock() if err != nil { return utils.ErrInfo(err) } // проверим данные, указанные в заголовке блока err = p.CheckBlockHeader() if err != nil { return utils.ErrInfo(err) } utils.WriteSelectiveLog("DELETE FROM transactions WHERE used = 1") afect, err := p.ExecSqlGetAffect("DELETE FROM transactions WHERE used = 1") if err != nil { utils.WriteSelectiveLog(err) return utils.ErrInfo(err) } utils.WriteSelectiveLog("afect: " + utils.Int64ToStr(afect)) txCounter := make(map[int64]int64) p.fullTxBinaryData = p.BinaryData var txForRollbackTo []byte if len(p.BinaryData) > 0 { for { // обработка тр-ий может занять много времени, нужно отметиться p.UpdDaemonTime(p.GoroutineName) p.halfRollback = false log.Debug("&p.BinaryData", p.BinaryData) transactionSize := utils.DecodeLength(&p.BinaryData) if len(p.BinaryData) == 0 { return utils.ErrInfo(fmt.Errorf("empty BinaryData")) } // отчекрыжим одну транзакцию от списка транзакций //log.Debug("++p.BinaryData=%x\n", p.BinaryData) //log.Debug("transactionSize", transactionSize) transactionBinaryData := utils.BytesShift(&p.BinaryData, transactionSize) transactionBinaryDataFull := transactionBinaryData //ioutil.WriteFile("/tmp/dctx", transactionBinaryDataFull, 0644) //ioutil.WriteFile("/tmp/dctxhash", utils.Md5(transactionBinaryDataFull), 0644) // добавляем взятую тр-ию в набор тр-ий для RollbackTo, в котором пойдем в обратном порядке txForRollbackTo = append(txForRollbackTo, utils.EncodeLengthPlusData(transactionBinaryData)...) //log.Debug("transactionBinaryData: %x\n", transactionBinaryData) //log.Debug("txForRollbackTo: %x\n", txForRollbackTo) err = p.CheckLogTx(transactionBinaryDataFull) if err != nil { p.RollbackTo(txForRollbackTo, true, false) return utils.ErrInfo(err) } utils.WriteSelectiveLog("UPDATE transactions SET used=1 WHERE hex(hash) = " + string(utils.Md5(transactionBinaryDataFull))) affect, err := p.ExecSqlGetAffect("UPDATE transactions SET used=1 WHERE hex(hash) = ?", utils.Md5(transactionBinaryDataFull)) if err != nil { utils.WriteSelectiveLog(err) utils.WriteSelectiveLog("RollbackTo") p.RollbackTo(txForRollbackTo, true, false) return utils.ErrInfo(err) } utils.WriteSelectiveLog("affect: " + utils.Int64ToStr(affect)) //log.Debug("transactionBinaryData", transactionBinaryData) p.TxHash = utils.Md5(transactionBinaryData) log.Debug("p.TxHash %s", p.TxHash) p.TxSlice, err = p.ParseTransaction(&transactionBinaryData) log.Debug("p.TxSlice %v", p.TxSlice) if err != nil { p.RollbackTo(txForRollbackTo, true, false) return err } if p.BlockData.BlockId > 1 { var userId int64 // txSlice[3] могут подсунуть пустой if len(p.TxSlice) > 3 { if !utils.CheckInputData(p.TxSlice[3], "int64") { return utils.ErrInfo(fmt.Errorf("empty user_id")) } else { userId = utils.BytesToInt64(p.TxSlice[3]) } } else { return utils.ErrInfo(fmt.Errorf("empty user_id")) } // считаем по каждому юзеру, сколько в блоке от него транзакций txCounter[userId]++ // чтобы 1 юзер не смог прислать дос-блок размером в 10гб, который заполнит своими же транзакциями if txCounter[userId] > p.Variables.Int64["max_block_user_transactions"] { p.RollbackTo(txForRollbackTo, true, false) return utils.ErrInfo(fmt.Errorf("max_block_user_transactions")) } } // время в транзакции не может быть больше, чем на MAX_TX_FORW сек времени блока // и время в транзакции не может быть меньше времени блока -24ч. if utils.BytesToInt64(p.TxSlice[2])-consts.MAX_TX_FORW > p.BlockData.Time || utils.BytesToInt64(p.TxSlice[2]) < p.BlockData.Time-consts.MAX_TX_BACK { p.RollbackTo(txForRollbackTo, true, false) return utils.ErrInfo(fmt.Errorf("incorrect transaction time")) } // проверим, есть ли такой тип тр-ий _, ok := consts.TxTypes[utils.BytesToInt(p.TxSlice[1])] if !ok { return utils.ErrInfo(fmt.Errorf("nonexistent type")) } p.TxMap = map[string][]byte{} // для статы p.TxIds = append(p.TxIds, string(p.TxSlice[1])) MethodName := consts.TxTypes[utils.BytesToInt(p.TxSlice[1])] log.Debug("MethodName", MethodName+"Init") err_ := utils.CallMethod(p, MethodName+"Init") if _, ok := err_.(error); ok { log.Error("error: %v", err) return utils.ErrInfo(err_.(error)) } log.Debug("MethodName", MethodName+"Front") err_ = utils.CallMethod(p, MethodName+"Front") if _, ok := err_.(error); ok { log.Error("error: %v", err_) p.RollbackTo(txForRollbackTo, true, false) return utils.ErrInfo(err_.(error)) } log.Debug("MethodName", MethodName) err_ = utils.CallMethod(p, MethodName) if _, ok := err_.(error); ok { log.Error("error: %v", err) return utils.ErrInfo(err_.(error)) } // даем юзеру понять, что его тр-ия попала в блок p.ExecSql("UPDATE transactions_status SET block_id = ? WHERE hex(hash) = ?", p.BlockData.BlockId, utils.Md5(transactionBinaryDataFull)) // Тут было time(). А значит если бы в цепочке блоков были блоки в которых были бы одинаковые хэши тр-ий, то ParseDataFull вернул бы error err = p.InsertInLogTx(transactionBinaryDataFull, utils.BytesToInt64(p.TxMap["time"])) if err != nil { return utils.ErrInfo(err) } if len(p.BinaryData) == 0 { break } } } p.UpdBlockInfo() return nil }
func CleaningDb(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "CleaningDb" 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 = 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 } curBlockId, err := d.GetBlockId() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // пишем свежие блоки в резервный блокчейн endBlockId, err := utils.GetEndBlockId() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } // чтобы не стопориться тут, а дойти до пересборки БД endBlockId = 4294967295 } log.Debug("curBlockId: %v / endBlockId: %v", curBlockId, endBlockId) if curBlockId-30 > endBlockId { file, err := os.OpenFile(*utils.Dir+"/public/blockchain", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } rows, err := d.Query(d.FormatQuery(` SELECT id, data FROM block_chain WHERE id > ? AND id <= ? ORDER BY id `), endBlockId, curBlockId-30) if err != nil { file.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var id, data string err = rows.Scan(&id, &data) if err != nil { rows.Close() file.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } blockData := append(utils.DecToBin(id, 5), utils.EncodeLengthPlusData(data)...) sizeAndData := append(utils.DecToBin(len(blockData), 5), blockData...) //err := ioutil.WriteFile(*utils.Dir+"/public/blockchain", append(sizeAndData, utils.DecToBin(len(sizeAndData), 5)...), 0644) if _, err = file.Write(append(sizeAndData, utils.DecToBin(len(sizeAndData), 5)...)); err != nil { rows.Close() file.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if err != nil { rows.Close() file.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } rows.Close() file.Close() } autoReload, err := d.Single("SELECT auto_reload FROM config").Int64() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } log.Debug("autoReload: %v", autoReload) if autoReload < 60 { if d.dPrintSleep(utils.ErrInfo("autoReload < 60"), d.sleepTime) { break BEGIN } continue BEGIN } // если main_lock висит более x минут, значит был какой-то сбой mainLock, err := d.Single("SELECT lock_time FROM main_lock WHERE script_name NOT IN ('my_lock', 'cleaning_db')").Int64() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } var infoBlockRestart bool // если с main_lock всё норм, то возможно, что новые блоки не собираются из-за бана нодов if mainLock == 0 || utils.Time()-autoReload < mainLock { timeInfoBlock, err := d.Single(`SELECT time FROM info_block`).Int64() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if utils.Time()-timeInfoBlock > autoReload { // подождем 5 минут и проверим еще раз if d.dSleep(300) { break BEGIN } newTimeInfoBlock, err := d.Single(`SELECT time FROM info_block`).Int64() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // Если за 5 минут info_block тот же, значит обновление блокчейна не идет if newTimeInfoBlock == timeInfoBlock { infoBlockRestart = true } } } log.Debug("mainLock: %v", mainLock) log.Debug("utils.Time(): %v", utils.Time()) if (mainLock > 0 && utils.Time()-autoReload > mainLock) || infoBlockRestart { // ClearDb - убивает демонов, чистит БД, а потом заново запускает демонов // не забываем, что это тоже демон и он должен отчитаться о завершении err = ClearDb(d.chAnswer, GoroutineName) if err != nil { fmt.Println(utils.ErrInfo(err)) if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } if d.dSleep(d.sleepTime) { break BEGIN } } log.Debug("break BEGIN %v", GoroutineName) }
func MakeFrontTest(transactionArray [][]byte, time int64, dataForSign string, txType string, userId int64, MY_PREFIX string, blockId int64) error { db := DbConn() priv, pub := genKeys() nodeArr := []string{"new_admin", "votes_node_new_miner", "NewPct"} var binSign []byte if utils.InSliceString(txType, nodeArr) { err := db.ExecSql("UPDATE my_node_keys SET private_key = ?", priv) if err != nil { return utils.ErrInfo(err) } err = db.ExecSql("UPDATE miners_data SET node_public_key = [hex] WHERE user_id = ?", pub, userId) if err != nil { return utils.ErrInfo(err) } k, err := db.GetNodePrivateKey(MY_PREFIX) if err != nil { return utils.ErrInfo(err) } fmt.Println("k", k) privateKey, err := utils.MakePrivateKey(k) if err != nil { return utils.ErrInfo(err) } //fmt.Println("privateKey.PublicKey", privateKey.PublicKey) //fmt.Println("privateKey.D", privateKey.D) //fmt.Printf("privateKey.N %x\n", privateKey.N) //fmt.Println("privateKey.Public", privateKey.Public()) binSign, err = rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA1, utils.HashSha1(dataForSign)) //nodePublicKey, err := db.GetNodePublicKey(userId) //fmt.Println("nodePublicKey", nodePublicKey) //if err != nil { // return utils.ErrInfo(err) //} //CheckSignResult, err := utils.CheckSign([][]byte{nodePublicKey}, dataForSign, binSign, true); //fmt.Printf("binSign: %x\n", binSign) //fmt.Println("err", err) //fmt.Println("CheckSignResult", CheckSignResult) } else { err := db.ExecSql("UPDATE my_keys SET private_key = ?", priv) if err != nil { return utils.ErrInfo(err) } err = db.ExecSql("UPDATE users SET public_key_0 = [hex]", pub) if err != nil { return utils.ErrInfo(err) } k, err := db.GetPrivateKey(MY_PREFIX) privateKey, err := utils.MakePrivateKey(k) if err != nil { return utils.ErrInfo(err) } binSign, err = rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA1, utils.HashSha1(dataForSign)) binSign = utils.EncodeLengthPlusData(binSign) } //fmt.Println("HashSha1", utils.HashSha1(dataForSign)) //fmt.Printf("binSign %x\n", binSign) //fmt.Println("dataForSign", dataForSign) transactionArray = append(transactionArray, binSign) parser := new(dcparser.Parser) parser.DCDB = db parser.GoroutineName = "test" parser.TxSlice = transactionArray parser.BlockData = &utils.BlockData{BlockId: blockId, Time: time, UserId: userId} parser.TxHash = []byte("111111111111111") parser.Variables, _ = parser.DCDB.GetAllVariables() err0 := utils.CallMethod(parser, txType+"Init") if i, ok := err0.(error); ok { fmt.Println(err0.(error), i) return err0.(error) } err0 = utils.CallMethod(parser, txType+"Front") if i, ok := err0.(error); ok { fmt.Println(err0.(error), i) return err0.(error) } err0 = utils.CallMethod(parser, txType+"RollbackFront") if i, ok := err0.(error); ok { fmt.Println(err0.(error), i) return err0.(error) } return nil }
func MaxOtherCurrenciesGenerator(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "MaxOtherCurrenciesGenerator" d := new(daemon) d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } d.goRoutineName = GoroutineName d.chAnswer = chAnswer d.chBreaker = chBreaker if utils.Mobile() { d.sleepTime = 3600 } else { d.sleepTime = 60 } if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) { return } d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } err = d.notMinerSetSleepTime(1800) if err != nil { log.Error("%v", err) return } BEGIN: for { log.Info(GoroutineName) MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())} // проверим, не нужно ли нам выйти из цикла if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } err, restart := d.dbLock() if restart { break BEGIN } if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } blockId, err := d.GetBlockId() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if blockId == 0 { if d.unlockPrintSleep(utils.ErrInfo("blockId == 0"), d.sleepTime) { break BEGIN } continue BEGIN } _, _, myMinerId, _, _, _, err := d.TestBlock() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // а майнер ли я ? if myMinerId == 0 { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } variables, err := d.GetAllVariables() curTime := utils.Time() totalCountCurrencies, err := d.GetCountCurrencies() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // проверим, прошло ли 2 недели с момента последнего обновления pctTime, err := d.Single("SELECT max(time) FROM max_other_currencies_time").Int64() if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if curTime-pctTime <= variables.Int64["new_max_other_currencies"] { if d.unlockPrintSleep(utils.ErrInfo("14 day error"), d.sleepTime) { break BEGIN } continue BEGIN } // берем все голоса maxOtherCurrenciesVotes := make(map[int64][]map[int64]int64) rows, err := d.Query("SELECT currency_id, count, count(user_id) as votes FROM votes_max_other_currencies GROUP BY currency_id, count ORDER BY currency_id, count ASC") if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var currency_id, count, votes int64 err = rows.Scan(¤cy_id, &count, &votes) if err != nil { rows.Close() if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } maxOtherCurrenciesVotes[currency_id] = append(maxOtherCurrenciesVotes[currency_id], map[int64]int64{count: votes}) } rows.Close() newMaxOtherCurrenciesVotes := make(map[string]int64) for currencyId, countAndVotes := range maxOtherCurrenciesVotes { newMaxOtherCurrenciesVotes[utils.Int64ToStr(currencyId)] = utils.GetMaxVote(countAndVotes, 0, totalCountCurrencies, 10) } jsonData, err := json.Marshal(newMaxOtherCurrenciesVotes) _, myUserId, _, _, _, _, err := d.TestBlock() forSign := fmt.Sprintf("%v,%v,%v,%s", utils.TypeInt("NewMaxOtherCurrencies"), curTime, myUserId, jsonData) log.Debug("forSign = %v", forSign) binSign, err := d.GetBinSign(forSign, myUserId) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } data := utils.DecToBin(utils.TypeInt("NewMaxOtherCurrencies"), 1) data = append(data, utils.DecToBin(curTime, 4)...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(myUserId))...) data = append(data, utils.EncodeLengthPlusData(jsonData)...) data = append(data, utils.EncodeLengthPlusData([]byte(binSign))...) err = d.InsertReplaceTxInQueue(data) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } p := new(dcparser.Parser) p.DCDB = d.DCDB err = p.TxParser(utils.HexToBin(utils.Md5(data)), data, true) if err != nil { 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) }
/* * Каждые 2 недели собираем инфу о голосах за % и создаем тр-ию, которая * попадет в DC сеть только, если мы окажемся генератором блока * */ func ReductionGenerator(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "ReductionGenerator" d := new(daemon) d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } d.goRoutineName = GoroutineName d.chAnswer = chAnswer d.chBreaker = chBreaker if utils.Mobile() { d.sleepTime = 3600 } else { d.sleepTime = 60 } if !d.CheckInstall(chBreaker, chAnswer, GoroutineName) { return } d.DCDB = DbConnect(chBreaker, chAnswer, GoroutineName) if d.DCDB == nil { return } err = d.notMinerSetSleepTime(1800) if err != nil { log.Error("%v", err) return } BEGIN: for { log.Info(GoroutineName) MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())} // проверим, не нужно ли нам выйти из цикла if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } err, restart := d.dbLock() if restart { break BEGIN } if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } blockId, err := d.GetBlockId() if err != nil { if d.unlockPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } if blockId == 0 { if d.unlockPrintSleep(errors.New("blockId == 0"), d.sleepTime) { break BEGIN } continue BEGIN } _, _, myMinerId, _, _, _, err := d.TestBlock() if err != nil { if d.unlockPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } // а майнер ли я ? if myMinerId == 0 { if d.unlockPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } variables, err := d.GetAllVariables() curTime := utils.Time() var reductionType string var reductionCurrencyId int var reductionPct int64 // ===== ручное урезание денежной массы // получаем кол-во обещанных сумм у разных юзеров по каждой валюте. start_time есть только у тех, у кого статус mining/repaid promisedAmount, err := d.GetMap(` SELECT currency_id, count(user_id) as count FROM ( SELECT currency_id, user_id FROM promised_amount WHERE start_time < ? AND del_block_id = 0 AND del_mining_block_id = 0 AND status IN ('mining', 'repaid') GROUP BY user_id, currency_id ) as t1 GROUP BY currency_id`, "currency_id", "count", (curTime - variables.Int64["min_hold_time_promise_amount"])) if err != nil { if d.unlockPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } log.Info("%v", "promisedAmount", promisedAmount) // берем все голоса юзеров rows, err := d.Query(d.FormatQuery(` SELECT currency_id, pct, count(currency_id) as votes FROM votes_reduction WHERE time > ? GROUP BY currency_id, pct `), curTime-variables.Int64["reduction_period"]) if err != nil { if d.unlockPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var votes, pct int64 var currency_id string err = rows.Scan(¤cy_id, &pct, &votes) if err != nil { rows.Close() if d.unlockPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } if len(promisedAmount[currency_id]) == 0 || promisedAmount[currency_id] == "0" { continue } // если голосов за урезание > 50% от числа всех держателей данной валюты if votes >= utils.StrToInt64(promisedAmount[currency_id])/2 { // проверим, прошло ли 2 недели с последнего урезания reductionTime, err := d.Single("SELECT max(time) FROM reduction WHERE currency_id = ? AND type = 'manual'", currency_id).Int64() if err != nil { rows.Close() if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } if curTime-reductionTime > variables.Int64["reduction_period"] { reductionCurrencyId = utils.StrToInt(currency_id) reductionPct = pct reductionType = "manual" log.Info("%v", "reductionCurrencyId", reductionCurrencyId, "reductionPct", reductionPct, "reductionType", reductionType) break } } } rows.Close() // ======= авто-урезание денежной массы из-за малого объема обещанных сумм // получаем кол-во DC на кошельках sumWallets_, err := d.GetMap("SELECT currency_id, sum(amount) as sum_amount FROM wallets GROUP BY currency_id", "currency_id", "sum_amount") if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } sumWallets := make(map[int]float64) for currencyId, amount := range sumWallets_ { sumWallets[utils.StrToInt(currencyId)] = utils.StrToFloat64(amount) } // получаем кол-во TDC на обещанных суммах, плюсуем к тому, что на кошельках sumTdc, err := d.GetMap("SELECT currency_id, sum(tdc_amount) as sum_amount FROM promised_amount GROUP BY currency_id", "currency_id", "sum_amount") if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } for currencyId, amount := range sumTdc { currencyIdInt := utils.StrToInt(currencyId) if sumWallets[currencyIdInt] == 0 { sumWallets[currencyIdInt] = utils.StrToFloat64(amount) } else { sumWallets[currencyIdInt] += utils.StrToFloat64(amount) } } log.Debug("sumWallets", sumWallets) // получаем суммы обещанных сумм sumPromisedAmount, err := d.GetMap(` SELECT currency_id, sum(amount) as sum_amount FROM promised_amount WHERE status = 'mining' AND del_block_id = 0 AND del_mining_block_id = 0 AND (cash_request_out_time = 0 OR cash_request_out_time > ?) GROUP BY currency_id `, "currency_id", "sum_amount", curTime-variables.Int64["cash_request_time"]) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } log.Debug("sumPromisedAmount", sumPromisedAmount) if len(sumWallets) > 0 { for currencyId, sumAmount := range sumWallets { //недопустимо для WOC if currencyId == 1 { continue } reductionTime, err := d.Single("SELECT max(time) FROM reduction WHERE currency_id = ? AND type = 'auto'", currencyId).Int64() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } log.Debug("reductionTime", reductionTime) // прошло ли 48 часов if curTime-reductionTime <= consts.AUTO_REDUCTION_PERIOD { log.Debug("curTime-reductionTime <= consts.AUTO_REDUCTION_PERIOD %d <= %d", curTime-reductionTime, consts.AUTO_REDUCTION_PERIOD) continue } // если обещанных сумм менее чем 100% от объема DC на кошельках, то запускаем урезание log.Debug("utils.StrToFloat64(sumPromisedAmount[utils.IntToStr(currencyId)]) < sumAmount*consts.AUTO_REDUCTION_PROMISED_AMOUNT_PCT %d < %d", utils.StrToFloat64(sumPromisedAmount[utils.IntToStr(currencyId)]), sumAmount*consts.AUTO_REDUCTION_PROMISED_AMOUNT_PCT) if utils.StrToFloat64(sumPromisedAmount[utils.IntToStr(currencyId)]) < sumAmount*consts.AUTO_REDUCTION_PROMISED_AMOUNT_PCT { // проверим, есть ли хотя бы 1000 юзеров, у которых на кошелках есть или была данная валюты countUsers, err := d.Single("SELECT count(user_id) FROM wallets WHERE currency_id = ?", currencyId).Int64() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } log.Debug("countUsers>=countUsers %d >= %d", countUsers, consts.AUTO_REDUCTION_PROMISED_AMOUNT_MIN) if countUsers >= consts.AUTO_REDUCTION_PROMISED_AMOUNT_MIN { reductionCurrencyId = currencyId reductionPct = consts.AUTO_REDUCTION_PCT reductionType = "promised_amount" break } } } } if reductionCurrencyId > 0 && reductionPct > 0 { _, myUserId, _, _, _, _, err := d.TestBlock() forSign := fmt.Sprintf("%v,%v,%v,%v,%v,%v", utils.TypeInt("NewReduction"), curTime, myUserId, reductionCurrencyId, reductionPct, reductionType) log.Debug("forSign = %v", forSign) binSign, err := d.GetBinSign(forSign, myUserId) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } data := utils.DecToBin(utils.TypeInt("NewReduction"), 1) data = append(data, utils.DecToBin(curTime, 4)...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(myUserId))...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(int64(reductionCurrencyId)))...) data = append(data, utils.EncodeLengthPlusData(utils.Int64ToByte(reductionPct))...) data = append(data, utils.EncodeLengthPlusData([]byte(reductionType))...) data = append(data, utils.EncodeLengthPlusData([]byte(binSign))...) err = d.InsertReplaceTxInQueue(data) if err != nil { if d.unlockPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // и не закрывая main_lock переводим нашу тр-ию в verified=1, откатив все несовместимые тр-ии // таким образом у нас будут в блоке только актуальные голоса. // а если придет другой блок и станет verified=0, то эта тр-ия просто удалится. p := new(dcparser.Parser) p.DCDB = d.DCDB err = p.TxParser(utils.HexToBin(utils.Md5(data)), data, true) if err != nil { 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 (p *Parser) ParseDataGate(onlyTx bool) error { var err error p.dataPre() p.TxIds = []string{} p.Variables, err = p.GetAllVariables() if err != nil { return utils.ErrInfo(err) } transactionBinaryData := p.BinaryData var transactionBinaryDataFull []byte // если это транзакции (type>0), а не блок (type==0) if p.dataType > 0 { // проверим, есть ли такой тип тр-ий if len(consts.TxTypes[p.dataType]) == 0 { return p.ErrInfo("Incorrect tx type " + utils.IntToStr(p.dataType)) } log.Debug("p.dataType %v", p.dataType) transactionBinaryData = append(utils.DecToBin(int64(p.dataType), 1), transactionBinaryData...) transactionBinaryDataFull = transactionBinaryData // нет ли хэша этой тр-ии у нас в БД? err = p.CheckLogTx(transactionBinaryDataFull) if err != nil { return p.ErrInfo(err) } p.TxHash = utils.Md5(transactionBinaryData) // преобразуем бинарные данные транзакции в массив p.TxSlice, err = p.ParseTransaction(&transactionBinaryData) if err != nil { return p.ErrInfo(err) } log.Debug("p.TxSlice", p.TxSlice) if len(p.TxSlice) < 3 { return p.ErrInfo(errors.New("len(p.TxSlice) < 3")) } // время транзакции может быть немного больше, чем время на ноде. // у нода может быть просто не настроено время. // время транзакции используется только для борьбы с атаками вчерашними транзакциями. // А т.к. мы храним хэши в log_transaction за 36 часов, то боятся нечего. curTime := utils.Time() if utils.BytesToInt64(p.TxSlice[2])-consts.MAX_TX_FORW > curTime || utils.BytesToInt64(p.TxSlice[2]) < curTime-consts.MAX_TX_BACK { return p.ErrInfo(errors.New("incorrect tx time")) } // $this->transaction_array[3] могут подсунуть пустой if !utils.CheckInputData(p.TxSlice[3], "bigint") { return p.ErrInfo(errors.New("incorrect user id")) } } // если это блок if p.dataType == 0 { txCounter := make(map[int64]int64) // если есть $only_tx=true, то значит идет восстановление уже проверенного блока и заголовок не требуется if !onlyTx { err = p.ParseBlock() if err != nil { return p.ErrInfo(err) } // проверим данные, указанные в заголовке блока err = p.CheckBlockHeader() if err != nil { return p.ErrInfo(err) } } log.Debug("onlyTx", onlyTx) // если в ходе проверки тр-ий возникает ошибка, то вызываем откатчик всех занесенных тр-ий. Эта переменная для него p.fullTxBinaryData = p.BinaryData var txForRollbackTo []byte if len(p.BinaryData) > 0 { for { transactionSize := utils.DecodeLength(&p.BinaryData) if len(p.BinaryData) == 0 { return utils.ErrInfo(fmt.Errorf("empty BinaryData")) } // отчекрыжим одну транзакцию от списка транзакций transactionBinaryData := utils.BytesShift(&p.BinaryData, transactionSize) transactionBinaryDataFull = transactionBinaryData // добавляем взятую тр-ию в набор тр-ий для RollbackTo, в котором пойдем в обратном порядке txForRollbackTo = append(txForRollbackTo, utils.EncodeLengthPlusData(transactionBinaryData)...) // нет ли хэша этой тр-ии у нас в БД? err = p.CheckLogTx(transactionBinaryDataFull) if err != nil { p.RollbackTo(txForRollbackTo, true, false) return p.ErrInfo(err) } p.TxHash = utils.Md5(transactionBinaryData) p.TxSlice, err = p.ParseTransaction(&transactionBinaryData) log.Debug("p.TxSlice %s", p.TxSlice) if err != nil { p.RollbackTo(txForRollbackTo, true, false) return p.ErrInfo(err) } var userId int64 // txSlice[3] могут подсунуть пустой if len(p.TxSlice) > 3 { if !utils.CheckInputData(p.TxSlice[3], "int64") { return utils.ErrInfo(fmt.Errorf("empty user_id")) } else { userId = utils.BytesToInt64(p.TxSlice[3]) } } else { return utils.ErrInfo(fmt.Errorf("empty user_id")) } // считаем по каждому юзеру, сколько в блоке от него транзакций txCounter[userId]++ // чтобы 1 юзер не смог прислать дос-блок размером в 10гб, который заполнит своими же транзакциями if txCounter[userId] > p.Variables.Int64["max_block_user_transactions"] { p.RollbackTo(txForRollbackTo, true, false) return utils.ErrInfo(fmt.Errorf("max_block_user_transactions")) } // проверим, есть ли такой тип тр-ий _, ok := consts.TxTypes[utils.BytesToInt(p.TxSlice[1])] if !ok { return utils.ErrInfo(fmt.Errorf("nonexistent type")) } p.TxMap = map[string][]byte{} // для статы p.TxIds = append(p.TxIds, string(p.TxSlice[1])) MethodName := consts.TxTypes[utils.BytesToInt(p.TxSlice[1])] log.Debug("MethodName", MethodName+"Init") err_ := utils.CallMethod(p, MethodName+"Init") if _, ok := err_.(error); ok { log.Debug("error: %v", err) p.RollbackTo(txForRollbackTo, true, true) return utils.ErrInfo(err_.(error)) } log.Debug("MethodName", MethodName+"Front") err_ = utils.CallMethod(p, MethodName+"Front") if _, ok := err_.(error); ok { log.Debug("error: %v", err) p.RollbackTo(txForRollbackTo, true, true) return utils.ErrInfo(err_.(error)) } // пишем хэш тр-ии в лог err = p.InsertInLogTx(transactionBinaryDataFull, utils.BytesToInt64(p.TxMap["time"])) if err != nil { return utils.ErrInfo(err) } if len(p.BinaryData) == 0 { break } } } } else { // Оперативные транзакции MethodName := consts.TxTypes[p.dataType] log.Debug("MethodName", MethodName+"Init") err_ := utils.CallMethod(p, MethodName+"Init") if _, ok := err_.(error); ok { return utils.ErrInfo(err_.(error)) } log.Debug("MethodName", MethodName+"Front") err_ = utils.CallMethod(p, MethodName+"Front") if _, ok := err_.(error); ok { return utils.ErrInfo(err_.(error)) } // пишем хэш тр-ии в лог err = p.InsertInLogTx(transactionBinaryDataFull, utils.BytesToInt64(p.TxMap["time"])) if err != nil { return utils.ErrInfo(err) } } return nil }
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") } }