Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
account_query_db.cpp
Go to the documentation of this file.
2
6
7#include <boost/multi_index_container.hpp>
8#include <boost/multi_index/ordered_index.hpp>
9#include <boost/multi_index/composite_key.hpp>
10#include <boost/multi_index/member.hpp>
11
12#include <boost/bimap.hpp>
13#include <boost/bimap/multiset_of.hpp>
14#include <boost/bimap/set_of.hpp>
15
16#include <shared_mutex>
17
18using namespace sysio;
19using namespace sysio::chain::literals;
20using namespace boost::multi_index;
21using namespace boost::bimaps;
22
23namespace {
28 struct permission_info {
29 // indexed data
30 chain::name owner;
32 uint32_t last_updated_height;
33
34 // un-indexed data
35 uint32_t threshold;
36
37 using cref = std::reference_wrapper<const permission_info>;
38 };
39
40 struct by_owner_name;
41 struct by_last_updated_height;
42
46 using permission_info_index_t = multi_index_container<
47 permission_info,
48 indexed_by<
49 ordered_unique<
50 tag<by_owner_name>,
51 composite_key<permission_info,
52 member<permission_info, chain::name, &permission_info::owner>,
53 member<permission_info, chain::name, &permission_info::name>
54 >
55 >,
56 ordered_non_unique<
57 tag<by_last_updated_height>,
58 member<permission_info, uint32_t, &permission_info::last_updated_height>
59 >
60 >
61 >;
62
69 if (p->action_traces.empty())
70 return false;
71 const auto& act = p->action_traces[0].act;
72 if (act.account != sysio::chain::config::system_account_name || act.name != "onblock"_n ||
73 act.authorization.size() != 1)
74 return false;
75 const auto& auth = act.authorization[0];
76 return auth.actor == sysio::chain::config::system_account_name &&
77 auth.permission == sysio::chain::config::active_name;
78 }
79
80 template<typename T>
81 struct weighted {
82 T value;
83 chain::weight_type weight;
84
85 static weighted lower_bound_for( const T& value ) {
86 return {value, std::numeric_limits<chain::weight_type>::min()};
87 }
88
89 static weighted upper_bound_for( const T& value ) {
90 return {value, std::numeric_limits<chain::weight_type>::max()};
91 }
92 };
93
94 template<typename Output, typename Input>
95 auto make_optional_authorizer(const Input& authorizer) -> std::optional<Output> {
96 if constexpr (std::is_same_v<Input, Output>) {
97 return authorizer;
98 } else {
99 return {};
100 }
101 }
102}
103
104namespace std {
108 template<>
109 struct less<permission_info::cref> {
110 bool operator()( const permission_info::cref& lhs, const permission_info::cref& rhs ) const {
111 return std::uintptr_t(&lhs.get()) < std::uintptr_t(&rhs.get());
112 }
113 };
114
118 template<typename T>
119 struct less<weighted<T>> {
120 bool operator()( const weighted<T>& lhs, const weighted<T>& rhs ) const {
121 return std::tie(lhs.value, lhs.weight) < std::tie(rhs.value, rhs.weight);
122 }
123 };
124
125}
126
127namespace sysio::chain_apis {
135
141 std::unique_lock write_lock(rw_mutex);
142
143 ilog("Building account query DB");
144 auto start = fc::time_point::now();
145 const auto& index = controller.db().get_index<chain::permission_index>().indices().get<by_id>();
146
147 // build a initial time to block number map
148 const auto lib_num = controller.last_irreversible_block_num();
149 const auto head_num = controller.head_block_num();
150
151 for (uint32_t block_num = lib_num + 1; block_num <= head_num; block_num++) {
152 const auto block_p = controller.fetch_block_by_number(block_num);
153 SYS_ASSERT(block_p, chain::plugin_exception, "cannot fetch reversible block ${block_num}, required for account_db initialization", ("block_num", block_num));
154 time_to_block_num.emplace(block_p->timestamp.to_time_point(), block_num);
155 }
156
157 for (const auto& po : index ) {
158 uint32_t last_updated_height = last_updated_time_to_height(po.last_updated);
159 const auto& pi = permission_info_index.emplace( permission_info{ po.owner, po.name, last_updated_height, po.auth.threshold } ).first;
160 add_to_bimaps(*pi, po);
161 }
162 auto duration = fc::time_point::now() - start;
163 ilog("Finished building account query DB in ${sec}", ("sec", (duration.count() / 1'000'000.0 )));
164 }
165
171 void add_to_bimaps( const permission_info& pi, const chain::permission_object& po ) {
172 // For each account, add this permission info's non-owning reference to the bimap for accounts
173 for (const auto& a : po.auth.accounts) {
174 name_bimap.insert(name_bimap_t::value_type {{a.permission, a.weight}, pi});
175 }
176
177 // for each key, add this permission info's non-owning reference to the bimap for keys
178 for (const auto& k: po.auth.keys) {
179 chain::public_key_type key = k.key;
180 key_bimap.insert(key_bimap_t::value_type {{std::move(key), k.weight}, pi});
181 }
182 }
183
188 void remove_from_bimaps( const permission_info& pi ) {
189 // remove all entries from the name bimap that refer to this permission_info's reference
190 const auto name_range = name_bimap.right.equal_range(pi);
191 name_bimap.right.erase(name_range.first, name_range.second);
192
193 // remove all entries from the key bimap that refer to this permission_info's reference
194 const auto key_range = key_bimap.right.equal_range(pi);
195 key_bimap.right.erase(key_range.first, key_range.second);
196 }
197
199 std::shared_lock read_lock(rw_mutex);
200 const auto bnum = bsp->block->block_num();
201 const auto& index = permission_info_index.get<by_last_updated_height>();
202
203 if (index.empty()) {
204 return false;
205 } else {
206 const auto& pi = (*index.rbegin());
207 if (pi.last_updated_height < bnum) {
208 return false;
209 }
210 }
211
212 return true;
213 }
214
216 const auto lib_num = controller.last_irreversible_block_num();
217 const auto lib_time = controller.last_irreversible_block_time();
218
219 uint32_t last_updated_height = lib_num;
220 if (last_updated > lib_time) {
221 const auto iter = time_to_block_num.find(last_updated);
222 SYS_ASSERT(iter != time_to_block_num.end(), chain::plugin_exception, "invalid block time encountered in on-chain accounts ${time}", ("time", last_updated));
223 last_updated_height = iter->second;
224 }
225
226 return last_updated_height;
227 }
228
238 const auto bnum = bsp->block->block_num();
239 auto& index = permission_info_index.get<by_last_updated_height>();
240 const auto& permission_by_owner = controller.db().get_index<chain::permission_index>().indices().get<chain::by_owner>();
241
242 // roll back time-map
243 auto time_iter = time_to_block_num.rbegin();
244 while (time_iter != time_to_block_num.rend() && time_iter->second >= bnum) {
245 time_iter = decltype(time_iter){time_to_block_num.erase( std::next(time_iter).base() )};
246 }
247
248 auto curr_iter = index.rbegin();
249 while (!index.empty()) {
250 if (curr_iter == index.rend()) {
251 break;
252 }
253
254 const auto& pi = (*curr_iter);
255 if (pi.last_updated_height < bnum) {
256 break;
257 }
258
259 // remove this entry from the bimaps
261
262 auto itr = permission_by_owner.find(std::make_tuple(pi.owner, pi.name));
263 if (itr == permission_by_owner.end()) {
264 // this permission does not exist at this point in the chains history
265 curr_iter = decltype(curr_iter)( index.erase(index.iterator_to(pi)) );
266 } else {
267 const auto& po = *itr;
268
269 uint32_t last_updated_height = po.last_updated == bsp->header.timestamp ? bsp->block_num : last_updated_time_to_height(po.last_updated);
270
271 index.modify(index.iterator_to(pi), [&po, last_updated_height](auto& mutable_pi) {
272 mutable_pi.last_updated_height = last_updated_height;
273 mutable_pi.threshold = po.auth.threshold;
274 });
275 add_to_bimaps(pi, po);
276 ++curr_iter;
277 }
278 }
279 }
280
287 if( !trace->receipt ) return;
288 // include only executed transactions; soft_fail included so that onerror (and any inlines via onerror) are included
289 if((trace->receipt->status != chain::transaction_receipt_header::executed &&
290 trace->receipt->status != chain::transaction_receipt_header::soft_fail)) {
291 return;
292 }
293 if( is_onblock( trace )) {
294 onblock_trace.emplace( trace );
295 } else if( trace->failed_dtrx_trace ) {
296 cached_trace_map[trace->failed_dtrx_trace->id] = trace;
297 } else {
298 cached_trace_map[trace->id] = trace;
299 }
300 }
301
302 using permission_set_t = std::set<chain::permission_level>;
309 permission_set_t updated;
310 permission_set_t deleted;
311
316 auto process_trace = [&](const chain::transaction_trace_ptr& trace) {
317 for( const auto& at : trace->action_traces ) {
318 if (std::tie(at.receiver, at.act.account) != std::tie(chain::config::system_account_name,chain::config::system_account_name)) {
319 continue;
320 }
321
322 if (at.act.name == chain::updateauth::get_name()) {
323 auto data = at.act.data_as<chain::updateauth>();
324 auto itr = updated.emplace(chain::permission_level{data.account, data.permission}).first;
325 deleted.erase(*itr);
326 } else if (at.act.name == chain::deleteauth::get_name()) {
327 auto data = at.act.data_as<chain::deleteauth>();
328 auto itr = deleted.emplace(chain::permission_level{data.account, data.permission}).first;
329 updated.erase(*itr);
330 } else if (at.act.name == chain::newaccount::get_name()) {
331 auto data = at.act.data_as<chain::newaccount>();
332 updated.emplace(chain::permission_level{data.name, "owner"_n});
333 updated.emplace(chain::permission_level{data.name, "active"_n});
334 }
335 }
336 };
337
338 if( onblock_trace )
339 process_trace(*onblock_trace);
340
341 for( const auto& r : bsp->block->transactions ) {
343 if( std::holds_alternative<chain::transaction_id_type>(r.trx)) {
344 id = std::get<chain::transaction_id_type>(r.trx);
345 } else {
346 id = std::get<chain::packed_transaction>(r.trx).id();
347 }
348
349 const auto it = cached_trace_map.find( id );
350 if( it != cached_trace_map.end() ) {
351 process_trace( it->second );
352 }
353 }
354
355 return std::make_tuple(std::move(updated), std::move(deleted), is_rollback_required(bsp));
356 }
357
364 permission_set_t updated;
365 permission_set_t deleted;
366 bool rollback_required = false;
367
368 std::tie(updated, deleted, rollback_required) = commit_block_prelock(bsp);
369
370 // optimistic skip of locking section if there is nothing to do
371 if (!updated.empty() || !deleted.empty() || rollback_required) {
372 std::unique_lock write_lock(rw_mutex);
373
375
376 // insert this blocks time into the time map
377 time_to_block_num.emplace(bsp->header.timestamp, bsp->block_num);
378
379 const auto bnum = bsp->block_num;
380 auto& index = permission_info_index.get<by_owner_name>();
381 const auto& permission_by_owner = controller.db().get_index<chain::permission_index>().indices().get<chain::by_owner>();
382
383 // for each updated permission, find the new values and update the account query db
384 for (const auto& up: updated) {
385 auto key = std::make_tuple(up.actor, up.permission);
386 auto source_itr = permission_by_owner.find(key);
387 SYS_ASSERT(source_itr != permission_by_owner.end(), chain::plugin_exception, "chain data is missing");
388 auto itr = index.find(key);
389 if (itr == index.end()) {
390 const auto& po = *source_itr;
391 itr = index.emplace(permission_info{ po.owner, po.name, bnum, po.auth.threshold }).first;
392 } else {
393 remove_from_bimaps(*itr);
394 index.modify(itr, [&](auto& mutable_pi){
395 mutable_pi.last_updated_height = bnum;
396 mutable_pi.threshold = source_itr->auth.threshold;
397 });
398 }
399
400 add_to_bimaps(*itr, *source_itr);
401 }
402
403 // for all deleted permissions, process their removal from the account query DB
404 for (const auto& dp: deleted) {
405 auto key = std::make_tuple(dp.actor, dp.permission);
406 auto itr = index.find(key);
407 if (itr != index.end()) {
408 remove_from_bimaps(*itr);
409 index.erase(itr);
410 }
411 }
412 }
413
414 // drop any unprocessed cached traces
415 cached_trace_map.clear();
416 onblock_trace.reset();
417 }
418
421 std::shared_lock read_lock(rw_mutex);
422
424 result_t result;
425
426 // deduplicate inputs
427 auto account_set = std::set<chain::permission_level>(args.accounts.begin(), args.accounts.end());
428 const auto key_set = std::set<chain::public_key_type>(args.keys.begin(), args.keys.end());
429
433 auto push_results = [&result](const auto& begin, const auto& end) {
434 for (auto itr = begin; itr != end; ++itr) {
435 const auto& pi = itr->second.get();
436 const auto& authorizer = itr->first.value;
437 auto weight = itr->first.weight;
438
439 result.accounts.emplace_back(result_t::account_result{
440 pi.owner,
441 pi.name,
442 make_optional_authorizer<chain::permission_level>(authorizer),
443 make_optional_authorizer<chain::public_key_type>(authorizer),
444 weight,
445 pi.threshold
446 });
447 }
448 };
449
450
451 for (const auto& a: account_set) {
452 if (a.permission.empty()) {
453 // empty permission is a wildcard
454 // construct a range between the lower bound of the given account and the lower bound of the
455 // next possible account name
456 const auto begin = name_bimap.left.lower_bound(weighted<chain::permission_level>::lower_bound_for({a.actor, a.permission}));
457 const auto next_account_name = chain::name(a.actor.to_uint64_t() + 1);
458 const auto end = name_bimap.left.lower_bound(weighted<chain::permission_level>::lower_bound_for({next_account_name, a.permission}));
459 push_results(begin, end);
460 } else {
461 // construct a range of all possible weights for an account/permission pair
462 const auto p = chain::permission_level{a.actor, a.permission};
463 const auto begin = name_bimap.left.lower_bound(weighted<chain::permission_level>::lower_bound_for(p));
464 const auto end = name_bimap.left.upper_bound(weighted<chain::permission_level>::upper_bound_for(p));
465 push_results(begin, end);
466 }
467 }
468
469 for (const auto& k: key_set) {
470 // construct a range of all possible weights for a key
471 const auto begin = key_bimap.left.lower_bound(weighted<chain::public_key_type>::lower_bound_for(k));
472 const auto end = key_bimap.left.upper_bound(weighted<chain::public_key_type>::upper_bound_for(k));
473 push_results(begin, end);
474 }
475
476 return result;
477 }
478
482 using cached_trace_map_t = std::map<chain::transaction_id_type, chain::transaction_trace_ptr>;
483 using onblock_trace_t = std::optional<chain::transaction_trace_ptr>;
484
488
489 using time_map_t = std::map<fc::time_point, uint32_t>;
491
492
493
494 using name_bimap_t = bimap<multiset_of<weighted<chain::permission_level>>, multiset_of<permission_info::cref>>;
495 using key_bimap_t = bimap<multiset_of<weighted<chain::public_key_type>>, multiset_of<permission_info::cref>>;
496
497 /*
498 * The structures below are shared between the writing thread and the reading thread(s) and must be protected
499 * by the `rw_mutex`
500 */
501 permission_info_index_t permission_info_index;
504
505 mutable std::shared_mutex rw_mutex;
506 };
507
509 :_impl(std::make_unique<account_query_db_impl>(controller))
510 {
511 _impl->build_account_query_map();
512 }
513
515 account_query_db & account_query_db::operator=(account_query_db &&) = default;
516
518 try {
519 _impl->cache_transaction_trace(trace);
520 } FC_LOG_AND_DROP(("ACCOUNT DB cache_transaction_trace ERROR"));
521 }
522
524 try {
525 _impl->commit_block(block);
526 } FC_LOG_AND_DROP(("ACCOUNT DB commit_block ERROR"));
527 }
528
532
533}
const mie::Vuint & p
Definition bn.cpp:27
const mie::Vuint & r
Definition bn.cpp:28
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
const generic_index< MultiIndexType > & get_index() const
static time_point now()
Definition time.cpp:14
const chainbase::database & db() const
signed_block_ptr fetch_block_by_number(uint32_t block_num) const
uint32_t head_block_num() const
uint32_t last_irreversible_block_num() const
time_point last_irreversible_block_time() const
get_accounts_by_authorizers_result get_accounts_by_authorizers(const get_accounts_by_authorizers_params &args) const
account_query_db & operator=(account_query_db &&)
account_query_db(const class sysio::chain::controller &chain)
void commit_block(const chain::block_state_ptr &block)
void cache_transaction_trace(const chain::transaction_trace_ptr &trace)
uint64_t id
Definition code_cache.cpp:0
#define FC_LOG_AND_DROP(...)
#define ilog(FORMAT,...)
Definition logger.hpp:118
Definition name.hpp:106
chainbase::shared_multi_index_container< permission_object, indexed_by< ordered_unique< tag< by_id >, member< permission_object, permission_object::id_type, &permission_object::id > >, ordered_unique< tag< by_parent >, composite_key< permission_object, member< permission_object, permission_object::id_type, &permission_object::parent >, member< permission_object, permission_object::id_type, &permission_object::id > > >, ordered_unique< tag< by_owner >, composite_key< permission_object, member< permission_object, account_name, &permission_object::owner >, member< permission_object, permission_name, &permission_object::name > > >, ordered_unique< tag< by_name >, composite_key< permission_object, member< permission_object, permission_name, &permission_object::name >, member< permission_object, permission_object::id_type, &permission_object::id > > > > > permission_index
std::shared_ptr< transaction_trace > transaction_trace_ptr
Definition trace.hpp:20
bool is_onblock(const transaction_trace &tt)
Definition trace.hpp:71
std::shared_ptr< block_state > block_state_ptr
uint16_t weight_type
Definition types.hpp:238
#define value
Definition pkcs11.h:157
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1181
#define T(meth, val, expected)
unsigned int uint32_t
Definition stdint.h:126
bool operator()(const permission_info::cref &lhs, const permission_info::cref &rhs) const
bool operator()(const weighted< T > &lhs, const weighted< T > &rhs) const
static action_name get_name()
Immutable except for fc::from_variant.
Definition name.hpp:43
static action_name get_name()
shared_vector< permission_level_weight > accounts
shared_vector< shared_key_weight > keys
@ executed
succeed, no error handler executed
Definition block.hpp:14
@ soft_fail
objectively failed (not executed), error handler executed
Definition block.hpp:15
static action_name get_name()
const chain::controller & controller
the controller to read data from
bool is_rollback_required(const chain::block_state_ptr &bsp) const
account_query_db::get_accounts_by_authorizers_result get_accounts_by_authorizers(const account_query_db::get_accounts_by_authorizers_params &args) const
uint32_t last_updated_time_to_height(const fc::time_point &last_updated)
void rollback_to_before(const chain::block_state_ptr &bsp)
onblock_trace_t onblock_trace
temporary cache of on_block trace
name_bimap_t name_bimap
many:many bimap of names:permission_infos
account_query_db_impl(const chain::controller &controller)
std::set< chain::permission_level > permission_set_t
void add_to_bimaps(const permission_info &pi, const chain::permission_object &po)
void remove_from_bimaps(const permission_info &pi)
void commit_block(const chain::block_state_ptr &bsp)
auto commit_block_prelock(const chain::block_state_ptr &bsp) const
std::map< fc::time_point, uint32_t > time_map_t
permission_info_index_t permission_info_index
multi-index that holds ephemeral indices
std::optional< chain::transaction_trace_ptr > onblock_trace_t
bimap< multiset_of< weighted< chain::permission_level > >, multiset_of< permission_info::cref > > name_bimap_t
cached_trace_map_t cached_trace_map
temporary cache of uncommitted traces
std::shared_mutex rw_mutex
mutex for read/write locking on the Multi-index and bimaps
key_bimap_t key_bimap
many:many bimap of keys:permission_infos
bimap< multiset_of< weighted< chain::public_key_type > >, multiset_of< permission_info::cref > > key_bimap_t
void cache_transaction_trace(const chain::transaction_trace_ptr &trace)
std::map< chain::transaction_id_type, chain::transaction_trace_ptr > cached_trace_map_t