/** * Демон, который мониторит таблу 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) }
/* * просто шлем всем, кто есть в nodes_connection хэши блока и тр-ий * если мы не майнер, то шлем всю тр-ию целиком, блоки слать не можем * если майнер - то шлем только хэши, т.к. у нас есть хост, откуда всё можно скачать * */ func Disseminator(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "Disseminator" 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 = 1 } 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 } var hosts []map[string]string var nodeData map[string]string nodeConfig, err := d.GetNodeConfig() if len(nodeConfig["local_gate_ip"]) == 0 { // обычный режим hosts, err = d.GetAll(` SELECT miners_data.user_id, miners_data.tcp_host as host, node_public_key FROM nodes_connection LEFT JOIN miners_data ON nodes_connection.user_id = miners_data.user_id `, -1) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } if len(hosts) == 0 { if d.dSleep(d.sleepTime) { break BEGIN } log.Debug("len(hosts) == 0") continue } } else { // защищенный режим nodeData, err = d.OneRow("SELECT node_public_key, tcp_host FROM miners_data WHERE user_id = ?", nodeConfig["static_node_user_id"]).String() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } hosts = append(hosts, map[string]string{"host": nodeConfig["local_gate_ip"], "node_public_key": nodeData["node_public_key"], "user_id": nodeConfig["static_node_user_id"]}) } myUsersIds, err := d.GetMyUsersIds(false, false) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } myMinersIds, err := d.GetMyMinersIds(myUsersIds) if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue } log.Debug("%v", myUsersIds) log.Debug("%v", myMinersIds) // если среди тр-ий есть смена нодовского ключа, то слать через отправку хэшей с последющей отдачей данных может не получиться // т.к. при некорректном нодовском ключе придет зашифрованый запрос на отдачу данных, а мы его не сможем расшифровать т.к. ключ у нас неверный var changeNodeKey int64 if len(myUsersIds) > 0 { changeNodeKey, err = d.Single(` SELECT count(*) FROM transactions WHERE type = ? AND user_id IN (`+strings.Join(utils.SliceInt64ToString(myUsersIds), ",")+`) `, utils.TypeInt("ChangeNodeKey")).Int64() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } } var dataType int64 // это тип для того, чтобы принимающая сторона могла понять, как именно надо обрабатывать присланные данные // если я майнер и работаю в обычном режиме, то должен слать хэши if len(myMinersIds) > 0 && len(nodeConfig["local_gate_ip"]) == 0 && changeNodeKey == 0 { log.Debug("0") dataType = 1 // определим, от кого будем слать r := utils.RandInt(0, len(myMinersIds)) myMinerId := myMinersIds[r] myUserId, err := d.Single("SELECT user_id FROM miners_data WHERE miner_id = ?", myMinerId).Int64() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } // возьмем хэш текущего блока и номер блока // для теста ролбеков отключим на время data, err := d.OneRow("SELECT block_id, hash, head_hash FROM info_block WHERE sent = 0").Bytes() if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } err = d.ExecSql("UPDATE info_block SET sent = 1") if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } /* * Составляем данные на отправку * */ // 5 байт = наш user_id. Но они будут не первые, т.к. m_curl допишет вперед user_id получателя (нужно для пулов) toBeSent := utils.DecToBin(myUserId, 5) if len(data) > 0 { // блок // если 5-й байт = 0, то на приемнике будем читать блок, если = 1 , то сразу хэши тр-ий toBeSent = append(toBeSent, utils.DecToBin(0, 1)...) toBeSent = append(toBeSent, utils.DecToBin(utils.BytesToInt64(data["block_id"]), 3)...) toBeSent = append(toBeSent, data["hash"]...) toBeSent = append(toBeSent, data["head_hash"]...) err = d.ExecSql("UPDATE info_block SET sent = 1") if err != nil { if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } } else { // тр-ии без блока toBeSent = append(toBeSent, utils.DecToBin(1, 1)...) } // возьмем хэши тр-ий //utils.WriteSelectiveLog("SELECT hash, high_rate FROM transactions WHERE sent = 0 AND for_self_use = 0") transactions, err := d.GetAll("SELECT hash, high_rate FROM transactions WHERE sent = 0 AND for_self_use = 0", -1) if err != nil { utils.WriteSelectiveLog(err) if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } // нет ни транзакций, ни блока для отправки... if len(transactions) == 0 && len(toBeSent) < 10 { //utils.WriteSelectiveLog("len(transactions) == 0") //log.Debug("len(transactions) == 0") if d.dSleep(d.sleepTime) { break BEGIN } log.Debug("len(transactions) == 0 && len(toBeSent) == 0") continue BEGIN } for _, data := range transactions { hexHash := utils.BinToHex([]byte(data["hash"])) toBeSent = append(toBeSent, utils.DecToBin(utils.StrToInt64(data["high_rate"]), 1)...) toBeSent = append(toBeSent, []byte(data["hash"])...) utils.WriteSelectiveLog("UPDATE transactions SET sent = 1 WHERE hex(hash) = " + string(hexHash)) affect, err := d.ExecSqlGetAffect("UPDATE transactions SET sent = 1 WHERE hex(hash) = ?", hexHash) if err != nil { utils.WriteSelectiveLog(err) if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } utils.WriteSelectiveLog("affect: " + utils.Int64ToStr(affect)) } // отправляем блок и хэши тр-ий, если есть что отправлять if len(toBeSent) > 0 { for _, host := range hosts { go d.DisseminatorType1(host["host"], utils.StrToInt64(host["user_id"]), host["node_public_key"], toBeSent, dataType) } } } else { log.Debug("1") var remoteNodeHost string // если просто юзер или работаю в защищенном режиме, то шлю тр-ии целиком. слать блоки не имею права. if len(nodeConfig["local_gate_ip"]) > 0 { dataType = 3 remoteNodeHost = nodeData["host"] } else { dataType = 2 remoteNodeHost = "" } log.Debug("dataType: %d", dataType) var toBeSent []byte // сюда пишем все тр-ии, которые будем слать другим нодам // возьмем хэши и сами тр-ии utils.WriteSelectiveLog("SELECT hash, data FROM transactions WHERE sent = 0") rows, err := d.Query("SELECT hash, data FROM transactions WHERE sent = 0") if err != nil { utils.WriteSelectiveLog(err) if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var hash, data []byte err = rows.Scan(&hash, &data) if err != nil { rows.Close() if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } log.Debug("hash %x", hash) hashHex := utils.BinToHex(hash) utils.WriteSelectiveLog("UPDATE transactions SET sent = 1 WHERE hex(hash) = " + string(hashHex)) affect, err := d.ExecSqlGetAffect("UPDATE transactions SET sent = 1 WHERE hex(hash) = ?", hashHex) if err != nil { utils.WriteSelectiveLog(err) rows.Close() if d.dPrintSleep(err, d.sleepTime) { break BEGIN } continue BEGIN } utils.WriteSelectiveLog("affect: " + utils.Int64ToStr(affect)) toBeSent = append(toBeSent, data...) } rows.Close() // шлем тр-ии if len(toBeSent) > 0 { for _, host := range hosts { userId := utils.StrToInt64(host["user_id"]) go func(host string, userId int64, node_public_key string) { 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 + ключ + зашифрованный текст encryptedData, _, _, err := utils.EncryptData(toBeSent, []byte(node_public_key), randTestblockHash) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // вначале шлем тип данных, чтобы принимающая сторона могла понять, как именно надо обрабатывать присланные данные _, err = conn.Write(utils.DecToBin(dataType, 1)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // т.к. на приеме может быть пул, то нужно дописать в начало user_id, чьим нодовским ключем шифруем /*_, err = conn.Write(utils.DecToBin(userId, 5)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return }*/ encryptedData = append(utils.DecToBin(userId, 5), encryptedData...) // это может быть защищенное локальное соедниение (dataType = 3) и принимающему ноду нужно знать, куда дальше слать данные и чьим они зашифрованы ключем if len(remoteNodeHost) > 0 { /* _, err = conn.Write([]byte(remoteNodeHost)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return }*/ encryptedData = append([]byte(remoteNodeHost), encryptedData...) } log.Debug("encryptedData %x", encryptedData) // в 4-х байтах пишем размер данных, которые пошлем далее size := utils.DecToBin(len(encryptedData), 4) _, err = conn.Write(size) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } // далее шлем сами данные _, err = conn.Write(encryptedData) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } }(host["host"], userId, host["node_public_key"]) } } } d.dbUnlock() if d.dSleep(d.sleepTime) { break BEGIN } } log.Debug("break BEGIN %v", GoroutineName) }
func (p *Parser) ChangeCommissionFront() error { err := p.generalCheck() if err != nil { return p.ErrInfo(err) } // является ли данный юзер майнером err = p.checkMiner(p.TxUserID) if err != nil { return p.ErrInfo(err) } if len(p.TxMap["commission"]) > 3000 { return p.ErrInfo("len commission") } commissionMap := make(map[string][3]float64) err = json.Unmarshal(p.TxMaps.Bytes["commission"], &commissionMap) if err != nil { return p.ErrInfo(err) } var currencyArray []int64 minusCf := 0 for currencyId_, data := range commissionMap { currencyId := utils.StrToInt64(currencyId_) if len(data) != 3 { return p.ErrInfo("len(data) !=3") } if !utils.CheckInputData(currencyId, "int") { return p.ErrInfo("currencyId") } // % от 0 до 10 if !utils.CheckInputData(utils.Float64ToStrPct(data[0]), "currency_commission") || data[0] > 10 { return p.ErrInfo("pct") } // минимальная комиссия от 0. При 0% будет = 0 if !utils.CheckInputData(utils.Float64ToStrPct(data[1]), "currency_commission") { return p.ErrInfo("currency_min_commission") } // макс. комиссия. 0 - значит, считается по % if !utils.CheckInputData(utils.Float64ToStrPct(data[2]), "currency_commission") { return p.ErrInfo("currency_max_commission") } if data[1] > data[2] && data[2] > 0 { return p.ErrInfo("currency_max_commission") } // проверим, существует ли такая валюта в таблице DC-валют if ok, err := p.CheckCurrency(currencyId); !ok { // если нет, то это может быть $currency_id 1000, которая определяет комиссию для всх CF-валют if currencyId != 1000 { return p.ErrInfo(err) } } if currencyId != 1000 { currencyArray = append(currencyArray, currencyId) } else { minusCf = 1 } } count, err := p.Single("SELECT count(id) FROM currency WHERE id IN (" + strings.Join(utils.SliceInt64ToString(currencyArray), ",") + ")").Int() if err != nil { return p.ErrInfo(err) } if count != len(commissionMap)-minusCf { return p.ErrInfo("count != len(commissionMap) - minusCf") } forSign := fmt.Sprintf("%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["commission"]) CheckSignResult, err := utils.CheckSign(p.PublicKeys, forSign, p.TxMap["sign"], false) if err != nil || !CheckSignResult { return p.ErrInfo("incorrect sign") } err = p.limitRequest(p.Variables.Int64["limit_commission"], "commission", p.Variables.Int64["limit_commission_period"]) if err != nil { return p.ErrInfo(err) } return nil }