From 5aedb65e5c0e97d7b307411b7f06281b728ec9be Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Thu, 16 Mar 2017 18:38:35 -0400 Subject: [PATCH] Implement the apparently-unused feature allowing later operations in a transaction to reference objects created by earlier operations. Change all betting operations to allow use of this feature, and test it in operation_tests2 --- libraries/chain/betting_market_evaluator.cpp | 30 ++- libraries/chain/competitor_evaluator.cpp | 14 +- libraries/chain/evaluator.cpp | 14 ++ libraries/chain/event_evaluator.cpp | 38 +++- libraries/chain/event_group_evaluator.cpp | 14 +- .../chain/betting_market_evaluator.hpp | 4 + .../graphene/chain/competitor_evaluator.hpp | 2 + .../graphene/chain/event_evaluator.hpp | 3 + .../graphene/chain/event_group_evaluator.hpp | 3 + .../chain/protocol/betting_market.hpp | 12 +- .../graphene/chain/protocol/competitor.hpp | 5 +- .../include/graphene/chain/protocol/event.hpp | 12 +- .../graphene/chain/protocol/event_group.hpp | 6 +- tests/tests/operation_tests2.cpp | 180 +++++++++++++++--- 14 files changed, 294 insertions(+), 43 deletions(-) diff --git a/libraries/chain/betting_market_evaluator.cpp b/libraries/chain/betting_market_evaluator.cpp index 77a15711..237a7819 100644 --- a/libraries/chain/betting_market_evaluator.cpp +++ b/libraries/chain/betting_market_evaluator.cpp @@ -35,6 +35,19 @@ void_result betting_market_group_create_evaluator::do_evaluate(const betting_mar { try { FC_ASSERT(trx_state->_is_proposed_trx); + // the event_id in the operation can be a relative id. If it is, + // resolve it and verify that it is truly an event + object_id_type resolved_event_id = op.event_id; + if (is_relative(op.event_id)) + resolved_event_id = get_relative_id(op.event_id); + + FC_ASSERT(resolved_event_id.space() == event_id_type::space_id && + resolved_event_id.type() == event_id_type::type_id, + "event_id must refer to a event_id_type"); + event_id = resolved_event_id; + FC_ASSERT( db().find_object(event_id), "Invalid event specified" ); + + // TODO: should we prevent creating multiple identical betting market groups for an event (same type & options)? return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -42,7 +55,7 @@ object_id_type betting_market_group_create_evaluator::do_apply(const betting_mar { try { const betting_market_group_object& new_betting_market_group = db().create( [&]( betting_market_group_object& betting_market_group_obj ) { - betting_market_group_obj.event_id = op.event_id; + betting_market_group_obj.event_id = event_id; betting_market_group_obj.options = op.options; }); return new_betting_market_group.id; @@ -52,6 +65,19 @@ void_result betting_market_create_evaluator::do_evaluate(const betting_market_cr { try { FC_ASSERT(trx_state->_is_proposed_trx); + // the betting_market_group_id in the operation can be a relative id. If it is, + // resolve it and verify that it is truly an betting_market_group + object_id_type resolved_betting_market_group_id = op.group_id; + if (is_relative(op.group_id)) + resolved_betting_market_group_id = get_relative_id(op.group_id); + + FC_ASSERT(resolved_betting_market_group_id.space() == betting_market_group_id_type::space_id && + resolved_betting_market_group_id.type() == betting_market_group_id_type::type_id, + "betting_market_group_id must refer to a betting_market_group_id_type"); + group_id = resolved_betting_market_group_id; + FC_ASSERT( db().find_object(group_id), "Invalid betting_market_group specified" ); + + // TODO: should we prevent creating multiple identical betting markets groups in a group (same asset and payout condition)? return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -59,7 +85,7 @@ object_id_type betting_market_create_evaluator::do_apply(const betting_market_cr { try { const betting_market_object& new_betting_market = db().create( [&]( betting_market_object& betting_market_obj ) { - betting_market_obj.group_id = op.group_id; + betting_market_obj.group_id = group_id; betting_market_obj.payout_condition = op.payout_condition; betting_market_obj.asset_id = op.asset_id; }); diff --git a/libraries/chain/competitor_evaluator.cpp b/libraries/chain/competitor_evaluator.cpp index 3d67708d..5156d95d 100644 --- a/libraries/chain/competitor_evaluator.cpp +++ b/libraries/chain/competitor_evaluator.cpp @@ -23,6 +23,7 @@ */ #include #include +#include #include #include #include @@ -35,6 +36,17 @@ void_result competitor_create_evaluator::do_evaluate(const competitor_create_ope { try { FC_ASSERT(trx_state->_is_proposed_trx); + // the sport id in the operation can be a relative id. If it is, + // resolve it and verify that it is truly a sport + object_id_type resolved_id = op.sport_id; + if (is_relative(op.sport_id)) + resolved_id = get_relative_id(op.sport_id); + + FC_ASSERT(resolved_id.space() == sport_id_type::space_id && + resolved_id.type() == sport_id_type::type_id, "sport_id must refer to a sport_id_type"); + sport_id = resolved_id; + + FC_ASSERT( db().find_object(sport_id), "Invalid sport specified" ); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -43,7 +55,7 @@ object_id_type competitor_create_evaluator::do_apply(const competitor_create_ope const competitor_object& new_competitor = db().create( [&]( competitor_object& competitor_obj ) { competitor_obj.name = op.name; - competitor_obj.sport_id = op.sport_id; + competitor_obj.sport_id = sport_id; }); return new_competitor.id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp index a4127c25..2ae8f0e7 100644 --- a/libraries/chain/evaluator.cpp +++ b/libraries/chain/evaluator.cpp @@ -128,4 +128,18 @@ database& generic_evaluator::db()const { return trx_state->db(); } db().adjust_balance(fee_payer, fee_from_account); } + object_id_type generic_evaluator::get_relative_id( object_id_type rel_id )const + { + if (!is_relative(rel_id)) + FC_THROW("get_relative_id() called for non-relative id ${id}", ("id", rel_id)); + if (rel_id.instance() >= trx_state->operation_results.size()) + FC_THROW("get_relative_id() asked for id of operation ${op_num} (zero-based), but we only have ${count} operations", + ("op_num", rel_id.instance())("count", trx_state->operation_results.size())); + if (trx_state->operation_results[rel_id.instance()].which() != operation_result::tag::value) + FC_THROW("get_relative_id() asked for the result of operation ${op_num}, but that operation did not return an object_id", + ("op_num", rel_id.instance())); + return trx_state->operation_results[rel_id.instance()].get(); + } + + } } diff --git a/libraries/chain/event_evaluator.cpp b/libraries/chain/event_evaluator.cpp index d0d903ce..dae85357 100644 --- a/libraries/chain/event_evaluator.cpp +++ b/libraries/chain/event_evaluator.cpp @@ -23,7 +23,8 @@ */ #include #include -#include +#include +#include #include #include #include @@ -35,6 +36,37 @@ void_result event_create_evaluator::do_evaluate(const event_create_operation& op { try { FC_ASSERT(trx_state->_is_proposed_trx); + database& d = db(); + + // the event_group_id in the operation can be a relative id. If it is, + // resolve it and verify that it is truly an event_group + object_id_type resolved_event_group_id = op.event_group_id; + if (is_relative(op.event_group_id)) + resolved_event_group_id = get_relative_id(op.event_group_id); + + FC_ASSERT(resolved_event_group_id.space() == event_group_id_type::space_id && + resolved_event_group_id.type() == event_group_id_type::type_id, + "event_group_id must refer to a event_group_id_type"); + event_group_id = resolved_event_group_id; + const event_group_object& event_group = event_group_id(d); + + // Likewise for each competitor in the list + for (const object_id_type& raw_competitor_id : op.competitors) + { + object_id_type resolved_competitor_id = raw_competitor_id; + if (is_relative(raw_competitor_id)) + resolved_competitor_id = get_relative_id(raw_competitor_id); + + FC_ASSERT(resolved_competitor_id.space() == competitor_id_type::space_id && + resolved_competitor_id.type() == competitor_id_type::type_id, + "competitor must refer to a competitor_id_type"); + competitor_id_type competitor_id = resolved_competitor_id; + const competitor_object& competitor = competitor_id(d); + FC_ASSERT(competitor.sport_id == event_group.sport_id, + "competitor must compete in the same sport as the event they're competing in"); + competitors.push_back(competitor_id); + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -43,6 +75,10 @@ object_id_type event_create_evaluator::do_apply(const event_create_operation& op const event_object& new_event = db().create( [&]( event_object& event_obj ) { event_obj.name = op.name; + event_obj.season = op.season; + event_obj.start_time = op.start_time; + event_obj.event_group_id = event_group_id; + event_obj.competitors = competitors; }); return new_event.id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/event_group_evaluator.cpp b/libraries/chain/event_group_evaluator.cpp index f676e334..1f37afd9 100644 --- a/libraries/chain/event_group_evaluator.cpp +++ b/libraries/chain/event_group_evaluator.cpp @@ -35,6 +35,18 @@ void_result event_group_create_evaluator::do_evaluate(const event_group_create_o { try { FC_ASSERT(trx_state->_is_proposed_trx); + // the sport id in the operation can be a relative id. If it is, + // resolve it and verify that it is truly a sport + object_id_type resolved_id = op.sport_id; + if (is_relative(op.sport_id)) + resolved_id = get_relative_id(op.sport_id); + + FC_ASSERT(resolved_id.space() == sport_id_type::space_id && + resolved_id.type() == sport_id_type::type_id, "sport_id must refer to a sport_id_type"); + sport_id = resolved_id; + + FC_ASSERT( db().find_object(sport_id), "Invalid sport specified" ); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -43,7 +55,7 @@ object_id_type event_group_create_evaluator::do_apply(const event_group_create_o const event_group_object& new_event_group = db().create( [&]( event_group_object& event_group_obj ) { event_group_obj.name = op.name; - event_group_obj.sport_id = op.sport_id; + event_group_obj.sport_id = sport_id; }); return new_event_group.id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/include/graphene/chain/betting_market_evaluator.hpp b/libraries/chain/include/graphene/chain/betting_market_evaluator.hpp index 41a38619..eccef223 100644 --- a/libraries/chain/include/graphene/chain/betting_market_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/betting_market_evaluator.hpp @@ -36,6 +36,8 @@ namespace graphene { namespace chain { void_result do_evaluate( const betting_market_group_create_operation& o ); object_id_type do_apply( const betting_market_group_create_operation& o ); + private: + event_id_type event_id; }; class betting_market_create_evaluator : public evaluator @@ -45,6 +47,8 @@ namespace graphene { namespace chain { void_result do_evaluate( const betting_market_create_operation& o ); object_id_type do_apply( const betting_market_create_operation& o ); + private: + betting_market_group_id_type group_id; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/competitor_evaluator.hpp b/libraries/chain/include/graphene/chain/competitor_evaluator.hpp index a22c9f50..b8e977c7 100644 --- a/libraries/chain/include/graphene/chain/competitor_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/competitor_evaluator.hpp @@ -36,6 +36,8 @@ namespace graphene { namespace chain { void_result do_evaluate( const competitor_create_operation& o ); object_id_type do_apply( const competitor_create_operation& o ); + private: + sport_id_type sport_id; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/event_evaluator.hpp b/libraries/chain/include/graphene/chain/event_evaluator.hpp index d271fa72..5f82966f 100644 --- a/libraries/chain/include/graphene/chain/event_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/event_evaluator.hpp @@ -36,6 +36,9 @@ namespace graphene { namespace chain { void_result do_evaluate( const event_create_operation& o ); object_id_type do_apply( const event_create_operation& o ); + private: + event_group_id_type event_group_id; + vector competitors; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/event_group_evaluator.hpp b/libraries/chain/include/graphene/chain/event_group_evaluator.hpp index e1bca6a3..2160a0c2 100644 --- a/libraries/chain/include/graphene/chain/event_group_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/event_group_evaluator.hpp @@ -36,6 +36,9 @@ namespace graphene { namespace chain { void_result do_evaluate( const event_group_create_operation& o ); object_id_type do_apply( const event_group_create_operation& o ); + + private: + sport_id_type sport_id; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/protocol/betting_market.hpp b/libraries/chain/include/graphene/chain/protocol/betting_market.hpp index f61650e5..1383766b 100644 --- a/libraries/chain/include/graphene/chain/protocol/betting_market.hpp +++ b/libraries/chain/include/graphene/chain/protocol/betting_market.hpp @@ -53,7 +53,11 @@ struct betting_market_group_create_operation : public base_operation struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; asset fee; - event_id_type event_id; + /** + * This can be a event_id_type, or a + * relative object id that resolves to a event_id_type + */ + object_id_type event_id; betting_market_options_type options; @@ -68,7 +72,11 @@ struct betting_market_create_operation : public base_operation struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; asset fee; - betting_market_group_id_type group_id; + /** + * This can be a betting_market_group_id_type, or a + * relative object id that resolves to a betting_market_group_id_type + */ + object_id_type group_id; internationalized_string_type payout_condition; diff --git a/libraries/chain/include/graphene/chain/protocol/competitor.hpp b/libraries/chain/include/graphene/chain/protocol/competitor.hpp index 4406a734..2da18c07 100644 --- a/libraries/chain/include/graphene/chain/protocol/competitor.hpp +++ b/libraries/chain/include/graphene/chain/protocol/competitor.hpp @@ -39,9 +39,10 @@ struct competitor_create_operation : public base_operation internationalized_string_type name; /** - * The sport the competitor, um, competes in + * The sport the competitor, um, competes in. This can be a sport_id_type, or a + * relative object id that resolves to a sport_id_type */ - sport_id_type sport_id; + object_id_type sport_id; extensions_type extensions; diff --git a/libraries/chain/include/graphene/chain/protocol/event.hpp b/libraries/chain/include/graphene/chain/protocol/event.hpp index 6830cf0b..4c89d8f3 100644 --- a/libraries/chain/include/graphene/chain/protocol/event.hpp +++ b/libraries/chain/include/graphene/chain/protocol/event.hpp @@ -42,9 +42,17 @@ struct event_create_operation : public base_operation optional start_time; - event_group_id_type event_group_id; + /** + * This can be a event_group_id_type, or a + * relative object id that resolves to a event_group_id_type + */ + object_id_type event_group_id; - vector competitors; + /** + * Each entry in this vector can be a competitor_id_type, or a relative object id that + * resolves to a competitor_id_type + */ + vector competitors; extensions_type extensions; diff --git a/libraries/chain/include/graphene/chain/protocol/event_group.hpp b/libraries/chain/include/graphene/chain/protocol/event_group.hpp index d678a671..bf1cf2f1 100644 --- a/libraries/chain/include/graphene/chain/protocol/event_group.hpp +++ b/libraries/chain/include/graphene/chain/protocol/event_group.hpp @@ -38,7 +38,11 @@ struct event_group_create_operation : public base_operation */ internationalized_string_type name; - sport_id_type sport_id; + /** + * This can be a sport_id_type, or a + * relative object id that resolves to a sport_id_type + */ + object_id_type sport_id; extensions_type extensions; diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 8880741e..d9d24356 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -1631,14 +1632,17 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test ) { { const flat_set& active_witnesses = db.get_global_properties().active_witnesses; - // Propose the create_sport operation + + BOOST_TEST_MESSAGE("Propose the create_sport operation"); { - sport_create_operation create_op; - create_op.name.insert(internationalized_string_type::value_type("en", "Ice Hockey")); - + sport_create_operation sport_create_op; + sport_create_op.name.insert(internationalized_string_type::value_type("en", "Football")); + sport_create_op.name.insert(internationalized_string_type::value_type("en_US", "Soccer")); + sport_create_op.name.insert(internationalized_string_type::value_type("zh_Hans", "足球")); + sport_create_op.name.insert(internationalized_string_type::value_type("ja", "サッカー")); proposal_create_operation proposal_op; proposal_op.fee_paying_account = (*active_witnesses.begin())(db).witness_account; - proposal_op.proposed_ops.emplace_back(create_op); + proposal_op.proposed_ops.emplace_back(sport_create_op); proposal_op.expiration_time = db.head_block_time() + fc::days(1); signed_transaction tx; @@ -1651,39 +1655,153 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test ) db.push_transaction(tx); } - BOOST_TEST_MESSAGE( "Witness account: " << fc::json::to_pretty_string(GRAPHENE_WITNESS_ACCOUNT(db))); - - BOOST_TEST_MESSAGE("There are now " << db.get_index_type().indices().size() << " proposals"); - const proposal_object& prop = *db.get_index_type().indices().begin(); - BOOST_TEST_MESSAGE("Just created sport creation proposal " << fc::variant(prop.id).as()); - - - BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1); // should require GRAPHENE_WITNESS_ACCOUNT only - BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 0); - BOOST_CHECK(!prop.is_authorized_to_execute(db)); - - for (const witness_id_type& witness_id : active_witnesses) { - BOOST_TEST_MESSAGE("Approving sport creation from witness " << fc::variant(witness_id).as()); - const witness_object& witness = witness_id(db); - const account_object& witness_account = witness.witness_account(db); + //BOOST_TEST_MESSAGE( "Witness account: " << fc::json::to_pretty_string(GRAPHENE_WITNESS_ACCOUNT(db))); - proposal_update_operation pup; - pup.proposal = prop.id; - pup.fee_paying_account = witness_account.id; - //pup.key_approvals_to_add.insert(witness.signing_key); - pup.active_approvals_to_add.insert(witness_account.id); + BOOST_TEST_MESSAGE("There are now " << db.get_index_type().indices().size() << " proposals"); + const proposal_object& prop = *db.get_index_type().indices().begin(); + BOOST_TEST_MESSAGE("Just created sport creation proposal " << fc::variant(prop.id).as()); + + + BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1); // should require GRAPHENE_WITNESS_ACCOUNT only + BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 0); + BOOST_CHECK(!prop.is_authorized_to_execute(db)); + + for (const witness_id_type& witness_id : active_witnesses) + { + BOOST_TEST_MESSAGE("Approving sport creation from witness " << fc::variant(witness_id).as()); + const witness_object& witness = witness_id(db); + const account_object& witness_account = witness.witness_account(db); + + proposal_update_operation pup; + pup.proposal = prop.id; + pup.fee_paying_account = witness_account.id; + //pup.key_approvals_to_add.insert(witness.signing_key); + pup.active_approvals_to_add.insert(witness_account.id); + + signed_transaction tx; + tx.operations.push_back( pup ); + set_expiration( db, tx ); + sign(tx, init_account_priv_key); + + db.push_transaction(tx, ~0); + if (db.get_index_type().indices().size() > 0) + { + BOOST_TEST_MESSAGE("The sport creation operation has been approved, new sport object on the blockchain is " << fc::json::to_pretty_string(*db.get_index_type().indices().begin())); + break; + } + } + } + + BOOST_REQUIRE_EQUAL(db.get_index_type().indices().size(), 1); + + BOOST_TEST_MESSAGE("Creating a sport and competitors in the same proposal"); + { + // operation 0 in the transaction + sport_create_operation sport_create_op; + sport_create_op.name.insert(internationalized_string_type::value_type("en", "Ice Hockey")); + sport_create_op.name.insert(internationalized_string_type::value_type("zh_Hans", "冰球")); + sport_create_op.name.insert(internationalized_string_type::value_type("ja", "アイスホッケー")); + + // operation 1 + competitor_create_operation competitor1_create_op; + competitor1_create_op.sport_id = object_id_type(relative_protocol_ids, 0, 0); + competitor1_create_op.name.insert(internationalized_string_type::value_type("en", "Washington Capitals")); + competitor1_create_op.name.insert(internationalized_string_type::value_type("zh_Hans", "華盛頓首都隊")); + competitor1_create_op.name.insert(internationalized_string_type::value_type("ja", "ワシントン・キャピタルズ")); + //BOOST_TEST_MESSAGE("Just constructed competitor_create_operation " << fc::json::to_pretty_string(competitor1_create_op)); + + // operation 2 + competitor_create_operation competitor2_create_op; + competitor2_create_op.sport_id = object_id_type(relative_protocol_ids, 0, 0); + competitor2_create_op.name.insert(internationalized_string_type::value_type("en", "Chicago Blackhawks")); + competitor2_create_op.name.insert(internationalized_string_type::value_type("zh_Hans", "芝加哥黑鷹")); + competitor2_create_op.name.insert(internationalized_string_type::value_type("ja", "シカゴ・ブラックホークス")); + + // operation 3 + event_group_create_operation event_group_create_op; + event_group_create_op.name.insert(internationalized_string_type::value_type("en", "NHL")); + event_group_create_op.name.insert(internationalized_string_type::value_type("zh_Hans", "國家冰球聯盟")); + event_group_create_op.name.insert(internationalized_string_type::value_type("ja", "ナショナルホッケーリーグ")); + event_group_create_op.sport_id = object_id_type(relative_protocol_ids, 0, 0); + + // operation 4 + // leave name and start time blank + event_create_operation event_create_op; + event_create_op.season.insert(internationalized_string_type::value_type("en", "2016-17")); + event_create_op.event_group_id = object_id_type(relative_protocol_ids, 0, 3); + event_create_op.competitors.push_back(object_id_type(relative_protocol_ids, 0, 1)); + event_create_op.competitors.push_back(object_id_type(relative_protocol_ids, 0, 2)); + + // operation 5 + betting_market_group_create_operation betting_market_group_create_op; + betting_market_group_create_op.event_id = object_id_type(relative_protocol_ids, 0, 4); + betting_market_group_create_op.options = moneyline_market_options{}; + + // operation 6 + betting_market_create_operation caps_win_betting_market_create_op; + caps_win_betting_market_create_op.group_id = object_id_type(relative_protocol_ids, 0, 5); + caps_win_betting_market_create_op.payout_condition.insert(internationalized_string_type::value_type("en", "Washington Capitals win")); + caps_win_betting_market_create_op.asset_id = asset_id_type(); + + // operation 7 + betting_market_create_operation blackhawks_win_betting_market_create_op; + blackhawks_win_betting_market_create_op.group_id = object_id_type(relative_protocol_ids, 0, 5); + blackhawks_win_betting_market_create_op.payout_condition.insert(internationalized_string_type::value_type("en", "Chicago Blackhawks win")); + blackhawks_win_betting_market_create_op.asset_id = asset_id_type(); + + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = (*active_witnesses.begin())(db).witness_account; + proposal_op.proposed_ops.emplace_back(sport_create_op); + proposal_op.proposed_ops.emplace_back(competitor1_create_op); + proposal_op.proposed_ops.emplace_back(competitor2_create_op); + proposal_op.proposed_ops.emplace_back(event_group_create_op); + proposal_op.proposed_ops.emplace_back(event_create_op); + proposal_op.proposed_ops.emplace_back(betting_market_group_create_op); + proposal_op.proposed_ops.emplace_back(caps_win_betting_market_create_op); + proposal_op.proposed_ops.emplace_back(blackhawks_win_betting_market_create_op); + proposal_op.expiration_time = db.head_block_time() + fc::days(1); signed_transaction tx; - tx.operations.push_back( pup ); - set_expiration( db, tx ); + tx.operations.push_back(proposal_op); + set_expiration(db, tx); sign(tx, init_account_priv_key); + //sign( tx, philbin_private_key ); - db.push_transaction(tx, ~0); - if (db.get_index_type().indices().size() > 0) + // Alice and Philbin signed, but asset issuer is invalid + db.push_transaction(tx); + } + + BOOST_REQUIRE_EQUAL(db.get_index_type().indices().size(), 1); + { + const proposal_object& prop = *db.get_index_type().indices().begin(); + + for (const witness_id_type& witness_id : active_witnesses) { - BOOST_TEST_MESSAGE("The sport creation operation has been approved, new sport object on the blockchain is " << fc::json::to_pretty_string(*db.get_index_type().indices().begin())); - break; + BOOST_TEST_MESSAGE("Approving sport+competitors creation from witness " << fc::variant(witness_id).as()); + const witness_object& witness = witness_id(db); + const account_object& witness_account = witness.witness_account(db); + + proposal_update_operation pup; + pup.proposal = prop.id; + pup.fee_paying_account = witness_account.id; + //pup.key_approvals_to_add.insert(witness.signing_key); + pup.active_approvals_to_add.insert(witness_account.id); + + signed_transaction tx; + tx.operations.push_back( pup ); + set_expiration( db, tx ); + sign(tx, init_account_priv_key); + + db.push_transaction(tx, ~0); + if (db.get_index_type().indices().size() > 1) + { + BOOST_REQUIRE_EQUAL(db.get_index_type().indices().size(), 2); + //BOOST_TEST_MESSAGE("The sport creation operation has been approved, new sport object on the blockchain is " << fc::json::to_pretty_string(*db.get_index_type().indices().rbegin())); + //BOOST_TEST_MESSAGE("The first competitor object on the blockchain is " << fc::json::to_pretty_string(*db.get_index_type().indices().begin())); + break; + } } } }