Skip to content

Commit 6c9de46

Browse files
Optimized celldb and package import for archive nodes (#1653)
* celldb: version 2 - thread safe cache - parallel commit - multiple optimizations - support of key-value merge operations - improved tests and benchmarks - in-memory version won't read from key value after start - uses vector in-memory table now - use rocksdb::WALRecoveryMode::kTolerateCorruptedTailRecords - do not silently ignore errors during recovery * celldb: add test for load nonexisting cell, test thread safeness of CellUsageTree, fixes * Cache validator sets for create_shard_state * Permanent celldb, improve importing archive slices, new flags for validator-engine * Fix persistent state lookup * Tool for generating liteserver desc for global config * Import proof for initial block * Perf timer for archive import * Do not recompute validator set in MasterchainStateQ::mc_reinit This does not improve performance (as unpack_validator_set result is cached regardless) but makes the code clearer. * Remove debug logs * Fix downloading initial proof * Fix error processing in CellDbIn::store_block_state_permanent_bulk * Improve some logs --------- Co-authored-by: birydrad <> Co-authored-by: Dan Klishch <[email protected]>
1 parent a174fb1 commit 6c9de46

38 files changed

+1739
-109
lines changed

crypto/block/mc-config.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -650,8 +650,8 @@ class Config {
650650
const WorkchainSet& get_workchain_list() const {
651651
return workchains_;
652652
}
653-
const ValidatorSet* get_cur_validator_set() const {
654-
return cur_validators_.get();
653+
std::shared_ptr<ValidatorSet> const& get_cur_validator_set() const& {
654+
return cur_validators_;
655655
}
656656
std::pair<ton::UnixTime, ton::UnixTime> get_validator_set_start_stop(int next = 0) const;
657657
ton::ValidatorSessionConfig get_consensus_config() const;

crypto/vm/db/CellStorage.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ namespace {
3232

3333
class RefcntCellStorer {
3434
public:
35-
RefcntCellStorer(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc)
36-
: refcnt_(refcnt), cell_(cell), as_boc_(as_boc) {
35+
RefcntCellStorer(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc, int max_level = vm::Cell::max_level)
36+
: refcnt_(refcnt), cell_(cell), as_boc_(as_boc), max_level_(max_level) {
37+
CHECK(!as_boc_ || max_level_ == vm::Cell::max_level);
3738
}
3839

3940
template <class StorerT>
@@ -51,10 +52,25 @@ class RefcntCellStorer {
5152
CHECK(refcnt_ > 0);
5253
store(refcnt_, storer);
5354
CHECK(cell_.not_null())
54-
store(*cell_, storer);
55+
if (max_level_ == vm::Cell::max_level) {
56+
store(*cell_, storer);
57+
} else {
58+
auto level_mask = cell_->get_level_mask().apply(max_level_);
59+
storer.template store_binary<td::uint8>(
60+
static_cast<unsigned char>(cell_->get_refs_cnt() + 8 * cell_->is_special() + 32 * level_mask.get_mask()));
61+
auto d2 = static_cast<unsigned char>((cell_->get_bits() / 8) * 2);
62+
if ((cell_->get_bits() & 7) != 0) {
63+
d2 = static_cast<unsigned char>(d2 + 1);
64+
}
65+
storer.template store_binary<td::uint8>(d2);
66+
storer.store_slice(td::Slice(cell_->get_data(), (cell_->get_bits() + 7) / 8));
67+
}
68+
5569
for (unsigned i = 0; i < cell_->size_refs(); i++) {
5670
auto cell = cell_->get_ref(i);
57-
auto level_mask = cell->get_level_mask();
71+
auto level_mask =
72+
cell->get_level_mask().apply(max_level_ + (cell_->special_type() == CellTraits::SpecialType::MerkleProof ||
73+
cell_->special_type() == CellTraits::SpecialType::MerkleUpdate));
5874
auto level = level_mask.get_level();
5975
td::uint8 x = static_cast<td::uint8>(level_mask.get_mask());
6076
storer.store_slice(td::Slice(&x, 1));
@@ -79,6 +95,7 @@ class RefcntCellStorer {
7995
td::int32 refcnt_;
8096
td::Ref<DataCell> cell_;
8197
bool as_boc_;
98+
int max_level_;
8299
};
83100

84101
class RefcntCellParser {
@@ -120,13 +137,13 @@ class RefcntCellParser {
120137
Ref<Cell> refs[Cell::max_refs];
121138
for (int i = 0; i < info.refs_cnt; i++) {
122139
if (data.size() < 1) {
123-
return td::Status::Error("Not enought data");
140+
return td::Status::Error("Not enough data");
124141
}
125142
Cell::LevelMask level_mask(data[0]);
126143
auto n = level_mask.get_hashes_count();
127144
auto end_offset = 1 + n * (Cell::hash_bytes + Cell::depth_bytes);
128145
if (data.size() < end_offset) {
129-
return td::Status::Error("Not enought data");
146+
return td::Status::Error("Not enough data");
130147
}
131148

132149
TRY_RESULT(ext_cell, ext_cell_creator.ext_cell(level_mask, data.substr(1, n * Cell::hash_bytes),
@@ -243,8 +260,8 @@ td::Status CellStorer::erase(td::Slice hash) {
243260
return kv_.erase(hash);
244261
}
245262

246-
std::string CellStorer::serialize_value(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc) {
247-
return td::serialize(RefcntCellStorer(refcnt, cell, as_boc));
263+
std::string CellStorer::serialize_value(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc, int max_level) {
264+
return td::serialize(RefcntCellStorer(refcnt, cell, as_boc, max_level));
248265
}
249266

250267
td::Status CellStorer::set(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc) {

crypto/vm/db/CellStorage.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ class CellStorer {
7272
static void merge_refcnt_diffs(std::string &left, td::Slice right);
7373
static std::string serialize_refcnt_diffs(td::int32 refcnt_diff);
7474

75-
static std::string serialize_value(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc);
75+
static std::string serialize_value(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc,
76+
int max_level = vm::Cell::max_level);
7677

7778
struct Diff {
7879
enum Type { Set, Erase, Merge } type{Set};

utils/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ target_include_directories(pack-viewer PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_
2222
add_executable(proxy-liteserver proxy-liteserver.cpp)
2323
target_link_libraries(proxy-liteserver tdutils tdactor adnl dht tl_api ton_crypto git lite-client-common)
2424

25+
add_executable(prepare-ls-slice-config prepare-ls-slice-config.cpp)
26+
target_link_libraries(prepare-ls-slice-config tdutils tdactor adnl dht tl_api ton_crypto git lite-client-common)
27+
2528
install(TARGETS generate-random-id proxy-liteserver RUNTIME DESTINATION bin)

utils/prepare-ls-slice-config.cpp

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
This file is part of TON Blockchain source code.
3+
4+
TON Blockchain is free software; you can redistribute it and/or
5+
modify it under the terms of the GNU General Public License
6+
as published by the Free Software Foundation; either version 2
7+
of the License, or (at your option) any later version.
8+
9+
TON Blockchain is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
#include "td/utils/filesystem.h"
18+
#include "td/actor/actor.h"
19+
#include "td/actor/MultiPromise.h"
20+
#include "td/utils/OptionParser.h"
21+
#include "td/utils/port/path.h"
22+
#include "td/utils/port/signals.h"
23+
#include "td/utils/port/IPAddress.h"
24+
#include "td/utils/Random.h"
25+
#include "td/utils/FileLog.h"
26+
#include "git.h"
27+
#include "auto/tl/ton_api.h"
28+
#include "auto/tl/lite_api.h"
29+
#include "tl-utils/lite-utils.hpp"
30+
#include "auto/tl/ton_api_json.h"
31+
#include "adnl/adnl.h"
32+
#include "lite-client/ext-client.h"
33+
#include "ton/lite-tl.hpp"
34+
35+
#include "td/utils/overloaded.h"
36+
37+
#include <iostream>
38+
#include "td/utils/tl_storers.h"
39+
#include "vm/boc.h"
40+
#include "vm/cells/MerkleProof.h"
41+
42+
#include <ton/ton-tl.hpp>
43+
#include "block/block-auto.h"
44+
#include "block/mc-config.h"
45+
46+
using namespace ton;
47+
48+
std::string global_config_file;
49+
td::optional<BlockSeqno> start_mc_seqno, end_mc_seqno;
50+
std::vector<ShardIdFull> shards;
51+
52+
class PrepareLsSliceConfig : public td::actor::Actor {
53+
public:
54+
void start_up() override {
55+
if (start_mc_seqno && end_mc_seqno && start_mc_seqno.value() > end_mc_seqno.value()) {
56+
LOG(FATAL) << "from-seqno is greater than to-seqno";
57+
}
58+
59+
if (!start_mc_seqno && !end_mc_seqno) {
60+
auto slice = create_tl_object<ton_api::liteserver_descV2_sliceSimple>();
61+
if (shards.empty()) {
62+
slice->shards_.push_back(create_tl_shard_id(ShardIdFull{basechainId, shardIdAll}));
63+
} else {
64+
for (const ShardIdFull& shard : shards) {
65+
if (!shard.is_masterchain()) {
66+
slice->shards_.push_back(create_tl_shard_id(shard));
67+
}
68+
}
69+
}
70+
print_result(*slice);
71+
return;
72+
}
73+
74+
auto gc_s = td::read_file(global_config_file).move_as_ok();
75+
auto gc_j = td::json_decode(gc_s.as_slice()).move_as_ok();
76+
ton_api::liteclient_config_global gc;
77+
ton_api::from_json(gc, gc_j.get_object()).ensure();
78+
auto r_servers = liteclient::LiteServerConfig::parse_global_config(gc);
79+
r_servers.ensure();
80+
client_ = liteclient::ExtClient::create(r_servers.move_as_ok(), nullptr);
81+
82+
slice_timed_ = create_tl_object<ton_api::liteserver_descV2_sliceTimed>();
83+
++pending_;
84+
request_shards_info(start_mc_seqno, true);
85+
request_shards_info(end_mc_seqno, false);
86+
dec_pending();
87+
}
88+
89+
template <class Type, class... Args>
90+
static td::BufferSlice create_query(Args&&... args) {
91+
Type object(std::forward<Args>(args)...);
92+
return create_serialize_tl_object<lite_api::liteServer_query>(serialize_tl_object(&object, true));
93+
}
94+
95+
template <class Type>
96+
static tl_object_ptr<Type> parse_response(const td::Result<td::BufferSlice>& R) {
97+
R.ensure();
98+
auto err = fetch_tl_object<lite_api::liteServer_error>(R.ok(), true);
99+
if (err.is_ok()) {
100+
LOG(FATAL) << "liteserver error: " << err.ok()->message_;
101+
}
102+
auto res = fetch_tl_object<Type>(R.ok(), true);
103+
res.ensure();
104+
return res.move_as_ok();
105+
}
106+
107+
void request_shards_info(td::optional<BlockSeqno> seqno, bool is_start) {
108+
if (!seqno) {
109+
return;
110+
}
111+
++pending_;
112+
td::actor::send_closure(
113+
client_, &liteclient::ExtClient::send_query, "q",
114+
create_query<lite_api::liteServer_lookupBlock>(
115+
1, create_tl_object<lite_api::tonNode_blockId>(masterchainId, shardIdAll, seqno.value()), 0, 0),
116+
td::Timestamp::in(5.0), [=, client = client_.get(), SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
117+
auto mc_header = parse_response<lite_api::liteServer_blockHeader>(std::move(R));
118+
auto block_id = create_block_id(mc_header->id_);
119+
td::actor::send_closure(
120+
client, &liteclient::ExtClient::send_query, "q",
121+
create_query<lite_api::liteServer_getAllShardsInfo>(create_tl_lite_block_id(block_id)),
122+
td::Timestamp::in(5.0), [=, mc_header = std::move(mc_header)](td::Result<td::BufferSlice> R) mutable {
123+
auto shards_info = parse_response<lite_api::liteServer_allShardsInfo>(std::move(R));
124+
td::actor::send_closure(SelfId, &PrepareLsSliceConfig::got_shards_info, std::move(mc_header),
125+
std::move(shards_info), is_start);
126+
});
127+
});
128+
}
129+
130+
static tl_object_ptr<ton_api::liteserver_descV2_shardInfo> parse_header(const lite_api::liteServer_blockHeader& obj,
131+
bool is_start) {
132+
auto res = create_tl_object<ton_api::liteserver_descV2_shardInfo>();
133+
134+
BlockIdExt block_id = create_block_id(obj.id_);
135+
res->shard_id_ = create_tl_shard_id(block_id.shard_full());
136+
res->seqno_ = block_id.seqno();
137+
138+
auto root = vm::std_boc_deserialize(obj.header_proof_).move_as_ok();
139+
root = vm::MerkleProof::virtualize(root, 1);
140+
block::gen::Block::Record blk;
141+
block::gen::BlockInfo::Record info;
142+
CHECK(tlb::unpack_cell(root, blk) && tlb::unpack_cell(blk.info, info));
143+
res->utime_ = info.gen_utime;
144+
res->lt_ = (is_start ? info.start_lt : info.end_lt);
145+
146+
return res;
147+
}
148+
149+
void got_shards_info(tl_object_ptr<lite_api::liteServer_blockHeader> mc_header,
150+
tl_object_ptr<lite_api::liteServer_allShardsInfo> shards_info, bool is_start) {
151+
(is_start ? slice_timed_->shards_from_ : slice_timed_->shards_to_).push_back(parse_header(*mc_header, is_start));
152+
153+
auto root = vm::std_boc_deserialize(shards_info->data_).move_as_ok();
154+
block::ShardConfig sh_conf;
155+
CHECK(sh_conf.unpack(vm::load_cell_slice_ref(root)));
156+
auto ids = sh_conf.get_shard_hash_ids(true);
157+
for (auto id : ids) {
158+
BlockIdExt block_id = sh_conf.get_shard_hash(ton::ShardIdFull(id))->top_block_id();
159+
bool ok = shards.empty();
160+
for (const auto& our_shard : shards) {
161+
if (shard_intersects(our_shard, block_id.shard_full())) {
162+
ok = true;
163+
break;
164+
}
165+
}
166+
if (ok) {
167+
++pending_;
168+
td::actor::send_closure(
169+
client_, &liteclient::ExtClient::send_query, "q",
170+
create_query<lite_api::liteServer_getBlockHeader>(create_tl_lite_block_id(block_id), 0xffff),
171+
td::Timestamp::in(5.0), [=, SelfId = actor_id(this)](td::Result<td::BufferSlice> R) mutable {
172+
auto header = parse_response<lite_api::liteServer_blockHeader>(std::move(R));
173+
td::actor::send_closure(SelfId, &PrepareLsSliceConfig::got_block_header, std::move(header), is_start);
174+
});
175+
}
176+
}
177+
178+
dec_pending();
179+
}
180+
181+
void got_block_header(tl_object_ptr<lite_api::liteServer_blockHeader> header, bool is_start) {
182+
(is_start ? slice_timed_->shards_from_ : slice_timed_->shards_to_).push_back(parse_header(*header, is_start));
183+
dec_pending();
184+
}
185+
186+
void print_result(const ton_api::liteserver_descV2_Slice& result) {
187+
auto s = td::json_encode<std::string>(td::ToJson(result), true);
188+
std::cout << s << "\n";
189+
std::cout.flush();
190+
exit(0);
191+
}
192+
193+
private:
194+
td::actor::ActorOwn<liteclient::ExtClient> client_;
195+
tl_object_ptr<ton_api::liteserver_descV2_sliceTimed> slice_timed_;
196+
size_t pending_ = 0;
197+
198+
void dec_pending() {
199+
--pending_;
200+
if (pending_ == 0) {
201+
auto cmp = [](const tl_object_ptr<ton_api::liteserver_descV2_shardInfo>& a,
202+
const tl_object_ptr<ton_api::liteserver_descV2_shardInfo>& b) {
203+
return create_shard_id(a->shard_id_) < create_shard_id(b->shard_id_);
204+
};
205+
std::sort(slice_timed_->shards_from_.begin(), slice_timed_->shards_from_.end(), cmp);
206+
std::sort(slice_timed_->shards_to_.begin(), slice_timed_->shards_to_.end(), cmp);
207+
print_result(*slice_timed_);
208+
}
209+
}
210+
};
211+
212+
int main(int argc, char* argv[]) {
213+
SET_VERBOSITY_LEVEL(verbosity_INFO);
214+
215+
td::unique_ptr<td::LogInterface> logger_;
216+
SCOPE_EXIT {
217+
td::log_interface = td::default_log_interface;
218+
};
219+
220+
td::OptionParser p;
221+
p.set_description(
222+
"Generate liteserver.descV2.Slice for global-config.json from given shards and masterchain seqnos\n");
223+
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
224+
int v = VERBOSITY_NAME(FATAL) + (td::to_integer<int>(arg));
225+
SET_VERBOSITY_LEVEL(v);
226+
});
227+
p.add_option('V', "version", "show build information", [&]() {
228+
std::cout << "prepare-ls-slice-config build information: [ Commit: " << GitMetadata::CommitSHA1()
229+
<< ", Date: " << GitMetadata::CommitDate() << "]\n";
230+
std::exit(0);
231+
});
232+
p.add_option('h', "help", "print help", [&]() {
233+
char b[10240];
234+
td::StringBuilder sb(td::MutableSlice{b, 10000});
235+
sb << p;
236+
std::cout << sb.as_cslice().c_str();
237+
std::exit(2);
238+
});
239+
p.add_option('C', "global-config", "global TON configuration file (used to fetch shard configuration)",
240+
[&](td::Slice arg) { global_config_file = arg.str(); });
241+
p.add_checked_option('f', "from-seqno", "starting masterchain seqno (default: none)",
242+
[&](td::Slice arg) -> td::Status {
243+
TRY_RESULT_ASSIGN(start_mc_seqno, td::to_integer_safe<BlockSeqno>(arg))
244+
return td::Status::OK();
245+
});
246+
p.add_checked_option('t', "to-seqno", "ending masterchain seqno (default: none)", [&](td::Slice arg) -> td::Status {
247+
TRY_RESULT_ASSIGN(end_mc_seqno, td::to_integer_safe<BlockSeqno>(arg))
248+
return td::Status::OK();
249+
});
250+
p.add_checked_option('s', "shard", "shard in format 0:8000000000000000 (default: all shards)",
251+
[&](td::Slice arg) -> td::Status {
252+
TRY_RESULT(shard, ShardIdFull::parse(arg));
253+
if (!shard.is_valid_ext()) {
254+
return td::Status::Error(PSTRING() << "invalid shard " << arg);
255+
}
256+
shards.push_back(shard);
257+
return td::Status::OK();
258+
});
259+
260+
p.run(argc, argv).ensure();
261+
td::actor::Scheduler scheduler({3});
262+
263+
scheduler.run_in_context([&] { td::actor::create_actor<PrepareLsSliceConfig>("main").release(); });
264+
while (scheduler.run(1)) {
265+
}
266+
}

0 commit comments

Comments
 (0)