diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 77578f864fdc8edad9f3ce7e1e7aedaf67b0c33a..2e7d70c95c053c37b8c2f0ee69ba0bd947e7ed01 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -496,6 +496,132 @@ void chain_controller::apply_block(const signed_block& next_block, uint32_t skip }); } +template +void check_output(const T& expected, const T& actual) { + EOS_ASSERT((expected == actual), block_tx_output_exception, + "Value mismatch, expected: ${expected}, actual: ${actual}", ("expected", expected)("actual", actual)); +} + +template +void check_output(const vector& expected, const vector& actual) { + EOS_ASSERT((expected.size() == actual.size()), block_tx_output_exception, + "Vector size mismatch, expected: ${expected}, actual: ${actual}", ("expected", expected.size())("actual", actual.size())); + + for(int idx=0; idx < expected.size(); idx++) { + const auto &expected_element = expected.at(idx); + const auto &actual_element = actual.at(idx); + try { + check_output(expected_element, actual_element); + } FC_RETHROW_EXCEPTIONS( warn, "at index ${idx}", ("idx", idx ) ); + } +} + +template<> +void check_output(const types::Bytes& expected, const types::Bytes& actual) { + EOS_ASSERT((expected.size() == actual.size()), block_tx_output_exception, + "Binary blob size mismatch, expected: ${expected}, actual: ${actual}", ("expected", expected.size())("actual", actual.size())); + + EOS_ASSERT((std::memcmp(expected.data(), actual.data(), expected.size()) == 0), block_tx_output_exception, + "Binary blob contents differ"); +} + +template<> +void check_output(const types::AccountPermission& expected, const types::AccountPermission& actual) { + try { + check_output(expected.account, actual.account); + } FC_RETHROW_EXCEPTIONS(warn, "in .account"); + try { + check_output(expected.permission, actual.permission); + } FC_RETHROW_EXCEPTIONS(warn, "in .permission"); +} + +template<> +void check_output(const types::Message& expected, const types::Message& actual) { + try { + check_output(expected.code, actual.code); + } FC_RETHROW_EXCEPTIONS(warn, "in .code"); + try { + check_output(expected.type, actual.type); + } FC_RETHROW_EXCEPTIONS(warn, "in .type"); + try { + check_output(expected.authorization, actual.authorization); + } FC_RETHROW_EXCEPTIONS(warn, "in .authorization"); + try { + check_output(expected.data, actual.data); + } FC_RETHROW_EXCEPTIONS(warn, "in .data"); +} + +template<> +void check_output(const MessageOutput& expected, const MessageOutput& actual); + +template<> +void check_output(const NotifyOutput& expected, const NotifyOutput& actual) { + try { + check_output(expected.name, actual.name); + } FC_RETHROW_EXCEPTIONS(warn, "in .name"); + try { + check_output(expected.output, actual.output); + } FC_RETHROW_EXCEPTIONS(warn, "in .output"); +} + +template<> +void check_output(const Transaction& expected, const Transaction& actual) { + try { + check_output(expected.refBlockNum, actual.refBlockNum); + } FC_RETHROW_EXCEPTIONS(warn, "in .refBlockNum"); + try { + check_output(expected.refBlockPrefix, actual.refBlockPrefix); + } FC_RETHROW_EXCEPTIONS(warn, "in .refBlockPrefix"); + try { + check_output(expected.expiration, actual.expiration); + } FC_RETHROW_EXCEPTIONS(warn, "in .expiration"); + try { + check_output(expected.scope, actual.scope); + } FC_RETHROW_EXCEPTIONS(warn, "in .scope"); + try { + check_output(expected.messages, actual.messages); + } FC_RETHROW_EXCEPTIONS(warn, "in .messages"); +} + + +template<> +void check_output(const ProcessedSyncTransaction& expected, const ProcessedSyncTransaction& actual) { + check_output(expected, actual); + try { + check_output(expected.output, actual.output); + } FC_RETHROW_EXCEPTIONS(warn, "in .output"); +} + +template<> +void check_output(const GeneratedTransaction& expected, const GeneratedTransaction& actual) { + try { + check_output(expected.id, actual.id); + } FC_RETHROW_EXCEPTIONS(warn, "in .id"); + check_output(expected, actual); +} + +template<> +void check_output(const MessageOutput& expected, const MessageOutput& actual) { + try { + check_output(expected.notify, actual.notify); + } FC_RETHROW_EXCEPTIONS(warn, "in .notify"); + + try { + check_output(expected.sync_transactions, actual.sync_transactions); + } FC_RETHROW_EXCEPTIONS(warn, "in .sync_transactions"); + + try { + check_output(expected.async_transactions, actual.async_transactions); + } FC_RETHROW_EXCEPTIONS(warn, "in .async_transactions"); +} + +template +void chain_controller::check_transaction_output(const T& expected, const T& actual)const { + if (!(_skip_flags & skip_output_check)) { + check_output(expected.output, actual.output); + } +} + void chain_controller::_apply_block(const signed_block& next_block) { try { uint32_t next_block_num = next_block.block_num(); @@ -522,14 +648,32 @@ void chain_controller::_apply_block(const signed_block& next_block) * 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& ptrx : thread.generated_input ) { - auto const trx = get_generated_transaction(ptrx.id); - apply_transaction(trx); + for (int c_idx = 0; c_idx < next_block.cycles.size(); c_idx++) { + const auto& cycle = next_block.cycles.at(c_idx); + + for (int t_idx = 0; t_idx < cycle.size(); t_idx++) { + const auto& thread = cycle.at(t_idx); + + 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); + try { + check_transaction_output(ptrx, processed); + } FC_RETHROW_EXCEPTIONS(warn, + "cycle: ${c_idx}, thread: ${t_idx}, generated_input: ${p_idx}", + ("c_idx", c_idx)("t_idx", t_idx)("p_idx", p_idx) ); } - for(const auto& trx : thread.user_input ) { - apply_transaction(trx); + + for(int p_idx = 0; p_idx < thread.user_input.size(); p_idx++ ) { + const auto& ptrx = thread.user_input.at(p_idx); + const SignedTransaction& trx = ptrx; + auto processed = apply_transaction(trx); + try { + check_transaction_output(ptrx, processed); + } FC_RETHROW_EXCEPTIONS(warn, + "cycle: ${c_idx}, thread: ${t_idx}, user_input: ${p_idx}", + ("c_idx", c_idx)("t_idx", t_idx)("p_idx", p_idx) ); } } } diff --git a/libraries/chain/include/eos/chain/chain_controller.hpp b/libraries/chain/include/eos/chain/chain_controller.hpp index 928ec3519f7a507ebf7bdc4c004bab97f46326dc..b1220694943e1415a98b08410507659a52bf0244 100644 --- a/libraries/chain/include/eos/chain/chain_controller.hpp +++ b/libraries/chain/include/eos/chain/chain_controller.hpp @@ -103,7 +103,8 @@ namespace eos { namespace chain { skip_undo_history_check = 1 << 9, ///< used while reindexing skip_producer_schedule_check= 1 << 10, ///< used while reindexing skip_validate = 1 << 11, ///< used prior to checkpoint, skips validate() call on transaction - skip_scope_check = 1 << 12 ///< used to skip checks for proper scope + skip_scope_check = 1 << 12, ///< used to skip checks for proper scope + skip_output_check = 1 << 13 ///< used to skip checks for outputs in block exactly matching those created from apply }; /** @@ -277,6 +278,9 @@ namespace eos { namespace chain { void check_transaction_authorization(const SignedTransaction& trx, bool allow_unused_signatures = false)const; + template + void check_transaction_output(const T& expected, const T& actual)const; + template typename T::Processed apply_transaction(const T& trx); diff --git a/libraries/chain/include/eos/chain/exceptions.hpp b/libraries/chain/include/eos/chain/exceptions.hpp index 6bf6e813d368ec2f03a3ecec57767466a8f21cdf..0417830830f464aa046f148e42b68ebe73024730 100644 --- a/libraries/chain/include/eos/chain/exceptions.hpp +++ b/libraries/chain/include/eos/chain/exceptions.hpp @@ -42,6 +42,8 @@ namespace eos { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( black_swan_exception, eos::chain::chain_exception, 3100000, "black swan" ) FC_DECLARE_DERIVED_EXCEPTION( unknown_block_exception, eos::chain::chain_exception, 3110000, "unknown block" ) + FC_DECLARE_DERIVED_EXCEPTION( block_tx_output_exception, eos::chain::block_validate_exception, 3020001, "transaction outputs in block do not match transaction outputs from applying block" ) + FC_DECLARE_DERIVED_EXCEPTION( tx_missing_auth, eos::chain::transaction_exception, 3030001, "missing required authority" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_sigs, eos::chain::transaction_exception, 3030002, "signatures do not satisfy declared authorizations" ) FC_DECLARE_DERIVED_EXCEPTION( tx_irrelevant_auth, eos::chain::transaction_exception, 3030003, "irrelevant authority included" ) diff --git a/libraries/chain/include/eos/chain/transaction.hpp b/libraries/chain/include/eos/chain/transaction.hpp index a8229de67a7646851b399914ea01abeb30ea86fc..81cdf90311914ac9a2b4ba5283cade9fbb8f475b 100644 --- a/libraries/chain/include/eos/chain/transaction.hpp +++ b/libraries/chain/include/eos/chain/transaction.hpp @@ -53,6 +53,7 @@ namespace eos { namespace chain { */ struct ProcessedTransaction; + struct ProcessedSyncTransaction; struct ProcessedGeneratedTransaction; @@ -160,9 +161,9 @@ namespace eos { namespace chain { * Output generated by applying a particular message. */ struct MessageOutput { - vector notify; ///< accounts to notify, may only be notified once - vector sync_transactions; ///< transactions generated and applied after notify - vector async_transactions; ///< transactions generated but not applied + vector notify; ///< accounts to notify, may only be notified once + vector sync_transactions; ///< transactions generated and applied after notify + vector async_transactions; ///< transactions generated but not applied }; struct NotifyOutput { @@ -177,6 +178,13 @@ namespace eos { namespace chain { vector output; }; + struct ProcessedSyncTransaction : public Transaction { + explicit ProcessedSyncTransaction( const Transaction& t ):Transaction(t){} + ProcessedSyncTransaction(){} + + vector output; + }; + struct ProcessedGeneratedTransaction { explicit ProcessedGeneratedTransaction( const generated_transaction_id_type& _id ):id(_id){} explicit ProcessedGeneratedTransaction( const GeneratedTransaction& t ):id(t.id){} @@ -193,5 +201,6 @@ FC_REFLECT(eos::chain::GeneratedTransaction, (id)) FC_REFLECT_DERIVED(eos::chain::SignedTransaction, (eos::types::SignedTransaction), ) FC_REFLECT(eos::chain::MessageOutput, (notify)(sync_transactions)(async_transactions) ) FC_REFLECT_DERIVED(eos::chain::ProcessedTransaction, (eos::types::SignedTransaction), (output) ) +FC_REFLECT_DERIVED(eos::chain::ProcessedSyncTransaction, (eos::types::Transaction), (output) ) FC_REFLECT(eos::chain::ProcessedGeneratedTransaction, (id)(output) ) FC_REFLECT(eos::chain::NotifyOutput, (name)(output) )