chain_controller.cpp 42.9 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>
N
Nathan Hourt 已提交
18

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

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

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

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

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

P
Pravin 已提交
37
namespace eosio { namespace chain {
38
bool chain_controller::is_known_block(const block_id_type& id)const
N
Nathan Hourt 已提交
39
{
40
   return _fork_db.is_known_block(id) || _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
41 42 43 44 45 46
}
/**
 * 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.
 */
47
bool chain_controller::is_known_transaction(const transaction_id_type& id)const
N
Nathan Hourt 已提交
48
{
49
   const auto& trx_idx = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
50 51 52
   return trx_idx.find( id ) != trx_idx.end();
}

53
block_id_type chain_controller::get_block_id_for_num(uint32_t block_num)const
N
Nathan Hourt 已提交
54
{ try {
55 56
   if (const auto& block = fetch_block_by_number(block_num))
      return block->id();
N
Nathan Hourt 已提交
57

58 59 60
   FC_THROW_EXCEPTION(unknown_block_exception, "Could not find block");
} FC_CAPTURE_AND_RETHROW((block_num)) }

61
optional<signed_block> chain_controller::fetch_block_by_id(const block_id_type& id)const
N
Nathan Hourt 已提交
62
{
63 64
   auto b = _fork_db.fetch_block(id);
   if(b) return b->data;
65
   return _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
66 67
}

68
optional<signed_block> chain_controller::fetch_block_by_number(uint32_t num)const
N
Nathan Hourt 已提交
69
{
70
   if (const auto& block = _block_log.read_block_by_num(num))
71 72
      return *block;

N
Nathan Hourt 已提交
73
   // Not in _block_log, so it must be since the last irreversible block. Grab it from _fork_db instead
74 75 76 77 78 79 80 81
   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 已提交
82 83 84
   return optional<signed_block>();
}

D
Daniel Larimer 已提交
85
/*
D
Daniel Larimer 已提交
86
const signed_transaction& chain_controller::get_recent_transaction(const transaction_id_type& trx_id) const
N
Nathan Hourt 已提交
87
{
88
   auto& index = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
89 90 91 92
   auto itr = index.find(trx_id);
   FC_ASSERT(itr != index.end());
   return itr->trx;
}
D
Daniel Larimer 已提交
93
*/
N
Nathan Hourt 已提交
94

95
std::vector<block_id_type> chain_controller::get_block_ids_on_fork(block_id_type head_of_fork) const
N
Nathan Hourt 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
{
  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;
}

113

N
Nathan Hourt 已提交
114 115 116 117 118 119
/**
 * 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.
 */
120
bool chain_controller::push_block(const signed_block& new_block, uint32_t skip)
D
Daniel Larimer 已提交
121 122 123
{ try {
   return with_skip_flags( skip, [&](){ 
      return without_pending_transactions( [&]() {
124
         return _db.with_write_lock( [&]() {
125 126
            return _push_block(new_block);
         } );
N
Nathan Hourt 已提交
127 128
      });
   });
129
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
130

131
bool chain_controller::_push_block(const signed_block& new_block)
N
Nathan Hourt 已提交
132
{ try {
N
Nathan Hourt 已提交
133
   uint32_t skip = _skip_flags;
134
   if (!(skip&skip_fork_db)) {
N
Nathan Hourt 已提交
135
      /// TODO: if the block is greater than the head block and before the next maintenance interval
N
Nathan Hourt 已提交
136 137 138 139
      // 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.
140
      if (new_head->data.previous != head_block_id()) {
N
Nathan Hourt 已提交
141 142
         //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
143 144
         if (new_head->data.block_num() > head_block_num()) {
            wlog("Switching to fork: ${id}", ("id",new_head->data.id()));
N
Nathan Hourt 已提交
145 146 147
            auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());

            // pop blocks until we hit the forked block
148
            while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
149 150 151
               pop_block();

            // push all blocks on the new fork
152 153
            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 已提交
154 155
                optional<fc::exception> except;
                try {
156
                   auto session = _db.start_undo_session(true);
157
                   apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
158 159
                   session.push();
                }
160 161 162
                catch (const fc::exception& e) { except = e; }
                if (except) {
                   wlog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
N
Nathan Hourt 已提交
163
                   // remove the rest of branches.first from the fork_db, those blocks are invalid
164 165
                   while (ritr != branches.first.rend()) {
                      _fork_db.remove((*ritr)->data.id());
N
Nathan Hourt 已提交
166 167
                      ++ritr;
                   }
168
                   _fork_db.set_head(branches.second.front());
N
Nathan Hourt 已提交
169 170

                   // pop all blocks from the bad fork
171
                   while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
172 173 174
                      pop_block();

                   // restore all blocks from the good fork
175
                   for (auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr) {
176
                      auto session = _db.start_undo_session(true);
177
                      apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
178 179 180 181 182 183 184 185 186 187 188 189
                      session.push();
                   }
                   throw *except;
                }
            }
            return true;
         }
         else return false;
      }
   }

   try {
190
      auto session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
191 192 193 194 195 196 197 198 199
      apply_block(new_block, skip);
      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;
200
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
201 202 203 204 205 206 207 208 209 210

/**
 * 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 已提交
211
void chain_controller::push_transaction(const signed_transaction& trx, uint32_t skip)
N
Nathan Hourt 已提交
212
{ try {
213 214 215
   return with_skip_flags(skip, [&]() {
      return _db.with_write_lock([&]() {
         return _push_transaction(trx);
D
Daniel Larimer 已提交
216
      });
N
Nathan Hourt 已提交
217 218
   });
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
219

D
Daniel Larimer 已提交
220
void chain_controller::_push_transaction(const signed_transaction& trx) {
221 222
   // 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 已提交
223
   if( !_pending_block ) {
D
Daniel Larimer 已提交
224
      _start_pending_block();
D
Daniel Larimer 已提交
225
   }
226

227
   auto temp_session = _db.start_undo_session(true);
D
Daniel Larimer 已提交
228 229

#if 0
230
   validate_referenced_accounts(trx);
N
Nathan Hourt 已提交
231
   check_transaction_authorization(trx);
D
Daniel Larimer 已提交
232
   /*auto pt = */_apply_transaction(trx);
D
Daniel Larimer 已提交
233
   _pending_transactions.push_back(trx);
N
Nathan Hourt 已提交
234

D
Daniel Larimer 已提交
235 236
#endif

N
Nathan Hourt 已提交
237 238 239 240 241
   // notify_changed_objects();
   // The transaction applied successfully. Merge its changes into the pending block session.
   temp_session.squash();

   // notify anyone listening to pending transactions
D
Daniel Larimer 已提交
242
   on_pending_transaction(trx); 
N
Nathan Hourt 已提交
243 244
}

245
signed_block chain_controller::generate_block(
D
Daniel Larimer 已提交
246 247 248
   block_timestamp_type when,
   account_name producer,
   const private_key_type& block_signing_private_key,
N
Nathan Hourt 已提交
249 250 251
   uint32_t skip /* = 0 */
   )
{ try {
N
Nathan Hourt 已提交
252 253
   return with_skip_flags( skip, [&](){
      auto b = _db.with_write_lock( [&](){
D
Daniel Larimer 已提交
254
         return _generate_block( when, producer, block_signing_private_key );
D
Daniel Larimer 已提交
255
      });
D
Daniel Larimer 已提交
256
//      push_block(b, skip);
N
Nathan Hourt 已提交
257 258
      return b;
   });
D
Daniel Larimer 已提交
259
} FC_CAPTURE_AND_RETHROW( (when) ) }
N
Nathan Hourt 已提交
260

D
Daniel Larimer 已提交
261 262 263 264 265
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 已提交
266 267
   uint32_t slot_num = get_slot_at_time( when );
   FC_ASSERT( slot_num > 0 );
D
Daniel Larimer 已提交
268
   account_name scheduled_producer = get_scheduled_producer( slot_num );
N
Nathan Hourt 已提交
269
   FC_ASSERT( scheduled_producer == producer );
N
Nathan Hourt 已提交
270

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

D
Daniel Larimer 已提交
273
   if( !_pending_block ) {
D
Daniel Larimer 已提交
274
      _start_pending_block();
275 276
   }

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

D
Daniel Larimer 已提交
280 281
   _pending_block->timestamp = when;
   _pending_block->producer = producer_obj.owner;
282

N
Nathan Hourt 已提交
283
   if( !(skip & skip_producer_signature) )
D
Daniel Larimer 已提交
284
      _pending_block->sign( block_signing_key );
N
Nathan Hourt 已提交
285

D
Daniel Larimer 已提交
286 287 288 289 290
   auto result = move( *_pending_block );

   if (!(skip&skip_fork_db)) {
      /*shared_ptr<fork_item> new_head = */_fork_db.push_block(result);
      //FC_ASSERT( new_head.id == new_block.id )
N
Nathan Hourt 已提交
291 292
   }

D
Daniel Larimer 已提交
293
   _pending_block_session->push();
D
Daniel Larimer 已提交
294 295
   _pending_block.reset();
   return result;
N
Nathan Hourt 已提交
296

N
Nathan Hourt 已提交
297
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
298

D
Daniel Larimer 已提交
299 300 301 302
void chain_controller::_start_pending_block() {
   FC_ASSERT( !_pending_block );
   _pending_block         = signed_block();
   _pending_block_session = _db.start_undo_session(true);
D
Daniel Larimer 已提交
303 304
}

N
Nathan Hourt 已提交
305
/**
N
Nathan Hourt 已提交
306
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
307
 */
308
void chain_controller::pop_block()
N
Nathan Hourt 已提交
309
{ try {
D
Daniel Larimer 已提交
310
   _pending_block_session.reset();
N
Nathan Hourt 已提交
311 312 313 314 315
   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();
316
   _db.undo();
N
Nathan Hourt 已提交
317 318
} FC_CAPTURE_AND_RETHROW() }

319
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
320
{ try {
D
Daniel Larimer 已提交
321
   _pending_block.reset();
D
Daniel Larimer 已提交
322
   _pending_block_session.reset();
N
Nathan Hourt 已提交
323 324 325 326
} FC_CAPTURE_AND_RETHROW() }

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

327
void chain_controller::apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
328 329
{
   auto block_num = next_block.block_num();
330 331 332 333 334
   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 已提交
335

336
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
337 338
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
N
Nathan Hourt 已提交
339 340 341 342 343 344

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

347

348
void chain_controller::_apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
349
{ try {
N
Nathan Hourt 已提交
350
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
351

D
Daniel Larimer 已提交
352 353 354
   /*
   FC_ASSERT((skip & skip_merkle_check) 
             || next_block.transaction_merkle_root == next_block.calculate_merkle_root(),
355 356
             "", ("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 已提交
357
             */
N
Nathan Hourt 已提交
358 359

   const producer_object& signing_producer = validate_block_header(skip, next_block);
N
Nathan Hourt 已提交
360
   
D
Daniel Larimer 已提交
361
   /*
N
Nathan Hourt 已提交
362 363
   for (const auto& cycle : next_block.cycles)
      for (const auto& thread : cycle)
364 365
         for (const auto& trx : thread.user_input) {
            validate_referenced_accounts(trx);
366 367 368
            // Check authorization, and allow irrelevant signatures.
            // If the block producer let it slide, we'll roll with it.
            check_transaction_authorization(trx, true);
369
         }
D
Daniel Larimer 已提交
370
   */
N
Nathan Hourt 已提交
371

372
   update_global_properties(next_block);
N
Nathan Hourt 已提交
373 374 375 376 377 378 379 380
   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
381
   // TODO: do this outside the write lock...? 
N
Nathan Hourt 已提交
382
   applied_block( next_block ); //emit
K
Kevin Heifner 已提交
383 384 385
   if (_currently_replaying_blocks)
     applied_irreversible_block(next_block);

N
Nathan Hourt 已提交
386 387 388

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

389 390 391
namespace {

  auto make_get_permission(const chainbase::database& db) {
D
Daniel Larimer 已提交
392 393
     return [&db](const permission_level& permission) {
        auto key = boost::make_tuple(permission.actor, permission.permission);
394 395 396 397
        return db.get<permission_object, by_owner>(key);
     };
  }

D
Daniel Larimer 已提交
398 399 400 401
  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;
402
     };
D
Daniel Larimer 已提交
403 404
     auto depth_limit = db.get<global_property_object>().configuration.max_authority_depth;
     return make_auth_checker( move(get_authority), depth_limit, signing_keys);
405 406 407 408
  }

}

D
Daniel Larimer 已提交
409 410 411
flat_set<public_key_type> chain_controller::get_required_keys(const signed_transaction& trx, 
                                                              const flat_set<public_key_type>& candidateKeys)const 
{
412 413
   auto checker = make_authority_checker(_db, candidateKeys);

D
Daniel Larimer 已提交
414 415 416 417
   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 已提交
418
                       "transaction declares authority '${auth}', but does not have signatures for it.",
D
Daniel Larimer 已提交
419
                       ("auth", declared_auth));
420 421 422 423 424 425 426
         }
      }
   }

   return checker.used_keys();
}

D
Daniel Larimer 已提交
427 428 429
void chain_controller::check_transaction_authorization(const signed_transaction& trx, 
                                                       bool allow_unused_signatures)const 
{
N
Nathan Hourt 已提交
430
   if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
431
      //ilog("Skipping auth and sigs checks");
N
Nathan Hourt 已提交
432 433 434
      return;
   }

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

D
Daniel Larimer 已提交
439 440 441 442 443
   for (const auto& act : trx.actions )
      for (const auto& declared_auth : act.authorization) {

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

N
Nathan Hourt 已提交
444 445
         if ((_skip_flags & skip_authority_check) == false) {
            const auto& index = _db.get_index<permission_index>().indices();
D
Daniel Larimer 已提交
446
            EOS_ASSERT(get_permission(declared_auth).satisfies(min_permission, index), tx_irrelevant_auth,
D
Daniel Larimer 已提交
447
                       "action declares irrelevant authority '${auth}'; minimum authority is ${min}",
D
Daniel Larimer 已提交
448
                       ("auth", declared_auth)("min", min_permission.name));
N
Nathan Hourt 已提交
449 450
         }
         if ((_skip_flags & skip_transaction_signatures) == false) {
D
Daniel Larimer 已提交
451
            EOS_ASSERT(checker.satisfied(declared_auth), tx_missing_sigs,
D
Daniel Larimer 已提交
452
                       "transaction declares authority '${auth}', but does not have signatures for it.",
D
Daniel Larimer 已提交
453
                       ("auth", declared_auth));
N
Nathan Hourt 已提交
454 455
         }
      }
N
Nathan Hourt 已提交
456

457
   if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
458
      EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
D
Daniel Larimer 已提交
459
                 "transaction bears irrelevant signatures from these keys: ${keys}", ("keys", checker.unused_keys()));
N
Nathan Hourt 已提交
460 461
}

D
Daniel Larimer 已提交
462
void chain_controller::validate_scope( const transaction& trx )const {
D
Daniel Larimer 已提交
463 464 465 466 467 468 469 470
   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(),
471 472
                          std::back_inserter(intersection) );
   FC_ASSERT( intersection.size() == 0, "a transaction may not redeclare scope in readscope" );
473 474
}

D
Daniel Larimer 已提交
475 476 477
const permission_object& chain_controller::lookup_minimum_permission(account_name authorizer_account,
                                                                    account_name scope,
                                                                    action_name act_name) const {
N
Nathan Hourt 已提交
478
   try {
D
Daniel Larimer 已提交
479 480 481
      // 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);
482 483 484
      // If no specific link found, check for a contract-wide default
      if (link == nullptr) {
         get<2>(key) = "";
D
Daniel Larimer 已提交
485
         link = _db.find<permission_link_object, by_action_name>(key);
486 487 488
      }

      // If no specific or default link found, use active permission
D
Daniel Larimer 已提交
489
      auto permission_key = boost::make_tuple<account_name, permission_name>(authorizer_account, config::active_name );
490
      if (link != nullptr)
D
Daniel Larimer 已提交
491 492 493
         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 已提交
494 495
}

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

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

D
Daniel Larimer 已提交
503
void chain_controller::record_transaction(const signed_transaction& trx) {
504 505
   //Insert transaction into unique transactions database.
    _db.create<transaction_object>([&](transaction_object& transaction) {
D
Daniel Larimer 已提交
506 507
        transaction.trx_id = trx.id(); 
        transaction.expiration = trx.expiration;
508 509 510 511
    });
}


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

D
Daniel Larimer 已提交
515
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.ref_block_num);
516 517

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

D
Daniel Larimer 已提交
523 524
void chain_controller::validate_referenced_accounts( const transaction& trx )const 
{ try { 
D
Daniel Larimer 已提交
525 526 527
   for( const auto& scope : trx.read_scope )
      require_account(scope);
   for( const auto& scope : trx.write_scope )
N
Nathan Hourt 已提交
528
      require_account(scope);
D
Daniel Larimer 已提交
529

D
Daniel Larimer 已提交
530 531
   for( const auto& act : trx.actions ) {
      require_account(act.scope);
D
Daniel Larimer 已提交
532
      for (const auto& auth : act.authorization )
D
Daniel Larimer 已提交
533
         require_account(auth.actor);
534
   }
D
Daniel Larimer 已提交
535
} FC_CAPTURE_AND_RETHROW() }
D
Daniel Larimer 已提交
536

D
Daniel Larimer 已提交
537
void chain_controller::validate_expiration( const transaction& trx ) const
538
{ try {
D
Daniel Larimer 已提交
539
   fc::time_point now = head_block_time();
D
Daniel Larimer 已提交
540
   const auto& chain_configuration = get_global_properties().configuration;
541

D
Daniel Larimer 已提交
542
   EOS_ASSERT( time_point(trx.expiration) <= now + fc::seconds(chain_configuration.max_transaction_lifetime),
D
Daniel Larimer 已提交
543
              transaction_exception, "transaction expiration is too far in the future",
544
              ("trx.expiration",trx.expiration)("now",now)
D
Daniel Larimer 已提交
545 546
              ("max_til_exp",chain_configuration.max_transaction_lifetime));
   EOS_ASSERT( now <= time_point(trx.expiration), transaction_exception, "transaction is expired",
547 548
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
549

550

D
Daniel Larimer 已提交
551
/*
D
Daniel Larimer 已提交
552 553
void chain_controller::process_message( const transaction& trx, account_name code,
                                        const action& message, actionOutput& output, apply_context* parent_context) {
554
   apply_context apply_ctx(*this, _db, trx, message, code);
N
Nathan Hourt 已提交
555 556
   apply_message(apply_ctx);

557 558 559
   output.notify.reserve( apply_ctx.notified.size() );

   for( uint32_t i = 0; i < apply_ctx.notified.size(); ++i ) {
N
Nathan Hourt 已提交
560
      try {
561 562
         auto notify_code = apply_ctx.notified[i];
         output.notify.push_back( {notify_code} );
563
         process_message(trx, notify_code, message, output.notify.back().output, &apply_ctx);
564 565 566
      } FC_CAPTURE_AND_RETHROW((apply_ctx.notified[i]))
   }

567
   // combine inline messages and process
568
   if (apply_ctx.inline_messages.size() > 0) {
D
Daniel Larimer 已提交
569
      output.inline_transaction = Inlinetransaction(trx);
570 571
      (*output.inline_transaction).messages = std::move(apply_ctx.inline_messages);
   }
572 573 574 575 576 577

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

580
      _db.create<generated_transaction_object>([&](generated_transaction_object& transaction) {
581
         transaction.trx = gtrx;
582 583 584
         transaction.status = generated_transaction_object::PENDING;
      });

585
      output.deferred_transactions.emplace_back( gtrx );
N
Nathan Hourt 已提交
586
   }
587 588 589 590 591 592 593 594 595 596

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

D
Daniel Larimer 已提交
602
/*
603
void chain_controller::apply_message(apply_context& context)
604
{ try {
D
Daniel Larimer 已提交
605 606
    /// context.code => the execution namespace
    /// message.code / message.type => Event
607
    const auto& m = context.msg;
D
Daniel Larimer 已提交
608
    auto contract_handlers_itr = apply_handlers.find(context.code);
609 610 611
    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 已提交
612
          message_handler_itr->second(context);
613 614 615
          return;
       }
    }
D
Daniel Larimer 已提交
616
    const auto& recipient = _db.get<account_object,by_name>(context.code);
617
    if (recipient.code.size()) {
618
       //idump((context.code)(context.msg.type));
619 620
       const uint32_t execution_time =
          _skip_flags | received_block
B
Brian Johnson 已提交
621
             ? _rcvd_block_txn_execution_time
622
             : _skip_flags | created_block
B
Brian Johnson 已提交
623 624
               ? _create_block_txn_execution_time
               : _txn_execution_time;
625 626
       const bool is_received_block = _skip_flags & received_block;
       wasm_interface::get().apply(context, execution_time, is_received_block);
627
    }
628

N
Nathan Hourt 已提交
629
} FC_CAPTURE_AND_RETHROW((context.msg)) }
D
Daniel Larimer 已提交
630
*/
N
Nathan Hourt 已提交
631

632

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

638
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
639 640
   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 已提交
641
   EOS_ASSERT(head_block_time() < (fc::time_point)next_block.timestamp, block_validate_exception, "",
642
              ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()));
D
Daniel Larimer 已提交
643 644
   if (next_block.block_num() % config::blocks_per_round != 0) {
      EOS_ASSERT(!next_block.new_producers, block_validate_exception,
645
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
646
   }
D
Daniel Larimer 已提交
647 648
   
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
649

N
Nathan Hourt 已提交
650
   if(!(skip&skip_producer_signature))
651 652 653
      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 已提交
654

655
   if(!(skip&skip_producer_schedule_check)) {
656 657 658
      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 已提交
659 660 661 662 663
   }

   return producer;
}

664
void chain_controller::create_block_summary(const signed_block& next_block) {
665
   auto sid = next_block.block_num() & 0xffff;
666
   _db.modify( _db.get<block_summary_object,by_id>(sid), [&](block_summary_object& p) {
667 668
         p.block_id = next_block.id();
   });
N
Nathan Hourt 已提交
669 670
}

671
void chain_controller::update_global_properties(const signed_block& b) {
672 673
   // If we're at the end of a round, update the BlockchainConfiguration, producer schedule
   // and "producers" special account authority
D
Daniel Larimer 已提交
674 675
   if (b.block_num() % config::blocks_per_round == 0) {
      /*
D
Daniel Larimer 已提交
676 677
      auto schedule = _admin->get_next_round(_db);
      auto config   = _admin->get_blockchain_configuration(_db, schedule);
678

679 680
      const auto& gpo = get_global_properties();
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
681 682 683
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
684

685
      auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {});
686
      for(auto& name : gpo.active_producers) {
687
         active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1});
688 689
      }

D
Daniel Larimer 已提交
690 691
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::producers_account_name, 
                                                                         config::active_level_name ) );
692 693 694
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
D
Daniel Larimer 已提交
695
      */
696 697 698
   }
}

699
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
700
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
701 702 703
      _checkpoints[i.first] = i.second;
}

704
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
705 706 707
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

708
const global_property_object& chain_controller::get_global_properties()const {
709
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
710 711
}

712
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
713
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
714 715
}

D
Daniel Larimer 已提交
716
time_point chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
717 718 719
   return get_dynamic_global_properties().time;
}

720
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
721 722 723
   return get_dynamic_global_properties().head_block_number;
}

724
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
725 726 727
   return get_dynamic_global_properties().head_block_id;
}

D
Daniel Larimer 已提交
728
account_name chain_controller::head_block_producer() const {
729 730 731
   auto b = _fork_db.fetch_block(head_block_id());
   if( b ) return b->data.producer;

N
Nathan Hourt 已提交
732
   if (auto head_block = fetch_block_by_id(head_block_id()))
733
      return head_block->producer;
N
Nathan Hourt 已提交
734 735 736
   return {};
}

D
Daniel Larimer 已提交
737
const producer_object& chain_controller::get_producer(const account_name& ownerName) const {
N
Nathan Hourt 已提交
738 739 740
   return _db.get<producer_object, by_owner>(ownerName);
}

741
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
742
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
743 744
}

745
void chain_controller::initialize_indexes() {
746 747
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
748
   _db.add_index<permission_link_index>();
749 750
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
M
Matias Romeo 已提交
751
   _db.add_index<keystr_value_index>();
752
   _db.add_index<key128x128_value_index>();
753
   _db.add_index<key64x64x64_value_index>();
754 755 756 757 758

   _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>();
759
   _db.add_index<generated_transaction_multi_index>();
760
   _db.add_index<producer_multi_index>();
N
Nathan Hourt 已提交
761 762
}

D
Daniel Larimer 已提交
763
void chain_controller::initialize_chain(contracts::chain_initializer& starter)
N
Nathan Hourt 已提交
764
{ try {
765
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
766 767
      _db.with_write_lock([this, &starter] {
         auto initial_timestamp = starter.get_chain_start_time();
D
Daniel Larimer 已提交
768 769 770
         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 已提交
771

772
         // Create global properties
N
Nathan Hourt 已提交
773 774 775
         _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();
776 777
         });
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
778
            p.time = initial_timestamp;
779 780
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
781

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

K
Kevin Heifner 已提交
786
         // create a dummy block and cycle for our dummy transactions to send to applied_irreversible_block below
787
         signed_block block{};
D
Daniel Larimer 已提交
788 789
         block.producer = config::system_account_name;
         /*
790 791 792
         block.cycles.emplace_back();
         block.cycles[0].emplace_back();

793
         auto messages = starter.prepare_database(*this, _db);
D
Daniel Larimer 已提交
794 795 796 797 798 799 800
         std::for_each(messages.begin(), messages.end(), [&](const action& m) {
            //actionOutput output;
            signed_transaction trx; /// dummy transaction required for scope validation
            //std::sort(trx.scope.begin(), trx.scope.end() );

            with_skip_flags(skip_scope_check | skip_transaction_signatures | skip_authority_check | received_block, 
                            [&](){ process_message(trx,m.code,m,output); });
801 802 803

            trx.messages.push_back(m);
            block.cycles[0][0].user_input.push_back(std::move(trx));
804
         });
805 806 807

         // TODO: Should we write this genesis block instead of faking it on startup?
         applied_irreversible_block(block);
D
Daniel Larimer 已提交
808
         */
809 810
      });
   }
N
Nathan Hourt 已提交
811 812
} FC_CAPTURE_AND_RETHROW() }

813
chain_controller::chain_controller(database& database, fork_database& fork_db, block_log& blocklog,
D
Daniel Larimer 已提交
814
                                   contracts::chain_initializer& starter, 
B
Brian Johnson 已提交
815 816
                                   uint32_t txn_execution_time, uint32_t rcvd_block_txn_execution_time,
                                   uint32_t create_block_txn_execution_time,
K
Kevin Heifner 已提交
817
                                   const applied_irreverisable_block_func& applied_func)
D
Daniel Larimer 已提交
818 819
:_db(database), _fork_db(fork_db), _block_log(blocklog) 
{
N
Nathan Hourt 已提交
820

K
Kevin Heifner 已提交
821 822 823
   if (applied_func)
      applied_irreversible_block.connect(*applied_func);

824
   initialize_indexes();
825
   starter.register_types(*this, _db);
N
Nathan Hourt 已提交
826 827 828 829 830 831

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

832 833
   spinup_db();
   spinup_fork_db();
834 835 836

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

839 840 841 842 843
chain_controller::~chain_controller() {
   clear_pending();
   _db.flush();
   _fork_db.reset();
}
N
Nathan Hourt 已提交
844

845
void chain_controller::replay() {
846
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
847
   auto start = fc::time_point::now();
K
Kevin Heifner 已提交
848

K
Kevin Heifner 已提交
849
   auto on_exit = fc::make_scoped_exit([&_currently_replaying_blocks = _currently_replaying_blocks](){
K
Kevin Heifner 已提交
850 851 852 853
      _currently_replaying_blocks = false;
   });
   _currently_replaying_blocks = true;

854 855 856
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
857 858 859 860 861
      return;
   }

   const auto last_block_num = last_block->block_num();

862
   ilog("Replaying ${n} blocks...", ("n", last_block_num) );
863 864 865 866 867
   for (uint32_t i = 1; i <= last_block_num; ++i) {
      if (i % 5000 == 0)
         std::cerr << "   " << double(i*100)/last_block_num << "%   "<<i << " of " <<last_block_num<<"   \n";
      fc::optional<signed_block> block = _block_log.read_block_by_num(i);
      FC_ASSERT(block, "Could not find block #${n} in block_log!", ("n", i));
N
Nathan Hourt 已提交
868 869 870 871 872
      apply_block(*block, skip_producer_signature |
                          skip_transaction_signatures |
                          skip_transaction_dupe_check |
                          skip_tapos_check |
                          skip_producer_schedule_check |
873 874
                          skip_authority_check |
                          received_block);
N
Nathan Hourt 已提交
875 876
   }
   auto end = fc::time_point::now();
877 878
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
879

880
   _db.set_revision(head_block_num());
881
}
N
Nathan Hourt 已提交
882

883 884 885 886
void chain_controller::spinup_db() {
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
D
Daniel Larimer 已提交
887

888 889 890 891
      FC_ASSERT(_db.revision() == head_block_num(), "Chainbase revision does not match head block num",
                ("rev", _db.revision())("head_block", head_block_num()));
   });
}
N
Nathan Hourt 已提交
892

893
void chain_controller::spinup_fork_db()
N
Nathan Hourt 已提交
894
{
895 896 897 898 899 900 901 902
   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 已提交
903 904
}

D
Daniel Larimer 已提交
905
/*
906 907
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
908 909 910 911
   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 已提交
912 913 914
   
   fc::time_point tp = (fc::time_point)next_block.timestamp;
   utilities::rand::random rng(tp.sec_since_epoch());
915 916
   rng.shuffle(schedule);
   return schedule;
D
Daniel Larimer 已提交
917
}*/
918

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

P
Pravin 已提交
922
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time((fc::time_point)b.timestamp);
923
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
924
   missed_blocks--;
N
Nathan Hourt 已提交
925

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

N
Nathan Hourt 已提交
929
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
930
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
931
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
932 933 934 935 936 937
         /*
         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) );
            */

938
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
939 940 941 942 943 944
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
945
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
946 947 948
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
949
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
950 951 952 953 954 955 956 957 958
      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 已提交
959 960 961 962 963
   });

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

964
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
965 966
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
P
Pravin 已提交
967
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( (fc::time_point)new_block.timestamp );
N
Nathan Hourt 已提交
968

969
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
970 971 972 973 974 975
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

976
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
977 978 979 980
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

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

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

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

D
Daniel Larimer 已提交
989
   size_t offset = EOS_PERCENT(producer_objs.size(), config::percent_100- config::irreversible_threshold_percent);
990 991
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
992
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
993
      });
N
Nathan Hourt 已提交
994

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

997
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
998
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
999
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1000
      });
N
Nathan Hourt 已提交
1001
   }
1002 1003

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1004
   auto old_last_irreversible_block = _block_log.head();
1005 1006 1007 1008
   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();
1009

1010 1011 1012 1013 1014 1015
   if (last_block_on_disk < new_last_irreversible_block_num)
      for (auto block_to_write = last_block_on_disk + 1;
           block_to_write <= new_last_irreversible_block_num;
           ++block_to_write) {
         auto block = fetch_block_by_number(block_to_write);
         assert(block);
1016
         _block_log.append(*block);
K
Kevin Heifner 已提交
1017
         applied_irreversible_block(*block);
1018
      }
N
Nathan Hourt 已提交
1019 1020 1021

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

1025
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1026 1027
{ try {
   //Look for expired transactions in the deduplication list, and remove them.
D
Daniel Larimer 已提交
1028
   //transactions must have expired by at least two forking windows in order to be removed.
D
Daniel Larimer 已提交
1029
   /*
1030
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1031
   const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
D
Daniel Larimer 已提交
1032
   while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->expiration) )
N
Nathan Hourt 已提交
1033
      transaction_idx.remove(*dedupe_index.rbegin());
1034 1035

   //Look for expired transactions in the pending generated list, and remove them.
D
Daniel Larimer 已提交
1036
   //transactions must have expired by at least two forking windows in order to be removed.
1037 1038 1039 1040
   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 已提交
1041
      */
N
Nathan Hourt 已提交
1042 1043 1044 1045
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

D
Daniel Larimer 已提交
1046
account_name chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1047 1048
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1049
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1050
   const auto& gpo = _db.get<global_property_object>();
D
Daniel Larimer 已提交
1051 1052 1053 1054 1055
   //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 已提交
1056 1057
}

D
Daniel Larimer 已提交
1058
block_timestamp_type chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1059
{
P
Pravin 已提交
1060
   if( slot_num == 0)
D
Daniel Larimer 已提交
1061
      return block_timestamp_type();
N
Nathan Hourt 已提交
1062 1063 1064 1065 1066 1067

   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 已提交
1068 1069 1070
      auto genesis_time = block_timestamp_type(dpo.time);
      genesis_time.slot += slot_num;
      return (fc::time_point)genesis_time;
N
Nathan Hourt 已提交
1071 1072
   }

D
Daniel Larimer 已提交
1073
   auto head_block_abs_slot = block_timestamp_type(head_block_time());
P
Pravin 已提交
1074
   head_block_abs_slot.slot += slot_num;
D
Daniel Larimer 已提交
1075
   return head_block_abs_slot;
N
Nathan Hourt 已提交
1076 1077
}

D
Daniel Larimer 已提交
1078
uint32_t chain_controller::get_slot_at_time( block_timestamp_type when )const
N
Nathan Hourt 已提交
1079
{
D
Daniel Larimer 已提交
1080
   auto first_slot_time = get_slot_time(1);
N
Nathan Hourt 已提交
1081 1082
   if( when < first_slot_time )
      return 0;
D
Daniel Larimer 已提交
1083
   return block_timestamp_type(when).slot - first_slot_time.slot + 1;
N
Nathan Hourt 已提交
1084 1085
}

1086
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1087 1088
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
D
Daniel Larimer 已提交
1089
   return uint64_t(config::percent_100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1090 1091
}

D
Daniel Larimer 已提交
1092 1093 1094 1095
void chain_controller::set_apply_handler( const account_name& contract, 
                                          const account_name& scope, 
                                          const action_name& action, apply_handler v ) {

D
Daniel Larimer 已提交
1096
   //apply_handlers[contract][std::make_pair(scope,action)] = v;
1097
}
N
Nathan Hourt 已提交
1098

1099

N
Nathan Hourt 已提交
1100
} }