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 ), roots_(std::move(roots)), total_cells_(total_cells), total_bits_(total_bits) {
27
+ }
28
+
29
+ AccountStorageStat::AccountStorageStat (const AccountStorageStat& other)
30
+ : AccountStorageStat(other.dict_.get_root_cell(), other.roots_, other.total_cells_, other.total_bits_) {
31
+ }
32
+
33
+ AccountStorageStat::AccountStorageStat (AccountStorageStat&& other)
34
+ : AccountStorageStat(other.dict_.get_root_cell(), std::move(other.roots_), other.total_cells_, other.total_bits_) {
35
+ cache_ = std::move (other.cache_ );
36
+ }
37
+
38
+ AccountStorageStat& AccountStorageStat::operator =(const AccountStorageStat& other) {
39
+ dict_ = other.dict_ ;
40
+ total_cells_ = other.total_cells_ ;
41
+ total_bits_ = other.total_bits_ ;
42
+ roots_ = other.roots_ ;
43
+ cache_ = {};
44
+ return *this ;
45
+ }
46
+
47
+ AccountStorageStat& AccountStorageStat::operator =(AccountStorageStat&& other) {
48
+ dict_ = std::move (other.dict_ );
49
+ total_cells_ = other.total_cells_ ;
50
+ total_bits_ = other.total_bits_ ;
51
+ roots_ = std::move (other.roots_ );
52
+ cache_ = std::move (other.cache_ );
53
+ return *this ;
54
+ }
55
+
56
+ td::Result<AccountStorageStat::CellInfo> AccountStorageStat::add_root (const Ref<vm::Cell>& cell) {
57
+ roots_.push_back (cell);
58
+ return add_cell (cell);
59
+ }
60
+
61
+ td::Status AccountStorageStat::remove_root (const Ref<vm::Cell>& cell) {
62
+ auto it = std::find_if (roots_.begin (), roots_.end (),
63
+ [&](const Ref<vm::Cell>& c) { return c->get_hash () == cell->get_hash (); });
64
+ if (it == roots_.end ()) {
65
+ return td::Status::Error (PSTRING () << " no such root " << cell->get_hash ().to_hex ());
66
+ }
67
+ roots_.erase (it);
68
+ return remove_cell (cell);
69
+ }
70
+
71
+ td::Result<AccountStorageStat::CellInfo> AccountStorageStat::replace_roots (std::vector<Ref<vm::Cell>> new_roots) {
72
+ std::vector<Ref<vm::Cell>> old_roots = roots_;
73
+ td::uint32 max_merkle_depth = 0 ;
74
+ for (const Ref<vm::Cell>& root : new_roots) {
75
+ if (root.is_null ()) {
76
+ continue ;
77
+ }
78
+ TRY_RESULT (info, add_root (root));
79
+ max_merkle_depth = std::max (max_merkle_depth, info.max_merkle_depth );
80
+ }
81
+ for (const Ref<vm::Cell>& root : old_roots) {
82
+ TRY_STATUS (remove_root (root));
83
+ }
84
+ return CellInfo{max_merkle_depth};
85
+ }
86
+
87
+ td::Result<AccountStorageStat::CellInfo> AccountStorageStat::add_cell (const Ref<vm::Cell>& cell) {
88
+ Entry& e = get_entry (cell);
89
+ ++e.refcnt ;
90
+ if (e.refcnt == 0 ) {
91
+ return td::Status::Error (PSTRING () << " cell " << cell->get_hash ().to_hex () << " : refcnt overflow" );
92
+ }
93
+ if (e.refcnt != 1 ) {
94
+ update_dict (e);
95
+ return CellInfo{e.max_merkle_depth };
96
+ }
97
+ td::uint32 max_merkle_depth = 0 ;
98
+ vm::CellSlice cs{vm::NoVm{}, cell};
99
+ ++total_cells_;
100
+ total_bits_ += cs.size ();
101
+ for (unsigned i = 0 ; i < cs.size_refs (); ++i) {
102
+ TRY_RESULT (info, add_cell (cs.prefetch_ref (i)));
103
+ max_merkle_depth = std::max (max_merkle_depth, info.max_merkle_depth );
104
+ }
105
+ if (cs.special_type () == vm::CellTraits::SpecialType::MerkleProof ||
106
+ cs.special_type () == vm::CellTraits::SpecialType::MerkleUpdate) {
107
+ ++max_merkle_depth;
108
+ }
109
+ max_merkle_depth = std::min (max_merkle_depth, MERKLE_DEPTH_LIMIT);
110
+ Entry& e2 = get_entry (cell);
111
+ e2 .max_merkle_depth = max_merkle_depth;
112
+ update_dict (e2 );
113
+ return CellInfo{max_merkle_depth};
114
+ }
115
+
116
+ td::Status AccountStorageStat::remove_cell (const Ref<vm::Cell>& cell) {
117
+ Entry& e = get_entry (cell);
118
+ if (e.refcnt == 0 ) {
119
+ return td::Status::Error (PSTRING () << " cell " << cell->get_hash ().to_hex () << " is not in the dict" );
120
+ }
121
+ --e.refcnt ;
122
+ update_dict (e);
123
+ if (e.refcnt != 0 ) {
124
+ return td::Status::OK ();
125
+ }
126
+ vm::CellSlice cs{vm::NoVm{}, std::move (cell)};
127
+ if (total_cells_ == 0 || total_bits_ < cs.size ()) {
128
+ return td::Status::Error (" total_cell/total_bits becomes negative" );
129
+ }
130
+ --total_cells_;
131
+ total_bits_ -= cs.size ();
132
+ for (unsigned i = 0 ; i < cs.size_refs (); ++i) {
133
+ TRY_STATUS (remove_cell (cs.prefetch_ref (i)));
134
+ }
135
+ return td::Status::OK ();
136
+ }
137
+
138
+ bool AccountStorageStat::Entry::serialize (vm::CellBuilder& cb) const {
139
+ return cb.store_long_bool (refcnt, 32 ) && cb.store_long_bool (max_merkle_depth, 2 );
140
+ }
141
+
142
+ void AccountStorageStat::Entry::fetch (Ref<vm::CellSlice> cs) {
143
+ if (cs.is_null ()) {
144
+ refcnt = max_merkle_depth = 0 ;
145
+ } else {
146
+ refcnt = (td::uint32)cs.write ().fetch_ulong (32 );
147
+ max_merkle_depth = (td::uint32)cs.write ().fetch_ulong (2 );
148
+ }
149
+ }
150
+
151
+ AccountStorageStat::Entry& AccountStorageStat::get_entry (const Ref<vm::Cell>& cell) {
152
+ return cache_.apply (cell->get_hash ().as_slice (), [&](Entry& e) {
153
+ if (e.inited ) {
154
+ return ;
155
+ }
156
+ e.inited = true ;
157
+ e.hash = cell->get_hash ();
158
+ e.fetch (dict_.lookup (e.hash .as_bitslice ()));
159
+ });
160
+ }
161
+
162
+ void AccountStorageStat::update_dict (const Entry& e) {
163
+ if (e.refcnt == 0 ) {
164
+ dict_.lookup_delete (e.hash .as_bitslice ());
165
+ } else {
166
+ vm::CellBuilder cb;
167
+ CHECK (e.serialize (cb));
168
+ dict_.set_builder (e.hash .as_bitslice (), cb);
169
+ }
170
+ }
171
+
172
+ } // namespace block
0 commit comments