chain_controller.cpp 45.0 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 272 273 274 275 276 277 278
   auto shardnum = _pending_cycle.schedule( trx );
   if( shardnum == -1 ) { /// schedule conflict start new cycle
      _start_cycle();
   }

   auto& bcycle = _pending_block->cycles_summary.back();
   if( shardnum >= bcycle.size() ) {
      bcycle.resize( bcycle.size()+1 );
      bcycle.back().emplace_back( tid );
   }

279 280 281
   _apply_transaction(trx);
   /** for now we will just shove everything into the first shard */
   _pending_block->input_transactions.push_back(trx);
D
Daniel Larimer 已提交
282

N
Nathan Hourt 已提交
283 284 285 286
   // The transaction applied successfully. Merge its changes into the pending block session.
   temp_session.squash();

   // notify anyone listening to pending transactions
D
Daniel Larimer 已提交
287
   on_pending_transaction(trx); 
N
Nathan Hourt 已提交
288 289
}

290 291 292 293 294 295 296 297 298 299 300 301

/**
 *  Wraps up all work for current shards, starts a new cycle, and
 *  executes any pending transactions 
 */
void chain_controller::_start_cycle() {
   _pending_block->cycles_summary.resize( _pending_block->cycles_summary.size() + 1 );
   _pending_cycle = pending_cycle_state();

   /// TODO: check for deferred transactions and schedule them
} // _start_cycle

302
signed_block chain_controller::generate_block(
D
Daniel Larimer 已提交
303 304 305
   block_timestamp_type when,
   account_name producer,
   const private_key_type& block_signing_private_key,
N
Nathan Hourt 已提交
306 307 308
   uint32_t skip /* = 0 */
   )
{ try {
N
Nathan Hourt 已提交
309
   return with_skip_flags( skip, [&](){
D
Daniel Larimer 已提交
310
      return _db.with_write_lock( [&](){
D
Daniel Larimer 已提交
311
         return _generate_block( when, producer, block_signing_private_key );
D
Daniel Larimer 已提交
312
      });
N
Nathan Hourt 已提交
313
   });
D
Daniel Larimer 已提交
314
} FC_CAPTURE_AND_RETHROW( (when) ) }
N
Nathan Hourt 已提交
315

D
Daniel Larimer 已提交
316 317 318 319 320
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 已提交
321 322
   uint32_t slot_num = get_slot_at_time( when );
   FC_ASSERT( slot_num > 0 );
D
Daniel Larimer 已提交
323
   account_name scheduled_producer = get_scheduled_producer( slot_num );
N
Nathan Hourt 已提交
324
   FC_ASSERT( scheduled_producer == producer );
N
Nathan Hourt 已提交
325

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

D
Daniel Larimer 已提交
328
   if( !_pending_block ) {
D
Daniel Larimer 已提交
329
      _start_pending_block();
330 331
   }

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

335 336 337 338 339
   _pending_block->timestamp   = when;
   _pending_block->producer    = producer_obj.owner;
   _pending_block->previous    = head_block_id();
   _pending_block->block_mroot = get_dynamic_global_properties().block_merkle_root.get_root();
   _pending_block->transaction_mroot = _pending_block->calculate_transaction_merkle_root();
340

341 342 343 344
   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;
345 346
   }

N
Nathan Hourt 已提交
347
   if( !(skip & skip_producer_signature) )
D
Daniel Larimer 已提交
348
      _pending_block->sign( block_signing_key );
N
Nathan Hourt 已提交
349

D
Daniel Larimer 已提交
350 351
   _finalize_block( *_pending_block );

352 353
   _pending_block_session->push();

D
Daniel Larimer 已提交
354 355
   auto result = move( *_pending_block );

356 357 358
   _pending_block.reset();
   _pending_block_session.reset();

D
Daniel Larimer 已提交
359
   if (!(skip&skip_fork_db)) {
D
Daniel Larimer 已提交
360
      _fork_db.push_block(result);
N
Nathan Hourt 已提交
361
   }
D
Daniel Larimer 已提交
362
   return result;
N
Nathan Hourt 已提交
363

N
Nathan Hourt 已提交
364
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
365

D
Daniel Larimer 已提交
366 367 368
void chain_controller::_start_pending_block() {
   FC_ASSERT( !_pending_block );
   _pending_block         = signed_block();
369 370
   _pending_block->cycles_summary.resize(1);
   _pending_block->cycles_summary[0].resize(1);
D
Daniel Larimer 已提交
371
   _pending_block_session = _db.start_undo_session(true);
D
Daniel Larimer 已提交
372 373
}

N
Nathan Hourt 已提交
374
/**
N
Nathan Hourt 已提交
375
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
376
 */
377
void chain_controller::pop_block()
N
Nathan Hourt 已提交
378
{ try {
D
Daniel Larimer 已提交
379
   _pending_block_session.reset();
N
Nathan Hourt 已提交
380 381 382 383 384
   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();
385
   _db.undo();
N
Nathan Hourt 已提交
386 387
} FC_CAPTURE_AND_RETHROW() }

388
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
389
{ try {
D
Daniel Larimer 已提交
390
   _pending_block.reset();
D
Daniel Larimer 已提交
391
   _pending_block_session.reset();
N
Nathan Hourt 已提交
392 393 394 395
} FC_CAPTURE_AND_RETHROW() }

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

D
Daniel Larimer 已提交
396
void chain_controller::_apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
397 398
{
   auto block_num = next_block.block_num();
399 400 401 402 403
   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 已提交
404

405
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
406 407
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
N
Nathan Hourt 已提交
408 409 410

   with_applying_block([&] {
      with_skip_flags(skip, [&] {
D
Daniel Larimer 已提交
411
         __apply_block(next_block);
N
Nathan Hourt 已提交
412 413
      });
   });
N
Nathan Hourt 已提交
414 415
}

416

D
Daniel Larimer 已提交
417
void chain_controller::__apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
418
{ try {
N
Nathan Hourt 已提交
419
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
420

D
Daniel Larimer 已提交
421 422 423
   /*
   FC_ASSERT((skip & skip_merkle_check) 
             || next_block.transaction_merkle_root == next_block.calculate_merkle_root(),
424 425
             "", ("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 已提交
426
             */
N
Nathan Hourt 已提交
427 428

   const producer_object& signing_producer = validate_block_header(skip, next_block);
429 430 431 432 433 434 435 436


   /// 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 已提交
437
   
438 439 440 441 442 443 444 445 446 447 448 449 450 451
   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);
452 453
            // Check authorization, and allow irrelevant signatures.
            // If the block producer let it slide, we'll roll with it.
454 455 456 457
            // check_transaction_authorization(trx, true);
         } /// for each transaction id
      } /// for each shard
   } /// for each cycle
N
Nathan Hourt 已提交
458

459
   _finalize_block( next_block );
N
Nathan Hourt 已提交
460 461
} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) )  }

D
Daniel Larimer 已提交
462 463 464 465
/**
 *  After applying all transactions successfully we can update
 *  the current block time, block number, producer stats, etc
 */
466
void chain_controller::_finalize_block( const signed_block& b ) { try {
D
Daniel Larimer 已提交
467 468 469 470 471 472 473 474 475 476
   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();

477
   applied_block( b ); //emit
D
Daniel Larimer 已提交
478 479
   if (_currently_replaying_blocks)
     applied_irreversible_block(b);
480 481

} FC_CAPTURE_AND_RETHROW( (b) ) }
D
Daniel Larimer 已提交
482

D
Daniel Larimer 已提交
483
flat_set<public_key_type> chain_controller::get_required_keys(const signed_transaction& trx, 
B
Bart Wyatt 已提交
484
                                                              const flat_set<public_key_type>& candidate_keys)const 
D
Daniel Larimer 已提交
485
{
486 487 488
   auto checker = make_auth_checker( [&](auto p){ return get_permission(p).auth; }, 
                                     get_global_properties().configuration.max_authority_depth,
                                     candidate_keys);
489

D
Daniel Larimer 已提交
490 491 492 493
   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 已提交
494
                       "transaction declares authority '${auth}', but does not have signatures for it.",
D
Daniel Larimer 已提交
495
                       ("auth", declared_auth));
496 497 498 499 500 501 502
         }
      }
   }

   return checker.used_keys();
}

503 504 505 506
void chain_controller::check_authorization( const transaction& trx, 
                                            flat_set<public_key_type> provided_keys,
                                            bool allow_unused_signatures,
                                            flat_set<account_name>    provided_accounts  )const
D
Daniel Larimer 已提交
507
{
508 509
   auto checker = make_auth_checker( [&](auto p){ return get_permission(p).auth; }, 
                                     get_global_properties().configuration.max_authority_depth,
510 511
                                     provided_keys, provided_accounts );

N
Nathan Hourt 已提交
512

513
   for( const auto& act : trx.actions ) {
514
      for( const auto& declared_auth : act.authorization ) {
D
Daniel Larimer 已提交
515

516 517
         const auto& min_permission = lookup_minimum_permission(declared_auth.actor, 
                                                                act.scope, act.name);
D
Daniel Larimer 已提交
518

N
Nathan Hourt 已提交
519 520
         if ((_skip_flags & skip_authority_check) == false) {
            const auto& index = _db.get_index<permission_index>().indices();
521 522
            EOS_ASSERT(get_permission(declared_auth).satisfies(min_permission, index), 
                       tx_irrelevant_auth,
D
Daniel Larimer 已提交
523
                       "action declares irrelevant authority '${auth}'; minimum authority is ${min}",
D
Daniel Larimer 已提交
524
                       ("auth", declared_auth)("min", min_permission.name));
N
Nathan Hourt 已提交
525 526
         }
         if ((_skip_flags & skip_transaction_signatures) == false) {
D
Daniel Larimer 已提交
527
            EOS_ASSERT(checker.satisfied(declared_auth), tx_missing_sigs,
D
Daniel Larimer 已提交
528
                       "transaction declares authority '${auth}', but does not have signatures for it.",
D
Daniel Larimer 已提交
529
                       ("auth", declared_auth));
N
Nathan Hourt 已提交
530 531
         }
      }
532
   }
N
Nathan Hourt 已提交
533

534
   if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
535
      EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
536 537
                 "transaction bears irrelevant signatures from these keys: ${keys}", 
                 ("keys", checker.unused_keys()));
N
Nathan Hourt 已提交
538 539
}

540 541 542 543 544 545
void chain_controller::check_transaction_authorization(const signed_transaction& trx, 
                                                       bool allow_unused_signatures)const 
{
   check_authorization( trx, trx.get_signature_keys( chain_id_type{} ), allow_unused_signatures );
}

D
Daniel Larimer 已提交
546
void chain_controller::validate_scope( const transaction& trx )const {
547 548 549 550 551 552 553 554
   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" );
   }
D
Daniel Larimer 已提交
555 556 557 558

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

D
Daniel Larimer 已提交
563 564 565
const permission_object& chain_controller::lookup_minimum_permission(account_name authorizer_account,
                                                                    account_name scope,
                                                                    action_name act_name) const {
N
Nathan Hourt 已提交
566
   try {
D
Daniel Larimer 已提交
567 568 569
      // 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);
570 571 572
      // If no specific link found, check for a contract-wide default
      if (link == nullptr) {
         get<2>(key) = "";
D
Daniel Larimer 已提交
573
         link = _db.find<permission_link_object, by_action_name>(key);
574 575 576
      }

      // If no specific or default link found, use active permission
D
Daniel Larimer 已提交
577
      auto permission_key = boost::make_tuple<account_name, permission_name>(authorizer_account, config::active_name );
578
      if (link != nullptr)
D
Daniel Larimer 已提交
579 580 581
         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 已提交
582 583
}

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

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

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


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

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

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

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

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

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

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

638

639 640
void chain_controller::require_scope( const scope_name& scope )const {
   switch( uint64_t(scope) ) {
641 642
      case config::eosio_all_scope:
      case config::eosio_auth_scope:
643 644 645 646 647 648
         return; /// built in scopes
      default:
         require_account(scope);
   }
}

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

654
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
655 656
   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 已提交
657
   EOS_ASSERT(head_block_time() < (fc::time_point)next_block.timestamp, block_validate_exception, "",
658
              ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()));
D
Daniel Larimer 已提交
659 660
   if (next_block.block_num() % config::blocks_per_round != 0) {
      EOS_ASSERT(!next_block.new_producers, block_validate_exception,
661
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
662
   }
D
Daniel Larimer 已提交
663 664
   
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
665

N
Nathan Hourt 已提交
666
   if(!(skip&skip_producer_signature))
667 668 669
      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 已提交
670

671
   if(!(skip&skip_producer_schedule_check)) {
672 673 674
      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 已提交
675 676
   }

677 678 679
   
   FC_ASSERT( next_block.calculate_transaction_merkle_root() == next_block.transaction_mroot, "merkle root does not match" );

N
Nathan Hourt 已提交
680 681 682
   return producer;
}

683
void chain_controller::create_block_summary(const signed_block& next_block) {
684
   auto sid = next_block.block_num() & 0xffff;
685
   _db.modify( _db.get<block_summary_object,by_id>(sid), [&](block_summary_object& p) {
686 687
         p.block_id = next_block.id();
   });
N
Nathan Hourt 已提交
688 689
}

690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
/**
 *  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;
}

720
void chain_controller::update_global_properties(const signed_block& b) { try {
721 722
   // If we're at the end of a round, update the BlockchainConfiguration, producer schedule
   // and "producers" special account authority
723 724 725 726 727 728 729 730 731
   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" );
732 733 734
      }

      const auto& gpo = get_global_properties();
735 736 737

      if( _head_producer_schedule() != schedule ) {
         FC_ASSERT( b.new_producers, "pending producer set changed but block didn't indicate it" );
738
      }
739 740 741 742 743 744 745 746
      _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) );
      });


747

748
      auto active_producers_authority = authority(config::producers_authority_threshold, {}, {});
749
      for(auto& name : gpo.active_producers) {
750
         active_producers_authority.accounts.push_back({{name.producer_name, config::active_name}, 1});
751 752
      }

D
Daniel Larimer 已提交
753
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::producers_account_name, 
754
                                                                         config::active_name ) );
755 756 757
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
758
   }
759
} FC_CAPTURE_AND_RETHROW() } 
760

761
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
762
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
763 764 765
      _checkpoints[i.first] = i.second;
}

766
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
767 768 769
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

770
const global_property_object& chain_controller::get_global_properties()const {
771
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
772 773
}

774
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
775
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
776 777
}

D
Daniel Larimer 已提交
778
time_point chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
779 780 781
   return get_dynamic_global_properties().time;
}

782
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
783 784 785
   return get_dynamic_global_properties().head_block_number;
}

786
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
787 788 789
   return get_dynamic_global_properties().head_block_id;
}

D
Daniel Larimer 已提交
790
account_name chain_controller::head_block_producer() const {
791 792 793
   auto b = _fork_db.fetch_block(head_block_id());
   if( b ) return b->data.producer;

N
Nathan Hourt 已提交
794
   if (auto head_block = fetch_block_by_id(head_block_id()))
795
      return head_block->producer;
N
Nathan Hourt 已提交
796 797 798
   return {};
}

D
Daniel Larimer 已提交
799 800
const producer_object& chain_controller::get_producer(const account_name& owner_name) const 
{ try {
B
Bart Wyatt 已提交
801
   return _db.get<producer_object, by_owner>(owner_name);
D
Daniel Larimer 已提交
802
} FC_CAPTURE_AND_RETHROW( (owner_name) ) }
N
Nathan Hourt 已提交
803

804 805 806 807 808
const permission_object&   chain_controller::get_permission( const permission_level& level )const 
{ try {
   return _db.get<permission_object, by_owner>( boost::make_tuple(level.actor,level.permission) );
} FC_CAPTURE_AND_RETHROW( (level) ) }

809
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
810
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
811 812
}

D
Daniel Larimer 已提交
813
void chain_controller::_initialize_indexes() {
814 815
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
816
   _db.add_index<permission_link_index>();
817 818
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
M
Matias Romeo 已提交
819
   _db.add_index<keystr_value_index>();
820
   _db.add_index<key128x128_value_index>();
821
   _db.add_index<key64x64x64_value_index>();
822 823 824 825 826

   _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>();
827
   _db.add_index<generated_transaction_multi_index>();
828
   _db.add_index<producer_multi_index>();
N
Nathan Hourt 已提交
829 830
}

D
Daniel Larimer 已提交
831
void chain_controller::_initialize_chain(contracts::chain_initializer& starter)
N
Nathan Hourt 已提交
832
{ try {
833
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
834 835
      _db.with_write_lock([this, &starter] {
         auto initial_timestamp = starter.get_chain_start_time();
D
Daniel Larimer 已提交
836 837 838
         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 已提交
839

840
         // Create global properties
N
Nathan Hourt 已提交
841 842 843
         _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();
844
         });
845

846
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
847
            p.time = initial_timestamp;
848 849
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
850

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

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

D
Daniel Larimer 已提交
857
         transaction genesis_setup_transaction;
858
         genesis_setup_transaction.write_scope = { config::eosio_all_scope };
D
Daniel Larimer 已提交
859 860 861 862 863 864
         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 );
865
         });
866

867 868
      });
   }
N
Nathan Hourt 已提交
869 870
} FC_CAPTURE_AND_RETHROW() }

N
Nathan Hourt 已提交
871

872
void chain_controller::replay() {
873
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
874
   auto start = fc::time_point::now();
K
Kevin Heifner 已提交
875

K
Kevin Heifner 已提交
876
   auto on_exit = fc::make_scoped_exit([&_currently_replaying_blocks = _currently_replaying_blocks](){
K
Kevin Heifner 已提交
877 878 879 880
      _currently_replaying_blocks = false;
   });
   _currently_replaying_blocks = true;

881 882 883
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
884 885 886 887 888
      return;
   }

   const auto last_block_num = last_block->block_num();

889
   ilog("Replaying ${n} blocks...", ("n", last_block_num) );
890 891 892 893 894
   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 已提交
895
      _apply_block(*block, skip_producer_signature |
N
Nathan Hourt 已提交
896 897 898 899
                          skip_transaction_signatures |
                          skip_transaction_dupe_check |
                          skip_tapos_check |
                          skip_producer_schedule_check |
900 901
                          skip_authority_check |
                          received_block);
N
Nathan Hourt 已提交
902 903
   }
   auto end = fc::time_point::now();
904 905
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
906

907
   _db.set_revision(head_block_num());
908
}
N
Nathan Hourt 已提交
909

D
Daniel Larimer 已提交
910
void chain_controller::_spinup_db() {
911 912 913 914 915
   // 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()));
916

917 918
   });
}
N
Nathan Hourt 已提交
919

D
Daniel Larimer 已提交
920
void chain_controller::_spinup_fork_db()
N
Nathan Hourt 已提交
921
{
922 923 924 925 926 927 928 929
   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 已提交
930 931
}

D
Daniel Larimer 已提交
932
/*
933 934
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
935 936 937 938
   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 已提交
939 940 941
   
   fc::time_point tp = (fc::time_point)next_block.timestamp;
   utilities::rand::random rng(tp.sec_since_epoch());
942 943
   rng.shuffle(schedule);
   return schedule;
D
Daniel Larimer 已提交
944
}*/
945

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

949 950 951
   const auto& bmroot = _dgp.block_merkle_root.get_root();
   FC_ASSERT( bmroot == b.block_mroot, "block merkle root does not match expected value" );

P
Pravin 已提交
952
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time((fc::time_point)b.timestamp);
953
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
954
   missed_blocks--;
N
Nathan Hourt 已提交
955

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

N
Nathan Hourt 已提交
959
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
960
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
961
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
962 963 964 965 966 967
         /*
         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) );
            */

968
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
969 970 971 972 973 974
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
975
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
976 977 978
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
979
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
980 981 982 983 984 985 986
      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;
987
      } else {
N
Nathan Hourt 已提交
988
         dgp.recent_slots_filled = 0;
989 990
      }
      dgp.block_merkle_root.append( head_block_id() ); 
N
Nathan Hourt 已提交
991 992 993 994 995
   });

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

996
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
997 998
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
P
Pravin 已提交
999
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( (fc::time_point)new_block.timestamp );
N
Nathan Hourt 已提交
1000

1001
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
1002 1003 1004 1005 1006 1007
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

1008
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
1009 1010 1011 1012
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

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

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

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

D
Daniel Larimer 已提交
1021
   size_t offset = EOS_PERCENT(producer_objs.size(), config::percent_100- config::irreversible_threshold_percent);
1022 1023
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
1024
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
1025
      });
N
Nathan Hourt 已提交
1026

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

1029
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
1030
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
1031
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1032
      });
N
Nathan Hourt 已提交
1033
   }
1034 1035

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1036
   auto old_last_irreversible_block = _block_log.head();
1037 1038 1039 1040
   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();
1041

1042
   if (last_block_on_disk < new_last_irreversible_block_num) {
1043 1044 1045 1046 1047
      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);
1048
         _block_log.append(*block);
K
Kevin Heifner 已提交
1049
         applied_irreversible_block(*block);
1050
      }
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
   }

   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());
                 }
              }
1069
              props.active_producers = *new_producer_schedule;
1070 1071 1072 1073
         });
      }
   }

N
Nathan Hourt 已提交
1074 1075 1076

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

1080
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1081 1082
{ try {
   //Look for expired transactions in the deduplication list, and remove them.
D
Daniel Larimer 已提交
1083
   //transactions must have expired by at least two forking windows in order to be removed.
D
Daniel Larimer 已提交
1084
   /*
1085
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1086
   const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
D
Daniel Larimer 已提交
1087
   while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->expiration) )
N
Nathan Hourt 已提交
1088
      transaction_idx.remove(*dedupe_index.rbegin());
1089 1090

   //Look for expired transactions in the pending generated list, and remove them.
D
Daniel Larimer 已提交
1091
   //transactions must have expired by at least two forking windows in order to be removed.
1092 1093 1094 1095
   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 已提交
1096
      */
N
Nathan Hourt 已提交
1097 1098 1099 1100
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

D
Daniel Larimer 已提交
1101
account_name chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1102 1103
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1104
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1105
   const auto& gpo = _db.get<global_property_object>();
D
Daniel Larimer 已提交
1106 1107 1108 1109 1110
   //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 已提交
1111 1112
}

D
Daniel Larimer 已提交
1113
block_timestamp_type chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1114
{
P
Pravin 已提交
1115
   if( slot_num == 0)
D
Daniel Larimer 已提交
1116
      return block_timestamp_type();
N
Nathan Hourt 已提交
1117 1118 1119 1120 1121 1122

   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 已提交
1123 1124 1125
      auto genesis_time = block_timestamp_type(dpo.time);
      genesis_time.slot += slot_num;
      return (fc::time_point)genesis_time;
N
Nathan Hourt 已提交
1126 1127
   }

D
Daniel Larimer 已提交
1128
   auto head_block_abs_slot = block_timestamp_type(head_block_time());
P
Pravin 已提交
1129
   head_block_abs_slot.slot += slot_num;
D
Daniel Larimer 已提交
1130
   return head_block_abs_slot;
N
Nathan Hourt 已提交
1131 1132
}

D
Daniel Larimer 已提交
1133
uint32_t chain_controller::get_slot_at_time( block_timestamp_type when )const
N
Nathan Hourt 已提交
1134
{
D
Daniel Larimer 已提交
1135
   auto first_slot_time = get_slot_time(1);
N
Nathan Hourt 已提交
1136 1137
   if( when < first_slot_time )
      return 0;
D
Daniel Larimer 已提交
1138
   return block_timestamp_type(when).slot - first_slot_time.slot + 1;
N
Nathan Hourt 已提交
1139 1140
}

1141
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1142 1143
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
D
Daniel Larimer 已提交
1144
   return uint64_t(config::percent_100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1145 1146
}

D
Daniel Larimer 已提交
1147 1148
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;
1149
}
N
Nathan Hourt 已提交
1150

D
Daniel Larimer 已提交
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168
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 已提交
1169
} } /// eosio::chain