chain_controller.cpp 42.9 KB
Newer Older
N
Nathan Hourt 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright (c) 2017, Respective Authors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
N
Nathan Hourt 已提交
24

25
#include <eos/chain/chain_controller.hpp>
N
Nathan Hourt 已提交
26 27 28 29
#include <eos/chain/exceptions.hpp>

#include <eos/chain/block_summary_object.hpp>
#include <eos/chain/global_property_object.hpp>
N
Nathan Hourt 已提交
30 31
#include <eos/chain/key_value_object.hpp>
#include <eos/chain/action_objects.hpp>
N
Nathan Hourt 已提交
32 33 34
#include <eos/chain/transaction_object.hpp>
#include <eos/chain/producer_object.hpp>

35 36
#include <eos/chain/wasm_interface.hpp>

37 38 39
#include <eos/types/native.hpp>
#include <eos/types/generated.hpp>

40 41 42
#include <eos/utilities/randutils.hpp>
#include <eos/utilities/pcg-random/pcg_random.hpp>

N
Nathan Hourt 已提交
43
#include <fc/smart_ref_impl.hpp>
N
Nathan Hourt 已提交
44 45 46
#include <fc/uint128.hpp>
#include <fc/crypto/digest.hpp>

47
#include <boost/range/algorithm/copy.hpp>
N
Nathan Hourt 已提交
48
#include <boost/range/algorithm_ext/is_sorted.hpp>
49
#include <boost/range/adaptor/transformed.hpp>
N
Nathan Hourt 已提交
50
#include <boost/range/adaptor/map.hpp>
N
Nathan Hourt 已提交
51 52 53 54 55

#include <fstream>
#include <functional>
#include <iostream>

D
Daniel Larimer 已提交
56
//#include <Wren++.h>
57

N
Nathan Hourt 已提交
58 59
namespace eos { namespace chain {

60 61

String apply_context::get( String key )const {
62
   const auto& obj = db.get<key_value_object,by_scope_key>( boost::make_tuple(scope, key) );
63 64 65
   return String(obj.value.begin(),obj.value.end());
}
void apply_context::set( String key, String value ) {
66
   const auto* obj = db.find<key_value_object,by_scope_key>( boost::make_tuple(scope, key) );
67
   if( obj ) {
68
      mutable_db.modify( *obj, [&]( auto& o ) {
69 70 71 72
         o.value.resize( value.size() );
        // memcpy( o.value.data(), value.data(), value.size() );
      });
   } else {
73
      mutable_db.create<key_value_object>( [&](auto& o) {
74
         o.scope = scope;
75 76 77 78 79 80
         o.key.insert( 0, key.data(), key.size() );
         o.value.insert( 0, value.data(), value.size() );
      });
   }
}
void apply_context::remove( String key ) {
81
   const auto* obj = db.find<key_value_object,by_scope_key>( boost::make_tuple(scope, key) );
82
   if( obj ) {
83
      mutable_db.remove( *obj );
84 85 86 87 88
   }
}



89
bool chain_controller::is_known_block(const block_id_type& id)const
N
Nathan Hourt 已提交
90
{
91
   return _fork_db.is_known_block(id) || _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
92 93 94 95 96 97
}
/**
 * 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.
 */
98
bool chain_controller::is_known_transaction(const transaction_id_type& id)const
N
Nathan Hourt 已提交
99
{
100
   const auto& trx_idx = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
101 102 103
   return trx_idx.find( id ) != trx_idx.end();
}

104
block_id_type chain_controller::get_block_id_for_num(uint32_t block_num)const
N
Nathan Hourt 已提交
105
{ try {
106 107
   if (const auto& block = fetch_block_by_number(block_num))
      return block->id();
N
Nathan Hourt 已提交
108

109 110 111
   FC_THROW_EXCEPTION(unknown_block_exception, "Could not find block");
} FC_CAPTURE_AND_RETHROW((block_num)) }

112
optional<signed_block> chain_controller::fetch_block_by_id(const block_id_type& id)const
N
Nathan Hourt 已提交
113
{
114 115
   auto b = _fork_db.fetch_block(id);
   if(b) return b->data;
116
   return _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
117 118
}

119
optional<signed_block> chain_controller::fetch_block_by_number(uint32_t num)const
N
Nathan Hourt 已提交
120
{
121
   if (const auto& block = _block_log.read_block_by_num(num))
122 123
      return *block;

N
Nathan Hourt 已提交
124
   // Not in _block_log, so it must be since the last irreversible block. Grab it from _fork_db instead
125 126 127 128 129 130 131 132
   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 已提交
133 134 135
   return optional<signed_block>();
}

136
const SignedTransaction& chain_controller::get_recent_transaction(const transaction_id_type& trx_id) const
N
Nathan Hourt 已提交
137
{
138
   auto& index = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
139 140 141 142 143
   auto itr = index.find(trx_id);
   FC_ASSERT(itr != index.end());
   return itr->trx;
}

144
std::vector<block_id_type> chain_controller::get_block_ids_on_fork(block_id_type head_of_fork) const
N
Nathan Hourt 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
{
  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;
}

/**
 * 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.
 */
168
bool chain_controller::push_block(const signed_block& new_block, uint32_t skip)
D
Daniel Larimer 已提交
169 170 171
{ try {
   return with_skip_flags( skip, [&](){ 
      return without_pending_transactions( [&]() {
172
         return _db.with_write_lock( [&]() {
173 174
            return _push_block(new_block);
         } );
N
Nathan Hourt 已提交
175 176
      });
   });
177
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
178

179
bool chain_controller::_push_block(const signed_block& new_block)
N
Nathan Hourt 已提交
180 181
{ try {
   uint32_t skip = get_node_properties().skip_flags;
182
   if (!(skip&skip_fork_db)) {
N
Nathan Hourt 已提交
183 184 185 186 187
      /// TODO: if the block is greater than the head block and before the next maitenance interval
      // 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.
188
      if (new_head->data.previous != head_block_id()) {
N
Nathan Hourt 已提交
189 190
         //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
191 192
         if (new_head->data.block_num() > head_block_num()) {
            wlog("Switching to fork: ${id}", ("id",new_head->data.id()));
N
Nathan Hourt 已提交
193 194 195
            auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());

            // pop blocks until we hit the forked block
196
            while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
197 198 199
               pop_block();

            // push all blocks on the new fork
200 201
            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 已提交
202 203
                optional<fc::exception> except;
                try {
204
                   auto session = _db.start_undo_session(true);
205
                   apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
206 207
                   session.push();
                }
208 209 210
                catch (const fc::exception& e) { except = e; }
                if (except) {
                   wlog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
N
Nathan Hourt 已提交
211
                   // remove the rest of branches.first from the fork_db, those blocks are invalid
212 213
                   while (ritr != branches.first.rend()) {
                      _fork_db.remove((*ritr)->data.id());
N
Nathan Hourt 已提交
214 215
                      ++ritr;
                   }
216
                   _fork_db.set_head(branches.second.front());
N
Nathan Hourt 已提交
217 218

                   // pop all blocks from the bad fork
219
                   while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
220 221 222
                      pop_block();

                   // restore all blocks from the good fork
223
                   for (auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr) {
224
                      auto session = _db.start_undo_session(true);
225
                      apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
226 227 228 229 230 231 232 233 234 235 236 237
                      session.push();
                   }
                   throw *except;
                }
            }
            return true;
         }
         else return false;
      }
   }

   try {
238
      auto session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
239 240 241 242 243 244 245 246 247
      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;
248
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
249 250 251 252 253 254 255 256 257 258

/**
 * 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.
 */
259
void chain_controller::push_transaction(const SignedTransaction& trx, uint32_t skip)
N
Nathan Hourt 已提交
260
{ try {
261 262
   with_skip_flags(skip, [&]() {
      with_producing([&](){
263
         _db.with_write_lock([&]() {
D
Daniel Larimer 已提交
264 265 266
            _push_transaction(trx);
         });
      });
N
Nathan Hourt 已提交
267 268
   });
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
269

270
void chain_controller::_push_transaction(const SignedTransaction& trx) {
271 272
   // 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.
273
   if (!_pending_tx_session.valid())
274
      _pending_tx_session = _db.start_undo_session(true);
275

276
   auto temp_session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
277
   _apply_transaction(trx);
D
Daniel Larimer 已提交
278
   _pending_transactions.push_back(trx);
N
Nathan Hourt 已提交
279 280 281 282 283 284

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

   // notify anyone listening to pending transactions
N
Nathan Hourt 已提交
285
   on_pending_transaction(trx);
N
Nathan Hourt 已提交
286 287 288
}


289
signed_block chain_controller::generate_block(
N
Nathan Hourt 已提交
290
   fc::time_point_sec when,
N
Nathan Hourt 已提交
291
   const AccountName& producer,
N
Nathan Hourt 已提交
292 293 294 295
   const fc::ecc::private_key& block_signing_private_key,
   uint32_t skip /* = 0 */
   )
{ try {
D
Daniel Larimer 已提交
296 297
   return with_producing( [&]() {
      return with_skip_flags( skip, [&](){
298
         auto b = _db.with_write_lock( [&](){
N
Nathan Hourt 已提交
299
           return _generate_block( when, producer, block_signing_private_key );
D
Daniel Larimer 已提交
300
         });
301 302
         push_block(b);
         return b;
D
Daniel Larimer 已提交
303 304 305
      });
    });
} FC_CAPTURE_AND_RETHROW( (when) ) }
N
Nathan Hourt 已提交
306

307
signed_block chain_controller::_generate_block(
N
Nathan Hourt 已提交
308
   fc::time_point_sec when,
N
Nathan Hourt 已提交
309
   const AccountName& producer,
N
Nathan Hourt 已提交
310 311 312 313 314 315 316
   const fc::ecc::private_key& block_signing_private_key
   )
{
   try {
   uint32_t skip = get_node_properties().skip_flags;
   uint32_t slot_num = get_slot_at_time( when );
   FC_ASSERT( slot_num > 0 );
N
Nathan Hourt 已提交
317 318
   AccountName scheduled_producer = get_scheduled_producer( slot_num );
   FC_ASSERT( scheduled_producer == producer );
N
Nathan Hourt 已提交
319

N
Nathan Hourt 已提交
320
   const auto& producer_obj = get_producer(scheduled_producer);
N
Nathan Hourt 已提交
321 322 323 324 325

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

   static const size_t max_block_header_size = fc::raw::pack_size( signed_block_header() ) + 4;
326
   auto maximum_block_size = get_global_properties().configuration.maxBlockSize;
N
Nathan Hourt 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
   size_t total_block_size = max_block_header_size;

   signed_block pending_block;

   //
   // The following code throws away existing pending_tx_session and
   // rebuilds it by re-applying pending transactions.
   //
   // This rebuild is necessary because pending transactions' validity
   // and semantics may have changed since they were received, because
   // time-based semantics are evaluated based on the current block
   // time.  These changes can only be reflected in the database when
   // the value of the "when" variable is known, which means we need to
   // re-apply pending transactions in this method.
   //
   _pending_tx_session.reset();
343
   _pending_tx_session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
344 345 346

   uint64_t postponed_tx_count = 0;
   // pop pending state (reset to head block state)
N
Nathan Hourt 已提交
347
   for( const SignedTransaction& tx : _pending_transactions )
N
Nathan Hourt 已提交
348 349 350 351 352 353 354 355 356 357 358 359
   {
      size_t new_total_size = total_block_size + fc::raw::pack_size( tx );

      // postpone transaction if it would make block too big
      if( new_total_size >= maximum_block_size )
      {
         postponed_tx_count++;
         continue;
      }

      try
      {
360
         auto temp_session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
361
         _apply_transaction(tx);
N
Nathan Hourt 已提交
362 363
         temp_session.squash();

N
Nathan Hourt 已提交
364
         total_block_size += fc::raw::pack_size(tx);
N
Nathan Hourt 已提交
365 366 367 368 369 370
         if (pending_block.cycles.empty()) {
            pending_block.cycles.resize(1);
            pending_block.cycles.back().resize(1);
         }
         pending_block.cycles.back().back().user_input.emplace_back(tx);
#warning TODO: Populate generated blocks with generated transactions
N
Nathan Hourt 已提交
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
      }
      catch ( const fc::exception& e )
      {
         // Do nothing, transaction will not be re-applied
         wlog( "Transaction was not processed while generating block due to ${e}", ("e", e) );
         wlog( "The transaction was ${t}", ("t", tx) );
      }
   }
   if( postponed_tx_count > 0 )
   {
      wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
   }

   _pending_tx_session.reset();

   // We have temporarily broken the invariant that
   // _pending_tx_session is the result of applying _pending_tx, as
D
Daniel Larimer 已提交
388
   // _pending_transactions now consists of the set of postponed transactions.
N
Nathan Hourt 已提交
389 390 391 392 393 394
   // However, the push_block() call below will re-create the
   // _pending_tx_session.

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

396
   pending_block.producer = producer_obj.owner;
N
Nathan Hourt 已提交
397

398 399 400 401 402 403
   // If this block is last in a round, calculate the schedule for the new round
   if (pending_block.block_num() % config::BlocksPerRound == 0) {
      auto new_schedule = _admin->get_next_round(_db);
      pending_block.producer_changes = get_global_properties().active_producers - new_schedule;
   }

N
Nathan Hourt 已提交
404 405 406 407
   if( !(skip & skip_producer_signature) )
      pending_block.sign( block_signing_private_key );

   // TODO:  Move this to _push_block() so session is restored.
408
   /*
N
Nathan Hourt 已提交
409 410 411 412
   if( !(skip & skip_block_size_check) )
   {
      FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
   }
413
   */
N
Nathan Hourt 已提交
414

415
   // push_block( pending_block, skip );
N
Nathan Hourt 已提交
416 417

   return pending_block;
N
Nathan Hourt 已提交
418
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
419 420

/**
N
Nathan Hourt 已提交
421
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
422
 */
423
void chain_controller::pop_block()
N
Nathan Hourt 已提交
424 425 426 427 428 429 430
{ try {
   _pending_tx_session.reset();
   auto head_id = head_block_id();
   optional<signed_block> head_block = fetch_block_by_id( head_id );
   EOS_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );

   _fork_db.pop_block();
431
   _db.undo();
N
Nathan Hourt 已提交
432 433
} FC_CAPTURE_AND_RETHROW() }

434
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
435
{ try {
D
Daniel Larimer 已提交
436
   _pending_transactions.clear();
N
Nathan Hourt 已提交
437 438 439 440 441
   _pending_tx_session.reset();
} FC_CAPTURE_AND_RETHROW() }

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

442
void chain_controller::apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
443 444
{
   auto block_num = next_block.block_num();
445 446 447 448 449
   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 已提交
450

451
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
452 453
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
454
   with_skip_flags(skip, [&](){ _apply_block(next_block); });
N
Nathan Hourt 已提交
455 456
}

D
Daniel Larimer 已提交
457

458
void chain_controller::_apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
459 460 461 462
{ try {
   uint32_t next_block_num = next_block.block_num();
   uint32_t skip = get_node_properties().skip_flags;

463 464 465
   FC_ASSERT((skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(),
             "", ("next_block.transaction_merkle_root", next_block.transaction_merkle_root)
             ("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()));
N
Nathan Hourt 已提交
466 467 468

   const producer_object& signing_producer = validate_block_header(skip, next_block);

N
Nathan Hourt 已提交
469 470 471 472 473 474
   /* We do not need to push the undo state for each transaction
    * because they either all apply and are valid or the
    * entire block fails to apply.  We only need an "undo" state
    * for transactions when validating broadcast transactions or
    * when building a block.
    */
475 476 477
   for (const auto& cycle : next_block.cycles) {
      for (const auto& thread : cycle) {
         for(const auto& trx : thread.generated_input ) {
N
Nathan Hourt 已提交
478 479
#warning TODO: Process generated transaction
         }
480 481 482 483 484
         for(const auto& trx : thread.user_input ) {
            apply_transaction(trx);
         }
      }
   }
N
Nathan Hourt 已提交
485

486
   update_global_properties(next_block);
N
Nathan Hourt 已提交
487 488 489 490 491 492 493 494 495 496 497
   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();

   if( !_node_property_object.debug_updates.empty() )
      apply_debug_updates();

   // notify observers that the block has been applied
498
   // TODO: do this outside the write lock...? 
N
Nathan Hourt 已提交
499 500 501 502
   applied_block( next_block ); //emit

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

503
void chain_controller::apply_transaction(const SignedTransaction& trx, uint32_t skip)
N
Nathan Hourt 已提交
504
{
D
Daniel Larimer 已提交
505
   with_skip_flags( skip, [&]() { _apply_transaction(trx); });
N
Nathan Hourt 已提交
506 507
}

508
void chain_controller::validate_transaction(const SignedTransaction& trx)const {
509
try {
510
   EOS_ASSERT(trx.messages.size() > 0, transaction_exception, "A transaction must have at least one message");
N
Nathan Hourt 已提交
511

512 513 514 515
   validate_uniqueness(trx);
   validate_tapos(trx);
   validate_referenced_accounts(trx);
   validate_expiration(trx);
516
   validate_message_types(trx);
517

N
Nathan Hourt 已提交
518 519
   for (const auto& tm : trx.messages) { /// TODO: this loop can be processed in parallel
      Message m(tm);
N
Nathan Hourt 已提交
520
      message_validate_context mvc(m);
N
Nathan Hourt 已提交
521 522 523 524
      auto contract_handlers_itr = message_validate_handlers.find(m.recipient);
      if (contract_handlers_itr != message_validate_handlers.end()) {
         auto message_handler_itr = contract_handlers_itr->second.find({m.recipient, m.type});
         if (message_handler_itr != contract_handlers_itr->second.end()) {
525 526 527 528
            message_handler_itr->second(mvc);
            continue;
         }
      }
529

530 531
      /// TODO: dispatch to script if not handled above
   }
532 533
} FC_CAPTURE_AND_RETHROW( (trx) ) }

534
void chain_controller::validate_uniqueness( const SignedTransaction& trx )const {
N
Nathan Hourt 已提交
535
   if( !should_check_for_duplicate_transactions() ) return;
N
Nathan Hourt 已提交
536 537

   auto trx_id = trx.id();
538
   auto& trx_idx = _db.get_index<transaction_multi_index>();
539 540
   EOS_ASSERT(trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end(),
              transaction_exception, "Transaction is not unique");
541
}
542

543
void chain_controller::validate_tapos(const SignedTransaction& trx)const {
N
Nathan Hourt 已提交
544
   if (!should_check_tapos()) return;
545

546
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.refBlockNum);
547 548

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

554
void chain_controller::validate_referenced_accounts(const SignedTransaction& trx)const {
N
Nathan Hourt 已提交
555
   for(const auto& auth : trx.authorizations) {
N
Nathan Hourt 已提交
556
      require_account(auth.account);
557
   }
558
   for(const auto& msg : trx.messages) {
N
Nathan Hourt 已提交
559 560
      require_account(msg.sender);
      require_account(msg.recipient);
N
Nathan Hourt 已提交
561
      const AccountName* previous_notify_account = nullptr;
562
      for(const auto& current_notify_account : msg.notify) {
N
Nathan Hourt 已提交
563
         require_account(current_notify_account);
564
         if(previous_notify_account) {
565 566
            EOS_ASSERT(current_notify_account < *previous_notify_account, message_validate_exception,
                       "Message notify accounts out of order. Possibly a bug in the wallet?");
567 568
         }

569 570 571 572 573
         EOS_ASSERT(current_notify_account != msg.sender, message_validate_exception,
                    "Message sender is listed in accounts to notify. Possibly a bug in the wallet?");
         EOS_ASSERT(current_notify_account != msg.recipient, message_validate_exception,
                    "Message recipient is listed in accounts to notify. Possibly a bug in the wallet?");
         previous_notify_account = &current_notify_account;
574 575 576
      }
   }
}
D
Daniel Larimer 已提交
577

578
void chain_controller::validate_expiration(const SignedTransaction& trx) const
579 580
{ try {
   fc::time_point_sec now = head_block_time();
581
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
582

583
   EOS_ASSERT(trx.expiration <= now + int32_t(chain_configuration.maxTrxLifetime),
584 585
              transaction_exception, "Transaction expiration is too far in the future",
              ("trx.expiration",trx.expiration)("now",now)
586
              ("max_til_exp",chain_configuration.maxTrxLifetime));
587 588 589
   EOS_ASSERT(now <= trx.expiration, transaction_exception, "Transaction is expired",
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
590

591
void chain_controller::validate_message_types(const SignedTransaction& trx)const {
592 593 594
try {
   for( const auto& msg : trx.messages ) {
      try {
595
         _db.get<type_object, by_scope_name>( boost::make_tuple(msg.recipient, msg.type) );
N
Nathan Hourt 已提交
596 597 598 599
      } catch(std::out_of_range) {
         FC_THROW_EXCEPTION(message_validate_exception, "Unrecognized message recipient and type",
                            ("recipient", msg.recipient)("type", msg.type));
      }
600 601 602
   }
} FC_CAPTURE_AND_RETHROW( (trx) ) }

603
void chain_controller::validate_message_precondition( precondition_validate_context& context )const 
604
{ try {
605
    const auto& m = context.msg;
N
Nathan Hourt 已提交
606
    auto contract_handlers_itr = precondition_validate_handlers.find( m.recipient );
607
    if( contract_handlers_itr != precondition_validate_handlers.end() ) {
608
       auto message_handler_itr = contract_handlers_itr->second.find( {context.scope, m.type} );
N
Nathan Hourt 已提交
609 610
       if( message_handler_itr != contract_handlers_itr->second.end() ) {
          message_handler_itr->second(context);
611 612 613 614
          return;
       }
    }
    /// TODO: dispatch to script if not handled above
N
Nathan Hourt 已提交
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
   } FC_CAPTURE_AND_RETHROW() }

void chain_controller::process_message(Message message) {
   apply_context apply_ctx(_db, message, message.recipient);

   /** TODO: pre condition validation and application can occur in parallel */
   /** TODO: verify that message is fully authorized
          (check that @ref SignedTransaction::authorizations are all present) */
   validate_message_precondition(apply_ctx);
   apply_message(apply_ctx);

   for (const auto& notify_account : message.notify) {
      try {
         apply_context notify_ctx(_db, message, notify_account);
         validate_message_precondition(notify_ctx);
         apply_message(notify_ctx);
      } FC_CAPTURE_AND_RETHROW((notify_account)(message))
   }
}
634

635
void chain_controller::apply_message( apply_context& context ) 
636
{ try {
637
    const auto& m = context.msg;
N
Nathan Hourt 已提交
638
    auto contract_handlers_itr = apply_handlers.find( m.recipient );
639
    if( contract_handlers_itr != apply_handlers.end() ) {
640
       auto message_handler_itr = contract_handlers_itr->second.find( {context.scope, m.type} );
N
Nathan Hourt 已提交
641 642
       if( message_handler_itr != contract_handlers_itr->second.end() ) {
          message_handler_itr->second(context);
643 644 645
          return;
       }
    }
646 647 648
    const auto& recipient = _db.get<account_object,by_name>( context.scope );
    if( recipient.code.size() ) {
       wasm_interface::get().apply( context );
649
    }
650

N
Nathan Hourt 已提交
651
} FC_CAPTURE_AND_RETHROW((context.msg)) }
N
Nathan Hourt 已提交
652 653


654
void chain_controller::_apply_transaction(const SignedTransaction& trx)
655
{ try {
656
   validate_transaction(trx);
657

N
Nathan Hourt 已提交
658 659
   for (const auto& message : trx.messages) {
      process_message(message);
N
Nathan Hourt 已提交
660 661 662
   }

   //Insert transaction into unique transactions database.
N
Nathan Hourt 已提交
663
   if (should_check_for_duplicate_transactions())
N
Nathan Hourt 已提交
664
   {
665
      _db.create<transaction_object>([&](transaction_object& transaction) {
666
         transaction.trx_id = trx.id(); /// TODO: consider caching ID
N
Nathan Hourt 已提交
667 668 669
         transaction.trx = trx;
      });
   }
N
Nathan Hourt 已提交
670
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
671 672 673 674 675

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

677
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
678 679 680 681
   EOS_ASSERT(head_block_id() == next_block.previous, block_validate_exception, "",
              ("head_block_id",head_block_id())("next.prev",next_block.previous));
   EOS_ASSERT(head_block_time() < next_block.timestamp, block_validate_exception, "",
              ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()));
N
Nathan Hourt 已提交
682
   if (next_block.block_num() % config::BlocksPerRound != 0) {
683 684
      EOS_ASSERT(next_block.producer_changes.empty(), block_validate_exception,
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
685 686 687 688 689 690 691
   } else {
      using boost::is_sorted;
      using namespace boost::adaptors;
      EOS_ASSERT(is_sorted(keys(next_block.producer_changes)) && is_sorted(values(next_block.producer_changes)),
                 block_validate_exception, "Producer changes are not sorted correctly",
                 ("changes", next_block.producer_changes));
   }
N
Nathan Hourt 已提交
692
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
693

N
Nathan Hourt 已提交
694
   if(!(skip&skip_producer_signature))
695 696 697
      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 已提交
698

699
   if(!(skip&skip_producer_schedule_check)) {
700 701 702
      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 已提交
703 704 705 706 707
   }

   return producer;
}

708
void chain_controller::create_block_summary(const signed_block& next_block) {
709
   auto sid = next_block.block_num() & 0xffff;
710
   _db.modify( _db.get<block_summary_object,by_id>(sid), [&](block_summary_object& p) {
711 712
         p.block_id = next_block.id();
   });
N
Nathan Hourt 已提交
713 714
}

715
void chain_controller::update_global_properties(const signed_block& b) {
716
   // If we're at the end of a round, update the BlockchainConfiguration and producer schedule
N
Nathan Hourt 已提交
717
   if (b.block_num() % config::BlocksPerRound == 0) {
718
      auto schedule = calculate_next_round(b);
719 720
      auto config = _admin->get_blockchain_configuration(_db, schedule);

721 722
      const auto& gpo = get_global_properties();
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
723 724 725 726 727 728
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
   }
}

729
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
730
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
731 732 733
      _checkpoints[i.first] = i.second;
}

734
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
735 736 737 738 739 740 741
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

/**
 *  This method dumps the state of the blockchain in a semi-human readable form for the
 *  purpose of tracking down funds and mismatches in currency allocation
 */
742
void chain_controller::debug_dump() {
N
Nathan Hourt 已提交
743 744
}

745
void debug_apply_update(chain_controller& db, const fc::variant_object& vo) {
N
Nathan Hourt 已提交
746 747
}

748
void chain_controller::apply_debug_updates() {
N
Nathan Hourt 已提交
749 750
}

751
void chain_controller::debug_update(const fc::variant_object& update) {
N
Nathan Hourt 已提交
752 753
}

754
const global_property_object& chain_controller::get_global_properties()const {
755
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
756 757
}

758
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
759
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
760 761
}

762
time_point_sec chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
763 764 765
   return get_dynamic_global_properties().time;
}

766
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
767 768 769
   return get_dynamic_global_properties().head_block_number;
}

770
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
771 772 773
   return get_dynamic_global_properties().head_block_id;
}

N
Nathan Hourt 已提交
774
types::AccountName chain_controller::head_block_producer() const {
N
Nathan Hourt 已提交
775
   if (auto head_block = fetch_block_by_id(head_block_id()))
776
      return head_block->producer;
N
Nathan Hourt 已提交
777 778 779
   return {};
}

780
const node_property_object& chain_controller::get_node_properties()const {
N
Nathan Hourt 已提交
781 782 783
   return _node_property_object;
}

N
Nathan Hourt 已提交
784 785 786 787
const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const {
   return _db.get<producer_object, by_owner>(ownerName);
}

788
node_property_object& chain_controller::node_properties() {
N
Nathan Hourt 已提交
789 790 791
   return _node_property_object;
}

792
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
793
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
794 795
}

796
void chain_controller::initialize_indexes() {
797 798 799 800 801 802 803 804 805 806 807
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
   _db.add_index<action_permission_index>();
   _db.add_index<type_index>();
   _db.add_index<key_value_index>();

   _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>();
   _db.add_index<producer_multi_index>();
N
Nathan Hourt 已提交
808 809
}

810
void chain_controller::initialize_chain(chain_initializer_interface& starter)
N
Nathan Hourt 已提交
811
{ try {
812
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
813
      _db.with_write_lock([this, &starter] {
814 815 816 817 818 819 820 821 822 823
         struct auth_inhibitor {
            auth_inhibitor(chain_controller& db) : db(db), old_flags(db.node_properties().skip_flags)
            { db.node_properties().skip_flags |= skip_authority_check; }
            ~auth_inhibitor()
            { db.node_properties().skip_flags = old_flags; }
         private:
            chain_controller& db;
            uint32_t old_flags;
         } inhibitor(*this);

N
Nathan Hourt 已提交
824 825 826 827
         auto initial_timestamp = starter.get_chain_start_time();
         FC_ASSERT(initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." );
         FC_ASSERT(initial_timestamp.sec_since_epoch() % config::BlockIntervalSeconds == 0,
                    "Genesis timestamp must be divisible by config::BlockIntervalSeconds." );
N
Nathan Hourt 已提交
828

829
         // Create global properties
N
Nathan Hourt 已提交
830 831 832
         _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();
833 834
         });
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
835
            p.time = initial_timestamp;
836 837
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
838

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

843
         auto messages = starter.prepare_database(*this, _db);
N
Nathan Hourt 已提交
844
         std::for_each(messages.begin(), messages.end(), [this](const auto& m) { process_message(m); });
845 846
      });
   }
N
Nathan Hourt 已提交
847 848
} FC_CAPTURE_AND_RETHROW() }

849 850 851
chain_controller::chain_controller(database& database, fork_database& fork_db, block_log& blocklog,
                                   chain_initializer_interface& starter, unique_ptr<chain_administration_interface> admin)
   : _db(database), _fork_db(fork_db), _block_log(blocklog), _admin(std::move(admin)) {
D
Daniel Larimer 已提交
852
      /*
853 854 855 856 857 858 859 860 861
   static bool bound_apply = [](){
      wrenpp::beginModule( "main" )
         .bindClassReference<apply_context>( "ApplyContext" )
            .bindFunction< decltype( &apply_context::get ), &apply_context::get >( false, "get(_)")
            .bindFunction< decltype( &apply_context::set ), &apply_context::set >( false, "set(_,_)")
         .endClass()
      .endModule();
      return true;
   }();
D
Daniel Larimer 已提交
862
   */
N
Nathan Hourt 已提交
863

864
   initialize_indexes();
865
   starter.register_types(*this, _db);
N
Nathan Hourt 已提交
866
   initialize_chain(starter);
867 868
   spinup_db();
   spinup_fork_db();
869 870 871

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

874 875 876 877 878
chain_controller::~chain_controller() {
   clear_pending();
   _db.flush();
   _fork_db.reset();
}
N
Nathan Hourt 已提交
879

880
void chain_controller::replay() {
881
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
882
   auto start = fc::time_point::now();
883 884 885
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
886 887 888 889 890
      return;
   }

   const auto last_block_num = last_block->block_num();

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

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

911 912 913 914
void chain_controller::spinup_db() {
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
D
Daniel Larimer 已提交
915

916 917 918 919
      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 已提交
920

921
void chain_controller::spinup_fork_db()
N
Nathan Hourt 已提交
922
{
923 924 925 926 927 928 929 930
   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 已提交
931 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));
939 940 941 942 943 944 945

   randutils::seed_seq_fe<1> seed{next_block.timestamp.sec_since_epoch()};
   randutils::random_generator<pcg32_fast> rng(seed);
   rng.shuffle(schedule);
   return schedule;
}

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
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time(b.timestamp);
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
951
   missed_blocks--;
N
Nathan Hourt 已提交
952

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

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

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

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

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

991
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
992 993
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
994
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( new_block.timestamp );
N
Nathan Hourt 已提交
995

996
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
997 998 999 1000 1001 1002
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

1003
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
1004 1005 1006 1007
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

N
Nathan Hourt 已提交
1008 1009 1010
   vector<const producer_object*> producer_objs;
   producer_objs.reserve(gpo.active_producers.size());
   std::transform(gpo.active_producers.begin(), gpo.active_producers.end(), std::back_inserter(producer_objs),
N
Nathan Hourt 已提交
1011
                  [this](const AccountName& owner) { return &get_producer(owner); });
N
Nathan Hourt 已提交
1012

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

N
Nathan Hourt 已提交
1015
   size_t offset = EOS_PERCENT(producer_objs.size(), config::Percent100 - config::IrreversibleThresholdPercent);
1016 1017
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
1018
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
1019
      });
N
Nathan Hourt 已提交
1020

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

1023
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
1024
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
1025
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1026
      });
N
Nathan Hourt 已提交
1027
   }
1028 1029

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1030
   auto old_last_irreversible_block = _block_log.head();
1031 1032 1033 1034
   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();
1035

1036 1037 1038 1039 1040 1041
   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);
1042
         _block_log.append(*block);
1043
      }
N
Nathan Hourt 已提交
1044 1045 1046

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

1050
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1051 1052 1053
{ try {
   //Look for expired transactions in the deduplication list, and remove them.
   //Transactions must have expired by at least two forking windows in order to be removed.
1054
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1055 1056 1057 1058 1059 1060 1061
   const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
   while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->trx.expiration) )
      transaction_idx.remove(*dedupe_index.rbegin());
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

N
Nathan Hourt 已提交
1062
types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1063 1064
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1065
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1066
   const auto& gpo = _db.get<global_property_object>();
N
Nathan Hourt 已提交
1067
   return gpo.active_producers[current_aslot % gpo.active_producers.size()];
N
Nathan Hourt 已提交
1068 1069
}

1070
fc::time_point_sec chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
{
   if( slot_num == 0 )
      return fc::time_point_sec();

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

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

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

   return head_slot_time + (slot_num * interval);
}

1091
uint32_t chain_controller::get_slot_at_time(fc::time_point_sec when)const
N
Nathan Hourt 已提交
1092 1093 1094 1095 1096 1097 1098
{
   fc::time_point_sec first_slot_time = get_slot_time( 1 );
   if( when < first_slot_time )
      return 0;
   return (when - first_slot_time).to_seconds() / block_interval() + 1;
}

1099
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1100 1101
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1102
   return uint64_t(config::Percent100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1103 1104
}

1105
void chain_controller::set_validate_handler( const AccountName& contract, const AccountName& scope, const TypeName& action, message_validate_handler v ) {
D
Daniel Larimer 已提交
1106
   message_validate_handlers[contract][std::make_pair(scope,action)] = v;
1107
}
1108
void chain_controller::set_precondition_validate_handler(  const AccountName& contract, const AccountName& scope, const TypeName& action, precondition_validate_handler v ) {
D
Daniel Larimer 已提交
1109
   precondition_validate_handlers[contract][std::make_pair(scope,action)] = v;
1110
}
1111
void chain_controller::set_apply_handler( const AccountName& contract, const AccountName& scope, const TypeName& action, apply_handler v ) {
D
Daniel Larimer 已提交
1112
   apply_handlers[contract][std::make_pair(scope,action)] = v;
1113
}
N
Nathan Hourt 已提交
1114

1115
chain_initializer_interface::~chain_initializer_interface() {}
N
Nathan Hourt 已提交
1116

N
Nathan Hourt 已提交
1117
} }