mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
325
crypto/vm/db/TonDb.cpp
Normal file
325
crypto/vm/db/TonDb.cpp
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
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
|
Loading…
Add table
Add a link
Reference in a new issue