1
0
mirror of https://github.com/danog/ton.git synced 2024-12-02 17:38:33 +01:00
ton/crypto/vm/db/TonDb.cpp
2019-09-07 14:33:36 +04:00

326 lines
9.0 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-2019 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_transaction();
generation_++;
}
void TonDbTransactionImpl::commit() {
kv_->commit_transaction();
reader_.reset(kv_->snapshot().release());
}
void TonDbTransactionImpl::abort() {
kv_->abort_transaction();
}
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