controller.cpp 44.1 KB
Newer Older
D
Daniel Larimer 已提交
1
#include <eosio/chain/controller.hpp>
2
#include <eosio/chain/transaction_context.hpp>
D
Daniel Larimer 已提交
3 4 5 6 7 8 9 10 11 12 13 14

#include <eosio/chain/block_log.hpp>
#include <eosio/chain/fork_database.hpp>

#include <eosio/chain/account_object.hpp>
#include <eosio/chain/scope_sequence_object.hpp>
#include <eosio/chain/block_summary_object.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/contract_table_objects.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
#include <eosio/chain/transaction_object.hpp>

15
#include <eosio/chain/authorization_manager.hpp>
D
Daniel Larimer 已提交
16 17
#include <eosio/chain/resource_limits.hpp>

D
Daniel Larimer 已提交
18
#include <chainbase/chainbase.hpp>
D
Daniel Larimer 已提交
19 20
#include <fc/io/json.hpp>

D
Daniel Larimer 已提交
21 22
#include <eosio/chain/eosio_contract.hpp>

D
Daniel Larimer 已提交
23 24 25 26
namespace eosio { namespace chain {

using resource_limits::resource_limits_manager;

D
Daniel Larimer 已提交
27

D
Daniel Larimer 已提交
28 29 30 31 32 33 34 35
struct pending_state {
   pending_state( database::session&& s )
   :_db_session( move(s) ){}

   database::session                  _db_session;

   block_state_ptr                    _pending_block_state;

D
Daniel Larimer 已提交
36
   vector<action_receipt>             _actions;
D
Daniel Larimer 已提交
37

D
Daniel Larimer 已提交
38

D
Daniel Larimer 已提交
39 40 41 42 43 44
   void push() {
      _db_session.push();
   }
};

struct controller_impl {
D
Daniel Larimer 已提交
45
   controller&                    self;
D
Daniel Larimer 已提交
46 47 48
   chainbase::database            db;
   block_log                      blog;
   optional<pending_state>        pending;
49 50
   block_state_ptr                head;
   fork_database                  fork_db;
D
Daniel Larimer 已提交
51
   wasm_interface                 wasmif;
D
Daniel Larimer 已提交
52
   resource_limits_manager        resource_limits;
53
   authorization_manager          authorization;
D
Daniel Larimer 已提交
54
   controller::config             conf;
D
Daniel Larimer 已提交
55
   bool                           replaying = false;
D
Daniel Larimer 已提交
56

D
Daniel Larimer 已提交
57 58 59
   typedef pair<scope_name,action_name>                   handler_key;
   map< account_name, map<handler_key, apply_handler> >   apply_handlers;

D
Daniel Larimer 已提交
60 61 62 63 64
   /**
    *  Transactions that were undone by pop_block or abort_block, transactions
    *  are removed from this list if they are re-applied in other blocks. Producers
    *  can query this list when scheduling new transactions into blocks.
    */
65
   map<digest_type, transaction_metadata_ptr>     unapplied_transactions;
D
Daniel Larimer 已提交
66

D
Daniel Larimer 已提交
67 68 69 70 71 72 73 74 75 76 77
   block_id_type head_block_id()const {
      return head->id;
   }
   time_point head_block_time()const {
      return head->header.timestamp;
   }
   const block_header& head_block_header()const {
      return head->header;
   }

   void pop_block() {
78 79
      auto prev = fork_db.get_block( head->header.previous );
      FC_ASSERT( prev, "attempt to pop beyond last irreversible block" );
D
Daniel Larimer 已提交
80
      for( const auto& t : head->trxs )
81
         unapplied_transactions[t->signed_id] = t;
82
      head = prev;
D
Daniel Larimer 已提交
83
      db.undo();
D
Daniel Larimer 已提交
84 85 86
   }


D
Daniel Larimer 已提交
87 88 89 90
   void set_apply_handler( account_name contract, scope_name scope, action_name action, apply_handler v ) {
      apply_handlers[contract][make_pair(scope,action)] = v;
   }

D
Daniel Larimer 已提交
91
   controller_impl( const controller::config& cfg, controller& s  )
D
Daniel Larimer 已提交
92 93
   :self(s),
    db( cfg.shared_memory_dir,
D
Daniel Larimer 已提交
94 95 96
        cfg.read_only ? database::read_only : database::read_write,
        cfg.shared_memory_size ),
    blog( cfg.block_log_dir ),
D
Daniel Larimer 已提交
97
    fork_db( cfg.shared_memory_dir ),
D
Daniel Larimer 已提交
98
    wasmif( cfg.wasm_runtime ),
D
Daniel Larimer 已提交
99
    resource_limits( db ),
D
Daniel Larimer 已提交
100 101
    authorization( s, db ),
    conf( cfg )
D
Daniel Larimer 已提交
102
   {
D
Daniel Larimer 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

#define SET_APP_HANDLER( contract, scope, action, nspace ) \
   set_apply_handler( #contract, #scope, #action, &BOOST_PP_CAT(apply_, BOOST_PP_CAT(contract, BOOST_PP_CAT(_,action) ) ) )
   SET_APP_HANDLER( eosio, eosio, newaccount, eosio );
   SET_APP_HANDLER( eosio, eosio, setcode, eosio );
   SET_APP_HANDLER( eosio, eosio, setabi, eosio );
   SET_APP_HANDLER( eosio, eosio, updateauth, eosio );
   SET_APP_HANDLER( eosio, eosio, deleteauth, eosio );
   SET_APP_HANDLER( eosio, eosio, linkauth, eosio );
   SET_APP_HANDLER( eosio, eosio, unlinkauth, eosio );
   SET_APP_HANDLER( eosio, eosio, onerror, eosio );
   SET_APP_HANDLER( eosio, eosio, postrecovery, eosio );
   SET_APP_HANDLER( eosio, eosio, passrecovery, eosio );
   SET_APP_HANDLER( eosio, eosio, vetorecovery, eosio );
   SET_APP_HANDLER( eosio, eosio, canceldelay, eosio );


D
Daniel Larimer 已提交
120 121 122 123
   }

   void init() {
      // ilog( "${c}", ("c",fc::json::to_pretty_string(cfg)) );
124
      add_indices();
D
Daniel Larimer 已提交
125 126

      /**
127 128 129 130 131 132 133 134 135 136 137 138 139 140
      *  The fork database needs an initial block_state to be set before
      *  it can accept any new blocks. This initial block state can be found
      *  in the database (whose head block state should be irreversible) or
      *  it would be the genesis state.
      */
      if( !head ) {
         initialize_fork_db(); // set head to genesis state
      }

      FC_ASSERT( db.revision() == head->block_num, "fork database is inconsistent with shared memory",
                 ("db",db.revision())("head",head->block_num) );

      /**
       * The undoable state contains state transitions from blocks
D
Daniel Larimer 已提交
141 142 143 144 145
       * in the fork database that could be reversed. Because this
       * is a new startup and the fork database is empty, we must
       * unwind that pending state. This state will be regenerated
       * when we catch up to the head block later.
       */
146
      //clear_all_undo();
D
Daniel Larimer 已提交
147 148 149 150
   }

   ~controller_impl() {
      pending.reset();
D
Daniel Larimer 已提交
151 152 153

      edump((db.revision())(head->block_num));

D
Daniel Larimer 已提交
154 155 156
      db.flush();
   }

157
   void add_indices() {
D
Daniel Larimer 已提交
158
      db.add_index<account_index>();
159
      db.add_index<account_sequence_index>();
D
Daniel Larimer 已提交
160

D
Daniel Larimer 已提交
161 162 163 164 165 166
      db.add_index<table_id_multi_index>();
      db.add_index<key_value_index>();
      db.add_index<index64_index>();
      db.add_index<index128_index>();
      db.add_index<index256_index>();
      db.add_index<index_double_index>();
D
Daniel Larimer 已提交
167 168 169 170 171 172 173 174

      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<generated_transaction_multi_index>();
      db.add_index<scope_sequence_multi_index>();

175 176
      authorization.add_indices();
      resource_limits.add_indices();
D
Daniel Larimer 已提交
177 178 179 180 181 182 183
   }

   void clear_all_undo() {
      // Rewind the database to the last irreversible block
      db.with_write_lock([&] {
         db.undo_all();
         /*
184
         FC_ASSERT(db.revision() == self.head_block_num(),
D
Daniel Larimer 已提交
185 186 187 188 189 190 191
                   "Chainbase revision does not match head block num",
                   ("rev", db.revision())("head_block", self.head_block_num()));
                   */
      });
   }

   /**
192
    *  Sets fork database head to the genesis state.
D
Daniel Larimer 已提交
193 194
    */
   void initialize_fork_db() {
195 196
      wlog( " Initializing new blockchain with genesis state                  " );
      producer_schedule_type initial_schedule{ 0, {{N(eosio), conf.genesis.initial_key}} };
D
Daniel Larimer 已提交
197

198 199 200 201 202 203 204 205
      block_header_state genheader;
      genheader.active_schedule       = initial_schedule;
      genheader.pending_schedule      = initial_schedule;
      genheader.pending_schedule_hash = fc::sha256::hash(initial_schedule);
      genheader.header.timestamp      = conf.genesis.initial_timestamp;
      genheader.header.action_mroot   = conf.genesis.compute_chain_id();
      genheader.id                    = genheader.header.id();
      genheader.block_num             = genheader.header.block_num();
D
Daniel Larimer 已提交
206

207
      head = std::make_shared<block_state>( genheader );
D
Daniel Larimer 已提交
208
      head->block = std::make_shared<signed_block>(genheader.header);
209
      fork_db.set( head );
D
Daniel Larimer 已提交
210 211 212
      db.set_revision( head->block_num );

      initialize_database();
A
arhag 已提交
213

D
Daniel Larimer 已提交
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
      auto end = blog.read_head();
      if( end && end->block_num() > 1 ) {
         replaying = true;
         ilog( "existing block log, attempting to replay ${n} blocks", ("n",end->block_num()) );

         auto start = fc::time_point::now();
         while( auto next = blog.read_block_by_num( head->block_num + 1 ) ) {
            self.push_block( next );
            if( next->block_num() % 10 == 0 ) {
               std::cerr << std::setw(10) << next->block_num() << " of " << end->block_num() <<"\r";
            }
         }
         std::cerr<< "\n";
         auto end = fc::time_point::now();
         ilog( "replayed blocks in ${n} seconds", ("n", (end-start).count()/1000000.0) );
         replaying = false;

      } else if( !end ) {
         blog.append( head->block );
      }
D
Daniel Larimer 已提交
234 235
   }

236
   void create_native_account( account_name name, const authority& owner, const authority& active, bool is_privileged = false ) {
237
      db.create<account_object>([&](auto& a) {
D
Daniel Larimer 已提交
238 239
         a.name = name;
         a.creation_date = conf.genesis.initial_timestamp;
240
         a.privileged = is_privileged;
D
Daniel Larimer 已提交
241 242

         if( name == config::system_account_name ) {
D
Daniel Larimer 已提交
243
            a.set_abi(eosio_contract_abi(abi_def()));
D
Daniel Larimer 已提交
244 245
         }
      });
246 247 248 249
      db.create<account_sequence_object>([&](auto & a) {
        a.name = name;
      });

250 251 252 253
      const auto& owner_permission  = authorization.create_permission(name, config::owner_name, 0,
                                                                      owner, conf.genesis.initial_timestamp );
      const auto& active_permission = authorization.create_permission(name, config::active_name, owner_permission.id,
                                                                      active, conf.genesis.initial_timestamp );
D
Daniel Larimer 已提交
254 255

      resource_limits.initialize_account(name);
256 257 258 259 260 261 262 263
      resource_limits.add_pending_account_ram_usage(
         name,
         (int64_t)(config::billable_size_v<permission_object> + owner_permission.auth.get_billable_size())
      );
      resource_limits.add_pending_account_ram_usage(
         name,
         (int64_t)(config::billable_size_v<permission_object> + active_permission.auth.get_billable_size())
      );
D
Daniel Larimer 已提交
264 265 266 267 268 269 270
   }

   void initialize_database() {
      // Initialize block summary index
      for (int i = 0; i < 0x10000; i++)
         db.create<block_summary_object>([&](block_summary_object&) {});

271 272 273 274 275
      const auto& tapos_block_summary = db.get<block_summary_object>(1);
      db.modify( tapos_block_summary, [&]( auto& bs ) {
        bs.block_id = head->id;
      });

276 277 278 279
      db.create<global_property_object>([&](auto& gpo ){
        gpo.configuration = conf.genesis.initial_configuration;
      });
      db.create<dynamic_global_property_object>([](auto&){});
280 281 282 283 284 285

      authorization.initialize_database();
      resource_limits.initialize_database();

      authority system_auth(conf.genesis.initial_key);
      create_native_account( config::system_account_name, system_auth, system_auth, true );
D
Daniel Larimer 已提交
286 287 288 289 290

      auto empty_authority = authority(0, {}, {});
      auto active_producers_authority = authority(0, {}, {});
      active_producers_authority.accounts.push_back({{config::system_account_name, config::active_name}, 1});

291 292
      create_native_account( config::nobody_account_name, empty_authority, empty_authority );
      create_native_account( config::producers_account_name, empty_authority, active_producers_authority );
D
Daniel Larimer 已提交
293 294
   }

295 296 297 298 299 300 301
   void set_pending_tapos() {
      const auto& tapos_block_summary = db.get<block_summary_object>((uint16_t)pending->_pending_block_state->block_num);
      db.modify( tapos_block_summary, [&]( auto& bs ) {
        bs.block_id = pending->_pending_block_state->id;
      });
   }

D
Daniel Larimer 已提交
302
   void commit_block( bool add_to_fork_db ) {
303 304 305 306
      set_pending_tapos();
      resource_limits.process_account_limit_updates();
      resource_limits.process_block_usage( pending->_pending_block_state->block_num );

D
Daniel Larimer 已提交
307 308
      if( add_to_fork_db ) {
         pending->_pending_block_state->validated = true;
309 310 311
         auto new_bsp = fork_db.add( pending->_pending_block_state );
         head = fork_db.head();
         FC_ASSERT( new_bsp == head, "committed block did not become the new head in fork database" );
312
      }
313

314
      //ilog((fc::json::to_pretty_string(*pending->_pending_block_state->block)));
D
Daniel Larimer 已提交
315
      self.accepted_block( pending->_pending_block_state );
D
Daniel Larimer 已提交
316 317
      pending->push();
      pending.reset();
D
Daniel Larimer 已提交
318 319 320 321

      if( !replaying ) {
         self.log_irreversible_blocks();
      }
D
Daniel Larimer 已提交
322
   }
D
Daniel Larimer 已提交
323

324
   transaction_trace_ptr apply_onerror( const generated_transaction_object& gto, fc::time_point deadline, uint32_t cpu_usage ) {
D
Daniel Larimer 已提交
325 326 327
      signed_transaction etrx;
      etrx.actions.emplace_back(vector<permission_level>{{gto.sender,config::active_name}},
                                onerror( gto.sender_id, gto.packed_trx.data(), gto.packed_trx.size()) );
D
Daniel Larimer 已提交
328

D
Daniel Larimer 已提交
329 330 331 332 333 334 335 336 337 338

      etrx.expiration = self.pending_block_time() + fc::seconds(1);
      etrx.set_reference_block( self.head_block_id() );

      transaction_context trx_context( self, etrx, etrx.id() );
      trx_context.deadline = deadline;
      /// TODO: forward cpu usage into error trx_context.cpu_usage = cpu_usage;
      trx_context.is_input  = false;
      trx_context.exec();

339
      self.applied_transaction(trx_context.trace);
D
Daniel Larimer 已提交
340 341 342
      trx_context.squash();

      return move(trx_context.trace);
D
Daniel Larimer 已提交
343 344
   }

D
Daniel Larimer 已提交
345 346 347
   void expire_scheduled_transaction( const generated_transaction_object& gto ) {
      auto receipt = push_receipt( gto.trx_id, transaction_receipt::expired, 0, 0 );

348
      resource_limits.add_pending_account_ram_usage(gto.payer,
D
Daniel Larimer 已提交
349 350 351 352 353
               -(config::billable_size_v<generated_transaction_object> + gto.packed_trx.size()));

      db.remove( gto );
   }

D
Daniel Larimer 已提交
354
   void push_scheduled_transaction( const generated_transaction_object& gto, fc::time_point deadline  ) {
D
Daniel Larimer 已提交
355
      fc::datastream<const char*> ds( gto.packed_trx.data(), gto.packed_trx.size() );
D
Daniel Larimer 已提交
356

357
      FC_ASSERT( gto.delay_until <= self.pending_block_time(), "this transaction isn't ready", ("gto.delay_until",gto.delay_until)("pbt",self.pending_block_time()) );
D
Daniel Larimer 已提交
358 359 360 361
      if( gto.expiration <= self.pending_block_time() ) {
         expire_scheduled_transaction( gto );
         return;
      }
362 363 364

      optional<fc::exception> soft_except;
      optional<fc::exception> hard_except;
D
Daniel Larimer 已提交
365 366
      std::exception_ptr soft_except_ptr;
      std::exception_ptr hard_except_ptr;
367

368 369
      auto sender = gto.sender;

D
Daniel Larimer 已提交
370
      transaction_trace_ptr trace;
371
      uint32_t apply_cpu_usage = 0;
D
Daniel Larimer 已提交
372 373 374
      try {
         signed_transaction dtrx;
         fc::raw::unpack(ds,static_cast<transaction&>(dtrx) );
375 376

         transaction_context trx_context( self, dtrx, gto.trx_id );
D
Daniel Larimer 已提交
377
         trace = trx_context.trace;
378

379
         trx_context.trace->scheduled = true;
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
         trx_context.deadline  = deadline;
         trx_context.published = gto.published;
         trx_context.net_usage = 0;
         trx_context.apply_context_free = false;
         trx_context.is_input           = false;
         try {
            trx_context.exec();
         } catch ( ... ) {
            apply_cpu_usage = trx_context.trace->cpu_usage;
            throw;
         }

         fc::move_append( pending->_actions, move(trx_context.executed) );

         trx_context.trace->receipt = push_receipt( gto.trx_id, transaction_receipt::executed, trx_context.trace->kcpu_usage(), 0 );
395 396

         db.remove( gto );
397

D
Daniel Larimer 已提交
398
         self.applied_transaction( trx_context.trace );
399
         trx_context.squash();
400 401

         return;
D
Daniel Larimer 已提交
402
      } catch( const fc::exception& e ) {
403
         soft_except = e;
D
Daniel Larimer 已提交
404
         soft_except_ptr = std::current_exception();
D
Daniel Larimer 已提交
405
      }
406
      if( soft_except && sender != account_name() ) { /// TODO: soft errors should not go to error handlers (deadline error)
407 408 409 410
         edump((soft_except->to_detail_string()));
         try {
            auto trace = apply_onerror( gto, deadline, apply_cpu_usage  );
            trace->soft_except = soft_except;
D
Daniel Larimer 已提交
411
            self.applied_transaction( trace );
412 413
         } catch ( const fc::exception& e ) {
            hard_except = e;
D
Daniel Larimer 已提交
414
            trace->hard_except_ptr = std::current_exception();
415
         }
D
Daniel Larimer 已提交
416
      }
417

418
      /*
419
      if( hard_except )
420
         edump((hard_except->to_detail_string()));
421
      if( soft_except )
422 423 424
         edump((soft_except->to_detail_string()));
       */

425
      FC_ASSERT( bool(trace), "failed to deserialize transaction" );
426
      trace->receipt  = push_receipt( gto.trx_id, transaction_receipt::hard_fail, (apply_cpu_usage+1023)/1024, 0 );
D
Daniel Larimer 已提交
427 428
      trace->soft_except = soft_except;
      trace->hard_except = hard_except;
D
Daniel Larimer 已提交
429 430
      trace->soft_except_ptr = soft_except_ptr;
      trace->hard_except_ptr = hard_except_ptr;
431 432 433

      db.remove( gto );

D
Daniel Larimer 已提交
434
      self.applied_transaction( trace );
D
Daniel Larimer 已提交
435
   } /// push_scheduled_transaction
D
Daniel Larimer 已提交
436

437 438 439 440

   /**
    *  Adds the transaction receipt to the pending block and returns it.
    */
441 442
   template<typename T>
   const transaction_receipt& push_receipt( const T& trx, transaction_receipt_header::status_enum status,
443 444 445 446 447 448 449 450 451
                      uint32_t kcpu_usage, uint32_t net_usage_words ) {
      pending->_pending_block_state->block->transactions.emplace_back( trx );
      transaction_receipt& r = pending->_pending_block_state->block->transactions.back();
      r.kcpu_usage           = kcpu_usage;
      r.net_usage_words      = net_usage_words;
      r.status               = status;
      return r;
   }

452
   bool push_next_unapplied_transaction( fc::time_point deadline ) {
D
Daniel Larimer 已提交
453
      auto itr = unapplied_transactions.begin();
454
      if( itr == unapplied_transactions.end() )
455
         return false;
D
Daniel Larimer 已提交
456

457 458 459
      // Intentionally copy transaction_metadata_ptr because it will be removed from unapplied_transactions and make the const& dangling.
      push_transaction( transaction_metadata_ptr(itr->second), deadline );
      return true;
D
Daniel Larimer 已提交
460 461
   }

462 463
   void transaction_trace_notify( const transaction_metadata_ptr& trx, const transaction_trace_ptr& trace ) {
      if( trx->on_result ) {
464 465
         (trx->on_result)(trace);
         trx->on_result = decltype(trx->on_result)(); //assign empty std::function
466 467
      }
   }
468

469 470 471 472 473
   /**
    *  This is the entry point for new transactions to the block state. It will check authorization and
    *  determine whether to execute it now or to delay it. Lastly it inserts a transaction receipt into
    *  the pending block.
    */
474 475
   void push_transaction( const transaction_metadata_ptr& trx,
                          fc::time_point deadline = fc::time_point::maximum(),
D
Daniel Larimer 已提交
476
                          bool implicit = false ) {
D
Daniel Larimer 已提交
477 478 479
      //if( !implicit )
      //   idump((fc::json::to_pretty_string(trx->trx)));

D
Daniel Larimer 已提交
480
      if( deadline == fc::time_point() ) {
481
         unapplied_transactions[trx->signed_id] = trx;
D
Daniel Larimer 已提交
482 483
         return;
      }
484

485
      transaction_trace_ptr trace;
D
Daniel Larimer 已提交
486 487
      try {
         unapplied_transactions.erase( trx->signed_id );
488

D
Daniel Larimer 已提交
489
         transaction_context trx_context( self, trx->trx, trx->id );
490
         trace = trx_context.trace;
491

A
arhag 已提交
492
         auto required_delay = limit_delay( authorization.check_authorization( trx->trx.actions, trx->recover_keys() ) );
493 494 495 496
         trx_context.delay = fc::seconds(trx->trx.delay_sec);
         EOS_ASSERT( trx_context.delay >= required_delay, transaction_exception,
                     "authorization imposes a delay (${required_delay} sec) greater than the delay specified in transaction header (${specified_delay} sec)",
                     ("required_delay", required_delay.to_seconds())("specified_delay", trx_context.delay.to_seconds()) );
497

D
Daniel Larimer 已提交
498 499
         trx_context.deadline  = deadline;
         trx_context.published = self.pending_block_time();
500
         trx_context.net_usage = self.validate_net_usage( trx ); // / 8; // <-- BUG? Needed to be removed to fix auth_tests/no_double_billing
D
Daniel Larimer 已提交
501 502
         trx_context.is_input  = !implicit;
         trx_context.exec();
503

D
Daniel Larimer 已提交
504 505 506 507 508 509 510 511
         fc::move_append( pending->_actions, move(trx_context.executed) );

         if( !implicit ) {
            if( trx_context.delay == fc::seconds(0) ) {
               trace->receipt = push_receipt( trx->packed_trx, transaction_receipt::executed, trace->kcpu_usage(), trx_context.net_usage );
            } else {
               trace->receipt = push_receipt( trx->packed_trx, transaction_receipt::delayed, trace->kcpu_usage(), trx_context.net_usage );
            }
512
         }
513

514 515 516 517 518
         transaction_trace_notify(trx, trace);

         if( !implicit )
            pending->_pending_block_state->trxs.emplace_back(trx);

D
Daniel Larimer 已提交
519
         self.accepted_transaction(trx);
520
         self.applied_transaction(trace);
D
Daniel Larimer 已提交
521
         trx_context.squash();
522
         return;
D
Daniel Larimer 已提交
523
      } catch ( const fc::exception& e ) {
524
         trace->soft_except = e;
D
Daniel Larimer 已提交
525
         trace->hard_except_ptr = std::current_exception();
526 527
         //wlog( "caught exception in push_transaction" );
         //wdump((trace));
D
Daniel Larimer 已提交
528
      }
529
      transaction_trace_notify(trx, trace);
530

531
   } /// push_transaction
D
Daniel Larimer 已提交
532

533

534
   void start_block( block_timestamp_type when ) {
535
      FC_ASSERT( !pending );
D
Daniel Larimer 已提交
536

537
      FC_ASSERT( db.revision() == head->block_num, "",
A
arhag 已提交
538
                ("db_head_block", db.revision())("controller_head_block", head->block_num)("fork_db_head_block", fork_db.head()->block_num) );
D
Daniel Larimer 已提交
539

540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
      pending = db.start_undo_session(true);
      pending->_pending_block_state = std::make_shared<block_state>( *head, when ); // promotes pending schedule (if any) to active
      pending->_pending_block_state->in_current_chain = true;

      const auto& gpo = db.get<global_property_object>();
      if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ...
          ( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_last_irreversible_blocknum ) && // ... that has now become irreversible ...
          pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ...
          head->pending_schedule.producers.size() == 0 // ... and not just because it was promoted to active at the start of this block, then:
        )
      {
         // Promote proposed schedule to pending schedule.
         ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ",
               ("proposed_num", *gpo.proposed_schedule_block_num)("n", pending->_pending_block_state->block_num)
               ("lib", pending->_pending_block_state->dpos_last_irreversible_blocknum)
               ("schedule", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) );
         pending->_pending_block_state->set_new_producers( gpo.proposed_schedule );
         db.modify( gpo, [&]( auto& gp ) {
            gp.proposed_schedule_block_num = optional<block_num_type>();
            gp.proposed_schedule.clear();
         });
      }
D
Daniel Larimer 已提交
562

563 564 565 566 567 568
      try {
         auto onbtrx = std::make_shared<transaction_metadata>( get_on_block_transaction() );
         push_transaction( onbtrx, fc::time_point::maximum(), true );
      } catch ( ... ) {
         ilog( "on block transaction failed, but shouldn't impact block generation, system contract needs update" );
      }
569
   } // start_block
D
Daniel Larimer 已提交
570 571


D
Daniel Larimer 已提交
572

573 574
   void sign_block( const std::function<signature_type( const digest_type& )>& signer_callback ) {
      auto p = pending->_pending_block_state;
575
      p->sign( signer_callback );
576 577
      static_cast<signed_block_header&>(*p->block) = p->header;
   } /// sign_block
D
Daniel Larimer 已提交
578

579
   void apply_block( const signed_block_ptr& b ) { try {
580 581 582 583 584 585 586 587 588
      try {
         start_block( b->timestamp );

         for( const auto& receipt : b->transactions ) {
            if( receipt.trx.contains<packed_transaction>() ) {
               auto& pt = receipt.trx.get<packed_transaction>();
               auto mtrx = std::make_shared<transaction_metadata>(pt);
               push_transaction( mtrx );
            }
D
Daniel Larimer 已提交
589 590 591
            else if( receipt.trx.contains<transaction_id_type>() ) {
               self.push_scheduled_transaction( receipt.trx.get<transaction_id_type>() );
            }
592
         }
D
Daniel Larimer 已提交
593

594 595
         finalize_block();
         sign_block( [&]( const auto& ){ return b->producer_signature; } );
D
Daniel Larimer 已提交
596

597 598 599
         // this is implied by the signature passing
         //FC_ASSERT( b->id() == pending->_pending_block_state->block->id(),
         //           "applying block didn't produce expected block id" );
D
Daniel Larimer 已提交
600

601 602
         commit_block(false);
         return;
D
Daniel Larimer 已提交
603
      } catch ( const fc::exception& e ) {
604 605
         edump((e.to_detail_string()));
         abort_block();
D
Daniel Larimer 已提交
606 607
         throw;
      }
608
   } FC_CAPTURE_AND_RETHROW() } /// apply_block
609 610


611
   void push_block( const signed_block_ptr& b ) {
D
Daniel Larimer 已提交
612 613 614 615
      try {
         auto new_header_state = fork_db.add( b );
         self.accepted_block_header( new_header_state );
         maybe_switch_forks();
A
arhag 已提交
616
      } FC_LOG_AND_RETHROW()
617
   }
618

619 620 621 622 623 624 625
   void push_confirmation( const header_confirmation& c ) {
      fork_db.add( c );
      self.accepted_confirmation( c );
      maybe_switch_forks();
   }

   void maybe_switch_forks() {
626 627 628 629
      auto new_head = fork_db.head();

      if( new_head->header.previous == head->id ) {
         try {
630
            abort_block();
631
            apply_block( new_head->block );
632
            fork_db.mark_in_current_chain( new_head, true );
633 634 635
            fork_db.set_validity( new_head, true );
            head = new_head;
         } catch ( const fc::exception& e ) {
636
            fork_db.set_validity( new_head, false ); // Removes new_head from fork_db index, so no need to mark it as not in the current chain.
637 638
            throw;
         }
639
      } else if( new_head->id != head->id ) {
640
         ilog("switching forks from ${current_head_id} (block number ${current_head_num}) to ${new_head_id} (block number ${new_head_num})",
A
arhag 已提交
641
              ("current_head_id", head->id)("current_head_num", head->block_num)("new_head_id", new_head->id)("new_head_num", new_head->block_num) );
642 643
         auto branches = fork_db.fetch_branch_from( new_head->id, head->id );

644 645
         for( auto itr = branches.second.begin(); itr != branches.second.end(); ++itr ) {
            fork_db.mark_in_current_chain( *itr , false );
646
            pop_block();
647 648 649
         }
         FC_ASSERT( head_block_id() == branches.second.back()->header.previous,
                    "loss of sync between fork_db and chainbase during fork switch" ); // _should_ never fail
650 651 652 653 654

         for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr) {
            optional<fc::exception> except;
            try {
               apply_block( (*ritr)->block );
655
               head = *ritr;
656
               fork_db.mark_in_current_chain( *ritr, true );
657 658 659
            }
            catch (const fc::exception& e) { except = e; }
            if (except) {
660
               elog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
661

662 663 664 665
               while (ritr != branches.first.rend() ) {
                  fork_db.set_validity( *ritr, false );
                  ++ritr;
               }
666

667
               // pop all blocks from the bad fork
668 669
               for( auto itr = (ritr + 1).base(); itr != branches.second.end(); ++itr ) {
                  fork_db.mark_in_current_chain( *itr , false );
670
                  pop_block();
671 672 673
               }
               FC_ASSERT( head_block_id() == branches.second.back()->header.previous,
                          "loss of sync between fork_db and chainbase during fork switch reversal" ); // _should_ never fail
674

675 676 677
               // re-apply good blocks
               for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr ) {
                  apply_block( (*ritr)->block );
678
                  head = *ritr;
679
                  fork_db.mark_in_current_chain( *ritr, true );
680 681 682 683
               }
               throw *except;
            } // end if exception
         } /// end for each block in branch
684
         ilog("successfully switched fork to new head ${new_head_id}", ("new_head_id", new_head->id));
685 686 687
      }
   } /// push_block

688
   void abort_block() {
689
      if( pending ) {
690
         for( const auto& t : pending->_pending_block_state->trxs )
691
            unapplied_transactions[t->signed_id] = t;
692 693
         pending.reset();
      }
694 695
   }

D
Daniel Larimer 已提交
696 697 698 699 700

   bool should_enforce_runtime_limits()const {
      return false;
   }

D
Daniel Larimer 已提交
701 702 703 704 705 706 707 708
   void set_action_merkle() {
      vector<digest_type> action_digests;
      action_digests.reserve( pending->_actions.size() );
      for( const auto& a : pending->_actions )
         action_digests.emplace_back( a.digest() );

      pending->_pending_block_state->header.action_mroot = merkle( move(action_digests) );
   }
D
Daniel Larimer 已提交
709

D
Daniel Larimer 已提交
710 711
   void set_trx_merkle() {
      vector<digest_type> trx_digests;
D
Daniel Larimer 已提交
712 713 714
      const auto& trxs = pending->_pending_block_state->block->transactions;
      trx_digests.reserve( trxs.size() );
      for( const auto& a : trxs )
D
Daniel Larimer 已提交
715
         trx_digests.emplace_back( a.digest() );
D
Daniel Larimer 已提交
716

D
Daniel Larimer 已提交
717
      pending->_pending_block_state->header.transaction_mroot = merkle( move(trx_digests) );
D
Daniel Larimer 已提交
718 719 720
   }


721
   void finalize_block()
D
Daniel Larimer 已提交
722
   { try {
D
Daniel Larimer 已提交
723 724 725
      if( !pending ) self.start_block();

      /*
726
      ilog( "finalize block ${n} (${id}) at ${t} by ${p} (${signing_key}); schedule_version: ${v} lib: ${lib} #dtrxs: ${ndtrxs} ${np}",
A
arhag 已提交
727 728
            ("n",pending->_pending_block_state->block_num)
            ("id",pending->_pending_block_state->header.id())
729
            ("t",pending->_pending_block_state->header.timestamp)
A
arhag 已提交
730 731
            ("p",pending->_pending_block_state->header.producer)
            ("signing_key", pending->_pending_block_state->block_signing_key)
732 733
            ("v",pending->_pending_block_state->header.schedule_version)
            ("lib",pending->_pending_block_state->dpos_last_irreversible_blocknum)
734
            ("ndtrxs",db.get_index<generated_transaction_multi_index,by_trx_id>().size())
735 736
            ("np",pending->_pending_block_state->header.new_producers)
            );
737
      */
D
Daniel Larimer 已提交
738 739 740 741 742 743 744

      set_action_merkle();
      set_trx_merkle();

      auto p = pending->_pending_block_state;
      p->id = p->header.id();

D
Daniel Larimer 已提交
745
      create_block_summary();
D
Daniel Larimer 已提交
746

D
Daniel Larimer 已提交
747 748 749 750 751 752 753 754
      const auto& chain_config = self.get_global_properties().configuration;
      resource_limits.set_block_parameters(
         {EOS_PERCENT(chain_config.max_block_cpu_usage, chain_config.target_block_cpu_usage_pct), chain_config.max_block_cpu_usage, config::block_cpu_usage_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}},
         {EOS_PERCENT(chain_config.max_block_net_usage, chain_config.target_block_net_usage_pct), chain_config.max_block_net_usage, config::block_size_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}}
      );

   } FC_CAPTURE_AND_RETHROW() }

D
Daniel Larimer 已提交
755 756 757 758 759 760 761 762 763

   void create_block_summary() {
      auto p = pending->_pending_block_state;
      auto sid = p->block_num & 0xffff;
      db.modify( db.get<block_summary_object,by_id>(sid), [&](block_summary_object& bso ) {
          bso.block_id = p->id;
      });
   }

D
Daniel Larimer 已提交
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
   void clear_expired_transactions() {
      //Look for expired transactions in the deduplication list, and remove them.
      auto& transaction_idx = db.get_mutable_index<transaction_multi_index>();
      const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
      while( (!dedupe_index.empty()) && (head_block_time() > fc::time_point(dedupe_index.begin()->expiration) ) ) {
         transaction_idx.remove(*dedupe_index.begin());
      }

      // Look for expired transactions in the pending generated list, and remove them.
      // TODO: expire these by sending error to handler
      auto& generated_transaction_idx = db.get_mutable_index<generated_transaction_multi_index>();
      const auto& generated_index = generated_transaction_idx.indices().get<by_expiration>();
      while( (!generated_index.empty()) && (head_block_time() > generated_index.begin()->expiration) ) {
      // TODO:   destroy_generated_transaction(*generated_index.begin());
      }
   }

A
arhag 已提交
781 782 783 784 785 786 787
   fc::microseconds limit_delay( fc::microseconds delay )const {
      auto max_delay = fc::seconds( self.get_global_properties().configuration.max_transaction_delay );
      //return std::min(delay, max_delay); // for some reason this currently breaks block verification
      //QUESTION: Do we actually want the max_delay limiting the (potentially larger) delays on existing permission authorities?
      return delay;
   }

D
Daniel Larimer 已提交
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
   /*
   bool should_check_tapos()const { return true; }

   void validate_tapos( const transaction& trx )const {
      if( !should_check_tapos() ) return;

      const auto& tapos_block_summary = db.get<block_summary_object>((uint16_t)trx.ref_block_num);

      //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
      EOS_ASSERT(trx.verify_reference_block(tapos_block_summary.block_id), invalid_ref_block_exception,
                 "Transaction's reference block did not match. Is this transaction from a different fork?",
                 ("tapos_summary", tapos_block_summary));
   }
   */


   /**
    *  At the start of each block we notify the system contract with a transaction that passes in
    *  the block header of the prior block (which is currently our head block)
    */
D
Daniel Larimer 已提交
808
   signed_transaction get_on_block_transaction()
D
Daniel Larimer 已提交
809 810 811 812 813 814 815
   {
      action on_block_act;
      on_block_act.account = config::system_account_name;
      on_block_act.name = N(onblock);
      on_block_act.authorization = vector<permission_level>{{config::system_account_name, config::active_name}};
      on_block_act.data = fc::raw::pack(head_block_header());

D
Daniel Larimer 已提交
816
      signed_transaction trx;
D
Daniel Larimer 已提交
817 818 819 820 821 822
      trx.actions.emplace_back(std::move(on_block_act));
      trx.set_reference_block(head_block_id());
      trx.expiration = head_block_time() + fc::seconds(1);
      return trx;
   }

823
}; /// controller_impl
D
Daniel Larimer 已提交
824

825
const resource_limits_manager&   controller::get_resource_limits_manager()const
D
Daniel Larimer 已提交
826 827 828
{
   return my->resource_limits;
}
829
resource_limits_manager&         controller::get_mutable_resource_limits_manager()
D
Daniel Larimer 已提交
830 831 832
{
   return my->resource_limits;
}
D
Daniel Larimer 已提交
833

834 835 836 837 838 839 840 841
const authorization_manager&   controller::get_authorization_manager()const
{
   return my->authorization;
}
authorization_manager&         controller::get_mutable_authorization_manager()
{
   return my->authorization;
}
D
Daniel Larimer 已提交
842 843 844 845 846 847 848 849 850 851 852

controller::controller( const controller::config& cfg )
:my( new controller_impl( cfg, *this ) )
{
}

controller::~controller() {
}


void controller::startup() {
853
   my->init();
D
Daniel Larimer 已提交
854 855 856 857

   /*
   my->head = my->fork_db.head();
   if( !my->head ) {
D
Daniel Larimer 已提交
858
      elog( "No head block in fork db, perhaps we need to replay" );
D
Daniel Larimer 已提交
859 860 861 862
   }
   */
}

D
Daniel Larimer 已提交
863
chainbase::database& controller::db()const { return my->db; }
D
Daniel Larimer 已提交
864 865 866


void controller::start_block( block_timestamp_type when ) {
867
   my->start_block(when);
D
Daniel Larimer 已提交
868 869 870
}

void controller::finalize_block() {
D
Daniel Larimer 已提交
871
   my->finalize_block();
D
Daniel Larimer 已提交
872 873
}

874 875
void controller::sign_block( const std::function<signature_type( const digest_type& )>& signer_callback ) {
   my->sign_block( signer_callback );
D
Daniel Larimer 已提交
876 877 878
}

void controller::commit_block() {
D
Daniel Larimer 已提交
879
   my->commit_block(true);
D
Daniel Larimer 已提交
880 881
}

D
Daniel Larimer 已提交
882 883 884
block_state_ptr controller::head_block_state()const {
   return my->head;
}
885

D
Daniel Larimer 已提交
886 887 888 889
block_state_ptr controller::pending_block_state()const {
   if( my->pending ) return my->pending->_pending_block_state;
   return block_state_ptr();
}
D
Daniel Larimer 已提交
890

891 892
void controller::abort_block() {
   my->abort_block();
D
Daniel Larimer 已提交
893 894
}

D
Daniel Larimer 已提交
895
void controller::push_block( const signed_block_ptr& b ) {
896
   my->push_block( b );
D
Daniel Larimer 已提交
897 898 899
   if( !my->replaying ) {
      log_irreversible_blocks();
   }
D
Daniel Larimer 已提交
900 901
}

902 903 904 905
void controller::push_confirmation( const header_confirmation& c ) {
   my->push_confirmation( c );
}

D
Daniel Larimer 已提交
906 907 908
void controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline ) {
   my->push_transaction(trx, deadline);
}
909 910 911

bool controller::push_next_unapplied_transaction( fc::time_point deadline ) {
   return my->push_next_unapplied_transaction( deadline );
D
Daniel Larimer 已提交
912 913
}

D
Daniel Larimer 已提交
914 915
transaction_trace_ptr controller::sync_push( const transaction_metadata_ptr& trx, time_point deadline ) {
   FC_ASSERT( deadline != fc::time_point() );
916
   transaction_trace_ptr trace;
917
   trx->on_result = [&]( const transaction_trace_ptr& t ){ trace = t; };
D
Daniel Larimer 已提交
918
   my->push_transaction( trx, deadline );
919
   return trace;
D
Daniel Larimer 已提交
920
}
D
Daniel Larimer 已提交
921

922
bool controller::push_next_scheduled_transaction( fc::time_point deadline ) {
D
Daniel Larimer 已提交
923
   const auto& idx = db().get_index<generated_transaction_multi_index,by_delay>();
924 925 926 927 928 929
   auto itr = idx.begin();
   if( itr != idx.end() && itr->delay_until <= pending_block_time() ) {
      my->push_scheduled_transaction( *itr, deadline );
      return true;
   }
   return false;
D
Daniel Larimer 已提交
930
}
D
Daniel Larimer 已提交
931

932
void controller::push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline ) {
D
Daniel Larimer 已提交
933 934 935 936
   const auto& idx = db().get_index<generated_transaction_multi_index,by_trx_id>();
   auto itr = idx.find( trxid );
   FC_ASSERT( itr != idx.end(), "unknown transaction" );
   my->push_scheduled_transaction( *itr, deadline );
D
Daniel Larimer 已提交
937 938 939 940 941
}

uint32_t controller::head_block_num()const {
   return my->head->block_num;
}
D
Daniel Larimer 已提交
942 943 944
block_id_type controller::head_block_id()const {
   return my->head->id;
}
D
Daniel Larimer 已提交
945 946 947 948 949 950
account_name  controller::head_block_producer()const {
   return my->head->header.producer;
}
uint32_t controller::last_irreversible_block_num() const {
   return my->head->bft_irreversible_blocknum;
}
D
Daniel Larimer 已提交
951

D
Daniel Larimer 已提交
952
block_id_type controller::last_irreversible_block_id() const {
953
   //QUESTION/BUG: What if lib has not advanced for over 2^16 blocks?
D
Daniel Larimer 已提交
954 955 956 957
   const auto& tapos_block_summary = db().get<block_summary_object>((uint16_t)my->head->bft_irreversible_blocknum);
   return tapos_block_summary.block_id;
}

D
Daniel Larimer 已提交
958
time_point controller::head_block_time()const {
959 960 961 962 963 964
   return my->head_block_time();
}

time_point controller::pending_block_time()const {
   FC_ASSERT( my->pending, "no pending block" );
   return my->pending->_pending_block_state->header.timestamp;
D
Daniel Larimer 已提交
965 966
}

D
Daniel Larimer 已提交
967 968 969
const dynamic_global_property_object& controller::get_dynamic_global_properties()const {
  return my->db.get<dynamic_global_property_object>();
}
D
Daniel Larimer 已提交
970 971 972
const global_property_object& controller::get_global_properties()const {
  return my->db.get<global_property_object>();
}
D
Daniel Larimer 已提交
973 974 975 976

/**
 *  This method reads the current dpos_irreverible block number, if it is higher
 *  than the last block number of the log, it grabs the next block from the
977
 *  fork database, saves it to disk, then removes the block from the fork database.
D
Daniel Larimer 已提交
978 979 980 981
 *
 *  Any forks built off of a different block with the same number are also pruned.
 */
void controller::log_irreversible_blocks() {
982
   if( !my->blog.head() )
D
Daniel Larimer 已提交
983
      my->blog.read_head();
984

D
Daniel Larimer 已提交
985 986 987 988
   const auto& log_head = my->blog.head();
   auto lib = my->head->dpos_last_irreversible_blocknum;

   if( lib > 1 ) {
D
Daniel Larimer 已提交
989
      while( log_head && (log_head->block_num()+1) < lib ) {
D
Daniel Larimer 已提交
990
         auto lhead = log_head->block_num();
991 992
         auto blk = my->fork_db.get_block_in_current_chain_by_num( lhead + 1 );
         FC_ASSERT( blk, "unable to find block state", ("block_num",lhead+1));
993
         irreversible_block( blk );
D
Daniel Larimer 已提交
994
         my->blog.append( blk->block );
D
Daniel Larimer 已提交
995 996
         my->fork_db.prune( blk );
         my->db.commit( lhead );
D
Daniel Larimer 已提交
997 998 999
      }
   }
}
1000
signed_block_ptr controller::fetch_block_by_id( block_id_type id )const {
D
Daniel Larimer 已提交
1001
   idump((id));
1002 1003
   auto state = my->fork_db.get_block(id);
   if( state ) return state->block;
D
Daniel Larimer 已提交
1004
   edump((block_header::num_from_id(id)));
1005
   auto bptr = fetch_block_by_number( block_header::num_from_id(id) );
D
Daniel Larimer 已提交
1006 1007
   if( bptr && bptr->id() == id ) return bptr;
   elog( "not found" );
1008 1009 1010
   return signed_block_ptr();
}

D
Daniel Larimer 已提交
1011
signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const  { try {
1012
   auto blk_state = my->fork_db.get_block_in_current_chain_by_num( block_num );
D
Daniel Larimer 已提交
1013 1014 1015
   if( blk_state ) {
      return blk_state->block;
   }
1016

D
Daniel Larimer 已提交
1017
   ilog( "blog read by number ${n}", ("n", block_num) );
D
Daniel Larimer 已提交
1018
   return my->blog.read_block_by_num(block_num);
D
Daniel Larimer 已提交
1019
} FC_CAPTURE_AND_RETHROW( (block_num) ) }
D
Daniel Larimer 已提交
1020 1021

void controller::pop_block() {
1022
   my->pop_block();
D
Daniel Larimer 已提交
1023 1024
}

1025
bool controller::set_proposed_producers( vector<producer_key> producers ) {
1026 1027
   const auto& gpo = get_global_properties();
   auto cur_block_num = head_block_num() + 1;
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

   if( gpo.proposed_schedule_block_num.valid() ) {
      if( *gpo.proposed_schedule_block_num != cur_block_num )
         return false; // there is already a proposed schedule set in a previous block, wait for it to become pending

      if( std::equal( producers.begin(), producers.end(),
                      gpo.proposed_schedule.producers.begin(), gpo.proposed_schedule.producers.end() ) )
         return false; // the proposed producer schedule does not change
   }

   producer_schedule_type sch;

   decltype(sch.producers.cend()) end;
   decltype(end)                  begin;

1043
   if( my->pending->_pending_block_state->pending_schedule.producers.size() == 0 ) {
1044 1045 1046 1047
      const auto& active_sch = my->pending->_pending_block_state->active_schedule;
      begin = active_sch.producers.begin();
      end   = active_sch.producers.end();
      sch.version = active_sch.version + 1;
1048
   } else {
1049 1050 1051 1052
      const auto& pending_sch = my->pending->_pending_block_state->pending_schedule;
      begin = pending_sch.producers.begin();
      end   = pending_sch.producers.end();
      sch.version = pending_sch.version + 1;
1053
   }
1054 1055 1056 1057 1058 1059

   if( std::equal( producers.begin(), producers.end(), begin, end ) )
      return false; // the producer schedule would not change

   sch.producers = std::move(producers);

1060 1061
   my->db.modify( gpo, [&]( auto& gp ) {
      gp.proposed_schedule_block_num = cur_block_num;
1062
      gp.proposed_schedule = std::move(sch);
1063
   });
1064
   return true;
D
Daniel Larimer 已提交
1065
}
1066 1067

const producer_schedule_type&    controller::active_producers()const {
D
Daniel Larimer 已提交
1068 1069 1070
   return my->pending->_pending_block_state->active_schedule;
}

1071
const producer_schedule_type&    controller::pending_producers()const {
D
Daniel Larimer 已提交
1072 1073 1074
   return my->pending->_pending_block_state->pending_schedule;
}

1075 1076 1077 1078 1079 1080 1081 1082 1083
optional<producer_schedule_type> controller::proposed_producers()const {
   const auto& gpo = get_global_properties();
   if( !gpo.proposed_schedule_block_num.valid() )
      return optional<producer_schedule_type>();

   return gpo.proposed_schedule;
}


D
Daniel Larimer 已提交
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
const apply_handler* controller::find_apply_handler( account_name receiver, account_name scope, action_name act ) const
{
   auto native_handler_scope = my->apply_handlers.find( receiver );
   if( native_handler_scope != my->apply_handlers.end() ) {
      auto handler = native_handler_scope->second.find( make_pair( scope, act ) );
      if( handler != native_handler_scope->second.end() )
         return &handler->second;
   }
   return nullptr;
}
wasm_interface& controller::get_wasm_interface() {
   return my->wasmif;
}
D
Daniel Larimer 已提交
1097

1098
const account_object& controller::get_account( account_name name )const
D
Daniel Larimer 已提交
1099 1100 1101
{ try {
   return my->db.get<account_object, by_name>(name);
} FC_CAPTURE_AND_RETHROW( (name) ) }
D
Daniel Larimer 已提交
1102

1103 1104 1105 1106
const map<digest_type, transaction_metadata_ptr>&  controller::unapplied_transactions()const {
   return my->unapplied_transactions;
}

A
arhag 已提交
1107 1108 1109
fc::microseconds controller::limit_delay( fc::microseconds delay )const {
   return my->limit_delay( delay );
}
1110 1111 1112 1113 1114 1115

void controller::validate_referenced_accounts( const transaction& trx )const {
   for( const auto& a : trx.context_free_actions ) {
      get_account( a.account );
      FC_ASSERT( a.authorization.size() == 0 );
   }
D
Daniel Larimer 已提交
1116
   bool one_auth = false;
1117 1118
   for( const auto& a : trx.actions ) {
      get_account( a.account );
1119
      for( const auto& auth : a.authorization ) {
D
Daniel Larimer 已提交
1120
         one_auth = true;
1121 1122 1123
         get_account( auth.actor );
      }
   }
D
Daniel Larimer 已提交
1124
   EOS_ASSERT( one_auth, tx_no_auths, "transaction must have at least one authorization" );
1125 1126 1127 1128 1129
}

void controller::validate_expiration( const transaction& trx )const { try {
   const auto& chain_configuration = get_global_properties().configuration;

1130 1131 1132 1133 1134
   EOS_ASSERT( time_point(trx.expiration) >= pending_block_time(),
               expired_tx_exception,
               "transaction has expired, "
               "expiration is ${trx.expiration} and pending block time is ${pending_block_time}",
               ("trx.expiration",trx.expiration)("pending_block_time",pending_block_time()));
1135 1136 1137 1138 1139 1140 1141 1142
   EOS_ASSERT( time_point(trx.expiration) <= pending_block_time() + fc::seconds(chain_configuration.max_transaction_lifetime),
               tx_exp_too_far_exception,
               "Transaction expiration is too far in the future relative to the reference time of ${reference_time}, "
               "expiration is ${trx.expiration} and the maximum transaction lifetime is ${max_til_exp} seconds",
               ("trx.expiration",trx.expiration)("reference_time",pending_block_time())
               ("max_til_exp",chain_configuration.max_transaction_lifetime) );
} FC_CAPTURE_AND_RETHROW((trx)) }

D
Daniel Larimer 已提交
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
uint64_t controller::validate_net_usage( const transaction_metadata_ptr& trx )const {
   const auto& cfg = get_global_properties().configuration;

   auto actual_net_usage = cfg.base_per_transaction_net_usage + trx->packed_trx.get_billable_size();

   actual_net_usage = ((actual_net_usage + 7)/8) * 8; // Round up to nearest multiple of 8

   uint32_t net_usage_limit = trx->trx.max_net_usage_words.value * 8UL; // overflow checked in validate_transaction_without_state
   EOS_ASSERT( net_usage_limit == 0 || actual_net_usage <= net_usage_limit, tx_resource_exhausted,
               "declared net usage limit of transaction is too low: ${actual_net_usage} > ${declared_limit}",
               ("actual_net_usage", actual_net_usage)("declared_limit",net_usage_limit) );

   return actual_net_usage;
}

1158 1159 1160 1161 1162 1163 1164
void controller::validate_tapos( const transaction& trx )const { try {
   const auto& tapos_block_summary = db().get<block_summary_object>((uint16_t)trx.ref_block_num);

   //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
   EOS_ASSERT(trx.verify_reference_block(tapos_block_summary.block_id), invalid_ref_block_exception,
              "Transaction's reference block did not match. Is this transaction from a different fork?",
              ("tapos_summary", tapos_block_summary));
1165 1166
} FC_CAPTURE_AND_RETHROW() }

1167

D
Daniel Larimer 已提交
1168
} } /// eosio::chain