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