github.com/igggame/nebulas-go@v2.1.0+incompatible/nbre/runtime/dip/dip_handler.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/dip/dip_handler.h" 22 #include "common/configuration.h" 23 #include "core/ir_warden.h" 24 #include "fs/ir_manager/api/ir_api.h" 25 #include "fs/proto/ir.pb.h" 26 #include "fs/storage_holder.h" 27 #include "jit/jit_driver.h" 28 #include "runtime/dip/dip_reward.h" 29 #include <boost/foreach.hpp> 30 #include <boost/property_tree/json_parser.hpp> 31 #include <boost/property_tree/ptree.hpp> 32 #include <ff/functionflow.h> 33 34 namespace neb { 35 namespace rt { 36 namespace dip { 37 38 dip_handler::dip_handler() : m_has_curr(false) { 39 m_storage = neb::fs::storage_holder::instance().nbre_db_ptr(); 40 } 41 42 void dip_handler::check_dip_params(block_height_t height) { 43 44 if (!m_has_curr && m_incoming.empty()) { 45 load_storage("dip_rewards", m_dip_reward); 46 load_storage("nr_results", m_nr_result, 1 << 4); 47 load_storage("nr_sums", m_nr_sum, 1 << 4); 48 auto dip_versions_ptr = neb::fs::ir_api::get_ir_versions("dip", m_storage); 49 if (dip_versions_ptr->empty()) { 50 return; 51 } 52 LOG(INFO) << "dip versions not empty, size " << dip_versions_ptr->size(); 53 54 std::reverse(dip_versions_ptr->begin(), dip_versions_ptr->end()); 55 for (auto version : *dip_versions_ptr) { 56 auto nbre_ir_ptr = 57 neb::core::ir_warden::instance().get_ir_by_name_version("dip", 58 version); 59 block_height_t available_height = nbre_ir_ptr->height(); 60 m_incoming.push(std::make_pair(version, available_height)); 61 } 62 } 63 64 std::pair<version_t, block_height_t> tmp; 65 66 while (!m_incoming.empty() && m_incoming.front().second <= height) { 67 tmp = m_incoming.front(); 68 m_incoming.pop(); 69 70 try { 71 LOG(INFO) << "init dip param list, version: " << tmp.first 72 << ", available height " << tmp.second; 73 auto dip_ret = run_dip_ir("dip", tmp.first, tmp.second, 0); 74 if (!std::get<0>(dip_ret)) { 75 auto info = std::make_shared<dip_params_t>(); 76 info->deserialize_from_string(std::get<1>(dip_ret)); 77 m_dip_params_list.push_back(info); 78 } 79 } catch (const std::exception &e) { 80 LOG(INFO) << "dip params init failed " << e.what(); 81 } 82 } 83 84 if (tmp.first && tmp.second) { 85 m_has_curr = true; 86 } 87 } 88 89 void dip_handler::deploy(version_t version, block_height_t available_height) { 90 m_incoming.push(std::make_pair(version, available_height)); 91 } 92 93 void dip_handler::start(neb::block_height_t height, 94 const dip_params_t *dip_params) { 95 check_dip_params(height); 96 if (!m_has_curr) { 97 LOG(INFO) << "dip params not init"; 98 return; 99 } 100 101 // get start block and block interval if default 102 auto last_ele_ptr = m_dip_params_list.back(); 103 auto &last_ele = *last_ele_ptr; 104 block_height_t dip_start_block = last_ele.get<start_block>(); 105 block_height_t dip_block_interval = last_ele.get<block_interval>(); 106 107 if (dip_params) { 108 LOG(INFO) << "dip meta not null"; 109 dip_start_block = dip_params->get<start_block>(); 110 dip_block_interval = dip_params->get<block_interval>(); 111 } 112 113 if (height < dip_start_block + dip_block_interval) { 114 LOG(INFO) << "wait to sync"; 115 return; 116 } 117 118 uint64_t interval_nums = (height - dip_start_block) / dip_block_interval; 119 uint64_t hash_height = dip_start_block + dip_block_interval * interval_nums; 120 121 if (height != hash_height) { 122 return; 123 } 124 LOG(INFO) << "to start calculate dip reward for hash_height " << hash_height; 125 126 if (m_dip_reward.exist(hash_height)) { 127 LOG(INFO) << "dip reward already exists"; 128 return; 129 } 130 131 if (!m_in_process.insert_if_not_exist(hash_height, true)) { 132 LOG(INFO) << "dip reward already in processing"; 133 return; 134 } 135 136 // get dip version if default 137 std::string dip_name = "dip"; 138 uint64_t dip_version = 0; 139 auto tmp_ptr = neb::core::ir_warden::instance().get_ir_by_name_height( 140 dip_name, hash_height, false); 141 assert(!tmp_ptr->empty()); 142 dip_version = tmp_ptr->back().version(); 143 if (dip_params) { 144 dip_version = dip_params->get<p_version>(); 145 } 146 147 // ff::para<> p; 148 // p([this, dip_name, dip_version, hash_height]() { 149 try { 150 auto dip_ret = run_dip_ir(dip_name, dip_version, hash_height, hash_height); 151 if (std::get<0>(dip_ret)) { 152 auto dip_str_ptr = dip_reward::dip_info_to_json(dip_ret); 153 dump_storage("dip_rewards", hash_height, 154 str_sptr_t{std::move(dip_str_ptr)}, m_dip_reward); 155 LOG(INFO) << "dump dip rewards done"; 156 157 auto &nr_ret = std::get<3>(dip_ret); 158 auto nr_str_ptr = nr::nebulas_rank::nr_info_to_json(nr_ret); 159 dump_storage("nr_results", hash_height, str_sptr_t{std::move(nr_str_ptr)}, 160 m_nr_result, 1 << 4); 161 LOG(INFO) << "dump nr results done"; 162 163 auto nr_sum_ptr = nr::nebulas_rank::get_nr_sum_str(nr_ret); 164 dump_storage("nr_sums", hash_height, str_sptr_t{std::move(nr_sum_ptr)}, 165 m_nr_sum, 1 << 4); 166 LOG(INFO) << "dump nr sums done"; 167 } else { 168 LOG(INFO) << std::get<1>(dip_ret); 169 } 170 } catch (const std::exception &e) { 171 LOG(INFO) << "jit driver execute dip failed " << e.what(); 172 } 173 m_in_process.erase(hash_height); 174 //}); 175 } 176 177 std::shared_ptr<dip_params_t> 178 dip_handler::get_dip_params_previous(neb::block_height_t height) { 179 180 auto tmp_ptr = std::make_shared<dip_params_t>(); 181 tmp_ptr->set<start_block>(height); 182 auto ret = m_dip_params_list.try_previous( 183 tmp_ptr, [](const std::shared_ptr<dip_params_t> &d1, 184 const std::shared_ptr<dip_params_t> &d2) { 185 return d1->get<start_block>() < d2->get<start_block>(); 186 }); 187 LOG(INFO) << "try previous status " << ret.first; 188 assert(ret.first); 189 return ret.second; 190 } 191 192 std::string 193 dip_handler::get_dip_reward_when_missing(neb::block_height_t hash_height, 194 const dip_params_t &dip_params) { 195 196 LOG(INFO) << "call func get_dip_reward_when_missing"; 197 198 auto dip_start_block = dip_params.get<start_block>(); 199 auto dip_block_interval = dip_params.get<block_interval>(); 200 201 if (hash_height < dip_start_block + dip_block_interval) { 202 auto it_ptr = get_dip_params_previous(dip_start_block); 203 dip_start_block = it_ptr->get<start_block>(); 204 dip_block_interval = it_ptr->get<block_interval>(); 205 206 auto interval_nums = (hash_height - dip_start_block) / dip_block_interval; 207 hash_height = dip_start_block + dip_block_interval * interval_nums; 208 start(hash_height, it_ptr.get()); 209 auto ret = std::string("{\"err\":\"dip reward missing, wait to restart\"}"); 210 LOG(INFO) << ret; 211 return ret; 212 } 213 214 start(hash_height, &dip_params); 215 auto ret = std::string("{\"err\":\"dip reward missing, wait to restart or " 216 "height out of range\"}"); 217 LOG(INFO) << ret; 218 return ret; 219 } 220 221 std::shared_ptr<dip_params_t> 222 dip_handler::get_dip_params(neb::block_height_t height) { 223 224 LOG(INFO) << "get dip params height " << height; 225 auto tmp_ptr = std::make_shared<dip_params_t>(); 226 tmp_ptr->set<start_block>(height); 227 auto ret = m_dip_params_list.try_lower_than( 228 tmp_ptr, [](const std::shared_ptr<dip_params_t> &d1, 229 const std::shared_ptr<dip_params_t> &d2) { 230 return d1->get<start_block>() < d2->get<start_block>(); 231 }); 232 LOG(INFO) << "try lower than status " << ret.first; 233 assert(ret.first); 234 return ret.second; 235 } 236 237 str_sptr_t dip_handler::get_dip_reward(neb::block_height_t height) { 238 LOG(INFO) << "call func get_dip_reward"; 239 240 if (!m_has_curr) { 241 auto ret = std::string("{\"err\":\"dip params not init yet\"}"); 242 LOG(INFO) << ret; 243 return std::make_shared<std::string>(ret); 244 } 245 246 if (!m_dip_params_list.empty()) { 247 auto first_ele_ptr = m_dip_params_list.front(); 248 auto &first_ele = *first_ele_ptr; 249 if (height < 250 first_ele.get<start_block>() + first_ele.get<block_interval>()) { 251 auto ret = boost::str( 252 boost::format("{\"err\":\"available height is %1%\"}") % 253 (first_ele.get<start_block>() + first_ele.get<block_interval>())); 254 LOG(INFO) << ret; 255 return std::make_shared<std::string>(ret); 256 } 257 } 258 259 LOG(INFO) << "dip history size " << m_dip_params_list.size(); 260 auto it_ptr = get_dip_params(height); 261 auto &it = *it_ptr; 262 block_height_t dip_start_block = it.get<start_block>(); 263 block_height_t dip_block_interval = it.get<block_interval>(); 264 LOG(INFO) << "history start block " << dip_start_block << " , block interval " 265 << dip_block_interval; 266 267 uint64_t interval_nums = (height - dip_start_block) / dip_block_interval; 268 uint64_t hash_height = dip_start_block + dip_block_interval * interval_nums; 269 LOG(INFO) << "mapping height " << height << " to hash_height " << hash_height; 270 271 if (!m_dip_reward.exist(hash_height)) { 272 LOG(INFO) << "dip reward not exists"; 273 auto ret = get_dip_reward_when_missing(hash_height, it); 274 return std::make_shared<std::string>(ret); 275 } 276 LOG(INFO) << "dip reward exists"; 277 auto ret = m_dip_reward.try_get_val(hash_height); 278 assert(ret.first); 279 LOG(INFO) << *ret.second; 280 return ret.second; 281 } 282 283 str_sptr_t dip_handler::get_nr_result(neb::block_height_t height) { 284 LOG(INFO) << "call func get_nr_result height " << height; 285 auto ret = m_nr_result.try_lower_than(height); 286 LOG(INFO) << "try lower than returned status " << ret.first; 287 if (!ret.first) { 288 auto ret = std::string("{\"err\":\"no such nr result\"}"); 289 LOG(INFO) << ret; 290 return std::make_shared<std::string>(ret); 291 } 292 assert(ret.first); 293 auto &tmp = ret.second; 294 LOG(INFO) << *tmp.second; 295 return tmp.second; 296 } 297 298 str_sptr_t dip_handler::get_nr_sum(neb::block_height_t height) { 299 LOG(INFO) << "call func get_nr_sum height " << height; 300 auto ret = m_nr_sum.try_lower_than(height); 301 if (!ret.first) { 302 auto ret = std::string("{\"err\":\"no such nr sum\"}"); 303 LOG(INFO) << ret; 304 return std::make_shared<std::string>(ret); 305 } 306 assert(ret.first); 307 auto &tmp = ret.second; 308 LOG(INFO) << *tmp.second; 309 return tmp.second; 310 } 311 312 void dip_handler::dump_storage( 313 const std::string &key, neb::block_height_t hash_height, 314 const str_sptr_t &val_ptr, 315 thread_safe_map<block_height_t, str_sptr_t> &mem_cache, 316 size_t storage_max_size) { 317 std::unique_lock<std::mutex> _l(m_mutex); 318 319 auto update_to_storage = [](const std::string &key, 320 const boost::property_tree::ptree &val_pt, 321 neb::fs::rocksdb_storage *rs) { 322 std::stringstream ss; 323 boost::property_tree::json_parser::write_json(ss, val_pt, false); 324 rs->put(key, neb::string_to_byte(ss.str())); 325 }; 326 327 LOG(INFO) << "call func dump_storage"; 328 neb::bytes val_bytes; 329 try { 330 val_bytes = m_storage->get(key); 331 } catch (const std::exception &e) { 332 LOG(INFO) << key << " empty " << e.what(); 333 334 boost::property_tree::ptree ele, arr, root; 335 ele.put("", *val_ptr); 336 arr.push_back(std::make_pair("", ele)); 337 root.add_child(key, arr); 338 update_to_storage(key, root, m_storage); 339 340 mem_cache.insert(hash_height, val_ptr); 341 LOG(INFO) << "insert " << key << "pair height " << hash_height << ", " 342 << *val_ptr; 343 return; 344 } 345 346 LOG(INFO) << key << " not empty"; 347 boost::property_tree::ptree root; 348 std::stringstream ss(neb::byte_to_string(val_bytes)); 349 boost::property_tree::json_parser::read_json(ss, root); 350 351 boost::property_tree::ptree &arr = root.get_child(key); 352 boost::property_tree::ptree ele; 353 ele.put("", *val_ptr); 354 arr.push_back(std::make_pair("", ele)); 355 LOG(INFO) << "insert " << key; 356 update_to_storage(key, root, m_storage); 357 358 mem_cache.insert(hash_height, val_ptr); 359 LOG(INFO) << "insert " << key << " pair height " << hash_height << ", " 360 << *val_ptr; 361 362 if (mem_cache.size() > storage_max_size) { 363 auto first_ele = mem_cache.begin(); 364 mem_cache.erase(first_ele.first); 365 } 366 } 367 368 void dip_handler::load_storage( 369 const std::string &key, 370 thread_safe_map<block_height_t, str_sptr_t> &mem_cache, 371 size_t storage_max_size) { 372 std::unique_lock<std::mutex> _l(m_mutex); 373 374 LOG(INFO) << "call func load_storage"; 375 neb::bytes val_bytes; 376 try { 377 val_bytes = m_storage->get(key); 378 } catch (const std::exception &e) { 379 LOG(INFO) << key << " empty " << e.what(); 380 return; 381 } 382 383 LOG(INFO) << key << " not empty"; 384 boost::property_tree::ptree root; 385 std::stringstream ss(neb::byte_to_string(val_bytes)); 386 boost::property_tree::json_parser::read_json(ss, root); 387 388 BOOST_FOREACH (boost::property_tree::ptree::value_type &v, 389 root.get_child(key)) { 390 boost::property_tree::ptree pt = v.second; 391 auto record_ptr = 392 std::make_shared<std::string>(pt.get<std::string>(std::string())); 393 394 boost::property_tree::ptree tmp_pt; 395 std::stringstream ss(*record_ptr); 396 boost::property_tree::json_parser::read_json(ss, tmp_pt); 397 block_height_t end_height = tmp_pt.get<block_height_t>("end_height"); 398 399 auto h = end_height + 1; 400 mem_cache.insert(h, record_ptr); 401 LOG(INFO) << "insert " << key << " pair height " << h << ", " 402 << *record_ptr; 403 404 if (mem_cache.size() > storage_max_size) { 405 auto first_ele = mem_cache.begin(); 406 mem_cache.erase(first_ele.first); 407 } 408 } 409 } 410 411 dip_ret_type dip_handler::run_dip_ir(const std::string &name, version_t version, 412 block_height_t ir_height, 413 block_height_t var_height) { 414 std::stringstream ss; 415 ss << name << version; 416 std::string name_version = ss.str(); 417 LOG(INFO) << "dip name version " << name_version; 418 419 auto irs_ptr = 420 neb::core::ir_warden::instance().get_ir_by_name_height(name, ir_height); 421 LOG(INFO) << "dip ir and depends size " << irs_ptr->size(); 422 423 jit_driver &jd = jit_driver::instance(); 424 LOG(INFO) << "jit driver run with " << name_version << ',' << irs_ptr->size() 425 << ',' << neb::configuration::instance().dip_func_name() << ',' 426 << var_height; 427 auto dip_ret = jd.run<dip_ret_type>( 428 name_version, *irs_ptr, neb::configuration::instance().dip_func_name(), 429 var_height); 430 LOG(INFO) << "dip reward returned"; 431 return dip_ret; 432 } 433 434 } // namespace dip 435 } // namespace rt 436 } // namespace neb