diff --git a/Makefile.am b/Makefile.am
index 12a3acda..b1b5ccb7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -56,6 +56,7 @@ src_libbitcoin_node_la_SOURCES = \
src/protocols/protocol_header_in_70012.cpp \
src/protocols/protocol_header_out_31800.cpp \
src/protocols/protocol_header_out_70012.cpp \
+ src/protocols/protocol_observer.cpp \
src/protocols/protocol_transaction_in.cpp \
src/protocols/protocol_transaction_out.cpp \
src/sessions/session.cpp \
@@ -153,6 +154,7 @@ include_bitcoin_node_protocols_HEADERS = \
include/bitcoin/node/protocols/protocol_header_in_70012.hpp \
include/bitcoin/node/protocols/protocol_header_out_31800.hpp \
include/bitcoin/node/protocols/protocol_header_out_70012.hpp \
+ include/bitcoin/node/protocols/protocol_observer.hpp \
include/bitcoin/node/protocols/protocol_transaction_in.hpp \
include/bitcoin/node/protocols/protocol_transaction_out.hpp \
include/bitcoin/node/protocols/protocols.hpp
diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt
index be44db31..858df754 100644
--- a/builds/cmake/CMakeLists.txt
+++ b/builds/cmake/CMakeLists.txt
@@ -289,6 +289,7 @@ add_library( ${CANONICAL_LIB_NAME}
"../../src/protocols/protocol_header_in_70012.cpp"
"../../src/protocols/protocol_header_out_31800.cpp"
"../../src/protocols/protocol_header_out_70012.cpp"
+ "../../src/protocols/protocol_observer.cpp"
"../../src/protocols/protocol_transaction_in.cpp"
"../../src/protocols/protocol_transaction_out.cpp"
"../../src/sessions/session.cpp"
diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj
index c1cf45d0..0c750f9f 100644
--- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj
@@ -93,6 +93,7 @@
+
@@ -126,6 +127,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters
index 2ffaf9da..8525813e 100644
--- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters
@@ -108,6 +108,9 @@
src\protocols
+
+ src\protocols
+
src\protocols
@@ -203,6 +206,9 @@
include\bitcoin\node\protocols
+
+ include\bitcoin\node\protocols
+
include\bitcoin\node\protocols
diff --git a/console/executor.cpp b/console/executor.cpp
index fe68a2ba..eed5096b 100644
--- a/console/executor.cpp
+++ b/console/executor.cpp
@@ -45,6 +45,7 @@ using namespace std::placeholders;
// "c" avoids conflict with network "quit" messages.
const std::string executor::name_{ "bn" };
const std::string executor::close_{ "c" };
+const std::string executor::backup_{ "b" };
const std::unordered_map executor::defined_
{
{ levels::application, true },
@@ -93,10 +94,16 @@ const std::unordered_map executor::events_
{ database::event_t::close_file, "close_file" },
{ database::event_t::create_table, "create_table" },
{ database::event_t::verify_table, "verify_table" },
- { database::event_t::close_table, "close_table" }
+ { database::event_t::close_table, "close_table" },
+ { database::event_t::wait_lock, "wait_lock" },
+ { database::event_t::flush_table, "flush_table" },
+ { database::event_t::backup_table, "backup_table" },
+ { database::event_t::dump_table, "dump_table" },
+ { database::event_t::restore_table, "restore_table" }
};
const std::unordered_map executor::tables_
{
+ { database::table_t::store, "store" },
{ database::table_t::header_table, "header_table" },
{ database::table_t::header_head, "header_head" },
{ database::table_t::header_body, "header_body" },
@@ -1603,7 +1610,7 @@ void executor::subscribe_capture()
{
const auto token = system::trim_copy(line);
- // Close (this isn't a toggle).
+ // Close (not a toggle).
if (token == close_)
{
logger("CONSOLE: Close");
@@ -1611,6 +1618,26 @@ void executor::subscribe_capture()
return false;
}
+ // Backup (not a toggle).
+ if (token == backup_)
+ {
+ logger(BN_NODE_BACKUP_STARTED);
+ node_->pause();
+
+ const auto error = store_.snapshot([&](auto event, auto table)
+ {
+ logger(format(BN_BACKUP) % events_.at(event) % tables_.at(table));
+ });
+
+ if (error)
+ logger(format(BN_NODE_BACKUP_FAIL) % error.message());
+ else
+ logger(format(BN_NODE_BACKUP_COMPLETE));
+
+ node_->resume();
+ return !error;
+ }
+
if (!keys_.contains(token))
{
logger("CONSOLE: '" + line + "'");
diff --git a/console/executor.hpp b/console/executor.hpp
index da63aba4..0016ed33 100644
--- a/console/executor.hpp
+++ b/console/executor.hpp
@@ -85,6 +85,7 @@ class executor
static const std::string name_;
static const std::string close_;
+ static const std::string backup_;
static const std::unordered_map defined_;
static const std::unordered_map display_;
static const std::unordered_map keys_;
diff --git a/console/localize.hpp b/console/localize.hpp
index 10ccc4c3..3f0eaa5a 100644
--- a/console/localize.hpp
+++ b/console/localize.hpp
@@ -132,6 +132,8 @@ namespace node {
"open::%1%(%2%)"
#define BN_CLOSE \
"close::%1%(%2%)"
+#define BN_BACKUP \
+ "backup::%1%(%2%)"
#define BN_NODE_INTERRUPT \
"Press CTRL-C to stop the node."
@@ -141,6 +143,12 @@ namespace node {
"Please wait while the network is starting..."
#define BN_NODE_START_FAIL \
"Node failed to start with error, %1%."
+#define BN_NODE_BACKUP_STARTED \
+ "Node backup started."
+#define BN_NODE_BACKUP_FAIL \
+ "Node failed to backup with error, %1%."
+#define BN_NODE_BACKUP_COMPLETE \
+ "Node backup complete."
#define BN_NODE_STARTED \
"Node is started."
#define BN_NODE_RUNNING \
diff --git a/include/bitcoin/node.hpp b/include/bitcoin/node.hpp
index ad995182..805f4d15 100644
--- a/include/bitcoin/node.hpp
+++ b/include/bitcoin/node.hpp
@@ -46,6 +46,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/bitcoin/node/chasers/chaser.hpp b/include/bitcoin/node/chasers/chaser.hpp
index dd885f28..cfce568b 100644
--- a/include/bitcoin/node/chasers/chaser.hpp
+++ b/include/bitcoin/node/chasers/chaser.hpp
@@ -67,6 +67,14 @@ class BCN_API chaser
/// Issued by 'session_outbound' and handled by 'block_in_31800'.
stall,
+ /// Channels (all) are directed to pause reading.
+ /// Iissued by 'full_node' and handled by 'protocol'.
+ pause,
+
+ /// Channels (all) are directed to resume reading.
+ /// Iissued by 'full_node' and handled by 'protocol'.
+ resume,
+
/// A block has been downloaded, checked and stored (height_t).
/// Issued by 'block_in_31800' and handled by 'connect'.
checked,
diff --git a/include/bitcoin/node/full_node.hpp b/include/bitcoin/node/full_node.hpp
index 783bbd8a..40c9affc 100644
--- a/include/bitcoin/node/full_node.hpp
+++ b/include/bitcoin/node/full_node.hpp
@@ -29,6 +29,7 @@
namespace libbitcoin {
namespace node {
+ // Thread safe.
class BCN_API full_node
: public network::p2p
{
@@ -81,6 +82,12 @@ class BCN_API full_node
/// Methods.
/// -----------------------------------------------------------------------
+ /// Pause the node.
+ virtual void pause() NOEXCEPT;
+
+ /// Resume the node.
+ virtual void resume() NOEXCEPT;
+
/// The candidate chain is current.
virtual bool is_current() const NOEXCEPT;
diff --git a/include/bitcoin/node/protocols/protocol.hpp b/include/bitcoin/node/protocols/protocol.hpp
index fa23270f..b6a08473 100644
--- a/include/bitcoin/node/protocols/protocol.hpp
+++ b/include/bitcoin/node/protocols/protocol.hpp
@@ -32,7 +32,7 @@
namespace libbitcoin {
namespace node {
-/// Abstract base for node protocols.
+/// Abstract base for node protocols, thread safe.
class BCN_API protocol
: public network::protocol
{
diff --git a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp
index 897635d8..a13c9cb0 100644
--- a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp
+++ b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp
@@ -19,7 +19,6 @@
#ifndef LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BLOCK_IN_31800_HPP
#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BLOCK_IN_31800_HPP
-#include
#include
#include
#include
@@ -72,6 +71,8 @@ class BCN_API protocol_block_in_31800
chaser::chase event_, chaser::link value) NOEXCEPT;
virtual void do_get_downloads(chaser::count_t count) NOEXCEPT;
virtual void do_split(chaser::channel_t channel) NOEXCEPT;
+ void do_pause(chaser::channel_t channel) NOEXCEPT;
+ void do_resume(chaser::channel_t channel) NOEXCEPT;
/// Accept incoming block message.
virtual bool handle_receive_block(const code& ec,
diff --git a/include/bitcoin/node/protocols/protocol_observer.hpp b/include/bitcoin/node/protocols/protocol_observer.hpp
new file mode 100644
index 00000000..4cca0f7e
--- /dev/null
+++ b/include/bitcoin/node/protocols/protocol_observer.hpp
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_OBSERVER_HPP
+#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_OBSERVER_HPP
+
+#include
+#include
+#include
+
+namespace libbitcoin {
+namespace node {
+
+class BCN_API protocol_observer
+ : public node::protocol,
+ protected network::tracker
+{
+public:
+ typedef std::shared_ptr ptr;
+
+ template
+ protocol_observer(Session& session,
+ const channel_ptr& channel) NOEXCEPT
+ : node::protocol(session, channel),
+ network::tracker(session.log)
+ {
+ }
+
+ void start() NOEXCEPT override;
+
+ virtual void handle_event(const code& ec,
+ chaser::chase event_, chaser::link value) NOEXCEPT;
+
+ void do_pause(chaser::channel_t channel) NOEXCEPT;
+ void do_resume(chaser::channel_t channel) NOEXCEPT;
+};
+
+} // namespace node
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/node/protocols/protocols.hpp b/include/bitcoin/node/protocols/protocols.hpp
index 68dc5b78..e3d3d29f 100644
--- a/include/bitcoin/node/protocols/protocols.hpp
+++ b/include/bitcoin/node/protocols/protocols.hpp
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
#include
#include
diff --git a/include/bitcoin/node/sessions/attach.hpp b/include/bitcoin/node/sessions/attach.hpp
index f77c062e..b49f98f4 100644
--- a/include/bitcoin/node/sessions/attach.hpp
+++ b/include/bitcoin/node/sessions/attach.hpp
@@ -98,6 +98,7 @@ class attach
channel->attach(self)->start();
channel->attach(self)->start();
channel->attach(self)->start();
+ channel->attach(self)->start();
}
};
diff --git a/src/full_node.cpp b/src/full_node.cpp
index c6cff813..950b083f 100644
--- a/src/full_node.cpp
+++ b/src/full_node.cpp
@@ -173,6 +173,16 @@ void full_node::do_notify(const code& ec, chaser::chase event_,
// Methods.
// ----------------------------------------------------------------------------
+void full_node::pause() NOEXCEPT
+{
+ notify(error::success, chaser::chase::pause, {});
+}
+
+void full_node::resume() NOEXCEPT
+{
+ notify(error::success, chaser::chase::resume, {});
+}
+
bool full_node::is_current() const NOEXCEPT
{
if (is_zero(config_.node.currency_window_minutes))
@@ -207,7 +217,6 @@ chaser::event_subscriber& full_node::event_subscriber() NOEXCEPT
return event_subscriber_;
}
-// protected
const configuration& full_node::config() const NOEXCEPT
{
return config_;
diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp
index 6a1e5ff8..d674100a 100644
--- a/src/protocols/protocol_block_in_31800.cpp
+++ b/src/protocols/protocol_block_in_31800.cpp
@@ -201,39 +201,60 @@ void protocol_block_in_31800::stopping(const code& ec) NOEXCEPT
void protocol_block_in_31800::handle_event(const code&,
chaser::chase event_, chaser::link value) NOEXCEPT
{
+ constexpr auto minimum_for_stall_divide = 2_size;
+
if (stopped())
return;
- // There are count blocks to download at/above the given header.
if (event_ == chaser::chase::download)
{
+ // There are count blocks to download at/above the given header.
if (is_current())
{
BC_ASSERT(std::holds_alternative(value));
POST(do_get_downloads, std::get(value));
}
}
-
- // If value identifies this channel, split work and stop.
else if (event_ == chaser::chase::split)
{
BC_ASSERT(std::holds_alternative(value));
const auto channel = std::get(value);
+ // If value identifies this channel, split work and stop.
if (channel == identifier())
{
POST(do_split, channel);
}
}
-
- // If this channel has work, split it and stop.
else if (event_ == chaser::chase::stall)
{
- if (!map_->empty())
+ // If this channel has divisible work, split it and stop.
+ if (map_->size() >= minimum_for_stall_divide)
{
POST(do_split, chaser::count_t{});
}
}
+ else if (event_ == chaser::chase::pause)
+ {
+ // Pause local timers due to channel pause.
+ POST(do_pause, chaser::channel_t{});
+ }
+ else if (event_ == chaser::chase::resume)
+ {
+ // Resume local timers due to channel resume.
+ POST(do_resume, chaser::channel_t{});
+ }
+}
+
+void protocol_block_in_31800::do_pause(chaser::channel_t) NOEXCEPT
+{
+ pause_performance();
+}
+
+void protocol_block_in_31800::do_resume(chaser::channel_t) NOEXCEPT
+{
+ if (!map_->empty())
+ start_performance();
}
void protocol_block_in_31800::do_get_downloads(chaser::count_t) NOEXCEPT
diff --git a/src/protocols/protocol_observer.cpp b/src/protocols/protocol_observer.cpp
new file mode 100644
index 00000000..215103d9
--- /dev/null
+++ b/src/protocols/protocol_observer.cpp
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+#include
+#include
+#include
+#include
+
+namespace libbitcoin {
+namespace node {
+
+#define CLASS protocol_observer
+
+using namespace std::placeholders;
+
+void protocol_observer::start() NOEXCEPT
+{
+ BC_ASSERT(stranded());
+
+ if (started())
+ return;
+
+ // Events subscription is asynchronous.
+ async_subscribe_events(BIND(handle_event, _1, _2, _3));
+
+ protocol::start();
+}
+
+void protocol_observer::handle_event(const code&,
+ chaser::chase event_, chaser::link) NOEXCEPT
+{
+ if (stopped())
+ return;
+
+ if (event_ == chaser::chase::pause)
+ {
+ POST(do_pause, chaser::channel_t{});
+ }
+ else if (event_ == chaser::chase::resume)
+ {
+ POST(do_resume, chaser::channel_t{});
+ }
+}
+
+void protocol_observer::do_pause(chaser::channel_t) NOEXCEPT
+{
+ pause();
+}
+
+void protocol_observer::do_resume(chaser::channel_t) NOEXCEPT
+{
+ resume();
+}
+
+} // namespace node
+} // namespace libbitcoin
diff --git a/src/sessions/session_outbound.cpp b/src/sessions/session_outbound.cpp
index 82006773..08e3903e 100644
--- a/src/sessions/session_outbound.cpp
+++ b/src/sessions/session_outbound.cpp
@@ -130,7 +130,7 @@ void session_outbound::do_performance(uint64_t channel, uint64_t speed,
BC_ASSERT(stranded());
// Three elements are required to measure deviation, don't drop to two.
- constexpr auto mimimum_for_deviation = 3_size;
+ constexpr auto minimum_for_standard_deviation = 3_size;
if (speed == max_uint64)
{
@@ -150,7 +150,7 @@ void session_outbound::do_performance(uint64_t channel, uint64_t speed,
speeds_[channel] = static_cast(speed);
const auto count = speeds_.size();
- if (count <= mimimum_for_deviation)
+ if (count <= minimum_for_standard_deviation)
{
handler(error::success);
return;