From 1a9cf5afb3bae66e75a73d9a8bf0f19b5720b3af Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 31 Jul 2019 16:00:39 -0300 Subject: [PATCH 01/11] libs doc separate protocol from chain --- libraries/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/README.md b/libraries/README.md index 256d15029b..06f7fc9b8a 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -10,11 +10,12 @@ Code in libraries is the most important part of **bitshares-core** project and i Folder | Name | Description | Status ---|---|---|--- [app](app) | Application | Bundles component libraries (chain, network, plugins) into a useful application. Also provides API access. | Active -[chain](chain) | Blockchain | Defines all objects, operations and types. This include the consensus protocol, defines the whole blockchain behaviour. | Active +[chain](chain) | Blockchain | Blockchain implementation and business logic. Database structure in the form of objects and updates to the blockchain in the form of evaluators are implemented here. | Active [db](db) | Database | Defines the internal database graphene uses. | Active [egenesis](egenesis) | Genesis | Hardcodes the `genesis.json` file into the `witness_node` executable.| Active [fc](fc) | Fast-compiling C++ library | https://github.com/bitshares/bitshares-fc | Active [net](net) | Network | The graphene p2p layer. | Active [plugins](plugins) | Plugins | Collection of singleton designed modules used for extending the bitshares-core. | Active +[protocol](protocol) | Protocol | Fundamental structure of the data that will be transmitted on the wire. Operations are defined and basic data integrity checks are done for each. | Active [utilities](utilities) | Utilities | Common utility calls used in applications or other libraries. | Active [wallet](wallet) | Wallet | Wallet definition for the `cli_wallet` software. | Active From a66d0fe14cd308f5589f7f7d7f24568541bc3bd7 Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 16 Apr 2019 15:23:01 +0200 Subject: [PATCH 02/11] fc --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index 2c5c30848a..af572ba7d0 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 2c5c30848a2d1cdd774ecaef2b051565f6f151f9 +Subproject commit af572ba7d016136883c31daaf054e03e5f3695a1 From 9d219a2086410e1b2919c628afa3536b416017c2 Mon Sep 17 00:00:00 2001 From: dimfred Date: Wed, 23 Jan 2019 10:57:57 +0100 Subject: [PATCH 03/11] api added login_signed --- libraries/app/api.cpp | 85 ++++++++++++++++++++++ libraries/app/include/graphene/app/api.hpp | 6 ++ 2 files changed, 91 insertions(+) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index d661bf4735..69875c1a83 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -35,10 +35,14 @@ #include #include #include +#include #include #include #include +#include +#include +#include template class fc::api; template class fc::api; @@ -84,6 +88,87 @@ namespace graphene { namespace app { return true; } + bool login_api::login_signed( const string& b64_encoded_trx ) + { + const string trx_json = fc::base64_decode( b64_encoded_trx ); + auto var = fc::json::from_string( trx_json ); + auto trx = var.as( 200 ); + + auto db = _app.chain_database(); + int64_t offset( (trx.expiration - db->head_block_time()).to_seconds() ); + if( offset > 5*60 || offset < 0 ) + return false; + + auto op = trx.operations[0]; + if( op.which() != operation::tag::value ) // only transfer op for validation + return false; + const auto acc_id = op.get().from; + + const auto& signature_keys = trx.get_signature_keys( db->get_chain_id() ); + if( signature_keys.empty() ) + return false; + + const auto& public_key = *signature_keys.begin(); + + auto key_refs = (*_database_api)->get_key_references( {public_key} )[0]; + if( std::find( key_refs.begin(), key_refs.end(), acc_id ) == key_refs.end() ) + return false; + + const auto& acc = acc_id(*db); + optional< api_access_info_signed_variant > api_access_info_var = _app.get_api_access_info_signed( acc.name ); + if( !api_access_info_var ) + return false; + + if( api_access_info_var->which() == api_access_info_signed_variant::tag::value ) + { + const auto& api_access_info = api_access_info_var->get(); + if( !verify_api_access_info_signed( acc, api_access_info ) ) + return false; // TODO or try default login then??? + + for( const auto& api : api_access_info.allowed_apis ) + enable_api( api ); + + return true; + } + else // api_access_info_var.which() == api_access_info_signed_variant::tag>::value + { + const auto& api_access_info_vec = api_access_info_var->get>(); + for( const auto& api_access_info : api_access_info_vec ) + { + if( !verify_api_access_info_signed( acc, api_access_info ) ) + continue; + + for( const auto& api : api_access_info.allowed_apis ) + enable_api( api ); + + return true; + } + return false; + } + } + + bool login_api::verify_api_access_info_signed( const account_object& acc, + const api_access_info_signed& api_access_info ) + { + if( api_access_info.required_lifetime_member && !acc.is_lifetime_member() ) + return false; + + const auto& required_registrar_name = api_access_info.required_registrar; + if( required_registrar_name == "" ) + return true; + + auto db = _app.chain_database(); + const string account_registrar_name = acc.registrar(*db).name; + string account_original_registrar_name; + if( acc.original_registrar ) + account_original_registrar_name = (*acc.original_registrar)(*db).name; + + bool has_required_registrar = required_registrar_name == account_registrar_name + || required_registrar_name == account_original_registrar_name; + + return has_required_registrar; + } + void login_api::enable_api( const std::string& api_name ) { if( api_name == "database_api" ) diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index c85fab14b2..0150bf7e8b 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -549,6 +549,8 @@ namespace graphene { namespace app { * Other APIs may not be accessible until the client has sucessfully authenticated. */ bool login(const string& user, const string& password); + bool login_signed(const string& b64_encoded_trx); + /// @brief Retrieve the network block API fc::api block()const; /// @brief Retrieve the network broadcast API @@ -582,6 +584,9 @@ namespace graphene { namespace app { optional< fc::api > _asset_api; optional< fc::api > _orders_api; optional< fc::api > _debug_api; + + bool verify_api_access_info_signed( const account_object& acc, + const api_access_info_signed& api_access_info ); }; }} // graphene::app @@ -659,4 +664,5 @@ FC_API(graphene::app::login_api, (asset) (orders) (debug) + (login_signed) ) From 8b49aec25724ced58c981a71bf32ed5a26c6b105 Mon Sep 17 00:00:00 2001 From: dimfred Date: Wed, 23 Jan 2019 10:59:02 +0100 Subject: [PATCH 04/11] modified api_access for login_signed --- .../app/include/graphene/app/api_access.hpp | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/libraries/app/include/graphene/app/api_access.hpp b/libraries/app/include/graphene/app/api_access.hpp index 8dbad1a1d9..9e69d26b54 100644 --- a/libraries/app/include/graphene/app/api_access.hpp +++ b/libraries/app/include/graphene/app/api_access.hpp @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include @@ -38,11 +39,26 @@ struct api_access_info std::vector< std::string > allowed_apis; }; +struct api_access_info_signed +{ + bool required_lifetime_member; + std::string required_registrar; + std::vector< std::string > allowed_apis; +}; + struct api_access { std::map< std::string, api_access_info > permission_map; + std::vector< api_access_info_signed > permission_map_signed_default; + std::map< std::string, api_access_info_signed > permission_map_signed_user; }; +typedef fc::static_variant< + api_access_info_signed, + std::vector< api_access_info_signed > +> api_access_info_signed_variant; + + } } // graphene::app FC_REFLECT( graphene::app::api_access_info, @@ -51,6 +67,16 @@ FC_REFLECT( graphene::app::api_access_info, (allowed_apis) ) +FC_REFLECT( graphene::app::api_access_info_signed, + (required_lifetime_member) + (required_registrar) + (allowed_apis) + ) + FC_REFLECT( graphene::app::api_access, (permission_map) + (permission_map_signed_default) + (permission_map_signed_user) ) + +FC_REFLECT_TYPENAME( graphene::app::api_access_info_signed_variant ) From 54872bd38066ccbe249d927cce35bb9d1746d35d Mon Sep 17 00:00:00 2001 From: dimfred Date: Wed, 23 Jan 2019 10:56:50 +0100 Subject: [PATCH 05/11] added login_signed to application logic --- libraries/app/application.cpp | 71 +++++++++++++++---- libraries/app/application_impl.hxx | 6 ++ .../app/include/graphene/app/application.hpp | 3 + 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 4f835ff10d..13ae3adbb2 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -238,24 +238,30 @@ void application_impl::new_connection( const fc::http::websocket_connection_ptr& std::string username = "*"; std::string password = "*"; - // Try to extract login information from "Authorization" header if present std::string auth = c->get_request_header("Authorization"); - if( boost::starts_with(auth, "Basic ") ) { - - FC_ASSERT( auth.size() > 6 ); - auto user_pass = fc::base64_decode(auth.substr(6)); - - std::vector parts; - boost::split( parts, user_pass, boost::is_any_of(":") ); - - FC_ASSERT(parts.size() == 2); - + if( !boost::starts_with(auth, "Basic ") ) + login->login(username, password); + + FC_ASSERT( auth.size() > 6 ); + auto decoded_auth = fc::base64_decode(auth.substr(6)); + + std::vector parts; + boost::split( parts, decoded_auth, boost::is_any_of(":") ); + + size_t parts_size = parts.size(); + FC_ASSERT( parts_size == 2 ); + if( parts[0] != "Signature" ) + { username = parts[0]; password = parts[1]; + login->login( username, password ); + } + else + { + string base64_encoded_trx = parts[1]; + login->login_signed( base64_encoded_trx ); } - - login->login(username, password); } void application_impl::reset_websocket_server() @@ -521,11 +527,33 @@ optional< api_access_info > application_impl::get_api_access_info(const string& return it->second; } +optional< api_access_info_signed_variant > application_impl::get_api_access_info_signed(const string& username)const +{ + auto it = _apiaccess.permission_map_signed_user.find( username ); + if( it != _apiaccess.permission_map_signed_user.end() ) + return it->second; + + if( !_apiaccess.permission_map_signed_default.empty() ) + return _apiaccess.permission_map_signed_default; + + return optional< api_access_info_signed_variant >(); +} + void application_impl::set_api_access_info(const string& username, api_access_info&& permissions) { _apiaccess.permission_map.insert(std::make_pair(username, std::move(permissions))); } +void application_impl::set_api_access_info_signed_default(vector&& permissions) +{ + _apiaccess.permission_map_signed_default = std::move( permissions ); +} + +void application_impl::set_api_access_info_signed_user(const string& username, api_access_info_signed&& permissions) +{ + _apiaccess.permission_map_signed_user.insert( std::make_pair(username, std::move(permissions) ) ); +} + /** * If delegate has the item, the network has no need to fetch it. */ @@ -965,8 +993,6 @@ uint8_t application_impl::get_current_block_interval_in_seconds() const return _chain_db->get_global_properties().parameters.block_interval; } - - } } } // namespace graphene namespace app namespace detail namespace graphene { namespace app { @@ -1120,11 +1146,26 @@ optional< api_access_info > application::get_api_access_info( const string& user return my->get_api_access_info( username ); } +optional< api_access_info_signed_variant > application::get_api_access_info_signed( const string& username )const +{ + return my->get_api_access_info_signed( username ); +} + void application::set_api_access_info(const string& username, api_access_info&& permissions) { my->set_api_access_info(username, std::move(permissions)); } +void application::set_api_access_info_signed_default(vector&& permissions) +{ + my->set_api_access_info_signed_default( std::move(permissions) ); +} + +void application::set_api_access_info_signed_user(const string& username, api_access_info_signed&& permissions) +{ + my->set_api_access_info_signed_user( username, std::move(permissions) ); +} + bool application::is_finished_syncing() const { return my->_is_finished_syncing; diff --git a/libraries/app/application_impl.hxx b/libraries/app/application_impl.hxx index 175648e10f..81ba9c1b3c 100644 --- a/libraries/app/application_impl.hxx +++ b/libraries/app/application_impl.hxx @@ -47,8 +47,14 @@ class application_impl : public net::node_delegate fc::optional< api_access_info > get_api_access_info(const string& username)const; + fc::optional< api_access_info_signed_variant > get_api_access_info_signed(const string& username)const; + void set_api_access_info(const string& username, api_access_info&& permissions); + void set_api_access_info_signed_default(std::vector&& permissions); + + void set_api_access_info_signed_user(const string& username, api_access_info_signed&& permissions); + /** * If delegate has the item, the network has no need to fetch it. */ diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index 4df4cf6100..c575e6be17 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -116,7 +116,10 @@ namespace graphene { namespace app { void set_api_limit(); void set_block_production(bool producing_blocks); fc::optional< api_access_info > get_api_access_info( const string& username )const; + fc::optional< api_access_info_signed_variant > get_api_access_info_signed( const string& username )const; void set_api_access_info(const string& username, api_access_info&& permissions); + void set_api_access_info_signed_default(std::vector&& permissions); + void set_api_access_info_signed_user(const string& username, api_access_info_signed&& permissions); bool is_finished_syncing()const; /// Emitted when syncing finishes (is_finished_syncing will return true) From 1179eb5c05a5a4c644e7fc5fd0360fb1e92d239f Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 16 Apr 2019 16:24:26 +0200 Subject: [PATCH 06/11] changed account_upgrade_operation to store the original registrar --- libraries/chain/account_evaluator.cpp | 1 + .../include/graphene/chain/account_object.hpp | 38 +++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 674de46c43..9000823386 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -424,6 +424,7 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator: // Upgrade to lifetime member. I don't care what the account was before. a.statistics(d).process_fees(a, d); a.membership_expiration_date = time_point_sec::maximum(); + a.original_registrar = a.registrar; a.referrer = a.registrar = a.lifetime_referrer = a.get_id(); a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage; } else if( a.is_annual_member(d.head_block_time()) ) { diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 70aa0f78e9..c5d803d825 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -171,6 +171,8 @@ namespace graphene { namespace chain { account_id_type referrer; /// The lifetime member at the top of the referral tree. Receives a percentage of referral rewards. account_id_type lifetime_referrer; + /// The original registrar of this account, this value is set when account_upgrade_operation is performed + optional original_registrar; /// Percentage of fee which should go to network. uint16_t network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; @@ -443,13 +445,35 @@ namespace graphene { namespace chain { }} -MAP_OBJECT_ID_TO_TYPE(graphene::chain::account_object) -MAP_OBJECT_ID_TO_TYPE(graphene::chain::account_balance_object) -MAP_OBJECT_ID_TO_TYPE(graphene::chain::account_statistics_object) - -FC_REFLECT_TYPENAME( graphene::chain::account_object ) -FC_REFLECT_TYPENAME( graphene::chain::account_balance_object ) -FC_REFLECT_TYPENAME( graphene::chain::account_statistics_object ) +FC_REFLECT_DERIVED( graphene::chain::account_object, + (graphene::db::object), + (membership_expiration_date)(registrar)(referrer)(lifetime_referrer) + (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage) + (name)(owner)(active)(options)(statistics)(whitelisting_accounts)(blacklisting_accounts) + (whitelisted_accounts)(blacklisted_accounts) + (cashback_vb) + (owner_special_authority)(active_special_authority) + (top_n_control_flags) + (allowed_assets) + (original_registrar) + ) + +FC_REFLECT_DERIVED( graphene::chain::account_balance_object, + (graphene::db::object), + (owner)(asset_type)(balance)(maintenance_flag) ) + +FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, + (graphene::chain::object), + (owner)(name) + (most_recent_op) + (total_ops)(removed_ops) + (total_core_in_orders) + (core_in_balance) + (has_cashback_vb) + (is_voting) + (lifetime_fees_paid) + (pending_fees)(pending_vested_fees) + ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::account_object ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::account_balance_object ) From db688b571b62250578f1e98cd4a5dfbbebd24243 Mon Sep 17 00:00:00 2001 From: dimfred Date: Wed, 17 Apr 2019 18:16:06 +0200 Subject: [PATCH 07/11] added example api_access.json file --- api_access.json | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 api_access.json diff --git a/api_access.json b/api_access.json new file mode 100644 index 0000000000..98c0bb6067 --- /dev/null +++ b/api_access.json @@ -0,0 +1,63 @@ +{ + "permission_map": + [ + [ + "*", + { + "password_hash_b64": "*", + "password_salt_b64": "*", + "allowed_apis": [ + "database_api", + "network_broadcast_api", + "history_api", + "block_api", + "asset_api", + "orders_api" + ] + } + ] + ], + "permission_map_signed_default": + [ + { + "required_lifetime_member": false, + "required_registrar": "registrar_name1", + "allowed_apis": + [ + "database_api", + "history_api", + "block_api", + "orders_api" + ] + }, + { + "required_lifetime_member": true, + "required_registrar": "registrar_name2", + "allowed_apis": + [ + "database_api", + "history_api", + "block_api", + "orders_api" + ] + } + ], + "permission_map_signed_user": + [ + [ + "alice", + { + "required_lifetime_member": false, + "required_registrar": "", + "allowed_apis": + [ + "database_api", + "history_api", + "block_api", + "asset_api", + "orders_api" + ] + } + ] + ] +} From 839a5471d951f5c241921503c19b194b4ef66c46 Mon Sep 17 00:00:00 2001 From: dimfred Date: Fri, 3 May 2019 14:41:45 +0200 Subject: [PATCH 08/11] added test --- tests/tests/login_signed_tests.cpp | 304 +++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 tests/tests/login_signed_tests.cpp diff --git a/tests/tests/login_signed_tests.cpp b/tests/tests/login_signed_tests.cpp new file mode 100644 index 0000000000..c73db524db --- /dev/null +++ b/tests/tests/login_signed_tests.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2019 Blockchain Projects B.V. + * + * 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. + */ + +#include +#include + +#include + +#include + +#include +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; +using namespace graphene::app; + + +struct login_signed_fixture : public database_fixture +{ + +}; + +BOOST_FIXTURE_TEST_SUITE( login_signed_tests, login_signed_fixture ) + +BOOST_AUTO_TEST_CASE( fail_with_timestamp_too_fresh ) +{ try { + + ACTOR( alice ); + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60*60; // too far in the future + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( !logged_in ); + +} FC_LOG_AND_RETHROW() } + + +BOOST_AUTO_TEST_CASE( fail_with_timestamp_too_old ) +{ try { + + ACTOR( alice ); + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() - 60*60; // too far in the past + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( !logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( fail_with_not_transfer_op_in_trx ) +{ try { + + ACTOR( alice ); + account_update_operation op; + op.account = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( !logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( fail_with_empty_signature_keys ) +{ try { + + ACTOR( alice ); + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( !logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( fail_with_wrong_signature ) +{ try { + + ACTORS( (alice) (bob) ); + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; + trx.sign( bob_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + login_api.enable_api( "database_api" ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( !logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( fail_as_default_user_no_lifetime_member ) +{ try { + + api_access_info_signed info; + info.required_lifetime_member = true; + info.required_registrar = ""; + info.allowed_apis = { "database_api" }; + + app.set_api_access_info_signed_default( {info} ); + + ACTOR( alice ); + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + login_api.enable_api( "database_api" ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( !logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( fail_as_default_user_no_required_registrar ) +{ try { + + api_access_info_signed info; + info.required_lifetime_member = false; + info.required_registrar = "required_registrar_name"; + info.allowed_apis = { "database_api" }; + + app.set_api_access_info_signed_default( {info} ); + + ACTOR( alice ); + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + login_api.enable_api( "database_api" ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( !logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( pass_as_default_user_no_specials ) +{ try { + + api_access_info_signed info; + info.required_lifetime_member = false; + info.required_registrar = ""; + info.allowed_apis = { "database_api" }; + + app.set_api_access_info_signed_default( {info} ); + + ACTOR( alice ); + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + login_api.enable_api( "database_api" ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( pass_as_default_user_with_lifetime_member ) +{ try { + + api_access_info_signed info; + info.required_lifetime_member = true; + info.required_registrar = ""; + info.allowed_apis = { "database_api" }; + + app.set_api_access_info_signed_default( {info} ); + + ACTOR( alice ); + db.modify( alice, [](account_object& obj) { + obj.membership_expiration_date = time_point_sec::maximum(); + }); + + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + login_api.enable_api( "database_api" ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( pass_as_special_user ) +{ try { + + api_access_info_signed info; + info.required_lifetime_member = false; + info.required_registrar = ""; + info.allowed_apis = { "database_api" }; + + app.set_api_access_info_signed_user( "alice", std::move(info) ); + + ACTOR( alice ); + transfer_operation op; + op.from = alice_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + login_api.enable_api( "database_api" ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( logged_in ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_SUITE_END() From e9022996ba2f748219589d466cfa2233425549fc Mon Sep 17 00:00:00 2001 From: dimfred Date: Mon, 22 Jul 2019 16:14:25 +0200 Subject: [PATCH 09/11] fixed pull request things added original_referrer --- api_access.json | 2 + libraries/app/api.cpp | 55 ++++++++++++++----- libraries/app/application.cpp | 18 +++--- .../app/include/graphene/app/api_access.hpp | 3 +- libraries/chain/account_evaluator.cpp | 3 +- .../include/graphene/chain/account_object.hpp | 5 +- tests/tests/login_signed_tests.cpp | 33 ++++++++++- 7 files changed, 94 insertions(+), 25 deletions(-) diff --git a/api_access.json b/api_access.json index 98c0bb6067..748e2e1702 100644 --- a/api_access.json +++ b/api_access.json @@ -22,6 +22,7 @@ { "required_lifetime_member": false, "required_registrar": "registrar_name1", + "required_referrer": "", "allowed_apis": [ "database_api", @@ -33,6 +34,7 @@ { "required_lifetime_member": true, "required_registrar": "registrar_name2", + "required_referrer": "", "allowed_apis": [ "database_api", diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 69875c1a83..c07cbabf1d 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -102,18 +102,22 @@ namespace graphene { namespace app { auto op = trx.operations[0]; if( op.which() != operation::tag::value ) // only transfer op for validation return false; + const auto acc_id = op.get().from; + const auto to = op.get().to; + if( acc_id != to ) // prevent MITM attacks + return false; const auto& signature_keys = trx.get_signature_keys( db->get_chain_id() ); if( signature_keys.empty() ) return false; - + const auto& public_key = *signature_keys.begin(); - + auto key_refs = (*_database_api)->get_key_references( {public_key} )[0]; if( std::find( key_refs.begin(), key_refs.end(), acc_id ) == key_refs.end() ) return false; - + const auto& acc = acc_id(*db); optional< api_access_info_signed_variant > api_access_info_var = _app.get_api_access_info_signed( acc.name ); if( !api_access_info_var ) @@ -123,7 +127,7 @@ namespace graphene { namespace app { { const auto& api_access_info = api_access_info_var->get(); if( !verify_api_access_info_signed( acc, api_access_info ) ) - return false; // TODO or try default login then??? + return false; for( const auto& api : api_access_info.allowed_apis ) enable_api( api ); @@ -150,23 +154,48 @@ namespace graphene { namespace app { bool login_api::verify_api_access_info_signed( const account_object& acc, const api_access_info_signed& api_access_info ) { + auto db = _app.chain_database(); + if( api_access_info.required_lifetime_member && !acc.is_lifetime_member() ) return false; const auto& required_registrar_name = api_access_info.required_registrar; - if( required_registrar_name == "" ) + bool registrar_required = required_registrar_name != "" ? true : false; + + const auto& required_referrer_name = api_access_info.required_referrer; + bool referrer_required = required_referrer_name != "" ? true : false; + + if( !referrer_required && !registrar_required ) return true; - auto db = _app.chain_database(); - const string account_registrar_name = acc.registrar(*db).name; - string account_original_registrar_name; - if( acc.original_registrar ) - account_original_registrar_name = (*acc.original_registrar)(*db).name; - bool has_required_registrar = required_registrar_name == account_registrar_name - || required_registrar_name == account_original_registrar_name; + bool has_required_registrar = true; + if( registrar_required ) + { + const string acc_registrar_name = acc.registrar(*db).name; + + string acc_original_registrar_name; + if( acc.original_registrar ) + acc_original_registrar_name = (*acc.original_registrar)(*db).name; + + has_required_registrar = required_registrar_name == acc_registrar_name + || required_registrar_name == acc_original_registrar_name; + } + + bool has_required_referrer = true; + if( referrer_required ) + { + const string acc_referrer_name = acc.referrer(*db).name; + + string acc_original_referrer_name; + if( acc.original_referrer ) + acc_original_referrer_name = (*acc.original_registrar)(*db).name; + + has_required_referrer = required_referrer_name == acc_referrer_name + || required_referrer_name == acc_original_referrer_name; + } - return has_required_registrar; + return has_required_registrar && has_required_referrer; } void login_api::enable_api( const std::string& api_name ) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 13ae3adbb2..f4b9195e89 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -240,9 +240,11 @@ void application_impl::new_connection( const fc::http::websocket_connection_ptr& std::string password = "*"; // Try to extract login information from "Authorization" header if present std::string auth = c->get_request_header("Authorization"); - if( !boost::starts_with(auth, "Basic ") ) + if( !boost::starts_with(auth, "Basic ") ) { login->login(username, password); - + return; + } + FC_ASSERT( auth.size() > 6 ); auto decoded_auth = fc::base64_decode(auth.substr(6)); @@ -257,8 +259,8 @@ void application_impl::new_connection( const fc::http::websocket_connection_ptr& password = parts[1]; login->login( username, password ); } - else - { + else + { string base64_encoded_trx = parts[1]; login->login_signed( base64_encoded_trx ); } @@ -379,7 +381,7 @@ void application_impl::startup() modified_genesis = true; ilog( - "Used genesis timestamp: ${timestamp} (PLEASE RECORD THIS)", + "Used genesis timestamp: ${timestamp} (PLEASE RECORD THIS)", ("timestamp", genesis.initial_timestamp.to_iso_string()) ); } @@ -487,7 +489,7 @@ void application_impl::startup() fc::path api_access_file = _options->at("api-access").as(); - FC_ASSERT( fc::exists(api_access_file), + FC_ASSERT( fc::exists(api_access_file), "Failed to load file from ${path}", ("path", api_access_file) ); _apiaccess = fc::json::from_file( api_access_file ).as( 20 ); @@ -530,12 +532,12 @@ optional< api_access_info > application_impl::get_api_access_info(const string& optional< api_access_info_signed_variant > application_impl::get_api_access_info_signed(const string& username)const { auto it = _apiaccess.permission_map_signed_user.find( username ); - if( it != _apiaccess.permission_map_signed_user.end() ) + if( it != _apiaccess.permission_map_signed_user.end() ) return it->second; if( !_apiaccess.permission_map_signed_default.empty() ) return _apiaccess.permission_map_signed_default; - + return optional< api_access_info_signed_variant >(); } diff --git a/libraries/app/include/graphene/app/api_access.hpp b/libraries/app/include/graphene/app/api_access.hpp index 9e69d26b54..ff83bf211f 100644 --- a/libraries/app/include/graphene/app/api_access.hpp +++ b/libraries/app/include/graphene/app/api_access.hpp @@ -43,6 +43,7 @@ struct api_access_info_signed { bool required_lifetime_member; std::string required_registrar; + std::string required_referrer; std::vector< std::string > allowed_apis; }; @@ -58,7 +59,6 @@ typedef fc::static_variant< std::vector< api_access_info_signed > > api_access_info_signed_variant; - } } // graphene::app FC_REFLECT( graphene::app::api_access_info, @@ -70,6 +70,7 @@ FC_REFLECT( graphene::app::api_access_info, FC_REFLECT( graphene::app::api_access_info_signed, (required_lifetime_member) (required_registrar) + (required_referrer) (allowed_apis) ) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 9000823386..ed35e4f515 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -185,6 +185,7 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio { obj.registrar = o.registrar; obj.referrer = o.referrer; + obj.original_referrer = o.referrer; obj.lifetime_referrer = o.referrer(d).lifetime_referrer; const auto& params = global_properties.parameters; @@ -417,7 +418,7 @@ void_result account_upgrade_evaluator::do_evaluate(const account_upgrade_evaluat void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator::operation_type& o) { try { database& d = db(); - + d.modify(*account, [&](account_object& a) { if( o.upgrade_to_lifetime_member ) { diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index c5d803d825..9fa45843f0 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -173,6 +173,8 @@ namespace graphene { namespace chain { account_id_type lifetime_referrer; /// The original registrar of this account, this value is set when account_upgrade_operation is performed optional original_registrar; + /// The original referrer of this account, this value is set when account_upgrade_operation is performed + optional original_referrer; /// Percentage of fee which should go to network. uint16_t network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; @@ -184,7 +186,7 @@ namespace graphene { namespace chain { /// The account's name. This name must be unique among all account names on the graph. May not be empty. string name; - + /** * The owner authority represents absolute control over the account. Usually the keys in this authority will * be kept in cold storage, as they should not be needed very often and compromise of these keys constitutes @@ -456,6 +458,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, (top_n_control_flags) (allowed_assets) (original_registrar) + (original_referrer) ) FC_REFLECT_DERIVED( graphene::chain::account_balance_object, diff --git a/tests/tests/login_signed_tests.cpp b/tests/tests/login_signed_tests.cpp index c73db524db..6fc1ddc420 100644 --- a/tests/tests/login_signed_tests.cpp +++ b/tests/tests/login_signed_tests.cpp @@ -52,6 +52,7 @@ BOOST_AUTO_TEST_CASE( fail_with_timestamp_too_fresh ) ACTOR( alice ); transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); @@ -74,6 +75,7 @@ BOOST_AUTO_TEST_CASE( fail_with_timestamp_too_old ) ACTOR( alice ); transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); @@ -89,13 +91,35 @@ BOOST_AUTO_TEST_CASE( fail_with_timestamp_too_old ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( fail_with_from_neq_to ) +{ try { + + ACTORS( (alice) (bob) ); + transfer_operation op; + op.from = alice_id; + op.to = bob_id; + + signed_transaction trx; + trx.operations.push_back( op ); + trx.expiration = db.head_block_time() + 60; // too far in the past + trx.sign( alice_private_key, db.get_chain_id() ); + + auto json = fc::json::to_string( trx ); + auto encoded = fc::base64_encode( json ); + + login_api login_api( app ); + bool logged_in = login_api.login_signed( encoded ); + BOOST_CHECK( !logged_in ); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE( fail_with_not_transfer_op_in_trx ) { try { ACTOR( alice ); account_update_operation op; op.account = alice_id; - + signed_transaction trx; trx.operations.push_back( op ); trx.expiration = db.head_block_time() + 60; @@ -116,6 +140,7 @@ BOOST_AUTO_TEST_CASE( fail_with_empty_signature_keys ) ACTOR( alice ); transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); @@ -136,6 +161,7 @@ BOOST_AUTO_TEST_CASE( fail_with_wrong_signature ) ACTORS( (alice) (bob) ); transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); @@ -165,6 +191,7 @@ BOOST_AUTO_TEST_CASE( fail_as_default_user_no_lifetime_member ) ACTOR( alice ); transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); @@ -194,6 +221,7 @@ BOOST_AUTO_TEST_CASE( fail_as_default_user_no_required_registrar ) ACTOR( alice ); transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); @@ -223,6 +251,7 @@ BOOST_AUTO_TEST_CASE( pass_as_default_user_no_specials ) ACTOR( alice ); transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); @@ -256,6 +285,7 @@ BOOST_AUTO_TEST_CASE( pass_as_default_user_with_lifetime_member ) transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); @@ -285,6 +315,7 @@ BOOST_AUTO_TEST_CASE( pass_as_special_user ) ACTOR( alice ); transfer_operation op; op.from = alice_id; + op.to = alice_id; signed_transaction trx; trx.operations.push_back( op ); From 74c5692c6c71506a6c53ddef1dd91dfe281bedac Mon Sep 17 00:00:00 2001 From: Fabian Schuh Date: Mon, 5 Aug 2019 17:24:04 +0200 Subject: [PATCH 10/11] Cleanup --- api_access.json | 12 ++++++------ libraries/app/api.cpp | 12 ++++++------ libraries/app/application_impl.hxx | 4 ++-- libraries/app/include/graphene/app/api.hpp | 2 +- libraries/chain/account_evaluator.cpp | 2 +- .../chain/include/graphene/chain/account_object.hpp | 2 +- libraries/fc | 2 +- tests/tests/login_signed_tests.cpp | 6 +++--- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/api_access.json b/api_access.json index 748e2e1702..71285330b5 100644 --- a/api_access.json +++ b/api_access.json @@ -1,5 +1,5 @@ { - "permission_map": + "permission_map": [ [ "*", @@ -23,26 +23,26 @@ "required_lifetime_member": false, "required_registrar": "registrar_name1", "required_referrer": "", - "allowed_apis": + "allowed_apis": [ "database_api", "history_api", "block_api", "orders_api" ] - }, + }, { "required_lifetime_member": true, "required_registrar": "registrar_name2", "required_referrer": "", - "allowed_apis": + "allowed_apis": [ "database_api", "history_api", "block_api", "orders_api" ] - } + } ], "permission_map_signed_user": [ @@ -51,7 +51,7 @@ { "required_lifetime_member": false, "required_registrar": "", - "allowed_apis": + "allowed_apis": [ "database_api", "history_api", diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index c07cbabf1d..01094c5a7d 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -97,7 +97,7 @@ namespace graphene { namespace app { auto db = _app.chain_database(); int64_t offset( (trx.expiration - db->head_block_time()).to_seconds() ); if( offset > 5*60 || offset < 0 ) - return false; + return false; auto op = trx.operations[0]; if( op.which() != operation::tag::value ) // only transfer op for validation @@ -127,7 +127,7 @@ namespace graphene { namespace app { { const auto& api_access_info = api_access_info_var->get(); if( !verify_api_access_info_signed( acc, api_access_info ) ) - return false; + return false; for( const auto& api : api_access_info.allowed_apis ) enable_api( api ); @@ -141,10 +141,10 @@ namespace graphene { namespace app { { if( !verify_api_access_info_signed( acc, api_access_info ) ) continue; - + for( const auto& api : api_access_info.allowed_apis ) enable_api( api ); - + return true; } return false; @@ -156,7 +156,7 @@ namespace graphene { namespace app { { auto db = _app.chain_database(); - if( api_access_info.required_lifetime_member && !acc.is_lifetime_member() ) + if( api_access_info.required_lifetime_member && !acc.is_lifetime_member() ) return false; const auto& required_registrar_name = api_access_info.required_registrar; @@ -191,7 +191,7 @@ namespace graphene { namespace app { if( acc.original_referrer ) acc_original_referrer_name = (*acc.original_registrar)(*db).name; - has_required_referrer = required_referrer_name == acc_referrer_name + has_required_referrer = required_referrer_name == acc_referrer_name || required_referrer_name == acc_original_referrer_name; } diff --git a/libraries/app/application_impl.hxx b/libraries/app/application_impl.hxx index 81ba9c1b3c..9a467a636e 100644 --- a/libraries/app/application_impl.hxx +++ b/libraries/app/application_impl.hxx @@ -47,14 +47,14 @@ class application_impl : public net::node_delegate fc::optional< api_access_info > get_api_access_info(const string& username)const; - fc::optional< api_access_info_signed_variant > get_api_access_info_signed(const string& username)const; + fc::optional< api_access_info_signed_variant > get_api_access_info_signed(const string& username)const; void set_api_access_info(const string& username, api_access_info&& permissions); void set_api_access_info_signed_default(std::vector&& permissions); void set_api_access_info_signed_user(const string& username, api_access_info_signed&& permissions); - + /** * If delegate has the item, the network has no need to fetch it. */ diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 0150bf7e8b..fa1f9071d3 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -585,7 +585,7 @@ namespace graphene { namespace app { optional< fc::api > _orders_api; optional< fc::api > _debug_api; - bool verify_api_access_info_signed( const account_object& acc, + bool verify_api_access_info_signed( const account_object& acc, const api_access_info_signed& api_access_info ); }; diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index ed35e4f515..ccfc233f09 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -418,7 +418,7 @@ void_result account_upgrade_evaluator::do_evaluate(const account_upgrade_evaluat void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator::operation_type& o) { try { database& d = db(); - + d.modify(*account, [&](account_object& a) { if( o.upgrade_to_lifetime_member ) { diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 9fa45843f0..3dbb4d3559 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -186,7 +186,7 @@ namespace graphene { namespace chain { /// The account's name. This name must be unique among all account names on the graph. May not be empty. string name; - + /** * The owner authority represents absolute control over the account. Usually the keys in this authority will * be kept in cold storage, as they should not be needed very often and compromise of these keys constitutes diff --git a/libraries/fc b/libraries/fc index af572ba7d0..2c5c30848a 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit af572ba7d016136883c31daaf054e03e5f3695a1 +Subproject commit 2c5c30848a2d1cdd774ecaef2b051565f6f151f9 diff --git a/tests/tests/login_signed_tests.cpp b/tests/tests/login_signed_tests.cpp index 6fc1ddc420..89393939b0 100644 --- a/tests/tests/login_signed_tests.cpp +++ b/tests/tests/login_signed_tests.cpp @@ -48,7 +48,7 @@ BOOST_FIXTURE_TEST_SUITE( login_signed_tests, login_signed_fixture ) BOOST_AUTO_TEST_CASE( fail_with_timestamp_too_fresh ) { try { - + ACTOR( alice ); transfer_operation op; op.from = alice_id; @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE( fail_with_not_transfer_op_in_trx ) ACTOR( alice ); account_update_operation op; op.account = alice_id; - + signed_transaction trx; trx.operations.push_back( op ); trx.expiration = db.head_block_time() + 60; @@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE( fail_with_empty_signature_keys ) signed_transaction trx; trx.operations.push_back( op ); trx.expiration = db.head_block_time() + 60; - + auto json = fc::json::to_string( trx ); auto encoded = fc::base64_encode( json ); From dd09f814503034491a19a6a21fac7821df46db79 Mon Sep 17 00:00:00 2001 From: Fabian Schuh Date: Mon, 5 Aug 2019 17:24:56 +0200 Subject: [PATCH 11/11] reset readme.md --- libraries/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/README.md b/libraries/README.md index 06f7fc9b8a..256d15029b 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -10,12 +10,11 @@ Code in libraries is the most important part of **bitshares-core** project and i Folder | Name | Description | Status ---|---|---|--- [app](app) | Application | Bundles component libraries (chain, network, plugins) into a useful application. Also provides API access. | Active -[chain](chain) | Blockchain | Blockchain implementation and business logic. Database structure in the form of objects and updates to the blockchain in the form of evaluators are implemented here. | Active +[chain](chain) | Blockchain | Defines all objects, operations and types. This include the consensus protocol, defines the whole blockchain behaviour. | Active [db](db) | Database | Defines the internal database graphene uses. | Active [egenesis](egenesis) | Genesis | Hardcodes the `genesis.json` file into the `witness_node` executable.| Active [fc](fc) | Fast-compiling C++ library | https://github.com/bitshares/bitshares-fc | Active [net](net) | Network | The graphene p2p layer. | Active [plugins](plugins) | Plugins | Collection of singleton designed modules used for extending the bitshares-core. | Active -[protocol](protocol) | Protocol | Fundamental structure of the data that will be transmitted on the wire. Operations are defined and basic data integrity checks are done for each. | Active [utilities](utilities) | Utilities | Common utility calls used in applications or other libraries. | Active [wallet](wallet) | Wallet | Wallet definition for the `cli_wallet` software. | Active