chain_controller.cpp 64.4 KB
Newer Older
A
Andrianto Lie 已提交
1 2 3
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
N
Nathan Hourt 已提交
4
 */
N
Nathan Hourt 已提交
5

6
#include <eos/chain/chain_controller.hpp>
N
Nathan Hourt 已提交
7 8 9

#include <eos/chain/block_summary_object.hpp>
#include <eos/chain/global_property_object.hpp>
N
Nathan Hourt 已提交
10 11
#include <eos/chain/key_value_object.hpp>
#include <eos/chain/action_objects.hpp>
12
#include <eos/chain/generated_transaction_object.hpp>
N
Nathan Hourt 已提交
13 14
#include <eos/chain/transaction_object.hpp>
#include <eos/chain/producer_object.hpp>
15
#include <eos/chain/permission_link_object.hpp>
N
Nathan Hourt 已提交
16
#include <eos/chain/authority_checker.hpp>
17
#include <eos/chain/rate_limiting_object.hpp>
N
Nathan Hourt 已提交
18

19 20
#include <eos/chain/wasm_interface.hpp>

21 22
#include <eos/types/native.hpp>
#include <eos/types/generated.hpp>
23
#include <eos/types/AbiSerializer.hpp>
24

25
#include <eos/utilities/rand.hpp>
26

N
Nathan Hourt 已提交
27
#include <fc/smart_ref_impl.hpp>
N
Nathan Hourt 已提交
28 29 30
#include <fc/uint128.hpp>
#include <fc/crypto/digest.hpp>

31
#include <boost/range/algorithm/copy.hpp>
N
Nathan Hourt 已提交
32
#include <boost/range/algorithm_ext/is_sorted.hpp>
33
#include <boost/range/adaptor/transformed.hpp>
N
Nathan Hourt 已提交
34
#include <boost/range/adaptor/map.hpp>
N
Nathan Hourt 已提交
35 36 37 38

#include <fstream>
#include <functional>
#include <iostream>
39
#include <chrono>
N
Nathan Hourt 已提交
40 41 42

namespace eos { namespace chain {

43
bool chain_controller::is_known_block(const block_id_type& id)const
N
Nathan Hourt 已提交
44
{
45
   return _fork_db.is_known_block(id) || _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
46 47 48 49 50 51
}
/**
 * Only return true *if* the transaction has not expired or been invalidated. If this
 * method is called with a VERY old transaction we will return false, they should
 * query things by blocks if they are that old.
 */
52
bool chain_controller::is_known_transaction(const transaction_id_type& id)const
N
Nathan Hourt 已提交
53
{
54
   const auto& trx_idx = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
55 56 57
   return trx_idx.find( id ) != trx_idx.end();
}

58
block_id_type chain_controller::get_block_id_for_num(uint32_t block_num)const
N
Nathan Hourt 已提交
59
{ try {
60 61
   if (const auto& block = fetch_block_by_number(block_num))
      return block->id();
N
Nathan Hourt 已提交
62

63 64 65
   FC_THROW_EXCEPTION(unknown_block_exception, "Could not find block");
} FC_CAPTURE_AND_RETHROW((block_num)) }

66
optional<signed_block> chain_controller::fetch_block_by_id(const block_id_type& id)const
N
Nathan Hourt 已提交
67
{
68 69
   auto b = _fork_db.fetch_block(id);
   if(b) return b->data;
70
   return _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
71 72
}

73
optional<signed_block> chain_controller::fetch_block_by_number(uint32_t num)const
N
Nathan Hourt 已提交
74
{
75
   if (const auto& block = _block_log.read_block_by_num(num))
76 77
      return *block;

N
Nathan Hourt 已提交
78
   // Not in _block_log, so it must be since the last irreversible block. Grab it from _fork_db instead
79 80 81 82 83 84 85 86
   if (num <= head_block_num()) {
      auto block = _fork_db.head();
      while (block && block->num > num)
         block = block->prev.lock();
      if (block && block->num == num)
         return block->data;
   }

N
Nathan Hourt 已提交
87 88 89
   return optional<signed_block>();
}

90
const SignedTransaction& chain_controller::get_recent_transaction(const transaction_id_type& trx_id) const
N
Nathan Hourt 已提交
91
{
92
   auto& index = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
93 94 95 96 97
   auto itr = index.find(trx_id);
   FC_ASSERT(itr != index.end());
   return itr->trx;
}

98
std::vector<block_id_type> chain_controller::get_block_ids_on_fork(block_id_type head_of_fork) const
N
Nathan Hourt 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
{
  pair<fork_database::branch_type, fork_database::branch_type> branches = _fork_db.fetch_branch_from(head_block_id(), head_of_fork);
  if( !((branches.first.back()->previous_id() == branches.second.back()->previous_id())) )
  {
     edump( (head_of_fork)
            (head_block_id())
            (branches.first.size())
            (branches.second.size()) );
     assert(branches.first.back()->previous_id() == branches.second.back()->previous_id());
  }
  std::vector<block_id_type> result;
  for (const item_ptr& fork_block : branches.second)
    result.emplace_back(fork_block->id);
  result.emplace_back(branches.first.back()->previous_id());
  return result;
}

116 117 118 119 120 121 122
const GeneratedTransaction& chain_controller::get_generated_transaction( const generated_transaction_id_type& id ) const {
   auto& index = _db.get_index<generated_transaction_multi_index, generated_transaction_object::by_trx_id>();
   auto itr = index.find(id);
   FC_ASSERT(itr != index.end());
   return itr->trx;
}

N
Nathan Hourt 已提交
123 124 125 126 127 128
/**
 * Push block "may fail" in which case every partial change is unwound.  After
 * push block is successful the block is appended to the chain database on disk.
 *
 * @return true if we switched forks as a result of this push.
 */
129
bool chain_controller::push_block(const signed_block& new_block, uint32_t skip)
D
Daniel Larimer 已提交
130 131 132
{ try {
   return with_skip_flags( skip, [&](){ 
      return without_pending_transactions( [&]() {
133
         return _db.with_write_lock( [&]() {
134 135
            return _push_block(new_block);
         } );
N
Nathan Hourt 已提交
136 137
      });
   });
138
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
139

140
bool chain_controller::_push_block(const signed_block& new_block)
N
Nathan Hourt 已提交
141
{ try {
N
Nathan Hourt 已提交
142
   uint32_t skip = _skip_flags;
143
   if (!(skip&skip_fork_db)) {
N
Nathan Hourt 已提交
144
      /// TODO: if the block is greater than the head block and before the next maintenance interval
N
Nathan Hourt 已提交
145 146 147 148
      // verify that the block signer is in the current set of active producers.

      shared_ptr<fork_item> new_head = _fork_db.push_block(new_block);
      //If the head block from the longest chain does not build off of the current head, we need to switch forks.
149
      if (new_head->data.previous != head_block_id()) {
N
Nathan Hourt 已提交
150 151
         //If the newly pushed block is the same height as head, we get head back in new_head
         //Only switch forks if new_head is actually higher than head
152 153
         if (new_head->data.block_num() > head_block_num()) {
            wlog("Switching to fork: ${id}", ("id",new_head->data.id()));
N
Nathan Hourt 已提交
154 155 156
            auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());

            // pop blocks until we hit the forked block
157
            while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
158 159 160
               pop_block();

            // push all blocks on the new fork
161 162
            for (auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr) {
                ilog("pushing blocks from fork ${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->data.id()));
N
Nathan Hourt 已提交
163 164
                optional<fc::exception> except;
                try {
165
                   auto session = _db.start_undo_session(true);
166
                   apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
167 168
                   session.push();
                }
169 170 171
                catch (const fc::exception& e) { except = e; }
                if (except) {
                   wlog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
N
Nathan Hourt 已提交
172
                   // remove the rest of branches.first from the fork_db, those blocks are invalid
173 174
                   while (ritr != branches.first.rend()) {
                      _fork_db.remove((*ritr)->data.id());
N
Nathan Hourt 已提交
175 176
                      ++ritr;
                   }
177
                   _fork_db.set_head(branches.second.front());
N
Nathan Hourt 已提交
178 179

                   // pop all blocks from the bad fork
180
                   while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
181 182 183
                      pop_block();

                   // restore all blocks from the good fork
184
                   for (auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr) {
185
                      auto session = _db.start_undo_session(true);
186
                      apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
187 188 189 190 191 192 193 194 195 196 197 198
                      session.push();
                   }
                   throw *except;
                }
            }
            return true;
         }
         else return false;
      }
   }

   try {
199
      auto session = _db.start_undo_session(true);
200
      auto exec_start = std::chrono::high_resolution_clock::now();
N
Nathan Hourt 已提交
201
      apply_block(new_block, skip);
202 203 204 205 206 207
      if( (fc::time_point::now() - new_block.timestamp) < fc::seconds(60) )
      {
         auto exec_stop = std::chrono::high_resolution_clock::now();
         auto exec_ms = std::chrono::duration_cast<std::chrono::milliseconds>(exec_stop - exec_start);      
         size_t trxcount = 0;
         for (const auto& cycle : new_block.cycles)
M
matthew 已提交
208 209
            for (const auto& thread : cycle)
               trxcount += thread.user_input.size();
210 211 212 213 214 215
         ilog( "${producer} #${num} @${time}  | ${trxcount} trx, ${pending} pending, exectime_ms=${extm}", 
            ("producer", new_block.producer) 
            ("time", new_block.timestamp)
            ("num", new_block.block_num())
            ("trxcount", trxcount)
            ("pending", _pending_transactions.size())
M
matthew 已提交
216
            ("extm", exec_ms.count())
217 218
         );
      }
N
Nathan Hourt 已提交
219 220 221 222 223 224 225 226
      session.push();
   } catch ( const fc::exception& e ) {
      elog("Failed to push new block:\n${e}", ("e", e.to_detail_string()));
      _fork_db.remove(new_block.id());
      throw;
   }

   return false;
227
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
228 229 230 231 232 233 234 235 236 237

/**
 * Attempts to push the transaction into the pending queue
 *
 * When called to push a locally generated transaction, set the skip_block_size_check bit on the skip argument. This
 * will allow the transaction to be pushed even if it causes the pending block size to exceed the maximum block size.
 * Although the transaction will probably not propagate further now, as the peers are likely to have their pending
 * queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending
 * queues.
 */
238
ProcessedTransaction chain_controller::push_transaction(const SignedTransaction& trx, uint32_t skip)
N
Nathan Hourt 已提交
239
{ try {
240 241 242
   return with_skip_flags(skip, [&]() {
      return _db.with_write_lock([&]() {
         return _push_transaction(trx);
D
Daniel Larimer 已提交
243
      });
N
Nathan Hourt 已提交
244 245
   });
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
246

247
ProcessedTransaction chain_controller::_push_transaction(const SignedTransaction& trx) {
248 249
   // If this is the first transaction pushed after applying a block, start a new undo session.
   // This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
250
   if (!_pending_tx_session.valid())
251
      _pending_tx_session = _db.start_undo_session(true);
252

253
   auto temp_session = _db.start_undo_session(true);
254
   validate_referenced_accounts(trx);
N
Nathan Hourt 已提交
255
   check_transaction_authorization(trx);
256
   auto pt = apply_transaction(trx);
D
Daniel Larimer 已提交
257
   _pending_transactions.push_back(trx);
N
Nathan Hourt 已提交
258 259 260 261 262 263

   // notify_changed_objects();
   // The transaction applied successfully. Merge its changes into the pending block session.
   temp_session.squash();

   // notify anyone listening to pending transactions
264
   on_pending_transaction(trx); /// TODO move this to apply... ??? why... 
265 266

   return pt;
N
Nathan Hourt 已提交
267 268
}

269
signed_block chain_controller::generate_block(
N
Nathan Hourt 已提交
270
   fc::time_point_sec when,
N
Nathan Hourt 已提交
271
   const AccountName& producer,
N
Nathan Hourt 已提交
272
   const fc::ecc::private_key& block_signing_private_key,
273
   block_schedule::factory scheduler, /* = block_schedule::by_threading_conflits */
N
Nathan Hourt 已提交
274 275 276
   uint32_t skip /* = 0 */
   )
{ try {
N
Nathan Hourt 已提交
277 278
   return with_skip_flags( skip, [&](){
      auto b = _db.with_write_lock( [&](){
279
         return _generate_block( when, producer, block_signing_private_key, scheduler );
D
Daniel Larimer 已提交
280
      });
N
Nathan Hourt 已提交
281 282 283
      push_block(b, skip);
      return b;
   });
D
Daniel Larimer 已提交
284
} FC_CAPTURE_AND_RETHROW( (when) ) }
N
Nathan Hourt 已提交
285

286
signed_block chain_controller::_generate_block(
N
Nathan Hourt 已提交
287
   fc::time_point_sec when,
N
Nathan Hourt 已提交
288
   const AccountName& producer,
289 290
   const fc::ecc::private_key& block_signing_private_key,
   block_schedule::factory scheduler
N
Nathan Hourt 已提交
291 292 293
   )
{
   try {
N
Nathan Hourt 已提交
294
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
295 296
   uint32_t slot_num = get_slot_at_time( when );
   FC_ASSERT( slot_num > 0 );
N
Nathan Hourt 已提交
297 298
   AccountName scheduled_producer = get_scheduled_producer( slot_num );
   FC_ASSERT( scheduled_producer == producer );
N
Nathan Hourt 已提交
299

N
Nathan Hourt 已提交
300
   const auto& producer_obj = get_producer(scheduled_producer);
N
Nathan Hourt 已提交
301 302 303 304

   if( !(skip & skip_producer_signature) )
      FC_ASSERT( producer_obj.signing_key == block_signing_private_key.get_public_key() );

305
  
306 307 308 309 310 311 312 313 314 315 316 317 318 319
   //
   // The following code throws away existing pending_tx_session and
   // rebuilds it by re-applying pending transactions.
   //
   // This rebuild is necessary because pending transactions' validity
   // and semantics may have changed since they were received, because
   // time-based semantics are evaluated based on the current block
   // time.  These changes can only be reflected in the database when
   // the value of the "when" variable is known, which means we need to
   // re-apply pending transactions in this method.
   //
   _pending_tx_session.reset();
   _pending_tx_session = _db.start_undo_session(true);

320
   const auto& generated = _db.get_index<generated_transaction_multi_index, generated_transaction_object::by_status>().equal_range(generated_transaction_object::PENDING);
N
Nathan Hourt 已提交
321

322
   vector<pending_transaction> pending;
B
Bart Wyatt 已提交
323
   std::set<transaction_id_type> invalid_pending;
324 325 326
   pending.reserve(std::distance(generated.first, generated.second) + _pending_transactions.size());
   for (auto iter = generated.first; iter != generated.second; ++iter) {
      const auto& gt = *iter;
B
Bart Wyatt 已提交
327
      pending.emplace_back(std::reference_wrapper<const GeneratedTransaction> {gt.trx});
328 329
   }
   
B
Bart Wyatt 已提交
330 331
   for(const auto& st: _pending_transactions) {
      pending.emplace_back(std::reference_wrapper<const SignedTransaction> {st});
332 333
   }

334
   auto schedule = scheduler(pending, get_global_properties());
N
Nathan Hourt 已提交
335

336 337 338 339 340 341 342 343 344 345 346 347
   signed_block pending_block;
   pending_block.cycles.reserve(schedule.cycles.size());

   size_t invalid_transaction_count = 0;
   size_t valid_transaction_count = 0;

   for (const auto &c : schedule.cycles) {
     cycle block_cycle;
     block_cycle.reserve(c.size());

     for (const auto &t : c) {
       thread block_thread;
348 349 350 351 352 353
       block_thread.user_input.reserve(t.transactions.size());
       block_thread.generated_input.reserve(t.transactions.size());
       for (const auto &trx : t.transactions) {
          try
          {
             auto temp_session = _db.start_undo_session(true);
B
Bart Wyatt 已提交
354 355
             if (trx.contains<std::reference_wrapper<const SignedTransaction>>()) {
                const auto& t = trx.get<std::reference_wrapper<const SignedTransaction>>().get();
B
Bart Wyatt 已提交
356
                validate_referenced_accounts(t);
357
                check_transaction_authorization(t);
358
                auto processed = apply_transaction(t);
359
                block_thread.user_input.emplace_back(processed);
B
Bart Wyatt 已提交
360
             } else if (trx.contains<std::reference_wrapper<const GeneratedTransaction>>()) {
361 362 363
                const auto& t = trx.get<std::reference_wrapper<const GeneratedTransaction>>().get();
                auto processed = apply_transaction(t);
                block_thread.generated_input.emplace_back(processed);
364 365 366 367 368 369 370 371 372 373
             } else {
                FC_THROW_EXCEPTION(tx_scheduling_exception, "Unknown transaction type in block_schedule");
             }
             
             temp_session.squash();
             valid_transaction_count++;
          }
          catch ( const fc::exception& e )
          {
             // Do nothing, transaction will not be re-applied
B
Bart Wyatt 已提交
374
             elog( "Transaction was not processed while generating block due to ${e}", ("e", e) );
B
Bart Wyatt 已提交
375
             if (trx.contains<std::reference_wrapper<const SignedTransaction>>()) {
B
Bart Wyatt 已提交
376
                const auto& t = trx.get<std::reference_wrapper<const SignedTransaction>>().get();
B
Bart Wyatt 已提交
377
                wlog( "The transaction was ${t}", ("t", t ) );
B
Bart Wyatt 已提交
378
                invalid_pending.emplace(t.id());
B
Bart Wyatt 已提交
379 380
             } else if (trx.contains<std::reference_wrapper<const GeneratedTransaction>>()) {
                wlog( "The transaction was ${t}", ("t", trx.get<std::reference_wrapper<const GeneratedTransaction>>().get()) );
381 382 383
             } 
             invalid_transaction_count++;
          }
384
       }
N
Nathan Hourt 已提交
385

386
       if (!(block_thread.generated_input.empty() && block_thread.user_input.empty())) {
387 388
          block_thread.generated_input.shrink_to_fit();
          block_thread.user_input.shrink_to_fit();
389 390 391
          block_cycle.emplace_back(std::move(block_thread));
       }
     }
392

393 394 395
     if (!block_cycle.empty()) {
        pending_block.cycles.emplace_back(std::move(block_cycle));
     }
N
Nathan Hourt 已提交
396
   }
397
   
398
   size_t postponed_tx_count = pending.size() - valid_transaction_count - invalid_transaction_count;
N
Nathan Hourt 已提交
399 400 401 402 403
   if( postponed_tx_count > 0 )
   {
      wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
   }

404 405 406
   if( invalid_transaction_count > 0 )
   {
      wlog( "Postponed ${n} transactions errors when processing", ("n", invalid_transaction_count) );
B
Bart Wyatt 已提交
407 408 409 410 411 412 413 414 415 416 417 418

      // remove pending transactions determined to be bad during scheduling
      if (invalid_pending.size() > 0) {
         for (auto itr = _pending_transactions.begin(); itr != _pending_transactions.end(); ) {
            auto& tx = *itr;
            if (invalid_pending.find(tx.id()) != invalid_pending.end()) {
               itr = _pending_transactions.erase(itr);
            } else {
               ++itr;
            }
         }
      }
419 420
   }

N
Nathan Hourt 已提交
421 422 423 424
   _pending_tx_session.reset();

   // We have temporarily broken the invariant that
   // _pending_tx_session is the result of applying _pending_tx, as
D
Daniel Larimer 已提交
425
   // _pending_transactions now consists of the set of postponed transactions.
N
Nathan Hourt 已提交
426 427 428 429 430 431
   // However, the push_block() call below will re-create the
   // _pending_tx_session.

   pending_block.previous = head_block_id();
   pending_block.timestamp = when;
   pending_block.transaction_merkle_root = pending_block.calculate_merkle_root();
D
Daniel Larimer 已提交
432

433
   pending_block.producer = producer_obj.owner;
N
Nathan Hourt 已提交
434

435 436 437 438 439 440
   // If this block is last in a round, calculate the schedule for the new round
   if (pending_block.block_num() % config::BlocksPerRound == 0) {
      auto new_schedule = _admin->get_next_round(_db);
      pending_block.producer_changes = get_global_properties().active_producers - new_schedule;
   }

N
Nathan Hourt 已提交
441 442 443 444
   if( !(skip & skip_producer_signature) )
      pending_block.sign( block_signing_private_key );

   // TODO:  Move this to _push_block() so session is restored.
445
   /*
N
Nathan Hourt 已提交
446 447 448 449
   if( !(skip & skip_block_size_check) )
   {
      FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
   }
450
   */
N
Nathan Hourt 已提交
451

452
   // push_block( pending_block, skip );
N
Nathan Hourt 已提交
453 454

   return pending_block;
N
Nathan Hourt 已提交
455
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
456 457

/**
N
Nathan Hourt 已提交
458
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
459
 */
460
void chain_controller::pop_block()
N
Nathan Hourt 已提交
461 462 463 464 465 466 467
{ try {
   _pending_tx_session.reset();
   auto head_id = head_block_id();
   optional<signed_block> head_block = fetch_block_by_id( head_id );
   EOS_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );

   _fork_db.pop_block();
468
   _db.undo();
N
Nathan Hourt 已提交
469 470
} FC_CAPTURE_AND_RETHROW() }

471
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
472
{ try {
D
Daniel Larimer 已提交
473
   _pending_transactions.clear();
N
Nathan Hourt 已提交
474 475 476 477 478
   _pending_tx_session.reset();
} FC_CAPTURE_AND_RETHROW() }

//////////////////// private methods ////////////////////

479
void chain_controller::apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
480 481
{
   auto block_num = next_block.block_num();
482 483 484 485 486
   if (_checkpoints.size() && _checkpoints.rbegin()->second != block_id_type()) {
      auto itr = _checkpoints.find(block_num);
      if (itr != _checkpoints.end())
         FC_ASSERT(next_block.id() == itr->second,
                   "Block did not match checkpoint", ("checkpoint",*itr)("block_id",next_block.id()));
N
Nathan Hourt 已提交
487

488
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
489 490
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
N
Nathan Hourt 已提交
491 492 493 494 495 496

   with_applying_block([&] {
      with_skip_flags(skip, [&] {
         _apply_block(next_block);
      });
   });
N
Nathan Hourt 已提交
497 498
}

499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
struct path_cons_list {
   typedef static_variant<int, const char *> head_type;
   typedef const path_cons_list* tail_ref_type;
   typedef optional<tail_ref_type> tail_type;
   
   path_cons_list(int index, const path_cons_list &rest) 
      : head(head_type(index))
      , tail(tail_ref_type(&rest))
   { }

   path_cons_list(const char *path, const path_cons_list &rest) 
      : head(head_type(path))
      , tail(tail_ref_type(&rest))
   { }

   path_cons_list(const char *path) 
      : head(head_type(path))
      , tail()
   { }

   //path_cons_list( path_cons_list && ) = delete;
   
   const head_type head;
   tail_type tail;

   path_cons_list operator() (int index) const {
      return path_cons_list(index, *this);
   }

   path_cons_list operator() (const char *path) const {
      return path_cons_list(path, *this);
   }

   void add_to_stream( std::stringstream& ss ) const {
      if (tail) {
         (*tail)->add_to_stream(ss);
      }

      if (head.contains<int>()) {
         ss << "[" << head.get<int>() << "]";
      } else {
         ss << head.get<const char *>();
      }
   }
};

string resolve_path_string(const path_cons_list& path) {
   std::stringstream ss;
   path.add_to_stream(ss);
   return ss.str();
}

551
template<typename T>
552 553 554 555 556
void check_output(const T& expected, const T& actual, const path_cons_list& path) {
   try {
      EOS_ASSERT((expected == actual), block_tx_output_exception, 
         "expected: ${expected}, actual: ${actual}", ("expected", expected)("actual", actual));
   } FC_RETHROW_EXCEPTIONS(warn, "at: ${path}", ("path", resolve_path_string(path)));
557 558 559
}

template<typename T>
560 561
void check_output(const vector<T>& expected, const vector<T>& actual, const path_cons_list& path) {
   check_output(expected.size(), actual.size(), path(".size()"));
562 563 564
   for(int idx=0; idx < expected.size(); idx++) {
      const auto &expected_element = expected.at(idx);
      const auto &actual_element = actual.at(idx);
565
      check_output(expected_element, actual_element, path(idx));
566 567 568
   }
}

569 570 571 572 573 574 575 576
template<typename T>
void check_output(const fc::optional<T>& expected, const fc::optional<T>& actual, const path_cons_list& path) {
   check_output(expected.valid(), actual.valid(), path(".valid()"));
   if (expected.valid()) {
      check_output(*expected, *actual, path);
   }
}

577
template<>
578 579 580 581 582
void check_output(const types::Bytes& expected, const types::Bytes& actual, const path_cons_list& path) {
  check_output(expected.size(), actual.size(), path(".size()"));
  
  auto cmp_result = std::memcmp(expected.data(), actual.data(), expected.size());
  check_output(cmp_result, 0, path("@memcmp()"));
583 584 585
}

template<>
586 587 588
void check_output(const types::AccountPermission& expected, const types::AccountPermission& actual, const path_cons_list& path) {
   check_output(expected.account, actual.account, path(".account"));
   check_output(expected.permission, actual.permission, path(".permission"));
589 590 591
}

template<>
592 593 594 595 596
void check_output(const types::Message& expected, const types::Message& actual, const path_cons_list& path) {
   check_output(expected.code, actual.code, path(".code"));
   check_output(expected.type, actual.type, path(".type"));
   check_output(expected.authorization, actual.authorization, path(".authorization"));
   check_output(expected.data, actual.data, path(".data"));
597 598 599
}

template<>
600
void check_output(const MessageOutput& expected, const MessageOutput& actual, const path_cons_list& path);
601 602

template<>
603 604 605
void check_output(const NotifyOutput& expected, const NotifyOutput& actual, const path_cons_list& path) {
   check_output(expected.name, actual.name, path(".name"));
   check_output(expected.output, actual.output, path(".output"));
606 607 608
}

template<>
609 610 611 612 613 614
void check_output(const Transaction& expected, const Transaction& actual, const path_cons_list& path) {
   check_output(expected.refBlockNum, actual.refBlockNum, path(".refBlockNum"));
   check_output(expected.refBlockPrefix, actual.refBlockPrefix, path(".refBlockPrefix"));
   check_output(expected.expiration, actual.expiration, path(".expiration"));
   check_output(expected.scope, actual.scope, path(".scope"));
   check_output(expected.messages, actual.messages, path(".messages"));
615 616 617 618
}


template<>
619
void check_output(const InlineTransaction& expected, const InlineTransaction& actual, const path_cons_list& path) {
620 621
   check_output<Transaction>(expected, actual, path);
   check_output(expected.output, actual.output, path(".output"));
622 623 624
}

template<>
625 626 627
void check_output(const GeneratedTransaction& expected, const GeneratedTransaction& actual, const path_cons_list& path) {
   check_output(expected.id, actual.id, path(".id"));
   check_output<Transaction>(expected, actual, path);
628 629 630
}

template<>
631 632
void check_output(const MessageOutput& expected, const MessageOutput& actual, const path_cons_list& path) {
   check_output(expected.notify, actual.notify, path(".notify"));
633 634
   check_output(expected.inline_transaction, actual.inline_transaction, path(".inline_transaction"));
   check_output(expected.deferred_transactions, actual.deferred_transactions, path(".deferred_transactions"));
635 636 637
}

template<typename T>
638
void chain_controller::check_transaction_output(const T& expected, const T& actual, const path_cons_list& path)const {
639
   if (!(_skip_flags & skip_output_check)) {
640
      check_output(expected.output, actual.output, path(".output"));
641 642 643
   }
}

644
void chain_controller::_apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
645
{ try {
N
Nathan Hourt 已提交
646
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
647

648 649 650
   FC_ASSERT((skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(),
             "", ("next_block.transaction_merkle_root", next_block.transaction_merkle_root)
             ("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()));
N
Nathan Hourt 已提交
651 652

   const producer_object& signing_producer = validate_block_header(skip, next_block);
N
Nathan Hourt 已提交
653 654 655
   
   for (const auto& cycle : next_block.cycles)
      for (const auto& thread : cycle)
656 657
         for (const auto& trx : thread.user_input) {
            validate_referenced_accounts(trx);
658 659 660
            // Check authorization, and allow irrelevant signatures.
            // If the block producer let it slide, we'll roll with it.
            check_transaction_authorization(trx, true);
661
         }
N
Nathan Hourt 已提交
662

N
Nathan Hourt 已提交
663 664 665 666 667 668
   /* We do not need to push the undo state for each transaction
    * because they either all apply and are valid or the
    * entire block fails to apply.  We only need an "undo" state
    * for transactions when validating broadcast transactions or
    * when building a block.
    */
669
   auto root_path = path_cons_list("next_block.cycles");
670 671
   for (int c_idx = 0; c_idx < next_block.cycles.size(); c_idx++) {
      const auto& cycle = next_block.cycles.at(c_idx);
672
      auto c_path = path_cons_list(c_idx, root_path);
673 674 675

      for (int t_idx = 0; t_idx < cycle.size(); t_idx++) {
         const auto& thread = cycle.at(t_idx);
676
         auto t_path = path_cons_list(t_idx, c_path);
677

678
         auto gen_path = path_cons_list(".generated_input", t_path);
679 680 681 682
         for(int p_idx = 0; p_idx < thread.generated_input.size(); p_idx++ ) {
            const auto& ptrx = thread.generated_input.at(p_idx);
            const auto& trx = get_generated_transaction(ptrx.id);
            auto processed = apply_transaction(trx);
683
            check_transaction_output(ptrx, processed, gen_path(p_idx));
N
Nathan Hourt 已提交
684
         }
685

686
         auto user_path = path_cons_list(".user_input", t_path);
687 688
         for(int p_idx = 0; p_idx < thread.user_input.size(); p_idx++ ) {
            const auto& ptrx = thread.user_input.at(p_idx);
689 690
            const SignedTransaction& trx = ptrx;
            auto processed = apply_transaction(trx);
691
            check_transaction_output(ptrx, processed, user_path(p_idx));
692 693 694
         }
      }
   }
N
Nathan Hourt 已提交
695

696
   update_global_properties(next_block);
N
Nathan Hourt 已提交
697 698 699 700 701 702 703 704
   update_global_dynamic_data(next_block);
   update_signing_producer(signing_producer, next_block);
   update_last_irreversible_block();

   create_block_summary(next_block);
   clear_expired_transactions();

   // notify observers that the block has been applied
705
   // TODO: do this outside the write lock...? 
N
Nathan Hourt 已提交
706 707 708 709
   applied_block( next_block ); //emit

} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) )  }

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
namespace {

  auto make_get_permission(const chainbase::database& db) {
     return [&db](const types::AccountPermission& permission) {
        auto key = boost::make_tuple(permission.account, permission.permission);
        return db.get<permission_object, by_owner>(key);
     };
  }

  auto make_authority_checker(const chainbase::database& db, const flat_set<public_key_type>& signingKeys) {
     auto getPermission = make_get_permission(db);
     auto getAuthority = [getPermission](const types::AccountPermission& permission) {
        return getPermission(permission).auth;
     };
     auto depthLimit = db.get<global_property_object>().configuration.authDepthLimit;
     return MakeAuthorityChecker(std::move(getAuthority), depthLimit, signingKeys);
  }

}

flat_set<public_key_type> chain_controller::get_required_keys(const SignedTransaction& trx, const flat_set<public_key_type>& candidateKeys)const {
   auto checker = make_authority_checker(_db, candidateKeys);

   for (const auto& message : trx.messages) {
      for (const auto& declaredAuthority : message.authorization) {
         if (!checker.satisfied(declaredAuthority)) {
            EOS_ASSERT(checker.satisfied(declaredAuthority), tx_missing_sigs,
                       "Transaction declares authority '${auth}', but does not have signatures for it.",
                       ("auth", declaredAuthority));
         }
      }
   }

   return checker.used_keys();
}

746
void chain_controller::check_transaction_authorization(const SignedTransaction& trx, bool allow_unused_signatures)const {
N
Nathan Hourt 已提交
747
   if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
748
      //ilog("Skipping auth and sigs checks");
N
Nathan Hourt 已提交
749 750 751
      return;
   }

752
   auto getPermission = make_get_permission(_db);
N
Nathan Hourt 已提交
753
#warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
754
   auto checker = make_authority_checker(_db, trx.get_signature_keys(chain_id_type{}));
N
Nathan Hourt 已提交
755 756 757 758 759 760 761 762

   for (const auto& message : trx.messages)
      for (const auto& declaredAuthority : message.authorization) {
         const auto& minimumPermission = lookup_minimum_permission(declaredAuthority.account,
                                                                   message.code, message.type);
         if ((_skip_flags & skip_authority_check) == false) {
            const auto& index = _db.get_index<permission_index>().indices();
            EOS_ASSERT(getPermission(declaredAuthority).satisfies(minimumPermission, index), tx_irrelevant_auth,
N
Nathan Hourt 已提交
763 764
                       "Message declares irrelevant authority '${auth}'; minimum authority is ${min}",
                       ("auth", declaredAuthority)("min", minimumPermission.name));
N
Nathan Hourt 已提交
765 766 767 768 769 770 771
         }
         if ((_skip_flags & skip_transaction_signatures) == false) {
            EOS_ASSERT(checker.satisfied(declaredAuthority), tx_missing_sigs,
                       "Transaction declares authority '${auth}', but does not have signatures for it.",
                       ("auth", declaredAuthority));
         }
      }
N
Nathan Hourt 已提交
772

773
   if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
774 775
      EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
                 "Transaction bears irrelevant signatures from these keys: ${keys}", ("keys", checker.unused_keys()));
N
Nathan Hourt 已提交
776 777
}

778
void chain_controller::validate_scope( const Transaction& trx )const {
779
   EOS_ASSERT(trx.scope.size() + trx.readscope.size() > 0, transaction_exception, "No scope specified by transaction" );
780 781
   for( uint32_t i = 1; i < trx.scope.size(); ++i )
      EOS_ASSERT( trx.scope[i-1] < trx.scope[i], transaction_exception, "Scopes must be sorted and unique" );
782 783 784 785 786 787 788 789
   for( uint32_t i = 1; i < trx.readscope.size(); ++i )
      EOS_ASSERT( trx.readscope[i-1] < trx.readscope[i], transaction_exception, "Scopes must be sorted and unique" );

   vector<types::AccountName> intersection;
   std::set_intersection( trx.scope.begin(), trx.scope.end(),
                          trx.readscope.begin(), trx.readscope.end(),
                          std::back_inserter(intersection) );
   FC_ASSERT( intersection.size() == 0, "a transaction may not redeclare scope in readscope" );
790 791
}

N
Nathan Hourt 已提交
792 793 794 795
const permission_object& chain_controller::lookup_minimum_permission(types::AccountName authorizer_account,
                                                                    types::AccountName code_account,
                                                                    types::FuncName type) const {
   try {
796 797 798 799 800 801 802 803 804 805 806 807 808 809
      // First look up a specific link for this message type
      auto key = boost::make_tuple(authorizer_account, code_account, type);
      auto link = _db.find<permission_link_object, by_message_type>(key);
      // If no specific link found, check for a contract-wide default
      if (link == nullptr) {
         get<2>(key) = "";
         link = _db.find<permission_link_object, by_message_type>(key);
      }

      // If no specific or default link found, use active permission
      auto permissionKey = boost::make_tuple<AccountName, PermissionName>(authorizer_account, "active");
      if (link != nullptr)
         get<1>(permissionKey) = link->required_permission;
      return _db.get<permission_object, by_owner>(permissionKey);
N
Nathan Hourt 已提交
810 811 812
   } FC_CAPTURE_AND_RETHROW((authorizer_account)(code_account)(type))
}

813
void chain_controller::validate_uniqueness( const SignedTransaction& trx )const {
N
Nathan Hourt 已提交
814
   if( !should_check_for_duplicate_transactions() ) return;
N
Nathan Hourt 已提交
815

N
Nathan Hourt 已提交
816
   auto transaction = _db.find<transaction_object, by_trx_id>(trx.id());
817
   EOS_ASSERT(transaction == nullptr, tx_duplicate, "Transaction is not unique");
818
}
819

820 821 822 823
void chain_controller::validate_uniqueness( const GeneratedTransaction& trx )const {
   if( !should_check_for_duplicate_transactions() ) return;
}

824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
void chain_controller::record_transaction(const SignedTransaction& trx) {
   //Insert transaction into unique transactions database.
    _db.create<transaction_object>([&](transaction_object& transaction) {
        transaction.trx_id = trx.id(); /// TODO: consider caching ID
        transaction.trx = trx;
    });
}

void chain_controller::record_transaction(const GeneratedTransaction& trx) {
   _db.modify( _db.get<generated_transaction_object,generated_transaction_object::by_trx_id>(trx.id), [&](generated_transaction_object& transaction) {
      transaction.status = generated_transaction_object::PROCESSED;
   });
}     



840
void chain_controller::validate_tapos(const Transaction& trx)const {
N
Nathan Hourt 已提交
841
   if (!should_check_tapos()) return;
842

843
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.refBlockNum);
844 845

   //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
846
   EOS_ASSERT(transaction_verify_reference_block(trx, tapos_block_summary.block_id), transaction_exception,
N
Nathan Hourt 已提交
847 848 849
              "Transaction's reference block did not match. Is this transaction from a different fork?",
              ("tapos_summary", tapos_block_summary));
}
850

851
void chain_controller::validate_referenced_accounts(const Transaction& trx)const {
N
Nathan Hourt 已提交
852 853 854
   for (const auto& scope : trx.scope)
      require_account(scope);
   for (const auto& msg : trx.messages) {
855
      require_account(msg.code);
N
Nathan Hourt 已提交
856 857
      for (const auto& auth : msg.authorization)
         require_account(auth.account);
858 859
   }
}
D
Daniel Larimer 已提交
860

861
void chain_controller::validate_expiration(const Transaction& trx) const
862 863
{ try {
   fc::time_point_sec now = head_block_time();
864
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
865

866
   EOS_ASSERT(trx.expiration <= now + int32_t(chain_configuration.maxTrxLifetime),
867 868
              transaction_exception, "Transaction expiration is too far in the future",
              ("trx.expiration",trx.expiration)("now",now)
869
              ("max_til_exp",chain_configuration.maxTrxLifetime));
870 871 872
   EOS_ASSERT(now <= trx.expiration, transaction_exception, "Transaction is expired",
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
873

874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
uint32_t chain_controller::_transaction_message_rate(uint32_t now, uint32_t last_update_sec, uint32_t rate_limit_time_frame_sec,
                                                     uint32_t rate_limit, uint32_t previous_rate, const char* type, const AccountName& name)
{
   const auto delta_time = now - last_update_sec;
   uint32_t message_count = 1;
   if (delta_time <= rate_limit_time_frame_sec)
   {
      message_count += ( ( ( rate_limit_time_frame_sec - delta_time ) * fc::uint128( previous_rate ) )
                     / rate_limit_time_frame_sec ).to_uint64();
      EOS_ASSERT(message_count <= rate_limit, tx_msgs_exceeded,
                 "Rate limiting ${type}=${name} messages sent, ${count} exceeds ${max} messages limit per ${sec} seconds. Wait 1 second and try again",
                 ("type",type)
                 ("name",name)
                 ("count",message_count)
                 ("max",rate_limit)
                 ("sec", rate_limit_time_frame_sec));
   }

   return message_count;
}

void chain_controller::rate_limit_message(const Message& message)
{ try {
B
Brian Johnson 已提交
897
   for (const auto& permission : message.authorization)
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
   {
      auto rate_limiting = _db.find<rate_limiting_object, by_name>(permission.account);
      const auto now = head_block_time().sec_since_epoch();
      if (rate_limiting == nullptr)
      {
         _db.create<rate_limiting_object>([&](rate_limiting_object& rlo) {
            rlo.name = permission.account;
            rlo.trans_msg_rate_per_account = 1;
            rlo.last_update_sec = now;
         });
      }
      else
      {
         const auto message_rate =
               _transaction_message_rate(now, rate_limiting->last_update_sec, _per_scope_trans_msg_rate_limit_time_frame_sec,
                                        _per_scope_trans_msg_rate_limit, rate_limiting->trans_msg_rate_per_account, "account", permission.account);
         _db.modify(*rate_limiting, [&] (rate_limiting_object& rlo) {
            rlo.trans_msg_rate_per_account = message_rate;
            rlo.last_update_sec = now;
         });
      }
   }
} FC_CAPTURE_AND_RETHROW((message)) }
921

922
void chain_controller::process_message(const Transaction& trx, AccountName code,
923
                                       const Message& message, MessageOutput& output, apply_context* parent_context) {
924
   apply_context apply_ctx(*this, _db, trx, message, code);
N
Nathan Hourt 已提交
925 926
   apply_message(apply_ctx);

927 928 929
   output.notify.reserve( apply_ctx.notified.size() );

   for( uint32_t i = 0; i < apply_ctx.notified.size(); ++i ) {
N
Nathan Hourt 已提交
930
      try {
931 932
         auto notify_code = apply_ctx.notified[i];
         output.notify.push_back( {notify_code} );
933
         process_message(trx, notify_code, message, output.notify.back().output, &apply_ctx);
934 935 936
      } FC_CAPTURE_AND_RETHROW((apply_ctx.notified[i]))
   }

937
   // combine inline messages and process
938 939 940 941
   if (apply_ctx.inline_messages.size() > 0) {
      output.inline_transaction = InlineTransaction(trx);
      (*output.inline_transaction).messages = std::move(apply_ctx.inline_messages);
   }
942 943 944 945 946 947 948

   for( auto& asynctrx : apply_ctx.deferred_transactions ) {
      digest_type::encoder enc;
      fc::raw::pack( enc, trx );
      fc::raw::pack( enc, asynctrx );
      auto id = enc.result();
      auto gtrx = GeneratedTransaction(id, asynctrx);
949

950
      _db.create<generated_transaction_object>([&](generated_transaction_object& transaction) {
951
         transaction.trx = gtrx;
952 953 954
         transaction.status = generated_transaction_object::PENDING;
      });

955
      output.deferred_transactions.emplace_back( gtrx );
N
Nathan Hourt 已提交
956
   }
957 958 959 960 961 962 963 964 965 966 967 968

   // propagate used_authorizations up the context chain
   if (parent_context != nullptr)
      for (int i = 0; i < apply_ctx.used_authorizations.size(); ++i)
         if (apply_ctx.used_authorizations[i])
            parent_context->used_authorizations[i] = true;

   // process_message recurses for each notified account, but we only want to run this check at the top level
   if (parent_context == nullptr && (_skip_flags & skip_authority_check) == false)
      EOS_ASSERT(apply_ctx.all_authorizations_used(), tx_irrelevant_auth,
                 "Message declared authorities it did not need: ${unused}",
                 ("unused", apply_ctx.unused_authorizations())("message", message));
N
Nathan Hourt 已提交
969
}
970

971
void chain_controller::apply_message(apply_context& context)
972
{ try {
D
Daniel Larimer 已提交
973 974
    /// context.code => the execution namespace
    /// message.code / message.type => Event
975
    const auto& m = context.msg;
D
Daniel Larimer 已提交
976
    auto contract_handlers_itr = apply_handlers.find(context.code);
977 978 979
    if (contract_handlers_itr != apply_handlers.end()) {
       auto message_handler_itr = contract_handlers_itr->second.find({m.code, m.type});
       if (message_handler_itr != contract_handlers_itr->second.end()) {
N
Nathan Hourt 已提交
980
          message_handler_itr->second(context);
981 982 983
          return;
       }
    }
D
Daniel Larimer 已提交
984
    const auto& recipient = _db.get<account_object,by_name>(context.code);
985
    if (recipient.code.size()) {
986
       //idump((context.code)(context.msg.type));
987 988 989 990 991 992
       const uint32_t execution_time =
          _skip_flags | received_block
             ? _rcvd_block_trans_execution_time
             : _skip_flags | created_block
               ? _create_block_trans_execution_time
               : _trans_execution_time;
993 994
       const bool is_received_block = _skip_flags & received_block;
       wasm_interface::get().apply(context, execution_time, is_received_block);
995
    }
996

N
Nathan Hourt 已提交
997
} FC_CAPTURE_AND_RETHROW((context.msg)) }
N
Nathan Hourt 已提交
998

999 1000
template<typename T>
typename T::Processed chain_controller::apply_transaction(const T& trx)
1001
{ try {
1002
   validate_transaction(trx);
1003
   record_transaction(trx);
1004
   return process_transaction( trx, 0, fc::time_point::now());
1005

N
Nathan Hourt 已提交
1006
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
1007

1008 1009 1010
/**
 *  @pre the transaction is assumed valid and all signatures / duplicate checks have bee performed
 */
1011
template<typename T>
1012
typename T::Processed chain_controller::process_transaction( const T& trx, int depth, const fc::time_point& start_time ) 
1013
{ try {
1014 1015 1016 1017 1018 1019 1020
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
   EOS_ASSERT((fc::time_point::now() - start_time).count() < chain_configuration.maxTrxRuntime, checktime_exceeded,
      "Transaction exceeded maximum total transaction time of ${limit}ms", ("limit", chain_configuration.maxTrxRuntime));

   EOS_ASSERT(depth < chain_configuration.inlineDepthLimit, tx_resource_exhausted,
      "Transaction exceeded maximum inline recursion depth of ${limit}", ("limit", chain_configuration.inlineDepthLimit));

1021
   typename T::Processed ptrx( trx );
1022 1023
   ptrx.output.resize( trx.messages.size() );

1024
   for( uint32_t i = 0; i < trx.messages.size(); ++i ) {
1025
      auto& output = ptrx.output[i];
1026
      rate_limit_message(trx.messages[i]);
1027
      process_message(trx, trx.messages[i].code, trx.messages[i], output);
1028
      if (output.inline_transaction.valid() ) {
1029 1030
         const Transaction& trx = *output.inline_transaction;
         output.inline_transaction = process_transaction(PendingInlineTransaction(trx), depth + 1, start_time);
1031
      }
1032 1033 1034 1035 1036
   }

   return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }

N
Nathan Hourt 已提交
1037 1038 1039 1040
void chain_controller::require_account(const types::AccountName& name) const {
   auto account = _db.find<account_object, by_name>(name);
   FC_ASSERT(account != nullptr, "Account not found: ${name}", ("name", name));
}
N
Nathan Hourt 已提交
1041

1042
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
1043 1044 1045 1046
   EOS_ASSERT(head_block_id() == next_block.previous, block_validate_exception, "",
              ("head_block_id",head_block_id())("next.prev",next_block.previous));
   EOS_ASSERT(head_block_time() < next_block.timestamp, block_validate_exception, "",
              ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()));
N
Nathan Hourt 已提交
1047
   if (next_block.block_num() % config::BlocksPerRound != 0) {
1048 1049
      EOS_ASSERT(next_block.producer_changes.empty(), block_validate_exception,
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
1050 1051 1052 1053 1054 1055 1056
   } else {
      using boost::is_sorted;
      using namespace boost::adaptors;
      EOS_ASSERT(is_sorted(keys(next_block.producer_changes)) && is_sorted(values(next_block.producer_changes)),
                 block_validate_exception, "Producer changes are not sorted correctly",
                 ("changes", next_block.producer_changes));
   }
N
Nathan Hourt 已提交
1057
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
1058

N
Nathan Hourt 已提交
1059
   if(!(skip&skip_producer_signature))
1060 1061 1062
      EOS_ASSERT(next_block.validate_signee(producer.signing_key), block_validate_exception,
                 "Incorrect block producer key: expected ${e} but got ${a}",
                 ("e", producer.signing_key)("a", public_key_type(next_block.signee())));
N
Nathan Hourt 已提交
1063

1064
   if(!(skip&skip_producer_schedule_check)) {
1065 1066 1067
      EOS_ASSERT(next_block.producer == producer.owner, block_validate_exception,
                 "Producer produced block at wrong time",
                 ("block producer",next_block.producer)("scheduled producer",producer.owner));
N
Nathan Hourt 已提交
1068 1069 1070 1071 1072
   }

   return producer;
}

1073
void chain_controller::create_block_summary(const signed_block& next_block) {
1074
   auto sid = next_block.block_num() & 0xffff;
1075
   _db.modify( _db.get<block_summary_object,by_id>(sid), [&](block_summary_object& p) {
1076 1077
         p.block_id = next_block.id();
   });
N
Nathan Hourt 已提交
1078 1079
}

1080
void chain_controller::update_global_properties(const signed_block& b) {
1081 1082
   // If we're at the end of a round, update the BlockchainConfiguration, producer schedule
   // and "producers" special account authority
N
Nathan Hourt 已提交
1083
   if (b.block_num() % config::BlocksPerRound == 0) {
1084
      auto schedule = calculate_next_round(b);
1085 1086
      auto config = _admin->get_blockchain_configuration(_db, schedule);

1087 1088
      const auto& gpo = get_global_properties();
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
1089 1090 1091
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
1092

1093
      auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {});
1094
      for(auto& name : gpo.active_producers) {
1095
         active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1});
1096 1097
      }

1098
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::ProducersAccountName, config::ActiveName) );
1099 1100 1101
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
1102 1103 1104
   }
}

1105
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
1106
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
1107 1108 1109
      _checkpoints[i.first] = i.second;
}

1110
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
1111 1112 1113
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

1114
const global_property_object& chain_controller::get_global_properties()const {
1115
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
1116 1117
}

1118
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
1119
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
1120 1121
}

1122
time_point_sec chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
1123 1124 1125
   return get_dynamic_global_properties().time;
}

1126
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
1127 1128 1129
   return get_dynamic_global_properties().head_block_number;
}

1130
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
1131 1132 1133
   return get_dynamic_global_properties().head_block_id;
}

N
Nathan Hourt 已提交
1134
types::AccountName chain_controller::head_block_producer() const {
1135 1136 1137
   auto b = _fork_db.fetch_block(head_block_id());
   if( b ) return b->data.producer;

N
Nathan Hourt 已提交
1138
   if (auto head_block = fetch_block_by_id(head_block_id()))
1139
      return head_block->producer;
N
Nathan Hourt 已提交
1140 1141 1142
   return {};
}

N
Nathan Hourt 已提交
1143 1144 1145 1146
const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const {
   return _db.get<producer_object, by_owner>(ownerName);
}

1147
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
1148
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
1149 1150
}

1151
void chain_controller::initialize_indexes() {
1152 1153
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
1154
   _db.add_index<permission_link_index>();
1155 1156
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
M
Matias Romeo 已提交
1157
   _db.add_index<keystr_value_index>();
1158
   _db.add_index<key128x128_value_index>();
1159
   _db.add_index<key64x64x64_value_index>();
1160 1161 1162 1163 1164

   _db.add_index<global_property_multi_index>();
   _db.add_index<dynamic_global_property_multi_index>();
   _db.add_index<block_summary_multi_index>();
   _db.add_index<transaction_multi_index>();
1165
   _db.add_index<generated_transaction_multi_index>();
1166
   _db.add_index<producer_multi_index>();
1167
   _db.add_index<rate_limiting_index>();
N
Nathan Hourt 已提交
1168 1169
}

1170
void chain_controller::initialize_chain(chain_initializer_interface& starter)
N
Nathan Hourt 已提交
1171
{ try {
1172
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
1173 1174 1175 1176 1177
      _db.with_write_lock([this, &starter] {
         auto initial_timestamp = starter.get_chain_start_time();
         FC_ASSERT(initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." );
         FC_ASSERT(initial_timestamp.sec_since_epoch() % config::BlockIntervalSeconds == 0,
                    "Genesis timestamp must be divisible by config::BlockIntervalSeconds." );
N
Nathan Hourt 已提交
1178

1179
         // Create global properties
N
Nathan Hourt 已提交
1180 1181 1182
         _db.create<global_property_object>([&starter](global_property_object& p) {
            p.configuration = starter.get_chain_start_configuration();
            p.active_producers = starter.get_chain_start_producers();
1183 1184
         });
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
1185
            p.time = initial_timestamp;
1186 1187
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
1188

N
Nathan Hourt 已提交
1189
         // Initialize block summary index
1190 1191
         for (int i = 0; i < 0x10000; i++)
            _db.create<block_summary_object>([&](block_summary_object&) {});
N
Nathan Hourt 已提交
1192

1193
         auto messages = starter.prepare_database(*this, _db);
1194 1195
         std::for_each(messages.begin(), messages.end(), [&](const Message& m) { 
            MessageOutput output;
1196
            ProcessedTransaction trx; /// dummy tranaction required for scope validation
1197
            std::sort(trx.scope.begin(), trx.scope.end() );
1198
            with_skip_flags(skip_scope_check | skip_transaction_signatures | skip_authority_check | received_block, [&](){
1199
               process_message(trx,m.code,m,output); 
1200
            });
1201
         });
1202 1203
      });
   }
N
Nathan Hourt 已提交
1204 1205
} FC_CAPTURE_AND_RETHROW() }

1206
chain_controller::chain_controller(database& database, fork_database& fork_db, block_log& blocklog,
1207 1208
                                   chain_initializer_interface& starter, unique_ptr<chain_administration_interface> admin,
                                   uint32_t trans_execution_time, uint32_t rcvd_block_trans_execution_time,
1209 1210
                                   uint32_t create_block_trans_execution_time, uint32_t per_scope_trans_msg_rate_limit_time_frame_sec,
                                   uint32_t per_scope_trans_msg_rate_limit)
1211
   : _db(database), _fork_db(fork_db), _block_log(blocklog), _admin(std::move(admin)), _trans_execution_time(trans_execution_time),
1212 1213
     _rcvd_block_trans_execution_time(rcvd_block_trans_execution_time), _create_block_trans_execution_time(create_block_trans_execution_time),
     _per_scope_trans_msg_rate_limit_time_frame_sec(per_scope_trans_msg_rate_limit_time_frame_sec), _per_scope_trans_msg_rate_limit(per_scope_trans_msg_rate_limit) {
N
Nathan Hourt 已提交
1214

1215
   initialize_indexes();
1216
   starter.register_types(*this, _db);
N
Nathan Hourt 已提交
1217 1218 1219 1220 1221 1222

   // Behave as though we are applying a block during chain initialization (it's the genesis block!)
   with_applying_block([&] {
      initialize_chain(starter);
   });

1223 1224
   spinup_db();
   spinup_fork_db();
1225 1226 1227

   if (_block_log.read_head() && head_block_num() < _block_log.read_head()->block_num())
      replay();
N
Nathan Hourt 已提交
1228 1229
}

1230 1231 1232 1233 1234
chain_controller::~chain_controller() {
   clear_pending();
   _db.flush();
   _fork_db.reset();
}
N
Nathan Hourt 已提交
1235

1236
void chain_controller::replay() {
1237
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
1238
   auto start = fc::time_point::now();
1239 1240 1241
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
1242 1243 1244 1245 1246
      return;
   }

   const auto last_block_num = last_block->block_num();

1247
   ilog("Replaying ${n} blocks...", ("n", last_block_num) );
1248 1249 1250 1251 1252
   for (uint32_t i = 1; i <= last_block_num; ++i) {
      if (i % 5000 == 0)
         std::cerr << "   " << double(i*100)/last_block_num << "%   "<<i << " of " <<last_block_num<<"   \n";
      fc::optional<signed_block> block = _block_log.read_block_by_num(i);
      FC_ASSERT(block, "Could not find block #${n} in block_log!", ("n", i));
N
Nathan Hourt 已提交
1253 1254 1255 1256 1257
      apply_block(*block, skip_producer_signature |
                          skip_transaction_signatures |
                          skip_transaction_dupe_check |
                          skip_tapos_check |
                          skip_producer_schedule_check |
1258 1259
                          skip_authority_check |
                          received_block);
N
Nathan Hourt 已提交
1260 1261
   }
   auto end = fc::time_point::now();
1262 1263
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
1264

1265
   _db.set_revision(head_block_num());
1266
}
N
Nathan Hourt 已提交
1267

1268 1269 1270 1271
void chain_controller::spinup_db() {
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
D
Daniel Larimer 已提交
1272

1273 1274 1275 1276
      FC_ASSERT(_db.revision() == head_block_num(), "Chainbase revision does not match head block num",
                ("rev", _db.revision())("head_block", head_block_num()));
   });
}
N
Nathan Hourt 已提交
1277

1278
void chain_controller::spinup_fork_db()
N
Nathan Hourt 已提交
1279
{
1280 1281 1282 1283 1284 1285 1286 1287
   fc::optional<signed_block> last_block = _block_log.read_head();
   if(last_block.valid()) {
      _fork_db.start_block(*last_block);
      if (last_block->id() != head_block_id()) {
           FC_ASSERT(head_block_num() == 0, "last block ID does not match current chain state",
                     ("last_block->id", last_block->id())("head_block_num",head_block_num()));
      }
   }
N
Nathan Hourt 已提交
1288 1289
}

1290 1291
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
1292 1293 1294 1295
   auto changes = get_global_properties().active_producers - schedule;
   EOS_ASSERT(boost::range::equal(next_block.producer_changes, changes), block_validate_exception,
              "Unexpected round changes in new block header",
              ("expected changes", changes)("block changes", next_block.producer_changes));
1296

1297
   utilities::rand::random rng(next_block.timestamp.sec_since_epoch());
1298 1299 1300 1301
   rng.shuffle(schedule);
   return schedule;
}

1302
void chain_controller::update_global_dynamic_data(const signed_block& b) {
1303
   const dynamic_global_property_object& _dgp = _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
1304

1305 1306
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time(b.timestamp);
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
1307
   missed_blocks--;
N
Nathan Hourt 已提交
1308

1309 1310
//   if (missed_blocks)
//      wlog("Blockchain continuing after gap of ${b} missed blocks", ("b", missed_blocks));
N
Nathan Hourt 已提交
1311

N
Nathan Hourt 已提交
1312
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
1313
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
1314
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
1315 1316 1317 1318 1319 1320
         /*
         const auto& producer_account = producer_missed.producer_account(*this);
         if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) )
            wlog( "Producer ${name} missed block ${n} around ${t}", ("name",producer_account.name)("n",b.block_num())("t",b.timestamp) );
            */

1321
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
1322 1323 1324 1325 1326 1327
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
1328
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
1329 1330 1331
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
1332
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
1333 1334 1335 1336 1337 1338 1339 1340 1341
      dgp.current_absolute_slot += missed_blocks+1;

      // If we've missed more blocks than the bitmap stores, skip calculations and simply reset the bitmap
      if (missed_blocks < sizeof(dgp.recent_slots_filled) * 8) {
         dgp.recent_slots_filled <<= 1;
         dgp.recent_slots_filled += 1;
         dgp.recent_slots_filled <<= missed_blocks;
      } else
         dgp.recent_slots_filled = 0;
N
Nathan Hourt 已提交
1342 1343 1344 1345 1346
   });

   _fork_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );
}

1347
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
1348 1349
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1350
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( new_block.timestamp );
N
Nathan Hourt 已提交
1351

1352
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
1353 1354 1355 1356 1357 1358
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

1359
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
1360 1361 1362 1363
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

N
Nathan Hourt 已提交
1364 1365 1366
   vector<const producer_object*> producer_objs;
   producer_objs.reserve(gpo.active_producers.size());
   std::transform(gpo.active_producers.begin(), gpo.active_producers.end(), std::back_inserter(producer_objs),
N
Nathan Hourt 已提交
1367
                  [this](const AccountName& owner) { return &get_producer(owner); });
N
Nathan Hourt 已提交
1368

N
Nathan Hourt 已提交
1369
   static_assert(config::IrreversibleThresholdPercent > 0, "irreversible threshold must be nonzero");
N
Nathan Hourt 已提交
1370

N
Nathan Hourt 已提交
1371
   size_t offset = EOS_PERCENT(producer_objs.size(), config::Percent100 - config::IrreversibleThresholdPercent);
1372 1373
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
1374
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
1375
      });
N
Nathan Hourt 已提交
1376

N
Nathan Hourt 已提交
1377
   uint32_t new_last_irreversible_block_num = producer_objs[offset]->last_confirmed_block_num;
N
Nathan Hourt 已提交
1378

1379
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
1380
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
1381
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1382
      });
N
Nathan Hourt 已提交
1383
   }
1384 1385

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1386
   auto old_last_irreversible_block = _block_log.head();
1387 1388 1389 1390
   int last_block_on_disk = 0;
   // If this is null, there are no blocks on disk, so the zero is correct
   if (old_last_irreversible_block)
      last_block_on_disk = old_last_irreversible_block->block_num();
1391

1392 1393 1394 1395 1396 1397
   if (last_block_on_disk < new_last_irreversible_block_num)
      for (auto block_to_write = last_block_on_disk + 1;
           block_to_write <= new_last_irreversible_block_num;
           ++block_to_write) {
         auto block = fetch_block_by_number(block_to_write);
         assert(block);
1398
         _block_log.append(*block);
1399
      }
N
Nathan Hourt 已提交
1400 1401 1402

   // Trim fork_database and undo histories
   _fork_db.set_max_size(head_block_num() - new_last_irreversible_block_num + 1);
1403
   _db.commit(new_last_irreversible_block_num);
N
Nathan Hourt 已提交
1404 1405
}

1406
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1407 1408 1409
{ try {
   //Look for expired transactions in the deduplication list, and remove them.
   //Transactions must have expired by at least two forking windows in order to be removed.
1410
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1411 1412 1413
   const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
   while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->trx.expiration) )
      transaction_idx.remove(*dedupe_index.rbegin());
1414 1415 1416 1417 1418 1419 1420

   //Look for expired transactions in the pending generated list, and remove them.
   //Transactions must have expired by at least two forking windows in order to be removed.
   auto& generated_transaction_idx = _db.get_mutable_index<generated_transaction_multi_index>();
   const auto& generated_index = generated_transaction_idx.indices().get<generated_transaction_object::by_expiration>();
   while( (!generated_index.empty()) && (head_block_time() > generated_index.rbegin()->trx.expiration) )
      generated_transaction_idx.remove(*generated_index.rbegin());
N
Nathan Hourt 已提交
1421 1422 1423 1424
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

N
Nathan Hourt 已提交
1425
types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1426 1427
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1428
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1429
   const auto& gpo = _db.get<global_property_object>();
N
Nathan Hourt 已提交
1430
   return gpo.active_producers[current_aslot % gpo.active_producers.size()];
N
Nathan Hourt 已提交
1431 1432
}

1433
fc::time_point_sec chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453
{
   if( slot_num == 0 )
      return fc::time_point_sec();

   auto interval = block_interval();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

   if( head_block_num() == 0 )
   {
      // n.b. first block is at genesis_time plus one block interval
      fc::time_point_sec genesis_time = dpo.time;
      return genesis_time + slot_num * interval;
   }

   int64_t head_block_abs_slot = head_block_time().sec_since_epoch() / interval;
   fc::time_point_sec head_slot_time(head_block_abs_slot * interval);

   return head_slot_time + (slot_num * interval);
}

1454
uint32_t chain_controller::get_slot_at_time(fc::time_point_sec when)const
N
Nathan Hourt 已提交
1455 1456 1457 1458 1459 1460 1461
{
   fc::time_point_sec first_slot_time = get_slot_time( 1 );
   if( when < first_slot_time )
      return 0;
   return (when - first_slot_time).to_seconds() / block_interval() + 1;
}

1462
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1463 1464
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1465
   return uint64_t(config::Percent100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1466 1467
}

1468
void chain_controller::set_apply_handler( const AccountName& contract, const AccountName& scope, const ActionName& action, apply_handler v ) {
D
Daniel Larimer 已提交
1469
   apply_handlers[contract][std::make_pair(scope,action)] = v;
1470
}
N
Nathan Hourt 已提交
1471

1472
chain_initializer_interface::~chain_initializer_interface() {}
N
Nathan Hourt 已提交
1473

1474

D
Daniel Larimer 已提交
1475
ProcessedTransaction chain_controller::transaction_from_variant( const fc::variant& v )const {
1476 1477 1478 1479
   const variant_object& vo = v.get_object();
#define GET_FIELD( VO, FIELD, RESULT ) \
   if( VO.contains(#FIELD) ) fc::from_variant( VO[#FIELD], RESULT.FIELD )

D
Daniel Larimer 已提交
1480
   ProcessedTransaction result;
1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493
   GET_FIELD( vo, refBlockNum, result );
   GET_FIELD( vo, refBlockPrefix, result );
   GET_FIELD( vo, expiration, result );
   GET_FIELD( vo, scope, result );
   GET_FIELD( vo, signatures, result );

   if( vo.contains( "messages" ) ) {
      const vector<variant>& msgs = vo["messages"].get_array();
      result.messages.resize( msgs.size() );
      for( uint32_t i = 0; i <  msgs.size(); ++i ) {
         const auto& vo = msgs[i].get_object();
         GET_FIELD( vo, code, result.messages[i] );
         GET_FIELD( vo, type, result.messages[i] );
1494
         GET_FIELD( vo, authorization, result.messages[i] );
1495 1496 1497 1498 1499 1500

         if( vo.contains( "data" ) ) {
            const auto& data = vo["data"];
            if( data.is_string() ) {
               GET_FIELD( vo, data, result.messages[i] );
            } else if ( data.is_object() ) {
1501 1502
               result.messages[i].data = message_to_binary( result.messages[i].code, result.messages[i].type, data ); 
               /*
1503
               const auto& code_account = _db.get<account_object,by_name>( result.messages[i].code );
1504 1505
               eos::types::Abi abi;
               if( AbiSerializer::to_abi(code_account.abi, abi) ) {
1506 1507 1508
                  types::AbiSerializer abis( abi );
                  result.messages[i].data = abis.variantToBinary( abis.getActionType( result.messages[i].type ), data );
               }
1509
               */
1510 1511 1512 1513
            }
         }
      }
   }
D
Daniel Larimer 已提交
1514 1515 1516
   if( vo.contains( "output" ) ) {
      const vector<variant>& outputs = vo["output"].get_array();
   }
1517 1518 1519 1520
   return result;
#undef GET_FIELD
}

1521 1522
vector<char> chain_controller::message_to_binary( Name code, Name type, const fc::variant& obj )const 
{ try {
1523
   const auto& code_account = _db.get<account_object,by_name>( code );
1524 1525
   eos::types::Abi abi;
   if( types::AbiSerializer::to_abi(code_account.abi, abi) ) {
1526 1527 1528 1529
      types::AbiSerializer abis( abi );
      return abis.variantToBinary( abis.getActionType( type ), obj );
   }
   return vector<char>();
1530
} FC_CAPTURE_AND_RETHROW( (code)(type)(obj) ) }
D
Daniel Larimer 已提交
1531 1532
fc::variant chain_controller::message_from_binary( Name code, Name type, const vector<char>& data )const {
   const auto& code_account = _db.get<account_object,by_name>( code );
1533 1534
   eos::types::Abi abi;
   if( types::AbiSerializer::to_abi(code_account.abi, abi) ) {
D
Daniel Larimer 已提交
1535 1536 1537 1538 1539
      types::AbiSerializer abis( abi );
      return abis.binaryToVariant( abis.getActionType( type ), data );
   }
   return fc::variant();
}
1540

D
Daniel Larimer 已提交
1541
fc::variant  chain_controller::transaction_to_variant( const ProcessedTransaction& trx )const {
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558
#define SET_FIELD( MVO, OBJ, FIELD ) MVO(#FIELD, OBJ.FIELD)

    fc::mutable_variant_object trx_mvo;
    SET_FIELD( trx_mvo, trx, refBlockNum );
    SET_FIELD( trx_mvo, trx, refBlockPrefix );
    SET_FIELD( trx_mvo, trx, expiration );
    SET_FIELD( trx_mvo, trx, scope );
    SET_FIELD( trx_mvo, trx, signatures );

    vector<fc::mutable_variant_object> msgs( trx.messages.size() );
    vector<fc::variant> msgsv(msgs.size());

    for( uint32_t i = 0; i < trx.messages.size(); ++i ) {
       auto& msg_mvo = msgs[i];
       auto& msg     = trx.messages[i];
       SET_FIELD( msg_mvo, msg, code );
       SET_FIELD( msg_mvo, msg, type );
1559
       SET_FIELD( msg_mvo, msg, authorization );
1560 1561

       const auto& code_account = _db.get<account_object,by_name>( msg.code );
1562
       if( !types::AbiSerializer::is_empty_abi(code_account.abi) ) {
D
Daniel Larimer 已提交
1563
          try {
D
Daniel Larimer 已提交
1564
             msg_mvo( "data", message_from_binary( msg.code, msg.type, msg.data ) ); 
D
Daniel Larimer 已提交
1565 1566 1567 1568
             msg_mvo( "hex_data", msg.data );
          } catch ( ... ) {
            SET_FIELD( msg_mvo, msg, data );
          }
1569 1570 1571 1572 1573 1574 1575 1576
       }
       else {
         SET_FIELD( msg_mvo, msg, data );
       }
       msgsv[i] = std::move( msgs[i] );
    }
    trx_mvo( "messages", std::move(msgsv) );

D
Daniel Larimer 已提交
1577 1578 1579 1580 1581 1582 1583 1584 1585
    /* TODO: recursively process generated transactions 
    vector<fc::mutable_variant_object> outs( trx.messages.size() );
    for( uint32_t i = 0; i < trx.output.size(); ++i ) {
       auto& out_mvo = outs[i];
       auto& out = trx.outputs[i];
    }
    */
    trx_mvo( "output", fc::variant( trx.output ) );

1586 1587 1588 1589
    return fc::variant( std::move( trx_mvo ) );
#undef SET_FIELD
}

1590

N
Nathan Hourt 已提交
1591
} }