diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index cbc2b8d43a6..4b48b0d63cb 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -111,23 +111,24 @@ namespace eos { namespace app { void network_broadcast_api::on_applied_block( const signed_block& b ) { - if( _callbacks.size() ) - { - /// we need to ensure the database_api is not deleted for the life of the async operation - auto capture_this = shared_from_this(); - for( uint32_t trx_num = 0; trx_num < b.transactions.size(); ++trx_num ) - { - const auto& trx = b.transactions[trx_num]; - auto id = trx.id(); - auto itr = _callbacks.find(id); - if( itr != _callbacks.end() ) - { - auto block_num = b.block_num(); - auto& callback = _callbacks.find(id)->second; - fc::async( [capture_this,this,id,block_num,trx_num,trx,callback](){ callback( fc::variant(transaction_confirmation{ id, block_num, trx_num, trx}) ); } ); - } - } - } +#warning TODO: Figure out how to handle this +// if( _callbacks.size() ) +// { +// /// we need to ensure the database_api is not deleted for the life of the async operation +// auto capture_this = shared_from_this(); +// for( uint32_t trx_num = 0; trx_num < b.transactions.size(); ++trx_num ) +// { +// const auto& trx = b.transactions[trx_num]; +// auto id = trx.id(); +// auto itr = _callbacks.find(id); +// if( itr != _callbacks.end() ) +// { +// auto block_num = b.block_num(); +// auto& callback = _callbacks.find(id)->second; +// fc::async( [capture_this,this,id,block_num,trx_num,trx,callback](){ callback( fc::variant(transaction_confirmation{ id, block_num, trx_num, trx}) ); } ); +// } +// } +// } } void network_broadcast_api::broadcast_transaction(const signed_transaction& trx) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index e75bc1aed9d..c13eae4e41f 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -536,12 +536,13 @@ namespace detail { // included in blocks before we see the free-floating transaction itself. If that // happens, there's no reason to fetch the transactions, so construct a list of the // transaction message ids we no longer need. - // during sync, it is unlikely that we'll see any old - for (const signed_transaction& transaction : blk_msg.block.transactions) - { - eos::net::trx_message transaction_message(transaction); - contained_transaction_message_ids.push_back(eos::net::message(transaction_message).id()); - } + for (const auto& cycle : blk_msg.block.cycles) + for (const auto& thread : cycle) + for (const auto& transaction : thread.input_transactions) + if (transaction.which() == decay_t::tag::value) { + eos::net::trx_message transaction_message(transaction.get()); + contained_transaction_message_ids.push_back(eos::net::message(transaction_message).id()); + } } return result; diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index fd94ddc413b..79c92a6eb71 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -229,35 +229,30 @@ bool database::_push_block(const signed_block& new_block) * queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending * queues. */ -signed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) +void database::push_transaction(const signed_transaction& trx, uint32_t skip) { try { - signed_transaction result; - detail::with_skip_flags( *this, skip, [&]() + detail::with_skip_flags(*this, skip, [&]() { - result = _push_transaction( trx ); - } ); - return result; -} FC_CAPTURE_AND_RETHROW( (trx) ) } + _push_transaction(trx); + }); +} FC_CAPTURE_AND_RETHROW((trx)) } -signed_transaction database::_push_transaction( const signed_transaction& trx ) -{ +void database::_push_transaction(const signed_transaction& trx) { auto temp_session = start_undo_session(true); - auto processed_trx = _apply_transaction( trx ); - _pending_tx.push_back(processed_trx); + _apply_transaction(trx); + _pending_tx.push_back(trx); // notify_changed_objects(); // The transaction applied successfully. Merge its changes into the pending block session. temp_session.squash(); // notify anyone listening to pending transactions - on_pending_transaction( trx ); - return processed_trx; + on_pending_transaction(trx); } -signed_transaction database::validate_transaction( const signed_transaction& trx ) -{ +void database::validate_transaction(const signed_transaction& trx) { auto session = start_undo_session(true); - return _apply_transaction( trx ); + _apply_transaction(trx); } signed_block database::generate_block( @@ -329,14 +324,12 @@ signed_block database::_generate_block( try { auto temp_session = start_undo_session(true); - signed_transaction ptx = _apply_transaction( tx ); + _apply_transaction(tx); temp_session.squash(); - // We have to recompute pack_size(ptx) because it may be different - // than pack_size(tx) (i.e. if one or more results increased - // their size) - total_block_size += fc::raw::pack_size( ptx ); - pending_block.transactions.push_back( ptx ); + total_block_size += fc::raw::pack_size(tx); +// pending_block.transactions.push_back(tx); +#warning TODO: Populate generated blocks with transactions } catch ( const fc::exception& e ) { @@ -378,8 +371,7 @@ signed_block database::_generate_block( } FC_CAPTURE_AND_RETHROW( (producer_id) ) } /** - * Removes the most recent block from the database and - * undoes any changes it made. + * Removes the most recent block from the database and undoes any changes it made. */ void database::pop_block() { try { @@ -390,9 +382,6 @@ void database::pop_block() _fork_db.pop_block(); undo(); - - _popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() ); - } FC_CAPTURE_AND_RETHROW() } void database::clear_pending() @@ -424,7 +413,7 @@ void database::apply_block( const signed_block& next_block, uint32_t skip ) return; } -void database::_apply_block( const signed_block& next_block ) +void database::_apply_block(const signed_block& next_block) { try { uint32_t next_block_num = next_block.block_num(); uint32_t skip = get_node_properties().skip_flags; @@ -436,17 +425,31 @@ void database::_apply_block( const signed_block& next_block ) _current_block_num = next_block_num; _current_trx_in_block = 0; - for( const auto& trx : next_block.transactions ) - { - /* 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. - */ - apply_transaction( trx, skip | skip_transaction_signatures ); - ++_current_trx_in_block; - } + /* 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. + */ + for (const auto& cycle : next_block.cycles) + for (const auto& thread : cycle) + for(const auto& trx : thread.input_transactions) + { + struct { + using result_type = void; + void operator()(const signed_transaction& trx) { + db.apply_transaction(trx); + } + void operator()(generated_transaction_id_type) { +#warning TODO: Process generated transaction + } + + database& db; + } visitor{*this}; + + trx.visit(visitor); + ++_current_trx_in_block; + } update_global_dynamic_data(next_block); update_signing_producer(signing_producer, next_block); @@ -467,17 +470,15 @@ void database::_apply_block( const signed_block& next_block ) } FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) } -signed_transaction database::apply_transaction(const signed_transaction& trx, uint32_t skip) +void database::apply_transaction(const signed_transaction& trx, uint32_t skip) { - signed_transaction result; - detail::with_skip_flags( *this, skip, [&]() + detail::with_skip_flags(*this, skip, [&]() { - result = _apply_transaction(trx); + _apply_transaction(trx); }); - return result; } -signed_transaction database::_apply_transaction(const signed_transaction& trx) +void database::_apply_transaction(const signed_transaction& trx) { try { uint32_t skip = get_node_properties().skip_flags; @@ -526,8 +527,6 @@ signed_transaction database::_apply_transaction(const signed_transaction& trx) #warning TODO: Process messages in transaction ++_current_message_in_trx; } - - return ptrx; } FC_CAPTURE_AND_RETHROW( (trx) ) } const producer_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const diff --git a/libraries/chain/include/eos/chain/database.hpp b/libraries/chain/include/eos/chain/database.hpp index c7047b1a1a6..940aa4d7c2b 100644 --- a/libraries/chain/include/eos/chain/database.hpp +++ b/libraries/chain/include/eos/chain/database.hpp @@ -121,9 +121,9 @@ namespace eos { namespace chain { bool before_last_checkpoint()const; bool push_block( const signed_block& b, uint32_t skip = skip_nothing ); - signed_transaction push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); + void push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); bool _push_block( const signed_block& b ); - signed_transaction _push_transaction( const signed_transaction& trx ); + void _push_transaction( const signed_transaction& trx ); signed_block generate_block( const fc::time_point_sec when, @@ -217,38 +217,33 @@ namespace eos { namespace chain { void debug_dump(); void apply_debug_updates(); - void debug_update( const fc::variant_object& update ); + void debug_update(const fc::variant_object& update); /** * This method validates transactions without adding it to the pending state. * @return true if the transaction would validate */ - signed_transaction validate_transaction( const signed_transaction& trx ); - - - /** when popping a block, the transactions that were removed get cached here so they - * can be reapplied at the proper time */ - std::deque< signed_transaction > _popped_tx; + void validate_transaction(const signed_transaction& trx); private: optional _pending_tx_session; public: // these were formerly private, but they have a fairly well-defined API, so let's make them public - void apply_block( const signed_block& next_block, uint32_t skip = skip_nothing ); - signed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); + void apply_block(const signed_block& next_block, uint32_t skip = skip_nothing); + void apply_transaction(const signed_transaction& trx, uint32_t skip = skip_nothing); private: - void _apply_block( const signed_block& next_block ); - signed_transaction _apply_transaction( const signed_transaction& trx ); + void _apply_block(const signed_block& next_block); + void _apply_transaction(const signed_transaction& trx); ///Steps involved in applying a new block ///@{ - const producer_object& validate_block_header( uint32_t skip, const signed_block& next_block )const; - const producer_object& _validate_block_header( const signed_block& next_block )const; + const producer_object& validate_block_header(uint32_t skip, const signed_block& next_block)const; + const producer_object& _validate_block_header(const signed_block& next_block)const; void create_block_summary(const signed_block& next_block); - void update_global_dynamic_data( const signed_block& b ); + void update_global_dynamic_data(const signed_block& b); void update_signing_producer(const producer_object& signing_producer, const signed_block& new_block); void update_last_irreversible_block(); void clear_expired_transactions(); diff --git a/libraries/chain/include/eos/chain/protocol/block.hpp b/libraries/chain/include/eos/chain/protocol/block.hpp index 4380528a8eb..abe3aee05ce 100644 --- a/libraries/chain/include/eos/chain/protocol/block.hpp +++ b/libraries/chain/include/eos/chain/protocol/block.hpp @@ -48,14 +48,26 @@ namespace eos { namespace chain { signature_type producer_signature; }; + struct thread { + using input_transaction = static_variant; + + vector input_transactions; + vector output_transactions; + + digest_type merkle_digest() const; + }; + + using cycle = vector; + struct signed_block : public signed_block_header { checksum_type calculate_merkle_root()const; - vector transactions; + vector cycles; }; } } // eos::chain -FC_REFLECT( eos::chain::block_header, (previous)(timestamp)(producer)(transaction_merkle_root) ) -FC_REFLECT_DERIVED( eos::chain::signed_block_header, (eos::chain::block_header), (producer_signature) ) -FC_REFLECT_DERIVED( eos::chain::signed_block, (eos::chain::signed_block_header), (transactions) ) +FC_REFLECT(eos::chain::block_header, (previous)(timestamp)(producer)(transaction_merkle_root)) +FC_REFLECT_DERIVED(eos::chain::signed_block_header, (eos::chain::block_header), (producer_signature)) +FC_REFLECT(eos::chain::thread, (input_transactions)(output_transactions)) +FC_REFLECT_DERIVED(eos::chain::signed_block, (eos::chain::signed_block_header), (cycles)) diff --git a/libraries/chain/include/eos/chain/protocol/transaction.hpp b/libraries/chain/include/eos/chain/protocol/transaction.hpp index 7474a7111ae..0d37e5f37ae 100644 --- a/libraries/chain/include/eos/chain/protocol/transaction.hpp +++ b/libraries/chain/include/eos/chain/protocol/transaction.hpp @@ -99,6 +99,25 @@ namespace eos { namespace chain { void set_reference_block(const block_id_type& reference_block); }; + /** + * @brief A generated_transaction is a transaction which was internally generated by the blockchain, typically as a + * result of running a contract. + * + * When contracts run and seek to interact with other contracts, or mutate chain state, they generate transactions + * containing messages which effect these interactions and mutations. These generated transactions are automatically + * generated by contracts, and thus are authorized by the script that generated them rather than by signatures. The + * generated_transaction struct records such a transaction. + * + * These transactions are generated while processing other transactions. The generated transactions are assigned a + * sequential ID, then stored in the block that generated them. These generated transactions can then be included in + * subsequent blocks by referencing this ID. + */ + struct generated_transaction : public transaction { + generated_transaction_id_type id; + + digest_type merkle_digest() const; + }; + /** * @brief A single authorization used to authorize a transaction * @@ -169,5 +188,6 @@ namespace eos { namespace chain { } } // eos::chain FC_REFLECT(eos::chain::transaction, (ref_block_num)(ref_block_prefix)(expiration)(messages)) +FC_REFLECT(eos::chain::generated_transaction, (id)) FC_REFLECT(eos::chain::authorization, (authorizing_account)(privileges)) FC_REFLECT_DERIVED(eos::chain::signed_transaction, (eos::chain::transaction), (signatures)) diff --git a/libraries/chain/include/eos/chain/protocol/types.hpp b/libraries/chain/include/eos/chain/protocol/types.hpp index b4c50477027..8425736608b 100644 --- a/libraries/chain/include/eos/chain/protocol/types.hpp +++ b/libraries/chain/include/eos/chain/protocol/types.hpp @@ -98,11 +98,11 @@ namespace eos { namespace chain { using privilege_class = std::string; /** - * List all object types from all namespaces here so they can - * be easily reflected and displayed in debug output. If a 3rd party - * wants to extend the core code then they will have to change the - * packed_object::type field from enum_type to uint16 to avoid - * warnings when converting packed_objects to/from json. + * List all object types from all namespaces here so they can + * be easily reflected and displayed in debug output. If a 3rd party + * wants to extend the core code then they will have to change the + * packed_object::type field from enum_type to uint16 to avoid + * warnings when converting packed_objects to/from json. */ enum object_type { @@ -123,13 +123,14 @@ namespace eos { namespace chain { using account_id_type = chainbase::oid; using producer_id_type = chainbase::oid; - typedef fc::ripemd160 block_id_type; - typedef fc::ripemd160 checksum_type; - typedef fc::ripemd160 transaction_id_type; - typedef fc::sha256 digest_type; - typedef fc::ecc::compact_signature signature_type; - typedef safe share_type; - typedef uint16_t weight_type; + using generated_transaction_id_type = uint32_t; + using block_id_type = fc::ripemd160; + using checksum_type = fc::ripemd160; + using transaction_id_type = fc::ripemd160; + using digest_type = fc::sha256; + using signature_type = fc::ecc::compact_signature; + using share_type = safe; + using weight_type = uint16_t; struct public_key_type { diff --git a/libraries/chain/protocol/block.cpp b/libraries/chain/protocol/block.cpp index cc3c00788b7..d783ef73809 100644 --- a/libraries/chain/protocol/block.cpp +++ b/libraries/chain/protocol/block.cpp @@ -62,31 +62,43 @@ namespace eos { namespace chain { return signee() == expected_signee; } + digest_type merkle(vector ids) { + while (ids.size() > 1) { + if (ids.size() % 2) + ids.push_back(ids.back()); + for (int i = 0; i < ids.size() / 2; ++i) + ids[i/2] = digest_type::hash(std::make_pair(ids[i], ids[i+1])); + ids.resize(ids.size() / 2); + } + + return ids.front(); + } + checksum_type signed_block::calculate_merkle_root()const { - if( transactions.size() == 0 ) + if(cycles.empty()) return checksum_type(); vector ids; - ids.resize( transactions.size() ); - for( uint32_t i = 0; i < transactions.size(); ++i ) - ids[i] = transactions[i].merkle_digest(); + for (const auto& cycle : cycles) + for (const auto& thread : cycle) + ids.emplace_back(thread.merkle_digest()); - vector::size_type current_number_of_hashes = ids.size(); - while( current_number_of_hashes > 1 ) - { - // hash ID's in pairs - uint32_t i_max = current_number_of_hashes - (current_number_of_hashes&1); - uint32_t k = 0; + return checksum_type::hash(merkle(ids)); + } - for( uint32_t i = 0; i < i_max; i += 2 ) - ids[k++] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) ); + digest_type thread::merkle_digest() const { + vector ids; + std::transform(input_transactions.begin(), input_transactions.end(), std::back_inserter(ids), + [](const input_transaction& trx) { + if (trx.which() == input_transaction::tag::value) + return trx.get().merkle_digest(); +#warning How do I get the digest from a generated_transaction_id_type?... + }); + std::transform(output_transactions.begin(), output_transactions.end(), std::back_inserter(ids), + std::bind(&generated_transaction::merkle_digest, std::placeholders::_1)); - if( current_number_of_hashes&1 ) - ids[k++] = ids[i_max]; - current_number_of_hashes = k; - } - return checksum_type::hash( ids[0] ); + return merkle(ids); } } } diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index ec4b8bac0dd..b35b95de49e 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -29,57 +29,49 @@ namespace eos { namespace chain { -digest_type transaction::digest()const -{ +digest_type transaction::digest()const { digest_type::encoder enc; fc::raw::pack( enc, *this ); return enc.result(); } -digest_type transaction::sig_digest( const chain_id_type& chain_id )const -{ +digest_type transaction::sig_digest( const chain_id_type& chain_id )const { digest_type::encoder enc; fc::raw::pack( enc, chain_id ); fc::raw::pack( enc, *this ); return enc.result(); } -void transaction::validate() const -{ +void transaction::validate() const { FC_ASSERT( messages.size() > 0, "A transaction must have at least one message", ("trx",*this) ); #warning TODO: Figure out how to validate trxs } -eos::chain::transaction_id_type eos::chain::transaction::id() const -{ +eos::chain::transaction_id_type eos::chain::transaction::id() const { auto h = digest(); transaction_id_type result; memcpy(result._hash, h._hash, std::min(sizeof(result), sizeof(h))); return result; } -const signature_type& eos::chain::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id) -{ +const signature_type& eos::chain::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id) { digest_type h = sig_digest( chain_id ); signatures.push_back(key.sign_compact(h)); return signatures.back(); } -signature_type eos::chain::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id)const -{ +signature_type eos::chain::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id)const { digest_type::encoder enc; fc::raw::pack( enc, chain_id ); fc::raw::pack( enc, *this ); return key.sign_compact(enc.result()); } -void transaction::set_expiration( fc::time_point_sec expiration_time ) -{ +void transaction::set_expiration( fc::time_point_sec expiration_time ) { expiration = expiration_time; } -void transaction::set_reference_block( const block_id_type& reference_block ) -{ +void transaction::set_reference_block( const block_id_type& reference_block ) { ref_block_num = fc::endian_reverse_u32(reference_block._hash[0]); ref_block_prefix = reference_block._hash[1]; } @@ -98,8 +90,13 @@ flat_set signed_transaction::get_signature_keys( const chain_id return result; } FC_CAPTURE_AND_RETHROW() } -eos::chain::digest_type eos::chain::signed_transaction::merkle_digest() const -{ +eos::chain::digest_type eos::chain::signed_transaction::merkle_digest() const { + digest_type::encoder enc; + fc::raw::pack(enc, *this); + return enc.result(); +} + +digest_type generated_transaction::merkle_digest() const { digest_type::encoder enc; fc::raw::pack(enc, *this); return enc.result();