37
37
#include < graphene/chain/exceptions.hpp>
38
38
#include < graphene/chain/evaluator.hpp>
39
39
40
+ #include < fc/thread/parallel.hpp>
40
41
#include < fc/smart_ref_impl.hpp>
41
42
42
43
namespace graphene { namespace chain {
@@ -227,7 +228,7 @@ bool database::_push_block(const signed_block& new_block)
227
228
* queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending
228
229
* queues.
229
230
*/
230
- processed_transaction database::push_transaction ( const signed_transaction & trx, uint32_t skip )
231
+ processed_transaction database::push_transaction ( const precomputable_transaction & trx, uint32_t skip )
231
232
{ try {
232
233
processed_transaction result;
233
234
detail::with_skip_flags ( *this , skip, [&]()
@@ -237,7 +238,7 @@ processed_transaction database::push_transaction( const signed_transaction& trx,
237
238
return result;
238
239
} FC_CAPTURE_AND_RETHROW ( (trx) ) }
239
240
240
- processed_transaction database::_push_transaction ( const signed_transaction & trx )
241
+ processed_transaction database::_push_transaction ( const precomputable_transaction & trx )
241
242
{
242
243
// If this is the first transaction pushed after applying a block, start a new undo session.
243
244
// This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
@@ -465,15 +466,17 @@ signed_block database::_generate_block(
465
466
void database::pop_block ()
466
467
{ try {
467
468
_pending_tx_session.reset ();
468
- auto head_id = head_block_id ();
469
- optional<signed_block> head_block = fetch_block_by_id ( head_id );
470
- GRAPHENE_ASSERT ( head_block.valid (), pop_empty_chain, " there are no blocks to pop" );
471
-
472
- _fork_db.pop_block ();
469
+ auto fork_db_head = _fork_db.head ();
470
+ FC_ASSERT ( fork_db_head, " Trying to pop() from empty fork database!?" );
471
+ if ( fork_db_head->id == head_block_id () )
472
+ _fork_db.pop_block ();
473
+ else
474
+ {
475
+ fork_db_head = _fork_db.fetch_block ( head_block_id () );
476
+ FC_ASSERT ( fork_db_head, " Trying to pop() block that's not in fork database!?" );
477
+ }
473
478
pop_undo ();
474
-
475
- _popped_tx.insert ( _popped_tx.begin (), head_block->transactions .begin (), head_block->transactions .end () );
476
-
479
+ _popped_tx.insert ( _popped_tx.begin (), fork_db_head->data .transactions .begin (), fork_db_head->data .transactions .end () );
477
480
} FC_CAPTURE_AND_RETHROW () }
478
481
479
482
void database::clear_pending ()
@@ -621,22 +624,17 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
621
624
{ try {
622
625
uint32_t skip = get_node_properties ().skip_flags ;
623
626
624
- if ( true || !(skip&skip_validate) ) /* issue #505 explains why this skip_flag is disabled */
625
- trx.validate ();
627
+ trx.validate ();
626
628
627
629
auto & trx_idx = get_mutable_index_type<transaction_index>();
628
630
const chain_id_type& chain_id = get_chain_id ();
629
- transaction_id_type trx_id;
630
631
if ( !(skip & skip_transaction_dupe_check) )
631
- {
632
- trx_id = trx.id ();
633
- FC_ASSERT ( trx_idx.indices ().get <by_trx_id>().find (trx_id) == trx_idx.indices ().get <by_trx_id>().end () );
634
- }
632
+ FC_ASSERT ( trx_idx.indices ().get <by_trx_id>().find (trx.id ()) == trx_idx.indices ().get <by_trx_id>().end () );
635
633
transaction_evaluation_state eval_state (this );
636
634
const chain_parameters& chain_parameters = get_global_properties ().parameters ;
637
635
eval_state._trx = &trx;
638
636
639
- if ( !(skip & ( skip_transaction_signatures | skip_authority_check) ) )
637
+ if ( !(skip & skip_transaction_signatures) )
640
638
{
641
639
auto get_active = [&]( account_id_type id ) { return &id (*this ).active ; };
642
640
auto get_owner = [&]( account_id_type id ) { return &id (*this ).owner ; };
@@ -665,8 +663,8 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
665
663
// Insert transaction into unique transactions database.
666
664
if ( !(skip & skip_transaction_dupe_check) )
667
665
{
668
- create<transaction_object>([&trx_id,& trx](transaction_object& transaction) {
669
- transaction.trx_id = trx_id ;
666
+ create<transaction_object>([&trx](transaction_object& transaction) {
667
+ transaction.trx_id = trx. id () ;
670
668
transaction.trx = trx;
671
669
});
672
670
}
@@ -750,4 +748,65 @@ bool database::before_last_checkpoint()const
750
748
return (_checkpoints.size () > 0 ) && (_checkpoints.rbegin ()->first >= head_block_num ());
751
749
}
752
750
751
+
752
+ static const uint32_t skip_expensive = database::skip_transaction_signatures | database::skip_witness_signature
753
+ | database::skip_merkle_check | database::skip_transaction_dupe_check;
754
+
755
+ template <typename Trx>
756
+ void database::_precompute_parallel ( const Trx* trx, const size_t count, const uint32_t skip )const
757
+ {
758
+ for ( size_t i = 0 ; i < count; ++i, ++trx )
759
+ {
760
+ trx->validate (); // TODO - parallelize wrt confidential operations
761
+ if ( !(skip&skip_transaction_dupe_check) )
762
+ trx->id ();
763
+ if ( !(skip&skip_transaction_signatures) )
764
+ trx->get_signature_keys ( get_chain_id () );
765
+ }
766
+ }
767
+
768
+ fc::future<void > database::precompute_parallel ( const signed_block& block, const uint32_t skip )const
769
+ { try {
770
+ std::vector<fc::future<void >> workers;
771
+ if ( !block.transactions .empty () )
772
+ {
773
+ if ( (skip & skip_expensive) == skip_expensive )
774
+ _precompute_parallel ( &block.transactions [0 ], block.transactions .size (), skip );
775
+ else
776
+ {
777
+ uint32_t chunks = fc::asio::default_io_service_scope::get_num_threads ();
778
+ uint32_t chunk_size = ( block.transactions .size () + chunks - 1 ) / chunks;
779
+ workers.reserve ( chunks + 1 );
780
+ for ( size_t base = 0 ; base < block.transactions .size (); base += chunk_size )
781
+ workers.push_back ( fc::do_parallel ( [this ,&block,base,chunk_size,skip] () {
782
+ _precompute_parallel ( &block.transactions [base],
783
+ base + chunk_size < block.transactions .size () ? chunk_size : block.transactions .size () - base,
784
+ skip );
785
+ }) );
786
+ }
787
+ }
788
+
789
+ if ( !(skip&skip_witness_signature) )
790
+ workers.push_back ( fc::do_parallel ( [&block] () { block.signee (); } ) );
791
+ if ( !(skip&skip_merkle_check) )
792
+ block.calculate_merkle_root ();
793
+ block.id ();
794
+
795
+ if ( workers.empty () )
796
+ return fc::future< void >( fc::promise< void >::ptr ( new fc::promise< void >( true ) ) );
797
+
798
+ auto first = workers.begin ();
799
+ auto worker = first;
800
+ while ( ++worker != workers.end () )
801
+ worker->wait ();
802
+ return *first;
803
+ } FC_LOG_AND_RETHROW () }
804
+
805
+ fc::future<void > database::precompute_parallel ( const precomputable_transaction& trx )const
806
+ {
807
+ return fc::do_parallel ([this ,&trx] () {
808
+ _precompute_parallel ( &trx, 1 , skip_nothing );
809
+ });
810
+ }
811
+
753
812
} }
0 commit comments