github.com/igggame/nebulas-go@v2.1.0+incompatible/nbre/runtime/nr/impl/nebulas_rank.cpp (about) 1 // Copyright (C) 2018 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or 6 // modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // (at your option) any later version. 10 // 11 // the go-nebulas library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with the go-nebulas library. If not, see 18 // <http://www.gnu.org/licenses/>. 19 // 20 21 #include "runtime/nr/impl/nebulas_rank.h" 22 #include "common/int128_conversion.h" 23 #include "common/log.h" 24 #include "common/version.h" 25 #include <boost/algorithm/string/replace.hpp> 26 #include <boost/foreach.hpp> 27 #include <boost/property_tree/json_parser.hpp> 28 #include <chrono> 29 #include <thread> 30 31 namespace neb { 32 namespace rt { 33 34 namespace nr { 35 36 std::unique_ptr<std::vector<std::vector<neb::fs::transaction_info_t>>> 37 nebulas_rank::split_transactions_by_block_interval( 38 const std::vector<neb::fs::transaction_info_t> &txs, 39 int32_t block_interval) { 40 41 auto ret = 42 std::make_unique<std::vector<std::vector<neb::fs::transaction_info_t>>>(); 43 44 if (block_interval < 1 || txs.empty()) { 45 return ret; 46 } 47 48 auto it = txs.begin(); 49 block_height_t block_first = it->m_height; 50 it = txs.end(); 51 it--; 52 block_height_t block_last = it->m_height; 53 54 std::vector<neb::fs::transaction_info_t> v; 55 it = txs.begin(); 56 block_height_t b = block_first; 57 while (b <= block_last) { 58 block_height_t h = it->m_height; 59 if (h < b + block_interval) { 60 v.push_back(*it++); 61 } else { 62 ret->push_back(v); 63 v.clear(); 64 b += block_interval; 65 } 66 if (it == txs.end()) { 67 ret->push_back(v); 68 break; 69 } 70 } 71 return ret; 72 } 73 74 void nebulas_rank::filter_empty_transactions_this_interval( 75 std::vector<std::vector<neb::fs::transaction_info_t>> &txs) { 76 for (auto it = txs.begin(); it != txs.end();) { 77 if (it->empty()) { 78 it = txs.erase(it); 79 } else { 80 it++; 81 } 82 } 83 } 84 85 transaction_graph_ptr_t nebulas_rank::build_graph_from_transactions( 86 const std::vector<neb::fs::transaction_info_t> &trans) { 87 auto ret = std::make_unique<neb::rt::transaction_graph>(); 88 89 for (auto ite = trans.begin(); ite != trans.end(); ite++) { 90 address_t from = ite->m_from; 91 address_t to = ite->m_to; 92 wei_t value = ite->m_tx_value; 93 int64_t timestamp = ite->m_timestamp; 94 ret->add_edge(from, to, value, timestamp); 95 } 96 return ret; 97 } 98 99 std::unique_ptr<std::vector<transaction_graph_ptr_t>> 100 nebulas_rank::build_transaction_graphs( 101 const std::vector<std::vector<neb::fs::transaction_info_t>> &txs) { 102 103 std::unique_ptr<std::vector<transaction_graph_ptr_t>> tgs = 104 std::make_unique<std::vector<transaction_graph_ptr_t>>(); 105 106 for (auto it = txs.begin(); it != txs.end(); it++) { 107 auto p = build_graph_from_transactions(*it); 108 tgs->push_back(std::move(p)); 109 } 110 return tgs; 111 } 112 113 block_height_t nebulas_rank::get_max_height_this_block_interval( 114 const std::vector<neb::fs::transaction_info_t> &txs) { 115 if (txs.empty()) { 116 return 0; 117 } 118 // suppose transactions in height increasing order 119 return txs.back().m_height; 120 } 121 122 std::unique_ptr<std::unordered_set<address_t>> 123 nebulas_rank::get_normal_accounts( 124 const std::vector<neb::fs::transaction_info_t> &txs) { 125 126 auto ret = std::make_unique<std::unordered_set<address_t>>(); 127 128 for (auto it = txs.begin(); it != txs.end(); it++) { 129 auto from = it->m_from; 130 ret->insert(from); 131 132 auto to = it->m_to; 133 ret->insert(to); 134 } 135 return ret; 136 } 137 138 std::unique_ptr<std::unordered_map<address_t, floatxx_t>> 139 nebulas_rank::get_account_balance_median( 140 const std::unordered_set<address_t> &accounts, 141 const std::vector<std::vector<neb::fs::transaction_info_t>> &txs, 142 const account_db_ptr_t &db_ptr) { 143 144 auto ret = std::make_unique<std::unordered_map<address_t, floatxx_t>>(); 145 std::unordered_map<address_t, std::vector<wei_t>> addr_balance_v; 146 147 for (auto it = txs.begin(); it != txs.end(); it++) { 148 block_height_t max_height = get_max_height_this_block_interval(*it); 149 for (auto ite = accounts.begin(); ite != accounts.end(); ite++) { 150 address_t addr = *ite; 151 wei_t balance = db_ptr->get_account_balance_internal(addr, max_height); 152 addr_balance_v[addr].push_back(balance); 153 } 154 } 155 156 floatxx_t zero = softfloat_cast<uint32_t, typename floatxx_t::value_type>(0); 157 for (auto it = addr_balance_v.begin(); it != addr_balance_v.end(); it++) { 158 std::vector<wei_t> &v = it->second; 159 sort(v.begin(), v.end(), 160 [](const wei_t &w1, const wei_t &w2) { return w1 < w2; }); 161 size_t v_len = v.size(); 162 floatxx_t median = to_float<floatxx_t>(v[v_len >> 1]); 163 if ((v_len & 0x1) == 0) { 164 auto tmp = to_float<floatxx_t>(v[(v_len >> 1) - 1]); 165 median = (median + tmp) / 2; 166 } 167 168 floatxx_t normalized_median = db_ptr->get_normalized_value(median); 169 ret->insert(std::make_pair(it->first, math::max(zero, normalized_median))); 170 } 171 172 return ret; 173 } 174 175 floatxx_t nebulas_rank::f_account_weight(floatxx_t in_val, floatxx_t out_val) { 176 floatxx_t pi = math::constants<floatxx_t>::pi(); 177 floatxx_t atan_val = pi / 2.0; 178 if (in_val > 0) { 179 atan_val = math::arctan(out_val / in_val); 180 } 181 auto tmp = math::sin(pi / 4.0 - atan_val); 182 return (in_val + out_val) * math::exp((-2.0) * tmp * tmp); 183 } 184 185 std::unique_ptr<std::unordered_map<address_t, floatxx_t>> 186 nebulas_rank::get_account_weight( 187 const std::unordered_map<address_t, neb::rt::in_out_val_t> &in_out_vals, 188 const account_db_ptr_t &db_ptr) { 189 190 auto ret = std::make_unique<std::unordered_map<address_t, floatxx_t>>(); 191 192 for (auto it = in_out_vals.begin(); it != in_out_vals.end(); it++) { 193 wei_t in_val = it->second.m_in_val; 194 wei_t out_val = it->second.m_out_val; 195 196 floatxx_t f_in_val = to_float<floatxx_t>(in_val); 197 floatxx_t f_out_val = to_float<floatxx_t>(out_val); 198 199 floatxx_t normalized_in_val = db_ptr->get_normalized_value(f_in_val); 200 floatxx_t normalized_out_val = db_ptr->get_normalized_value(f_out_val); 201 202 auto tmp = f_account_weight(normalized_in_val, normalized_out_val); 203 ret->insert(std::make_pair(it->first, tmp)); 204 } 205 return ret; 206 } 207 208 floatxx_t nebulas_rank::f_account_rank(int64_t a, int64_t b, int64_t c, 209 int64_t d, floatxx_t theta, floatxx_t mu, 210 floatxx_t lambda, floatxx_t S, 211 floatxx_t R) { 212 floatxx_t zero = softfloat_cast<uint32_t, typename floatxx_t::value_type>(0); 213 floatxx_t one = softfloat_cast<uint32_t, typename floatxx_t::value_type>(1); 214 auto gamma = math::pow(theta * R / (R + mu), lambda); 215 auto ret = zero; 216 if (S > 0) { 217 ret = (S / (one + math::pow(a / S, one / b))) * gamma; 218 } 219 return ret; 220 } 221 222 std::unique_ptr<std::unordered_map<address_t, floatxx_t>> 223 nebulas_rank::get_account_rank( 224 const std::unordered_map<address_t, floatxx_t> &account_median, 225 const std::unordered_map<address_t, floatxx_t> &account_weight, 226 const rank_params_t &rp) { 227 228 auto ret = std::make_unique<std::unordered_map<address_t, floatxx_t>>(); 229 230 for (auto it_m = account_median.begin(); it_m != account_median.end(); 231 it_m++) { 232 auto it_w = account_weight.find(it_m->first); 233 if (it_w != account_weight.end()) { 234 floatxx_t rank_val = 235 f_account_rank(rp.m_a, rp.m_b, rp.m_c, rp.m_d, rp.m_theta, rp.m_mu, 236 rp.m_lambda, it_m->second, it_w->second); 237 ret->insert(std::make_pair(it_m->first, rank_val)); 238 } 239 } 240 241 return ret; 242 } 243 244 std::vector<std::shared_ptr<nr_info_t>> nebulas_rank::get_nr_score( 245 const transaction_db_ptr_t &tdb_ptr, const account_db_ptr_t &adb_ptr, 246 const rank_params_t &rp, neb::block_height_t start_block, 247 neb::block_height_t end_block) { 248 249 auto start_time = std::chrono::high_resolution_clock::now(); 250 // transactions in total and account inter transactions 251 auto txs_ptr = 252 tdb_ptr->read_transactions_from_db_with_duration(start_block, end_block); 253 LOG(INFO) << "raw tx size: " << txs_ptr->size(); 254 auto inter_txs_ptr = fs::transaction_db::read_transactions_with_address_type( 255 *txs_ptr, NAS_ADDRESS_ACCOUNT_MAGIC_NUM, NAS_ADDRESS_ACCOUNT_MAGIC_NUM); 256 LOG(INFO) << "account to account: " << inter_txs_ptr->size(); 257 258 // graph operation 259 auto txs_v_ptr = split_transactions_by_block_interval(*inter_txs_ptr); 260 LOG(INFO) << "split by block interval: " << txs_v_ptr->size(); 261 262 filter_empty_transactions_this_interval(*txs_v_ptr); 263 auto tgs_ptr = build_transaction_graphs(*txs_v_ptr); 264 if (tgs_ptr->empty()) { 265 return std::vector<std::shared_ptr<nr_info_t>>(); 266 } 267 LOG(INFO) << "we have " << tgs_ptr->size() << " subgraphs."; 268 for (auto it = tgs_ptr->begin(); it != tgs_ptr->end(); it++) { 269 transaction_graph *ptr = it->get(); 270 graph_algo::non_recursive_remove_cycles_based_on_time_sequence( 271 ptr->internal_graph()); 272 graph_algo::merge_edges_with_same_from_and_same_to(ptr->internal_graph()); 273 } 274 LOG(INFO) << "done with remove cycle."; 275 276 transaction_graph *tg = neb::rt::graph_algo::merge_graphs(*tgs_ptr); 277 graph_algo::merge_topk_edges_with_same_from_and_same_to(tg->internal_graph()); 278 LOG(INFO) << "done with merge graphs."; 279 280 // in_out amount 281 auto in_out_vals_p = graph_algo::get_in_out_vals(tg->internal_graph()); 282 auto in_out_vals = *in_out_vals_p; 283 LOG(INFO) << "done with get in_out_vals"; 284 285 // median, weight, rank 286 auto accounts_ptr = get_normal_accounts(*inter_txs_ptr); 287 LOG(INFO) << "account size: " << accounts_ptr->size(); 288 289 std::unordered_map<neb::address_t, neb::wei_t> addr_balance; 290 for (auto &acc : *accounts_ptr) { 291 auto balance = adb_ptr->get_balance(acc, start_block); 292 addr_balance.insert(std::make_pair(acc, balance)); 293 } 294 LOG(INFO) << "done with get balance"; 295 adb_ptr->set_height_address_val_internal(*txs_ptr, addr_balance); 296 LOG(INFO) << "done with set height address"; 297 298 auto account_median_ptr = 299 get_account_balance_median(*accounts_ptr, *txs_v_ptr, adb_ptr); 300 LOG(INFO) << "done with get account balance median"; 301 auto account_weight_ptr = get_account_weight(in_out_vals, adb_ptr); 302 LOG(INFO) << "done with get account weight"; 303 304 auto account_median = *account_median_ptr; 305 auto account_weight = *account_weight_ptr; 306 auto account_rank_ptr = get_account_rank(account_median, account_weight, rp); 307 auto account_rank = *account_rank_ptr; 308 LOG(INFO) << "account rank size: " << account_rank.size(); 309 310 std::vector<std::shared_ptr<nr_info_t>> infos; 311 for (auto it = accounts_ptr->begin(); it != accounts_ptr->end(); it++) { 312 address_t addr = *it; 313 if (in_out_vals.find(addr) != in_out_vals.end() && 314 account_median.find(addr) != account_median.end() && 315 account_weight.find(addr) != account_weight.end() && 316 account_rank.find(addr) != account_rank.end()) { 317 auto in_outs = in_out_vals[addr].m_in_val + in_out_vals[addr].m_out_val; 318 auto f_in_outs = to_float<floatxx_t>(in_outs); 319 320 auto info = std::shared_ptr<nr_info_t>( 321 new nr_info_t({addr, f_in_outs, account_median[addr], 322 account_weight[addr], account_rank[addr]})); 323 infos.push_back(info); 324 } 325 } 326 327 auto end_time = std::chrono::high_resolution_clock::now(); 328 LOG(INFO) << "time spend: " 329 << std::chrono::duration_cast<std::chrono::seconds>(end_time - 330 start_time) 331 .count() 332 << " seconds"; 333 return infos; 334 } 335 336 void nebulas_rank::convert_nr_info_to_ptree(const nr_info_t &info, 337 boost::property_tree::ptree &p) { 338 339 neb::bytes addr_bytes = info.m_address; 340 floatxx_t f_in_outs = info.m_in_outs; 341 floatxx_t f_median = info.m_median; 342 floatxx_t f_weight = info.m_weight; 343 floatxx_t f_nr_score = info.m_nr_score; 344 345 p.put(std::string("address"), addr_bytes.to_base58()); 346 p.put(std::string("in_outs"), neb::math::to_string(f_in_outs)); 347 p.put(std::string("median"), neb::math::to_string(f_median)); 348 p.put(std::string("weight"), neb::math::to_string(f_weight)); 349 p.put(std::string("score"), neb::math::to_string(f_nr_score)); 350 } 351 352 void nebulas_rank::full_fill_meta_info( 353 const std::vector<std::pair<std::string, std::string>> &meta, 354 boost::property_tree::ptree &root) { 355 356 assert(meta.size() == 3); 357 358 for (auto &ele : meta) { 359 root.put(ele.first, ele.second); 360 } 361 } 362 363 str_uptr_t nebulas_rank::nr_info_to_json(const nr_ret_type &nr_ret) { 364 365 boost::property_tree::ptree root; 366 boost::property_tree::ptree arr; 367 368 const auto &meta_info_json = std::get<1>(nr_ret); 369 const auto &meta_info = neb::rt::json_to_meta_info(meta_info_json); 370 if (!meta_info.empty()) { 371 full_fill_meta_info(meta_info, root); 372 } 373 374 auto &nr_infos = std::get<2>(nr_ret); 375 if (nr_infos.empty()) { 376 boost::property_tree::ptree p; 377 arr.push_back(std::make_pair(std::string(), p)); 378 } 379 380 for (auto &it : nr_infos) { 381 const neb::rt::nr::nr_info_t &info = *it; 382 boost::property_tree::ptree p; 383 convert_nr_info_to_ptree(info, p); 384 arr.push_back(std::make_pair(std::string(), p)); 385 } 386 root.add_child("nrs", arr); 387 388 std::stringstream ss; 389 boost::property_tree::json_parser::write_json(ss, root, false); 390 auto tmp_ptr = std::make_unique<std::string>(ss.str()); 391 boost::replace_all(*tmp_ptr, "[\"\"]", "[]"); 392 return tmp_ptr; 393 } 394 395 nr_ret_type nebulas_rank::json_to_nr_info(const std::string &nr_result) { 396 397 nr_ret_type nr_ret; 398 std::get<0>(nr_ret) = 0; 399 400 boost::property_tree::ptree pt; 401 std::stringstream ss(nr_result); 402 boost::property_tree::json_parser::read_json(ss, pt); 403 404 auto start_block = pt.get<std::string>("start_height"); 405 auto end_block = pt.get<std::string>("end_height"); 406 auto version = pt.get<std::string>("version"); 407 408 std::vector<std::pair<std::string, std::string>> meta_info; 409 meta_info.push_back(std::make_pair("start_height", start_block)); 410 meta_info.push_back(std::make_pair("end_height", end_block)); 411 meta_info.push_back(std::make_pair("version", version)); 412 std::get<1>(nr_ret) = meta_info_to_json(meta_info); 413 414 boost::property_tree::ptree nrs = pt.get_child("nrs"); 415 auto &infos = std::get<2>(nr_ret); 416 417 BOOST_FOREACH (boost::property_tree::ptree::value_type &v, nrs) { 418 boost::property_tree::ptree nr = v.second; 419 auto info_ptr = std::make_shared<nr_info_t>(); 420 nr_info_t &info = *info_ptr; 421 const auto &address = nr.get<base58_address_t>("address"); 422 info.m_address = neb::bytes::from_base58(address); 423 424 const auto &in_outs = nr.get<std::string>("in_outs"); 425 const auto &median = nr.get<std::string>("median"); 426 const auto &weight = nr.get<std::string>("weight"); 427 const auto &score = nr.get<std::string>("score"); 428 info.m_in_outs = neb::math::from_string<floatxx_t>(in_outs); 429 info.m_median = neb::math::from_string<floatxx_t>(median); 430 info.m_weight = neb::math::from_string<floatxx_t>(weight); 431 info.m_nr_score = neb::math::from_string<floatxx_t>(score); 432 infos.push_back(info_ptr); 433 } 434 std::get<0>(nr_ret) = 1; 435 436 return nr_ret; 437 } 438 439 str_uptr_t nebulas_rank::get_nr_sum_str(const nr_ret_type &nr_ret) { 440 441 boost::property_tree::ptree root; 442 443 const auto &meta_info_json = std::get<1>(nr_ret); 444 const auto &meta_info = neb::rt::json_to_meta_info(meta_info_json); 445 if (!meta_info.empty()) { 446 full_fill_meta_info(meta_info, root); 447 } 448 449 auto &nr_infos = std::get<2>(nr_ret); 450 floatxx_t zero = softfloat_cast<uint32_t, typename floatxx_t::value_type>(0); 451 floatxx_t sum_in_outs = zero; 452 floatxx_t sum_score = zero; 453 454 for (auto &info : nr_infos) { 455 sum_in_outs += info->m_in_outs; 456 sum_score += info->m_nr_score; 457 } 458 459 boost::property_tree::ptree pt; 460 auto str_in_outs = neb::math::to_string(from_float(sum_in_outs)); 461 auto str_score = neb::math::to_string(from_float(sum_score)); 462 463 pt.put(std::string("in_outs"), str_in_outs); 464 pt.put(std::string("score"), str_score); 465 root.add_child("sum", pt); 466 467 std::stringstream ss; 468 boost::property_tree::json_parser::write_json(ss, root, false); 469 auto tmp_ptr = std::make_unique<std::string>(ss.str()); 470 return tmp_ptr; 471 } 472 473 } // namespace nr 474 } // namespace rt 475 } // namespace neb