chain_controller.cpp 60.2 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

#include <eos/chain/block_summary_object.hpp>
#include <eos/chain/global_property_object.hpp>
N
Nathan Hourt 已提交
29 30
#include <eos/chain/key_value_object.hpp>
#include <eos/chain/action_objects.hpp>
31
#include <eos/chain/generated_transaction_object.hpp>
N
Nathan Hourt 已提交
32 33
#include <eos/chain/transaction_object.hpp>
#include <eos/chain/producer_object.hpp>
34
#include <eos/chain/permission_link_object.hpp>
N
Nathan Hourt 已提交
35
#include <eos/chain/authority_checker.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

#include <fstream>
#include <functional>
#include <iostream>
57
#include <chrono>
N
Nathan Hourt 已提交
58 59 60

namespace eos { namespace chain {

61
bool chain_controller::is_known_block(const block_id_type& id)const
N
Nathan Hourt 已提交
62
{
63
   return _fork_db.is_known_block(id) || _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
64 65 66 67 68 69
}
/**
 * Only return true *if* the transaction has not expired or been invalidated. If this
 * method is called with a VERY old transaction we will return false, they should
 * query things by blocks if they are that old.
 */
70
bool chain_controller::is_known_transaction(const transaction_id_type& id)const
N
Nathan Hourt 已提交
71
{
72
   const auto& trx_idx = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
73 74 75
   return trx_idx.find( id ) != trx_idx.end();
}

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

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

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

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

N
Nathan Hourt 已提交
96
   // Not in _block_log, so it must be since the last irreversible block. Grab it from _fork_db instead
97 98 99 100 101 102 103 104
   if (num <= head_block_num()) {
      auto block = _fork_db.head();
      while (block && block->num > num)
         block = block->prev.lock();
      if (block && block->num == num)
         return block->data;
   }

N
Nathan Hourt 已提交
105 106 107
   return optional<signed_block>();
}

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

116
std::vector<block_id_type> chain_controller::get_block_ids_on_fork(block_id_type head_of_fork) const
N
Nathan Hourt 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
{
  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;
}

134 135 136 137 138 139 140
const GeneratedTransaction& chain_controller::get_generated_transaction( const generated_transaction_id_type& id ) const {
   auto& index = _db.get_index<generated_transaction_multi_index, generated_transaction_object::by_trx_id>();
   auto itr = index.find(id);
   FC_ASSERT(itr != index.end());
   return itr->trx;
}

N
Nathan Hourt 已提交
141 142 143 144 145 146
/**
 * 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.
 */
147
bool chain_controller::push_block(const signed_block& new_block, uint32_t skip)
D
Daniel Larimer 已提交
148 149 150
{ try {
   return with_skip_flags( skip, [&](){ 
      return without_pending_transactions( [&]() {
151
         return _db.with_write_lock( [&]() {
152 153
            return _push_block(new_block);
         } );
N
Nathan Hourt 已提交
154 155
      });
   });
156
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
157

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

            // pop blocks until we hit the forked block
175
            while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
176 177 178
               pop_block();

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

                   // pop all blocks from the bad fork
198
                   while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
199 200 201
                      pop_block();

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

   try {
217
      auto session = _db.start_undo_session(true);
218
      auto exec_start = std::chrono::high_resolution_clock::now();
N
Nathan Hourt 已提交
219
      apply_block(new_block, skip);
220 221 222 223 224 225
      if( (fc::time_point::now() - new_block.timestamp) < fc::seconds(60) )
      {
         auto exec_stop = std::chrono::high_resolution_clock::now();
         auto exec_ms = std::chrono::duration_cast<std::chrono::milliseconds>(exec_stop - exec_start);      
         size_t trxcount = 0;
         for (const auto& cycle : new_block.cycles)
M
matthew 已提交
226 227
            for (const auto& thread : cycle)
               trxcount += thread.user_input.size();
M
matthew 已提交
228 229 230 231 232 233 234
         ilog( "producer=[${prod}], blocktime=${bktm}, blocknum=${bknu}, trxcount=${txco}, pendingcount=${pend}, exectime_ms=${extm}", 
            ("prod", new_block.producer) 
            ("bktm", new_block.timestamp)
            ("bknu", new_block.block_num())
            ("txco", trxcount)
            ("pend", _pending_transactions.size())
            ("extm", exec_ms.count())
235 236
         );
      }
N
Nathan Hourt 已提交
237 238 239 240 241 242 243 244
      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;
245
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
246 247 248 249 250 251 252 253 254 255

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

265
ProcessedTransaction chain_controller::_push_transaction(const SignedTransaction& trx) {
266 267
   // 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.
268
   if (!_pending_tx_session.valid())
269
      _pending_tx_session = _db.start_undo_session(true);
270

271
   auto temp_session = _db.start_undo_session(true);
272
   validate_referenced_accounts(trx);
N
Nathan Hourt 已提交
273
   check_transaction_authorization(trx);
274
   auto pt = apply_transaction(trx);
D
Daniel Larimer 已提交
275
   _pending_transactions.push_back(trx);
N
Nathan Hourt 已提交
276 277 278 279 280 281

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

   // notify anyone listening to pending transactions
282 283 284
   on_pending_transaction(trx); /// TODO move this to apply...

   return pt;
N
Nathan Hourt 已提交
285 286
}

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

304
signed_block chain_controller::_generate_block(
N
Nathan Hourt 已提交
305
   fc::time_point_sec when,
N
Nathan Hourt 已提交
306
   const AccountName& producer,
307 308
   const fc::ecc::private_key& block_signing_private_key,
   block_schedule::factory scheduler
N
Nathan Hourt 已提交
309 310 311
   )
{
   try {
N
Nathan Hourt 已提交
312
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
313 314
   uint32_t slot_num = get_slot_at_time( when );
   FC_ASSERT( slot_num > 0 );
N
Nathan Hourt 已提交
315 316
   AccountName scheduled_producer = get_scheduled_producer( slot_num );
   FC_ASSERT( scheduled_producer == producer );
N
Nathan Hourt 已提交
317

N
Nathan Hourt 已提交
318
   const auto& producer_obj = get_producer(scheduled_producer);
N
Nathan Hourt 已提交
319 320 321 322

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

323
  
324
   const auto& generated = _db.get_index<generated_transaction_multi_index, generated_transaction_object::by_status>().equal_range(generated_transaction_object::PENDING);
N
Nathan Hourt 已提交
325

326
   vector<pending_transaction> pending;
B
Bart Wyatt 已提交
327
   std::set<transaction_id_type> invalid_pending;
328 329 330
   pending.reserve(std::distance(generated.first, generated.second) + _pending_transactions.size());
   for (auto iter = generated.first; iter != generated.second; ++iter) {
      const auto& gt = *iter;
B
Bart Wyatt 已提交
331
      pending.emplace_back(std::reference_wrapper<const GeneratedTransaction> {gt.trx});
332 333
   }
   
B
Bart Wyatt 已提交
334 335
   for(const auto& st: _pending_transactions) {
      pending.emplace_back(std::reference_wrapper<const SignedTransaction> {st});
336 337
   }

338
   auto schedule = scheduler(pending, get_global_properties());
N
Nathan Hourt 已提交
339 340 341 342 343 344 345 346 347 348 349 350 351

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

354 355 356 357 358 359 360 361 362 363 364 365
   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;
366 367 368 369 370 371
       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);
B
Bart Wyatt 已提交
372 373
             if (trx.contains<std::reference_wrapper<const SignedTransaction>>()) {
                const auto& t = trx.get<std::reference_wrapper<const SignedTransaction>>().get();
B
Bart Wyatt 已提交
374
                validate_referenced_accounts(t);
375
                check_transaction_authorization(t);
376
                auto processed = apply_transaction(t);
377
                block_thread.user_input.emplace_back(processed);
B
Bart Wyatt 已提交
378
             } else if (trx.contains<std::reference_wrapper<const GeneratedTransaction>>()) {
379 380 381
                const auto& t = trx.get<std::reference_wrapper<const GeneratedTransaction>>().get();
                auto processed = apply_transaction(t);
                block_thread.generated_input.emplace_back(processed);
382 383 384 385 386 387 388 389 390 391
             } 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
B
Bart Wyatt 已提交
392
             elog( "Transaction was not processed while generating block due to ${e}", ("e", e) );
B
Bart Wyatt 已提交
393
             if (trx.contains<std::reference_wrapper<const SignedTransaction>>()) {
B
Bart Wyatt 已提交
394
                const auto& t = trx.get<std::reference_wrapper<const SignedTransaction>>().get();
B
Bart Wyatt 已提交
395
                wlog( "The transaction was ${t}", ("t", t ) );
B
Bart Wyatt 已提交
396
                invalid_pending.emplace(t.id());
B
Bart Wyatt 已提交
397 398
             } else if (trx.contains<std::reference_wrapper<const GeneratedTransaction>>()) {
                wlog( "The transaction was ${t}", ("t", trx.get<std::reference_wrapper<const GeneratedTransaction>>().get()) );
399 400 401
             } 
             invalid_transaction_count++;
          }
402
       }
N
Nathan Hourt 已提交
403

404
       if (!(block_thread.generated_input.empty() && block_thread.user_input.empty())) {
405 406
          block_thread.generated_input.shrink_to_fit();
          block_thread.user_input.shrink_to_fit();
407 408 409
          block_cycle.emplace_back(std::move(block_thread));
       }
     }
410

411 412 413
     if (!block_cycle.empty()) {
        pending_block.cycles.emplace_back(std::move(block_cycle));
     }
N
Nathan Hourt 已提交
414
   }
415 416
   
   size_t postponed_tx_count = _pending_transactions.size() - valid_transaction_count - invalid_transaction_count;
N
Nathan Hourt 已提交
417 418 419 420 421
   if( postponed_tx_count > 0 )
   {
      wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
   }

422 423 424
   if( invalid_transaction_count > 0 )
   {
      wlog( "Postponed ${n} transactions errors when processing", ("n", invalid_transaction_count) );
B
Bart Wyatt 已提交
425 426 427 428 429 430 431 432 433 434 435 436

      // remove pending transactions determined to be bad during scheduling
      if (invalid_pending.size() > 0) {
         for (auto itr = _pending_transactions.begin(); itr != _pending_transactions.end(); ) {
            auto& tx = *itr;
            if (invalid_pending.find(tx.id()) != invalid_pending.end()) {
               itr = _pending_transactions.erase(itr);
            } else {
               ++itr;
            }
         }
      }
437 438
   }

N
Nathan Hourt 已提交
439 440 441 442
   _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 已提交
443
   // _pending_transactions now consists of the set of postponed transactions.
N
Nathan Hourt 已提交
444 445 446 447 448 449
   // 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 已提交
450

451
   pending_block.producer = producer_obj.owner;
N
Nathan Hourt 已提交
452

453 454 455 456 457 458
   // 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 已提交
459 460 461 462
   if( !(skip & skip_producer_signature) )
      pending_block.sign( block_signing_private_key );

   // TODO:  Move this to _push_block() so session is restored.
463
   /*
N
Nathan Hourt 已提交
464 465 466 467
   if( !(skip & skip_block_size_check) )
   {
      FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
   }
468
   */
N
Nathan Hourt 已提交
469

470
   // push_block( pending_block, skip );
N
Nathan Hourt 已提交
471 472

   return pending_block;
N
Nathan Hourt 已提交
473
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
474 475

/**
N
Nathan Hourt 已提交
476
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
477
 */
478
void chain_controller::pop_block()
N
Nathan Hourt 已提交
479 480 481 482 483 484 485
{ 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();
486
   _db.undo();
N
Nathan Hourt 已提交
487 488
} FC_CAPTURE_AND_RETHROW() }

489
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
490
{ try {
D
Daniel Larimer 已提交
491
   _pending_transactions.clear();
N
Nathan Hourt 已提交
492 493 494 495 496
   _pending_tx_session.reset();
} FC_CAPTURE_AND_RETHROW() }

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

497
void chain_controller::apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
498 499
{
   auto block_num = next_block.block_num();
500 501 502 503 504
   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 已提交
505

506
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
507 508
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
N
Nathan Hourt 已提交
509 510 511 512 513 514

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

517 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 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
struct path_cons_list {
   typedef static_variant<int, const char *> head_type;
   typedef const path_cons_list* tail_ref_type;
   typedef optional<tail_ref_type> tail_type;
   
   path_cons_list(int index, const path_cons_list &rest) 
      : head(head_type(index))
      , tail(tail_ref_type(&rest))
   { }

   path_cons_list(const char *path, const path_cons_list &rest) 
      : head(head_type(path))
      , tail(tail_ref_type(&rest))
   { }

   path_cons_list(const char *path) 
      : head(head_type(path))
      , tail()
   { }

   //path_cons_list( path_cons_list && ) = delete;
   
   const head_type head;
   tail_type tail;

   path_cons_list operator() (int index) const {
      return path_cons_list(index, *this);
   }

   path_cons_list operator() (const char *path) const {
      return path_cons_list(path, *this);
   }

   void add_to_stream( std::stringstream& ss ) const {
      if (tail) {
         (*tail)->add_to_stream(ss);
      }

      if (head.contains<int>()) {
         ss << "[" << head.get<int>() << "]";
      } else {
         ss << head.get<const char *>();
      }
   }
};

string resolve_path_string(const path_cons_list& path) {
   std::stringstream ss;
   path.add_to_stream(ss);
   return ss.str();
}

569
template<typename T>
570 571 572 573 574
void check_output(const T& expected, const T& actual, const path_cons_list& path) {
   try {
      EOS_ASSERT((expected == actual), block_tx_output_exception, 
         "expected: ${expected}, actual: ${actual}", ("expected", expected)("actual", actual));
   } FC_RETHROW_EXCEPTIONS(warn, "at: ${path}", ("path", resolve_path_string(path)));
575 576 577
}

template<typename T>
578 579
void check_output(const vector<T>& expected, const vector<T>& actual, const path_cons_list& path) {
   check_output(expected.size(), actual.size(), path(".size()"));
580 581 582
   for(int idx=0; idx < expected.size(); idx++) {
      const auto &expected_element = expected.at(idx);
      const auto &actual_element = actual.at(idx);
583
      check_output(expected_element, actual_element, path(idx));
584 585 586 587
   }
}

template<>
588 589 590 591 592
void check_output(const types::Bytes& expected, const types::Bytes& actual, const path_cons_list& path) {
  check_output(expected.size(), actual.size(), path(".size()"));
  
  auto cmp_result = std::memcmp(expected.data(), actual.data(), expected.size());
  check_output(cmp_result, 0, path("@memcmp()"));
593 594 595
}

template<>
596 597 598
void check_output(const types::AccountPermission& expected, const types::AccountPermission& actual, const path_cons_list& path) {
   check_output(expected.account, actual.account, path(".account"));
   check_output(expected.permission, actual.permission, path(".permission"));
599 600 601
}

template<>
602 603 604 605 606
void check_output(const types::Message& expected, const types::Message& actual, const path_cons_list& path) {
   check_output(expected.code, actual.code, path(".code"));
   check_output(expected.type, actual.type, path(".type"));
   check_output(expected.authorization, actual.authorization, path(".authorization"));
   check_output(expected.data, actual.data, path(".data"));
607 608 609
}

template<>
610
void check_output(const MessageOutput& expected, const MessageOutput& actual, const path_cons_list& path);
611 612

template<>
613 614 615
void check_output(const NotifyOutput& expected, const NotifyOutput& actual, const path_cons_list& path) {
   check_output(expected.name, actual.name, path(".name"));
   check_output(expected.output, actual.output, path(".output"));
616 617 618
}

template<>
619 620 621 622 623 624
void check_output(const Transaction& expected, const Transaction& actual, const path_cons_list& path) {
   check_output(expected.refBlockNum, actual.refBlockNum, path(".refBlockNum"));
   check_output(expected.refBlockPrefix, actual.refBlockPrefix, path(".refBlockPrefix"));
   check_output(expected.expiration, actual.expiration, path(".expiration"));
   check_output(expected.scope, actual.scope, path(".scope"));
   check_output(expected.messages, actual.messages, path(".messages"));
625 626 627 628
}


template<>
629 630 631
void check_output(const ProcessedSyncTransaction& expected, const ProcessedSyncTransaction& actual, const path_cons_list& path) {
   check_output<Transaction>(expected, actual, path);
   check_output(expected.output, actual.output, path(".output"));
632 633 634
}

template<>
635 636 637
void check_output(const GeneratedTransaction& expected, const GeneratedTransaction& actual, const path_cons_list& path) {
   check_output(expected.id, actual.id, path(".id"));
   check_output<Transaction>(expected, actual, path);
638 639 640
}

template<>
641 642 643 644
void check_output(const MessageOutput& expected, const MessageOutput& actual, const path_cons_list& path) {
   check_output(expected.notify, actual.notify, path(".notify"));
   check_output(expected.sync_transactions, actual.sync_transactions, path(".sync_transactions"));
   check_output(expected.async_transactions, actual.async_transactions, path(".async_transactions"));
645 646 647
}

template<typename T>
648
void chain_controller::check_transaction_output(const T& expected, const T& actual, const path_cons_list& path)const {
649
   if (!(_skip_flags & skip_output_check)) {
650
      check_output(expected.output, actual.output, path(".output"));
651 652 653
   }
}

654
void chain_controller::_apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
655 656
{ try {
   uint32_t next_block_num = next_block.block_num();
N
Nathan Hourt 已提交
657
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
658

659 660 661
   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 已提交
662 663

   const producer_object& signing_producer = validate_block_header(skip, next_block);
N
Nathan Hourt 已提交
664 665 666
   
   for (const auto& cycle : next_block.cycles)
      for (const auto& thread : cycle)
667 668
         for (const auto& trx : thread.user_input) {
            validate_referenced_accounts(trx);
669 670 671
            // Check authorization, and allow irrelevant signatures.
            // If the block producer let it slide, we'll roll with it.
            check_transaction_authorization(trx, true);
672
         }
N
Nathan Hourt 已提交
673

N
Nathan Hourt 已提交
674 675 676 677 678 679
   /* 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.
    */
680
   auto root_path = path_cons_list("next_block.cycles");
681 682
   for (int c_idx = 0; c_idx < next_block.cycles.size(); c_idx++) {
      const auto& cycle = next_block.cycles.at(c_idx);
683
      auto c_path = path_cons_list(c_idx, root_path);
684 685 686

      for (int t_idx = 0; t_idx < cycle.size(); t_idx++) {
         const auto& thread = cycle.at(t_idx);
687
         auto t_path = path_cons_list(t_idx, c_path);
688

689
         auto gen_path = path_cons_list(".generated_input", t_path);
690 691 692 693
         for(int p_idx = 0; p_idx < thread.generated_input.size(); p_idx++ ) {
            const auto& ptrx = thread.generated_input.at(p_idx);
            const auto& trx = get_generated_transaction(ptrx.id);
            auto processed = apply_transaction(trx);
694
            check_transaction_output(ptrx, processed, gen_path(p_idx));
N
Nathan Hourt 已提交
695
         }
696

697
         auto user_path = path_cons_list(".user_input", t_path);
698 699
         for(int p_idx = 0; p_idx < thread.user_input.size(); p_idx++ ) {
            const auto& ptrx = thread.user_input.at(p_idx);
700 701
            const SignedTransaction& trx = ptrx;
            auto processed = apply_transaction(trx);
702
            check_transaction_output(ptrx, processed, user_path(p_idx));
703 704 705
         }
      }
   }
N
Nathan Hourt 已提交
706

707
   update_global_properties(next_block);
N
Nathan Hourt 已提交
708 709 710 711 712 713 714 715
   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
716
   // TODO: do this outside the write lock...? 
N
Nathan Hourt 已提交
717 718 719 720
   applied_block( next_block ); //emit

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

721
void chain_controller::check_transaction_authorization(const SignedTransaction& trx, bool allow_unused_signatures)const {
N
Nathan Hourt 已提交
722 723 724 725 726
   if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
      ilog("Skipping auth and sigs checks");
      return;
   }

727

N
Nathan Hourt 已提交
728 729 730 731 732 733 734
   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;
   };
735
   auto depthLimit = get_global_properties().configuration.authDepthLimit;
N
Nathan Hourt 已提交
736
#warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
737
   auto checker = MakeAuthorityChecker(std::move(getAuthority), depthLimit, trx.get_signature_keys(chain_id_type{}));
N
Nathan Hourt 已提交
738 739 740 741 742 743 744 745

   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,
N
Nathan Hourt 已提交
746 747
                       "Message declares irrelevant authority '${auth}'; minimum authority is ${min}",
                       ("auth", declaredAuthority)("min", minimumPermission.name));
N
Nathan Hourt 已提交
748 749 750 751 752 753 754
         }
         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));
         }
      }
N
Nathan Hourt 已提交
755

756
   if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
757 758
      EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
                 "Transaction bears irrelevant signatures from these keys: ${keys}", ("keys", checker.unused_keys()));
N
Nathan Hourt 已提交
759 760
}

761
void chain_controller::validate_scope( const Transaction& trx )const {
762
   EOS_ASSERT(trx.scope.size() + trx.readscope.size() > 0, transaction_exception, "No scope specified by transaction" );
763 764
   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" );
765 766 767 768 769 770 771 772
   for( uint32_t i = 1; i < trx.readscope.size(); ++i )
      EOS_ASSERT( trx.readscope[i-1] < trx.readscope[i], transaction_exception, "Scopes must be sorted and unique" );

   vector<types::AccountName> intersection;
   std::set_intersection( trx.scope.begin(), trx.scope.end(),
                          trx.readscope.begin(), trx.readscope.end(),
                          std::back_inserter(intersection) );
   FC_ASSERT( intersection.size() == 0, "a transaction may not redeclare scope in readscope" );
773 774
}

N
Nathan Hourt 已提交
775 776 777 778
const permission_object& chain_controller::lookup_minimum_permission(types::AccountName authorizer_account,
                                                                    types::AccountName code_account,
                                                                    types::FuncName type) const {
   try {
779 780 781 782 783 784 785 786 787 788 789 790 791 792
      // 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 已提交
793 794 795
   } FC_CAPTURE_AND_RETHROW((authorizer_account)(code_account)(type))
}

796
void chain_controller::validate_uniqueness( const SignedTransaction& trx )const {
N
Nathan Hourt 已提交
797
   if( !should_check_for_duplicate_transactions() ) return;
N
Nathan Hourt 已提交
798

N
Nathan Hourt 已提交
799
   auto transaction = _db.find<transaction_object, by_trx_id>(trx.id());
800
   EOS_ASSERT(transaction == nullptr, tx_duplicate, "Transaction is not unique");
801
}
802

803 804 805 806
void chain_controller::validate_uniqueness( const GeneratedTransaction& trx )const {
   if( !should_check_for_duplicate_transactions() ) return;
}

807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
void chain_controller::record_transaction(const SignedTransaction& trx) {
   //Insert transaction into unique transactions database.
    _db.create<transaction_object>([&](transaction_object& transaction) {
        transaction.trx_id = trx.id(); /// TODO: consider caching ID
        transaction.trx = trx;
    });
}

void chain_controller::record_transaction(const GeneratedTransaction& trx) {
   _db.modify( _db.get<generated_transaction_object,generated_transaction_object::by_trx_id>(trx.id), [&](generated_transaction_object& transaction) {
      transaction.status = generated_transaction_object::PROCESSED;
   });
}     



823
void chain_controller::validate_tapos(const Transaction& trx)const {
N
Nathan Hourt 已提交
824
   if (!should_check_tapos()) return;
825

826
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.refBlockNum);
827 828

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

834
void chain_controller::validate_referenced_accounts(const Transaction& trx)const {
N
Nathan Hourt 已提交
835 836 837
   for (const auto& scope : trx.scope)
      require_account(scope);
   for (const auto& msg : trx.messages) {
838
      require_account(msg.code);
N
Nathan Hourt 已提交
839 840
      for (const auto& auth : msg.authorization)
         require_account(auth.account);
841 842
   }
}
D
Daniel Larimer 已提交
843

844
void chain_controller::validate_expiration(const Transaction& trx) const
845 846
{ try {
   fc::time_point_sec now = head_block_time();
847
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
848

849
   EOS_ASSERT(trx.expiration <= now + int32_t(chain_configuration.maxTrxLifetime),
850 851
              transaction_exception, "Transaction expiration is too far in the future",
              ("trx.expiration",trx.expiration)("now",now)
852
              ("max_til_exp",chain_configuration.maxTrxLifetime));
853 854 855
   EOS_ASSERT(now <= trx.expiration, transaction_exception, "Transaction is expired",
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
856

857

858
void chain_controller::process_message(const Transaction& trx, AccountName code,
859
                                       const Message& message, MessageOutput& output, apply_context* parent_context) {
860
   apply_context apply_ctx(*this, _db, trx, message, code);
N
Nathan Hourt 已提交
861 862
   apply_message(apply_ctx);

863 864 865
   output.notify.reserve( apply_ctx.notified.size() );

   for( uint32_t i = 0; i < apply_ctx.notified.size(); ++i ) {
N
Nathan Hourt 已提交
866
      try {
867 868
         auto notify_code = apply_ctx.notified[i];
         output.notify.push_back( {notify_code} );
869
         process_message(trx, notify_code, message, output.notify.back().output, &apply_ctx);
870 871 872 873 874 875 876 877 878 879
      } 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 ) {
880 881 882 883 884 885
      _db.create<generated_transaction_object>([&](generated_transaction_object& transaction) {
         transaction.trx = asynctrx;
         transaction.status = generated_transaction_object::PENDING;
      });

      output.async_transactions.emplace_back( std::move( asynctrx ) );
N
Nathan Hourt 已提交
886
   }
887 888 889 890 891 892 893 894 895 896 897 898

   // propagate used_authorizations up the context chain
   if (parent_context != nullptr)
      for (int i = 0; i < apply_ctx.used_authorizations.size(); ++i)
         if (apply_ctx.used_authorizations[i])
            parent_context->used_authorizations[i] = true;

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

901
void chain_controller::apply_message(apply_context& context)
902
{ try {
D
Daniel Larimer 已提交
903 904
    /// context.code => the execution namespace
    /// message.code / message.type => Event
905
    const auto& m = context.msg;
D
Daniel Larimer 已提交
906
    auto contract_handlers_itr = apply_handlers.find(context.code);
907 908 909
    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 已提交
910
          message_handler_itr->second(context);
911 912 913
          return;
       }
    }
D
Daniel Larimer 已提交
914
    const auto& recipient = _db.get<account_object,by_name>(context.code);
915
    if (recipient.code.size()) {
916
       //idump((context.code)(context.msg.type));
917
       wasm_interface::get().apply(context);
918
    }
919

N
Nathan Hourt 已提交
920
} FC_CAPTURE_AND_RETHROW((context.msg)) }
N
Nathan Hourt 已提交
921

922 923
template<typename T>
typename T::Processed chain_controller::apply_transaction(const T& trx)
924
{ try {
925
   validate_transaction(trx);
926
   record_transaction(trx);
927 928
   return process_transaction( trx );

N
Nathan Hourt 已提交
929
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
930

931 932 933
/**
 *  @pre the transaction is assumed valid and all signatures / duplicate checks have bee performed
 */
934 935
template<typename T>
typename T::Processed chain_controller::process_transaction( const T& trx ) 
936
{ try {
937
   typename T::Processed ptrx( trx );
938 939
   ptrx.output.resize( trx.messages.size() );

940 941
   for( uint32_t i = 0; i < trx.messages.size(); ++i ) {
      process_message(trx, trx.messages[i].code, trx.messages[i], ptrx.output[i] );
942 943 944 945 946
   }

   return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }

N
Nathan Hourt 已提交
947 948 949 950
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 已提交
951

952
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
953 954 955 956
   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 已提交
957
   if (next_block.block_num() % config::BlocksPerRound != 0) {
958 959
      EOS_ASSERT(next_block.producer_changes.empty(), block_validate_exception,
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
960 961 962 963 964 965 966
   } 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 已提交
967
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
968

N
Nathan Hourt 已提交
969
   if(!(skip&skip_producer_signature))
970 971 972
      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 已提交
973

974
   if(!(skip&skip_producer_schedule_check)) {
975 976 977
      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 已提交
978 979 980 981 982
   }

   return producer;
}

983
void chain_controller::create_block_summary(const signed_block& next_block) {
984
   auto sid = next_block.block_num() & 0xffff;
985
   _db.modify( _db.get<block_summary_object,by_id>(sid), [&](block_summary_object& p) {
986 987
         p.block_id = next_block.id();
   });
N
Nathan Hourt 已提交
988 989
}

990
void chain_controller::update_global_properties(const signed_block& b) {
991 992
   // If we're at the end of a round, update the BlockchainConfiguration, producer schedule
   // and "producers" special account authority
N
Nathan Hourt 已提交
993
   if (b.block_num() % config::BlocksPerRound == 0) {
994
      auto schedule = calculate_next_round(b);
995 996
      auto config = _admin->get_blockchain_configuration(_db, schedule);

997 998
      const auto& gpo = get_global_properties();
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
999 1000 1001
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
1002

1003
      auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {});
1004
      for(auto& name : gpo.active_producers) {
1005
         active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1});
1006 1007
      }

1008
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::ProducersAccountName, config::ActiveName) );
1009 1010 1011
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
1012 1013 1014
   }
}

1015
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
1016
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
1017 1018 1019
      _checkpoints[i.first] = i.second;
}

1020
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
1021 1022 1023
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

1024
const global_property_object& chain_controller::get_global_properties()const {
1025
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
1026 1027
}

1028
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
1029
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
1030 1031
}

1032
time_point_sec chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
1033 1034 1035
   return get_dynamic_global_properties().time;
}

1036
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
1037 1038 1039
   return get_dynamic_global_properties().head_block_number;
}

1040
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
1041 1042 1043
   return get_dynamic_global_properties().head_block_id;
}

N
Nathan Hourt 已提交
1044
types::AccountName chain_controller::head_block_producer() const {
1045 1046 1047
   auto b = _fork_db.fetch_block(head_block_id());
   if( b ) return b->data.producer;

N
Nathan Hourt 已提交
1048
   if (auto head_block = fetch_block_by_id(head_block_id()))
1049
      return head_block->producer;
N
Nathan Hourt 已提交
1050 1051 1052
   return {};
}

N
Nathan Hourt 已提交
1053 1054 1055 1056
const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const {
   return _db.get<producer_object, by_owner>(ownerName);
}

1057
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
1058
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
1059 1060
}

1061
void chain_controller::initialize_indexes() {
1062 1063
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
1064
   _db.add_index<permission_link_index>();
1065 1066
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
1067
   _db.add_index<key128x128_value_index>();
1068
   _db.add_index<key64x64x64_value_index>();
1069 1070 1071 1072 1073

   _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>();
1074
   _db.add_index<generated_transaction_multi_index>();
1075
   _db.add_index<producer_multi_index>();
N
Nathan Hourt 已提交
1076 1077
}

1078
void chain_controller::initialize_chain(chain_initializer_interface& starter)
N
Nathan Hourt 已提交
1079
{ try {
1080
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
1081 1082 1083 1084 1085
      _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 已提交
1086

1087
         // Create global properties
N
Nathan Hourt 已提交
1088 1089 1090
         _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();
1091 1092
         });
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
1093
            p.time = initial_timestamp;
1094 1095
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
1096

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

1101
         auto messages = starter.prepare_database(*this, _db);
1102 1103
         std::for_each(messages.begin(), messages.end(), [&](const Message& m) { 
            MessageOutput output;
1104
            ProcessedTransaction trx; /// dummy tranaction required for scope validation
1105
            std::sort(trx.scope.begin(), trx.scope.end() );
1106
            with_skip_flags(skip_scope_check | skip_transaction_signatures | skip_authority_check, [&](){
1107
               process_message(trx,m.code,m,output); 
1108
            });
1109
         });
1110 1111
      });
   }
N
Nathan Hourt 已提交
1112 1113
} FC_CAPTURE_AND_RETHROW() }

1114 1115 1116
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 已提交
1117

1118
   initialize_indexes();
1119
   starter.register_types(*this, _db);
N
Nathan Hourt 已提交
1120 1121 1122 1123 1124 1125

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

1126 1127
   spinup_db();
   spinup_fork_db();
1128 1129 1130

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

1133 1134 1135 1136 1137
chain_controller::~chain_controller() {
   clear_pending();
   _db.flush();
   _fork_db.reset();
}
N
Nathan Hourt 已提交
1138

1139
void chain_controller::replay() {
1140
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
1141
   auto start = fc::time_point::now();
1142 1143 1144
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
1145 1146 1147 1148 1149
      return;
   }

   const auto last_block_num = last_block->block_num();

1150 1151 1152 1153 1154 1155
   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 已提交
1156 1157 1158 1159 1160 1161 1162 1163
      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();
1164 1165
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
1166

1167
   _db.set_revision(head_block_num());
1168
}
N
Nathan Hourt 已提交
1169

1170 1171 1172 1173
void chain_controller::spinup_db() {
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
D
Daniel Larimer 已提交
1174

1175 1176 1177 1178
      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 已提交
1179

1180
void chain_controller::spinup_fork_db()
N
Nathan Hourt 已提交
1181
{
1182 1183 1184 1185 1186 1187 1188 1189
   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 已提交
1190 1191
}

1192 1193
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
1194 1195 1196 1197
   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));
1198

1199
   utilities::rand::random rng(next_block.timestamp.sec_since_epoch());
1200 1201 1202 1203
   rng.shuffle(schedule);
   return schedule;
}

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

1207 1208
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time(b.timestamp);
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
1209
   missed_blocks--;
N
Nathan Hourt 已提交
1210

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

N
Nathan Hourt 已提交
1214
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
1215
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
1216
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
1217 1218 1219 1220 1221 1222
         /*
         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) );
            */

1223
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
1224 1225 1226 1227 1228 1229
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
1230
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
1231 1232 1233
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
1234
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
1235 1236 1237 1238 1239 1240 1241 1242 1243
      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 已提交
1244 1245 1246 1247 1248
   });

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

1249
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
1250 1251
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1252
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( new_block.timestamp );
N
Nathan Hourt 已提交
1253

1254
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
1255 1256 1257 1258 1259 1260
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

1261
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
1262 1263 1264 1265
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

N
Nathan Hourt 已提交
1266 1267 1268
   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 已提交
1269
                  [this](const AccountName& owner) { return &get_producer(owner); });
N
Nathan Hourt 已提交
1270

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

N
Nathan Hourt 已提交
1273
   size_t offset = EOS_PERCENT(producer_objs.size(), config::Percent100 - config::IrreversibleThresholdPercent);
1274 1275
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
1276
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
1277
      });
N
Nathan Hourt 已提交
1278

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

1281
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
1282
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
1283
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1284
      });
N
Nathan Hourt 已提交
1285
   }
1286 1287

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1288
   auto old_last_irreversible_block = _block_log.head();
1289 1290 1291 1292
   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();
1293

1294 1295 1296 1297 1298 1299
   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);
1300
         _block_log.append(*block);
1301
      }
N
Nathan Hourt 已提交
1302 1303 1304

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

1308
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1309 1310 1311
{ 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.
1312
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1313 1314 1315
   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());
1316 1317 1318 1319 1320 1321 1322

   //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 已提交
1323 1324 1325 1326
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

N
Nathan Hourt 已提交
1327
types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1328 1329
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1330
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1331
   const auto& gpo = _db.get<global_property_object>();
N
Nathan Hourt 已提交
1332
   return gpo.active_producers[current_aslot % gpo.active_producers.size()];
N
Nathan Hourt 已提交
1333 1334
}

1335
fc::time_point_sec chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
{
   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);
}

1356
uint32_t chain_controller::get_slot_at_time(fc::time_point_sec when)const
N
Nathan Hourt 已提交
1357 1358 1359 1360 1361 1362 1363
{
   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;
}

1364
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1365 1366
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1367
   return uint64_t(config::Percent100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1368 1369
}

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

1374
chain_initializer_interface::~chain_initializer_interface() {}
N
Nathan Hourt 已提交
1375

1376

D
Daniel Larimer 已提交
1377
ProcessedTransaction chain_controller::transaction_from_variant( const fc::variant& v )const {
1378 1379 1380 1381
   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 已提交
1382
   ProcessedTransaction result;
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
   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] );
1396
         GET_FIELD( vo, authorization, result.messages[i] );
1397 1398 1399 1400 1401 1402

         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() ) {
1403 1404
               result.messages[i].data = message_to_binary( result.messages[i].code, result.messages[i].type, data ); 
               /*
1405 1406 1407 1408 1409 1410 1411 1412
               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 );
               }
1413
               */
1414 1415 1416 1417
            }
         }
      }
   }
D
Daniel Larimer 已提交
1418 1419 1420
   if( vo.contains( "output" ) ) {
      const vector<variant>& outputs = vo["output"].get_array();
   }
1421 1422 1423 1424
   return result;
#undef GET_FIELD
}

1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
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 已提交
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446
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();
}
1447

D
Daniel Larimer 已提交
1448
fc::variant  chain_controller::transaction_to_variant( const ProcessedTransaction& trx )const {
1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
#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 );
1466
       SET_FIELD( msg_mvo, msg, authorization );
1467 1468 1469

       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 已提交
1470
          try {
D
Daniel Larimer 已提交
1471
             msg_mvo( "data", message_from_binary( msg.code, msg.type, msg.data ) ); 
D
Daniel Larimer 已提交
1472 1473 1474 1475
             msg_mvo( "hex_data", msg.data );
          } catch ( ... ) {
            SET_FIELD( msg_mvo, msg, data );
          }
1476 1477 1478 1479 1480 1481 1482 1483
       }
       else {
         SET_FIELD( msg_mvo, msg, data );
       }
       msgsv[i] = std::move( msgs[i] );
    }
    trx_mvo( "messages", std::move(msgsv) );

D
Daniel Larimer 已提交
1484 1485 1486 1487 1488 1489 1490 1491 1492
    /* 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 ) );

1493 1494 1495 1496
    return fc::variant( std::move( trx_mvo ) );
#undef SET_FIELD
}

1497

N
Nathan Hourt 已提交
1498
} }