Skip to content

Commit 2981cce

Browse files
committed
Merge branch 'testnet' into archive-sync
2 parents c8b90a7 + 86e6726 commit 2981cce

File tree

138 files changed

+5768
-1201
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

138 files changed

+5768
-1201
lines changed

common/global-version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
namespace ton {
2020

2121
// See doc/GlobalVersions.md
22-
constexpr int SUPPORTED_VERSION = 10;
22+
constexpr int SUPPORTED_VERSION = 11;
2323

2424
}

crypto/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ set(BLOCK_SOURCE
212212
block/mc-config.cpp
213213
block/output-queue-merger.cpp
214214
block/transaction.cpp
215+
block/account-storage-stat.h
216+
block/account-storage-stat.cpp
215217
block/precompiled-smc/PrecompiledSmartContract.cpp
216218
${TLB_BLOCK_AUTO}
217219

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
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 "account-storage-stat.h"
18+
19+
namespace block {
20+
21+
AccountStorageStat::AccountStorageStat() : AccountStorageStat({}, {}, 0, 0) {
22+
}
23+
24+
AccountStorageStat::AccountStorageStat(Ref<vm::Cell> dict_root, std::vector<Ref<vm::Cell>> roots,
25+
td::uint64 total_cells, td::uint64 total_bits)
26+
: dict_(std::move(dict_root), 256), total_cells_(total_cells), total_bits_(total_bits), roots_(std::move(roots)) {
27+
}
28+
29+
AccountStorageStat::AccountStorageStat(const AccountStorageStat* parent)
30+
: dict_(parent->dict_)
31+
, dict_up_to_date_(parent->dict_up_to_date_)
32+
, total_cells_(parent->total_cells_)
33+
, total_bits_(parent->total_bits_)
34+
, roots_(parent->roots_)
35+
, parent_(parent) {
36+
CHECK(parent_->parent_ == nullptr);
37+
}
38+
39+
td::Status AccountStorageStat::replace_roots(std::vector<Ref<vm::Cell>> new_roots, bool check_merkle_depth) {
40+
std::erase_if(new_roots, [](const Ref<vm::Cell>& c) { return c.is_null(); });
41+
if (new_roots.empty()) {
42+
roots_.clear();
43+
total_bits_ = total_cells_ = 0;
44+
dict_ = vm::Dictionary{256};
45+
cache_ = {};
46+
dict_up_to_date_ = true;
47+
parent_ = nullptr;
48+
return td::Status::OK();
49+
}
50+
51+
auto cmp = [](const Ref<vm::Cell>& c1, const Ref<vm::Cell>& c2) { return c1->get_hash() < c2->get_hash(); };
52+
std::sort(new_roots.begin(), new_roots.end(), cmp);
53+
std::sort(roots_.begin(), roots_.end(), cmp);
54+
std::vector<Ref<vm::Cell>> to_add, to_del;
55+
std::set_difference(new_roots.begin(), new_roots.end(), roots_.begin(), roots_.end(), std::back_inserter(to_add),
56+
cmp);
57+
std::set_difference(roots_.begin(), roots_.end(), new_roots.begin(), new_roots.end(), std::back_inserter(to_del),
58+
cmp);
59+
if (to_add.empty() && to_del.empty()) {
60+
return td::Status::OK();
61+
}
62+
63+
for (const Ref<vm::Cell>& root : to_add) {
64+
TRY_RESULT(info, add_cell(root));
65+
if (check_merkle_depth && info.max_merkle_depth > MAX_MERKLE_DEPTH) {
66+
return td::Status::Error("too big Merkle depth");
67+
}
68+
}
69+
for (const Ref<vm::Cell>& root : to_del) {
70+
TRY_STATUS(remove_cell(root));
71+
}
72+
73+
roots_ = std::move(new_roots);
74+
dict_up_to_date_ = false;
75+
for (auto& [_, e] : cache_) {
76+
TRY_STATUS(finalize_entry(e));
77+
}
78+
return td::Status::OK();
79+
}
80+
81+
void AccountStorageStat::add_hint(const td::HashSet<vm::CellHash>& hint) {
82+
td::HashSet<vm::CellHash> visited;
83+
std::function<void(const Ref<vm::Cell>&, bool)> dfs = [&](const Ref<vm::Cell>& cell, bool is_root) {
84+
if (!visited.insert(cell->get_hash()).second) {
85+
return;
86+
}
87+
Entry& e = get_entry(cell);
88+
e.exists = e.exists_known = true;
89+
if (is_root) {
90+
fetch_from_dict(e).ignore();
91+
if (e.max_merkle_depth && e.max_merkle_depth.value() != 0) {
92+
return;
93+
}
94+
}
95+
if (hint.contains(cell->get_hash())) {
96+
bool spec;
97+
vm::CellSlice cs = vm::load_cell_slice_special(cell, spec);
98+
e.size_bits = cs.size();
99+
for (unsigned i = 0; i < cs.size_refs(); ++i) {
100+
dfs(cs.prefetch_ref(i), false);
101+
}
102+
}
103+
};
104+
for (const Ref<vm::Cell>& root : roots_) {
105+
dfs(root, true);
106+
}
107+
}
108+
109+
td::Result<AccountStorageStat::CellInfo> AccountStorageStat::add_cell(const Ref<vm::Cell>& cell) {
110+
Entry& e = get_entry(cell);
111+
if (!e.exists_known || e.refcnt_diff < 0) {
112+
TRY_STATUS(fetch_from_dict(e));
113+
}
114+
++e.refcnt_diff;
115+
if (e.exists || e.refcnt_diff > 1 || (e.refcnt && e.refcnt.value() + e.refcnt_diff != 1)) {
116+
if (!e.max_merkle_depth) {
117+
TRY_STATUS(fetch_from_dict(e));
118+
if (!e.max_merkle_depth) {
119+
return td::Status::Error(PSTRING() << "unexpected unknown Merkle depth of cell " << cell->get_hash());
120+
}
121+
}
122+
return CellInfo{e.max_merkle_depth.value()};
123+
}
124+
125+
td::uint32 max_merkle_depth = 0;
126+
bool spec;
127+
vm::CellSlice cs = vm::load_cell_slice_special(cell, spec);
128+
e.size_bits = cs.size();
129+
for (unsigned i = 0; i < cs.size_refs(); ++i) {
130+
TRY_RESULT(info, add_cell(cs.prefetch_ref(i)));
131+
max_merkle_depth = std::max(max_merkle_depth, info.max_merkle_depth);
132+
}
133+
if (cs.special_type() == vm::CellTraits::SpecialType::MerkleProof ||
134+
cs.special_type() == vm::CellTraits::SpecialType::MerkleUpdate) {
135+
++max_merkle_depth;
136+
}
137+
max_merkle_depth = std::min(max_merkle_depth, MERKLE_DEPTH_LIMIT);
138+
Entry& e2 = get_entry(cell);
139+
e2.max_merkle_depth = max_merkle_depth;
140+
return CellInfo{max_merkle_depth};
141+
}
142+
143+
td::Status AccountStorageStat::remove_cell(const Ref<vm::Cell>& cell) {
144+
Entry& e = get_entry(cell);
145+
if (!e.exists_known) {
146+
TRY_STATUS(fetch_from_dict(e));
147+
}
148+
if (!e.exists) {
149+
return td::Status::Error(PSTRING() << "Failed to remove cell " << cell->get_hash().to_hex()
150+
<< " : does not exist in the dict");
151+
}
152+
--e.refcnt_diff;
153+
if (e.refcnt_diff < 0 && !e.refcnt) {
154+
TRY_STATUS(fetch_from_dict(e));
155+
}
156+
if (e.refcnt.value() + e.refcnt_diff != 0) {
157+
return td::Status::OK();
158+
}
159+
bool spec;
160+
vm::CellSlice cs = vm::load_cell_slice_special(cell, spec);
161+
e.size_bits = cs.size();
162+
for (unsigned i = 0; i < cs.size_refs(); ++i) {
163+
TRY_STATUS(remove_cell(cs.prefetch_ref(i)));
164+
}
165+
return td::Status::OK();
166+
}
167+
168+
td::Result<Ref<vm::Cell>> AccountStorageStat::get_dict_root() {
169+
if (!dict_up_to_date_) {
170+
std::vector<std::pair<td::ConstBitPtr, Ref<vm::CellBuilder>>> values;
171+
for (auto& [_, e] : cache_) {
172+
if (e.dict_refcnt_diff == 0) {
173+
continue;
174+
}
175+
if (!e.exists_known || !e.refcnt || (e.exists && !e.max_merkle_depth)) {
176+
return td::Status::Error("unexpected state of storage stat");
177+
}
178+
if (e.exists) {
179+
Ref<vm::CellBuilder> cbr{true};
180+
auto& cb = cbr.write();
181+
CHECK(cb.store_long_bool(e.refcnt.value(), 32) && cb.store_long_bool(e.max_merkle_depth.value(), 2));
182+
values.emplace_back(e.hash.bits(), std::move(cbr));
183+
} else {
184+
values.emplace_back(e.hash.bits(), Ref<vm::CellBuilder>{});
185+
}
186+
e.dict_refcnt_diff = 0;
187+
}
188+
if (!dict_.multiset(values)) {
189+
return td::Status::Error("failed to update dictionary");
190+
}
191+
dict_up_to_date_ = true;
192+
}
193+
return dict_.get_root_cell();
194+
}
195+
196+
void AccountStorageStat::apply_child_stat(AccountStorageStat&& child) {
197+
CHECK(parent_ == nullptr);
198+
if (child.parent_ == nullptr) {
199+
*this = std::move(child);
200+
return;
201+
}
202+
CHECK(child.parent_ == this);
203+
total_bits_ = child.total_bits_;
204+
total_cells_ = child.total_cells_;
205+
dict_ = std::move(child.dict_);
206+
dict_up_to_date_ = child.dict_up_to_date_;
207+
roots_ = std::move(child.roots_);
208+
for (auto& [hash, e] : child.cache_) {
209+
cache_[hash] = std::move(e);
210+
}
211+
}
212+
213+
AccountStorageStat::Entry& AccountStorageStat::get_entry(const Ref<vm::Cell>& cell) {
214+
Entry& e = cache_[cell->get_hash()];
215+
if (e.inited) {
216+
return e;
217+
}
218+
if (parent_) {
219+
auto it = parent_->cache_.find(cell->get_hash());
220+
if (it != parent_->cache_.end()) {
221+
CHECK(it->second.inited);
222+
e = it->second;
223+
return e;
224+
}
225+
}
226+
e.inited = true;
227+
e.hash = cell->get_hash();
228+
return e;
229+
}
230+
231+
td::Status AccountStorageStat::fetch_from_dict(Entry& e) {
232+
if (e.exists_known && e.refcnt && (!e.exists || e.max_merkle_depth)) {
233+
return td::Status::OK();
234+
}
235+
auto cs = dict_.lookup(e.hash.as_bitslice());
236+
if (cs.is_null()) {
237+
e.exists = false;
238+
e.refcnt = 0;
239+
} else {
240+
if (cs->size_ext() != 32 + 2) {
241+
return td::Status::Error(PSTRING() << "invalid record for cell " << e.hash.to_hex());
242+
}
243+
e.exists = true;
244+
e.refcnt = (td::uint32)cs.write().fetch_ulong(32);
245+
e.max_merkle_depth = (td::uint32)cs.write().fetch_ulong(2);
246+
if (e.refcnt.value() == 0) {
247+
return td::Status::Error(PSTRING() << "invalid refcnt=0 for cell " << e.hash.to_hex());
248+
}
249+
}
250+
e.exists_known = true;
251+
return td::Status::OK();
252+
}
253+
254+
td::Status AccountStorageStat::finalize_entry(Entry& e) {
255+
if (e.refcnt_diff == 0) {
256+
return td::Status::OK();
257+
}
258+
TRY_STATUS(fetch_from_dict(e));
259+
e.refcnt.value() += e.refcnt_diff;
260+
e.dict_refcnt_diff += e.refcnt_diff;
261+
e.refcnt_diff = 0;
262+
if (e.refcnt.value() == 0) {
263+
if (!e.size_bits) {
264+
return td::Status::Error(PSTRING() << "Failed to store entry " << e.hash.to_hex() << " : unknown cell bits");
265+
}
266+
--total_cells_;
267+
total_bits_ -= e.size_bits.value();
268+
e.exists = false;
269+
} else {
270+
if (!e.exists) {
271+
if (!e.size_bits) {
272+
return td::Status::Error(PSTRING() << "Failed to store entry " << e.hash.to_hex() << " : unknown cell bits");
273+
}
274+
++total_cells_;
275+
total_bits_ += e.size_bits.value();
276+
}
277+
e.exists = true;
278+
if (!e.max_merkle_depth) {
279+
return td::Status::Error(PSTRING() << "Failed to store entry " << e.hash.to_hex() << " : unknown merkle depth");
280+
}
281+
}
282+
return td::Status::OK();
283+
}
284+
285+
} // namespace block

0 commit comments

Comments
 (0)