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 } } }
/* * просто шлем всем, кто есть в 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) }