chain_controller.cpp 45.5 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
#include <eos/types/native.hpp>
#include <eos/types/generated.hpp>
39
#include <eos/types/AbiSerializer.hpp>
40

41
#include <eos/utilities/rand.hpp>
42

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
bool chain_controller::is_known_block(const block_id_type& id)const
N
Nathan Hourt 已提交
62
{
63
   return _fork_db.is_known_block(id) || _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
64 65 66 67 68 69
}
/**
 * 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.
 */
70
bool chain_controller::is_known_transaction(const transaction_id_type& id)const
N
Nathan Hourt 已提交
71
{
72
   const auto& trx_idx = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
73 74 75
   return trx_idx.find( id ) != trx_idx.end();
}

76
block_id_type chain_controller::get_block_id_for_num(uint32_t block_num)const
N
Nathan Hourt 已提交
77
{ try {
78 79
   if (const auto& block = fetch_block_by_number(block_num))
      return block->id();
N
Nathan Hourt 已提交
80

81 82 83
   FC_THROW_EXCEPTION(unknown_block_exception, "Could not find block");
} FC_CAPTURE_AND_RETHROW((block_num)) }

84
optional<signed_block> chain_controller::fetch_block_by_id(const block_id_type& id)const
N
Nathan Hourt 已提交
85
{
86 87
   auto b = _fork_db.fetch_block(id);
   if(b) return b->data;
88
   return _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
89 90
}

91
optional<signed_block> chain_controller::fetch_block_by_number(uint32_t num)const
N
Nathan Hourt 已提交
92
{
93
   if (const auto& block = _block_log.read_block_by_num(num))
94 95
      return *block;

N
Nathan Hourt 已提交
96
   // Not in _block_log, so it must be since the last irreversible block. Grab it from _fork_db instead
97 98 99 100 101 102 103 104
   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 已提交
105 106 107
   return optional<signed_block>();
}

108
const SignedTransaction& chain_controller::get_recent_transaction(const transaction_id_type& trx_id) const
N
Nathan Hourt 已提交
109
{
110
   auto& index = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
111 112 113 114 115
   auto itr = index.find(trx_id);
   FC_ASSERT(itr != index.end());
   return itr->trx;
}

116
std::vector<block_id_type> chain_controller::get_block_ids_on_fork(block_id_type head_of_fork) const
N
Nathan Hourt 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
{
  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.
 */
140
bool chain_controller::push_block(const signed_block& new_block, uint32_t skip)
D
Daniel Larimer 已提交
141 142 143
{ try {
   return with_skip_flags( skip, [&](){ 
      return without_pending_transactions( [&]() {
144
         return _db.with_write_lock( [&]() {
145 146
            return _push_block(new_block);
         } );
N
Nathan Hourt 已提交
147 148
      });
   });
149
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
150

151
bool chain_controller::_push_block(const signed_block& new_block)
N
Nathan Hourt 已提交
152
{ try {
N
Nathan Hourt 已提交
153
   uint32_t skip = _skip_flags;
154
   if (!(skip&skip_fork_db)) {
N
Nathan Hourt 已提交
155
      /// TODO: if the block is greater than the head block and before the next maintenance interval
N
Nathan Hourt 已提交
156 157 158 159
      // 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.
160
      if (new_head->data.previous != head_block_id()) {
N
Nathan Hourt 已提交
161 162
         //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
163 164
         if (new_head->data.block_num() > head_block_num()) {
            wlog("Switching to fork: ${id}", ("id",new_head->data.id()));
N
Nathan Hourt 已提交
165 166 167
            auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());

            // pop blocks until we hit the forked block
168
            while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
169 170 171
               pop_block();

            // push all blocks on the new fork
172 173
            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 已提交
174 175
                optional<fc::exception> except;
                try {
176
                   auto session = _db.start_undo_session(true);
177
                   apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
178 179
                   session.push();
                }
180 181 182
                catch (const fc::exception& e) { except = e; }
                if (except) {
                   wlog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
N
Nathan Hourt 已提交
183
                   // remove the rest of branches.first from the fork_db, those blocks are invalid
184 185
                   while (ritr != branches.first.rend()) {
                      _fork_db.remove((*ritr)->data.id());
N
Nathan Hourt 已提交
186 187
                      ++ritr;
                   }
188
                   _fork_db.set_head(branches.second.front());
N
Nathan Hourt 已提交
189 190

                   // pop all blocks from the bad fork
191
                   while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
192 193 194
                      pop_block();

                   // restore all blocks from the good fork
195
                   for (auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr) {
196
                      auto session = _db.start_undo_session(true);
197
                      apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
198 199 200 201 202 203 204 205 206 207 208 209
                      session.push();
                   }
                   throw *except;
                }
            }
            return true;
         }
         else return false;
      }
   }

   try {
210
      auto session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
211 212 213 214 215 216 217 218 219
      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;
220
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
221 222 223 224 225 226 227 228 229 230

/**
 * 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.
 */
231
void chain_controller::push_transaction(const SignedTransaction& trx, uint32_t skip)
N
Nathan Hourt 已提交
232
{ try {
233
   with_skip_flags(skip, [&]() {
N
Nathan Hourt 已提交
234 235
      _db.with_write_lock([&]() {
         _push_transaction(trx);
D
Daniel Larimer 已提交
236
      });
N
Nathan Hourt 已提交
237 238
   });
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
239

240
void chain_controller::_push_transaction(const SignedTransaction& trx) {
241 242
   // 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.
243
   if (!_pending_tx_session.valid())
244
      _pending_tx_session = _db.start_undo_session(true);
245

246
   auto temp_session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
247
   _apply_transaction(trx);
D
Daniel Larimer 已提交
248
   _pending_transactions.push_back(trx);
N
Nathan Hourt 已提交
249 250 251 252 253 254

   // 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 已提交
255
   on_pending_transaction(trx);
N
Nathan Hourt 已提交
256 257 258
}


259
signed_block chain_controller::generate_block(
N
Nathan Hourt 已提交
260
   fc::time_point_sec when,
N
Nathan Hourt 已提交
261
   const AccountName& producer,
N
Nathan Hourt 已提交
262 263 264 265
   const fc::ecc::private_key& block_signing_private_key,
   uint32_t skip /* = 0 */
   )
{ try {
N
Nathan Hourt 已提交
266 267 268
   return with_skip_flags( skip, [&](){
      auto b = _db.with_write_lock( [&](){
         return _generate_block( when, producer, block_signing_private_key );
D
Daniel Larimer 已提交
269
      });
N
Nathan Hourt 已提交
270 271 272
      push_block(b, skip);
      return b;
   });
D
Daniel Larimer 已提交
273
} FC_CAPTURE_AND_RETHROW( (when) ) }
N
Nathan Hourt 已提交
274

275
signed_block chain_controller::_generate_block(
N
Nathan Hourt 已提交
276
   fc::time_point_sec when,
N
Nathan Hourt 已提交
277
   const AccountName& producer,
N
Nathan Hourt 已提交
278 279 280 281
   const fc::ecc::private_key& block_signing_private_key
   )
{
   try {
N
Nathan Hourt 已提交
282
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
283 284
   uint32_t slot_num = get_slot_at_time( when );
   FC_ASSERT( slot_num > 0 );
N
Nathan Hourt 已提交
285 286
   AccountName scheduled_producer = get_scheduled_producer( slot_num );
   FC_ASSERT( scheduled_producer == producer );
N
Nathan Hourt 已提交
287

N
Nathan Hourt 已提交
288
   const auto& producer_obj = get_producer(scheduled_producer);
N
Nathan Hourt 已提交
289 290 291 292 293

   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;
294
   auto maximum_block_size = get_global_properties().configuration.maxBlockSize;
N
Nathan Hourt 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
   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();
311
   _pending_tx_session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
312 313 314

   uint64_t postponed_tx_count = 0;
   // pop pending state (reset to head block state)
N
Nathan Hourt 已提交
315
   for( const SignedTransaction& tx : _pending_transactions )
N
Nathan Hourt 已提交
316 317 318 319 320 321 322 323 324 325 326 327
   {
      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
      {
328
         auto temp_session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
329
         _apply_transaction(tx);
N
Nathan Hourt 已提交
330 331
         temp_session.squash();

N
Nathan Hourt 已提交
332
         total_block_size += fc::raw::pack_size(tx);
N
Nathan Hourt 已提交
333 334 335 336 337 338
         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 已提交
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
      }
      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 已提交
356
   // _pending_transactions now consists of the set of postponed transactions.
N
Nathan Hourt 已提交
357 358 359 360 361 362
   // 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 已提交
363

364
   pending_block.producer = producer_obj.owner;
N
Nathan Hourt 已提交
365

366 367 368 369 370 371
   // 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 已提交
372 373 374 375
   if( !(skip & skip_producer_signature) )
      pending_block.sign( block_signing_private_key );

   // TODO:  Move this to _push_block() so session is restored.
376
   /*
N
Nathan Hourt 已提交
377 378 379 380
   if( !(skip & skip_block_size_check) )
   {
      FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
   }
381
   */
N
Nathan Hourt 已提交
382

383
   // push_block( pending_block, skip );
N
Nathan Hourt 已提交
384 385

   return pending_block;
N
Nathan Hourt 已提交
386
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
387 388

/**
N
Nathan Hourt 已提交
389
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
390
 */
391
void chain_controller::pop_block()
N
Nathan Hourt 已提交
392 393 394 395 396 397 398
{ 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();
399
   _db.undo();
N
Nathan Hourt 已提交
400 401
} FC_CAPTURE_AND_RETHROW() }

402
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
403
{ try {
D
Daniel Larimer 已提交
404
   _pending_transactions.clear();
N
Nathan Hourt 已提交
405 406 407 408 409
   _pending_tx_session.reset();
} FC_CAPTURE_AND_RETHROW() }

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

410
void chain_controller::apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
411 412
{
   auto block_num = next_block.block_num();
413 414 415 416 417
   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 已提交
418

419
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
420 421
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
422
   with_skip_flags(skip, [&](){ _apply_block(next_block); });
N
Nathan Hourt 已提交
423 424
}

425
void chain_controller::_apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
426 427
{ try {
   uint32_t next_block_num = next_block.block_num();
N
Nathan Hourt 已提交
428
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
429

430 431 432
   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 已提交
433 434 435

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

N
Nathan Hourt 已提交
436 437 438 439 440 441
   /* 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.
    */
442 443 444
   for (const auto& cycle : next_block.cycles) {
      for (const auto& thread : cycle) {
         for(const auto& trx : thread.generated_input ) {
N
Nathan Hourt 已提交
445 446
#warning TODO: Process generated transaction
         }
447 448 449 450 451
         for(const auto& trx : thread.user_input ) {
            apply_transaction(trx);
         }
      }
   }
N
Nathan Hourt 已提交
452

453
   update_global_properties(next_block);
N
Nathan Hourt 已提交
454 455 456 457 458 459 460 461
   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
462
   // TODO: do this outside the write lock...? 
N
Nathan Hourt 已提交
463 464 465 466
   applied_block( next_block ); //emit

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

467
void chain_controller::apply_transaction(const SignedTransaction& trx, uint32_t skip)
N
Nathan Hourt 已提交
468
{
D
Daniel Larimer 已提交
469
   with_skip_flags( skip, [&]() { _apply_transaction(trx); });
N
Nathan Hourt 已提交
470 471
}

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

476 477
   validate_scope(trx);
   validate_expiration(trx);
478 479 480
   validate_uniqueness(trx);
   validate_tapos(trx);
   validate_referenced_accounts(trx);
481

482
   for (const auto& tm : trx.messages) { 
483 484
      const Message* m = reinterpret_cast<const Message*>(&tm); //  m(tm);
      m->for_each_handler( [&]( const AccountName& a ) {
485 486 487

         #warning TODO: call validate handlers on all notified accounts, currently it only calls the recipient's validate

488
         message_validate_context mvc(*this,_db,trx,*m,a);
D
Daniel Larimer 已提交
489
         auto contract_handlers_itr = message_validate_handlers.find(a); /// namespace is the notifier
490
         if (contract_handlers_itr != message_validate_handlers.end()) {
D
Daniel Larimer 已提交
491
            auto message_handler_itr = contract_handlers_itr->second.find({m->code, m->type});
492 493 494 495
            if (message_handler_itr != contract_handlers_itr->second.end()) {
               message_handler_itr->second(mvc);
               return;
            }
496
         }
497 498 499 500 501
         const auto& acnt = _db.get<account_object,by_name>( a );
         if( acnt.code.size() ) {
            wasm_interface::get().validate( mvc );
         }
       });
502
   }
503

504 505
} FC_CAPTURE_AND_RETHROW( (trx) ) }

506 507 508 509 510 511
void chain_controller::validate_scope( const SignedTransaction& trx )const {
   EOS_ASSERT(trx.scope.size() > 0, transaction_exception, "No scope specified by transaction" );
   for( uint32_t i = 1; i < trx.scope.size(); ++i )
      EOS_ASSERT( trx.scope[i-1] < trx.scope[i], transaction_exception, "Scopes must be sorted and unique" );
}

512
void chain_controller::validate_uniqueness( const SignedTransaction& trx )const {
N
Nathan Hourt 已提交
513
   if( !should_check_for_duplicate_transactions() ) return;
N
Nathan Hourt 已提交
514

N
Nathan Hourt 已提交
515 516
   auto transaction = _db.find<transaction_object, by_trx_id>(trx.id());
   EOS_ASSERT(transaction == nullptr, transaction_exception, "Transaction is not unique");
517
}
518

519
void chain_controller::validate_tapos(const SignedTransaction& trx)const {
N
Nathan Hourt 已提交
520
   if (!should_check_tapos()) return;
521

522
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.refBlockNum);
523 524

   //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
N
Nathan Hourt 已提交
525 526 527 528
   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));
}
529

530
void chain_controller::validate_referenced_accounts(const SignedTransaction& trx)const {
N
Nathan Hourt 已提交
531
   for(const auto& auth : trx.authorizations) {
N
Nathan Hourt 已提交
532
      require_account(auth.account);
533
   }
534
   for(const auto& msg : trx.messages) {
535 536 537 538 539
      require_account(msg.code);
      const AccountName* previous_recipient = nullptr;
      for(const auto& current_recipient : msg.recipients) {
         require_account(current_recipient);
         if(previous_recipient) {
540 541 542
            EOS_ASSERT(*previous_recipient < current_recipient, message_validate_exception,
                       "Message recipient accounts out of order. Possibly a bug in the wallet?",
                       ("current", current_recipient.value)("previous", previous_recipient->value));
543
         }
544
         previous_recipient = &current_recipient;
545 546 547
      }
   }
}
D
Daniel Larimer 已提交
548

549
void chain_controller::validate_expiration(const SignedTransaction& trx) const
550 551
{ try {
   fc::time_point_sec now = head_block_time();
552
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
553

554
   EOS_ASSERT(trx.expiration <= now + int32_t(chain_configuration.maxTrxLifetime),
555 556
              transaction_exception, "Transaction expiration is too far in the future",
              ("trx.expiration",trx.expiration)("now",now)
557
              ("max_til_exp",chain_configuration.maxTrxLifetime));
558 559 560
   EOS_ASSERT(now <= trx.expiration, transaction_exception, "Transaction is expired",
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
561

562

563
void chain_controller::validate_message_precondition( precondition_validate_context& context )const 
564
{ try {
565
    const auto& m = context.msg;
D
Daniel Larimer 已提交
566
    auto contract_handlers_itr = precondition_validate_handlers.find(context.code);
567 568 569
    if (contract_handlers_itr != precondition_validate_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 已提交
570
          message_handler_itr->second(context);
571 572 573
          return;
       }
    }
D
Daniel Larimer 已提交
574
    const auto& recipient = _db.get<account_object,by_name>(context.code);
575 576
    if (recipient.code.size()) {
       wasm_interface::get().precondition(context);
577 578
    }
} FC_CAPTURE_AND_RETHROW() }
N
Nathan Hourt 已提交
579

D
Daniel Larimer 已提交
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600

/**
 *  When processing a message it needs to run:
 *
 *  code::precondition( message.code, message.apply )
 *  code::apply( message.code, message.apply )
 *
 *  Then for each recipient it needs to run 
 *
 *  ````
 *   recipient::precondition( message.code, message.apply )
 *   recipient::apply( message.code, message.apply )
 *  ```
 *
 *  The data that can be read is anything declared in trx.scope
 *  The data that can be written is anything stored in  * / [code | recipient] / *
 *
 *  The order of execution of precondition and apply can impact the validity of the
 *  entire message.
 */
void chain_controller::process_message( const Transaction& trx, const Message& message) {
601
   apply_context apply_ctx(*this, _db, trx, message, message.code);
N
Nathan Hourt 已提交
602 603 604 605 606 607 608

   /** 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);

609
   for (const auto& recipient : message.recipients) {
610
      FC_ASSERT( recipient != message.code, "message::code handler is always called and shouldn't be included in recipient list" );
N
Nathan Hourt 已提交
611
      try {
612
         apply_context recipient_ctx(*this,_db, trx, message, recipient);
613 614 615
         validate_message_precondition(recipient_ctx);
         apply_message(recipient_ctx);
      } FC_CAPTURE_AND_RETHROW((recipient)(message))
N
Nathan Hourt 已提交
616 617
   }
}
618

619
void chain_controller::apply_message(apply_context& context)
620
{ try {
D
Daniel Larimer 已提交
621 622
    /// context.code => the execution namespace
    /// message.code / message.type => Event
623
    const auto& m = context.msg;
D
Daniel Larimer 已提交
624
    auto contract_handlers_itr = apply_handlers.find(context.code);
625 626 627
    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 已提交
628
          message_handler_itr->second(context);
629 630 631
          return;
       }
    }
D
Daniel Larimer 已提交
632
    const auto& recipient = _db.get<account_object,by_name>(context.code);
633
    if (recipient.code.size()) {
634
       //idump((context.code)(context.msg.type));
635
       wasm_interface::get().apply(context);
636
    }
637

N
Nathan Hourt 已提交
638
} FC_CAPTURE_AND_RETHROW((context.msg)) }
N
Nathan Hourt 已提交
639

640
void chain_controller::_apply_transaction(const SignedTransaction& trx)
641
{ try {
642
   validate_transaction(trx);
643

644 645 646 647 648 649 650 651 652 653
   auto getAuthority = [&db=_db](const types::AccountPermission& permission) {
      auto key = boost::make_tuple(permission.account, permission.permission);
      return db.get<permission_object, by_owner>(key).auth;
   };
#warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
   auto checker = MakeAuthorityChecker(std::move(getAuthority), trx.get_signature_keys(chain_id_type{}));

   for (const auto& requiredAuthority : trx.authorizations)
      EOS_ASSERT(checker.satisfied(requiredAuthority), tx_missing_auth, "Transaction is not authorized.");

N
Nathan Hourt 已提交
654
   for (const auto& message : trx.messages) {
D
Daniel Larimer 已提交
655
      process_message(trx, message);
N
Nathan Hourt 已提交
656 657 658
   }

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

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 已提交
672

673
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
674 675 676 677
   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 已提交
678
   if (next_block.block_num() % config::BlocksPerRound != 0) {
679 680
      EOS_ASSERT(next_block.producer_changes.empty(), block_validate_exception,
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
681 682 683 684 685 686 687
   } 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 已提交
688
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
689

N
Nathan Hourt 已提交
690
   if(!(skip&skip_producer_signature))
691 692 693
      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 已提交
694

695
   if(!(skip&skip_producer_schedule_check)) {
696 697 698
      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 已提交
699 700 701 702 703
   }

   return producer;
}

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

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

718 719
      const auto& gpo = get_global_properties();
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
720 721 722
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
723

724
      auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {});
725
      for(auto& name : gpo.active_producers) {
726
         active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1});
727 728
      }

729
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::ProducersAccountName, config::ActiveName) );
730 731 732
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
733 734 735
   }
}

736
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
737
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
738 739 740
      _checkpoints[i.first] = i.second;
}

741
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
742 743 744
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

745
const global_property_object& chain_controller::get_global_properties()const {
746
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
747 748
}

749
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
750
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
751 752
}

753
time_point_sec chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
754 755 756
   return get_dynamic_global_properties().time;
}

757
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
758 759 760
   return get_dynamic_global_properties().head_block_number;
}

761
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
762 763 764
   return get_dynamic_global_properties().head_block_id;
}

N
Nathan Hourt 已提交
765
types::AccountName chain_controller::head_block_producer() const {
N
Nathan Hourt 已提交
766
   if (auto head_block = fetch_block_by_id(head_block_id()))
767
      return head_block->producer;
N
Nathan Hourt 已提交
768 769 770
   return {};
}

N
Nathan Hourt 已提交
771 772 773 774
const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const {
   return _db.get<producer_object, by_owner>(ownerName);
}

775
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
776
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
777 778
}

779
void chain_controller::initialize_indexes() {
780 781 782 783
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
784
   _db.add_index<key128x128_value_index>();
785 786 787 788 789 790

   _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 已提交
791 792
}

793
void chain_controller::initialize_chain(chain_initializer_interface& starter)
N
Nathan Hourt 已提交
794
{ try {
795
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
796 797 798 799 800
      _db.with_write_lock([this, &starter] {
         auto initial_timestamp = starter.get_chain_start_time();
         FC_ASSERT(initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." );
         FC_ASSERT(initial_timestamp.sec_since_epoch() % config::BlockIntervalSeconds == 0,
                    "Genesis timestamp must be divisible by config::BlockIntervalSeconds." );
N
Nathan Hourt 已提交
801

802
         // Create global properties
N
Nathan Hourt 已提交
803 804 805
         _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();
806 807
         });
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
808
            p.time = initial_timestamp;
809 810
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
811

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

816
         auto messages = starter.prepare_database(*this, _db);
D
Daniel Larimer 已提交
817 818
         const Transaction trx; /// dummy tranaction required for scope validation
         std::for_each(messages.begin(), messages.end(), [&](const Message& m) { process_message(trx,m); });
819 820
      });
   }
N
Nathan Hourt 已提交
821 822
} FC_CAPTURE_AND_RETHROW() }

823 824 825
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)) {
N
Nathan Hourt 已提交
826

827
   initialize_indexes();
828
   starter.register_types(*this, _db);
N
Nathan Hourt 已提交
829
   initialize_chain(starter);
830 831
   spinup_db();
   spinup_fork_db();
832 833 834

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

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

843
void chain_controller::replay() {
844
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
845
   auto start = fc::time_point::now();
846 847 848
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
849 850 851 852 853
      return;
   }

   const auto last_block_num = last_block->block_num();

854 855 856 857 858 859
   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 已提交
860 861 862 863 864 865 866 867
      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();
868 869
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
870

871
   _db.set_revision(head_block_num());
872
}
N
Nathan Hourt 已提交
873

874 875 876 877
void chain_controller::spinup_db() {
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
D
Daniel Larimer 已提交
878

879 880 881 882
      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 已提交
883

884
void chain_controller::spinup_fork_db()
N
Nathan Hourt 已提交
885
{
886 887 888 889 890 891 892 893
   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 已提交
894 895
}

896 897
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
898 899 900 901
   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));
902

903
   utilities::rand::random rng(next_block.timestamp.sec_since_epoch());
904 905 906 907
   rng.shuffle(schedule);
   return schedule;
}

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

911 912
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time(b.timestamp);
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
913
   missed_blocks--;
N
Nathan Hourt 已提交
914

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

N
Nathan Hourt 已提交
918
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
919
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
920
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
921 922 923 924 925 926
         /*
         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) );
            */

927
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
928 929 930 931 932 933
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
934
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
935 936 937
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
938
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
939 940 941 942 943 944 945 946 947
      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 已提交
948 949 950 951 952
   });

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

953
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
954 955
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
956
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( new_block.timestamp );
N
Nathan Hourt 已提交
957

958
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
959 960 961 962 963 964
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

965
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
966 967 968 969
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

N
Nathan Hourt 已提交
970 971 972
   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 已提交
973
                  [this](const AccountName& owner) { return &get_producer(owner); });
N
Nathan Hourt 已提交
974

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

N
Nathan Hourt 已提交
977
   size_t offset = EOS_PERCENT(producer_objs.size(), config::Percent100 - config::IrreversibleThresholdPercent);
978 979
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
980
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
981
      });
N
Nathan Hourt 已提交
982

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

985
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
986
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
987
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
988
      });
N
Nathan Hourt 已提交
989
   }
990 991

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
992
   auto old_last_irreversible_block = _block_log.head();
993 994 995 996
   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();
997

998 999 1000 1001 1002 1003
   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);
1004
         _block_log.append(*block);
1005
      }
N
Nathan Hourt 已提交
1006 1007 1008

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

1012
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1013 1014 1015
{ 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.
1016
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1017 1018 1019 1020 1021 1022 1023
   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 已提交
1024
types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1025 1026
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1027
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1028
   const auto& gpo = _db.get<global_property_object>();
N
Nathan Hourt 已提交
1029
   return gpo.active_producers[current_aslot % gpo.active_producers.size()];
N
Nathan Hourt 已提交
1030 1031
}

1032
fc::time_point_sec chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
{
   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);
}

1053
uint32_t chain_controller::get_slot_at_time(fc::time_point_sec when)const
N
Nathan Hourt 已提交
1054 1055 1056 1057 1058 1059 1060
{
   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;
}

1061
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1062 1063
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1064
   return uint64_t(config::Percent100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1065 1066
}

1067
void chain_controller::set_validate_handler( const AccountName& contract, const AccountName& scope, const ActionName& action, message_validate_handler v ) {
D
Daniel Larimer 已提交
1068
   message_validate_handlers[contract][std::make_pair(scope,action)] = v;
1069
}
1070
void chain_controller::set_precondition_validate_handler(  const AccountName& contract, const AccountName& scope, const ActionName& action, precondition_validate_handler v ) {
D
Daniel Larimer 已提交
1071
   precondition_validate_handlers[contract][std::make_pair(scope,action)] = v;
1072
}
1073
void chain_controller::set_apply_handler( const AccountName& contract, const AccountName& scope, const ActionName& action, apply_handler v ) {
D
Daniel Larimer 已提交
1074
   apply_handlers[contract][std::make_pair(scope,action)] = v;
1075
}
N
Nathan Hourt 已提交
1076

1077
chain_initializer_interface::~chain_initializer_interface() {}
N
Nathan Hourt 已提交
1078

1079

1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
SignedTransaction chain_controller::transaction_from_variant( const fc::variant& v )const {
   const variant_object& vo = v.get_object();
#define GET_FIELD( VO, FIELD, RESULT ) \
   if( VO.contains(#FIELD) ) fc::from_variant( VO[#FIELD], RESULT.FIELD )

   SignedTransaction result;
   GET_FIELD( vo, refBlockNum, result );
   GET_FIELD( vo, refBlockPrefix, result );
   GET_FIELD( vo, expiration, result );
   GET_FIELD( vo, scope, result );
   GET_FIELD( vo, signatures, result );

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

         if( vo.contains( "data" ) ) {
            const auto& data = vo["data"];
            if( data.is_string() ) {
               GET_FIELD( vo, data, result.messages[i] );
            } else if ( data.is_object() ) {
               const auto& code_account = _db.get<account_object,by_name>( result.messages[i].code );
               if( code_account.abi.size() > 4 ) { /// 4 == packsize of empty Abi
                  fc::datastream<const char*> ds( code_account.abi.data(), code_account.abi.size() );
                  eos::types::Abi abi;
                  fc::raw::unpack( ds, abi );
                  types::AbiSerializer abis( abi );
                  result.messages[i].data = abis.variantToBinary( abis.getActionType( result.messages[i].type ), data );
               }
            }
         }
      }
   }
   return result;
#undef GET_FIELD
}

fc::variant  chain_controller::transaction_to_variant( const SignedTransaction& trx )const {
#define SET_FIELD( MVO, OBJ, FIELD ) MVO(#FIELD, OBJ.FIELD)

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

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

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

       const auto& code_account = _db.get<account_object,by_name>( msg.code );
       if( code_account.abi.size() > 4 ) { /// 4 == packsize of empty Abi
          fc::datastream<const char*> ds( code_account.abi.data(), code_account.abi.size() );
          eos::types::Abi abi;
          fc::raw::unpack( ds, abi );
          types::AbiSerializer abis( abi );
          msg_mvo( "data", abis.binaryToVariant( abis.getActionType( msg.type ), msg.data ) );
       }
       else {
         SET_FIELD( msg_mvo, msg, data );
       }
       msgsv[i] = std::move( msgs[i] );
    }
    trx_mvo( "messages", std::move(msgsv) );

    return fc::variant( std::move( trx_mvo ) );
#undef SET_FIELD
}

1163

N
Nathan Hourt 已提交
1164
} }