chain_controller.cpp 62.3 KB
Newer Older
A
Andrianto Lie 已提交
1 2 3
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
N
Nathan Hourt 已提交
4
 */
N
Nathan Hourt 已提交
5

6
#include <eos/chain/chain_controller.hpp>
N
Nathan Hourt 已提交
7 8 9

#include <eos/chain/block_summary_object.hpp>
#include <eos/chain/global_property_object.hpp>
N
Nathan Hourt 已提交
10 11
#include <eos/chain/key_value_object.hpp>
#include <eos/chain/action_objects.hpp>
12
#include <eos/chain/generated_transaction_object.hpp>
N
Nathan Hourt 已提交
13 14
#include <eos/chain/transaction_object.hpp>
#include <eos/chain/producer_object.hpp>
15
#include <eos/chain/permission_link_object.hpp>
N
Nathan Hourt 已提交
16
#include <eos/chain/authority_checker.hpp>
N
Nathan Hourt 已提交
17

18 19
#include <eos/chain/wasm_interface.hpp>

20 21
#include <eos/types/native.hpp>
#include <eos/types/generated.hpp>
22
#include <eos/types/AbiSerializer.hpp>
23

24
#include <eos/utilities/rand.hpp>
25

N
Nathan Hourt 已提交
26
#include <fc/smart_ref_impl.hpp>
N
Nathan Hourt 已提交
27 28 29
#include <fc/uint128.hpp>
#include <fc/crypto/digest.hpp>

30
#include <boost/range/algorithm/copy.hpp>
N
Nathan Hourt 已提交
31
#include <boost/range/algorithm_ext/is_sorted.hpp>
32
#include <boost/range/adaptor/transformed.hpp>
N
Nathan Hourt 已提交
33
#include <boost/range/adaptor/map.hpp>
N
Nathan Hourt 已提交
34 35 36 37

#include <fstream>
#include <functional>
#include <iostream>
38
#include <chrono>
N
Nathan Hourt 已提交
39 40 41

namespace eos { namespace chain {

42
bool chain_controller::is_known_block(const block_id_type& id)const
N
Nathan Hourt 已提交
43
{
44
   return _fork_db.is_known_block(id) || _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
45 46 47 48 49 50
}
/**
 * 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.
 */
51
bool chain_controller::is_known_transaction(const transaction_id_type& id)const
N
Nathan Hourt 已提交
52
{
53
   const auto& trx_idx = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
54 55 56
   return trx_idx.find( id ) != trx_idx.end();
}

57
block_id_type chain_controller::get_block_id_for_num(uint32_t block_num)const
N
Nathan Hourt 已提交
58
{ try {
59 60
   if (const auto& block = fetch_block_by_number(block_num))
      return block->id();
N
Nathan Hourt 已提交
61

62 63 64
   FC_THROW_EXCEPTION(unknown_block_exception, "Could not find block");
} FC_CAPTURE_AND_RETHROW((block_num)) }

65
optional<signed_block> chain_controller::fetch_block_by_id(const block_id_type& id)const
N
Nathan Hourt 已提交
66
{
67 68
   auto b = _fork_db.fetch_block(id);
   if(b) return b->data;
69
   return _block_log.read_block_by_id(id);
N
Nathan Hourt 已提交
70 71
}

72
optional<signed_block> chain_controller::fetch_block_by_number(uint32_t num)const
N
Nathan Hourt 已提交
73
{
74
   if (const auto& block = _block_log.read_block_by_num(num))
75 76
      return *block;

N
Nathan Hourt 已提交
77
   // Not in _block_log, so it must be since the last irreversible block. Grab it from _fork_db instead
78 79 80 81 82 83 84 85
   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 已提交
86 87 88
   return optional<signed_block>();
}

89
const SignedTransaction& chain_controller::get_recent_transaction(const transaction_id_type& trx_id) const
N
Nathan Hourt 已提交
90
{
91
   auto& index = _db.get_index<transaction_multi_index, by_trx_id>();
N
Nathan Hourt 已提交
92 93 94 95 96
   auto itr = index.find(trx_id);
   FC_ASSERT(itr != index.end());
   return itr->trx;
}

97
std::vector<block_id_type> chain_controller::get_block_ids_on_fork(block_id_type head_of_fork) const
N
Nathan Hourt 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
{
  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;
}

115 116 117 118 119 120 121
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 已提交
122 123 124 125 126 127
/**
 * 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.
 */
128
bool chain_controller::push_block(const signed_block& new_block, uint32_t skip)
D
Daniel Larimer 已提交
129 130 131
{ try {
   return with_skip_flags( skip, [&](){ 
      return without_pending_transactions( [&]() {
132
         return _db.with_write_lock( [&]() {
133 134
            return _push_block(new_block);
         } );
N
Nathan Hourt 已提交
135 136
      });
   });
137
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
138

139
bool chain_controller::_push_block(const signed_block& new_block)
N
Nathan Hourt 已提交
140
{ try {
N
Nathan Hourt 已提交
141
   uint32_t skip = _skip_flags;
142
   if (!(skip&skip_fork_db)) {
N
Nathan Hourt 已提交
143
      /// TODO: if the block is greater than the head block and before the next maintenance interval
N
Nathan Hourt 已提交
144 145 146 147
      // 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.
148
      if (new_head->data.previous != head_block_id()) {
N
Nathan Hourt 已提交
149 150
         //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
151 152
         if (new_head->data.block_num() > head_block_num()) {
            wlog("Switching to fork: ${id}", ("id",new_head->data.id()));
N
Nathan Hourt 已提交
153 154 155
            auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());

            // pop blocks until we hit the forked block
156
            while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
157 158 159
               pop_block();

            // push all blocks on the new fork
160 161
            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 已提交
162 163
                optional<fc::exception> except;
                try {
164
                   auto session = _db.start_undo_session(true);
165
                   apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
166 167
                   session.push();
                }
168 169 170
                catch (const fc::exception& e) { except = e; }
                if (except) {
                   wlog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
N
Nathan Hourt 已提交
171
                   // remove the rest of branches.first from the fork_db, those blocks are invalid
172 173
                   while (ritr != branches.first.rend()) {
                      _fork_db.remove((*ritr)->data.id());
N
Nathan Hourt 已提交
174 175
                      ++ritr;
                   }
176
                   _fork_db.set_head(branches.second.front());
N
Nathan Hourt 已提交
177 178

                   // pop all blocks from the bad fork
179
                   while (head_block_id() != branches.second.back()->data.previous)
N
Nathan Hourt 已提交
180 181 182
                      pop_block();

                   // restore all blocks from the good fork
183
                   for (auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr) {
184
                      auto session = _db.start_undo_session(true);
185
                      apply_block((*ritr)->data, skip);
N
Nathan Hourt 已提交
186 187 188 189 190 191 192 193 194 195 196 197
                      session.push();
                   }
                   throw *except;
                }
            }
            return true;
         }
         else return false;
      }
   }

   try {
198
      auto session = _db.start_undo_session(true);
199
      auto exec_start = std::chrono::high_resolution_clock::now();
N
Nathan Hourt 已提交
200
      apply_block(new_block, skip);
201 202 203 204 205 206
      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 已提交
207 208
            for (const auto& thread : cycle)
               trxcount += thread.user_input.size();
209 210 211 212 213 214
         ilog( "${producer} #${num} @${time}  | ${trxcount} trx, ${pending} pending, exectime_ms=${extm}", 
            ("producer", new_block.producer) 
            ("time", new_block.timestamp)
            ("num", new_block.block_num())
            ("trxcount", trxcount)
            ("pending", _pending_transactions.size())
M
matthew 已提交
215
            ("extm", exec_ms.count())
216 217
         );
      }
N
Nathan Hourt 已提交
218 219 220 221 222 223 224 225
      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;
226
} FC_CAPTURE_AND_RETHROW((new_block)) }
N
Nathan Hourt 已提交
227 228 229 230 231 232 233 234 235 236

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

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

252
   auto temp_session = _db.start_undo_session(true);
253
   validate_referenced_accounts(trx);
N
Nathan Hourt 已提交
254
   check_transaction_authorization(trx);
255
   auto pt = apply_transaction(trx);
D
Daniel Larimer 已提交
256
   _pending_transactions.push_back(trx);
N
Nathan Hourt 已提交
257 258 259 260 261 262

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

   // notify anyone listening to pending transactions
263
   on_pending_transaction(trx); /// TODO move this to apply... ??? why... 
264 265

   return pt;
N
Nathan Hourt 已提交
266 267
}

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

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

N
Nathan Hourt 已提交
299
   const auto& producer_obj = get_producer(scheduled_producer);
N
Nathan Hourt 已提交
300 301 302 303

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

304
  
305 306 307 308 309 310 311 312 313 314 315 316 317 318
   //
   // 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();
   _pending_tx_session = _db.start_undo_session(true);

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

321
   vector<pending_transaction> pending;
B
Bart Wyatt 已提交
322
   std::set<transaction_id_type> invalid_pending;
323 324 325
   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 已提交
326
      pending.emplace_back(std::reference_wrapper<const GeneratedTransaction> {gt.trx});
327 328
   }
   
B
Bart Wyatt 已提交
329 330
   for(const auto& st: _pending_transactions) {
      pending.emplace_back(std::reference_wrapper<const SignedTransaction> {st});
331 332
   }

333
   auto schedule = scheduler(pending, get_global_properties());
N
Nathan Hourt 已提交
334

335 336 337 338 339 340 341 342 343 344 345 346
   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;
347 348 349 350 351 352
       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 已提交
353 354
             if (trx.contains<std::reference_wrapper<const SignedTransaction>>()) {
                const auto& t = trx.get<std::reference_wrapper<const SignedTransaction>>().get();
B
Bart Wyatt 已提交
355
                validate_referenced_accounts(t);
356
                check_transaction_authorization(t);
357
                auto processed = apply_transaction(t);
358
                block_thread.user_input.emplace_back(processed);
B
Bart Wyatt 已提交
359
             } else if (trx.contains<std::reference_wrapper<const GeneratedTransaction>>()) {
360 361 362
                const auto& t = trx.get<std::reference_wrapper<const GeneratedTransaction>>().get();
                auto processed = apply_transaction(t);
                block_thread.generated_input.emplace_back(processed);
363 364 365 366 367 368 369 370 371 372
             } 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 已提交
373
             elog( "Transaction was not processed while generating block due to ${e}", ("e", e) );
B
Bart Wyatt 已提交
374
             if (trx.contains<std::reference_wrapper<const SignedTransaction>>()) {
B
Bart Wyatt 已提交
375
                const auto& t = trx.get<std::reference_wrapper<const SignedTransaction>>().get();
B
Bart Wyatt 已提交
376
                wlog( "The transaction was ${t}", ("t", t ) );
B
Bart Wyatt 已提交
377
                invalid_pending.emplace(t.id());
B
Bart Wyatt 已提交
378 379
             } else if (trx.contains<std::reference_wrapper<const GeneratedTransaction>>()) {
                wlog( "The transaction was ${t}", ("t", trx.get<std::reference_wrapper<const GeneratedTransaction>>().get()) );
380 381 382
             } 
             invalid_transaction_count++;
          }
383
       }
N
Nathan Hourt 已提交
384

385
       if (!(block_thread.generated_input.empty() && block_thread.user_input.empty())) {
386 387
          block_thread.generated_input.shrink_to_fit();
          block_thread.user_input.shrink_to_fit();
388 389 390
          block_cycle.emplace_back(std::move(block_thread));
       }
     }
391

392 393 394
     if (!block_cycle.empty()) {
        pending_block.cycles.emplace_back(std::move(block_cycle));
     }
N
Nathan Hourt 已提交
395
   }
396
   
397
   size_t postponed_tx_count = pending.size() - valid_transaction_count - invalid_transaction_count;
N
Nathan Hourt 已提交
398 399 400 401 402
   if( postponed_tx_count > 0 )
   {
      wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
   }

403 404 405
   if( invalid_transaction_count > 0 )
   {
      wlog( "Postponed ${n} transactions errors when processing", ("n", invalid_transaction_count) );
B
Bart Wyatt 已提交
406 407 408 409 410 411 412 413 414 415 416 417

      // 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;
            }
         }
      }
418 419
   }

N
Nathan Hourt 已提交
420 421 422 423
   _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 已提交
424
   // _pending_transactions now consists of the set of postponed transactions.
N
Nathan Hourt 已提交
425 426 427 428 429 430
   // 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 已提交
431

432
   pending_block.producer = producer_obj.owner;
N
Nathan Hourt 已提交
433

434 435 436 437 438 439
   // 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 已提交
440 441 442 443
   if( !(skip & skip_producer_signature) )
      pending_block.sign( block_signing_private_key );

   // TODO:  Move this to _push_block() so session is restored.
444
   /*
N
Nathan Hourt 已提交
445 446 447 448
   if( !(skip & skip_block_size_check) )
   {
      FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
   }
449
   */
N
Nathan Hourt 已提交
450

451
   // push_block( pending_block, skip );
N
Nathan Hourt 已提交
452 453

   return pending_block;
N
Nathan Hourt 已提交
454
} FC_CAPTURE_AND_RETHROW( (producer) ) }
N
Nathan Hourt 已提交
455 456

/**
N
Nathan Hourt 已提交
457
 * Removes the most recent block from the database and undoes any changes it made.
N
Nathan Hourt 已提交
458
 */
459
void chain_controller::pop_block()
N
Nathan Hourt 已提交
460 461 462 463 464 465 466
{ 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();
467
   _db.undo();
N
Nathan Hourt 已提交
468 469
} FC_CAPTURE_AND_RETHROW() }

470
void chain_controller::clear_pending()
N
Nathan Hourt 已提交
471
{ try {
D
Daniel Larimer 已提交
472
   _pending_transactions.clear();
N
Nathan Hourt 已提交
473 474 475 476 477
   _pending_tx_session.reset();
} FC_CAPTURE_AND_RETHROW() }

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

478
void chain_controller::apply_block(const signed_block& next_block, uint32_t skip)
N
Nathan Hourt 已提交
479 480
{
   auto block_num = next_block.block_num();
481 482 483 484 485
   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 已提交
486

487
      if (_checkpoints.rbegin()->first >= block_num)
N
Nathan Hourt 已提交
488 489
         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
   }
N
Nathan Hourt 已提交
490 491 492 493 494 495

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

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 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
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();
}

550
template<typename T>
551 552 553 554 555
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)));
556 557 558
}

template<typename T>
559 560
void check_output(const vector<T>& expected, const vector<T>& actual, const path_cons_list& path) {
   check_output(expected.size(), actual.size(), path(".size()"));
561 562 563
   for(int idx=0; idx < expected.size(); idx++) {
      const auto &expected_element = expected.at(idx);
      const auto &actual_element = actual.at(idx);
564
      check_output(expected_element, actual_element, path(idx));
565 566 567
   }
}

568 569 570 571 572 573 574 575
template<typename T>
void check_output(const fc::optional<T>& expected, const fc::optional<T>& actual, const path_cons_list& path) {
   check_output(expected.valid(), actual.valid(), path(".valid()"));
   if (expected.valid()) {
      check_output(*expected, *actual, path);
   }
}

576
template<>
577 578 579 580 581
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()"));
582 583 584
}

template<>
585 586 587
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"));
588 589 590
}

template<>
591 592 593 594 595
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"));
596 597 598
}

template<>
599
void check_output(const MessageOutput& expected, const MessageOutput& actual, const path_cons_list& path);
600 601

template<>
602 603 604
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"));
605 606 607
}

template<>
608 609 610 611 612 613
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"));
614 615 616 617
}


template<>
618
void check_output(const InlineTransaction& expected, const InlineTransaction& actual, const path_cons_list& path) {
619 620
   check_output<Transaction>(expected, actual, path);
   check_output(expected.output, actual.output, path(".output"));
621 622 623
}

template<>
624 625 626
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);
627 628 629
}

template<>
630 631
void check_output(const MessageOutput& expected, const MessageOutput& actual, const path_cons_list& path) {
   check_output(expected.notify, actual.notify, path(".notify"));
632 633
   check_output(expected.inline_transaction, actual.inline_transaction, path(".inline_transaction"));
   check_output(expected.deferred_transactions, actual.deferred_transactions, path(".deferred_transactions"));
634 635 636
}

template<typename T>
637
void chain_controller::check_transaction_output(const T& expected, const T& actual, const path_cons_list& path)const {
638
   if (!(_skip_flags & skip_output_check)) {
639
      check_output(expected.output, actual.output, path(".output"));
640 641 642
   }
}

643
void chain_controller::_apply_block(const signed_block& next_block)
N
Nathan Hourt 已提交
644 645
{ try {
   uint32_t next_block_num = next_block.block_num();
N
Nathan Hourt 已提交
646
   uint32_t skip = _skip_flags;
N
Nathan Hourt 已提交
647

648 649 650
   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 已提交
651 652

   const producer_object& signing_producer = validate_block_header(skip, next_block);
N
Nathan Hourt 已提交
653 654 655
   
   for (const auto& cycle : next_block.cycles)
      for (const auto& thread : cycle)
656 657
         for (const auto& trx : thread.user_input) {
            validate_referenced_accounts(trx);
658 659 660
            // Check authorization, and allow irrelevant signatures.
            // If the block producer let it slide, we'll roll with it.
            check_transaction_authorization(trx, true);
661
         }
N
Nathan Hourt 已提交
662

N
Nathan Hourt 已提交
663 664 665 666 667 668
   /* 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.
    */
669
   auto root_path = path_cons_list("next_block.cycles");
670 671
   for (int c_idx = 0; c_idx < next_block.cycles.size(); c_idx++) {
      const auto& cycle = next_block.cycles.at(c_idx);
672
      auto c_path = path_cons_list(c_idx, root_path);
673 674 675

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

678
         auto gen_path = path_cons_list(".generated_input", t_path);
679 680 681 682
         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);
683
            check_transaction_output(ptrx, processed, gen_path(p_idx));
N
Nathan Hourt 已提交
684
         }
685

686
         auto user_path = path_cons_list(".user_input", t_path);
687 688
         for(int p_idx = 0; p_idx < thread.user_input.size(); p_idx++ ) {
            const auto& ptrx = thread.user_input.at(p_idx);
689 690
            const SignedTransaction& trx = ptrx;
            auto processed = apply_transaction(trx);
691
            check_transaction_output(ptrx, processed, user_path(p_idx));
692 693 694
         }
      }
   }
N
Nathan Hourt 已提交
695

696
   update_global_properties(next_block);
N
Nathan Hourt 已提交
697 698 699 700 701 702 703 704
   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
705
   // TODO: do this outside the write lock...? 
N
Nathan Hourt 已提交
706 707 708 709
   applied_block( next_block ); //emit

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

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
namespace {

  auto make_get_permission(const chainbase::database& db) {
     return [&db](const types::AccountPermission& permission) {
        auto key = boost::make_tuple(permission.account, permission.permission);
        return db.get<permission_object, by_owner>(key);
     };
  }

  auto make_authority_checker(const chainbase::database& db, const flat_set<public_key_type>& signingKeys) {
     auto getPermission = make_get_permission(db);
     auto getAuthority = [getPermission](const types::AccountPermission& permission) {
        return getPermission(permission).auth;
     };
     auto depthLimit = db.get<global_property_object>().configuration.authDepthLimit;
     return MakeAuthorityChecker(std::move(getAuthority), depthLimit, signingKeys);
  }

}

flat_set<public_key_type> chain_controller::get_required_keys(const SignedTransaction& trx, const flat_set<public_key_type>& candidateKeys)const {
   auto checker = make_authority_checker(_db, candidateKeys);

   for (const auto& message : trx.messages) {
      for (const auto& declaredAuthority : message.authorization) {
         if (!checker.satisfied(declaredAuthority)) {
            EOS_ASSERT(checker.satisfied(declaredAuthority), tx_missing_sigs,
                       "Transaction declares authority '${auth}', but does not have signatures for it.",
                       ("auth", declaredAuthority));
         }
      }
   }

   return checker.used_keys();
}

746
void chain_controller::check_transaction_authorization(const SignedTransaction& trx, bool allow_unused_signatures)const {
N
Nathan Hourt 已提交
747
   if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
748
      //ilog("Skipping auth and sigs checks");
N
Nathan Hourt 已提交
749 750 751
      return;
   }

752
   auto getPermission = make_get_permission(_db);
N
Nathan Hourt 已提交
753
#warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
754
   auto checker = make_authority_checker(_db, trx.get_signature_keys(chain_id_type{}));
N
Nathan Hourt 已提交
755 756 757 758 759 760 761 762

   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 已提交
763 764
                       "Message declares irrelevant authority '${auth}'; minimum authority is ${min}",
                       ("auth", declaredAuthority)("min", minimumPermission.name));
N
Nathan Hourt 已提交
765 766 767 768 769 770 771
         }
         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 已提交
772

773
   if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
774 775
      EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
                 "Transaction bears irrelevant signatures from these keys: ${keys}", ("keys", checker.unused_keys()));
N
Nathan Hourt 已提交
776 777
}

778
void chain_controller::validate_scope( const Transaction& trx )const {
779
   EOS_ASSERT(trx.scope.size() + trx.readscope.size() > 0, transaction_exception, "No scope specified by transaction" );
780 781
   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" );
782 783 784 785 786 787 788 789
   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" );
790 791
}

N
Nathan Hourt 已提交
792 793 794 795
const permission_object& chain_controller::lookup_minimum_permission(types::AccountName authorizer_account,
                                                                    types::AccountName code_account,
                                                                    types::FuncName type) const {
   try {
796 797 798 799 800 801 802 803 804 805 806 807 808 809
      // 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 已提交
810 811 812
   } FC_CAPTURE_AND_RETHROW((authorizer_account)(code_account)(type))
}

813
void chain_controller::validate_uniqueness( const SignedTransaction& trx )const {
N
Nathan Hourt 已提交
814
   if( !should_check_for_duplicate_transactions() ) return;
N
Nathan Hourt 已提交
815

N
Nathan Hourt 已提交
816
   auto transaction = _db.find<transaction_object, by_trx_id>(trx.id());
817
   EOS_ASSERT(transaction == nullptr, tx_duplicate, "Transaction is not unique");
818
}
819

820 821 822 823
void chain_controller::validate_uniqueness( const GeneratedTransaction& trx )const {
   if( !should_check_for_duplicate_transactions() ) return;
}

824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
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;
   });
}     



840
void chain_controller::validate_tapos(const Transaction& trx)const {
N
Nathan Hourt 已提交
841
   if (!should_check_tapos()) return;
842

843
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.refBlockNum);
844 845

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

851
void chain_controller::validate_referenced_accounts(const Transaction& trx)const {
N
Nathan Hourt 已提交
852 853 854
   for (const auto& scope : trx.scope)
      require_account(scope);
   for (const auto& msg : trx.messages) {
855
      require_account(msg.code);
N
Nathan Hourt 已提交
856 857
      for (const auto& auth : msg.authorization)
         require_account(auth.account);
858 859
   }
}
D
Daniel Larimer 已提交
860

861
void chain_controller::validate_expiration(const Transaction& trx) const
862 863
{ try {
   fc::time_point_sec now = head_block_time();
864
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
865

866
   EOS_ASSERT(trx.expiration <= now + int32_t(chain_configuration.maxTrxLifetime),
867 868
              transaction_exception, "Transaction expiration is too far in the future",
              ("trx.expiration",trx.expiration)("now",now)
869
              ("max_til_exp",chain_configuration.maxTrxLifetime));
870 871 872
   EOS_ASSERT(now <= trx.expiration, transaction_exception, "Transaction is expired",
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
873

874

875
void chain_controller::process_message(const Transaction& trx, AccountName code,
876
                                       const Message& message, MessageOutput& output, apply_context* parent_context) {
877
   apply_context apply_ctx(*this, _db, trx, message, code);
N
Nathan Hourt 已提交
878 879
   apply_message(apply_ctx);

880 881 882
   output.notify.reserve( apply_ctx.notified.size() );

   for( uint32_t i = 0; i < apply_ctx.notified.size(); ++i ) {
N
Nathan Hourt 已提交
883
      try {
884 885
         auto notify_code = apply_ctx.notified[i];
         output.notify.push_back( {notify_code} );
886
         process_message(trx, notify_code, message, output.notify.back().output, &apply_ctx);
887 888 889
      } FC_CAPTURE_AND_RETHROW((apply_ctx.notified[i]))
   }

890
   // combine inline messages and process
891 892 893 894
   if (apply_ctx.inline_messages.size() > 0) {
      output.inline_transaction = InlineTransaction(trx);
      (*output.inline_transaction).messages = std::move(apply_ctx.inline_messages);
   }
895 896 897 898 899 900 901

   for( auto& asynctrx : apply_ctx.deferred_transactions ) {
      digest_type::encoder enc;
      fc::raw::pack( enc, trx );
      fc::raw::pack( enc, asynctrx );
      auto id = enc.result();
      auto gtrx = GeneratedTransaction(id, asynctrx);
902

903
      _db.create<generated_transaction_object>([&](generated_transaction_object& transaction) {
904
         transaction.trx = gtrx;
905 906 907
         transaction.status = generated_transaction_object::PENDING;
      });

908
      output.deferred_transactions.emplace_back( gtrx );
N
Nathan Hourt 已提交
909
   }
910 911 912 913 914 915 916 917 918 919 920 921

   // 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 已提交
922
}
923

924
void chain_controller::apply_message(apply_context& context)
925
{ try {
D
Daniel Larimer 已提交
926 927
    /// context.code => the execution namespace
    /// message.code / message.type => Event
928
    const auto& m = context.msg;
D
Daniel Larimer 已提交
929
    auto contract_handlers_itr = apply_handlers.find(context.code);
930 931 932
    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 已提交
933
          message_handler_itr->second(context);
934 935 936
          return;
       }
    }
D
Daniel Larimer 已提交
937
    const auto& recipient = _db.get<account_object,by_name>(context.code);
938
    if (recipient.code.size()) {
939
       //idump((context.code)(context.msg.type));
940 941 942 943 944 945 946
       const uint32_t execution_time =
          _skip_flags | received_block
             ? _rcvd_block_trans_execution_time
             : _skip_flags | created_block
               ? _create_block_trans_execution_time
               : _trans_execution_time;
       wasm_interface::get().apply(context, execution_time);
947
    }
948

N
Nathan Hourt 已提交
949
} FC_CAPTURE_AND_RETHROW((context.msg)) }
N
Nathan Hourt 已提交
950

951 952
template<typename T>
typename T::Processed chain_controller::apply_transaction(const T& trx)
953
{ try {
954
   validate_transaction(trx);
955
   record_transaction(trx);
956
   return process_transaction( trx, 0, fc::time_point::now());
957

N
Nathan Hourt 已提交
958
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
959

960 961 962
/**
 *  @pre the transaction is assumed valid and all signatures / duplicate checks have bee performed
 */
963
template<typename T>
964
typename T::Processed chain_controller::process_transaction( const T& trx, int depth, const fc::time_point& start_time ) 
965
{ try {
966 967 968 969 970 971 972
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
   EOS_ASSERT((fc::time_point::now() - start_time).count() < chain_configuration.maxTrxRuntime, checktime_exceeded,
      "Transaction exceeded maximum total transaction time of ${limit}ms", ("limit", chain_configuration.maxTrxRuntime));

   EOS_ASSERT(depth < chain_configuration.inlineDepthLimit, tx_resource_exhausted,
      "Transaction exceeded maximum inline recursion depth of ${limit}", ("limit", chain_configuration.inlineDepthLimit));

973
   typename T::Processed ptrx( trx );
974 975
   ptrx.output.resize( trx.messages.size() );

976
   for( uint32_t i = 0; i < trx.messages.size(); ++i ) {
977 978
      auto& output = ptrx.output[i];
      process_message(trx, trx.messages[i].code, trx.messages[i], output);
979
      if (output.inline_transaction.valid() ) {
980 981
         const Transaction& trx = *output.inline_transaction;
         output.inline_transaction = process_transaction(PendingInlineTransaction(trx), depth + 1, start_time);
982
      }
983 984 985 986 987
   }

   return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }

N
Nathan Hourt 已提交
988 989 990 991
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 已提交
992

993
const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const {
994 995 996 997
   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 已提交
998
   if (next_block.block_num() % config::BlocksPerRound != 0) {
999 1000
      EOS_ASSERT(next_block.producer_changes.empty(), block_validate_exception,
                 "Producer changes may only occur at the end of a round.");
N
Nathan Hourt 已提交
1001 1002 1003 1004 1005 1006 1007
   } 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 已提交
1008
   const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp)));
N
Nathan Hourt 已提交
1009

N
Nathan Hourt 已提交
1010
   if(!(skip&skip_producer_signature))
1011 1012 1013
      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 已提交
1014

1015
   if(!(skip&skip_producer_schedule_check)) {
1016 1017 1018
      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 已提交
1019 1020 1021 1022 1023
   }

   return producer;
}

1024
void chain_controller::create_block_summary(const signed_block& next_block) {
1025
   auto sid = next_block.block_num() & 0xffff;
1026
   _db.modify( _db.get<block_summary_object,by_id>(sid), [&](block_summary_object& p) {
1027 1028
         p.block_id = next_block.id();
   });
N
Nathan Hourt 已提交
1029 1030
}

1031
void chain_controller::update_global_properties(const signed_block& b) {
1032 1033
   // If we're at the end of a round, update the BlockchainConfiguration, producer schedule
   // and "producers" special account authority
N
Nathan Hourt 已提交
1034
   if (b.block_num() % config::BlocksPerRound == 0) {
1035
      auto schedule = calculate_next_round(b);
1036 1037
      auto config = _admin->get_blockchain_configuration(_db, schedule);

1038 1039
      const auto& gpo = get_global_properties();
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
1040 1041 1042
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
1043

1044
      auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {});
1045
      for(auto& name : gpo.active_producers) {
1046
         active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1});
1047 1048
      }

1049
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::ProducersAccountName, config::ActiveName) );
1050 1051 1052
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
1053 1054 1055
   }
}

1056
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
1057
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
1058 1059 1060
      _checkpoints[i.first] = i.second;
}

1061
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
1062 1063 1064
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

1065
const global_property_object& chain_controller::get_global_properties()const {
1066
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
1067 1068
}

1069
const dynamic_global_property_object&chain_controller::get_dynamic_global_properties() const {
1070
   return _db.get<dynamic_global_property_object>();
N
Nathan Hourt 已提交
1071 1072
}

1073
time_point_sec chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
1074 1075 1076
   return get_dynamic_global_properties().time;
}

1077
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
1078 1079 1080
   return get_dynamic_global_properties().head_block_number;
}

1081
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
1082 1083 1084
   return get_dynamic_global_properties().head_block_id;
}

N
Nathan Hourt 已提交
1085
types::AccountName chain_controller::head_block_producer() const {
1086 1087 1088
   auto b = _fork_db.fetch_block(head_block_id());
   if( b ) return b->data.producer;

N
Nathan Hourt 已提交
1089
   if (auto head_block = fetch_block_by_id(head_block_id()))
1090
      return head_block->producer;
N
Nathan Hourt 已提交
1091 1092 1093
   return {};
}

N
Nathan Hourt 已提交
1094 1095 1096 1097
const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const {
   return _db.get<producer_object, by_owner>(ownerName);
}

1098
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
1099
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
1100 1101
}

1102
void chain_controller::initialize_indexes() {
1103 1104
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
1105
   _db.add_index<permission_link_index>();
1106 1107
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
M
Matias Romeo 已提交
1108
   _db.add_index<keystr_value_index>();
1109
   _db.add_index<key128x128_value_index>();
1110
   _db.add_index<key64x64x64_value_index>();
1111 1112 1113 1114 1115

   _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>();
1116
   _db.add_index<generated_transaction_multi_index>();
1117
   _db.add_index<producer_multi_index>();
N
Nathan Hourt 已提交
1118 1119
}

1120
void chain_controller::initialize_chain(chain_initializer_interface& starter)
N
Nathan Hourt 已提交
1121
{ try {
1122
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
1123 1124 1125 1126 1127
      _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 已提交
1128

1129
         // Create global properties
N
Nathan Hourt 已提交
1130 1131 1132
         _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();
1133 1134
         });
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
1135
            p.time = initial_timestamp;
1136 1137
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
1138

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

1143
         auto messages = starter.prepare_database(*this, _db);
1144 1145
         std::for_each(messages.begin(), messages.end(), [&](const Message& m) { 
            MessageOutput output;
1146
            ProcessedTransaction trx; /// dummy tranaction required for scope validation
1147
            std::sort(trx.scope.begin(), trx.scope.end() );
1148
            with_skip_flags(skip_scope_check | skip_transaction_signatures | skip_authority_check | received_block, [&](){
1149
               process_message(trx,m.code,m,output); 
1150
            });
1151
         });
1152 1153
      });
   }
N
Nathan Hourt 已提交
1154 1155
} FC_CAPTURE_AND_RETHROW() }

1156
chain_controller::chain_controller(database& database, fork_database& fork_db, block_log& blocklog,
1157 1158 1159 1160 1161
                                   chain_initializer_interface& starter, unique_ptr<chain_administration_interface> admin,
                                   uint32_t trans_execution_time, uint32_t rcvd_block_trans_execution_time,
                                   uint32_t create_block_trans_execution_time)
   : _db(database), _fork_db(fork_db), _block_log(blocklog), _admin(std::move(admin)), _trans_execution_time(trans_execution_time),
     _rcvd_block_trans_execution_time(rcvd_block_trans_execution_time), _create_block_trans_execution_time(create_block_trans_execution_time) {
N
Nathan Hourt 已提交
1162

1163
   initialize_indexes();
1164
   starter.register_types(*this, _db);
N
Nathan Hourt 已提交
1165 1166 1167 1168 1169 1170

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

1171 1172
   spinup_db();
   spinup_fork_db();
1173 1174 1175

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

1178 1179 1180 1181 1182
chain_controller::~chain_controller() {
   clear_pending();
   _db.flush();
   _fork_db.reset();
}
N
Nathan Hourt 已提交
1183

1184
void chain_controller::replay() {
1185
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
1186
   auto start = fc::time_point::now();
1187 1188 1189
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
1190 1191 1192 1193 1194
      return;
   }

   const auto last_block_num = last_block->block_num();

1195
   ilog("Replaying ${n} blocks...", ("n", last_block_num) );
1196 1197 1198 1199 1200
   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 已提交
1201 1202 1203 1204 1205
      apply_block(*block, skip_producer_signature |
                          skip_transaction_signatures |
                          skip_transaction_dupe_check |
                          skip_tapos_check |
                          skip_producer_schedule_check |
1206 1207
                          skip_authority_check |
                          received_block);
N
Nathan Hourt 已提交
1208 1209
   }
   auto end = fc::time_point::now();
1210 1211
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
1212

1213
   _db.set_revision(head_block_num());
1214
}
N
Nathan Hourt 已提交
1215

1216 1217 1218 1219
void chain_controller::spinup_db() {
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
D
Daniel Larimer 已提交
1220

1221 1222 1223 1224
      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 已提交
1225

1226
void chain_controller::spinup_fork_db()
N
Nathan Hourt 已提交
1227
{
1228 1229 1230 1231 1232 1233 1234 1235
   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 已提交
1236 1237
}

1238 1239
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
1240 1241 1242 1243
   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));
1244

1245
   utilities::rand::random rng(next_block.timestamp.sec_since_epoch());
1246 1247 1248 1249
   rng.shuffle(schedule);
   return schedule;
}

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

1253 1254
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time(b.timestamp);
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
1255
   missed_blocks--;
N
Nathan Hourt 已提交
1256

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

N
Nathan Hourt 已提交
1260
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
1261
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
1262
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
1263 1264 1265 1266 1267 1268
         /*
         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) );
            */

1269
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
1270 1271 1272 1273 1274 1275
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
1276
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
1277 1278 1279
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
1280
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
1281 1282 1283 1284 1285 1286 1287 1288 1289
      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 已提交
1290 1291 1292 1293 1294
   });

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

1295
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
1296 1297
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1298
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( new_block.timestamp );
N
Nathan Hourt 已提交
1299

1300
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
1301 1302 1303 1304 1305 1306
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

1307
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
1308 1309 1310 1311
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

N
Nathan Hourt 已提交
1312 1313 1314
   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 已提交
1315
                  [this](const AccountName& owner) { return &get_producer(owner); });
N
Nathan Hourt 已提交
1316

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

N
Nathan Hourt 已提交
1319
   size_t offset = EOS_PERCENT(producer_objs.size(), config::Percent100 - config::IrreversibleThresholdPercent);
1320 1321
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
1322
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
1323
      });
N
Nathan Hourt 已提交
1324

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

1327
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
1328
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
1329
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1330
      });
N
Nathan Hourt 已提交
1331
   }
1332 1333

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1334
   auto old_last_irreversible_block = _block_log.head();
1335 1336 1337 1338
   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();
1339

1340 1341 1342 1343 1344 1345
   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);
1346
         _block_log.append(*block);
1347
      }
N
Nathan Hourt 已提交
1348 1349 1350

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

1354
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1355 1356 1357
{ 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.
1358
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1359 1360 1361
   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());
1362 1363 1364 1365 1366 1367 1368

   //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 已提交
1369 1370 1371 1372
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

N
Nathan Hourt 已提交
1373
types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1374 1375
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1376
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1377
   const auto& gpo = _db.get<global_property_object>();
N
Nathan Hourt 已提交
1378
   return gpo.active_producers[current_aslot % gpo.active_producers.size()];
N
Nathan Hourt 已提交
1379 1380
}

1381
fc::time_point_sec chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401
{
   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);
}

1402
uint32_t chain_controller::get_slot_at_time(fc::time_point_sec when)const
N
Nathan Hourt 已提交
1403 1404 1405 1406 1407 1408 1409
{
   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;
}

1410
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1411 1412
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1413
   return uint64_t(config::Percent100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1414 1415
}

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

1420
chain_initializer_interface::~chain_initializer_interface() {}
N
Nathan Hourt 已提交
1421

1422

D
Daniel Larimer 已提交
1423
ProcessedTransaction chain_controller::transaction_from_variant( const fc::variant& v )const {
1424 1425 1426 1427
   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 已提交
1428
   ProcessedTransaction result;
1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441
   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] );
1442
         GET_FIELD( vo, authorization, result.messages[i] );
1443 1444 1445 1446 1447 1448

         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() ) {
1449 1450
               result.messages[i].data = message_to_binary( result.messages[i].code, result.messages[i].type, data ); 
               /*
1451 1452 1453 1454 1455 1456 1457 1458
               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 );
               }
1459
               */
1460 1461 1462 1463
            }
         }
      }
   }
D
Daniel Larimer 已提交
1464 1465 1466
   if( vo.contains( "output" ) ) {
      const vector<variant>& outputs = vo["output"].get_array();
   }
1467 1468 1469 1470
   return result;
#undef GET_FIELD
}

1471 1472
vector<char> chain_controller::message_to_binary( Name code, Name type, const fc::variant& obj )const 
{ try {
1473 1474 1475 1476 1477 1478 1479 1480 1481
   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>();
1482
} FC_CAPTURE_AND_RETHROW( (code)(type)(obj) ) }
D
Daniel Larimer 已提交
1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493
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();
}
1494

D
Daniel Larimer 已提交
1495
fc::variant  chain_controller::transaction_to_variant( const ProcessedTransaction& trx )const {
1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
#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 );
1513
       SET_FIELD( msg_mvo, msg, authorization );
1514 1515 1516

       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 已提交
1517
          try {
D
Daniel Larimer 已提交
1518
             msg_mvo( "data", message_from_binary( msg.code, msg.type, msg.data ) ); 
D
Daniel Larimer 已提交
1519 1520 1521 1522
             msg_mvo( "hex_data", msg.data );
          } catch ( ... ) {
            SET_FIELD( msg_mvo, msg, data );
          }
1523 1524 1525 1526 1527 1528 1529 1530
       }
       else {
         SET_FIELD( msg_mvo, msg, data );
       }
       msgsv[i] = std::move( msgs[i] );
    }
    trx_mvo( "messages", std::move(msgsv) );

D
Daniel Larimer 已提交
1531 1532 1533 1534 1535 1536 1537 1538 1539
    /* 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 ) );

1540 1541 1542 1543
    return fc::variant( std::move( trx_mvo ) );
#undef SET_FIELD
}

1544

N
Nathan Hourt 已提交
1545
} }