chain_controller.cpp 61.5 KB
Newer Older
N
Nathan Hourt 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright (c) 2017, Respective Authors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
N
Nathan Hourt 已提交
24

25
#include <eos/chain/chain_controller.hpp>
N
Nathan Hourt 已提交
26 27 28

#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();
228 229 230 231 232 233
         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 已提交
234
            ("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
   on_pending_transaction(trx); /// TODO move this to apply... ??? why... 
283 284

   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
void check_output(const InlineTransaction& expected, const InlineTransaction& actual, const path_cons_list& path) {
630 631
   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
void check_output(const MessageOutput& expected, const MessageOutput& actual, const path_cons_list& path) {
   check_output(expected.notify, actual.notify, path(".notify"));
643 644
   check_output(expected.inline_transaction, actual.inline_transaction, path(".inline_transaction"));
   check_output(expected.deferred_transactions, actual.deferred_transactions, path(".deferred_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 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
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();
}

757
void chain_controller::check_transaction_authorization(const SignedTransaction& trx, bool allow_unused_signatures)const {
N
Nathan Hourt 已提交
758
   if ((_skip_flags & skip_transaction_signatures) && (_skip_flags & skip_authority_check)) {
759
      //ilog("Skipping auth and sigs checks");
N
Nathan Hourt 已提交
760 761 762
      return;
   }

763
   auto getPermission = make_get_permission(_db);
N
Nathan Hourt 已提交
764
#warning TODO: Use a real chain_id here (where is this stored? Do we still need it?)
765
   auto checker = make_authority_checker(_db, trx.get_signature_keys(chain_id_type{}));
N
Nathan Hourt 已提交
766 767 768 769 770 771 772 773

   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 已提交
774 775
                       "Message declares irrelevant authority '${auth}'; minimum authority is ${min}",
                       ("auth", declaredAuthority)("min", minimumPermission.name));
N
Nathan Hourt 已提交
776 777 778 779 780 781 782
         }
         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 已提交
783

784
   if (!allow_unused_signatures && (_skip_flags & skip_transaction_signatures) == false)
785 786
      EOS_ASSERT(checker.all_keys_used(), tx_irrelevant_sig,
                 "Transaction bears irrelevant signatures from these keys: ${keys}", ("keys", checker.unused_keys()));
N
Nathan Hourt 已提交
787 788
}

789
void chain_controller::validate_scope( const Transaction& trx )const {
790
   EOS_ASSERT(trx.scope.size() + trx.readscope.size() > 0, transaction_exception, "No scope specified by transaction" );
791 792
   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" );
793 794 795 796 797 798 799 800
   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" );
801 802
}

N
Nathan Hourt 已提交
803 804 805 806
const permission_object& chain_controller::lookup_minimum_permission(types::AccountName authorizer_account,
                                                                    types::AccountName code_account,
                                                                    types::FuncName type) const {
   try {
807 808 809 810 811 812 813 814 815 816 817 818 819 820
      // 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 已提交
821 822 823
   } FC_CAPTURE_AND_RETHROW((authorizer_account)(code_account)(type))
}

824
void chain_controller::validate_uniqueness( const SignedTransaction& trx )const {
N
Nathan Hourt 已提交
825
   if( !should_check_for_duplicate_transactions() ) return;
N
Nathan Hourt 已提交
826

N
Nathan Hourt 已提交
827
   auto transaction = _db.find<transaction_object, by_trx_id>(trx.id());
828
   EOS_ASSERT(transaction == nullptr, tx_duplicate, "Transaction is not unique");
829
}
830

831 832 833 834
void chain_controller::validate_uniqueness( const GeneratedTransaction& trx )const {
   if( !should_check_for_duplicate_transactions() ) return;
}

835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
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;
   });
}     



851
void chain_controller::validate_tapos(const Transaction& trx)const {
N
Nathan Hourt 已提交
852
   if (!should_check_tapos()) return;
853

854
   const auto& tapos_block_summary = _db.get<block_summary_object>((uint16_t)trx.refBlockNum);
855 856

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

862
void chain_controller::validate_referenced_accounts(const Transaction& trx)const {
N
Nathan Hourt 已提交
863 864 865
   for (const auto& scope : trx.scope)
      require_account(scope);
   for (const auto& msg : trx.messages) {
866
      require_account(msg.code);
N
Nathan Hourt 已提交
867 868
      for (const auto& auth : msg.authorization)
         require_account(auth.account);
869 870
   }
}
D
Daniel Larimer 已提交
871

872
void chain_controller::validate_expiration(const Transaction& trx) const
873 874
{ try {
   fc::time_point_sec now = head_block_time();
875
   const BlockchainConfiguration& chain_configuration = get_global_properties().configuration;
876

877
   EOS_ASSERT(trx.expiration <= now + int32_t(chain_configuration.maxTrxLifetime),
878 879
              transaction_exception, "Transaction expiration is too far in the future",
              ("trx.expiration",trx.expiration)("now",now)
880
              ("max_til_exp",chain_configuration.maxTrxLifetime));
881 882 883
   EOS_ASSERT(now <= trx.expiration, transaction_exception, "Transaction is expired",
              ("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW((trx)) }
884

885

886
void chain_controller::process_message(const Transaction& trx, AccountName code,
887
                                       const Message& message, MessageOutput& output, apply_context* parent_context) {
888
   apply_context apply_ctx(*this, _db, trx, message, code);
N
Nathan Hourt 已提交
889 890
   apply_message(apply_ctx);

891 892 893
   output.notify.reserve( apply_ctx.notified.size() );

   for( uint32_t i = 0; i < apply_ctx.notified.size(); ++i ) {
N
Nathan Hourt 已提交
894
      try {
895 896
         auto notify_code = apply_ctx.notified[i];
         output.notify.push_back( {notify_code} );
897
         process_message(trx, notify_code, message, output.notify.back().output, &apply_ctx);
898 899 900
      } FC_CAPTURE_AND_RETHROW((apply_ctx.notified[i]))
   }

901 902 903 904 905 906 907 908 909 910 911 912 913 914
   // combine inline messages and process
   auto inline_transaction = PendingInlineTransaction(trx);
   inline_transaction.messages = std::move(apply_ctx.inline_messages);
   try {
      output.inline_transaction = process_transaction(inline_transaction);
   } FC_CAPTURE_AND_RETHROW((inline_transaction))
   

   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);
915

916
      _db.create<generated_transaction_object>([&](generated_transaction_object& transaction) {
917
         transaction.trx = gtrx;
918 919 920
         transaction.status = generated_transaction_object::PENDING;
      });

921
      output.deferred_transactions.emplace_back( gtrx );
N
Nathan Hourt 已提交
922
   }
923 924 925 926 927 928 929 930 931 932 933 934

   // 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 已提交
935
}
936

937
void chain_controller::apply_message(apply_context& context)
938
{ try {
D
Daniel Larimer 已提交
939 940
    /// context.code => the execution namespace
    /// message.code / message.type => Event
941
    const auto& m = context.msg;
D
Daniel Larimer 已提交
942
    auto contract_handlers_itr = apply_handlers.find(context.code);
943 944 945
    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 已提交
946
          message_handler_itr->second(context);
947 948 949
          return;
       }
    }
D
Daniel Larimer 已提交
950
    const auto& recipient = _db.get<account_object,by_name>(context.code);
951
    if (recipient.code.size()) {
952
       //idump((context.code)(context.msg.type));
953
       wasm_interface::get().apply(context);
954
    }
955

N
Nathan Hourt 已提交
956
} FC_CAPTURE_AND_RETHROW((context.msg)) }
N
Nathan Hourt 已提交
957

958 959
template<typename T>
typename T::Processed chain_controller::apply_transaction(const T& trx)
960
{ try {
961
   validate_transaction(trx);
962
   record_transaction(trx);
963 964
   return process_transaction( trx );

N
Nathan Hourt 已提交
965
} FC_CAPTURE_AND_RETHROW((trx)) }
N
Nathan Hourt 已提交
966

967 968 969
/**
 *  @pre the transaction is assumed valid and all signatures / duplicate checks have bee performed
 */
970 971
template<typename T>
typename T::Processed chain_controller::process_transaction( const T& trx ) 
972
{ try {
973
   typename T::Processed ptrx( trx );
974 975
   ptrx.output.resize( trx.messages.size() );

976 977
   for( uint32_t i = 0; i < trx.messages.size(); ++i ) {
      process_message(trx, trx.messages[i].code, trx.messages[i], ptrx.output[i] );
978 979 980 981 982
   }

   return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }

N
Nathan Hourt 已提交
983 984 985 986
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 已提交
987

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

N
Nathan Hourt 已提交
1005
   if(!(skip&skip_producer_signature))
1006 1007 1008
      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 已提交
1009

1010
   if(!(skip&skip_producer_schedule_check)) {
1011 1012 1013
      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 已提交
1014 1015 1016 1017 1018
   }

   return producer;
}

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

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

1033 1034
      const auto& gpo = get_global_properties();
      _db.modify(gpo, [schedule = std::move(schedule), config = std::move(config)] (global_property_object& gpo) {
1035 1036 1037
         gpo.active_producers = std::move(schedule);
         gpo.configuration = std::move(config);
      });
1038

1039
      auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {});
1040
      for(auto& name : gpo.active_producers) {
1041
         active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1});
1042 1043
      }

1044
      auto& po = _db.get<permission_object, by_owner>( boost::make_tuple(config::ProducersAccountName, config::ActiveName) );
1045 1046 1047
      _db.modify(po,[active_producers_authority] (permission_object& po) {
         po.auth = active_producers_authority;
      });
1048 1049 1050
   }
}

1051
void chain_controller::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts ) {
1052
   for (const auto& i : checkpts)
N
Nathan Hourt 已提交
1053 1054 1055
      _checkpoints[i.first] = i.second;
}

1056
bool chain_controller::before_last_checkpoint()const {
N
Nathan Hourt 已提交
1057 1058 1059
   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
}

1060
const global_property_object& chain_controller::get_global_properties()const {
1061
   return _db.get<global_property_object>();
N
Nathan Hourt 已提交
1062 1063
}

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

1068
time_point_sec chain_controller::head_block_time()const {
N
Nathan Hourt 已提交
1069 1070 1071
   return get_dynamic_global_properties().time;
}

1072
uint32_t chain_controller::head_block_num()const {
N
Nathan Hourt 已提交
1073 1074 1075
   return get_dynamic_global_properties().head_block_number;
}

1076
block_id_type chain_controller::head_block_id()const {
N
Nathan Hourt 已提交
1077 1078 1079
   return get_dynamic_global_properties().head_block_id;
}

N
Nathan Hourt 已提交
1080
types::AccountName chain_controller::head_block_producer() const {
1081 1082 1083
   auto b = _fork_db.fetch_block(head_block_id());
   if( b ) return b->data.producer;

N
Nathan Hourt 已提交
1084
   if (auto head_block = fetch_block_by_id(head_block_id()))
1085
      return head_block->producer;
N
Nathan Hourt 已提交
1086 1087 1088
   return {};
}

N
Nathan Hourt 已提交
1089 1090 1091 1092
const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const {
   return _db.get<producer_object, by_owner>(ownerName);
}

1093
uint32_t chain_controller::last_irreversible_block_num() const {
N
Nathan Hourt 已提交
1094
   return get_dynamic_global_properties().last_irreversible_block_num;
N
Nathan Hourt 已提交
1095 1096
}

1097
void chain_controller::initialize_indexes() {
1098 1099
   _db.add_index<account_index>();
   _db.add_index<permission_index>();
1100
   _db.add_index<permission_link_index>();
1101 1102
   _db.add_index<action_permission_index>();
   _db.add_index<key_value_index>();
1103
   _db.add_index<key128x128_value_index>();
1104
   _db.add_index<key64x64x64_value_index>();
1105 1106 1107 1108 1109

   _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>();
1110
   _db.add_index<generated_transaction_multi_index>();
1111
   _db.add_index<producer_multi_index>();
N
Nathan Hourt 已提交
1112 1113
}

1114
void chain_controller::initialize_chain(chain_initializer_interface& starter)
N
Nathan Hourt 已提交
1115
{ try {
1116
   if (!_db.find<global_property_object>()) {
N
Nathan Hourt 已提交
1117 1118 1119 1120 1121
      _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 已提交
1122

1123
         // Create global properties
N
Nathan Hourt 已提交
1124 1125 1126
         _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();
1127 1128
         });
         _db.create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
N
Nathan Hourt 已提交
1129
            p.time = initial_timestamp;
1130 1131
            p.recent_slots_filled = uint64_t(-1);
         });
N
Nathan Hourt 已提交
1132

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

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

1150 1151 1152
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 已提交
1153

1154
   initialize_indexes();
1155
   starter.register_types(*this, _db);
N
Nathan Hourt 已提交
1156 1157 1158 1159 1160 1161

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

1162 1163
   spinup_db();
   spinup_fork_db();
1164 1165 1166

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

1169 1170 1171 1172 1173
chain_controller::~chain_controller() {
   clear_pending();
   _db.flush();
   _fork_db.reset();
}
N
Nathan Hourt 已提交
1174

1175
void chain_controller::replay() {
1176
   ilog("Replaying blockchain");
N
Nathan Hourt 已提交
1177
   auto start = fc::time_point::now();
1178 1179 1180
   auto last_block = _block_log.read_head();
   if (!last_block) {
      elog("No blocks in block log; skipping replay");
N
Nathan Hourt 已提交
1181 1182 1183 1184 1185
      return;
   }

   const auto last_block_num = last_block->block_num();

1186
   ilog("Replaying ${n} blocks...", ("n", last_block_num) );
1187 1188 1189 1190 1191
   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 已提交
1192 1193 1194 1195 1196 1197 1198 1199
      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();
1200 1201
   ilog("Done replaying ${n} blocks, elapsed time: ${t} sec",
        ("n", head_block_num())("t",double((end-start).count())/1000000.0));
N
Nathan Hourt 已提交
1202

1203
   _db.set_revision(head_block_num());
1204
}
N
Nathan Hourt 已提交
1205

1206 1207 1208 1209
void chain_controller::spinup_db() {
   // Rewind the database to the last irreversible block
   _db.with_write_lock([&] {
      _db.undo_all();
D
Daniel Larimer 已提交
1210

1211 1212 1213 1214
      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 已提交
1215

1216
void chain_controller::spinup_fork_db()
N
Nathan Hourt 已提交
1217
{
1218 1219 1220 1221 1222 1223 1224 1225
   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 已提交
1226 1227
}

1228 1229
ProducerRound chain_controller::calculate_next_round(const signed_block& next_block) {
   auto schedule = _admin->get_next_round(_db);
N
Nathan Hourt 已提交
1230 1231 1232 1233
   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));
1234

1235
   utilities::rand::random rng(next_block.timestamp.sec_since_epoch());
1236 1237 1238 1239
   rng.shuffle(schedule);
   return schedule;
}

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

1243 1244
   uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time(b.timestamp);
   assert(missed_blocks != 0);
N
Nathan Hourt 已提交
1245
   missed_blocks--;
N
Nathan Hourt 已提交
1246

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

N
Nathan Hourt 已提交
1250
   for(uint32_t i = 0; i < missed_blocks; ++i) {
N
Nathan Hourt 已提交
1251
      const auto& producer_missed = get_producer(get_scheduled_producer(i+1));
1252
      if(producer_missed.owner != b.producer) {
N
Nathan Hourt 已提交
1253 1254 1255 1256 1257 1258
         /*
         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) );
            */

1259
         _db.modify( producer_missed, [&]( producer_object& w ) {
N
Nathan Hourt 已提交
1260 1261 1262 1263 1264 1265
           w.total_missed++;
         });
      }
   }

   // dynamic global properties updating
1266
   _db.modify( _dgp, [&]( dynamic_global_property_object& dgp ){
N
Nathan Hourt 已提交
1267 1268 1269
      dgp.head_block_number = b.block_num();
      dgp.head_block_id = b.id();
      dgp.time = b.timestamp;
1270
      dgp.current_producer = b.producer;
N
Nathan Hourt 已提交
1271 1272 1273 1274 1275 1276 1277 1278 1279
      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 已提交
1280 1281 1282 1283 1284
   });

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

1285
void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block)
N
Nathan Hourt 已提交
1286 1287
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1288
   uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( new_block.timestamp );
N
Nathan Hourt 已提交
1289

1290
   _db.modify( signing_producer, [&]( producer_object& _wit )
N
Nathan Hourt 已提交
1291 1292 1293 1294 1295 1296
   {
      _wit.last_aslot = new_block_aslot;
      _wit.last_confirmed_block_num = new_block.block_num();
   } );
}

1297
void chain_controller::update_last_irreversible_block()
N
Nathan Hourt 已提交
1298 1299 1300 1301
{
   const global_property_object& gpo = get_global_properties();
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();

N
Nathan Hourt 已提交
1302 1303 1304
   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 已提交
1305
                  [this](const AccountName& owner) { return &get_producer(owner); });
N
Nathan Hourt 已提交
1306

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

N
Nathan Hourt 已提交
1309
   size_t offset = EOS_PERCENT(producer_objs.size(), config::Percent100 - config::IrreversibleThresholdPercent);
1310 1311
   std::nth_element(producer_objs.begin(), producer_objs.begin() + offset, producer_objs.end(),
      [](const producer_object* a, const producer_object* b) {
N
Nathan Hourt 已提交
1312
         return a->last_confirmed_block_num < b->last_confirmed_block_num;
1313
      });
N
Nathan Hourt 已提交
1314

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

1317
   if (new_last_irreversible_block_num > dpo.last_irreversible_block_num) {
1318
      _db.modify(dpo, [&](dynamic_global_property_object& _dpo) {
N
Nathan Hourt 已提交
1319
         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
1320
      });
N
Nathan Hourt 已提交
1321
   }
1322 1323

   // Write newly irreversible blocks to disk. First, get the number of the last block on disk...
1324
   auto old_last_irreversible_block = _block_log.head();
1325 1326 1327 1328
   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();
1329

1330 1331 1332 1333 1334 1335
   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);
1336
         _block_log.append(*block);
1337
      }
N
Nathan Hourt 已提交
1338 1339 1340

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

1344
void chain_controller::clear_expired_transactions()
N
Nathan Hourt 已提交
1345 1346 1347
{ 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.
1348
   auto& transaction_idx = _db.get_mutable_index<transaction_multi_index>();
N
Nathan Hourt 已提交
1349 1350 1351
   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());
1352 1353 1354 1355 1356 1357 1358

   //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 已提交
1359 1360 1361 1362
} FC_CAPTURE_AND_RETHROW() }

using boost::container::flat_set;

N
Nathan Hourt 已提交
1363
types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)const
N
Nathan Hourt 已提交
1364 1365
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1366
   uint64_t current_aslot = dpo.current_absolute_slot + slot_num;
1367
   const auto& gpo = _db.get<global_property_object>();
N
Nathan Hourt 已提交
1368
   return gpo.active_producers[current_aslot % gpo.active_producers.size()];
N
Nathan Hourt 已提交
1369 1370
}

1371
fc::time_point_sec chain_controller::get_slot_time(uint32_t slot_num)const
N
Nathan Hourt 已提交
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
{
   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);
}

1392
uint32_t chain_controller::get_slot_at_time(fc::time_point_sec when)const
N
Nathan Hourt 已提交
1393 1394 1395 1396 1397 1398 1399
{
   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;
}

1400
uint32_t chain_controller::producer_participation_rate()const
N
Nathan Hourt 已提交
1401 1402
{
   const dynamic_global_property_object& dpo = get_dynamic_global_properties();
N
Nathan Hourt 已提交
1403
   return uint64_t(config::Percent100) * __builtin_popcountll(dpo.recent_slots_filled) / 64;
N
Nathan Hourt 已提交
1404 1405
}

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

1410
chain_initializer_interface::~chain_initializer_interface() {}
N
Nathan Hourt 已提交
1411

1412

D
Daniel Larimer 已提交
1413
ProcessedTransaction chain_controller::transaction_from_variant( const fc::variant& v )const {
1414 1415 1416 1417
   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 已提交
1418
   ProcessedTransaction result;
1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431
   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] );
1432
         GET_FIELD( vo, authorization, result.messages[i] );
1433 1434 1435 1436 1437 1438

         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() ) {
1439 1440
               result.messages[i].data = message_to_binary( result.messages[i].code, result.messages[i].type, data ); 
               /*
1441 1442 1443 1444 1445 1446 1447 1448
               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 );
               }
1449
               */
1450 1451 1452 1453
            }
         }
      }
   }
D
Daniel Larimer 已提交
1454 1455 1456
   if( vo.contains( "output" ) ) {
      const vector<variant>& outputs = vo["output"].get_array();
   }
1457 1458 1459 1460
   return result;
#undef GET_FIELD
}

1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471
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 已提交
1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482
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();
}
1483

D
Daniel Larimer 已提交
1484
fc::variant  chain_controller::transaction_to_variant( const ProcessedTransaction& trx )const {
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501
#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 );
1502
       SET_FIELD( msg_mvo, msg, authorization );
1503 1504 1505

       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 已提交
1506
          try {
D
Daniel Larimer 已提交
1507
             msg_mvo( "data", message_from_binary( msg.code, msg.type, msg.data ) ); 
D
Daniel Larimer 已提交
1508 1509 1510 1511
             msg_mvo( "hex_data", msg.data );
          } catch ( ... ) {
            SET_FIELD( msg_mvo, msg, data );
          }
1512 1513 1514 1515 1516 1517 1518 1519
       }
       else {
         SET_FIELD( msg_mvo, msg, data );
       }
       msgsv[i] = std::move( msgs[i] );
    }
    trx_mvo( "messages", std::move(msgsv) );

D
Daniel Larimer 已提交
1520 1521 1522 1523 1524 1525 1526 1527 1528
    /* 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 ) );

1529 1530 1531 1532
    return fc::variant( std::move( trx_mvo ) );
#undef SET_FIELD
}

1533

N
Nathan Hourt 已提交
1534
} }