mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	- updated smartcontracts - updated fullnode database layout - fixed memory leak in blockchain-explorer - updated tonlib
		
			
				
	
	
		
			332 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "archive-db.hpp"
 | 
						|
#include "common/errorcode.h"
 | 
						|
 | 
						|
#include "common/int-to-string.hpp"
 | 
						|
#include "files-async.hpp"
 | 
						|
 | 
						|
#include "td/db/RocksDb.h"
 | 
						|
#include "validator/fabric.h"
 | 
						|
 | 
						|
namespace ton {
 | 
						|
 | 
						|
namespace validator {
 | 
						|
 | 
						|
void PackageWriter::append(std::string filename, td::BufferSlice data,
 | 
						|
                           td::Promise<std::pair<td::uint64, td::uint64>> promise) {
 | 
						|
  auto offset = package_->append(std::move(filename), std::move(data));
 | 
						|
  auto size = package_->size();
 | 
						|
 | 
						|
  promise.set_value(std::pair<td::uint64, td::uint64>{offset, size});
 | 
						|
}
 | 
						|
 | 
						|
class PackageReader : public td::actor::Actor {
 | 
						|
 public:
 | 
						|
  PackageReader(std::shared_ptr<Package> package, td::uint64 offset,
 | 
						|
                td::Promise<std::pair<std::string, td::BufferSlice>> promise)
 | 
						|
      : package_(std::move(package)), offset_(offset), promise_(std::move(promise)) {
 | 
						|
  }
 | 
						|
  void start_up() {
 | 
						|
    promise_.set_result(package_->read(offset_));
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  std::shared_ptr<Package> package_;
 | 
						|
  td::uint64 offset_;
 | 
						|
  td::Promise<std::pair<std::string, td::BufferSlice>> promise_;
 | 
						|
};
 | 
						|
 | 
						|
void ArchiveFile::start_up() {
 | 
						|
  auto R = Package::open(path_, false, true);
 | 
						|
  if (R.is_error()) {
 | 
						|
    LOG(FATAL) << "failed to open/create archive '" << path_ << "': " << R.move_as_error();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  package_ = std::make_shared<Package>(R.move_as_ok());
 | 
						|
  index_ = std::make_shared<td::RocksDb>(td::RocksDb::open(path_ + ".index").move_as_ok());
 | 
						|
 | 
						|
  std::string value;
 | 
						|
  auto R2 = index_->get("status", value);
 | 
						|
  R2.ensure();
 | 
						|
 | 
						|
  if (R2.move_as_ok() == td::KeyValue::GetStatus::Ok) {
 | 
						|
    auto len = td::to_integer<td::uint64>(value);
 | 
						|
    package_->truncate(len);
 | 
						|
  } else {
 | 
						|
    package_->truncate(0);
 | 
						|
  }
 | 
						|
 | 
						|
  writer_ = td::actor::create_actor<PackageWriter>("writer", package_);
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveFile::write(FileDb::RefId ref_id, td::BufferSlice data, td::Promise<td::Unit> promise) {
 | 
						|
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, promise = std::move(promise)](
 | 
						|
                                          td::Result<std::pair<td::uint64, td::uint64>> R) mutable {
 | 
						|
    if (R.is_error()) {
 | 
						|
      promise.set_error(R.move_as_error());
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    auto v = R.move_as_ok();
 | 
						|
    td::actor::send_closure(SelfId, &ArchiveFile::completed_write, std::move(ref_id), v.first, v.second,
 | 
						|
                            std::move(promise));
 | 
						|
  });
 | 
						|
 | 
						|
  FileHash hash;
 | 
						|
  ref_id.visit([&](const auto &obj) { hash = obj.hash(); });
 | 
						|
 | 
						|
  td::actor::send_closure(writer_, &PackageWriter::append, hash.to_hex(), std::move(data), std::move(P));
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveFile::write_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
 | 
						|
  FileHash hash = fileref::BlockInfo{handle->id()}.hash();
 | 
						|
  index_->begin_transaction().ensure();
 | 
						|
  do {
 | 
						|
    auto version = handle->version();
 | 
						|
    index_->set(hash.to_hex(), handle->serialize().as_slice().str()).ensure();
 | 
						|
    handle->flushed_upto(version);
 | 
						|
  } while (handle->need_flush());
 | 
						|
  index_->commit_transaction().ensure();
 | 
						|
  promise.set_value(td::Unit());
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveFile::completed_write(FileDb::RefId ref_id, td::uint64 offset, td::uint64 new_size,
 | 
						|
                                  td::Promise<td::Unit> promise) {
 | 
						|
  FileHash hash;
 | 
						|
  ref_id.visit([&](const auto &obj) { hash = obj.hash(); });
 | 
						|
  index_->begin_transaction().ensure();
 | 
						|
  index_->set("status", td::to_string(new_size)).ensure();
 | 
						|
  index_->set(hash.to_hex(), td::to_string(offset)).ensure();
 | 
						|
  index_->commit_transaction().ensure();
 | 
						|
  promise.set_value(td::Unit());
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveFile::read(FileDb::RefId ref_id, td::Promise<td::BufferSlice> promise) {
 | 
						|
  FileHash hash;
 | 
						|
  ref_id.visit([&](const auto &obj) { hash = obj.hash(); });
 | 
						|
  std::string value;
 | 
						|
  auto R = index_->get(hash.to_hex(), value);
 | 
						|
  R.ensure();
 | 
						|
  if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
 | 
						|
    promise.set_error(td::Status::Error(ErrorCode::notready, "not in db (archive)"));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  auto offset = td::to_integer<td::uint64>(value);
 | 
						|
  auto P = td::PromiseCreator::lambda(
 | 
						|
      [promise = std::move(promise)](td::Result<std::pair<std::string, td::BufferSlice>> R) mutable {
 | 
						|
        if (R.is_error()) {
 | 
						|
          promise.set_error(R.move_as_error());
 | 
						|
        } else {
 | 
						|
          promise.set_value(std::move(R.move_as_ok().second));
 | 
						|
        }
 | 
						|
      });
 | 
						|
  td::actor::create_actor<PackageReader>("reader", package_, offset, std::move(P)).release();
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveFile::read_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise) {
 | 
						|
  FileHash hash = fileref::BlockInfo{block_id}.hash();
 | 
						|
 | 
						|
  std::string value;
 | 
						|
  auto R = index_->get(hash.to_hex(), value);
 | 
						|
  R.ensure();
 | 
						|
 | 
						|
  if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
 | 
						|
    promise.set_error(td::Status::Error(ErrorCode::notready, "not in archive db"));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  promise.set_result(create_block_handle(td::BufferSlice{value}));
 | 
						|
}
 | 
						|
 | 
						|
ArchiveManager::ArchiveManager(std::string db_root) : db_root_(db_root) {
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveManager::write(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::BufferSlice data,
 | 
						|
                           td::Promise<td::Unit> promise) {
 | 
						|
  auto f = get_file(ts, key_block);
 | 
						|
  td::actor::send_closure(f->file_actor_id(), &ArchiveFile::write, std::move(ref_id), std::move(data),
 | 
						|
                          std::move(promise));
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveManager::write_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
 | 
						|
  auto f = get_file(handle->unix_time(), handle->is_key_block());
 | 
						|
  update_desc(*f, handle->id().shard_full(), handle->id().seqno(), handle->logical_time());
 | 
						|
  td::actor::send_closure(f->file_actor_id(), &ArchiveFile::write_handle, std::move(handle), std::move(promise));
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveManager::read(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::Promise<td::BufferSlice> promise) {
 | 
						|
  auto f = get_file(ts, key_block);
 | 
						|
  td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read, std::move(ref_id), std::move(promise));
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveManager::read_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise) {
 | 
						|
  if (block_id.is_masterchain()) {
 | 
						|
    auto f = get_file_by_seqno(block_id.shard_full(), block_id.seqno(), true);
 | 
						|
    if (!f) {
 | 
						|
      read_handle_cont(block_id, std::move(promise));
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    auto P = td::PromiseCreator::lambda(
 | 
						|
        [SelfId = actor_id(this), block_id, promise = std::move(promise)](td::Result<BlockHandle> R) mutable {
 | 
						|
          if (R.is_error()) {
 | 
						|
            td::actor::send_closure(SelfId, &ArchiveManager::read_handle_cont, block_id, std::move(promise));
 | 
						|
          } else {
 | 
						|
            promise.set_value(R.move_as_ok());
 | 
						|
          }
 | 
						|
        });
 | 
						|
    td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read_handle, block_id, std::move(P));
 | 
						|
  } else {
 | 
						|
    read_handle_cont(block_id, std::move(promise));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveManager::read_handle_cont(BlockIdExt block_id, td::Promise<BlockHandle> promise) {
 | 
						|
  auto f = get_file_by_seqno(block_id.shard_full(), block_id.seqno(), false);
 | 
						|
  if (!f) {
 | 
						|
    promise.set_error(td::Status::Error(ErrorCode::notready, "not in archive db"));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read_handle, block_id, std::move(promise));
 | 
						|
}
 | 
						|
 | 
						|
UnixTime ArchiveManager::convert_ts(UnixTime ts, bool key_block) {
 | 
						|
  if (!key_block) {
 | 
						|
    return ts - (ts % (1 << 17));
 | 
						|
  } else {
 | 
						|
    return ts - (ts % (1 << 22));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
ArchiveManager::FileDescription *ArchiveManager::get_file(UnixTime ts, bool key_block, bool force) {
 | 
						|
  ts = convert_ts(ts, key_block);
 | 
						|
  auto &f = key_block ? key_files_ : files_;
 | 
						|
  auto it = f.find(ts);
 | 
						|
  if (it != f.end()) {
 | 
						|
    return &it->second;
 | 
						|
  }
 | 
						|
  if (!force) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  return add_file(ts, key_block);
 | 
						|
}
 | 
						|
 | 
						|
ArchiveManager::FileDescription *ArchiveManager::add_file(UnixTime ts, bool key_block) {
 | 
						|
  CHECK((key_block ? key_files_ : files_).count(ts) == 0);
 | 
						|
  index_->begin_transaction().ensure();
 | 
						|
  {
 | 
						|
    std::vector<td::int32> t;
 | 
						|
    std::vector<td::int32> tk;
 | 
						|
    for (auto &e : files_) {
 | 
						|
      t.push_back(e.first);
 | 
						|
    }
 | 
						|
    for (auto &e : key_files_) {
 | 
						|
      tk.push_back(e.first);
 | 
						|
    }
 | 
						|
    (key_block ? tk : t).push_back(ts);
 | 
						|
    index_
 | 
						|
        ->set(create_serialize_tl_object<ton_api::db_archive_index_key>().as_slice(),
 | 
						|
              create_serialize_tl_object<ton_api::db_archive_index_value>(std::move(t), std::move(tk)).as_slice())
 | 
						|
        .ensure();
 | 
						|
  }
 | 
						|
  {
 | 
						|
    index_
 | 
						|
        ->set(create_serialize_tl_object<ton_api::db_archive_package_key>(ts, key_block).as_slice(),
 | 
						|
              create_serialize_tl_object<ton_api::db_archive_package_value>(
 | 
						|
                  ts, key_block, std::vector<tl_object_ptr<ton_api::db_archive_package_firstBlock>>(), false)
 | 
						|
                  .as_slice())
 | 
						|
        .ensure();
 | 
						|
  }
 | 
						|
  index_->commit_transaction().ensure();
 | 
						|
  FileDescription desc{ts, key_block};
 | 
						|
  auto w = td::actor::create_actor<ArchiveFile>(
 | 
						|
      PSTRING() << "archivefile" << ts,
 | 
						|
      PSTRING() << db_root_ << "/packed/" << (key_block ? "key" : "") << ts << ".pack", ts);
 | 
						|
  desc.file = std::move(w);
 | 
						|
 | 
						|
  return &(key_block ? key_files_ : files_).emplace(ts, std::move(desc)).first->second;
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveManager::load_package(UnixTime ts, bool key_block) {
 | 
						|
  auto key = create_serialize_tl_object<ton_api::db_archive_package_key>(ts, key_block);
 | 
						|
 | 
						|
  std::string value;
 | 
						|
  auto v = index_->get(key.as_slice(), value);
 | 
						|
  v.ensure();
 | 
						|
  CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok);
 | 
						|
 | 
						|
  auto R = fetch_tl_object<ton_api::db_archive_package_value>(value, true);
 | 
						|
  R.ensure();
 | 
						|
  auto x = R.move_as_ok();
 | 
						|
 | 
						|
  if (x->deleted_) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  FileDescription desc{ts, key_block};
 | 
						|
  for (auto &e : x->firstblocks_) {
 | 
						|
    desc.first_blocks[ShardIdFull{e->workchain_, static_cast<ShardId>(e->shard_)}] =
 | 
						|
        FileDescription::Desc{static_cast<td::uint32>(e->seqno_), static_cast<LogicalTime>(e->lt_)};
 | 
						|
  }
 | 
						|
  desc.file = td::actor::create_actor<ArchiveFile>(
 | 
						|
      PSTRING() << "archivefile" << ts,
 | 
						|
      PSTRING() << db_root_ << "/packed/" << (key_block ? "key" : "") << ts << ".pack", ts);
 | 
						|
 | 
						|
  (key_block ? key_files_ : files_).emplace(ts, std::move(desc));
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveManager::update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, LogicalTime lt) {
 | 
						|
  auto it = desc.first_blocks.find(shard);
 | 
						|
  if (it != desc.first_blocks.end() && it->second.seqno <= seqno) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  desc.first_blocks[shard] = FileDescription::Desc{seqno, lt};
 | 
						|
  std::vector<tl_object_ptr<ton_api::db_archive_package_firstBlock>> vec;
 | 
						|
  for (auto &e : desc.first_blocks) {
 | 
						|
    vec.push_back(create_tl_object<ton_api::db_archive_package_firstBlock>(e.first.workchain, e.first.shard,
 | 
						|
                                                                           e.second.seqno, e.second.lt));
 | 
						|
  }
 | 
						|
  index_->begin_transaction().ensure();
 | 
						|
  index_
 | 
						|
      ->set(create_serialize_tl_object<ton_api::db_archive_package_key>(desc.unix_time, desc.key_block).as_slice(),
 | 
						|
            create_serialize_tl_object<ton_api::db_archive_package_value>(desc.unix_time, desc.key_block,
 | 
						|
                                                                          std::move(vec), false)
 | 
						|
                .as_slice())
 | 
						|
      .ensure();
 | 
						|
  index_->commit_transaction().ensure();
 | 
						|
}
 | 
						|
 | 
						|
ArchiveManager::FileDescription *ArchiveManager::get_file_by_seqno(ShardIdFull shard, BlockSeqno seqno,
 | 
						|
                                                                   bool key_block) {
 | 
						|
  auto &f = (key_block ? key_files_ : files_);
 | 
						|
 | 
						|
  for (auto it = f.rbegin(); it != f.rend(); it++) {
 | 
						|
    auto i = it->second.first_blocks.find(shard);
 | 
						|
    if (i != it->second.first_blocks.end() && i->second.seqno <= seqno) {
 | 
						|
      return &it->second;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void ArchiveManager::start_up() {
 | 
						|
  td::mkdir(db_root_).ensure();
 | 
						|
  td::mkdir(db_root_ + "/packed").ensure();
 | 
						|
  index_ = std::make_shared<td::RocksDb>(td::RocksDb::open(db_root_ + "/packed/globalindex").move_as_ok());
 | 
						|
  std::string value;
 | 
						|
  auto v = index_->get(create_serialize_tl_object<ton_api::db_archive_index_key>().as_slice(), value);
 | 
						|
  v.ensure();
 | 
						|
  if (v.move_as_ok() == td::KeyValue::GetStatus::Ok) {
 | 
						|
    auto R = fetch_tl_object<ton_api::db_archive_index_value>(value, true);
 | 
						|
    R.ensure();
 | 
						|
    auto x = R.move_as_ok();
 | 
						|
 | 
						|
    for (auto &d : x->packages_) {
 | 
						|
      load_package(d, false);
 | 
						|
    }
 | 
						|
    for (auto &d : x->key_packages_) {
 | 
						|
      load_package(d, true);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace validator
 | 
						|
 | 
						|
}  // namespace ton
 |