mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	- added new fift/func code for validator complaint creation - bugfixes in validator - updates in tonlib - new versions of rocksdb/abseil - hardfork support
		
			
				
	
	
		
			325 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			325 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
    This file is part of TON Blockchain Library.
 | 
						|
 | 
						|
    TON Blockchain Library is free software: you can redistribute it and/or modify
 | 
						|
    it under the terms of the GNU Lesser General Public License as published by
 | 
						|
    the Free Software Foundation, either version 2 of the License, or
 | 
						|
    (at your option) any later version.
 | 
						|
 | 
						|
    TON Blockchain Library is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU Lesser General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU Lesser General Public License
 | 
						|
    along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
    Copyright 2017-2020 Telegram Systems LLP
 | 
						|
*/
 | 
						|
#include "vm/db/TonDb.h"
 | 
						|
 | 
						|
#include "td/utils/tl_helpers.h"
 | 
						|
#include "td/utils/Random.h"
 | 
						|
 | 
						|
#if TDDB_USE_ROCKSDB
 | 
						|
#include "td/db/RocksDb.h"
 | 
						|
#endif
 | 
						|
 | 
						|
namespace vm {
 | 
						|
 | 
						|
template <class StorerT>
 | 
						|
void SmartContractMeta::store(StorerT &storer) const {
 | 
						|
  using td::store;
 | 
						|
  store(stats.cells_total_count, storer);
 | 
						|
  store(stats.cells_total_size, storer);
 | 
						|
  store(type, storer);
 | 
						|
}
 | 
						|
template <class ParserT>
 | 
						|
void SmartContractMeta::parse(ParserT &parser) {
 | 
						|
  using td::parse;
 | 
						|
  parse(stats.cells_total_count, parser);
 | 
						|
  parse(stats.cells_total_size, parser);
 | 
						|
  parse(type, parser);
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// SmartContractDbImpl
 | 
						|
//
 | 
						|
Ref<Cell> SmartContractDbImpl::get_root() {
 | 
						|
  if (sync_root_with_db_ || !new_root_.is_null()) {
 | 
						|
    return new_root_;
 | 
						|
  }
 | 
						|
 | 
						|
  sync_root_with_db();
 | 
						|
  return new_root_;
 | 
						|
}
 | 
						|
 | 
						|
void SmartContractDbImpl::set_root(Ref<Cell> new_root) {
 | 
						|
  CHECK(new_root.not_null());
 | 
						|
  sync_root_with_db();
 | 
						|
  if (is_dynamic()) {
 | 
						|
    cell_db_->dec(new_root_);
 | 
						|
  }
 | 
						|
  new_root_ = std::move(new_root);
 | 
						|
  if (is_dynamic()) {
 | 
						|
    cell_db_->inc(new_root_);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
SmartContractDbImpl::SmartContractDbImpl(td::Slice hash, std::shared_ptr<KeyValueReader> kv)
 | 
						|
    : hash_(hash.str()), kv_(std::move(kv)) {
 | 
						|
  cell_db_ = DynamicBagOfCellsDb::create();
 | 
						|
}
 | 
						|
 | 
						|
SmartContractMeta SmartContractDbImpl::get_meta() {
 | 
						|
  sync_root_with_db();
 | 
						|
  return meta_;
 | 
						|
}
 | 
						|
td::Status SmartContractDbImpl::validate_meta() {
 | 
						|
  if (!is_dynamic()) {
 | 
						|
    return td::Status::OK();
 | 
						|
  }
 | 
						|
  sync_root_with_db();
 | 
						|
  TRY_RESULT(in_db, kv_->count({}));
 | 
						|
  if (static_cast<td::int64>(in_db) != meta_.stats.cells_total_count + 2) {
 | 
						|
    return td::Status::Error(PSLICE() << "Invalid meta " << td::tag("expected_count", in_db)
 | 
						|
                                      << td::tag("meta_count", meta_.stats.cells_total_count + 2));
 | 
						|
  }
 | 
						|
  return td::Status::OK();
 | 
						|
}
 | 
						|
 | 
						|
bool SmartContractDbImpl::is_dynamic() const {
 | 
						|
  return meta_.type == SmartContractMeta::Dynamic;
 | 
						|
}
 | 
						|
 | 
						|
bool SmartContractDbImpl::is_root_changed() const {
 | 
						|
  return !new_root_.is_null() && (db_root_.is_null() || db_root_->get_hash() != new_root_->get_hash());
 | 
						|
}
 | 
						|
 | 
						|
void SmartContractDbImpl::sync_root_with_db() {
 | 
						|
  if (sync_root_with_db_) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  std::string root_hash;
 | 
						|
  kv_->get("root", root_hash);
 | 
						|
  std::string meta_serialized;
 | 
						|
  kv_->get("meta", meta_serialized);
 | 
						|
  // TODO: proper serialization
 | 
						|
  td::unserialize(meta_, meta_serialized).ignore();
 | 
						|
  sync_root_with_db_ = true;
 | 
						|
 | 
						|
  if (root_hash.empty()) {
 | 
						|
    meta_.type = SmartContractMeta::Static;
 | 
						|
    //meta_.type = SmartContractMeta::Dynamic;
 | 
						|
  } else {
 | 
						|
    if (is_dynamic()) {
 | 
						|
      //FIXME: error handling
 | 
						|
      db_root_ = cell_db_->load_cell(root_hash).move_as_ok();
 | 
						|
    } else {
 | 
						|
      std::string boc_serialized;
 | 
						|
      kv_->get("boc", boc_serialized);
 | 
						|
      BagOfCells boc;
 | 
						|
      //TODO: check error
 | 
						|
      boc.deserialize(boc_serialized);
 | 
						|
      db_root_ = boc.get_root_cell();
 | 
						|
    }
 | 
						|
    CHECK(db_root_->get_hash().as_slice() == root_hash);
 | 
						|
    new_root_ = db_root_;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
enum { boc_size = 2000 };
 | 
						|
void SmartContractDbImpl::prepare_commit_dynamic(bool force) {
 | 
						|
  if (!is_dynamic()) {
 | 
						|
    CHECK(force);
 | 
						|
    meta_.stats = {};
 | 
						|
    cell_db_->inc(new_root_);
 | 
						|
  }
 | 
						|
  cell_db_->prepare_commit();
 | 
						|
  meta_.stats.apply_diff(cell_db_->get_stats_diff());
 | 
						|
 | 
						|
  if (!force && meta_.stats.cells_total_size < boc_size) {
 | 
						|
    //LOG(ERROR) << "DYNAMIC -> BOC";
 | 
						|
    return prepare_commit_static(true);
 | 
						|
  }
 | 
						|
  is_dynamic_commit_ = true;
 | 
						|
};
 | 
						|
 | 
						|
void SmartContractDbImpl::prepare_commit_static(bool force) {
 | 
						|
  BagOfCells boc;
 | 
						|
  boc.add_root(new_root_);
 | 
						|
  boc.import_cells().ensure();  // FIXME
 | 
						|
  if (!force && boc.estimate_serialized_size(15) > boc_size) {
 | 
						|
    //LOG(ERROR) << "BOC -> DYNAMIC ";
 | 
						|
    return prepare_commit_dynamic(true);
 | 
						|
  }
 | 
						|
  if (is_dynamic()) {
 | 
						|
    cell_db_->dec(new_root_);
 | 
						|
    cell_db_->prepare_commit();
 | 
						|
    // stats is invalid now
 | 
						|
  }
 | 
						|
  is_dynamic_commit_ = false;
 | 
						|
  boc_to_commit_ = boc.serialize_to_string(15);
 | 
						|
  meta_.stats = {};
 | 
						|
}
 | 
						|
 | 
						|
void SmartContractDbImpl::prepare_transaction() {
 | 
						|
  sync_root_with_db();
 | 
						|
  if (!is_root_changed()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (is_dynamic()) {
 | 
						|
    prepare_commit_dynamic(false);
 | 
						|
  } else {
 | 
						|
    prepare_commit_static(false);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void SmartContractDbImpl::commit_transaction(KeyValue &kv) {
 | 
						|
  if (!is_root_changed()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (is_dynamic_commit_) {
 | 
						|
    //LOG(ERROR) << "STORE DYNAMIC";
 | 
						|
    if (!is_dynamic() && db_root_.not_null()) {
 | 
						|
      kv.erase("boc");
 | 
						|
    }
 | 
						|
    CellStorer storer(kv);
 | 
						|
    cell_db_->commit(storer);
 | 
						|
    meta_.type = SmartContractMeta::Dynamic;
 | 
						|
  } else {
 | 
						|
    //LOG(ERROR) << "STORE BOC";
 | 
						|
    if (is_dynamic() && db_root_.not_null()) {
 | 
						|
      //LOG(ERROR) << "Clear Dynamic db";
 | 
						|
      CellStorer storer(kv);
 | 
						|
      cell_db_->commit(storer);
 | 
						|
      cell_db_ = DynamicBagOfCellsDb::create();
 | 
						|
    }
 | 
						|
    meta_.type = SmartContractMeta::Static;
 | 
						|
    kv.set("boc", boc_to_commit_);
 | 
						|
    boc_to_commit_ = {};
 | 
						|
  }
 | 
						|
 | 
						|
  kv.set("root", new_root_->get_hash().as_slice());
 | 
						|
  kv.set("meta", td::serialize(meta_));
 | 
						|
  db_root_ = new_root_;
 | 
						|
}
 | 
						|
 | 
						|
void SmartContractDbImpl::set_reader(std::shared_ptr<KeyValueReader> reader) {
 | 
						|
  kv_ = std::move(reader);
 | 
						|
  cell_db_->set_loader(std::make_unique<CellLoader>(kv_));
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// TonDbTransactionImpl
 | 
						|
//
 | 
						|
SmartContractDb TonDbTransactionImpl::begin_smartcontract(td::Slice hash) {
 | 
						|
  SmartContractDb res;
 | 
						|
  contracts_.apply(hash, [&](auto &info) {
 | 
						|
    if (!info.is_inited) {
 | 
						|
      info.is_inited = true;
 | 
						|
      info.hash = hash.str();
 | 
						|
      info.smart_contract_db = std::make_unique<SmartContractDbImpl>(hash, nullptr);
 | 
						|
    }
 | 
						|
    LOG_CHECK(info.generation_ != generation_) << "Cannot begin one smartcontract twice during the same transaction";
 | 
						|
    CHECK(info.smart_contract_db);
 | 
						|
    info.smart_contract_db->set_reader(std::make_shared<td::PrefixedKeyValueReader>(reader_, hash));
 | 
						|
    res = std::move(info.smart_contract_db);
 | 
						|
  });
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
void TonDbTransactionImpl::commit_smartcontract(SmartContractDb txn) {
 | 
						|
  commit_smartcontract(SmartContractDiff(std::move(txn)));
 | 
						|
}
 | 
						|
void TonDbTransactionImpl::commit_smartcontract(SmartContractDiff txn) {
 | 
						|
  {
 | 
						|
    td::PrefixedKeyValue kv(kv_, txn.hash());
 | 
						|
    txn.commit_transaction(kv);
 | 
						|
  }
 | 
						|
  end_smartcontract(txn.extract_smartcontract());
 | 
						|
}
 | 
						|
 | 
						|
void TonDbTransactionImpl::abort_smartcontract(SmartContractDb txn) {
 | 
						|
  end_smartcontract(std::move(txn));
 | 
						|
}
 | 
						|
void TonDbTransactionImpl::abort_smartcontract(SmartContractDiff txn) {
 | 
						|
  end_smartcontract(txn.extract_smartcontract());
 | 
						|
}
 | 
						|
 | 
						|
TonDbTransactionImpl::TonDbTransactionImpl(std::shared_ptr<KeyValue> kv) : kv_(std::move(kv)) {
 | 
						|
  CHECK(kv_ != nullptr);
 | 
						|
  reader_.reset(kv_->snapshot().release());
 | 
						|
}
 | 
						|
 | 
						|
void TonDbTransactionImpl::begin() {
 | 
						|
  kv_->begin_write_batch();
 | 
						|
  generation_++;
 | 
						|
}
 | 
						|
void TonDbTransactionImpl::commit() {
 | 
						|
  kv_->commit_write_batch();
 | 
						|
  reader_.reset(kv_->snapshot().release());
 | 
						|
}
 | 
						|
void TonDbTransactionImpl::abort() {
 | 
						|
  kv_->abort_write_batch();
 | 
						|
}
 | 
						|
void TonDbTransactionImpl::clear_cache() {
 | 
						|
  contracts_ = {};
 | 
						|
}
 | 
						|
 | 
						|
void TonDbTransactionImpl::end_smartcontract(SmartContractDb smart_contract) {
 | 
						|
  contracts_.apply(smart_contract->hash(), [&](auto &info) {
 | 
						|
    CHECK(info.hash == smart_contract->hash());
 | 
						|
    CHECK(!info.smart_contract_db);
 | 
						|
    info.smart_contract_db = std::move(smart_contract);
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// TonDbImpl
 | 
						|
//
 | 
						|
TonDbImpl::TonDbImpl(std::unique_ptr<KeyValue> kv)
 | 
						|
    : kv_(std::move(kv)), transaction_(std::make_unique<TonDbTransactionImpl>(kv_)) {
 | 
						|
}
 | 
						|
TonDbImpl::~TonDbImpl() {
 | 
						|
  CHECK(transaction_);
 | 
						|
  kv_->flush();
 | 
						|
}
 | 
						|
TonDbTransaction TonDbImpl::begin_transaction() {
 | 
						|
  CHECK(transaction_);
 | 
						|
  transaction_->begin();
 | 
						|
  return std::move(transaction_);
 | 
						|
}
 | 
						|
void TonDbImpl::commit_transaction(TonDbTransaction transaction) {
 | 
						|
  CHECK(!transaction_);
 | 
						|
  CHECK(&transaction->kv() == kv_.get());
 | 
						|
  transaction_ = std::move(transaction);
 | 
						|
  transaction_->commit();
 | 
						|
}
 | 
						|
void TonDbImpl::abort_transaction(TonDbTransaction transaction) {
 | 
						|
  CHECK(!transaction_);
 | 
						|
  CHECK(&transaction->kv() == kv_.get());
 | 
						|
  transaction_ = std::move(transaction);
 | 
						|
  transaction_->abort();
 | 
						|
}
 | 
						|
void TonDbImpl::clear_cache() {
 | 
						|
  CHECK(transaction_);
 | 
						|
  transaction_->clear_cache();
 | 
						|
}
 | 
						|
 | 
						|
std::string TonDbImpl::stats() const {
 | 
						|
  return kv_->stats();
 | 
						|
}
 | 
						|
 | 
						|
td::Result<TonDb> TonDbImpl::open(td::Slice path) {
 | 
						|
#if TDDB_USE_ROCKSDB
 | 
						|
  TRY_RESULT(rocksdb, td::RocksDb::open(path.str()));
 | 
						|
  return std::make_unique<TonDbImpl>(std::make_unique<td::RocksDb>(std::move(rocksdb)));
 | 
						|
#else
 | 
						|
  return td::Status::Error("TonDb is not supported in this build");
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace vm
 |