From 5fbff6cf522ff490b644632b29ca005b70bf9e3c Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 19 Aug 2015 10:23:06 -0400 Subject: [PATCH 1/5] Resolve #238: Light client now fetches chain_id from chain_properties --- programs/light_client/ChainDataModel.cpp | 2 ++ programs/light_client/ChainDataModel.hpp | 2 ++ programs/light_client/GrapheneApplication.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/programs/light_client/ChainDataModel.cpp b/programs/light_client/ChainDataModel.cpp index 62a843da..56643533 100644 --- a/programs/light_client/ChainDataModel.cpp +++ b/programs/light_client/ChainDataModel.cpp @@ -35,6 +35,8 @@ void ChainDataModel::setDatabaseAPI(fc::api dbapi) { m_db_api->subscribe_to_objects([this](const variant& d) { m_dynamic_global_properties = d.as(); }, {m_dynamic_global_properties.id}); + + m_chain_properties = m_db_api->get_chain_properties(); }); } diff --git a/programs/light_client/ChainDataModel.hpp b/programs/light_client/ChainDataModel.hpp index 39700246..d213bec4 100644 --- a/programs/light_client/ChainDataModel.hpp +++ b/programs/light_client/ChainDataModel.hpp @@ -61,6 +61,7 @@ public: const graphene::chain::global_property_object& global_properties() const { return m_global_properties; } const graphene::chain::dynamic_global_property_object& dynamic_global_properties() const { return m_dynamic_global_properties; } + const graphene::chain::chain_property_object& chain_properties() const { return m_chain_properties; } public Q_SLOTS: void broadcast(Transaction* transaction); @@ -78,6 +79,7 @@ private: graphene::chain::global_property_object m_global_properties; graphene::chain::dynamic_global_property_object m_dynamic_global_properties; + graphene::chain::chain_property_object m_chain_properties; ObjectId m_account_query_num = -1; account_multi_index_type m_accounts; diff --git a/programs/light_client/GrapheneApplication.cpp b/programs/light_client/GrapheneApplication.cpp index 463b2c4b..9365f968 100644 --- a/programs/light_client/GrapheneApplication.cpp +++ b/programs/light_client/GrapheneApplication.cpp @@ -100,7 +100,7 @@ void GrapheneApplication::signTransaction(Transaction* transaction) const return &model()->getAccount(id.instance.value)->accountObject().owner; }; - auto& chainId = model()->global_properties().chain_id; + auto& chainId = model()->chain_properties().chain_id; auto& trx = transaction->internalTransaction(); trx.set_reference_block(model()->dynamic_global_properties().head_block_id); flat_set pubKeys = wallet()->getAvailablePrivateKeys(); From 3a9d0df75c383e4a0e961d45384079b07e7a1c82 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 19 Aug 2015 11:16:22 -0400 Subject: [PATCH 2/5] [FWN] Create privileged API login for web UI This resolves #249 --- libraries/app/application.cpp | 10 ++++++++++ libraries/app/include/graphene/app/application.hpp | 1 + programs/full_web_node/BlockChain.cpp | 11 ++++++++++- programs/full_web_node/BlockChain.hpp | 4 ++++ programs/full_web_node/qml/main.qml | 6 +++++- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 51b861ed..00dc0dfc 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -313,6 +313,11 @@ namespace detail { return it->second; } + void set_api_access_info(const string& username, api_access_info&& permissions) + { + _apiaccess.permission_map.insert(std::make_pair(username, std::move(permissions))); + } + /** * If delegate has the item, the network has no need to fetch it. */ @@ -668,6 +673,11 @@ optional< api_access_info > application::get_api_access_info( const string& user return my->get_api_access_info( username ); } +void application::set_api_access_info(const string& username, api_access_info&& permissions) +{ + my->set_api_access_info(username, std::move(permissions)); +} + bool application::is_finished_syncing() const { return my->_is_finished_syncing; diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index 5c76d021..9518b608 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -76,6 +76,7 @@ namespace graphene { namespace app { void set_block_production(bool producing_blocks); fc::optional< api_access_info > get_api_access_info( const string& username )const; + void set_api_access_info(const string& username, api_access_info&& permissions); bool is_finished_syncing()const; /// Emitted when syncing finishes (is_finished_syncing will return true) diff --git a/programs/full_web_node/BlockChain.cpp b/programs/full_web_node/BlockChain.cpp index e2a0137b..0214c7f9 100644 --- a/programs/full_web_node/BlockChain.cpp +++ b/programs/full_web_node/BlockChain.cpp @@ -17,7 +17,9 @@ BlockChain::BlockChain() : chainThread(new fc::thread("chainThread")), fcTaskScheduler(new QTimer(this)), - grapheneApp(new graphene::app::application) + grapheneApp(new graphene::app::application), + webUsername(QStringLiteral("webui")), + webPassword(QString::fromStdString(fc::sha256::hash(fc::ecc::private_key::generate()))) { fcTaskScheduler->setInterval(100); fcTaskScheduler->setSingleShot(false); @@ -50,6 +52,13 @@ void BlockChain::start() grapheneApp->initialize_plugins(map); grapheneApp->startup(); grapheneApp->startup_plugins(); + + graphene::app::api_access_info webPermissions; + auto passHash = fc::sha256::hash(webPassword.toStdString()); + webPermissions.password_hash_b64 = fc::base64_encode(passHash.data(), passHash.data_size()); + webPermissions.password_salt_b64 = fc::base64_encode(""); + webPermissions.allowed_apis = {"database_api", "network_broadcast_api", "network_node_api", "history_api"}; + grapheneApp->set_api_access_info(webUsername.toStdString(), std::move(webPermissions)); } catch (const fc::exception& e) { elog("Crap went wrong: ${e}", ("e", e.to_detail_string())); } diff --git a/programs/full_web_node/BlockChain.hpp b/programs/full_web_node/BlockChain.hpp index b1efef63..90c745ea 100644 --- a/programs/full_web_node/BlockChain.hpp +++ b/programs/full_web_node/BlockChain.hpp @@ -10,11 +10,15 @@ namespace fc { class thread; } namespace graphene { namespace app { class application; } } class BlockChain : public QObject { Q_OBJECT + Q_PROPERTY(QString webUsername MEMBER webUsername CONSTANT) + Q_PROPERTY(QString webPassword MEMBER webPassword CONSTANT) fc::thread* chainThread; QTimer* fcTaskScheduler; graphene::app::application* grapheneApp; fc::future startFuture; + QString webUsername; + QString webPassword; public: BlockChain(); diff --git a/programs/full_web_node/qml/main.qml b/programs/full_web_node/qml/main.qml index f19ea3ae..7c156cb9 100644 --- a/programs/full_web_node/qml/main.qml +++ b/programs/full_web_node/qml/main.qml @@ -12,7 +12,11 @@ Window { BlockChain { id: blockChain - onStarted: webView.url = "qrc:/index.html" + onStarted: { + var url = "qrc:/index.html#authTokens/" + webUsername + ":" + webPassword + console.log("Loading %1 in web view".arg(url)) + webView.url = url + } } Component.onCompleted: blockChain.start() From 9c0c588ed62165886a510775d76c8524c50c09c5 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 19 Aug 2015 11:31:57 -0400 Subject: [PATCH 3/5] [FWN] Remove deprecated scheduling code --- programs/full_web_node/BlockChain.cpp | 8 +------- programs/full_web_node/BlockChain.hpp | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/programs/full_web_node/BlockChain.cpp b/programs/full_web_node/BlockChain.cpp index 0214c7f9..65fa4b4b 100644 --- a/programs/full_web_node/BlockChain.cpp +++ b/programs/full_web_node/BlockChain.cpp @@ -16,16 +16,10 @@ BlockChain::BlockChain() : chainThread(new fc::thread("chainThread")), - fcTaskScheduler(new QTimer(this)), grapheneApp(new graphene::app::application), webUsername(QStringLiteral("webui")), webPassword(QString::fromStdString(fc::sha256::hash(fc::ecc::private_key::generate()))) -{ - fcTaskScheduler->setInterval(100); - fcTaskScheduler->setSingleShot(false); - connect(fcTaskScheduler, &QTimer::timeout, [] {fc::yield();}); - fcTaskScheduler->start(); -} +{} BlockChain::~BlockChain() { startFuture.cancel_and_wait(__FUNCTION__); diff --git a/programs/full_web_node/BlockChain.hpp b/programs/full_web_node/BlockChain.hpp index 90c745ea..fbb45183 100644 --- a/programs/full_web_node/BlockChain.hpp +++ b/programs/full_web_node/BlockChain.hpp @@ -14,7 +14,6 @@ class BlockChain : public QObject { Q_PROPERTY(QString webPassword MEMBER webPassword CONSTANT) fc::thread* chainThread; - QTimer* fcTaskScheduler; graphene::app::application* grapheneApp; fc::future startFuture; QString webUsername; From eeeab17477a635fcff7f2d294dff8ecf25449b10 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 19 Aug 2015 13:07:13 -0400 Subject: [PATCH 4/5] Polish out-of-order-block handling, write test case --- libraries/chain/fork_database.cpp | 12 ++--- .../include/graphene/chain/fork_database.hpp | 2 + tests/tests/block_tests.cpp | 53 +++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index f867e69a..dcd48be1 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -51,7 +51,7 @@ shared_ptr fork_database::push_block(const signed_block& b) auto item = std::make_shared(b); try { _push_block(item); - } + } catch ( const unlinkable_block_exception& e ) { wlog( "Pushing block to fork database that failed to link." ); @@ -65,13 +65,14 @@ void fork_database::_push_block(const item_ptr& item) if( _head ) // make sure the block is within the range that we are caching { FC_ASSERT( item->num > std::max( 0, int64_t(_head->num) - (_max_size) ) ); - FC_ASSERT( item->num < _head->num + 32 ); + FC_ASSERT( item->num < _head->num + MAX_BLOCK_REORDERING ); } if( _head && item->previous_id() != block_id_type() ) { - auto itr = _index.get().find(item->previous_id()); - GRAPHENE_ASSERT(itr != _index.get().end(), unlinkable_block_exception, "block does not link to known chain"); + auto& index = _index.get(); + auto itr = index.find(item->previous_id()); + GRAPHENE_ASSERT(itr != index.end(), unlinkable_block_exception, "block does not link to known chain"); FC_ASSERT(!(*itr)->invalid); item->prev = *itr; } @@ -97,14 +98,13 @@ void fork_database::_push_block(const item_ptr& item) void fork_database::_push_next( const item_ptr& new_item ) { auto& prev_idx = _unlinked_index.get(); - vector newly_inserted; auto itr = prev_idx.find( new_item->id ); while( itr != prev_idx.end() ) { auto tmp = *itr; prev_idx.erase( itr ); - _push_block( tmp ); + _push_block( tmp ); itr = prev_idx.find( new_item->id ); } diff --git a/libraries/chain/include/graphene/chain/fork_database.hpp b/libraries/chain/include/graphene/chain/fork_database.hpp index a2144c41..34801b1a 100644 --- a/libraries/chain/include/graphene/chain/fork_database.hpp +++ b/libraries/chain/include/graphene/chain/fork_database.hpp @@ -63,6 +63,8 @@ namespace graphene { namespace chain { { public: typedef vector branch_type; + /// The maximum number of blocks that may be skipped in an out-of-order push + const static int MAX_BLOCK_REORDERING = 32; fork_database(); void reset(); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 7af5fc91..427fdd51 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -276,6 +276,59 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) } } +BOOST_AUTO_TEST_CASE( out_of_order_blocks ) +{ + try { + fc::temp_directory data_dir1( graphene::utilities::temp_directory_path() ); + fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() ); + + database db1; + db1.open(data_dir1.path(), make_genesis); + database db2; + db2.open(data_dir2.path(), make_genesis); + BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() ); + + auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); + auto b1 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b2 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b3 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b4 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b5 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b6 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b7 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b8 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b9 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b10 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b11 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b12 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + BOOST_CHECK_EQUAL(db1.head_block_num(), 12); + BOOST_CHECK_EQUAL(db2.head_block_num(), 0); + PUSH_BLOCK( db2, b1 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 1); + PUSH_BLOCK( db2, b3 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 1); + PUSH_BLOCK( db2, b2 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 3); + PUSH_BLOCK( db2, b5 ); + PUSH_BLOCK( db2, b6 ); + PUSH_BLOCK( db2, b7 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 3); + PUSH_BLOCK( db2, b4 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 7); + PUSH_BLOCK( db2, b8 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 8); + PUSH_BLOCK( db2, b11 ); + PUSH_BLOCK( db2, b10 ); + PUSH_BLOCK( db2, b12 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 8); + PUSH_BLOCK( db2, b9 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 12); + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE( undo_pending ) { try { From 7332797b76bc0040830e4bd47aed6cc4c9b53771 Mon Sep 17 00:00:00 2001 From: James Calfee Date: Thu, 20 Aug 2015 06:30:01 -0500 Subject: [PATCH 5/5] Updated witness command in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 55df22f1..1ab2ecfa 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Witness node The role of the witness node is to broadcast transactions, download blocks, and optionally sign them. ``` -./witness_node --rpc-endpoint 127.0.0.1:8090 --enable-stale-production -w '"1.6.0"' '"1.6.1"' '"1.6.2"' '"1.6.3"' '"1.6.4"' '"1.6.5"' '"1.6.6"' '"1.6.7"' '"1.6.8"' '"1.6.9"' +./witness_node --rpc-endpoint 127.0.0.1:8090 --enable-stale-production -w '"1.6.0"' '"1.6.1"' '"1.6.2"' '"1.6.3"' '"1.6.4"' '"1.6.5"' '"1.6.6"' '"1.6.7"' '"1.6.8"' '"1.6.9"' '"1.6.10"' '"1.6.11"' '"1.6.12"' '"1.6.13"' '"1.6.14"' '"1.6.15"' '"1.6.16"' '"1.6.17"' '"1.6.18"' '"1.6.19"' '"1.6.20"' '"1.6.21"' '"1.6.22"' '"1.6.23"' '"1.6.24"' '"1.6.25"' '"1.6.26"' '"1.6.27"' '"1.6.28"' '"1.6.29"' '"1.6.30"' '"1.6.31"' '"1.6.32"' '"1.6.33"' '"1.6.34"' '"1.6.35"' '"1.6.36"' '"1.6.37"' '"1.6.38"' '"1.6.39"' '"1.6.40"' '"1.6.41"' '"1.6.42"' '"1.6.43"' '"1.6.44"' '"1.6.45"' '"1.6.46"' '"1.6.47"' '"1.6.48"' '"1.6.49"' '"1.6.50"' '"1.6.51"' '"1.6.52"' '"1.6.53"' '"1.6.54"' '"1.6.55"' '"1.6.56"' '"1.6.57"' '"1.6.58"' '"1.6.59"' '"1.6.60"' '"1.6.61"' '"1.6.62"' '"1.6.63"' '"1.6.64"' '"1.6.65"' '"1.6.66"' '"1.6.67"' '"1.6.68"' '"1.6.69"' '"1.6.70"' '"1.6.71"' '"1.6.72"' '"1.6.73"' '"1.6.74"' '"1.6.75"' '"1.6.76"' '"1.6.77"' '"1.6.78"' '"1.6.79"' '"1.6.80"' '"1.6.81"' '"1.6.82"' '"1.6.83"' '"1.6.84"' '"1.6.85"' '"1.6.86"' '"1.6.87"' '"1.6.88"' '"1.6.89"' '"1.6.90"' '"1.6.91"' '"1.6.92"' '"1.6.93"' '"1.6.94"' '"1.6.95"' '"1.6.96"' '"1.6.97"' '"1.6.98"' '"1.6.99"' '"1.6.100"' ``` Running specific tests