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