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

B
Bart Wyatt 已提交
6
#include <eosio/chain/chain_controller.hpp>
N
Nathan Hourt 已提交
7

B
Bart Wyatt 已提交
8 9 10 11 12 13 14 15 16
#include <eosio/chain/block_summary_object.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/key_value_object.hpp>
#include <eosio/chain/action_objects.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
#include <eosio/chain/transaction_object.hpp>
#include <eosio/chain/producer_object.hpp>
#include <eosio/chain/permission_link_object.hpp>
#include <eosio/chain/authority_checker.hpp>
D
Daniel Larimer 已提交
17
#include <eosio/chain/contracts/chain_initializer.hpp>
18
#include <eosio/chain/contracts/producer_objects.hpp>
N
Nathan Hourt 已提交
19

B
Bart Wyatt 已提交
20
#include <eosio/chain/wasm_interface.hpp>
21

22
#include <eos/utilities/rand.hpp>
23

N
Nathan Hourt 已提交
24
#include <fc/smart_ref_impl.hpp>
N
Nathan Hourt 已提交
25 26 27
#include <fc/uint128.hpp>
#include <fc/crypto/digest.hpp>

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

#include <fstream>
#include <functional>
#include <iostream>
36
#include <chrono>
N
Nathan Hourt 已提交
37

P
Pravin 已提交
38
namespace eosio { namespace chain {
D
Daniel Larimer 已提交
39

40 41 42 43
bool is_start_of_round( block_num_type block_num ) {
  return (block_num % config::blocks_per_round) == 0; 
}

D
Daniel Larimer 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
chain_controller::chain_controller( const chain_controller::controller_config& cfg )
:_db( cfg.shared_memory_dir, 
      (cfg.read_only ? database::read_only : database::read_write), 
      cfg.shared_memory_size), 
 _block_log(cfg.block_log_dir) 
{
   _initialize_indexes();

   contracts::chain_initializer starter(cfg.genesis);
   starter.register_types(*this, _db);

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

   _spinup_db();
   _spinup_fork_db();

   if (_block_log.read_head() && head_block_num() < _block_log.read_head()->block_num())
      replay();
} /// chain_controller::chain_controller


chain_controller::~chain_controller() {
   clear_pending();
   _db.flush();
}

73
bool chain_controller::is_known_block(const block_id_type& id)const
N
Nathan Hourt 已提交
74
{
75
   return _fork_db.is_known_block(id) || _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
76 77 78 79 80 81
}
/**
 * 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.
 */
82
bool chain_controller::is_known_transaction(const transaction_id_type& id)const
N
Nathan Hourt 已提交
83
{
84
   const auto& trx_idx = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
85 86 87
   return trx_idx.find( id ) != trx_idx.end();
}

88
block_id_type chain_controller::get_block_id_for_num(uint32_t block_num)const
N
Nathan Hourt 已提交
89
{ try {
90 91
   if (const auto& block = fetch_block_by_number(block_num))
      return block->id();
N
Nathan Hourt 已提交
92

93 94 95
   FC_THROW_EXCEPTION(unknown_block_exception, "Could not find block");
} FC_CAPTURE_AND_RETHROW((block_num)) }

96
optional<signed_block> chain_controller::fetch_block_by_id(const block_id_type& id)const
N
Nathan Hourt 已提交
97
{
98 99
   auto b = _fork_db.fetch_block(id);
   if(b) return b->data;
100
   return _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
101 102
}

103
optional<signed_block> chain_controller::fetch_block_by_number(uint32_t num)const
N
Nathan Hourt 已提交
104
{
105
   if (const auto& block = _block_log.read_block_by_num(num))
106 107
      return *block;

N
Nathan Hourt 已提交
108
   // Not in _block_log, so it must be since the last irreversible block. Grab it from _fork_db instead
109 110 111 112 113 114 115 116
   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 已提交
117 118 119
   return optional<signed_block>();
}

D
Daniel Larimer 已提交
120
/*
D
Daniel Larimer 已提交
121
const signed_transaction& chain_controller::get_recent_transaction(const transaction_id_type& trx_id) const
N
Nathan Hourt 已提交
122
{
123
   auto& index = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
124 125 126 127
   auto itr = index.find(trx_id);
   FC_ASSERT(itr != index.end());
   return itr->trx;
}
D
Daniel Larimer 已提交
128
*/
N
Nathan Hourt 已提交
129

130
std::vector<block_id_type> chain_controller::get_block_ids_on_fork(block_id_type head_of_fork) const
N
Nathan Hourt 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
{
  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;
}

148

N
Nathan Hourt 已提交
149 150 151 152 153 154
/**
 * 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.
 */
D
Daniel Larimer 已提交
155
void chain_controller::push_block(const signed_block& new_block, uint32_t skip)
D
Daniel Larimer 已提交
156
{ try {
D
Daniel Larimer 已提交
157
   with_skip_flags( skip, [&](){ 
D
Daniel Larimer 已提交
158
      return without_pending_transactions( [&]() {
159
         return _db.with_write_lock( [&]() {
160 161
            return _push_block(new_block);
         } );
N
Nathan Hourt 已提交
162 163
      });
   });
164
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
165

166
bool chain_controller::_push_block(const signed_block& new_block)
N
Nathan Hourt 已提交
167
{ try {
N
Nathan Hourt 已提交
168
   uint32_t skip = _skip_flags;
169
   if (!(skip&skip_fork_db)) {
N
Nathan Hourt 已提交
170
      /// TODO: if the block is greater than the head block and before the next maintenance interval
N
Nathan Hourt 已提交
171 172 173 174
      // 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.
175
      if (new_head->data.previous != head_block_id()) {
N
Nathan Hourt 已提交
176 177
         //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
178 179
         if (new_head->data.block_num() > head_block_num()) {
            wlog("Switching to fork: ${id}", ("id",new_head->data.id()));
N
Nathan Hourt 已提交
180 181 182
            auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());

            // pop blocks until we hit the forked block
183
            while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
184 185 186
               pop_block();

            // push all blocks on the new fork
187 188
            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 已提交
189 190
                optional<fc::exception> except;
                try {
191
                   auto session = _db.start_undo_session(true);
D
Daniel Larimer 已提交
192
                   _apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
193 194
                   session.push();
                }
195 196 197
                catch (const fc::exception& e) { except = e; }
                if (except) {
                   wlog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
N
Nathan Hourt 已提交
198
                   // remove the rest of branches.first from the fork_db, those blocks are invalid
199 200
                   while (ritr != branches.first.rend()) {
                      _fork_db.remove((*ritr)->data.id());
N
Nathan Hourt 已提交
201 202
                      ++ritr;
                   }
203
                   _fork_db.set_head(branches.second.front());
N
Nathan Hourt 已提交
204 205

                   // pop all blocks from the bad fork
206
                   while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
207 208 209
                      pop_block();

                   // restore all blocks from the good fork
210
                   for (auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr) {
211
                      auto session = _db.start_undo_session(true);
D
Daniel Larimer 已提交
212
                      _apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
213 214 215 216 217
                      session.push();
                   }
                   throw *except;
                }
            }
D
Daniel Larimer 已提交
218
            return true; //swithced fork
N
Nathan Hourt 已提交
219
         }
D
Daniel Larimer 已提交
220
         else return false; // didn't switch fork
N
Nathan Hourt 已提交
221 222 223 224
      }
   }

   try {
225
      auto session = _db.start_undo_session(true);
D
Daniel Larimer 已提交
226
      _apply_block(new_block, skip);
N
Nathan Hourt 已提交
227 228 229 230 231 232 233 234
      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;
235
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
236 237 238 239 240 241 242 243 244 245

/**
 * 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.
 */
D
Daniel Larimer 已提交
246
void chain_controller::push_transaction(const signed_transaction& trx, uint32_t skip)
N
Nathan Hourt 已提交
247
{ try {
248 249 250
   return with_skip_flags(skip, [&]() {
      return _db.with_write_lock([&]() {
         return _push_transaction(trx);
D
Daniel Larimer 已提交
251
      });
N
Nathan Hourt 已提交
252 253
   });
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
254

D
Daniel Larimer 已提交
255
void chain_controller::_push_transaction(const signed_transaction& trx) {
256 257
   // 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.
D
Daniel Larimer 已提交
258
   if( !_pending_block ) {
D
Daniel Larimer 已提交
259
      _start_pending_block();
D
Daniel Larimer 已提交
260
   }
261

262
   auto temp_session = _db.start_undo_session(true);
D
Daniel Larimer 已提交
263

264
   auto tid = trx.id();
265
   validate_referenced_accounts(trx);
N
Nathan Hourt 已提交
266
   check_transaction_authorization(trx);
N
Nathan Hourt 已提交
267

268 269 270 271
   _apply_transaction(trx);
   /** for now we will just shove everything into the first shard */
   _pending_block->cycles_summary[0][0].emplace_back( tid );
   _pending_block->input_transactions.push_back(trx);
D
Daniel Larimer 已提交
272

N
Nathan Hourt 已提交
273 274 275 276
   // The transaction applied successfully. Merge its changes into the pending block session.
   temp_session.squash();

   // notify anyone listening to pending transactions
D
Daniel Larimer 已提交
277
   on_pending_transaction(trx); 
N
Nathan Hourt 已提交
278 279
}

280
signed_block chain_controller::generate_block(
D
Daniel Larimer 已提交
281 282 283
   block_timestamp_type when,
   account_name producer,
   const private_key_type& block_signing_private_key,
N
Nathan Hourt 已提交
284 285 286
   uint32_t skip /* = 0 */
   )
{ try {
N
Nathan Hourt 已提交
287
   return with_skip_flags( skip, [&](){
D
Daniel Larimer 已提交
288
      return _db.with_write_lock( [&](){
D
Daniel Larimer 已提交
289
         return _generate_block( when, producer, block_signing_private_key );
D
Daniel Larimer 已提交
290
      });
N
Nathan Hourt 已提交
291
   });
D
Daniel Larimer 已提交
292
} FC_CAPTURE_AND_RETHROW( (when) ) }
N
Nathan Hourt 已提交
293

D
Daniel Larimer 已提交
294 295 296 297 298
signed_block chain_controller::_generate_block( block_timestamp_type when, 
                                              account_name producer, 
                                              const private_key_type& block_signing_key )
{ try {
   uint32_t skip     = _skip_flags;
N
Nathan Hourt 已提交
299 300
   uint32_t slot_num = get_slot_at_time( when );
   FC_ASSERT( slot_num > 0 );
D
Daniel Larimer 已提交
301
   account_name scheduled_producer = get_scheduled_producer( slot_num );
N
Nathan Hourt 已提交
302
   FC_ASSERT( scheduled_producer == producer );
N
Nathan Hourt 已提交
303

N
Nathan Hourt 已提交
304
   const auto& producer_obj = get_producer(scheduled_producer);
N
Nathan Hourt 已提交
305

D
Daniel Larimer 已提交
306
   if( !_pending_block ) {
D
Daniel Larimer 已提交
307
      _start_pending_block();
308 309
   }

D
Daniel Larimer 已提交
310 311
   if( !(skip & skip_producer_signature) )
      FC_ASSERT( producer_obj.signing_key == block_signing_key.get_public_key() );
N
Nathan Hourt 已提交
312

D
Daniel Larimer 已提交
313
   _pending_block->timestamp = when;
D
Daniel Larimer 已提交
314 315
   _pending_block->producer  = producer_obj.owner;
   _pending_block->previous  = head_block_id();
316

317 318 319 320
   if( is_start_of_round( _pending_block->block_num() ) ) {
      auto latest_producer_schedule = _calculate_producer_schedule();
      if( latest_producer_schedule != _head_producer_schedule() )
         _pending_block->new_producers = latest_producer_schedule;
321 322
   }

N
Nathan Hourt 已提交
323
   if( !(skip & skip_producer_signature) )
D
Daniel Larimer 已提交
324
      _pending_block->sign( block_signing_key );
N
Nathan Hourt 已提交
325

D
Daniel Larimer 已提交
326 327
   _finalize_block( *_pending_block );

328 329
   _pending_block_session->push();

D
Daniel Larimer 已提交
330 331
   auto result = move( *_pending_block );

332 333 334
   _pending_block.reset();
   _pending_block_session.reset();

D
Daniel Larimer 已提交
335
   if (!(skip&skip_fork_db)) {
D
Daniel Larimer 已提交
336
      _fork_db.push_block(result);
N
Nathan Hourt 已提交
337
   }
D
Daniel Larimer 已提交
338
   return result;
N
Nathan Hourt 已提交
339

N
Nathan Hourt 已提交
340
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
341

D
Daniel Larimer 已提交
342 343 344
void chain_controller::_start_pending_block() {
   FC_ASSERT( !_pending_block );
   _pending_block         = signed_block();
345 346
   _pending_block->cycles_summary.resize(1);
   _pending_block->cycles_summary[0].resize(1);
D
Daniel Larimer 已提交
347
   _pending_block_session = _db.start_undo_session(true);
D
Daniel Larimer 已提交
348 349
}

N
Nathan Hourt 已提交
350
/**
N
Nathan Hourt 已提交
351
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
352
 */
353
void chain_controller::pop_block()
N
Nathan Hourt 已提交
354
{ try {
D
Daniel Larimer 已提交
355
   _pending_block_session.reset();
N
Nathan Hourt 已提交
356 357 358 359 360
   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();
361
   _db.undo();
N
Nathan Hourt 已提交
362 363
} FC_CAPTURE_AND_RETHROW() }

364
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
365
{ try {
D
Daniel Larimer 已提交
366
   _pending_block.reset();
D
Daniel Larimer 已提交
367
   _pending_block_session.reset();
N
Nathan Hourt 已提交
368 369 370 371
} FC_CAPTURE_AND_RETHROW() }

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

D
Daniel Larimer 已提交
372
void chain_controller::_apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
373 374
{
   auto block_num = next_block.block_num();
375 376 377 378 379
   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 已提交
380

381
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
382 383
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
N
Nathan Hourt 已提交
384 385 386

   with_applying_block([&] {
      with_skip_flags(skip, [&] {
D
Daniel Larimer 已提交
387
         __apply_block(next_block);
N
Nathan Hourt 已提交
388 389
      });
   });
N
Nathan Hourt 已提交
390 391
}

392

D
Daniel Larimer 已提交
393
void chain_controller::__apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
394
{ try {
N
Nathan Hourt 已提交
395
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
396

D
Daniel Larimer 已提交
397 398 399
   /*
   FC_ASSERT((skip & skip_merkle_check) 
             || next_block.transaction_merkle_root == next_block.calculate_merkle_root(),
400 401
             "", ("next_block.transaction_merkle_root", next_block.transaction_merkle_root)
             ("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()));
D
Daniel Larimer 已提交
402
             */
N
Nathan Hourt 已提交
403 404

   const producer_object& signing_producer = validate_block_header(skip, next_block);
405 406 407 408 409 410 411 412


   /// cache the input tranasction ids so that they can be looked up when executing the
   /// summary
   map<transaction_id_type,const signed_transaction*> trx_index;
   for( const auto& t : next_block.input_transactions ) {
      trx_index[t.id()] = &t;
   }
N
Nathan Hourt 已提交
413
   
414 415 416 417 418 419 420 421 422 423 424 425 426 427
   for (const auto& cycle : next_block.cycles_summary) {
      for (const auto& shard: cycle) {
         for (const auto& receipt : shard) {
            if( receipt.status == transaction_receipt::executed ) {
               auto itr = trx_index.find(receipt.id);
               if( itr != trx_index.end() ) {
                  _apply_transaction( *itr->second );
               } 
               else 
               {
                  FC_ASSERT( !"deferred transactions not yet supported" );
               }
            }
            // validate_referenced_accounts(trx);
428 429
            // Check authorization, and allow irrelevant signatures.
            // If the block producer let it slide, we'll roll with it.
430 431 432 433
            // check_transaction_authorization(trx, true);
         } /// for each transaction id
      } /// for each shard
   } /// for each cycle
N
Nathan Hourt 已提交
434

435 436
   _finalize_block( next_block );
   /*
437
   update_global_properties(next_block);
N
Nathan Hourt 已提交
438 439 440 441 442 443 444 445
   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
446
   // TODO: do this outside the write lock...? 
K
Kevin Heifner 已提交
447 448
   if (_currently_replaying_blocks)
     applied_irreversible_block(next_block);
449
     */
K
Kevin Heifner 已提交
450

N
Nathan Hourt 已提交
451 452 453

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

D
Daniel Larimer 已提交
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
/**
 *  After applying all transactions successfully we can update
 *  the current block time, block number, producer stats, etc
 */
void chain_controller::_finalize_block( const signed_block& b ) {
   const producer_object& signing_producer = validate_block_header(_skip_flags, b);

   update_global_properties( b );
   update_global_dynamic_data( b );
   update_signing_producer(signing_producer, b);
   update_last_irreversible_block();

   create_block_summary(b);
   clear_expired_transactions();

469
   applied_block( b ); //emit
D
Daniel Larimer 已提交
470 471 472 473
   if (_currently_replaying_blocks)
     applied_irreversible_block(b);
}

474 475 476
namespace {

  auto make_get_permission(const chainbase::database& db) {
D
Daniel Larimer 已提交
477 478
     return [&db](const permission_level& permission) {
        auto key = boost::make_tuple(permission.actor, permission.permission);
479 480 481 482
        return db.get<permission_object, by_owner>(key);
     };
  }

D
Daniel Larimer 已提交
483 484 485 486
  auto make_authority_checker(const chainbase::database& db, const flat_set<public_key_type>& signing_keys) {
     auto get_permission = make_get_permission(db);
     auto get_authority = [get_permission](const permission_level& permission) {
        return get_permission(permission).auth;
487
     };
D
Daniel Larimer 已提交
488
     auto depth_limit = db.get<global_property_object>().configuration.max_authority_depth;
489
     wdump((depth_limit));
D
Daniel Larimer 已提交
490
     return make_auth_checker( move(get_authority), depth_limit, signing_keys);
491 492 493 494
  }

}

D
Daniel Larimer 已提交
495
flat_set<public_key_type> chain_controller::get_required_keys(const signed_transaction& trx, 
B
Bart Wyatt 已提交
496
                                                              const flat_set<public_key_type>& candidate_keys)const 
D
Daniel Larimer 已提交
497
{
B
Bart Wyatt 已提交
498
   auto checker = make_authority_checker(_db, candidate_keys);
499

D
Daniel Larimer 已提交
500 501 502 503
   for (const auto& act : trx.actions ) {
      for (const auto& declared_auth : act.authorization) {
         if (!checker.satisfied(declared_auth)) {
            EOS_ASSERT(checker.satisfied(declared_auth), tx_missing_sigs,
D
Daniel Larimer 已提交
504
                       "transaction declares authority '${auth}', but does not have signatures for it.",
D
Daniel Larimer 已提交
505
                       ("auth", declared_auth));
506 507 508 509 510 511 512
         }
      }
   }

   return checker.used_keys();
}

D
Daniel Larimer 已提交
513 514 515
void chain_controller::check_transaction_authorization(const signed_transaction& trx, 
                                                       bool allow_unused_signatures)const 
{
N
Nathan Hourt 已提交
516
   if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
517
      //ilog("Skipping auth and sigs checks");
N
Nathan Hourt 已提交
518 519 520
      return;
   }

D
Daniel Larimer 已提交
521 522
   auto get_permission = make_get_permission(_db);
// #warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
523
   auto checker = make_authority_checker(_db, trx.get_signature_keys(chain_id_type{}));
N
Nathan Hourt 已提交
524

525 526
   for( const auto& act : trx.actions )
      for( const auto& declared_auth : act.authorization ) {
D
Daniel Larimer 已提交
527 528 529

         const auto& min_permission = lookup_minimum_permission(declared_auth.actor, act.scope, act.name);

N
Nathan Hourt 已提交
530 531
         if ((_skip_flags & skip_authority_check) == false) {
            const auto& index = _db.get_index<permission_index>().indices();
D
Daniel Larimer 已提交
532
            EOS_ASSERT(get_permission(declared_auth).satisfies(min_permission, index), tx_irrelevant_auth,
D
Daniel Larimer 已提交
533
                       "action declares irrelevant authority '${auth}'; minimum authority is ${min}",
D
Daniel Larimer 已提交
534
                       ("auth", declared_auth)("min", min_permission.name));
N
Nathan Hourt 已提交
535 536
         }
         if ((_skip_flags & skip_transaction_signatures) == false) {
D
Daniel Larimer 已提交
537
            EOS_ASSERT(checker.satisfied(declared_auth), tx_missing_sigs,
D
Daniel Larimer 已提交
538
                       "transaction declares authority '${auth}', but does not have signatures for it.",
D
Daniel Larimer 已提交
539
                       ("auth", declared_auth));
N
Nathan Hourt 已提交
540 541
         }
      }
N
Nathan Hourt 已提交
542

543
   if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
544
      EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
D
Daniel Larimer 已提交
545
                 "transaction bears irrelevant signatures from these keys: ${keys}", ("keys", checker.unused_keys()));
N
Nathan Hourt 已提交
546 547
}

D
Daniel Larimer 已提交
548
void chain_controller::validate_scope( const transaction& trx )const {
D
Daniel Larimer 已提交
549 550 551 552 553 554 555 556
   for( uint32_t i = 1; i < trx.read_scope.size(); ++i )
      EOS_ASSERT( trx.read_scope[i-1] < trx.read_scope[i], transaction_exception, "Scopes must be sorted and unique" );
   for( uint32_t i = 1; i < trx.write_scope.size(); ++i )
      EOS_ASSERT( trx.write_scope[i-1] < trx.write_scope[i], transaction_exception, "Scopes must be sorted and unique" );

   vector<account_name> intersection;
   std::set_intersection( trx.read_scope.begin(), trx.read_scope.end(),
                          trx.write_scope.begin(), trx.write_scope.end(),
557 558
                          std::back_inserter(intersection) );
   FC_ASSERT( intersection.size() == 0, "a transaction may not redeclare scope in readscope" );
559 560
}

D
Daniel Larimer 已提交
561 562 563
const permission_object& chain_controller::lookup_minimum_permission(account_name authorizer_account,
                                                                    account_name scope,
                                                                    action_name act_name) const {
N
Nathan Hourt 已提交
564
   try {
D
Daniel Larimer 已提交
565 566 567
      // First look up a specific link for this message act_name
      auto key = boost::make_tuple(authorizer_account, scope, act_name);
      auto link = _db.find<permission_link_object, by_action_name>(key);
568 569 570
      // If no specific link found, check for a contract-wide default
      if (link == nullptr) {
         get<2>(key) = "";
D
Daniel Larimer 已提交
571
         link = _db.find<permission_link_object, by_action_name>(key);
572 573 574
      }

      // If no specific or default link found, use active permission
D
Daniel Larimer 已提交
575
      auto permission_key = boost::make_tuple<account_name, permission_name>(authorizer_account, config::active_name );
576
      if (link != nullptr)
D
Daniel Larimer 已提交
577 578 579
         get<1>(permission_key) = link->required_permission;
      return _db.get<permission_object, by_owner>(permission_key);
   } FC_CAPTURE_AND_RETHROW((authorizer_account)(scope)(act_name))
N
Nathan Hourt 已提交
580 581
}

D
Daniel Larimer 已提交
582
void chain_controller::validate_uniqueness( const signed_transaction& trx )const {
N
Nathan Hourt 已提交
583
   if( !should_check_for_duplicate_transactions() ) return;
N
Nathan Hourt 已提交
584

N
Nathan Hourt 已提交
585
   auto transaction = _db.find<transaction_object, by_trx_id>(trx.id());
D
Daniel Larimer 已提交
586
   EOS_ASSERT(transaction == nullptr, tx_duplicate, "transaction is not unique");
587
}
588

D
Daniel Larimer 已提交
589
void chain_controller::record_transaction(const signed_transaction& trx) {
590 591
   //Insert transaction into unique transactions database.
    _db.create<transaction_object>([&](transaction_object& transaction) {
D
Daniel Larimer 已提交
592 593
        transaction.trx_id = trx.id(); 
        transaction.expiration = trx.expiration;
594 595 596 597
    });
}


D
Daniel Larimer 已提交
598
void chain_controller::validate_tapos(const transaction& trx)const {
N
Nathan Hourt 已提交
599
   if (!should_check_tapos()) return;
600

D
Daniel Larimer 已提交
601
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.ref_block_num);
602 603

   //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
D
Daniel Larimer 已提交
604
   EOS_ASSERT(trx.verify_reference_block(tapos_block_summary.block_id), transaction_exception,
D
Daniel Larimer 已提交
605
              "transaction's reference block did not match. Is this transaction from a different fork?",
N
Nathan Hourt 已提交
606 607
              ("tapos_summary", tapos_block_summary));
}
608

D
Daniel Larimer 已提交
609 610
void chain_controller::validate_referenced_accounts( const transaction& trx )const 
{ try { 
D
Daniel Larimer 已提交
611
   for( const auto& scope : trx.read_scope )
612
      require_scope(scope);
D
Daniel Larimer 已提交
613
   for( const auto& scope : trx.write_scope )
614
      require_scope(scope);
D
Daniel Larimer 已提交
615

D
Daniel Larimer 已提交
616 617
   for( const auto& act : trx.actions ) {
      require_account(act.scope);
D
Daniel Larimer 已提交
618
      for (const auto& auth : act.authorization )
D
Daniel Larimer 已提交
619
         require_account(auth.actor);
620
   }
D
Daniel Larimer 已提交
621
} FC_CAPTURE_AND_RETHROW() }
D
Daniel Larimer 已提交
622

D
Daniel Larimer 已提交
623
void chain_controller::validate_expiration( const transaction& trx ) const
624
{ try {
D
Daniel Larimer 已提交
625
   fc::time_point now = head_block_time();
D
Daniel Larimer 已提交
626
   const auto& chain_configuration = get_global_properties().configuration;
627

D
Daniel Larimer 已提交
628
   EOS_ASSERT( time_point(trx.expiration) <= now + fc::seconds(chain_configuration.max_transaction_lifetime),
D
Daniel Larimer 已提交
629
              transaction_exception, "transaction expiration is too far in the future",
630
              ("trx.expiration",trx.expiration)("now",now)
D
Daniel Larimer 已提交
631 632
              ("max_til_exp",chain_configuration.max_transaction_lifetime));
   EOS_ASSERT( now <= time_point(trx.expiration), transaction_exception, "transaction is expired",
633 634
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
635

636

D
Daniel Larimer 已提交
637
/*
D
Daniel Larimer 已提交
638 639
void chain_controller::process_message( const transaction& trx, account_name code,
                                        const action& message, actionOutput& output, apply_context* parent_context) {
640
   apply_context apply_ctx(*this, _db, trx, message, code);
N
Nathan Hourt 已提交
641 642
   apply_message(apply_ctx);

643 644 645
   output.notify.reserve( apply_ctx.notified.size() );

   for( uint32_t i = 0; i < apply_ctx.notified.size(); ++i ) {
N
Nathan Hourt 已提交
646
      try {
647 648
         auto notify_code = apply_ctx.notified[i];
         output.notify.push_back( {notify_code} );
649
         process_message(trx, notify_code, message, output.notify.back().output, &apply_ctx);
650 651 652
      } FC_CAPTURE_AND_RETHROW((apply_ctx.notified[i]))
   }

653
   // combine inline messages and process
654
   if (apply_ctx.inline_messages.size() > 0) {
D
Daniel Larimer 已提交
655
      output.inline_transaction = Inlinetransaction(trx);
656 657
      (*output.inline_transaction).messages = std::move(apply_ctx.inline_messages);
   }
658 659

   for( auto& asynctrx : apply_ctx.deferred_transactions ) {
D
Daniel Larimer 已提交
660
      digest_type::encoder enc; fc::raw::pack( enc, trx ); fc::raw::pack( enc, asynctrx );
661
      auto id = enc.result();
D
Daniel Larimer 已提交
662
      auto gtrx = Generatedtransaction(id, asynctrx);
663

664
      _db.create<generated_transaction_object>([&](generated_transaction_object& transaction) {
665
         transaction.trx = gtrx;
666 667 668
         transaction.status = generated_transaction_object::PENDING;
      });

669
      output.deferred_transactions.emplace_back( gtrx );
N
Nathan Hourt 已提交
670
   }
671 672 673 674 675 676 677 678 679 680

   // 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,
D
Daniel Larimer 已提交
681
                 "action declared authorities it did not need: ${unused}",
682
                 ("unused", apply_ctx.unused_authorizations())("message", message));
N
Nathan Hourt 已提交
683
}
D
Daniel Larimer 已提交
684
*/
685

D
Daniel Larimer 已提交
686
/*
687
void chain_controller::apply_message(apply_context& context)
688
{ try {
D
Daniel Larimer 已提交
689 690
    /// context.code => the execution namespace
    /// message.code / message.type => Event
691
    const auto& m = context.msg;
D
Daniel Larimer 已提交
692
    auto contract_handlers_itr = apply_handlers.find(context.code);
693 694 695
    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 已提交
696
          message_handler_itr->second(context);
697 698 699
          return;
       }
    }
D
Daniel Larimer 已提交
700
    const auto& recipient = _db.get<account_object,by_name>(context.code);
701
    if (recipient.code.size()) {
702
       //idump((context.code)(context.msg.type));
703 704
       const uint32_t execution_time =
          _skip_flags | received_block
B
Brian Johnson 已提交
705
             ? _rcvd_block_txn_execution_time
706
             : _skip_flags | created_block
B
Brian Johnson 已提交
707 708
               ? _create_block_txn_execution_time
               : _txn_execution_time;
709 710
       const bool is_received_block = _skip_flags & received_block;
       wasm_interface::get().apply(context, execution_time, is_received_block);
711
    }
712

N
Nathan Hourt 已提交
713
} FC_CAPTURE_AND_RETHROW((context.msg)) }
D
Daniel Larimer 已提交
714
*/
N
Nathan Hourt 已提交
715

716

717 718
void chain_controller::require_scope( const scope_name& scope )const {
   switch( uint64_t(scope) ) {
719 720
      case config::eosio_all_scope:
      case config::eosio_auth_scope:
721 722 723 724 725 726
         return; /// built in scopes
      default:
         require_account(scope);
   }
}

D
Daniel Larimer 已提交
727
void chain_controller::require_account(const account_name& name) const {
N
Nathan Hourt 已提交
728 729 730
   auto account = _db.find<account_object, by_name>(name);
   FC_ASSERT(account != nullptr, "Account not found: ${name}", ("name", name));
}
N
Nathan Hourt 已提交
731

732
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
733 734
   EOS_ASSERT(head_block_id() == next_block.previous, block_validate_exception, "",
              ("head_block_id",head_block_id())("next.prev",next_block.previous));
D
Daniel Larimer 已提交
735
   EOS_ASSERT(head_block_time() < (fc::time_point)next_block.timestamp, block_validate_exception, "",
736
              ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()));
D
Daniel Larimer 已提交
737 738
   if (next_block.block_num() % config::blocks_per_round != 0) {
      EOS_ASSERT(!next_block.new_producers, block_validate_exception,
739
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
740
   }
D
Daniel Larimer 已提交
741 742
   
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
743

N
Nathan Hourt 已提交
744
   if(!(skip&skip_producer_signature))
745 746 747
      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 已提交
748

749
   if(!(skip&skip_producer_schedule_check)) {
750 751 752
      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 已提交
753 754 755 756 757
   }

   return producer;
}

758
void chain_controller::create_block_summary(const signed_block& next_block) {
759
   auto sid = next_block.block_num() & 0xffff;
760
   _db.modify( _db.get<block_summary_object,by_id>(sid), [&](block_summary_object& p) {
761 762
         p.block_id = next_block.id();
   });
N
Nathan Hourt 已提交
763 764
}

765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
/**
 *  Takes the top config::producer_count producers by total vote excluding any producer whose
 *  block_signing_key is null.  
 */
producer_schedule_type chain_controller::_calculate_producer_schedule()const {
   const auto& producers_by_vote = _db.get_index<contracts::producer_votes_multi_index,contracts::by_votes>();
   auto itr = producers_by_vote.begin();
   producer_schedule_type schedule;
   uint32_t count = 0;
   while( itr != producers_by_vote.end() && count < schedule.size() ) {
      schedule[count].producer_name = itr->owner_name;
      schedule[count].block_signing_key = get_producer(itr->owner_name).signing_key;
      ++itr;
      if( schedule[count].block_signing_key != public_key_type() ) {
         ++count;
      }
   }
   return schedule;
}

/**
 *  Returns the most recent and/or pending producer schedule
 */
const producer_schedule_type& chain_controller::_head_producer_schedule()const {
   const auto& gpo = get_global_properties();
   if( gpo.pending_active_producers.size() ) 
      return gpo.pending_active_producers.back().second;
   return gpo.active_producers;
}

795
void chain_controller::update_global_properties(const signed_block& b) { try {
796 797
   // If we're at the end of a round, update the BlockchainConfiguration, producer schedule
   // and "producers" special account authority
798 799 800 801 802 803 804 805 806
   if( is_start_of_round( b.block_num() ) ) {
      auto schedule = _calculate_producer_schedule();
      if( b.new_producers )
      {
         for( uint32_t i = 0; i < schedule.size(); ++i ) {
            idump((schedule[i])((*b.new_producers)[i]) );
            FC_ASSERT( schedule[i] == (*b.new_producers)[i], "missmatch in expected producers", ("i", i) );
         }
          FC_ASSERT( schedule == *b.new_producers, "pending producer set different than expected" );
807 808 809
      }

      const auto& gpo = get_global_properties();
810 811 812

      if( _head_producer_schedule() != schedule ) {
         FC_ASSERT( b.new_producers, "pending producer set changed but block didn't indicate it" );
813
      }
814 815 816 817 818 819 820 821
      _db.modify( gpo, [&]( auto& props ) {
         if( props.pending_active_producers.size() && props.pending_active_producers.back().first == b.block_num() )
            props.pending_active_producers.back().second = schedule;
         else
            props.pending_active_producers.push_back( make_pair(b.block_num(),schedule) );
      });


D
Daniel Larimer 已提交
822
      /*
D
Daniel Larimer 已提交
823 824
      auto schedule = _admin->get_next_round(_db);
      auto config   = _admin->get_blockchain_configuration(_db, schedule);
825

826
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
827 828 829
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
830

831
      auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {});
832
      for(auto& name : gpo.active_producers) {
833
         active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1});
834 835
      }

D
Daniel Larimer 已提交
836 837
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::producers_account_name, 
                                                                         config::active_level_name ) );
838 839 840
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
D
Daniel Larimer 已提交
841
      */
842
   }
843
} FC_CAPTURE_AND_RETHROW() } 
844

845
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
846
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
847 848 849
      _checkpoints[i.first] = i.second;
}

850
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
851 852 853
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

854
const global_property_object& chain_controller::get_global_properties()const {
855
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
856 857
}

858
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
859
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
860 861
}

D
Daniel Larimer 已提交
862
time_point chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
863 864 865
   return get_dynamic_global_properties().time;
}

866
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
867 868 869
   return get_dynamic_global_properties().head_block_number;
}

870
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
871 872 873
   return get_dynamic_global_properties().head_block_id;
}

D
Daniel Larimer 已提交
874
account_name chain_controller::head_block_producer() const {
875 876 877
   auto b = _fork_db.fetch_block(head_block_id());
   if( b ) return b->data.producer;

N
Nathan Hourt 已提交
878
   if (auto head_block = fetch_block_by_id(head_block_id()))
879
      return head_block->producer;
N
Nathan Hourt 已提交
880 881 882
   return {};
}

D
Daniel Larimer 已提交
883 884
const producer_object& chain_controller::get_producer(const account_name& owner_name) const 
{ try {
B
Bart Wyatt 已提交
885
   return _db.get<producer_object, by_owner>(owner_name);
D
Daniel Larimer 已提交
886
} FC_CAPTURE_AND_RETHROW( (owner_name) ) }
N
Nathan Hourt 已提交
887

888
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
889
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
890 891
}

D
Daniel Larimer 已提交
892
void chain_controller::_initialize_indexes() {
893 894
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
895
   _db.add_index<permission_link_index>();
896 897
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
M
Matias Romeo 已提交
898
   _db.add_index<keystr_value_index>();
899
   _db.add_index<key128x128_value_index>();
900
   _db.add_index<key64x64x64_value_index>();
901 902 903 904 905

   _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>();
906
   _db.add_index<generated_transaction_multi_index>();
907
   _db.add_index<producer_multi_index>();
N
Nathan Hourt 已提交
908 909
}

D
Daniel Larimer 已提交
910
void chain_controller::_initialize_chain(contracts::chain_initializer& starter)
N
Nathan Hourt 已提交
911
{ try {
912
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
913 914
      _db.with_write_lock([this, &starter] {
         auto initial_timestamp = starter.get_chain_start_time();
D
Daniel Larimer 已提交
915 916 917
         FC_ASSERT(initial_timestamp != time_point(), "Must initialize genesis timestamp." );
         FC_ASSERT( block_timestamp_type(initial_timestamp) == initial_timestamp,
                    "Genesis timestamp must be divisible by config::block_interval_ms" );
N
Nathan Hourt 已提交
918

919
         // Create global properties
N
Nathan Hourt 已提交
920 921 922
         _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();
923
         });
924

925
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
926
            p.time = initial_timestamp;
927 928
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
929

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

D
Daniel Larimer 已提交
934 935
         auto acts = starter.prepare_database(*this, _db);

D
Daniel Larimer 已提交
936
         transaction genesis_setup_transaction;
937
         genesis_setup_transaction.write_scope = { config::eosio_all_scope };
D
Daniel Larimer 已提交
938 939 940 941 942 943
         genesis_setup_transaction.actions = move(acts);

         ilog( "applying genesis transaction" );
         with_skip_flags(skip_scope_check | skip_transaction_signatures | skip_authority_check | received_block, 
         [&](){ 
            _apply_transaction( genesis_setup_transaction );
944
         });
945

946 947
      });
   }
N
Nathan Hourt 已提交
948 949
} FC_CAPTURE_AND_RETHROW() }

N
Nathan Hourt 已提交
950

951
void chain_controller::replay() {
952
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
953
   auto start = fc::time_point::now();
K
Kevin Heifner 已提交
954

K
Kevin Heifner 已提交
955
   auto on_exit = fc::make_scoped_exit([&_currently_replaying_blocks = _currently_replaying_blocks](){
K
Kevin Heifner 已提交
956 957 958 959
      _currently_replaying_blocks = false;
   });
   _currently_replaying_blocks = true;

960 961 962
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
963 964 965 966 967
      return;
   }

   const auto last_block_num = last_block->block_num();

968
   ilog("Replaying ${n} blocks...", ("n", last_block_num) );
969 970 971 972 973
   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));
D
Daniel Larimer 已提交
974
      _apply_block(*block, skip_producer_signature |
N
Nathan Hourt 已提交
975 976 977 978
                          skip_transaction_signatures |
                          skip_transaction_dupe_check |
                          skip_tapos_check |
                          skip_producer_schedule_check |
979 980
                          skip_authority_check |
                          received_block);
N
Nathan Hourt 已提交
981 982
   }
   auto end = fc::time_point::now();
983 984
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
985

986
   _db.set_revision(head_block_num());
987
}
N
Nathan Hourt 已提交
988

D
Daniel Larimer 已提交
989
void chain_controller::_spinup_db() {
990 991 992 993 994
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
      FC_ASSERT(_db.revision() == head_block_num(), "Chainbase revision does not match head block num",
                ("rev", _db.revision())("head_block", head_block_num()));
995

996 997
   });
}
N
Nathan Hourt 已提交
998

D
Daniel Larimer 已提交
999
void chain_controller::_spinup_fork_db()
N
Nathan Hourt 已提交
1000
{
1001 1002 1003 1004 1005 1006 1007 1008
   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 已提交
1009 1010
}

D
Daniel Larimer 已提交
1011
/*
1012 1013
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
1014 1015 1016 1017
   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));
P
Pravin 已提交
1018 1019 1020
   
   fc::time_point tp = (fc::time_point)next_block.timestamp;
   utilities::rand::random rng(tp.sec_since_epoch());
1021 1022
   rng.shuffle(schedule);
   return schedule;
D
Daniel Larimer 已提交
1023
}*/
1024

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

P
Pravin 已提交
1028
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time((fc::time_point)b.timestamp);
1029
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
1030
   missed_blocks--;
N
Nathan Hourt 已提交
1031

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

N
Nathan Hourt 已提交
1035
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
1036
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
1037
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
1038 1039 1040 1041 1042 1043
         /*
         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) );
            */

1044
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
1045 1046 1047 1048 1049 1050
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
1051
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
1052 1053 1054
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
1055
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
1056 1057 1058 1059 1060 1061 1062 1063 1064
      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 已提交
1065 1066 1067 1068 1069
   });

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

1070
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
1071 1072
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
P
Pravin 已提交
1073
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( (fc::time_point)new_block.timestamp );
N
Nathan Hourt 已提交
1074

1075
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
1076 1077 1078 1079 1080 1081
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

1082
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
1083 1084 1085 1086
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

N
Nathan Hourt 已提交
1087 1088
   vector<const producer_object*> producer_objs;
   producer_objs.reserve(gpo.active_producers.size());
D
Daniel Larimer 已提交
1089

N
Nathan Hourt 已提交
1090
   std::transform(gpo.active_producers.begin(), gpo.active_producers.end(), std::back_inserter(producer_objs),
D
Daniel Larimer 已提交
1091
                  [this](const producer_key& pk) { return &get_producer(pk.producer_name); });
N
Nathan Hourt 已提交
1092

D
Daniel Larimer 已提交
1093
   static_assert(config::irreversible_threshold_percent > 0, "irreversible threshold must be nonzero");
N
Nathan Hourt 已提交
1094

D
Daniel Larimer 已提交
1095
   size_t offset = EOS_PERCENT(producer_objs.size(), config::percent_100- config::irreversible_threshold_percent);
1096 1097
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
1098
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
1099
      });
N
Nathan Hourt 已提交
1100

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

1103
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
1104
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
1105
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1106
      });
N
Nathan Hourt 已提交
1107
   }
1108 1109

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1110
   auto old_last_irreversible_block = _block_log.head();
1111 1112 1113 1114
   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();
1115

1116
   if (last_block_on_disk < new_last_irreversible_block_num) {
1117 1118 1119 1120 1121
      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);
1122
         _block_log.append(*block);
K
Kevin Heifner 已提交
1123
         applied_irreversible_block(*block);
1124
      }
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
   }

   if( new_last_irreversible_block_num > last_block_on_disk ) {
      /// TODO: use upper / lower bound to find
      optional<producer_schedule_type> new_producer_schedule;
      for( const auto& item : gpo.pending_active_producers ) {
         if( item.first < new_last_irreversible_block_num ) {
            new_producer_schedule = item.second;
         }
      }
      if( new_producer_schedule ) {
         _db.modify( gpo, [&]( auto& props ){
             /// TODO: use upper / lower bound to remove range
              while( gpo.pending_active_producers.size() ) {
                 if( gpo.pending_active_producers.front().first < new_last_irreversible_block_num ) {
                   props.pending_active_producers.erase(props.pending_active_producers.begin());
                 }
              }
1143
              props.active_producers = *new_producer_schedule;
1144 1145 1146 1147
         });
      }
   }

N
Nathan Hourt 已提交
1148 1149 1150

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

1154
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1155 1156
{ try {
   //Look for expired transactions in the deduplication list, and remove them.
D
Daniel Larimer 已提交
1157
   //transactions must have expired by at least two forking windows in order to be removed.
D
Daniel Larimer 已提交
1158
   /*
1159
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1160
   const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
D
Daniel Larimer 已提交
1161
   while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->expiration) )
N
Nathan Hourt 已提交
1162
      transaction_idx.remove(*dedupe_index.rbegin());
1163 1164

   //Look for expired transactions in the pending generated list, and remove them.
D
Daniel Larimer 已提交
1165
   //transactions must have expired by at least two forking windows in order to be removed.
1166 1167 1168 1169
   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());
D
Daniel Larimer 已提交
1170
      */
N
Nathan Hourt 已提交
1171 1172 1173 1174
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

D
Daniel Larimer 已提交
1175
account_name chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1176 1177
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1178
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1179
   const auto& gpo = _db.get<global_property_object>();
D
Daniel Larimer 已提交
1180 1181 1182 1183 1184
   //auto number_of_active_producers = gpo.active_producers.size();
   auto index = current_aslot % (config::blocks_per_round); //TODO configure number of repetitions by producer
   index /= config::producer_repititions;

   return gpo.active_producers[index].producer_name;
N
Nathan Hourt 已提交
1185 1186
}

D
Daniel Larimer 已提交
1187
block_timestamp_type chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1188
{
P
Pravin 已提交
1189
   if( slot_num == 0)
D
Daniel Larimer 已提交
1190
      return block_timestamp_type();
N
Nathan Hourt 已提交
1191 1192 1193 1194 1195 1196

   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
P
Pravin 已提交
1197 1198 1199
      auto genesis_time = block_timestamp_type(dpo.time);
      genesis_time.slot += slot_num;
      return (fc::time_point)genesis_time;
N
Nathan Hourt 已提交
1200 1201
   }

D
Daniel Larimer 已提交
1202
   auto head_block_abs_slot = block_timestamp_type(head_block_time());
P
Pravin 已提交
1203
   head_block_abs_slot.slot += slot_num;
D
Daniel Larimer 已提交
1204
   return head_block_abs_slot;
N
Nathan Hourt 已提交
1205 1206
}

D
Daniel Larimer 已提交
1207
uint32_t chain_controller::get_slot_at_time( block_timestamp_type when )const
N
Nathan Hourt 已提交
1208
{
D
Daniel Larimer 已提交
1209
   auto first_slot_time = get_slot_time(1);
N
Nathan Hourt 已提交
1210 1211
   if( when < first_slot_time )
      return 0;
D
Daniel Larimer 已提交
1212
   return block_timestamp_type(when).slot - first_slot_time.slot + 1;
N
Nathan Hourt 已提交
1213 1214
}

1215
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1216 1217
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
D
Daniel Larimer 已提交
1218
   return uint64_t(config::percent_100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1219 1220
}

D
Daniel Larimer 已提交
1221 1222
void chain_controller::_set_apply_handler( account_name contract, scope_name scope, action_name action, apply_handler v ) {
   _apply_handlers[contract][make_pair(scope,action)] = v;
1223
}
N
Nathan Hourt 已提交
1224

D
Daniel Larimer 已提交
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
void chain_controller::_apply_transaction( const transaction& trx ) {
   for( const auto& act : trx.actions ) {
      apply_context context( *this, _db, trx, act, act.scope );
      context.exec();
   }
}

const apply_handler* chain_controller::find_apply_handler( account_name receiver, account_name scope, action_name act ) const
{
   auto native_handler_scope = _apply_handlers.find( receiver );
   if( native_handler_scope != _apply_handlers.end() ) {
      auto handler = native_handler_scope->second.find( make_pair( scope, act ) );
      if( handler != native_handler_scope->second.end() ) 
         return &handler->second;
   }
   return nullptr;
}

D
Daniel Larimer 已提交
1243
} } /// eosio::chain