func (p *Parser) NewUserInit() error { err := p.GetTxMaps([]map[string]string{{"public_key": "bytes"}, {"sign": "bytes"}}) if err != nil { return p.ErrInfo(err) } p.TxMap["public_key_hex"] = utils.BinToHex(p.TxMap["public_key"]) p.TxMaps.Bytes["public_key_hex"] = utils.BinToHex(p.TxMaps.Bytes["public_key"]) return nil }
func (p *Parser) MessageToAdminInit() error { fields := []map[string]string{{"encrypted_message": "bytes"}, {"sign": "bytes"}} err := p.GetTxMaps(fields) if err != nil { return p.ErrInfo(err) } p.TxMaps.Bytes["encrypted_message"] = utils.BinToHex(p.TxMaps.Bytes["encrypted_message"]) p.TxMap["encrypted_message"] = utils.BinToHex(p.TxMap["encrypted_message"]) return nil }
func (p *Parser) ChangeNodeKeyInit() error { fields := []map[string]string{{"new_node_public_key": "bytes"}, {"sign": "bytes"}} err := p.GetTxMaps(fields) if err != nil { return p.ErrInfo(err) } p.TxMaps.Bytes["new_node_public_key"] = utils.BinToHex(p.TxMaps.Bytes["new_node_public_key"]) p.TxMap["new_node_public_key"] = utils.BinToHex(p.TxMap["new_node_public_key"]) return nil }
func (p *Parser) AdminChangePrimaryKeyInit() error { fields := []map[string]string{{"for_user_id": "int64"}, {"public_key": "bytes"}, {"sign": "bytes"}} err := p.GetTxMaps(fields) if err != nil { return p.ErrInfo(err) } p.TxMaps.Bytes["public_key_hex"] = utils.BinToHex(p.TxMaps.Bytes["public_key"]) p.TxMap["public_key_hex"] = utils.BinToHex(p.TxMap["public_key"]) return nil }
func (p *Parser) ChangePrimaryKeyRollback() error { // получим log_id, по которому можно найти данные, которые были до этого // $log_id всегда больше нуля, т.к. это откат обновления ключа logId, err := p.Single("SELECT log_id FROM users WHERE user_id = ?", p.TxUserID).Int64() if err != nil { return p.ErrInfo(err) } // данные, которые восстановим var public_key_0, public_key_1, public_key_2 []byte var prev_log_id int64 err = p.QueryRow(p.FormatQuery("SELECT public_key_0, public_key_1, public_key_2, prev_log_id FROM log_users WHERE log_id = ?"), logId).Scan(&public_key_0, &public_key_1, &public_key_2, &prev_log_id) if err != nil && err != sql.ErrNoRows { return p.ErrInfo(err) } if len(public_key_0) > 0 { public_key_0 = utils.BinToHex(public_key_0) } if len(public_key_1) > 0 { public_key_1 = utils.BinToHex(public_key_1) } if len(public_key_2) > 0 { public_key_2 = utils.BinToHex(public_key_2) } err = p.ExecSql("UPDATE users SET public_key_0 = [hex], public_key_1 = [hex], public_key_2 = [hex], log_id = ? WHERE user_id = ?", public_key_0, public_key_1, public_key_2, prev_log_id, p.TxUserID) if err != nil { return p.ErrInfo(err) } // подчищаем _log err = p.ExecSql("DELETE FROM log_users WHERE log_id = ?", logId) if err != nil { return p.ErrInfo(err) } p.rollbackAI("log_users", 1) // проверим, не наш ли это user_id myUserId, _, myPrefix, _, err := p.GetMyUserId(p.TxUserID) if err != nil { return err } if p.TxUserID == myUserId { // обновим статус в нашей локальной табле. err = p.ExecSql("UPDATE "+myPrefix+"my_keys SET status = 'my_pending', block_id = 0, time = 0 WHERE hex(public_key) = ? AND status = 'approved' AND block_id = ?", p.newPublicKeysHex[0], p.BlockData.BlockId) if err != nil { return p.ErrInfo(err) } } return nil }
func (p *Parser) NewMinerInit() error { var fields []map[string]string if p.BlockData != nil && p.BlockData.BlockId < 250900 { fields = []map[string]string{{"race": "int64"}, {"country": "int64"}, {"latitude": "float64"}, {"longitude": "float64"}, {"http_host": "string"}, {"face_coords": "string"}, {"profile_coords": "string"}, {"face_hash": "string"}, {"profile_hash": "string"}, {"video_type": "string"}, {"video_url_id": "string"}, {"node_public_key": "bytes"}, {"sign": "bytes"}} } else { fields = []map[string]string{{"race": "int64"}, {"country": "int64"}, {"latitude": "float64"}, {"longitude": "float64"}, {"http_host": "string"}, {"tcp_host": "string"}, {"face_coords": "string"}, {"profile_coords": "string"}, {"face_hash": "string"}, {"profile_hash": "string"}, {"video_type": "string"}, {"video_url_id": "string"}, {"node_public_key": "bytes"}, {"sign": "bytes"}} } err := p.GetTxMaps(fields) if err != nil { return p.ErrInfo(err) } p.TxMap["node_public_key"] = utils.BinToHex(p.TxMap["node_public_key"]) p.TxMaps.Bytes["node_public_key"] = utils.BinToHex(p.TxMaps.Bytes["node_public_key"]) return nil }
/* не забываем, что cash_request_OUT_front проверяет формат amount, * можно ли делать запрос указанному юзеру, есть ли у юзера * обещанные суммы на сумму amount, есть ли нужное кол-во DC у отправителя, * является ли отправитель майнером * * */ func (p *Parser) CashRequestInFront() error { err := p.generalCheck() if err != nil { return p.ErrInfo(err) } // code может быть чем угодно, т.к. отправитель шлет в сеть лишь хэш // нигде, кроме cash_request_in_front, code не используется // if ( !check_input_data ($this->tx_data['code'], 'cash_code') ) // return 'cash_request_in_front code'; verifyData := map[string]string{"cash_request_id": "bigint"} err = p.CheckInputData(verifyData) if err != nil { return p.ErrInfo(err) } var to_user_id, cTime int64 var status string var hash_code []byte err = p.QueryRow(p.FormatQuery("SELECT to_user_id, status, hash_code, time FROM cash_requests WHERE id = ?"), p.TxMaps.Int64["cash_request_id"]).Scan(&to_user_id, &status, &hash_code, &cTime) if err != nil && err != sql.ErrNoRows { return p.ErrInfo(err) } // ID cash_requests юзер указал сам, значит это может быть случайное число. // проверим, является получателем наш юзер if to_user_id != p.TxUserID { return p.ErrInfo("to_user_id!=user_id") } // должно быть pending if status != "pending" { return p.ErrInfo("status!=pending") } // проверим код if !bytes.Equal(utils.DSha256(p.TxMaps.String["code"]), utils.BinToHex(hash_code)) { return p.ErrInfo("code!=hash_code") } var txTime int64 if p.BlockData != nil { // тр-ия пришла в блоке txTime = p.BlockData.Time } else { txTime = time.Now().Unix() // просто на всякий случай небольшой запас } // запрос может быть принят, только если он был отправлен не позднее чем через cash_request_time сек назад if cTime < txTime-p.Variables.Int64["cash_request_time"] { return p.ErrInfo(fmt.Sprintf("%d < %d - %d", cTime, txTime, p.Variables.Int64["cash_request_time"])) } forSign := fmt.Sprintf("%s,%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["cash_request_id"], p.TxMap["code"]) CheckSignResult, err := utils.CheckSign(p.PublicKeys, forSign, p.TxMap["sign"], false) if err != nil { return p.ErrInfo(err) } if !CheckSignResult { return p.ErrInfo("incorrect sign") } return nil }
func (c *Controller) EncryptChatMessage() (string, error) { var err error c.r.ParseForm() receiver := c.r.FormValue("receiver") message := c.r.FormValue("message") if len(message) > 5120 { return "", errors.New("incorrect message") } publicKey, err := c.Single("SELECT public_key_0 FROM users WHERE user_id = ?", receiver).Bytes() if err != nil { return "", utils.ErrInfo(err) } if len(publicKey) > 0 { pub, err := utils.BinToRsaPubKey(publicKey) if err != nil { return "", utils.ErrInfo(err) } enc_, err := rsa.EncryptPKCS1v15(rand.Reader, pub, []byte(message)) if err != nil { return "", utils.ErrInfo(err) } return utils.JsonAnswer(string(utils.BinToHex(enc_)), "success").String(), nil } else { return utils.JsonAnswer("Incorrect user_id", "error").String(), nil } }
func (p *Parser) ChangeNodeKey() error { // Всегда есть, что логировать, т.к. это обновление ключа logData, err := p.OneRow("SELECT * FROM miners_data WHERE user_id = ?", p.TxUserID).String() if err != nil { return p.ErrInfo(err) } nodePublicKeyHex := utils.BinToHex([]byte(logData["node_public_key"])) logId, err := p.ExecSqlGetLastInsertId("INSERT INTO log_miners_data ( node_public_key, block_id, prev_log_id ) VALUES ( [hex], ?, ? )", "log_id", nodePublicKeyHex, p.BlockData.BlockId, logData["log_id"]) if err != nil { return p.ErrInfo(err) } err = p.ExecSql("UPDATE miners_data SET node_public_key = [hex], log_id = ? WHERE user_id = ?", p.TxMaps.Bytes["new_node_public_key"], logId, p.TxUserID) if err != nil { return p.ErrInfo(err) } // проверим, не наш ли это user_id myUserId, myBlockId, myPrefix, _, err := p.GetMyUserId(p.TxMaps.Int64["user_id"]) if err != nil { return err } if p.TxUserID == myUserId && myBlockId <= p.BlockData.BlockId { // обновим статус в нашей локальной табле. err = p.ExecSql("UPDATE "+myPrefix+"my_node_keys SET status = 'approved', block_id = ?, time = ? WHERE hex(public_key) = ? AND status = 'my_pending'", p.BlockData.BlockId, p.BlockData.Time, p.TxMaps.Bytes["new_node_public_key"]) if err != nil { return p.ErrInfo(err) } } return nil }
func (p *Parser) MoneyBackRequestFront() error { err := p.generalCheck() if err != nil { return p.ErrInfo(err) } verifyData := map[string]string{"order_id": "bigint", "seller_enc_text": "comment", "arbitrator0_enc_text": "comment", "arbitrator1_enc_text": "comment", "arbitrator2_enc_text": "comment", "arbitrator3_enc_text": "comment", "arbitrator4_enc_text": "comment"} err = p.CheckInputData(verifyData) if err != nil { return p.ErrInfo(err) } var txTime int64 if p.BlockData != nil { // тр-ия пришла в блоке txTime = p.BlockData.Time } else { // голая тр-ия txTime = time.Now().Unix() - 30 // просто на всякий случай небольшой запас } // проверим, есть ли такой ордер, не был ли ранее запрос, точно ли покупатель наш юзер orderId, err := p.Single("SELECT id FROM orders WHERE id = ? AND status = 'normal' AND end_time > ? AND buyer = ?", p.TxMaps.Int64["order_id"], txTime, p.TxUserID).Int64() if err != nil { return p.ErrInfo(err) } if orderId == 0 { return p.ErrInfo("orderId==0") } forSign := "" if p.BlockData != nil && p.BlockData.BlockId < 197115 { forSign = fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["order_id"], p.TxMap["arbitrator0_enc_text"], p.TxMap["arbitrator1_enc_text"], p.TxMap["arbitrator2_enc_text"], p.TxMap["arbitrator3_enc_text"], p.TxMap["arbitrator4_enc_text"], p.TxMap["seller_enc_text"]) } else { encData := make(map[string]string) for i := 0; i < 5; i++ { iStr := utils.IntToStr(i) encData["arbitrator"+iStr+"_enc_text"] = string(utils.BinToHex(p.TxMap["arbitrator"+iStr+"_enc_text"])) if encData["arbitrator"+iStr+"_enc_text"] == "00" { encData["arbitrator"+iStr+"_enc_text"] = "0" } } forSign = fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["order_id"], encData["arbitrator0_enc_text"], encData["arbitrator1_enc_text"], encData["arbitrator2_enc_text"], encData["arbitrator3_enc_text"], encData["arbitrator4_enc_text"], utils.BinToHex(p.TxMap["seller_enc_text"])) } CheckSignResult, err := utils.CheckSign(p.PublicKeys, forSign, p.TxMap["sign"], false) if err != nil { return p.ErrInfo(err) } if !CheckSignResult { return p.ErrInfo("incorrect sign") } err = p.limitRequest(consts.LIMIT_MONEY_BACK_REQUEST, "money_back_request", consts.LIMIT_MONEY_BACK_REQUEST_PERIOD) if err != nil { return p.ErrInfo(err) } return nil }
func (p *Parser) ChangeKeyActiveInit() error { fields := []map[string]string{{"secret": "bytes"}, {"sign": "bytes"}} err := p.GetTxMaps(fields) if err != nil { return p.ErrInfo(err) } p.TxMaps.String["secret_hex"] = string(utils.BinToHex(p.TxMaps.Bytes["secret"])) if p.TxMaps.String["secret_hex"] == "30" { p.TxMaps.Int64["active"] = 0 } else { p.TxMaps.Int64["active"] = 1 } p.TxMaps.String["secret_hex"] = string(utils.BinToHex(p.TxMaps.Bytes["secret"])) return nil }
func (p *Parser) SendDcInit() error { var fields []map[string]string if p.BlockData != nil && p.BlockData.BlockId <= consts.ARBITRATION_BLOCK_START { fields = []map[string]string{{"to_user_id": "int64"}, {"currency_id": "int64"}, {"amount": "float64"}, {"commission": "float64"}, {"comment": "string"}, {"sign": "bytes"}} } else { fields = []map[string]string{{"to_user_id": "int64"}, {"currency_id": "int64"}, {"amount": "float64"}, {"commission": "float64"}, {"arbitrator0": "int64"}, {"arbitrator1": "int64"}, {"arbitrator2": "int64"}, {"arbitrator3": "int64"}, {"arbitrator4": "int64"}, {"arbitrator0_commission": "float64"}, {"arbitrator1_commission": "float64"}, {"arbitrator2_commission": "float64"}, {"arbitrator3_commission": "float64"}, {"arbitrator4_commission": "float64"}, {"comment": "string"}, {"sign": "bytes"}} } err := p.GetTxMaps(fields) if err != nil { return p.ErrInfo(err) } p.TxMaps.Bytes["hash_hex"] = utils.BinToHex(p.TxMaps.Bytes["hash"]) p.TxMaps.Int64["from_user_id"] = p.TxMaps.Int64["user_id"] p.TxMap["hash_hex"] = utils.BinToHex(p.TxMap["hash"]) p.TxMap["from_user_id"] = p.TxMap["user_id"] if p.TxMaps.String["comment"] == "null" { p.TxMaps.String["comment"] = "" p.TxMap["comment"] = []byte("") } return nil }
func (c *Controller) CheckSetupPassword() (string, error) { c.r.ParseForm() password, err := c.Single(`SELECT setup_password FROM config WHERE setup_password = ?`, utils.DSha256(c.r.FormValue("password"))).String() if err != nil { return "", err } if len(password) > 0 && !c.Community { userId, err := c.GetMyUserId("") if err != nil { return "", err } publicKey, err := c.GetUserPublicKey(userId) if err != nil { return "", err } c.sess.Set("user_id", userId) c.sess.Set("public_key", string(utils.BinToHex(publicKey))) log.Debug("public_key check: %s", string(utils.BinToHex(publicKey))) return "ok", nil } return "", nil }
func (c *Controller) EDataBaseDump() (string, error) { if !c.NodeAdmin || c.SessRestricted != 0 { return "", utils.ErrInfo(errors.New("Permission denied")) } allTables, err := c.GetAllTables() if err != nil { return "", utils.ErrInfo(err) } c.r.ParseForm() mainMap := make(map[string][]map[string]string) for _, table := range allTables { re := regexp.MustCompile("^e_") match := re.FindStringSubmatch(table) if len(match) > 0 { data, err := c.GetAll(`SELECT * FROM `+table, -1) if err != nil { return "", utils.ErrInfo(err) } for k, arr := range data { for name, value := range arr { if ok, _ := regexp.MatchString("(tx_hash)", name); ok { data[k][name] = string(utils.BinToHex([]byte(value))) } } } mainMap[table] = data } } jsonData, _ := json.Marshal(mainMap) log.Debug(string(jsonData)) c.w.Header().Set("Content-Type", "text/plain") c.w.Header().Set("Content-Length", utils.IntToStr(len(jsonData))) t := time.Unix(utils.Time(), 0) c.w.Header().Set("Content-Disposition", `attachment; filename="dcoin_e_backup-`+t.Format(c.TimeFormat)+`.txt`) if _, err := c.w.Write(jsonData); err != nil { return "", utils.ErrInfo(errors.New("unable to write text")) } return "", nil }
func (p *Parser) ChangeNodeKeyRollback() error { // получим log_id, по которому можно найти данные, которые были до этого // $log_id всегда больше нуля, т.к. это откат обновления ключа logId, err := p.Single("SELECT log_id FROM miners_data WHERE user_id = ?", p.TxUserID).Int64() if err != nil { return p.ErrInfo(err) } // данные, которые восстановим data, err := p.OneRow("SELECT node_public_key, prev_log_id FROM log_miners_data WHERE log_id = ?", logId).String() if err != nil { return p.ErrInfo(err) } nodePublicKeyHex := utils.BinToHex([]byte(data["node_public_key"])) err = p.ExecSql("UPDATE miners_data SET node_public_key =[hex], log_id = ? WHERE user_id = ?", nodePublicKeyHex, data["prev_log_id"], p.TxUserID) if err != nil { return p.ErrInfo(err) } // подчищаем _log err = p.ExecSql("DELETE FROM log_miners_data WHERE log_id = ?", logId) if err != nil { return p.ErrInfo(err) } err = p.rollbackAI("log_miners_data", 1) if err != nil { return p.ErrInfo(err) } // проверим, не наш ли это user_id myUserId, _, myPrefix, _, err := p.GetMyUserId(p.TxMaps.Int64["user_id"]) if err != nil { return err } if p.TxUserID == myUserId { // обновим статус в нашей локальной табле. err = p.ExecSql("UPDATE "+myPrefix+"my_node_keys SET status = 'my_pending', block_id = 0, time = 0 WHERE hex(public_key) = ? AND status = 'approved' AND block_id = ?", p.TxMaps.Bytes["new_node_public_key"], p.BlockData.BlockId) if err != nil { return p.ErrInfo(err) } } return nil }
func (c *Controller) PoolAdminLogin() (string, error) { if !c.PoolAdmin { return "", utils.ErrInfo(errors.New("access denied")) } c.r.ParseForm() userId := int64(utils.StrToFloat64(c.Parameters["userId"])) publicKey, err := c.GetUserPublicKey(userId) if err != nil { return "", utils.ErrInfo(err) } c.sess.Set("user_id", userId) c.sess.Set("public_key", string(utils.BinToHex(publicKey))) TemplateStr, err := makeTemplate("pool_admin_login", "poolAdminLogin", &poolAdminPage{}) if err != nil { return "", utils.ErrInfo(err) } return TemplateStr, nil }
func (p *Parser) ChangePrimaryKeyInit() error { var err error fields := []string{"bin_public_keys", "sign"} p.TxMap, err = p.GetTxMap(fields) if err != nil { return p.ErrInfo(err) } // в 1 new_public_keys может быть от 1 до 3-х ключей i := 0 bin_public_keys := p.TxMap["bin_public_keys"] for { length := utils.DecodeLength(&bin_public_keys) pKey := utils.BytesShift(&bin_public_keys, length) p.newPublicKeysHex[i] = utils.BinToHex(pKey) if len(bin_public_keys) == 0 || i > 1 { break } i++ } return nil }
func (p *Parser) SendDcFront() error { err := p.generalCheck() if err != nil { return p.ErrInfo(err) } verifyData := map[string]string{"from_user_id": "bigint", "to_user_id": "bigint", "currency_id": "bigint", "amount": "amount", "commission": "amount", "comment": "comment"} err = p.CheckInputData(verifyData) if err != nil { return p.ErrInfo(err) } if p.TxMaps.Float64["amount"] < 0.01 { // 0.01 - минимальная сумма return p.ErrInfo("amount") } // проверим, существует ли такая валюта в таблиуе DC-валют checkCurrency, err := p.CheckCurrency(p.TxMaps.Int64["currency_id"]) if !checkCurrency { // если нет, то проверяем список CF-валют checkCurrency, err := p.CheckCurrencyCF(p.TxMaps.Int64["currency_id"]) if err != nil { return p.ErrInfo(err) } if !checkCurrency { return p.ErrInfo("currency_id") } } nodeCommission, err := p.getMyNodeCommission(p.TxMaps.Int64["currency_id"], p.TxUserID, p.TxMaps.Float64["amount"]) if err != nil { return p.ErrInfo(err) } // проверим, удовлетворяет ли нас комиссия, которую предлагает юзер if p.TxMaps.Float64["commission"] < nodeCommission { return p.ErrInfo(fmt.Sprintf("commission %v<%v", p.TxMaps.Float64["commission"], nodeCommission)) } if p.BlockData != nil && p.BlockData.BlockId <= consts.ARBITRATION_BLOCK_START { for i := 0; i < 5; i++ { p.TxMaps.Float64["arbitrator"+utils.IntToStr(i)+"_commission"] = 0 // для check_sender_money } forSign := fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["to_user_id"], p.TxMap["amount"], p.TxMap["commission"], utils.BinToHex(p.TxMap["comment"]), p.TxMap["currency_id"]) CheckSignResult, err := utils.CheckSign(p.PublicKeys, forSign, p.TxMap["sign"], false) if err != nil { return p.ErrInfo(err) } if !CheckSignResult { return p.ErrInfo("incorrect sign") } } else { dupArray := make(map[int64]int64) for i := 0; i < 5; i++ { arbitrator__commission := "arbitrator" + utils.IntToStr(i) + "_commission" arbitrator_ := "arbitrator" + utils.IntToStr(i) if !utils.CheckInputData(p.TxMap[arbitrator__commission], "amount") { return p.ErrInfo("arbitrator_commission") } if !utils.CheckInputData(p.TxMap[arbitrator_], "bigint") { return p.ErrInfo("arbitrator") } // если указал ID арбитра, то должна быть комиссия для него if p.TxMaps.Int64[arbitrator_] > 0 && p.TxMaps.Float64[arbitrator__commission] < 0.01 { return p.ErrInfo("arbitrator_commission") } // на всяк случай не даем арбитрам возможность быть арбитрами самим себе if p.TxMaps.Int64[arbitrator_] == p.TxUserID { return p.ErrInfo("arbitrator = user_id") } if p.TxMaps.Int64[arbitrator_] > 0 { dupArray[utils.BytesToInt64(p.TxMap[arbitrator_])]++ if dupArray[utils.BytesToInt64(p.TxMap[arbitrator_])] > 1 { return p.ErrInfo("doubles") } } if p.TxMaps.Int64[arbitrator_] > 0 { arbitrator := p.TxMap[arbitrator_] // проверим, является ли арбитром указанный user_id arbitratorConditionsJson, err := p.Single("SELECT conditions FROM arbitrator_conditions WHERE user_id = ?", utils.BytesToInt64(arbitrator)).Bytes() if err != nil { return p.ErrInfo(err) } arbitratorConditionsMap := make(map[string][5]string) err = json.Unmarshal(arbitratorConditionsJson, &arbitratorConditionsMap) // арбитр к этому моменту мог передумать и убрать свои условия, уйдя из арбитров для новых сделок поставив [0] что вызовет тут ошибку if err != nil { log.Debug("arbitratorConditionsJson", arbitratorConditionsJson) return p.ErrInfo(err) } // проверим, работает ли выбранный арбитр с валютой данной сделки var checkCurrency int64 if p.TxMaps.Int64["currency_id"] > 1000 { checkCurrency = 1000 } else { checkCurrency = p.TxMaps.Int64["currency_id"] } if len(arbitratorConditionsMap[utils.Int64ToStr(checkCurrency)]) == 0 { return p.ErrInfo("len(arbitratorConditionsMap[checkCurrency]) == 0") } // указан ли этот арбитр в списке доверенных у продавца sellerArbitrator, err := p.Single("SELECT user_id FROM arbitration_trust_list WHERE user_id = ? AND arbitrator_user_id = ?", p.TxMaps.Int64["to_user_id"], utils.BytesToInt64(arbitrator)).Int64() if err != nil { return p.ErrInfo(err) } if sellerArbitrator == 0 { return p.ErrInfo("sellerArbitrator == 0") } // указан ли этот арбитр в списке доверенных у покупателя buyerArbitrator, err := p.Single("SELECT user_id FROM arbitration_trust_list WHERE user_id = ? AND arbitrator_user_id = ?", p.TxMaps.Int64["from_user_id"], utils.BytesToInt64(arbitrator)).Int64() if err != nil { return p.ErrInfo(err) } if buyerArbitrator == 0 { return p.ErrInfo("buyerArbitrator == 0") } // согласен ли продавец на манибек arbitrationDaysRefund, err := p.Single("SELECT arbitration_days_refund FROM users WHERE user_id = ?", p.TxMaps.Int64["to_user_id"]).Int64() if err != nil { return p.ErrInfo(err) } if arbitrationDaysRefund == 0 { return p.ErrInfo("buyerArbitrator == 0") } // готов ли арбитр рассматривать такую сумму сделки currencyIdStr := utils.Int64ToStr(p.TxMaps.Int64["currency_id"]) if p.TxMaps.Float64["amount"] < utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][0]) || (p.TxMaps.Float64["amount"] > utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][1]) && utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][1]) > 0) { return p.ErrInfo("amount") } // мин. комиссия, на которую согласен арбитр minArbitratorCommission := utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][4]) / 100 * p.TxMaps.Float64["amount"] if minArbitratorCommission > utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][3]) && utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][3]) > 0 { minArbitratorCommission = utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][3]) } if minArbitratorCommission < utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][2]) { minArbitratorCommission = utils.StrToFloat64(arbitratorConditionsMap[currencyIdStr][2]) } if utils.BytesToFloat64(p.TxMap[arbitrator__commission]) < minArbitratorCommission { return p.ErrInfo(" < minArbitratorCommission") } } } forSign := fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["to_user_id"], p.TxMap["amount"], p.TxMap["commission"], p.TxMap["arbitrator0"], p.TxMap["arbitrator1"], p.TxMap["arbitrator2"], p.TxMap["arbitrator3"], p.TxMap["arbitrator4"], p.TxMap["arbitrator0_commission"], p.TxMap["arbitrator1_commission"], p.TxMap["arbitrator2_commission"], p.TxMap["arbitrator3_commission"], p.TxMap["arbitrator4_commission"], utils.BinToHex(p.TxMap["comment"]), p.TxMap["currency_id"]) CheckSignResult, err := utils.CheckSign(p.PublicKeys, forSign, p.TxMap["sign"], false) if err != nil { return p.ErrInfo(err) } if !CheckSignResult { return p.ErrInfo("incorrect sign") } } /* wallets_buffer сделан не для защиты от двойной траты, а для того, чтобы нода, которая генерит блок не записала двойное списание в свой блок, который будет отправлен другим нодам и будет ими отвергнут. Для тр-ий типа new_forex_order используется простой запрет на запись в блок тр-ии new_forex_order+new_forex_order или new_forex_order+send_dc и пр. защита от двойного списания на основе даннных из блока, полученного из сети заключается в постепенной обработке тр-ий путем проверки front_ и занесения данных в БД (ParseDataFull). */ amountAndCommission, err := p.checkSenderMoney(p.TxMaps.Int64["currency_id"], p.TxMaps.Int64["from_user_id"], p.TxMaps.Float64["amount"], p.TxMaps.Float64["commission"], p.TxMaps.Float64["arbitrator0_commission"], p.TxMaps.Float64["arbitrator1_commission"], p.TxMaps.Float64["arbitrator2_commission"], p.TxMaps.Float64["arbitrator3_commission"], p.TxMaps.Float64["arbitrator4_commission"]) if err != nil { return p.ErrInfo(err) } // существует ли юзер-получатель err = p.CheckUser(p.TxMaps.Int64["to_user_id"]) if err != nil { return p.ErrInfo(err) } err = p.checkSpamMoney(p.TxMaps.Int64["currency_id"], p.TxMaps.Float64["amount"]) if err != nil { return p.ErrInfo(err) } // вычитаем из wallets_buffer // amount_and_commission взято из check_sender_money() err = p.updateWalletsBuffer(amountAndCommission, p.TxMaps.Int64["currency_id"]) if err != nil { return p.ErrInfo(err) } return nil }
func (p *Parser) SendDc() error { // нужно отметить в log_time_money_orders, что тр-ия прошла в блок err := p.ExecSql("UPDATE log_time_money_orders SET del_block_id = ? WHERE hex(tx_hash) = ?", p.BlockData.BlockId, p.TxHash) if err != nil { return p.ErrInfo(err) } // 1 возможно нужно обновить таблицу points_status err = p.pointsUpdateMain(p.BlockData.UserId) if err != nil { return p.ErrInfo(err) } // 2 err = p.pointsUpdateMain(p.TxMaps.Int64["from_user_id"]) if err != nil { return p.ErrInfo(err) } // 3 err = p.pointsUpdateMain(p.TxMaps.Int64["to_user_id"]) if err != nil { return p.ErrInfo(err) } commission := p.TxMaps.Float64["commission"] var arbitration_days_refund int64 var seller_hold_back_pct float64 var arbitrators bool if p.BlockData.BlockId > consts.ARBITRATION_BLOCK_START { // на какой период манибека согласен продавец err := p.QueryRow(p.FormatQuery("SELECT arbitration_days_refund, seller_hold_back_pct FROM users WHERE user_id = ?"), p.TxMaps.Int64["to_user_id"]).Scan(&arbitration_days_refund, &seller_hold_back_pct) if err != nil && err != sql.ErrNoRows { return p.ErrInfo(err) } if arbitration_days_refund > 0 { for i := 0; i < 5; i++ { iStr := utils.IntToStr(i) if p.TxMaps.Int64["arbitrator"+iStr] > 0 && p.TxMaps.Float64["arbitrator"+iStr+"_commission"] >= 0.01 { // нужно учесть комиссию арбитра commission += p.TxMaps.Float64["arbitrator"+iStr+"_commission"] arbitrators = true } } } } // 4 обновим сумму на кошельке отправителя, залогировав предыдущее значение err = p.updateSenderWallet(p.TxMaps.Int64["from_user_id"], p.TxMaps.Int64["currency_id"], p.TxMaps.Float64["amount"], commission, "from_user", p.TxMaps.Int64["to_user_id"], p.TxMaps.Int64["to_user_id"], string(utils.BinToHex(p.TxMap["comment"])), "encrypted") if err != nil { return p.ErrInfo(err) } log.Debug("SendDC updateRecipientWallet") // 5 обновим сумму на кошельке получателю err = p.updateRecipientWallet(p.TxMaps.Int64["to_user_id"], p.TxMaps.Int64["currency_id"], p.TxMaps.Float64["amount"], "from_user", p.TxMaps.Int64["from_user_id"], p.TxMaps.String["comment"], "encrypted", true) if err != nil { return p.ErrInfo(err) } // 6 теперь начисляем комиссию майнеру, который этот блок сгенерил if p.TxMaps.Float64["commission"] >= 0.01 { err = p.updateRecipientWallet(p.BlockData.UserId, p.TxMaps.Int64["currency_id"], p.TxMaps.Float64["commission"], "node_commission", p.BlockData.BlockId, "", "encrypted", true) if err != nil { return p.ErrInfo(err) } } log.Debug("p.BlockData.BlockId", p.BlockData.BlockId) log.Debug("consts.ARBITRATION_BLOCK_START", consts.ARBITRATION_BLOCK_START) if p.BlockData.BlockId > consts.ARBITRATION_BLOCK_START { // если продавец не согласен на арбитраж, то $arbitration_days_refund будет равно 0 log.Debug("arbitration_days_refund", arbitration_days_refund) log.Debug("arbitrators", arbitrators) if arbitration_days_refund > 0 && arbitrators { holdBackAmount := math.Floor(utils.Round(p.TxMaps.Float64["amount"]*(seller_hold_back_pct/100), 3)*100) / 100 if holdBackAmount < 0.01 { holdBackAmount = 0.01 } orderId, err := p.ExecSqlGetLastInsertId("INSERT INTO orders ( time, buyer, seller, arbitrator0, arbitrator1, arbitrator2, arbitrator3, arbitrator4, amount, hold_back_amount, currency_id, block_id, end_time ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )", "id", p.BlockData.Time, p.TxMaps.Int64["from_user_id"], p.TxMaps.Int64["to_user_id"], p.TxMaps.Int64["arbitrator0"], p.TxMaps.Int64["arbitrator1"], p.TxMaps.Int64["arbitrator2"], p.TxMaps.Int64["arbitrator3"], p.TxMaps.Int64["arbitrator4"], p.TxMaps.Float64["amount"], holdBackAmount, p.TxMaps.Int64["currency_id"], p.BlockData.BlockId, (p.BlockData.Time + arbitration_days_refund*86400)) if err != nil { return p.ErrInfo(err) } // начисляем комиссию арбитрам for i := 0; i < 5; i++ { iStr := utils.IntToStr(i) log.Debug("arbitrator_commission "+iStr, p.TxMaps.Float64["arbitrator"+iStr+"_commission"]) if p.TxMaps.Int64["arbitrator"+iStr] > 0 && p.TxMaps.Float64["arbitrator"+iStr+"_commission"] >= 0.01 { log.Debug("updateRecipientWallet") err = p.updateRecipientWallet(p.TxMaps.Int64["arbitrator"+iStr], p.TxMaps.Int64["currency_id"], p.TxMaps.Float64["arbitrator"+iStr+"_commission"], "arbitrator_commission", orderId, "", "encrypted", true) if err != nil { return p.ErrInfo(err) } } } } } // отмечаем данную транзакцию в буфере как отработанную и ставим в очередь на удаление err = p.ExecSql("UPDATE wallets_buffer SET del_block_id = ? WHERE hex(hash) = ?", p.BlockData.BlockId, p.TxHash) if err != nil { return p.ErrInfo(err) } return nil }
func Exchange(chBreaker chan bool, chAnswer chan string) { defer func() { if r := recover(); r != nil { log.Error("daemon Recovered", r) panic(r) } }() const GoroutineName = "Exchange" 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 } BEGIN: for { log.Info(GoroutineName) MonitorDaemonCh <- []string{GoroutineName, utils.Int64ToStr(utils.Time())} // проверим, не нужно ли нам выйти из цикла if CheckDaemonsRestart(chBreaker, chAnswer, GoroutineName) { break BEGIN } blockId, err := d.GetConfirmedBlockId() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } var myPrefix string community, err := d.GetCommunityUsers() if len(community) > 0 { adminUserId, err := d.GetPoolAdminUserId() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } myPrefix = utils.Int64ToStr(adminUserId) + "_" } else { myPrefix = "" } eConfig, err := d.GetMap(`SELECT * FROM e_config`, "name", "value") if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } confirmations := utils.StrToInt64(eConfig["confirmations"]) mainDcAccount := utils.StrToInt64(eConfig["main_dc_account"]) // все валюты, с которыми работаем currencyList, err := utils.EGetCurrencyList() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // ++++++++++++ reduction ++++++++++++ // максимальный номер блока для процентов. Чтобы брать только новые maxReductionBlock, err := d.Single(`SELECT max(block_id) FROM e_reduction`).Int64() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // если есть свежая reduction, то нужно остановить торги reduction, err := d.Single(`SELECT block_id FROM reduction WHERE block_id > ? and pct > 0`, maxReductionBlock).Int64() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if reduction > 0 { err = d.ExecSql(`INSERT INTO e_reduction_lock (time) VALUES (?)`, utils.Time()) if err != nil { log.Error("%v", utils.ErrInfo(err)) } } // если уже прошло 10 блоков с момента обнаружения reduction, то производим сокращение объема монет rows, err := d.Query(d.FormatQuery(`SELECT pct, currency_id, time, block_id FROM reduction WHERE block_id > ? AND block_id < ?`), maxReductionBlock, blockId-confirmations) if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var pct float64 var currencyId, rTime, blockId int64 err = rows.Scan(&pct, ¤cyId, &rTime, &blockId) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // $k = (100-$row['pct'])/100; k := (100 - pct) / 100 // уменьшаем все средства на счетах err = d.ExecSql(`UPDATE e_wallets SET amount = amount * ? WHERE currency_id = ?`, k, currencyId) // уменьшаем все средства на вывод err = d.ExecSql(`UPDATE e_withdraw SET amount = amount * ? WHERE currency_id = ? AND close_time = 0`, k, currencyId) // уменьшаем все ордеры на продажу err = d.ExecSql(`UPDATE e_orders SET amount = amount * ?, begin_amount = begin_amount * ? WHERE sell_currency_id = ?`, k, k, currencyId) err = d.ExecSql(`INSERT INTO e_reduction ( time, block_id, currency_id, pct ) VALUES ( ?, ?, ?, ? )`, rTime, blockId, currencyId, pct) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } rows.Close() // ++++++++++++ DC ++++++++++++ /* * Важно! отключать в кроне при обнулении данных в БД * * 1. Получаем инфу о входящих переводах и начисляем их на счета юзеров * 2. Обновляем проценты * 3. Чистим кол-во отправленных смс-ок * */ nodePrivateKey, err := d.GetNodePrivateKey(myPrefix) if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } /* * Получаем инфу о входящих переводах и начисляем их на счета юзеров * */ // если всё остановлено из-за найденного блока с reduction, то входящие переводы не обрабатываем reductionLock, err := utils.EGetReductionLock() if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if reductionLock > 0 { if d.dPrintSleep(utils.ErrInfo("reductionLock"), d.sleepTime) { break BEGIN } continue BEGIN } rows, err = d.Query(d.FormatQuery(` SELECT amount, id, block_id, type_id, currency_id, to_user_id, time, comment, comment_status FROM my_dc_transactions WHERE type = 'from_user' AND block_id < ? AND exchange_checked = 0 AND status = 'approved' AND to_user_id = ? ORDER BY id DESC`), blockId-confirmations, mainDcAccount) if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var amount float64 var id, blockId, typeId, currencyId, toUserId, txTime int64 var comment, commentStatus string err = rows.Scan(&amount, &id, &blockId, &typeId, ¤cyId, &toUserId, &txTime, &comment, &commentStatus) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // отметим exchange_checked=1, чтобы больше не брать эту тр-ию err = d.ExecSql(`UPDATE my_dc_transactions SET exchange_checked = 1 WHERE id = ?`, id) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } // вначале нужно проверить, точно ли есть такой перевод в блоке binaryData, err := d.Single(`SELECT data FROM block_chain WHERE id = ?`, blockId).Bytes() if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } p := new(dcparser.Parser) p.DCDB = d.DCDB p.BinaryData = binaryData p.ParseDataLite() for _, txMap := range p.TxMapArr { // пропускаем все ненужные тр-ии if utils.BytesToInt64(txMap["type"]) != utils.TypeInt("SendDc") { continue } log.Debug("md5hash %s", txMap["md5hash"]) // если что-то случится с таблой my_dc_transactions, то все ввода на биржу будут зачислены по новой // поэтому нужно проверять e_adding_funds exists, err := d.Single(`SELECT id FROM e_adding_funds WHERE hex(tx_hash) = ?`, string(txMap["md5hash"])).Int64() if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } log.Debug("exists %d", exists) if exists != 0 { continue } log.Debug("user_id = %d / typeId = %d / currency_id = %d / currencyId = %d / amount = %f / amount = %f / comment = %s / comment = %s / to_user_id = %d / toUserId = %d ", utils.BytesToInt64(txMap["user_id"]), typeId, utils.BytesToInt64(txMap["currency_id"]), currencyId, utils.BytesToFloat64(txMap["amount"]), amount, string(utils.BinToHex(txMap["comment"])), comment, utils.BytesToInt64(txMap["to_user_id"]), toUserId) // сравнение данных из таблы my_dc_transactions с тем, что в блоке if utils.BytesToInt64(txMap["user_id"]) == typeId && utils.BytesToInt64(txMap["currency_id"]) == currencyId && utils.BytesToFloat64(txMap["amount"]) == amount && string(utils.BinToHex(txMap["comment"])) == comment && utils.BytesToInt64(txMap["to_user_id"]) == toUserId { decryptedComment := comment if commentStatus == "encrypted" { // расшифруем коммент block, _ := pem.Decode([]byte(nodePrivateKey)) if block == nil || block.Type != "RSA PRIVATE KEY" { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } private_key, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } decryptedComment_, err := rsa.DecryptPKCS1v15(rand.Reader, private_key, utils.HexToBin(comment)) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } decryptedComment = string(decryptedComment_) // запишем расшифрованный коммент, чтобы потом можно было найти перевод в ручном режиме err = d.ExecSql("UPDATE "+myPrefix+"my_dc_transactions SET comment = ?, comment_status = 'decrypted' WHERE id = ?", decryptedComment, id) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } // возможно юзер сделал перевод в той валюте, которая у нас на бирже еще не торгуется if len(currencyList[currencyId]) == 0 { log.Error("currencyId %d not trading", currencyId) continue } // возможно, что чуть раньше было reduction, а это значит, что все тр-ии, // которые мы ещё не обработали и которые были До блока с reduction нужно принимать с учетом reduction // т.к. средства на нашем счете уже урезались, а вот те, что после reduction - остались в том виде, в котором пришли lastReduction, err := d.OneRow("SELECT block_id, pct FROM reduction WHERE currency_id = ? ORDER BY block_id", currencyId).Int64() if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } if blockId <= lastReduction["block_id"] { // сумму с учетом reduction k0 := (100 - lastReduction["pct"]) / 100 amount = amount * float64(k0) } // начисляем средства на счет того, чей id указан в комменте r, _ := regexp.Compile(`(?i)\s*#\s*([0-9]+)\s*`) user := r.FindStringSubmatch(decryptedComment) if len(user) == 0 { log.Error("len(user) == 0") continue } log.Debug("user %s", user[1]) // user_id с биржевой таблы uid := utils.StrToInt64(user[1]) userAmountAndProfit := utils.EUserAmountAndProfit(uid, currencyId) newAmount_ := userAmountAndProfit + amount utils.UpdEWallet(uid, currencyId, utils.Time(), newAmount_, true) // для отчетности запишем в историю err = d.ExecSql(` INSERT INTO e_adding_funds ( user_id, currency_id, time, amount, tx_hash ) VALUES ( ?, ?, ?, ?, ? )`, uid, currencyId, txTime, amount, string(txMap["md5hash"])) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } } } } rows.Close() /* * Обновляем проценты * */ // максимальный номер блока для процентов. Чтобы брать только новые maxPctBlock, err := d.Single(`SELECT max(block_id) FROM e_user_pct`).Int64() log.Debug(`SELECT time, block_id, currency_id, user FROM pct WHERE block_id < ` + utils.Int64ToStr(blockId-confirmations) + ` AND block_id > ` + utils.Int64ToStr(maxPctBlock)) rows, err = d.Query(d.FormatQuery(`SELECT time, block_id, currency_id, user FROM pct WHERE block_id < ? AND block_id > ?`), blockId-confirmations, maxPctBlock) if err != nil { if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } for rows.Next() { var pct float64 var pTime, blockId, currencyId int64 err = rows.Scan(&pTime, &blockId, ¤cyId, &pct) if err != nil { rows.Close() if d.dPrintSleep(utils.ErrInfo(err), d.sleepTime) { break BEGIN } continue BEGIN } d.ExecSql(` INSERT INTO e_user_pct ( time, block_id, currency_id, pct ) VALUES ( ?, ?, ?, ? )`, pTime, blockId, currencyId, pct) } rows.Close() if d.dSleep(d.sleepTime) { break BEGIN } } log.Debug("break BEGIN %v", GoroutineName) }
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 (c *Controller) BlockExplorer() (string, error) { var err error blockId := int64(utils.StrToFloat64(c.Parameters["blockId"])) start := int64(utils.StrToFloat64(c.Parameters["start"])) var data, sql string if start > 0 || (start == 0 && blockId == 0) { if start == 0 && blockId == 0 { data += "<h3>Latest Blocks</h3>" sql = ` SELECT data, hash FROM block_chain ORDER BY id DESC LIMIT 15` } else { sql = ` SELECT data, hash FROM block_chain ORDER BY id ASC LIMIT ` + utils.Int64ToStr(start-1) + `, 100` } data += `<table class="table"><tr><th>Block</th><th>Hash</th><th>Time</th><th><nobr>User id</nobr></th><th><nobr>Miner id</nobr></th><th>Level</th><th>Transactions</th></tr>` blocksChain, err := c.GetAll(sql, -1) if err != nil { return "", utils.ErrInfo(err) } for _, blockData := range blocksChain { hash := utils.BinToHex([]byte(blockData["hash"])) binaryData := []byte(blockData["data"]) parser := new(dcparser.Parser) parser.DCDB = c.DCDB parser.BinaryData = binaryData err = parser.ParseDataLite() parser.BlockData.Sign = utils.BinToHex(parser.BlockData.Sign) minerId, err := c.GetMinerId(parser.BlockData.UserId) if err != nil { return "", utils.ErrInfo(err) } data += fmt.Sprintf(`<tr><td><a href="#" onclick="dc_navigate('blockExplorer', {'blockId':%d})">%d</a></td><td>%s</td><td><nobr><span class='unixtime'>%d</span></nobr></td><td>%d</td><td>%d</td><td>%d</td><td>`, parser.BlockData.BlockId, parser.BlockData.BlockId, hash, parser.BlockData.Time, parser.BlockData.UserId, minerId, parser.BlockData.Level) data += utils.IntToStr(len(parser.TxMapArr)) data += "</td></tr>" } data += "</table>" } else if blockId > 0 { data += `<table class="table">` blockChain, err := c.OneRow("SELECT data, hash, cur_0l_miner_id, max_miner_id FROM block_chain WHERE id = ?", blockId).String() if err != nil { return "", utils.ErrInfo(err) } binToHexArray := []string{"sign", "public_key", "encrypted_message", "comment", "bin_public_keys"} hash := utils.BinToHex([]byte(blockChain["hash"])) binaryData := blockChain["data"] parser := new(dcparser.Parser) parser.DCDB = c.DCDB parser.BinaryData = []byte(binaryData) err = parser.ParseDataLite() if err != nil { return "", utils.ErrInfo(err) } parser.BlockData.Sign = utils.BinToHex(parser.BlockData.Sign) previous := parser.BlockData.BlockId - 1 next := parser.BlockData.BlockId + 1 levelsRange := utils.GetBlockGeneratorMinerIdRange(utils.StrToInt64(blockChain["cur_0l_miner_id"]), utils.StrToInt64(blockChain["max_miner_id"])) minerId, err := c.GetMinerId(parser.BlockData.UserId) if err != nil { return "", utils.ErrInfo(err) } _, _, _, CurrentUserId, _, _, _ := c.TestBlock() maxMinerId, err := c.Single("SELECT max(miner_id) FROM miners").Int64() if err != nil { return "", utils.ErrInfo(err) } currentMinerId, err := c.Single("SELECT miner_id FROM miners_data WHERE user_id = ?", CurrentUserId).Int64() if err != nil { return "", utils.ErrInfo(err) } NextBlockLevelsRange := utils.GetBlockGeneratorMinerIdRange(currentMinerId, maxMinerId) data += fmt.Sprintf(`<tr><td><strong>Raw data</strong></td><td><a href='ajax?controllerName=getBlock&id=%d&download=1' target='_blank'>Download</a></td></tr>`, parser.BlockData.BlockId) data += fmt.Sprintf(`<tr><td><strong>Block_id</strong></td><td>%d (<a href="#" onclick="dc_navigate('blockExplorer', {'blockId':%d})">Previous</a> / <a href="#" onclick="dc_navigate('blockExplorer', {'blockId':%d})">Next</a> )</td></tr>`, parser.BlockData.BlockId, previous, next) data += fmt.Sprintf(`<tr><td><strong>Hash</strong></td><td>%s</td></tr>`, hash) data += fmt.Sprintf(`<tr><td><strong>Time</strong></td><td><span class='unixtime'>%d</span> / %d</td></tr>`, parser.BlockData.Time, parser.BlockData.Time) data += fmt.Sprintf(`<tr><td><strong>User_id</strong></td><td>%d</td></tr>`, parser.BlockData.UserId) data += fmt.Sprintf(`<tr><td><strong>Miner_Id</strong></td><td>%d</td></tr>`, minerId) data += fmt.Sprintf(`<tr><td><strong>Level</strong></td><td>%d (%v) next: %v</td></tr>`, parser.BlockData.Level, levelsRange, NextBlockLevelsRange) data += fmt.Sprintf(`<tr><td><strong>Sign</strong></td><td>%s</td></tr>`, parser.BlockData.Sign) if len(parser.TxMapArr) > 0 { data += `<tr><td><strong>Transactions</strong></td><td><div><pre style='width: 700px'>` for i := 0; i < len(parser.TxMapArr); i++ { for k, data_ := range parser.TxMapArr[i] { if utils.InSliceString(k, binToHexArray) { parser.TxMapArr[i][k] = utils.BinToHex(data_) } if k == "file" { parser.TxMapArr[i][k] = []byte("file size: " + utils.IntToStr(len(data_))) } else if k == "code" { parser.TxMapArr[i][k] = utils.DSha256(data_) } else if k == "secret" { parser.TxMapArr[i][k] = utils.BinToHex(data_) } data += fmt.Sprintf("%v : %s\n", k, parser.TxMapArr[i][k]) } data += "\n\n" } data += "</pre></div></td></tr>" } data += "</table>" } // пока панель тут myNotice := make(map[string]string) if c.SessUserId > 0 { myNotice, err = c.GetMyNoticeData(c.SessUserId, c.SessUserId, c.MyPrefix, c.Lang) if err != nil { return "", utils.ErrInfo(err) } } TemplateStr, err := makeTemplate("block_explorer", "blockExplorer", &BlockExplorerPage{ Lang: c.Lang, CurrencyList: c.CurrencyListCf, MyNotice: myNotice, Data: data, Start: start, BlockId: blockId, PoolAdmin: c.PoolAdmin, SessRestricted: c.SessRestricted, UserId: c.SessUserId}) if err != nil { return "", utils.ErrInfo(err) } return TemplateStr, nil }
func (p *Parser) CashRequestOut() error { // возможно нужно обновить таблицу points_status err := p.pointsUpdateMain(p.TxMaps.Int64["to_user_id"]) if err != nil { return p.ErrInfo(err) } // у получателя запроса останавливается майнинг по всем валютам и статусам, т.е. mining/pending. значит необходимо обновить tdc_amount и tdc_amount_update // WOC продолжает расти // обновление нужно, только если данный cash_request единственный с pending, иначе делать пересчет tdc_amount нельзя, т.к. уже были ранее пересчитаны existsRequests := p.CheckCashRequests(p.TxMaps.Int64["to_user_id"]) if existsRequests == nil { log.Debug("updPromisedAmounts=", existsRequests) err = p.updPromisedAmounts(p.TxMaps.Int64["to_user_id"], true, true, p.BlockData.Time) if err != nil { return p.ErrInfo(err) } } else { // записываем cash_request_out_time во всех обещанных суммах, после того, как юзер вызвал актуализацию. акутализацию юзер вызывал т.к. у него есть непогашенные cash_request. log.Debug("updPromisedAmountsCashRequestOutTime") err = p.updPromisedAmountsCashRequestOutTime(p.TxMaps.Int64["to_user_id"]) if err != nil { return p.ErrInfo(err) } } // пишем запрос в БД cashRequestId, err := p.ExecSqlGetLastInsertId("INSERT INTO cash_requests ( time, from_user_id, to_user_id, currency_id, amount, hash_code ) VALUES ( ?, ?, ?, ?, ?, [hex] )", "id", p.BlockData.Time, p.TxUserID, p.TxMaps.Int64["to_user_id"], p.TxMaps.Int64["currency_id"], p.TxMaps.Money["amount"], p.TxMaps.String["hash_code"]) if err != nil { return p.ErrInfo(err) } // а может быть наш юзер - получатель запроса myUserId, myBlockId, myPrefix, _, err := p.GetMyUserId(p.TxMaps.Int64["to_user_id"]) if err != nil { return err } if p.TxMaps.Int64["to_user_id"] == myUserId && myBlockId <= p.BlockData.BlockId { // пишем с таблу инфу, что к нам пришел новый запрос err = p.ExecSql("INSERT INTO "+myPrefix+"my_cash_requests ( time, to_user_id, currency_id, amount, comment, comment_status, status, hash_code, cash_request_id ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )", p.BlockData.Time, p.TxMaps.Int64["to_user_id"], p.TxMaps.Int64["currency_id"], p.TxMaps.Money["amount"], utils.BinToHex(p.TxMap["comment"]), "encrypted", "pending", p.TxMaps.String["hash_code"], cashRequestId) if err != nil { return p.ErrInfo(err) } } // или отправитель запроса - наш юзер myUserId, myBlockId, myPrefix, _, err = p.GetMyUserId(p.TxUserID) if err != nil { return err } if p.TxUserID == myUserId && myBlockId <= p.BlockData.BlockId { myId, err := p.Single("SELECT id FROM "+myPrefix+"my_cash_requests WHERE to_user_id = ? AND status = 'my_pending' ORDER BY id DESC", p.TxMaps.Int64["to_user_id"]).Int64() if err != nil { return p.ErrInfo(err) } if myId > 0 { // обновим статус в нашей локальной табле. // у юзера может быть только 1 запрос к 1 юзеру со статусом my_pending err = p.ExecSql("UPDATE "+myPrefix+"my_cash_requests SET status = 'pending', time = ?, cash_request_id = ? WHERE id = ?", p.BlockData.Time, cashRequestId, myId) if err != nil { return p.ErrInfo(err) } } else { err = p.ExecSql("INSERT INTO "+myPrefix+"my_cash_requests ( to_user_id, currency_id, amount, comment, hash_code, status, cash_request_id ) VALUES ( ?, ?, ?, ?, ?, ?, ? )", p.TxMaps.Int64["to_user_id"], p.TxMaps.Int64["currency_id"], p.TxMaps.Money["amount"], "", p.TxMaps.String["hash_code"], "pending", cashRequestId) if err != nil { return p.ErrInfo(err) } } myId, err = p.Single("SELECT id FROM "+myPrefix+"my_dc_transactions WHERE status = 'pending' AND type = 'cash_request' AND to_user_id = ? AND amount = ? AND currency_id = ?", p.TxMaps.Int64["to_user_id"], p.TxMaps.Money["amount"], p.TxMaps.Int64["currency_id"]).Int64() if err != nil { return p.ErrInfo(err) } if myId > 0 { // чтобы при вызове update_sender_wallet из cash_request_in можно было обновить my_dc_transactions, т.к. там в WHERE есть type_id err = p.ExecSql("UPDATE "+myPrefix+"my_dc_transactions SET type_id=?, time = ? WHERE id = ?", cashRequestId, p.BlockData.Time, myId) if err != nil { return p.ErrInfo(err) } } else { err = p.ExecSql("INSERT INTO "+myPrefix+"my_dc_transactions ( status, type, type_id, to_user_id, amount, currency_id, comment, comment_status ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )", "pending", "cash_request", cashRequestId, p.TxMaps.Int64["to_user_id"], p.TxMaps.Money["amount"], p.TxMaps.Int64["currency_id"], utils.BinToHex(p.TxMap["comment"]), "encrypted") if err != nil { return p.ErrInfo(err) } } } return nil }
func (p *Parser) CashRequestOutFront() error { err := p.generalCheck() if err != nil { return p.ErrInfo(err) } verifyData := map[string]string{"to_user_id": "bigint", "currency_id": "currency_id", "amount": "amount", "comment": "comment", "hash_code": "sha256"} err = p.CheckInputData(verifyData) if err != nil { return p.ErrInfo(err) } // нельзя слать запрос на woc if p.TxMaps.Int64["currency_id"] == 1 { return p.ErrInfo("WOC") } // прошло ли 30 дней с момента регистрации майнера minerNewbie := p.checkMinerNewbie() if minerNewbie != nil { // возможно, что майнер отдал наличные за DC и у него есть общенные суммы с repaid // нужно дать возможность вывести ровно столько, сколько он отдал repaidAmount, err := p.GetRepaidAmount(p.TxMaps.Int64["currency_id"], p.TxUserID) // сколько уже получил наличных amountCashRequests, err := p.Single("SELECT sum(amount) FROM cash_requests WHERE status = 'approved' AND currency_id = ? AND from_user_id = ?", p.TxMaps.Int64["currency_id"], p.TxUserID).Float64() if err != nil { return p.ErrInfo(err) } if amountCashRequests+p.TxMaps.Money["amount"] > repaidAmount { return p.ErrInfo(fmt.Sprintf("%f + %f > %f", amountCashRequests, p.TxMaps.Money["amount"], repaidAmount)) } } forSign := fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s", p.TxMap["type"], p.TxMap["time"], p.TxMap["user_id"], p.TxMap["to_user_id"], p.TxMap["amount"], utils.BinToHex(p.TxMap["comment"]), p.TxMap["currency_id"], p.TxMap["hash_code"]) CheckSignResult, err := utils.CheckSign(p.PublicKeys, forSign, p.TxMap["sign"], false) if err != nil { return p.ErrInfo(err) } if !CheckSignResult { return p.ErrInfo("incorrect sign") } // проверим, существует ли такая валюта в таблице DC-валют if ok, err := p.CheckCurrency(p.TxMaps.Int64["currency_id"]); !ok { return p.ErrInfo(err) } // === begin проверка to_user_id // является ли данный юзер майнером err = p.checkMiner(p.TxMaps.Int64["to_user_id"]) if err != nil { return p.ErrInfo(err) } maxPromisedAmount, err := p.GetMaxPromisedAmount(p.TxMaps.Int64["currency_id"]) if err != nil { return p.ErrInfo(err) } repaidAmount, err := p.GetRepaidAmount(p.TxMaps.Int64["currency_id"], p.TxMaps.Int64["to_user_id"]) if err != nil { return p.ErrInfo(err) } if p.TxMaps.Money["amount"]+repaidAmount > maxPromisedAmount { return p.ErrInfo(fmt.Sprintf("%f + %f > %f", p.TxMaps.Money["amount"], repaidAmount, maxPromisedAmount)) } // не даем превысить общий лимит promisedAmount, err := p.Single("SELECT amount FROM promised_amount WHERE status = 'mining' AND currency_id = ? AND user_id = ? AND del_block_id = 0 AND del_mining_block_id = 0", p.TxMaps.Int64["currency_id"], p.TxMaps.Int64["to_user_id"]).Float64() if err != nil { return p.ErrInfo(err) } rest := maxPromisedAmount - repaidAmount if rest < promisedAmount { promisedAmount = rest } // минимальная сумма. теоретически может делиться на min_promised_amount пока не достигнет 0.01 if p.TxMaps.Money["amount"] < promisedAmount/float64(p.Variables.Int64["min_promised_amount"]) { return p.ErrInfo(fmt.Sprintf("%f < %f / %d", p.TxMaps.Money["amount"], promisedAmount, p.Variables.Int64["min_promised_amount"])) } if p.TxMaps.Money["amount"] < 0.01 { return p.ErrInfo("amount<0.01") } var txTime int64 if p.BlockData != nil { // тр-ия пришла в блоке txTime = p.BlockData.Time } else { txTime = time.Now().Unix() - 30 // просто на всякий случай небольшой запас } // Чтобы не задалбывать получателей запроса на обмен, не даем отправить следующий запрос, пока не пройдет cash_request_time сек с момента предыдущего cashRequestPending, err := p.Single("SELECT status FROM cash_requests WHERE to_user_id = ? AND del_block_id = 0 AND for_repaid_del_block_id = 0 AND time > ? AND status = 'pending'", p.TxMaps.Int64["to_user_id"], (txTime - p.Variables.Int64["cash_request_time"])).String() if err != nil { return p.ErrInfo(err) } if len(cashRequestPending) > 0 { return p.ErrInfo("cash_requests status not null") } // не находится ли юзер в данный момент на каникулах. rows, err := p.Query(p.FormatQuery("SELECT start_time, end_time FROM holidays WHERE user_id = ? AND del = 0"), p.TxMaps.Int64["to_user_id"]) if err != nil { return p.ErrInfo(err) } defer rows.Close() for rows.Next() { var startTime, endTime int64 err = rows.Scan(&startTime, &endTime) if err != nil { return p.ErrInfo(err) } var time1, time2 int64 if p.BlockData != nil { time1 = p.BlockData.Time time2 = time1 } else { // тут используем time() с запасом 1800 сек, т.к. в момент, когда тр-ия попадет в блок, каникулы уже могут начаться. // т.е. у голой тр-ии проверка идет жестче time1 = time.Now().Unix() + 1800 time2 = time.Now().Unix() } if startTime <= time1 && endTime >= time2 { return p.ErrInfo("error holidays") } } // === end проверка to_user_id // === begin проверка отправителя // является ли данный юзер майнером err = p.checkMiner(p.TxUserID) if err != nil { return p.ErrInfo(err) } /* * WalletsBuffer тут не используем т.к. попадение 2-х тр-ий данного типа в 1 блок во время генарции блока исключается в clear_incompatible_tx. * там же исключается попадение данного типа с new_forex и пр. * А проверка 2-го списания идет в ParseDataFull * */ if p.BlockData != nil && p.BlockData.BlockId > 173941 { // в блоке 173941 была попытка отправить 2 запроса на 408 и 200 dUSD, в то время как на кошельке было только 449.6 // в итоге check_sender_money возвращало ошибку // есть ли нужная сумма на кошельке p.TxMaps.Int64["from_user_id"] = p.TxMaps.Int64["user_id"] for i := 0; i < 5; i++ { p.TxMaps.Money["arbitrator"+utils.IntToStr(i)+"_commission"] = 0 } p.TxMaps.Money["commission"] = 0 //func (p *Parser) checkSenderMoney(currencyId, fromUserId int64, amount, commission, arbitrator0_commission, arbitrator1_commission, arbitrator2_commission, arbitrator3_commission, arbitrator4_commission float64) (float64, error) { _, err := p.checkSenderMoney(p.TxMaps.Int64["currency_id"], p.TxMaps.Int64["from_user_id"], p.TxMaps.Money["amount"], p.TxMaps.Money["commission"], p.TxMaps.Money["arbitrator0_commission"], p.TxMaps.Money["arbitrator1_commission"], p.TxMaps.Money["arbitrator2_commission"], p.TxMaps.Money["arbitrator3_commission"], p.TxMaps.Money["arbitrator4_commission"]) if err != nil { return p.ErrInfo(err) } } // У юзера не должно быть cash_requests со статусом pending err = p.CheckCashRequests(p.TxUserID) if err != nil { return p.ErrInfo(err) } err = p.limitRequest(p.Variables.Int64["limit_cash_requests_out"], "cash_requests", p.Variables.Int64["limit_cash_requests_out_period"]) if err != nil { return p.ErrInfo(err) } // === end проверка отправителя return nil }
func (p *Parser) MoneyBackRequest() error { err := p.selectiveLoggingAndUpd([]string{"status"}, []interface{}{"refund"}, "orders", []string{"id"}, []string{utils.Int64ToStr(p.TxMaps.Int64["order_id"])}) if err != nil { return p.ErrInfo(err) } orderData, err := p.OneRow("SELECT seller, arbitrator0, arbitrator1, arbitrator2, arbitrator3, arbitrator4 FROM orders WHERE id = ?", p.TxMaps.Int64["order_id"]).Int64() if err != nil { return p.ErrInfo(err) } // проверим, не является ли мы продавцом или арбитром myUserId, _, myPrefix, _, err := p.GetMyUserId(orderData["seller"]) if err != nil { return p.ErrInfo(err) } if orderData["seller"] == myUserId { err = p.ExecSql("INSERT INTO "+myPrefix+"my_comments ( type, id, comment, comment_status ) VALUES ( 'seller', ?, ?, 'encrypted' )", p.TxMaps.Int64["order_id"], utils.BinToHex(p.TxMap["seller_enc_text"])) if err != nil { return p.ErrInfo(err) } } for i := 0; i < 5; i++ { iStr := utils.IntToStr(i) if orderData["arbitrator"+iStr] == 0 { continue } myUserId, _, myPrefix, _, err := p.GetMyUserId(orderData["arbitrator"+iStr]) if err != nil { return p.ErrInfo(err) } if orderData["arbitrator"+iStr] == myUserId { err = p.ExecSql("INSERT INTO "+myPrefix+"my_comments ( type, id, comment, comment_status ) VALUES ( 'arbitrator', ?, ?, 'encrypted' )", p.TxMaps.Int64["order_id"], utils.BinToHex(p.TxMaps.Bytes["arbitrator"+iStr+"_enc_text"])) if err != nil { return p.ErrInfo(err) } } } return nil }
func (p *Parser) AutoPayment() error { autoPaymentData, err := p.OneRow(` SELECT commission, amount, currency_id, recipient FROM auto_payments WHERE id = ? LIMIT 1 `, p.TxMaps.Int64["auto_payment_id"]).String() if err != nil { return p.ErrInfo(err) } commission := utils.StrToFloat64(autoPaymentData["commission"]) amount := utils.StrToFloat64(autoPaymentData["amount"]) currencyId := utils.StrToInt64(autoPaymentData["currency_id"]) recipient := utils.StrToInt64(autoPaymentData["recipient"]) // 1 возможно нужно обновить таблицу points_status err = p.pointsUpdateMain(p.BlockData.UserId) if err != nil { return p.ErrInfo(err) } // 2 err = p.pointsUpdateMain(p.TxUserID) if err != nil { return p.ErrInfo(err) } // 3 err = p.pointsUpdateMain(recipient) if err != nil { return p.ErrInfo(err) } // 4 обновим сумму на кошельке отправителя, залогировав предыдущее значение err = p.updateSenderWallet(p.TxUserID, currencyId, amount, commission, "from_user", recipient, recipient, string(utils.BinToHex(p.TxMap["comment"])), "encrypted") if err != nil { return p.ErrInfo(err) } log.Debug("AutoPayment updateRecipientWallet") // 5 обновим сумму на кошельке получателю err = p.updateRecipientWallet(recipient, currencyId, amount, "from_user", p.TxUserID, p.TxMaps.String["comment"], "encrypted", true) if err != nil { return p.ErrInfo(err) } // 6 теперь начисляем комиссию майнеру, который этот блок сгенерил if commission >= 0.01 { err = p.updateRecipientWallet(p.BlockData.UserId, currencyId, commission, "node_commission", p.BlockData.BlockId, "", "encrypted", true) if err != nil { return p.ErrInfo(err) } } // отмечаем данную транзакцию в буфере как отработанную и ставим в очередь на удаление err = p.ExecSql("UPDATE wallets_buffer SET del_block_id = ? WHERE hex(hash) = ?", p.BlockData.BlockId, p.TxHash) if err != nil { return p.ErrInfo(err) } return nil }
func (t *TcpServer) Type2() { // размер данных 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: %d", 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 } /* * Прием тр-ий от простых юзеров, а не нодов. Вызывается демоном disseminator * */ _, _, decryptedBinData, err := t.DecryptData(&binaryData) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("decryptedBinData: %x", decryptedBinData) // проверим размер if int64(len(binaryData)) > t.variables.Int64["max_tx_size"] { log.Debug("%v", utils.ErrInfo("len(txBinData) > max_tx_size")) return } if len(binaryData) < 5 { log.Debug("%v", utils.ErrInfo("len(binaryData) < 5")) return } decryptedBinDataFull := decryptedBinData txType := utils.BytesShift(&decryptedBinData, 1) // type txTime := utils.BytesShift(&decryptedBinData, 4) // time log.Debug("txType: %d", utils.BinToDec(txType)) log.Debug("txTime: %d", utils.BinToDec(txTime)) size := utils.DecodeLength(&decryptedBinData) log.Debug("size: %d", size) if int64(len(decryptedBinData)) < size { log.Debug("%v", utils.ErrInfo("len(binaryData) < size")) return } userId := utils.BytesToInt64(utils.BytesShift(&decryptedBinData, size)) log.Debug("userId: %d", userId) highRate := 0 if userId == 1 { highRate = 1 } // заливаем тр-ию в БД err = t.ExecSql(`DELETE FROM queue_tx WHERE hex(hash) = ?`, utils.Md5(decryptedBinDataFull)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } log.Debug("INSERT INTO queue_tx (hash, high_rate, data) (%s, %d, %s)", utils.Md5(decryptedBinDataFull), highRate, utils.BinToHex(decryptedBinDataFull)) err = t.ExecSql(`INSERT INTO queue_tx (hash, high_rate, data) VALUES ([hex], ?, [hex])`, utils.Md5(decryptedBinDataFull), highRate, utils.BinToHex(decryptedBinDataFull)) if err != nil { log.Error("%v", utils.ErrInfo(err)) return } } }
func (p *Parser) NewMiner() error { tcpHost := "" if p.BlockData != nil && p.BlockData.BlockId < 250900 { re := regexp.MustCompile(`^https?:\/\/([0-9a-z\_\.\-:]+)\/`) match := re.FindStringSubmatch(p.TxMaps.String["http_host"]) if len(match) != 0 { tcpHost = match[1] + ":8088" } } else { tcpHost = p.TxMaps.String["tcp_host"] } // получим массив майнеров, которые должны скопировать к себе 2 фото лица юзера maxMinerId, err := p.DCDB.Single("SELECT max(miner_id) FROM miners").Int64() if err != nil { return p.ErrInfo(err) } // т.к. у юзера это может быть не первая попытка стать майнером, то отменяем голосования по всем предыдущим err = p.DCDB.ExecSql("UPDATE votes_miners SET votes_end = 1, end_block_id = ? WHERE user_id = ? AND type = 'node_voting' AND end_block_id = 0 AND votes_end = 0", p.BlockData.BlockId, p.TxUserID) if err != nil { return p.ErrInfo(err) } // создаем новое голосование для нодов err = p.DCDB.ExecSql("INSERT INTO votes_miners (type, user_id, votes_start_time) VALUES ('node_voting', ?, ?)", p.TxUserID, p.BlockData.Time) if err != nil { return p.ErrInfo(err) } // переведем все координаты в отрезки. var faceCoords [][2]int err = json.Unmarshal(p.TxMap["face_coords"], &faceCoords) if err != nil { return p.ErrInfo(err) } faceCoords = append([][2]int{{0, 0}}, faceCoords...) // получим отрезки data, err := p.DCDB.OneRow("SELECT segments, version FROM spots_compatibility").String() if err != nil { return p.ErrInfo(err) } spotsVersion := data["version"] var segments map[string]map[string][]string err = json.Unmarshal([]byte(data["segments"]), &segments) if err != nil { return p.ErrInfo(err) } n := len(segments["face"]) + 1 faceRelations := make([]float64, n, n) faceRelations[0] = utils.PpLenght(faceCoords[1], faceCoords[2]) for num, spots := range segments["face"] { // 1. ширина головы // 2. глаз-нос // 3. нос-губа // 4. губа-подбородок // 5. ширина челюсти faceRelations[utils.StrToInt(num)] = utils.Round((utils.PpLenght(faceCoords[utils.StrToInt(spots[0])], faceCoords[utils.StrToInt(spots[1])]) / faceRelations[0]), 4) } faceRelations[0] = 1 // переведем все координаты в отрезки. var profileCoords [][2]int err = json.Unmarshal(p.TxMap["profile_coords"], &profileCoords) if err != nil { return p.ErrInfo(err) } profileCoords = append([][2]int{{0, 0}}, profileCoords...) n = len(segments["profile"]) + 1 profileRelations := make([]float64, n, n) profileRelations[0] = utils.PpLenght(profileCoords[1], profileCoords[2]) for num, spots := range segments["profile"] { // 1. край уха - край носа // 2. глаз - край носа // 3. подбородок - низ уха // 4. верх уха - низ уха profileRelations[utils.StrToInt(num)] = utils.Round((utils.PpLenght(profileCoords[utils.StrToInt(spots[0])], profileCoords[utils.StrToInt(spots[1])]) / profileRelations[0]), 4) } profileRelations[0] = 1 addSql := make(map[string]string) addSql["names"] = "" addSql["values"] = "" addSql["upd"] = "" for j := 1; j < len(faceRelations); j++ { addSql["names"] += fmt.Sprintf("f%v,\n", j) addSql["values"] += fmt.Sprintf("'%v',\n", faceRelations[j]) addSql["upd"] += fmt.Sprintf("f%v='%v',\n", j, faceRelations[j]) } for j := 1; j < len(profileRelations); j++ { addSql["names"] += fmt.Sprintf("p%v,\n", j) addSql["values"] += fmt.Sprintf("'%v',\n", profileRelations[j]) addSql["upd"] += fmt.Sprintf("p%v='%v',\n", j, profileRelations[j]) } addSql["names"] = addSql["names"][0 : len(addSql["names"])-2] addSql["values"] = addSql["values"][0 : len(addSql["values"])-2] addSql["upd"] = addSql["upd"][0 : len(addSql["upd"])-2] // Для откатов // проверим, есть ли в БД запись, которую нужно залогировать logData, err := p.DCDB.OneRow("SELECT * FROM faces WHERE user_id = ?", p.TxUserID).String() if err != nil { return p.ErrInfo(err) } if len(logData) > 0 { addSql1 := "" addSql2 := "" for i := 1; i <= 20; i++ { addSql1 += fmt.Sprintf("f%v, ", i) addSql2 += fmt.Sprintf("%v,", logData[fmt.Sprintf("f%v", i)]) } for i := 1; i <= 20; i++ { addSql1 += fmt.Sprintf("p%v, ", i) addSql2 += fmt.Sprintf("%v,", logData[fmt.Sprintf("p%v", i)]) } // для откатов logId, err := p.ExecSqlGetLastInsertId(` INSERT INTO log_faces ( user_id, version, status, race, country, `+addSql1+` prev_log_id, block_id ) VALUES ( `+logData["user_id"]+`, `+logData["version"]+`, '`+logData["status"]+`', `+logData["race"]+`, `+logData["country"]+`, `+addSql2+` `+logData["log_id"]+`, `+utils.Int64ToStr(p.BlockData.BlockId)+` )`, "log_id") if err != nil { return p.ErrInfo(err) } err = p.ExecSql("UPDATE faces SET "+addSql["upd"]+", version = ?, race = ?, country = ?, log_id = ? WHERE user_id = ?", spotsVersion, p.TxMaps.Int64["race"], p.TxMaps.Int64["country"], logId, p.TxUserID) if err != nil { return p.ErrInfo(err) } } else { // это первая запись в таблицу, и лог писать не с чего err = p.ExecSql(` INSERT INTO faces ( user_id, version, race, country, ` + addSql["names"] + ` ) VALUES ( ` + string(p.TxMap["user_id"]) + `, '` + spotsVersion + `', ` + string(p.TxMap["race"]) + `, ` + string(p.TxMap["country"]) + `, ` + addSql["values"] + ` )`) if err != nil { return p.ErrInfo(err) } } // проверим, есть ли в БД запись, которую надо залогировать logData, err = p.OneRow("SELECT * FROM miners_data WHERE user_id = ?", p.TxUserID).String() if err != nil { return p.ErrInfo(err) } if len(logData) > 0 { logData["node_public_key"] = string(utils.BinToHex([]byte(logData["node_public_key"]))) // для откатов logId, err := p.ExecSqlGetLastInsertId(` INSERT INTO log_miners_data ( user_id, miner_id, status, node_public_key, face_hash, profile_hash, photo_block_id, photo_max_miner_id, miners_keepers, face_coords, profile_coords, video_type, video_url_id, http_host, tcp_host, latitude, longitude, country, block_id, prev_log_id ) VALUES ( ?, ?, ?, [hex], ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) `, "log_id", logData["user_id"], logData["miner_id"], logData["status"], logData["node_public_key"], logData["face_hash"], logData["profile_hash"], logData["photo_block_id"], logData["photo_max_miner_id"], logData["miners_keepers"], logData["face_coords"], logData["profile_coords"], logData["video_type"], logData["video_url_id"], logData["http_host"], logData["tcp_host"], logData["latitude"], logData["longitude"], logData["country"], p.BlockData.BlockId, logData["log_id"]) if err != nil { return p.ErrInfo(err) } // обновляем таблу err = p.ExecSql(` UPDATE miners_data SET node_public_key = [hex], face_hash = ?, profile_hash = ?, photo_block_id = ?, photo_max_miner_id = ?, miners_keepers = ?, face_coords = ?, profile_coords = ?, video_type = ?, video_url_id = ?, latitude = ?, longitude = ?, country = ?, http_host = ?, tcp_host = ?, log_id = ? WHERE user_id = ?`, p.TxMap["node_public_key"], p.TxMaps.String["face_hash"], p.TxMaps.String["profile_hash"], p.BlockData.BlockId, maxMinerId, p.Variables.Int64["miners_keepers"], p.TxMaps.String["face_coords"], p.TxMaps.String["profile_coords"], p.TxMaps.String["video_type"], p.TxMaps.String["video_url_id"], p.TxMaps.Float64["latitude"], p.TxMaps.Float64["longitude"], p.TxMaps.Int64["country"], p.TxMaps.String["http_host"], tcpHost, logId, p.TxUserID) if err != nil { return p.ErrInfo(err) } } else { err = p.ExecSql(` INSERT INTO miners_data ( user_id, node_public_key, face_hash, profile_hash, photo_block_id, photo_max_miner_id, miners_keepers, face_coords, profile_coords, video_type, video_url_id, latitude, longitude, country, http_host, tcp_host ) VALUES (?, [hex], ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, p.TxUserID, p.TxMap["node_public_key"], p.TxMaps.String["face_hash"], p.TxMaps.String["profile_hash"], p.BlockData.BlockId, maxMinerId, p.Variables.Int64["miners_keepers"], p.TxMaps.String["face_coords"], p.TxMaps.String["profile_coords"], p.TxMaps.String["video_type"], p.TxMaps.String["video_url_id"], p.TxMaps.Float64["latitude"], p.TxMaps.Float64["longitude"], p.TxMaps.Int64["country"], p.TxMaps.String["http_host"], tcpHost) if err != nil { return p.ErrInfo(err) } } // проверим, не наш ли это user_id myUserId, myBlockId, myPrefix, _, err := p.GetMyUserId(p.TxUserID) if err != nil { return err } if p.TxUserID == myUserId && myBlockId <= p.BlockData.BlockId { err = p.DCDB.ExecSql("UPDATE "+myPrefix+"my_node_keys SET block_id = ? WHERE hex(public_key) = ?", p.BlockData.BlockId, p.TxMap["node_public_key"]) if err != nil { return p.ErrInfo(err) } } return nil }
/* * просто шлем всем, кто есть в 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 (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 } } }