chain_controller.cpp 50.8 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>
32
#include <eos/chain/generated_transaction_object.hpp>
N
Nathan Hourt 已提交
33 34
#include <eos/chain/transaction_object.hpp>
#include <eos/chain/producer_object.hpp>
35
#include <eos/chain/permission_link_object.hpp>
N
Nathan Hourt 已提交
36

37 38
#include <eos/chain/wasm_interface.hpp>

39 40
#include <eos/types/native.hpp>
#include <eos/types/generated.hpp>
41
#include <eos/types/AbiSerializer.hpp>
42

43
#include <eos/utilities/rand.hpp>
44

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

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

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

D
Daniel Larimer 已提交
58
//#include <Wren++.h>
59

N
Nathan Hourt 已提交
60 61
namespace eos { namespace chain {

62

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

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

83 84 85
   FC_THROW_EXCEPTION(unknown_block_exception, "Could not find block");
} FC_CAPTURE_AND_RETHROW((block_num)) }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

   // notify anyone listening to pending transactions
258 259 260
   on_pending_transaction(trx); /// TODO move this to apply...

   return pt;
N
Nathan Hourt 已提交
261 262
}

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

280
signed_block chain_controller::_generate_block(
N
Nathan Hourt 已提交
281
   fc::time_point_sec when,
N
Nathan Hourt 已提交
282
   const AccountName& producer,
283 284
   const fc::ecc::private_key& block_signing_private_key,
   block_schedule::factory scheduler
N
Nathan Hourt 已提交
285 286 287
   )
{
   try {
N
Nathan Hourt 已提交
288
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
289 290
   uint32_t slot_num = get_slot_at_time( when );
   FC_ASSERT( slot_num > 0 );
N
Nathan Hourt 已提交
291 292
   AccountName scheduled_producer = get_scheduled_producer( slot_num );
   FC_ASSERT( scheduled_producer == producer );
N
Nathan Hourt 已提交
293

N
Nathan Hourt 已提交
294
   const auto& producer_obj = get_producer(scheduled_producer);
N
Nathan Hourt 已提交
295 296 297 298

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

299 300 301 302 303 304 305 306 307 308 309 310 311
  
   auto& generated = _db.get_index<generated_transaction_multi_index, generated_transaction_object::by_trx_id>();

   vector<pending_transaction> pending;
   pending.reserve(generated.size() + _pending_transactions.size());
   for (auto const &gt: generated) {
      pending.emplace_back(pending_transaction {&gt.trx});
   }
   
   for(auto const &st: _pending_transactions) {
      pending.emplace_back(pending_transaction {&st});
   }

312
   auto schedule = scheduler(pending, get_global_properties());
N
Nathan Hourt 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325

   //
   // 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();
326
   _pending_tx_session = _db.start_undo_session(true);
N
Nathan Hourt 已提交
327

328 329 330 331 332 333 334 335 336 337 338 339
   signed_block pending_block;
   pending_block.cycles.reserve(schedule.cycles.size());

   size_t invalid_transaction_count = 0;
   size_t valid_transaction_count = 0;

   for (const auto &c : schedule.cycles) {
     cycle block_cycle;
     block_cycle.reserve(c.size());

     for (const auto &t : c) {
       thread block_thread;
340 341 342 343 344 345
       block_thread.user_input.reserve(t.transactions.size());
       block_thread.generated_input.reserve(t.transactions.size());
       for (const auto &trx : t.transactions) {
          try
          {
             auto temp_session = _db.start_undo_session(true);
346
             if (trx.contains<SignedTransaction const *>()) {
347 348 349
                auto const &t = *trx.get<SignedTransaction const *>();
                check_transaction_authorization(t);
                auto processed = _apply_transaction(t);
350
                block_thread.user_input.emplace_back(processed);
351
             } else if (trx.contains<GeneratedTransaction const *>()) {
352
                #warning TODO: Process generated transaction
353
                // auto processed = _apply_transaction(*trx.get<GeneratedTransaction const *>());
354 355 356 357 358 359 360 361 362 363 364 365
                // block_thread.generated_input.emplace_back(processed);
             } else {
                FC_THROW_EXCEPTION(tx_scheduling_exception, "Unknown transaction type in block_schedule");
             }
             
             temp_session.squash();
             valid_transaction_count++;
          }
          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) );
366 367 368 369
             if (trx.contains<SignedTransaction const *>()) {
                wlog( "The transaction was ${t}", ("t", *trx.get<SignedTransaction const *>()) );
             } else if (trx.contains<GeneratedTransaction const *>()) {
                wlog( "The transaction was ${t}", ("t", *trx.get<GeneratedTransaction const *>()) );
370 371 372
             } 
             invalid_transaction_count++;
          }
373 374 375
       }

       if (!(block_thread.generated_input.empty() && block_thread.user_input.empty())) {
376 377
          block_thread.generated_input.shrink_to_fit();
          block_thread.user_input.shrink_to_fit();
378 379 380 381 382 383 384
          block_cycle.emplace_back(std::move(block_thread));
       }
     }

     if (!block_cycle.empty()) {
        pending_block.cycles.emplace_back(std::move(block_cycle));
     }
N
Nathan Hourt 已提交
385
   }
386 387
   
   size_t postponed_tx_count = _pending_transactions.size() - valid_transaction_count - invalid_transaction_count;
N
Nathan Hourt 已提交
388 389 390 391 392
   if( postponed_tx_count > 0 )
   {
      wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
   }

393 394 395 396 397
   if( invalid_transaction_count > 0 )
   {
      wlog( "Postponed ${n} transactions errors when processing", ("n", invalid_transaction_count) );
   }

N
Nathan Hourt 已提交
398 399 400 401
   _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 已提交
402
   // _pending_transactions now consists of the set of postponed transactions.
N
Nathan Hourt 已提交
403 404 405 406 407 408
   // 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 已提交
409

410
   pending_block.producer = producer_obj.owner;
N
Nathan Hourt 已提交
411

412 413 414 415 416 417
   // 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 已提交
418 419 420 421
   if( !(skip & skip_producer_signature) )
      pending_block.sign( block_signing_private_key );

   // TODO:  Move this to _push_block() so session is restored.
422
   /*
N
Nathan Hourt 已提交
423 424 425 426
   if( !(skip & skip_block_size_check) )
   {
      FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
   }
427
   */
N
Nathan Hourt 已提交
428

429
   // push_block( pending_block, skip );
N
Nathan Hourt 已提交
430 431

   return pending_block;
N
Nathan Hourt 已提交
432
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
433 434

/**
N
Nathan Hourt 已提交
435
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
436
 */
437
void chain_controller::pop_block()
N
Nathan Hourt 已提交
438 439 440 441 442 443 444
{ 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();
445
   _db.undo();
N
Nathan Hourt 已提交
446 447
} FC_CAPTURE_AND_RETHROW() }

448
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
449
{ try {
D
Daniel Larimer 已提交
450
   _pending_transactions.clear();
N
Nathan Hourt 已提交
451 452 453 454 455
   _pending_tx_session.reset();
} FC_CAPTURE_AND_RETHROW() }

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

456
void chain_controller::apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
457 458
{
   auto block_num = next_block.block_num();
459 460 461 462 463
   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 已提交
464

465
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
466 467
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
468
   with_skip_flags(skip, [&](){ _apply_block(next_block); });
N
Nathan Hourt 已提交
469 470
}

471
void chain_controller::_apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
472 473
{ try {
   uint32_t next_block_num = next_block.block_num();
N
Nathan Hourt 已提交
474
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
475

476 477 478
   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 已提交
479 480

   const producer_object& signing_producer = validate_block_header(skip, next_block);
N
Nathan Hourt 已提交
481 482 483 484 485
   
   for (const auto& cycle : next_block.cycles)
      for (const auto& thread : cycle)
         for (const auto& trx : thread.user_input)
            check_transaction_authorization(trx);
N
Nathan Hourt 已提交
486

N
Nathan Hourt 已提交
487 488 489 490 491 492
   /* 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.
    */
493 494 495
   for (const auto& cycle : next_block.cycles) {
      for (const auto& thread : cycle) {
         for(const auto& trx : thread.generated_input ) {
N
Nathan Hourt 已提交
496 497
#warning TODO: Process generated transaction
         }
498
         for(const auto& trx : thread.user_input ) {
499
            _apply_transaction(trx);
500 501 502
         }
      }
   }
N
Nathan Hourt 已提交
503

504
   update_global_properties(next_block);
N
Nathan Hourt 已提交
505 506 507 508 509 510 511 512
   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
513
   // TODO: do this outside the write lock...? 
N
Nathan Hourt 已提交
514 515 516 517
   applied_block( next_block ); //emit

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

N
Nathan Hourt 已提交
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
void chain_controller::check_transaction_authorization(const SignedTransaction& trx)const {
   if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
      ilog("Skipping auth and sigs checks");
      return;
   }

   auto getPermission = [&db=_db](const types::AccountPermission& permission) {
      auto key = boost::make_tuple(permission.account, permission.permission);
      return db.get<permission_object, by_owner>(key);
   };
   auto getAuthority = [&getPermission](const types::AccountPermission& permission) {
      return getPermission(permission).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& message : trx.messages)
      for (const auto& declaredAuthority : message.authorization) {
         const auto& minimumPermission = lookup_minimum_permission(declaredAuthority.account,
                                                                   message.code, message.type);
         if ((_skip_flags & skip_authority_check) == false) {
            const auto& index = _db.get_index<permission_index>().indices();
            EOS_ASSERT(getPermission(declaredAuthority).satisfies(minimumPermission, index), tx_irrelevant_auth,
                       "Message declares irrelevant authority '${auth}'", ("auth", declaredAuthority));
         }
         if ((_skip_flags & skip_transaction_signatures) == false) {
            EOS_ASSERT(checker.satisfied(declaredAuthority), tx_missing_sigs,
                       "Transaction declares authority '${auth}', but does not have signatures for it.",
                       ("auth", declaredAuthority));
         }
      }
}

551
ProcessedTransaction chain_controller::apply_transaction(const SignedTransaction& trx, uint32_t skip)
N
Nathan Hourt 已提交
552
{
553
   return with_skip_flags( skip, [&]() { return _apply_transaction(trx); });
N
Nathan Hourt 已提交
554 555
}

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

560 561
   validate_scope(trx);
   validate_expiration(trx);
562 563 564
   validate_uniqueness(trx);
   validate_tapos(trx);
   validate_referenced_accounts(trx);
565

566
} FC_CAPTURE_AND_RETHROW( (trx) ) }
567

568 569 570 571 572 573
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" );
}

N
Nathan Hourt 已提交
574 575 576 577
const permission_object& chain_controller::lookup_minimum_permission(types::AccountName authorizer_account,
                                                                    types::AccountName code_account,
                                                                    types::FuncName type) const {
   try {
578 579 580 581 582 583 584 585 586 587 588 589 590 591
      // First look up a specific link for this message type
      auto key = boost::make_tuple(authorizer_account, code_account, type);
      auto link = _db.find<permission_link_object, by_message_type>(key);
      // If no specific link found, check for a contract-wide default
      if (link == nullptr) {
         get<2>(key) = "";
         link = _db.find<permission_link_object, by_message_type>(key);
      }

      // If no specific or default link found, use active permission
      auto permissionKey = boost::make_tuple<AccountName, PermissionName>(authorizer_account, "active");
      if (link != nullptr)
         get<1>(permissionKey) = link->required_permission;
      return _db.get<permission_object, by_owner>(permissionKey);
N
Nathan Hourt 已提交
592 593 594
   } FC_CAPTURE_AND_RETHROW((authorizer_account)(code_account)(type))
}

595
void chain_controller::validate_uniqueness( const SignedTransaction& trx )const {
N
Nathan Hourt 已提交
596
   if( !should_check_for_duplicate_transactions() ) return;
N
Nathan Hourt 已提交
597

N
Nathan Hourt 已提交
598
   auto transaction = _db.find<transaction_object, by_trx_id>(trx.id());
599
   EOS_ASSERT(transaction == nullptr, tx_duplicate, "Transaction is not unique");
600
}
601

602
void chain_controller::validate_tapos(const SignedTransaction& trx)const {
N
Nathan Hourt 已提交
603
   if (!should_check_tapos()) return;
604

605
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.refBlockNum);
606 607

   //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
N
Nathan Hourt 已提交
608 609 610 611
   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));
}
612

613
void chain_controller::validate_referenced_accounts(const SignedTransaction& trx)const {
N
Nathan Hourt 已提交
614 615 616
   for (const auto& scope : trx.scope)
      require_account(scope);
   for (const auto& msg : trx.messages) {
617
      require_account(msg.code);
N
Nathan Hourt 已提交
618 619
      for (const auto& auth : msg.authorization)
         require_account(auth.account);
620 621
   }
}
D
Daniel Larimer 已提交
622

623
void chain_controller::validate_expiration(const SignedTransaction& trx) const
624 625
{ try {
   fc::time_point_sec now = head_block_time();
626
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
627

628
   EOS_ASSERT(trx.expiration <= now + int32_t(chain_configuration.maxTrxLifetime),
629 630
              transaction_exception, "Transaction expiration is too far in the future",
              ("trx.expiration",trx.expiration)("now",now)
631
              ("max_til_exp",chain_configuration.maxTrxLifetime));
632 633 634
   EOS_ASSERT(now <= trx.expiration, transaction_exception, "Transaction is expired",
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
635

636

D
Daniel Larimer 已提交
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
/**
 *  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.
 */
N
Nathan Hourt 已提交
656 657
void chain_controller::process_message(const ProcessedTransaction& trx, AccountName code,
                                       const Message& message, MessageOutput& output) {
658
   apply_context apply_ctx(*this, _db, trx, message, code);
N
Nathan Hourt 已提交
659 660
   apply_message(apply_ctx);

661 662 663
   // process_message recurses for each notified account, but we only want to run this check at the top level
   if (code == message.code && (_skip_flags & skip_authority_check) == false)
      EOS_ASSERT(apply_ctx.all_authorizations_used(), tx_irrelevant_auth,
N
Nathan Hourt 已提交
664 665
                 "Message declared authorities it did not need: ${unused}",
                 ("unused", apply_ctx.unused_authorizations())("message", message));
666

667 668 669
   output.notify.reserve( apply_ctx.notified.size() );

   for( uint32_t i = 0; i < apply_ctx.notified.size(); ++i ) {
N
Nathan Hourt 已提交
670
      try {
671 672
         auto notify_code = apply_ctx.notified[i];
         output.notify.push_back( {notify_code} );
673
         process_message( trx, notify_code, message, output.notify.back().output );
674 675 676 677 678 679 680 681 682 683 684
      } FC_CAPTURE_AND_RETHROW((apply_ctx.notified[i]))
   }

   for( const auto& generated : apply_ctx.sync_transactions ) {
      try {
         output.sync_transactions.emplace_back( process_transaction( generated ) );
      } FC_CAPTURE_AND_RETHROW((generated))
   }

   for( auto& asynctrx : apply_ctx.async_transactions ) {
        output.async_transactions.emplace_back( std::move( asynctrx ) );
N
Nathan Hourt 已提交
685 686
   }
}
687

688
void chain_controller::apply_message(apply_context& context)
689
{ try {
D
Daniel Larimer 已提交
690 691
    /// context.code => the execution namespace
    /// message.code / message.type => Event
692
    const auto& m = context.msg;
D
Daniel Larimer 已提交
693
    auto contract_handlers_itr = apply_handlers.find(context.code);
694 695 696
    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 已提交
697
          message_handler_itr->second(context);
698 699 700
          return;
       }
    }
D
Daniel Larimer 已提交
701
    const auto& recipient = _db.get<account_object,by_name>(context.code);
702
    if (recipient.code.size()) {
703
       //idump((context.code)(context.msg.type));
704
       wasm_interface::get().apply(context);
705
    }
706

N
Nathan Hourt 已提交
707
} FC_CAPTURE_AND_RETHROW((context.msg)) }
N
Nathan Hourt 已提交
708

709
ProcessedTransaction chain_controller::_apply_transaction(const SignedTransaction& trx)
710
{ try {
711
   validate_transaction(trx);
712

N
Nathan Hourt 已提交
713
   //Insert transaction into unique transactions database.
N
Nathan Hourt 已提交
714
   if (should_check_for_duplicate_transactions())
N
Nathan Hourt 已提交
715
   {
716
      _db.create<transaction_object>([&](transaction_object& transaction) {
717
         transaction.trx_id = trx.id(); /// TODO: consider caching ID
N
Nathan Hourt 已提交
718 719 720
         transaction.trx = trx;
      });
   }
721 722 723

   return process_transaction( trx );

N
Nathan Hourt 已提交
724
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
725

726 727 728 729 730 731 732 733 734 735

/**
 *  @pre the transaction is assumed valid and all signatures / duplicate checks have bee performed
 */
ProcessedTransaction chain_controller::process_transaction( const SignedTransaction& trx ) 
{ try {
   ProcessedTransaction ptrx( trx );
   ptrx.output.resize( trx.messages.size() );

   for( uint32_t i = 0; i < ptrx.messages.size(); ++i ) {
736
      process_message(ptrx, ptrx.messages[i].code, ptrx.messages[i], ptrx.output[i] );
737 738 739 740 741 742
   }

   return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }


N
Nathan Hourt 已提交
743 744 745 746
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 已提交
747

748
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
749 750 751 752
   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 已提交
753
   if (next_block.block_num() % config::BlocksPerRound != 0) {
754 755
      EOS_ASSERT(next_block.producer_changes.empty(), block_validate_exception,
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
756 757 758 759 760 761 762
   } 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 已提交
763
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
764

N
Nathan Hourt 已提交
765
   if(!(skip&skip_producer_signature))
766 767 768
      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 已提交
769

770
   if(!(skip&skip_producer_schedule_check)) {
771 772 773
      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 已提交
774 775 776 777 778
   }

   return producer;
}

779
void chain_controller::create_block_summary(const signed_block& next_block) {
780
   auto sid = next_block.block_num() & 0xffff;
781
   _db.modify( _db.get<block_summary_object,by_id>(sid), [&](block_summary_object& p) {
782 783
         p.block_id = next_block.id();
   });
N
Nathan Hourt 已提交
784 785
}

786
void chain_controller::update_global_properties(const signed_block& b) {
787 788
   // If we're at the end of a round, update the BlockchainConfiguration, producer schedule
   // and "producers" special account authority
N
Nathan Hourt 已提交
789
   if (b.block_num() % config::BlocksPerRound == 0) {
790
      auto schedule = calculate_next_round(b);
791 792
      auto config = _admin->get_blockchain_configuration(_db, schedule);

793 794
      const auto& gpo = get_global_properties();
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
795 796 797
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
798

799
      auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {});
800
      for(auto& name : gpo.active_producers) {
801
         active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1});
802 803
      }

804
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::ProducersAccountName, config::ActiveName) );
805 806 807
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
808 809 810
   }
}

811
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
812
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
813 814 815
      _checkpoints[i.first] = i.second;
}

816
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
817 818 819
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

820
const global_property_object& chain_controller::get_global_properties()const {
821
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
822 823
}

824
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
825
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
826 827
}

828
time_point_sec chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
829 830 831
   return get_dynamic_global_properties().time;
}

832
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
833 834 835
   return get_dynamic_global_properties().head_block_number;
}

836
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
837 838 839
   return get_dynamic_global_properties().head_block_id;
}

N
Nathan Hourt 已提交
840
types::AccountName chain_controller::head_block_producer() const {
N
Nathan Hourt 已提交
841
   if (auto head_block = fetch_block_by_id(head_block_id()))
842
      return head_block->producer;
N
Nathan Hourt 已提交
843 844 845
   return {};
}

N
Nathan Hourt 已提交
846 847 848 849
const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const {
   return _db.get<producer_object, by_owner>(ownerName);
}

850
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
851
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
852 853
}

854
void chain_controller::initialize_indexes() {
855 856
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
857
   _db.add_index<permission_link_index>();
858 859
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
860
   _db.add_index<key128x128_value_index>();
861 862 863 864 865

   _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>();
866
   _db.add_index<generated_transaction_multi_index>();
867
   _db.add_index<producer_multi_index>();
N
Nathan Hourt 已提交
868 869
}

870
void chain_controller::initialize_chain(chain_initializer_interface& starter)
N
Nathan Hourt 已提交
871
{ try {
872
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
873 874 875 876 877
      _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 已提交
878

879
         // Create global properties
N
Nathan Hourt 已提交
880 881 882
         _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();
883 884
         });
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
885
            p.time = initial_timestamp;
886 887
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
888

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

893
         auto messages = starter.prepare_database(*this, _db);
894 895
         std::for_each(messages.begin(), messages.end(), [&](const Message& m) { 
            MessageOutput output;
896
            ProcessedTransaction trx; /// dummy tranaction required for scope validation
897
            std::sort(trx.scope.begin(), trx.scope.end() );
898
            with_skip_flags(skip_scope_check | skip_transaction_signatures | skip_authority_check, [&](){
899
               process_message(trx,m.code,m,output); 
900
            });
901
         });
902 903
      });
   }
N
Nathan Hourt 已提交
904 905
} FC_CAPTURE_AND_RETHROW() }

906 907 908
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 已提交
909

910
   initialize_indexes();
911
   starter.register_types(*this, _db);
N
Nathan Hourt 已提交
912
   initialize_chain(starter);
913 914
   spinup_db();
   spinup_fork_db();
915 916 917

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

920 921 922 923 924
chain_controller::~chain_controller() {
   clear_pending();
   _db.flush();
   _fork_db.reset();
}
N
Nathan Hourt 已提交
925

926
void chain_controller::replay() {
927
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
928
   auto start = fc::time_point::now();
929 930 931
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
932 933 934 935 936
      return;
   }

   const auto last_block_num = last_block->block_num();

937 938 939 940 941 942
   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 已提交
943 944 945 946 947 948 949 950
      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();
951 952
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
953

954
   _db.set_revision(head_block_num());
955
}
N
Nathan Hourt 已提交
956

957 958 959 960
void chain_controller::spinup_db() {
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
D
Daniel Larimer 已提交
961

962 963 964 965
      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 已提交
966

967
void chain_controller::spinup_fork_db()
N
Nathan Hourt 已提交
968
{
969 970 971 972 973 974 975 976
   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 已提交
977 978
}

979 980
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
981 982 983 984
   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));
985

986
   utilities::rand::random rng(next_block.timestamp.sec_since_epoch());
987 988 989 990
   rng.shuffle(schedule);
   return schedule;
}

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

994 995
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time(b.timestamp);
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
996
   missed_blocks--;
N
Nathan Hourt 已提交
997

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

N
Nathan Hourt 已提交
1001
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
1002
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
1003
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
1004 1005 1006 1007 1008 1009
         /*
         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) );
            */

1010
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
1011 1012 1013 1014 1015 1016
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
1017
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
1018 1019 1020
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
1021
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
1022 1023 1024 1025 1026 1027 1028 1029 1030
      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 已提交
1031 1032 1033 1034 1035
   });

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

1036
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
1037 1038
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1039
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( new_block.timestamp );
N
Nathan Hourt 已提交
1040

1041
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
1042 1043 1044 1045 1046 1047
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

1048
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
1049 1050 1051 1052
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

N
Nathan Hourt 已提交
1053 1054 1055
   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 已提交
1056
                  [this](const AccountName& owner) { return &get_producer(owner); });
N
Nathan Hourt 已提交
1057

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

N
Nathan Hourt 已提交
1060
   size_t offset = EOS_PERCENT(producer_objs.size(), config::Percent100 - config::IrreversibleThresholdPercent);
1061 1062
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
1063
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
1064
      });
N
Nathan Hourt 已提交
1065

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

1068
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
1069
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
1070
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1071
      });
N
Nathan Hourt 已提交
1072
   }
1073 1074

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1075
   auto old_last_irreversible_block = _block_log.head();
1076 1077 1078 1079
   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();
1080

1081 1082 1083 1084 1085 1086
   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);
1087
         _block_log.append(*block);
1088
      }
N
Nathan Hourt 已提交
1089 1090 1091

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

1095
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1096 1097 1098
{ 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.
1099
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1100 1101 1102
   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());
1103 1104 1105 1106 1107 1108 1109

   //Look for expired transactions in the pending generated list, and remove them.
   //Transactions must have expired by at least two forking windows in order to be removed.
   auto& generated_transaction_idx = _db.get_mutable_index<generated_transaction_multi_index>();
   const auto& generated_index = generated_transaction_idx.indices().get<generated_transaction_object::by_expiration>();
   while( (!generated_index.empty()) && (head_block_time() > generated_index.rbegin()->trx.expiration) )
      generated_transaction_idx.remove(*generated_index.rbegin());
N
Nathan Hourt 已提交
1110 1111 1112 1113
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

N
Nathan Hourt 已提交
1114
types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1115 1116
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1117
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1118
   const auto& gpo = _db.get<global_property_object>();
N
Nathan Hourt 已提交
1119
   return gpo.active_producers[current_aslot % gpo.active_producers.size()];
N
Nathan Hourt 已提交
1120 1121
}

1122
fc::time_point_sec chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
{
   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);
}

1143
uint32_t chain_controller::get_slot_at_time(fc::time_point_sec when)const
N
Nathan Hourt 已提交
1144 1145 1146 1147 1148 1149 1150
{
   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;
}

1151
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1152 1153
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1154
   return uint64_t(config::Percent100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1155 1156
}

1157
void chain_controller::set_apply_handler( const AccountName& contract, const AccountName& scope, const ActionName& action, apply_handler v ) {
D
Daniel Larimer 已提交
1158
   apply_handlers[contract][std::make_pair(scope,action)] = v;
1159
}
N
Nathan Hourt 已提交
1160

1161
chain_initializer_interface::~chain_initializer_interface() {}
N
Nathan Hourt 已提交
1162

1163

D
Daniel Larimer 已提交
1164
ProcessedTransaction chain_controller::transaction_from_variant( const fc::variant& v )const {
1165 1166 1167 1168
   const variant_object& vo = v.get_object();
#define GET_FIELD( VO, FIELD, RESULT ) \
   if( VO.contains(#FIELD) ) fc::from_variant( VO[#FIELD], RESULT.FIELD )

D
Daniel Larimer 已提交
1169
   ProcessedTransaction result;
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
   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] );
1183
         GET_FIELD( vo, authorization, result.messages[i] );
1184 1185 1186 1187 1188 1189

         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() ) {
1190 1191
               result.messages[i].data = message_to_binary( result.messages[i].code, result.messages[i].type, data ); 
               /*
1192 1193 1194 1195 1196 1197 1198 1199
               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 );
               }
1200
               */
1201 1202 1203 1204
            }
         }
      }
   }
D
Daniel Larimer 已提交
1205 1206 1207
   if( vo.contains( "output" ) ) {
      const vector<variant>& outputs = vo["output"].get_array();
   }
1208 1209 1210 1211
   return result;
#undef GET_FIELD
}

1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
vector<char> chain_controller::message_to_binary( Name code, Name type, const fc::variant& obj )const {
   const auto& code_account = _db.get<account_object,by_name>( 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 );
      return abis.variantToBinary( abis.getActionType( type ), obj );
   }
   return vector<char>();
}
D
Daniel Larimer 已提交
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
fc::variant chain_controller::message_from_binary( Name code, Name type, const vector<char>& data )const {
   const auto& code_account = _db.get<account_object,by_name>( 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 );
      return abis.binaryToVariant( abis.getActionType( type ), data );
   }
   return fc::variant();
}
1234

D
Daniel Larimer 已提交
1235
fc::variant  chain_controller::transaction_to_variant( const ProcessedTransaction& trx )const {
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
#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 );
1253
       SET_FIELD( msg_mvo, msg, authorization );
1254 1255 1256

       const auto& code_account = _db.get<account_object,by_name>( msg.code );
       if( code_account.abi.size() > 4 ) { /// 4 == packsize of empty Abi
D
Daniel Larimer 已提交
1257
          try {
D
Daniel Larimer 已提交
1258
             msg_mvo( "data", message_from_binary( msg.code, msg.type, msg.data ) ); 
D
Daniel Larimer 已提交
1259 1260 1261 1262
             msg_mvo( "hex_data", msg.data );
          } catch ( ... ) {
            SET_FIELD( msg_mvo, msg, data );
          }
1263 1264 1265 1266 1267 1268 1269 1270
       }
       else {
         SET_FIELD( msg_mvo, msg, data );
       }
       msgsv[i] = std::move( msgs[i] );
    }
    trx_mvo( "messages", std::move(msgsv) );

D
Daniel Larimer 已提交
1271 1272 1273 1274 1275 1276 1277 1278 1279
    /* TODO: recursively process generated transactions 
    vector<fc::mutable_variant_object> outs( trx.messages.size() );
    for( uint32_t i = 0; i < trx.output.size(); ++i ) {
       auto& out_mvo = outs[i];
       auto& out = trx.outputs[i];
    }
    */
    trx_mvo( "output", fc::variant( trx.output ) );

1280 1281 1282 1283
    return fc::variant( std::move( trx_mvo ) );
#undef SET_FIELD
}

1284

N
Nathan Hourt 已提交
1285
} }