From 6b342edc1cd84f417aedc5b26e2892ffdb4a6536 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 2 Jan 2026 04:20:42 -0500 Subject: [PATCH] Factor out libbitcoin-server. --- Makefile.am | 123 -- builds/cmake/CMakeLists.txt | 88 - builds/cmake/CMakePresets.json | 3 +- .../libbitcoin-node-test.vcxproj | 4 - .../libbitcoin-node-test.vcxproj.filters | 19 +- builds/msvc/vs2022/libbitcoin-node.sln | 18 - .../libbitcoin-node/libbitcoin-node.vcxproj | 46 - .../libbitcoin-node.vcxproj.filters | 169 +- console/embedded/embedded.hpp | 61 - console/embedded/explore_css.cpp | 36 - console/embedded/explore_ecma.cpp | 180 -- console/embedded/explore_font.cpp | 37 - console/embedded/explore_html.cpp | 44 - console/embedded/explore_icon.cpp | 37 - console/embedded/web_css.cpp | 33 - console/embedded/web_ecma.cpp | 32 - console/embedded/web_font.cpp | 37 - console/embedded/web_html.cpp | 42 - console/embedded/web_icon.cpp | 37 - console/executor.cpp | 151 -- console/executor.hpp | 178 -- console/executor_commands.cpp | 250 --- console/executor_dumps.cpp | 178 -- console/executor_events.cpp | 91 - console/executor_logging.cpp | 105 -- console/executor_options.cpp | 357 ---- console/executor_runner.cpp | 178 -- console/executor_scans.cpp | 445 ----- console/executor_store.cpp | 261 --- console/executor_test_reader.cpp | 947 ---------- console/executor_test_writer.cpp | 226 --- console/libbitcoin.ico | Bin 370070 -> 0 bytes console/localize.hpp | 276 --- console/main.cpp | 108 -- console/stack_trace.cpp | 256 --- console/stack_trace.hpp | 32 - include/bitcoin/node.hpp | 33 - .../node/channels/channel_electrum.hpp | 86 - .../bitcoin/node/channels/channel_http.hpp | 51 - .../node/channels/channel_stratum_v1.hpp | 56 - .../node/channels/channel_stratum_v2.hpp | 52 - include/bitcoin/node/channels/channel_ws.hpp | 109 -- include/bitcoin/node/channels/channels.hpp | 6 - include/bitcoin/node/chasers/chaser.hpp | 10 +- include/bitcoin/node/configuration.hpp | 37 +- include/bitcoin/node/define.hpp | 31 - include/bitcoin/node/error.hpp | 25 +- include/bitcoin/node/full_node.hpp | 23 +- .../node/impl/chasers/chaser_organize.ipp | 4 +- .../bitcoin/node/interfaces/bitcoind_rest.hpp | 86 - .../bitcoin/node/interfaces/bitcoind_rpc.hpp | 264 --- include/bitcoin/node/interfaces/electrum.hpp | 100 - include/bitcoin/node/interfaces/explore.hpp | 167 -- .../bitcoin/node/interfaces/interfaces.hpp | 45 - .../bitcoin/node/interfaces/stratum_v1.hpp | 73 - .../bitcoin/node/interfaces/stratum_v2.hpp | 109 -- include/bitcoin/node/interfaces/types.hpp | 58 - include/bitcoin/node/parser.hpp | 86 - .../bitcoin/node/parsers/bitcoind_query.hpp | 33 - .../bitcoin/node/parsers/bitcoind_target.hpp | 33 - .../bitcoin/node/parsers/electrum_version.hpp | 72 - .../bitcoin/node/parsers/explore_query.hpp | 33 - .../bitcoin/node/parsers/explore_target.hpp | 33 - include/bitcoin/node/parsers/parsers.hpp | 28 - include/bitcoin/node/protocols/protocol.hpp | 6 +- .../node/protocols/protocol_bitcoind_rest.hpp | 76 - .../node/protocols/protocol_bitcoind_rpc.hpp | 144 -- .../node/protocols/protocol_block_in_106.hpp | 2 +- .../protocols/protocol_block_in_31800.hpp | 4 +- .../node/protocols/protocol_block_out_106.hpp | 2 +- .../node/protocols/protocol_electrum.hpp | 133 -- .../protocols/protocol_electrum_version.hpp | 89 - .../node/protocols/protocol_explore.hpp | 184 -- .../bitcoin/node/protocols/protocol_html.hpp | 97 - .../bitcoin/node/protocols/protocol_http.hpp | 55 - .../node/protocols/protocol_observer.hpp | 4 +- .../node/protocols/protocol_performer.hpp | 6 +- .../bitcoin/node/protocols/protocol_rpc.hpp | 58 - .../node/protocols/protocol_stratum_v1.hpp | 92 - .../node/protocols/protocol_stratum_v2.hpp | 57 - .../protocols/protocol_transaction_in_106.hpp | 2 +- .../protocol_transaction_out_106.hpp | 2 +- .../bitcoin/node/protocols/protocol_web.hpp | 49 - include/bitcoin/node/protocols/protocols.hpp | 18 +- include/bitcoin/node/sessions/session.hpp | 6 +- .../node/sessions/session_handshake.hpp | 78 - .../bitcoin/node/sessions/session_peer.hpp | 12 +- .../bitcoin/node/sessions/session_server.hpp | 123 -- include/bitcoin/node/sessions/sessions.hpp | 19 - include/bitcoin/node/settings.hpp | 116 -- src/chasers/chaser.cpp | 30 +- src/chasers/chaser_block.cpp | 2 +- src/chasers/chaser_check.cpp | 10 +- src/chasers/chaser_confirm.cpp | 2 +- src/chasers/chaser_header.cpp | 2 +- src/chasers/chaser_snapshot.cpp | 6 +- src/chasers/chaser_storage.cpp | 2 +- src/chasers/chaser_validate.cpp | 16 +- src/configuration.cpp | 14 +- src/error.cpp | 25 +- src/full_node.cpp | 145 +- src/parser.cpp | 1618 ----------------- src/parsers/bitcoind_query.cpp | 42 - src/parsers/bitcoind_target.cpp | 44 - src/parsers/explore_query.cpp | 107 -- src/parsers/explore_target.cpp | 329 ---- src/protocols/protocol.cpp | 24 +- src/protocols/protocol_bitcoind_rest.cpp | 141 -- src/protocols/protocol_bitcoind_rpc.cpp | 440 ----- src/protocols/protocol_electrum.cpp | 260 --- src/protocols/protocol_electrum_version.cpp | 277 --- src/protocols/protocol_explore.cpp | 1232 ------------- src/protocols/protocol_html.cpp | 262 --- src/protocols/protocol_http.cpp | 53 - src/protocols/protocol_stratum_v1.cpp | 176 -- src/sessions/session.cpp | 24 +- src/sessions/session_inbound.cpp | 2 +- src/settings.cpp | 95 - test/configuration.cpp | 21 +- test/error.cpp | 175 -- test/parsers/bitcoind_query.cpp | 28 - test/parsers/bitcoind_target.cpp | 28 - test/parsers/explore_query.cpp | 31 - test/parsers/explore_target.cpp | 1289 ------------- test/settings.cpp | 213 --- 125 files changed, 182 insertions(+), 15245 deletions(-) delete mode 100644 console/embedded/embedded.hpp delete mode 100644 console/embedded/explore_css.cpp delete mode 100644 console/embedded/explore_ecma.cpp delete mode 100644 console/embedded/explore_font.cpp delete mode 100644 console/embedded/explore_html.cpp delete mode 100644 console/embedded/explore_icon.cpp delete mode 100644 console/embedded/web_css.cpp delete mode 100644 console/embedded/web_ecma.cpp delete mode 100644 console/embedded/web_font.cpp delete mode 100644 console/embedded/web_html.cpp delete mode 100644 console/embedded/web_icon.cpp delete mode 100644 console/executor.cpp delete mode 100644 console/executor.hpp delete mode 100644 console/executor_commands.cpp delete mode 100644 console/executor_dumps.cpp delete mode 100644 console/executor_events.cpp delete mode 100644 console/executor_logging.cpp delete mode 100644 console/executor_options.cpp delete mode 100644 console/executor_runner.cpp delete mode 100644 console/executor_scans.cpp delete mode 100644 console/executor_store.cpp delete mode 100644 console/executor_test_reader.cpp delete mode 100644 console/executor_test_writer.cpp delete mode 100644 console/libbitcoin.ico delete mode 100644 console/localize.hpp delete mode 100644 console/main.cpp delete mode 100644 console/stack_trace.cpp delete mode 100644 console/stack_trace.hpp delete mode 100644 include/bitcoin/node/channels/channel_electrum.hpp delete mode 100644 include/bitcoin/node/channels/channel_http.hpp delete mode 100644 include/bitcoin/node/channels/channel_stratum_v1.hpp delete mode 100644 include/bitcoin/node/channels/channel_stratum_v2.hpp delete mode 100644 include/bitcoin/node/channels/channel_ws.hpp delete mode 100644 include/bitcoin/node/interfaces/bitcoind_rest.hpp delete mode 100644 include/bitcoin/node/interfaces/bitcoind_rpc.hpp delete mode 100644 include/bitcoin/node/interfaces/electrum.hpp delete mode 100644 include/bitcoin/node/interfaces/explore.hpp delete mode 100644 include/bitcoin/node/interfaces/interfaces.hpp delete mode 100644 include/bitcoin/node/interfaces/stratum_v1.hpp delete mode 100644 include/bitcoin/node/interfaces/stratum_v2.hpp delete mode 100644 include/bitcoin/node/interfaces/types.hpp delete mode 100644 include/bitcoin/node/parser.hpp delete mode 100644 include/bitcoin/node/parsers/bitcoind_query.hpp delete mode 100644 include/bitcoin/node/parsers/bitcoind_target.hpp delete mode 100644 include/bitcoin/node/parsers/electrum_version.hpp delete mode 100644 include/bitcoin/node/parsers/explore_query.hpp delete mode 100644 include/bitcoin/node/parsers/explore_target.hpp delete mode 100644 include/bitcoin/node/parsers/parsers.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_bitcoind_rest.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_bitcoind_rpc.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_electrum.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_electrum_version.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_explore.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_html.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_http.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_rpc.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_stratum_v1.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_stratum_v2.hpp delete mode 100644 include/bitcoin/node/protocols/protocol_web.hpp delete mode 100644 include/bitcoin/node/sessions/session_handshake.hpp delete mode 100644 include/bitcoin/node/sessions/session_server.hpp delete mode 100644 src/parser.cpp delete mode 100644 src/parsers/bitcoind_query.cpp delete mode 100644 src/parsers/bitcoind_target.cpp delete mode 100644 src/parsers/explore_query.cpp delete mode 100644 src/parsers/explore_target.cpp delete mode 100644 src/protocols/protocol_bitcoind_rest.cpp delete mode 100644 src/protocols/protocol_bitcoind_rpc.cpp delete mode 100644 src/protocols/protocol_electrum.cpp delete mode 100644 src/protocols/protocol_electrum_version.cpp delete mode 100644 src/protocols/protocol_explore.cpp delete mode 100644 src/protocols/protocol_html.cpp delete mode 100644 src/protocols/protocol_http.cpp delete mode 100644 src/protocols/protocol_stratum_v1.cpp delete mode 100644 test/parsers/bitcoind_query.cpp delete mode 100644 test/parsers/bitcoind_target.cpp delete mode 100644 test/parsers/explore_query.cpp delete mode 100644 test/parsers/explore_target.cpp diff --git a/Makefile.am b/Makefile.am index 1b47545e3..da97c316f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,6 @@ src_libbitcoin_node_la_SOURCES = \ src/configuration.cpp \ src/error.cpp \ src/full_node.cpp \ - src/parser.cpp \ src/settings.cpp \ src/channels/channel_peer.cpp \ src/chasers/chaser.cpp \ @@ -53,31 +52,19 @@ src_libbitcoin_node_la_SOURCES = \ src/chasers/chaser_template.cpp \ src/chasers/chaser_transaction.cpp \ src/chasers/chaser_validate.cpp \ - src/parsers/bitcoind_query.cpp \ - src/parsers/bitcoind_target.cpp \ - src/parsers/explore_query.cpp \ - src/parsers/explore_target.cpp \ src/protocols/protocol.cpp \ - src/protocols/protocol_bitcoind_rest.cpp \ - src/protocols/protocol_bitcoind_rpc.cpp \ src/protocols/protocol_block_in_106.cpp \ src/protocols/protocol_block_in_31800.cpp \ src/protocols/protocol_block_out_106.cpp \ src/protocols/protocol_block_out_70012.cpp \ - src/protocols/protocol_electrum.cpp \ - src/protocols/protocol_electrum_version.cpp \ - src/protocols/protocol_explore.cpp \ src/protocols/protocol_filter_out_70015.cpp \ src/protocols/protocol_header_in_31800.cpp \ src/protocols/protocol_header_in_70012.cpp \ src/protocols/protocol_header_out_31800.cpp \ src/protocols/protocol_header_out_70012.cpp \ - src/protocols/protocol_html.cpp \ - src/protocols/protocol_http.cpp \ src/protocols/protocol_observer.cpp \ src/protocols/protocol_peer.cpp \ src/protocols/protocol_performer.cpp \ - src/protocols/protocol_stratum_v1.cpp \ src/protocols/protocol_transaction_in_106.cpp \ src/protocols/protocol_transaction_out_106.cpp \ src/sessions/session.cpp \ @@ -114,53 +101,11 @@ test_libbitcoin_node_test_SOURCES = \ test/chasers/chaser_template.cpp \ test/chasers/chaser_transaction.cpp \ test/chasers/chaser_validate.cpp \ - test/parsers/bitcoind_query.cpp \ - test/parsers/bitcoind_target.cpp \ - test/parsers/explore_query.cpp \ - test/parsers/explore_target.cpp \ test/protocols/protocol.cpp \ test/sessions/session.cpp endif WITH_TESTS -# console/bn => ${bindir} -#------------------------------------------------------------------------------ -if WITH_CONSOLE - -bin_PROGRAMS = console/bn -console_bn_CPPFLAGS = -I${srcdir}/include ${bitcoin_database_BUILD_CPPFLAGS} ${bitcoin_network_BUILD_CPPFLAGS} -console_bn_LDADD = src/libbitcoin-node.la ${bitcoin_database_LIBS} ${bitcoin_network_LIBS} -console_bn_SOURCES = \ - console/executor.cpp \ - console/executor.hpp \ - console/executor_commands.cpp \ - console/executor_dumps.cpp \ - console/executor_events.cpp \ - console/executor_logging.cpp \ - console/executor_options.cpp \ - console/executor_runner.cpp \ - console/executor_scans.cpp \ - console/executor_store.cpp \ - console/executor_test_reader.cpp \ - console/executor_test_writer.cpp \ - console/localize.hpp \ - console/main.cpp \ - console/stack_trace.cpp \ - console/stack_trace.hpp \ - console/embedded/embedded.hpp \ - console/embedded/explore_css.cpp \ - console/embedded/explore_ecma.cpp \ - console/embedded/explore_font.cpp \ - console/embedded/explore_html.cpp \ - console/embedded/explore_icon.cpp \ - console/embedded/web_css.cpp \ - console/embedded/web_ecma.cpp \ - console/embedded/web_font.cpp \ - console/embedded/web_html.cpp \ - console/embedded/web_icon.cpp - -endif WITH_CONSOLE - # files => ${includedir}/bitcoin #------------------------------------------------------------------------------ include_bitcoindir = ${includedir}/bitcoin @@ -177,19 +122,13 @@ include_bitcoin_node_HEADERS = \ include/bitcoin/node/error.hpp \ include/bitcoin/node/events.hpp \ include/bitcoin/node/full_node.hpp \ - include/bitcoin/node/parser.hpp \ include/bitcoin/node/settings.hpp \ include/bitcoin/node/version.hpp include_bitcoin_node_channelsdir = ${includedir}/bitcoin/node/channels include_bitcoin_node_channels_HEADERS = \ include/bitcoin/node/channels/channel.hpp \ - include/bitcoin/node/channels/channel_electrum.hpp \ - include/bitcoin/node/channels/channel_http.hpp \ include/bitcoin/node/channels/channel_peer.hpp \ - include/bitcoin/node/channels/channel_stratum_v1.hpp \ - include/bitcoin/node/channels/channel_stratum_v2.hpp \ - include/bitcoin/node/channels/channel_ws.hpp \ include/bitcoin/node/channels/channels.hpp include_bitcoin_node_chasersdir = ${includedir}/bitcoin/node/chasers @@ -211,93 +150,31 @@ include_bitcoin_node_impl_chasersdir = ${includedir}/bitcoin/node/impl/chasers include_bitcoin_node_impl_chasers_HEADERS = \ include/bitcoin/node/impl/chasers/chaser_organize.ipp -include_bitcoin_node_interfacesdir = ${includedir}/bitcoin/node/interfaces -include_bitcoin_node_interfaces_HEADERS = \ - include/bitcoin/node/interfaces/bitcoind_rest.hpp \ - include/bitcoin/node/interfaces/bitcoind_rpc.hpp \ - include/bitcoin/node/interfaces/electrum.hpp \ - include/bitcoin/node/interfaces/explore.hpp \ - include/bitcoin/node/interfaces/interfaces.hpp \ - include/bitcoin/node/interfaces/stratum_v1.hpp \ - include/bitcoin/node/interfaces/stratum_v2.hpp \ - include/bitcoin/node/interfaces/types.hpp - -include_bitcoin_node_parsersdir = ${includedir}/bitcoin/node/parsers -include_bitcoin_node_parsers_HEADERS = \ - include/bitcoin/node/parsers/bitcoind_query.hpp \ - include/bitcoin/node/parsers/bitcoind_target.hpp \ - include/bitcoin/node/parsers/electrum_version.hpp \ - include/bitcoin/node/parsers/explore_query.hpp \ - include/bitcoin/node/parsers/explore_target.hpp \ - include/bitcoin/node/parsers/parsers.hpp - include_bitcoin_node_protocolsdir = ${includedir}/bitcoin/node/protocols include_bitcoin_node_protocols_HEADERS = \ include/bitcoin/node/protocols/protocol.hpp \ - include/bitcoin/node/protocols/protocol_bitcoind_rest.hpp \ - include/bitcoin/node/protocols/protocol_bitcoind_rpc.hpp \ include/bitcoin/node/protocols/protocol_block_in_106.hpp \ include/bitcoin/node/protocols/protocol_block_in_31800.hpp \ include/bitcoin/node/protocols/protocol_block_out_106.hpp \ include/bitcoin/node/protocols/protocol_block_out_70012.hpp \ - include/bitcoin/node/protocols/protocol_electrum.hpp \ - include/bitcoin/node/protocols/protocol_electrum_version.hpp \ - include/bitcoin/node/protocols/protocol_explore.hpp \ include/bitcoin/node/protocols/protocol_filter_out_70015.hpp \ include/bitcoin/node/protocols/protocol_header_in_31800.hpp \ 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_html.hpp \ - include/bitcoin/node/protocols/protocol_http.hpp \ include/bitcoin/node/protocols/protocol_observer.hpp \ include/bitcoin/node/protocols/protocol_peer.hpp \ include/bitcoin/node/protocols/protocol_performer.hpp \ - include/bitcoin/node/protocols/protocol_rpc.hpp \ - include/bitcoin/node/protocols/protocol_stratum_v1.hpp \ - include/bitcoin/node/protocols/protocol_stratum_v2.hpp \ include/bitcoin/node/protocols/protocol_transaction_in_106.hpp \ include/bitcoin/node/protocols/protocol_transaction_out_106.hpp \ - include/bitcoin/node/protocols/protocol_web.hpp \ include/bitcoin/node/protocols/protocols.hpp include_bitcoin_node_sessionsdir = ${includedir}/bitcoin/node/sessions include_bitcoin_node_sessions_HEADERS = \ include/bitcoin/node/sessions/session.hpp \ - include/bitcoin/node/sessions/session_handshake.hpp \ include/bitcoin/node/sessions/session_inbound.hpp \ include/bitcoin/node/sessions/session_manual.hpp \ include/bitcoin/node/sessions/session_outbound.hpp \ include/bitcoin/node/sessions/session_peer.hpp \ - include/bitcoin/node/sessions/session_server.hpp \ include/bitcoin/node/sessions/sessions.hpp -# files => ${bash_completiondir} -#------------------------------------------------------------------------------ -if BASH_COMPLETIONDIR - -dist_bash_completion_DATA = \ - data/bn - -endif BASH_COMPLETIONDIR - -# files => ${sysconfdir}/libbitcoin -#------------------------------------------------------------------------------ -if WITH_CONSOLE - -sysconf_libbitcoindir = ${sysconfdir}/libbitcoin -sysconf_libbitcoin_DATA = \ - data/bn.cfg - -endif WITH_CONSOLE - - -# Custom make targets. -#============================================================================== -# make target: console -#------------------------------------------------------------------------------ -target_console = \ - console/bn - -console: ${target_console} - diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 15f2cd8de..ee4c8ed7d 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -252,7 +252,6 @@ add_library( ${CANONICAL_LIB_NAME} "../../src/configuration.cpp" "../../src/error.cpp" "../../src/full_node.cpp" - "../../src/parser.cpp" "../../src/settings.cpp" "../../src/channels/channel_peer.cpp" "../../src/chasers/chaser.cpp" @@ -265,31 +264,19 @@ add_library( ${CANONICAL_LIB_NAME} "../../src/chasers/chaser_template.cpp" "../../src/chasers/chaser_transaction.cpp" "../../src/chasers/chaser_validate.cpp" - "../../src/parsers/bitcoind_query.cpp" - "../../src/parsers/bitcoind_target.cpp" - "../../src/parsers/explore_query.cpp" - "../../src/parsers/explore_target.cpp" "../../src/protocols/protocol.cpp" - "../../src/protocols/protocol_bitcoind_rest.cpp" - "../../src/protocols/protocol_bitcoind_rpc.cpp" "../../src/protocols/protocol_block_in_106.cpp" "../../src/protocols/protocol_block_in_31800.cpp" "../../src/protocols/protocol_block_out_106.cpp" "../../src/protocols/protocol_block_out_70012.cpp" - "../../src/protocols/protocol_electrum.cpp" - "../../src/protocols/protocol_electrum_version.cpp" - "../../src/protocols/protocol_explore.cpp" "../../src/protocols/protocol_filter_out_70015.cpp" "../../src/protocols/protocol_header_in_31800.cpp" "../../src/protocols/protocol_header_in_70012.cpp" "../../src/protocols/protocol_header_out_31800.cpp" "../../src/protocols/protocol_header_out_70012.cpp" - "../../src/protocols/protocol_html.cpp" - "../../src/protocols/protocol_http.cpp" "../../src/protocols/protocol_observer.cpp" "../../src/protocols/protocol_peer.cpp" "../../src/protocols/protocol_performer.cpp" - "../../src/protocols/protocol_stratum_v1.cpp" "../../src/protocols/protocol_transaction_in_106.cpp" "../../src/protocols/protocol_transaction_out_106.cpp" "../../src/sessions/session.cpp" @@ -356,10 +343,6 @@ if (with-tests) "../../test/chasers/chaser_template.cpp" "../../test/chasers/chaser_transaction.cpp" "../../test/chasers/chaser_validate.cpp" - "../../test/parsers/bitcoind_query.cpp" - "../../test/parsers/bitcoind_target.cpp" - "../../test/parsers/explore_query.cpp" - "../../test/parsers/explore_target.cpp" "../../test/protocols/protocol.cpp" "../../test/sessions/session.cpp" ) @@ -384,51 +367,6 @@ if (with-tests) endif() -# Define bn project. -#------------------------------------------------------------------------------ -if (with-console) - add_executable( bn - "../../console/executor.cpp" - "../../console/executor.hpp" - "../../console/executor_commands.cpp" - "../../console/executor_dumps.cpp" - "../../console/executor_events.cpp" - "../../console/executor_logging.cpp" - "../../console/executor_options.cpp" - "../../console/executor_runner.cpp" - "../../console/executor_scans.cpp" - "../../console/executor_store.cpp" - "../../console/executor_test_reader.cpp" - "../../console/executor_test_writer.cpp" - "../../console/libbitcoin.ico" - "../../console/localize.hpp" - "../../console/main.cpp" - "../../console/stack_trace.cpp" - "../../console/stack_trace.hpp" - "../../console/embedded/embedded.hpp" - "../../console/embedded/explore_css.cpp" - "../../console/embedded/explore_ecma.cpp" - "../../console/embedded/explore_font.cpp" - "../../console/embedded/explore_html.cpp" - "../../console/embedded/explore_icon.cpp" - "../../console/embedded/web_css.cpp" - "../../console/embedded/web_ecma.cpp" - "../../console/embedded/web_font.cpp" - "../../console/embedded/web_html.cpp" - "../../console/embedded/web_icon.cpp" ) - -# bn project specific include directories. -#------------------------------------------------------------------------------ - target_include_directories( bn PRIVATE - "../../include" ) - -# bn project specific libraries/linker flags. -#------------------------------------------------------------------------------ - target_link_libraries( bn - ${CANONICAL_LIB_NAME} ) - -endif() - # Manage pkgconfig installation. #------------------------------------------------------------------------------ configure_file( @@ -458,34 +396,8 @@ install( TARGETS ${CANONICAL_LIB_NAME} ARCHIVE DESTINATION lib PUBLIC_HEADER DESTINATION include ) -# Manage bn installation. -#------------------------------------------------------------------------------ -if (with-console) - install( TARGETS bn - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - PUBLIC_HEADER DESTINATION include ) -endif() - # Manage include installation. #------------------------------------------------------------------------------ install( DIRECTORY "../../include/bitcoin" DESTINATION include ) -# Manage data installation for bash_completion prefixed product. -#------------------------------------------------------------------------------ -if (bash-completiondir) - install( FILES - "../../data/bn" - DESTINATION etc/libbitcoin ) -endif() - -# Manage data installation for sysconf prefixed product. -#------------------------------------------------------------------------------ -if (with-console) - install( FILES - "../../data/bn.cfg" - DESTINATION etc/libbitcoin ) -endif() - diff --git a/builds/cmake/CMakePresets.json b/builds/cmake/CMakePresets.json index 8637f1a33..3c9498677 100644 --- a/builds/cmake/CMakePresets.json +++ b/builds/cmake/CMakePresets.json @@ -125,8 +125,7 @@ "hidden": true, "targets": [ "bitcoin-node", - "libbitcoin-node-test", - "bn" + "libbitcoin-node-test" ] }, { diff --git a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj index 3ef94c2af..3e08a8b0d 100644 --- a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj @@ -134,10 +134,6 @@ - - - - diff --git a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters index ae03902e8..81248d215 100644 --- a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters @@ -13,14 +13,11 @@ {4BD50864-D3BC-4F64-0000-000000000001} - - {4BD50864-D3BC-4F64-0000-000000000002} - - {4BD50864-D3BC-4F64-0000-000000000003} + {4BD50864-D3BC-4F64-0000-000000000002} - {4BD50864-D3BC-4F64-0000-000000000004} + {4BD50864-D3BC-4F64-0000-000000000003} @@ -72,18 +69,6 @@ src - - src\parsers - - - src\parsers - - - src\parsers - - - src\parsers - src\protocols diff --git a/builds/msvc/vs2022/libbitcoin-node.sln b/builds/msvc/vs2022/libbitcoin-node.sln index 87409d8fe..70b4d374a 100644 --- a/builds/msvc/vs2022/libbitcoin-node.sln +++ b/builds/msvc/vs2022/libbitcoin-node.sln @@ -7,8 +7,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbitcoin-node", "libbitco EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbitcoin-node-test", "libbitcoin-node-test\libbitcoin-node-test.vcxproj", "{4BD50864-D3BC-4F64-AEBB-DDEB8A622FA8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bn", "bn\bn.vcxproj", "{D3404804-C83F-46CE-A5A6-3A39EF93DE46}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution StaticDebug|Win32 = StaticDebug|Win32 @@ -53,22 +51,6 @@ Global {4BD50864-D3BC-4F64-AEBB-DDEB8A622FA8}.StaticRelease|ARM.Build.0 = ReleaseSEXE|ARM {4BD50864-D3BC-4F64-AEBB-DDEB8A622FA8}.StaticRelease|ARM64.ActiveCfg = ReleaseSEXE|ARM64 {4BD50864-D3BC-4F64-AEBB-DDEB8A622FA8}.StaticRelease|ARM64.Build.0 = ReleaseSEXE|ARM64 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticDebug|Win32.ActiveCfg = DebugSEXE|Win32 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticDebug|Win32.Build.0 = DebugSEXE|Win32 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticDebug|x64.ActiveCfg = DebugSEXE|x64 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticDebug|x64.Build.0 = DebugSEXE|x64 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticRelease|Win32.ActiveCfg = ReleaseSEXE|Win32 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticRelease|Win32.Build.0 = ReleaseSEXE|Win32 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticRelease|x64.ActiveCfg = ReleaseSEXE|x64 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticRelease|x64.Build.0 = ReleaseSEXE|x64 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticDebug|ARM.ActiveCfg = DebugSEXE|ARM - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticDebug|ARM.Build.0 = DebugSEXE|ARM - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticDebug|ARM64.ActiveCfg = DebugSEXE|ARM64 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticDebug|ARM64.Build.0 = DebugSEXE|ARM64 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticRelease|ARM.ActiveCfg = ReleaseSEXE|ARM - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticRelease|ARM.Build.0 = ReleaseSEXE|ARM - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticRelease|ARM64.ActiveCfg = ReleaseSEXE|ARM64 - {D3404804-C83F-46CE-A5A6-3A39EF93DE46}.StaticRelease|ARM64.Build.0 = ReleaseSEXE|ARM64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj index 15549b8eb..f64d3771a 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj @@ -137,32 +137,19 @@ - - - - - - - - - - - - - @@ -176,12 +163,7 @@ - - - - - @@ -201,55 +183,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters index 9ecd4a174..4e4e414bd 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters @@ -8,40 +8,34 @@ - {5FFB5F52-0772-4404-0000-000000000006} + {5FFB5F52-0772-4404-0000-000000000005} - {5FFB5F52-0772-4404-0000-000000000007} + {5FFB5F52-0772-4404-0000-000000000006} - {5FFB5F52-0772-4404-0000-000000000008} + {5FFB5F52-0772-4404-0000-000000000007} - {5FFB5F52-0772-4404-0000-000000000009} + {5FFB5F52-0772-4404-0000-000000000008} - {5FFB5F52-0772-4404-0000-00000000000A} + {5FFB5F52-0772-4404-0000-000000000009} - {5FFB5F52-0772-4404-0000-00000000000B} + {5FFB5F52-0772-4404-0000-00000000000A} - {5FFB5F52-0772-4404-0000-000000000001} - - - {5FFB5F52-0772-4404-0000-00000000000C} - - {5FFB5F52-0772-4404-0000-00000000000D} - {5FFB5F52-0772-4404-0000-00000000000E} + {5FFB5F52-0772-4404-0000-00000000000B} - {5FFB5F52-0772-4404-0000-00000000000F} + {5FFB5F52-0772-4404-0000-00000000000C} - {5FFB5F52-0772-4404-0000-000000000002} + {5FFB5F52-0772-4404-0000-00000000000E} {5FFB5F52-0772-4404-0000-000000000000} @@ -52,14 +46,11 @@ {5FFB5F52-0772-4404-0000-000000000002} - - {5FFB5F52-0772-4404-0000-000000000003} - - {5FFB5F52-0772-4404-0000-000000000004} + {5FFB5F52-0772-4404-0000-000000000003} - {5FFB5F52-0772-4404-0000-000000000005} + {5FFB5F52-0772-4404-0000-000000000004} @@ -111,30 +102,9 @@ src - - src - - - src\parsers - - - src\parsers - - - src\parsers - - - src\parsers - src\protocols - - src\protocols - - - src\protocols - src\protocols @@ -147,15 +117,6 @@ src\protocols - - src\protocols - - - src\protocols - - - src\protocols - src\protocols @@ -171,12 +132,6 @@ src\protocols - - src\protocols - - - src\protocols - src\protocols @@ -186,9 +141,6 @@ src\protocols - - src\protocols - src\protocols @@ -224,24 +176,9 @@ include\bitcoin\node\channels - - include\bitcoin\node\channels - - - include\bitcoin\node\channels - include\bitcoin\node\channels - - include\bitcoin\node\channels - - - include\bitcoin\node\channels - - - include\bitcoin\node\channels - include\bitcoin\node\channels @@ -299,60 +236,9 @@ include\bitcoin\node - - include\bitcoin\node\interfaces - - - include\bitcoin\node\interfaces - - - include\bitcoin\node\interfaces - - - include\bitcoin\node\interfaces - - - include\bitcoin\node\interfaces - - - include\bitcoin\node\interfaces - - - include\bitcoin\node\interfaces - - - include\bitcoin\node\interfaces - - - include\bitcoin\node - - - include\bitcoin\node\parsers - - - include\bitcoin\node\parsers - - - include\bitcoin\node\parsers - - - include\bitcoin\node\parsers - - - include\bitcoin\node\parsers - - - include\bitcoin\node\parsers - include\bitcoin\node\protocols - - include\bitcoin\node\protocols - - - include\bitcoin\node\protocols - include\bitcoin\node\protocols @@ -365,15 +251,6 @@ include\bitcoin\node\protocols - - include\bitcoin\node\protocols - - - include\bitcoin\node\protocols - - - include\bitcoin\node\protocols - include\bitcoin\node\protocols @@ -389,12 +266,6 @@ include\bitcoin\node\protocols - - include\bitcoin\node\protocols - - - include\bitcoin\node\protocols - include\bitcoin\node\protocols @@ -404,33 +275,18 @@ include\bitcoin\node\protocols - - include\bitcoin\node\protocols - - - include\bitcoin\node\protocols - - - include\bitcoin\node\protocols - include\bitcoin\node\protocols include\bitcoin\node\protocols - - include\bitcoin\node\protocols - include\bitcoin\node\protocols include\bitcoin\node\sessions - - include\bitcoin\node\sessions - include\bitcoin\node\sessions @@ -443,9 +299,6 @@ include\bitcoin\node\sessions - - include\bitcoin\node\sessions - include\bitcoin\node\sessions diff --git a/console/embedded/embedded.hpp b/console/embedded/embedded.hpp deleted file mode 100644 index ab0d15b59..000000000 --- a/console/embedded/embedded.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_BN_EMBEDDED_HPP -#define LIBBITCOIN_BN_EMBEDDED_HPP - -#include "../executor.hpp" - -#include -#include - -#define DECLARE_EMBEDDED_PAGE(page) \ -span_value page() const NOEXCEPT override - -#define DECLARE_EMBEDDED_PAGES(container) \ -struct container : public server::settings::embedded_pages \ -{ \ - DECLARE_EMBEDDED_PAGE(css); \ - DECLARE_EMBEDDED_PAGE(html); \ - DECLARE_EMBEDDED_PAGE(ecma); \ - DECLARE_EMBEDDED_PAGE(font); \ - DECLARE_EMBEDDED_PAGE(icon); \ -} - -#define DEFINE_EMBEDDED_PAGE(container, type, name, ...) \ -span_value container::name() const NOEXCEPT \ -{ \ - static constexpr type name##_[] = __VA_ARGS__; \ - static const span_value out \ - ( \ - const_cast(reinterpret_cast(&name##_[0])), \ - literal_length(name##_) \ - ); \ - return out; \ -} - -namespace libbitcoin { -namespace server { - -DECLARE_EMBEDDED_PAGES(web_pages); -DECLARE_EMBEDDED_PAGES(explore_pages); - -} // namespace server -} // namespace libbitcoin - -#endif diff --git a/console/embedded/explore_css.cpp b/console/embedded/explore_css.cpp deleted file mode 100644 index 541f925f7..000000000 --- a/console/embedded/explore_css.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -// Simple test css for embedded page, links in font. -DEFINE_EMBEDDED_PAGE(explore_pages, char, css, - R"DELIM(*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}[type=text],input:where(:not([type])),[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:n)DELIM" - R"DELIM(one;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,input:where(:not([type])):focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}@media (forced-colors: active){[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}@media (forced-colors: active){[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}@media (forced-colors: active){[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background)DELIM" - R"DELIM(:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}.visible{visibility:visible}.order-1{order:1}.order-2{order:2}.order-3{order:3}.mx-auto{margin-left:auto;margin-right:auto}.mb-4{margin-bottom:1rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.\!block{display:block!important}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-4{height:1rem}.h-full{height:100%}.min-h-\[calc\(100vh-4rem\)\]{min-height:calc(100vh - 4rem)}.min-h-screen{min-height:100vh}.w-2{width:.5rem}.w-4{width:1rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.min-w-\[12rem\]{min-width:12rem}.min-w-full{min-width:100%}.max-w-7xl{max-width:80rem}.flex-1{flex:1 1 0%}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-10{gap:2.5rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-accentSecondary\/30>:not([hidden])~:not([hidden]){border-color:#c856374d}.place-self-start{place-self:start}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-accent{--tw-border-opacity: 1;border-color:rgb(255 160 34 / var(--tw-border-opacity, 1))}.border-accent\/20{border-color:#ffa02233}.border-accent\/30{border-color:#ffa0224d}.border-accent\/40{border-color:#ffa02266}.border-accent\/50{border-color:#ffa02280}.border-accentSecondary\/30{border-color:#c856374d}.border-amber-500\/50{border-color:#f59e0b80}.border-emerald-500\/50{border-color:#10b98180}.border-rose-500\/50{border-color:#f43f5e80}.border-slate-500\/50{border-color:#64748b80}.bg-accentSecondary\/20{background-color:#c8563733}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-amber-500\/20{background-color:#f59e0b33}.bg-background{--tw-bg-opacity: 1;background-color:rgb(39 40 34 / var(--tw-bg-opacity, 1))}.bg-black\/30{background-color:#0000004d}.bg-current{background-color:currentColor}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity, 1))}.bg-emerald-500\/20{background-color:#10b98133}.bg-rose-400{--tw-bg-opacity: 1;background-color:rgb(251 113 133 / var(--tw-bg-opacity, 1))}.bg-rose-500\/20{background-color:#f43f5e33}.bg-slate-400{--tw-bg-opacity: 1;background-color:rgb(148 163 184 / var(--tw-bg-opacity, 1))}.bg-slate-500\/20{background-color:#64748b33}.bg-surface\/60{background-color:#1f211c99}.bg-surface\/80{background-color:#1f211ccc}.bg-surface\/90{background-color:#1f211ce6}.bg-transparent{background-color:transparent}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Mona)DELIM" - R"DELIM(co,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.text-accent{--tw-text-opacity: 1;color:rgb(255 160 34 / var(--tw-text-opacity, 1))}.text-accentSecondary{--tw-text-opacity: 1;color:rgb(200 86 55 / var(--tw-text-opacity, 1))}.text-amber-300{--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity, 1))}.text-emerald-300{--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity, 1))}.text-rose-300{--tw-text-opacity: 1;color:rgb(253 164 175 / var(--tw-text-opacity, 1))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.text-textMuted{--tw-text-opacity: 1;color:rgb(207 207 207 / var(--tw-text-opacity, 1))}.text-textPrimary{--tw-text-opacity: 1;color:rgb(237 237 237 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}:root{color-scheme:dark}body{--tw-bg-opacity: 1;background-color:rgb(39 40 34 / var(--tw-bg-opacity, 1));font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";--tw-text-opacity: 1;color:rgb(237 237 237 / var(--tw-text-opacity, 1))}::-moz-selection{--tw-bg-opacity: 1;background-color:rgb(255 160 34 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(39 40 34 / var(--tw-text-opacity, 1))}::selection{--tw-bg-opacity: 1;background-color:rgb(255 160 34 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(39 40 34 / var(--tw-text-opacity, 1))}.placeholder\:text-textMuted::-moz-placeholder{--tw-text-opacity: 1;color:rgb(207 207 207 / var(--tw-text-opacity, 1))}.placeholder\:text-textMuted::placeholder{--tw-text-opacity: 1;color:rgb(207 207 207 / var(--tw-text-opacity, 1))}.hover\:border-accent:hover{--tw-border-opacity: 1;border-color:rgb(255 160 34 / var(--tw-border-opacity, 1))}.hover\:border-accentSecondary:hover{--tw-border-opacity: 1;border-color:rgb(200 86 55 / var(--tw-border-opacity, 1))}.hover\:bg-accentSecondary\/10:hover{background-color:#c856371a}.hover\:text-accent:hover{--tw-text-opacity: 1;color:rgb(255 160 34 / var(--tw-text-opacity, 1))}.hover\:text-accentSecondary:hover{--tw-text-opacity: 1;color:rgb(200 86 55 / var(--tw-text-opacity, 1))}.focus\:border-accent:focus{--tw-border-opacity: 1;border-color:rgb(255 160 34 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-accent:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(255 160 34 / var(--tw-ring-opacity, 1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-60:disabled{opacity:.6}@media (min-width: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}}@media (min-width: 768px){.md\:order-3{order:3}.md\:order-4{order:4}.md\:ml-auto{margin-left:auto}.md\:inline{display:inline}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-72{width:18rem}.md\:w-auto{width:auto}.md\:flex-none{flex:none}.md\:flex-nowrap{flex-wrap:nowrap}}@media (min-width: 1024px){.lg\:grid-co)DELIM" - R"DELIM(ls-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}} -)DELIM" - -) - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/explore_ecma.cpp b/console/embedded/explore_ecma.cpp deleted file mode 100644 index 12e7e1211..000000000 --- a/console/embedded/explore_ecma.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -// Simple test ecma script for embedded page. -DEFINE_EMBEDDED_PAGE(explore_pages, char, ecma, - R"DELIM(var Jf=e=>{throw TypeError(e)};var uu=(e,t,n)=>t.has(e)||Jf("Cannot "+n);var _=(e,t,n)=>(uu(e,t,"read from private field"),n?n.call(e):t.get(e)),ne=(e,t,n)=>t.has(e)?Jf("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),H=(e,t,n,r)=>(uu(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n),fe=(e,t,n)=>(uu(e,t,"access private method"),n);var so=(e,t,n,r)=>({set _(i){H(e,t,i,n)},get _(){return _(e,t,r)}});function jm(e,t){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const a of s.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&r(a)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var E0=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Ll(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Mm={exports:{}},Ol={},Lm={exports:{}},de={};/** - * @license React - * react.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Ua=Symbol.for("react.element"),C0=Symbol.for("react.portal"),T0=Symbol.for("react.fragment"),b0=Symbol.for("react.strict_mode"),N0=Symbol.for("react.profiler"),P0=Symbol.for("react.provider"),R0=Symbol.for("react.context"),j0=Symbol.for("react.forward_ref"),M0=Symbol.for("react.suspense"),L0=Symbol.for("react.memo"),O0=Symbol.for("react.lazy"),eh=Symbol.iterator;function D0(e){return e===null||typeof e!="object"?null:(e=eh&&e[eh]||e["@@iterator"],typeof e=="function"?e:null)}var Om={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Dm=Object.assign,Am={};function Cs(e,t,n){this.props=e,this.context=t,this.refs=Am,this.updater=n||Om}Cs.prototype.isReactComponent={};Cs.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};Cs.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Im(){}Im.prototype=Cs.prototype;function kd(e,t,n){this.props=e,this.context=t,this.refs=Am,this.updater=n||Om}var _d=kd.prototype=new Im;_d.constructor=kd;Dm(_d,Cs.prototype);_d.isPureReactComponent=!0;var th=Array.isArray,$m=Object.prototype.hasOwnProperty,Sd={current:null},Fm={key:!0,ref:!0,__self:!0,__source:!0};function zm(e,t,n){var r,i={},s=null,a=null;if(t!=null)for(r in t.ref!==void 0&&(a=t.ref),t.key!==void 0&&(s=""+t.key),t)$m.call(t,r)&&!Fm.hasOwnProperty(r)&&(i[r]=t[r]);var o=arguments.length-2;if(o===1)i.children=n;else if(1>>1,re=b[ee];if(0>>1;eei(Te,I))Iei(Ve,Te)?(b[ee]=Ve,b[Ie]=I,ee=Ie):(b[ee]=Te,b[_e]=I,ee=_e);else if(Iei(Ve,I))b[ee]=Ve,b[Ie]=I,ee=Ie;else break e}}return D}function i(b,D){var I=b.sortIndex-D.sortIndex;return I!==0?I:b.id-D.id}if(typeof performance=="object"&&typeof performance.now=="function"){var s=performance;e.unstable_now=function(){return s.now()}}else{var a=Date,o=a.now();e.unstable_now=function(){return a.now()-o}}var l=[],u=[],d=1,c=null,f=3,m=!1,g=!1,x=!1,T=typeof setTimeout=="function"?setTimeout:null,v=typeof clearTimeout=="function"?clearTimeout:null,p=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function y(b){for(var D=n(u);D!==null;){if(D.callback===null)r(u);else if(D.startTime<=b)r(u),D.sortIndex=D.expirationTime,t(l,D);else break;D=n(u)}}function S(b){if(x=!1,y(b),!g)if(n(l)!==null)g=!0,$(R);else{var D=n(u);D!==null&&K(S,D.startTime-b)}}function R(b,D){g=!1,x&&(x=!1,v(j),j=-1),m=!0;var I=f;try{for(y(D),c=n(l);c!==null&&(!(c.expirationTime>D)||b&&!B());){var ee=c.callback;if(typeof ee=="function"){c.callback=null,f=c.priorityLevel;var re=ee(c.expirationTime<=D);D=e.unstable_now(),typeof re=="function"?c.callback=re:c===n(l)&&r(l),y(D)}else r(l);c=n(l)}if(c!==null)var ke=!0;else{var _e=n(u);_e!==null&&K(S,_e.startTime-D),ke=!1}return ke}finally{c=null,f=I,m=!1}}var L=!1,k=null,j=-1,W=5,P=-1;function B(){return!(e.unstable_now()-Pb||125ee?(b.sortIndex=I,t(u,b),n(l)===null&&b===n(u)&&(x?(v(j),j=-1):x=!0,K(S,I-ee))):(b.sortIndex=re,t(l,b),g||m||(g=!0,$(R))),b},e.unstable_shouldYield=B,e.unstable_wrapCallback=function(b){var D=f;return function(){var I=f;f=D;try{return b.apply(this,arguments)}finally{f=I}}}})(Wm);Qm.exports=Wm;var Z0=Qm.exports;/** - * @license React - * react-dom.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var K0=C,Dt=Z0;function O(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),qu=Object.prototype.hasOwnProperty,G0=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,rh={},ih={};function q0(e){return qu.call(ih,e)?!0:qu.call(rh,e)?!1:G0.test(e)?ih[e]=!0:(rh[)DELIM" - R"DELIM(e]=!0,!1)}function Y0(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function X0(e,t,n,r){if(t===null||typeof t>"u"||Y0(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function kt(e,t,n,r,i,s,a){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=i,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=s,this.removeEmptyString=a}var ut={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){ut[e]=new kt(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];ut[t]=new kt(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){ut[e]=new kt(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){ut[e]=new kt(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){ut[e]=new kt(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){ut[e]=new kt(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){ut[e]=new kt(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){ut[e]=new kt(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){ut[e]=new kt(e,5,!1,e.toLowerCase(),null,!1,!1)});var Cd=/[\-:]([a-z])/g;function Td(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Cd,Td);ut[t]=new kt(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Cd,Td);ut[t]=new kt(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Cd,Td);ut[t]=new kt(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){ut[e]=new kt(e,1,!1,e.toLowerCase(),null,!1,!1)});ut.xlinkHref=new kt("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){ut[e]=new kt(e,1,!1,e.toLowerCase(),null,!0,!0)});function bd(e,t,n,r){var i=ut.hasOwnProperty(t)?ut[t]:null;(i!==null?i.type!==0:r||!(2o||i[a]!==s[o]){var l=` -`+i[a].replace(" at new "," at ");return e.displayName&&l.includes("")&&(l=l.replace("",e.displayName)),l}while(1<=a&&0<=o);break}}}finally{fu=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?Zs(e):""}function J0(e){switch(e.tag){case 5:return Zs(e.type);case 16:return Zs("Lazy");case 13:return Zs("Suspense");case 19:return Zs("SuspenseList");case 0:case 2:case 15:return e=hu(e.type,!1),e;case 11:return e=hu(e.type.render,!1),e;case 1:return e=hu(e.type,!0),e;default:return""}}function ec(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Ai:return"Fragment";case Di:return"Portal";case Yu:return"Profiler";case Nd:return"StrictMode";case Xu:return"Suspense";case Ju:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Gm:return(e.displayName||"Context")+".Consumer";case Km:return(e._context.displayName||"Context")+".Provider";case Pd:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Rd:return t=e.displayName||null,t!==null?t:ec(e.type)||"Memo";case or:t=e._payload,e=e._init;try{return ec(e(t))}catch{}}return null}function ex(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ec(t);case 8:return t===Nd?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function Dr(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Ym(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function tx(e){var t=Ym(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var i=n.get,s=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return i.call(this)},set:function(a){r=""+a,s.call(this,a)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(a){r=""+a},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function lo(e){e._valueTracker||(e._valueTracker=tx(e))}function Xm(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Ym(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function qo(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function tc(e,t){var n=t.checked;return Ae({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function ah(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=Dr(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Jm(e,t){t=t.checked,t!=null&&bd(e,"checked",t,!1)}function nc(e,t){Jm(e,t);var n=Dr(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?rc(e,t.type,n):t.hasOwnProperty("defaultValue")&&rc(e,t.type,Dr(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function oh(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperStat)DELIM" -R"DELIM(e.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function rc(e,t,n){(t!=="number"||qo(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Ks=Array.isArray;function Zi(e,t,n,r){if(e=e.options,t){t={};for(var i=0;i"+t.valueOf().toString()+"",t=uo.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function fa(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var ta={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},nx=["Webkit","ms","Moz","O"];Object.keys(ta).forEach(function(e){nx.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),ta[t]=ta[e]})});function rv(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||ta.hasOwnProperty(e)&&ta[e]?(""+t).trim():t+"px"}function iv(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,i=rv(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,i):e[n]=i}}var rx=Ae({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ac(e,t){if(t){if(rx[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(O(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(O(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(O(61))}if(t.style!=null&&typeof t.style!="object")throw Error(O(62))}}function oc(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var lc=null;function jd(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var uc=null,Ki=null,Gi=null;function ch(e){if(e=Qa(e)){if(typeof uc!="function")throw Error(O(280));var t=e.stateNode;t&&(t=Fl(t),uc(e.stateNode,e.type,t))}}function sv(e){Ki?Gi?Gi.push(e):Gi=[e]:Ki=e}function av(){if(Ki){var e=Ki,t=Gi;if(Gi=Ki=null,ch(e),t)for(e=0;e>>=0,e===0?32:31-(px(e)/mx|0)|0}var co=64,fo=4194304;function Gs(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function el(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,i=e.suspendedLanes,s=e.pingedLanes,a=n&268435455;if(a!==0){var o=a&~i;o!==0?r=Gs(o):(s&=a,s!==0&&(r=Gs(s)))}else a=n&~i,a!==0?r=Gs(a):s!==0&&(r=Gs(s));if(r===0)return 0;if(t!==0&&t!==r&&!(t&i)&&(i=r&-r,s=t&-t,i>=s||i===16&&(s&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Ha(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-cn(t),e[t]=n}function xx(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=ra),xh=" ",wh=!1;function Tv(e,t){switch(e){case"keyup":return Zx.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function bv(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Ii=!1;function Gx(e,t){switch(e){case"compositionend":return bv(t);case"keypress":return t.which!==32?null:(wh=!0,xh);case"textInput":return e=t.data,e===xh&&wh?null:e;default:return null}}function qx(e,t){if(Ii)return e==="compositionend"||!Fd&&Tv(e,t)?(e=Ev(),Ao=Ad=wr=null,Ii=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&)DELIM" -R"DELIM(&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Eh(n)}}function jv(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?jv(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Mv(){for(var e=window,t=qo();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=qo(e.document)}return t}function zd(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function s1(e){var t=Mv(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&jv(n.ownerDocument.documentElement,n)){if(r!==null&&zd(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var i=n.textContent.length,s=Math.min(r.start,i);r=r.end===void 0?s:Math.min(r.end,i),!e.extend&&s>r&&(i=r,r=s,s=i),i=Ch(n,s);var a=Ch(n,r);i&&a&&(e.rangeCount!==1||e.anchorNode!==i.node||e.anchorOffset!==i.offset||e.focusNode!==a.node||e.focusOffset!==a.offset)&&(t=t.createRange(),t.setStart(i.node,i.offset),e.removeAllRanges(),s>r?(e.addRange(t),e.extend(a.node,a.offset)):(t.setEnd(a.node,a.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,$i=null,mc=null,sa=null,vc=!1;function Th(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;vc||$i==null||$i!==qo(r)||(r=$i,"selectionStart"in r&&zd(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),sa&&ga(sa,r)||(sa=r,r=rl(mc,"onSelect"),0Bi||(e.current=_c[Bi],_c[Bi]=null,Bi--)}function Pe(e,t){Bi++,_c[Bi]=e.current,e.current=t}var Ar={},pt=zr(Ar),Nt=zr(!1),yi=Ar;function hs(e,t){var n=e.type.contextTypes;if(!n)return Ar;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var i={},s;for(s in n)i[s]=t[s];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=i),i}function Pt(e){return e=e.childContextTypes,e!=null}function sl(){je(Nt),je(pt)}function Lh(e,t,n){if(pt.current!==Ar)throw Error(O(168));Pe(pt,t),Pe(Nt,n)}function Bv(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var i in r)if(!(i in t))throw Error(O(108,ex(e)||"Unknown",i));return Ae({},n,r)}function al(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Ar,yi=pt.current,Pe(pt,e),Pe(Nt,Nt.current),!0}function Oh(e,t,n){var r=e.stateNode;if(!r)throw Error(O(169));n?(e=Bv(e,t,yi),r.__reactInternalMemoizedMergedChildContext=e,je(Nt),je(pt),Pe(pt,e)):je(Nt),Pe(Nt,n)}var An=null,zl=!1,bu=!1;function Uv(e){An===null?An=[e]:An.push(e)}function y1(e){zl=!0,Uv(e)}function Br(){if(!bu&&An!==null){bu=!0;var e=0,t=Ce;try{var n=An;for(Ce=1;e>=a,i-=a,Fn=1<<32-cn(t)+i|n<j?(W=k,k=null):W=k.sibling;var P=f(v,k,y[j],S);if(P===null){k===null&&(k=W);break}e&&k&&P.alternate===null&&t(v,k),p=s(P,p,j),L===null?R=P:L.sibling=P,L=P,k=W}if(j===y.length)return n(v,k),Le&&Gr(v,j),R;if(k===null){for(;jj?(W=k,k=null):W=k.sibling;var B=f(v,k,P.value,S);if(B===null){k===null&&(k=W);break}e&&k&&B.alternate===null&&t(v,k),p=s(B,p,j),L===null?R=B:L.sibling=B,L=B,k=W}if(P.done)return n(v,k),Le&&Gr(v,j),R;if(k===null){for(;!P.done;j++,P=y.next())P=c(v,P.value,S),P!==null&&(p=s(P,p,j),L===null?R=P:L.sibling=P,L=P);return Le&&Gr(v,j),R}for(k=r(v,k);!P.done;j++,P=y.next())P=m(k,v,j,P.value,S),P!==null&&(e&&P.alternate!==null&&k.delete(P.key===null?j:P.key),p=s(P,p,j),L===null?R=P:L.sibling=P,L=P);return e&&k.forEach(function(ue){return t(v,ue)}),Le&&Gr(v,j),R}function T(v,p,y,S){if(typeof y=="object"&&y!==null&&y.type===Ai&&y.key===null&&(y=y.props.children),typeof y=="object"&&y!==null){switch(y.$$typeof){case oo:e:{for(var R=y.key,L=p;L!==null;){if(L.key===R){if(R=y.type,R===Ai){if(L.tag===7){n(v,L.sibling),p=i(L,y.props.children),p.return=v,v=p;break e}}else if(L.elementType===R||typeof R=="object"&&R!==null&&R.$$typeof===or&&Ih(R)===L.type){n(v,L.sibling),p=i(L,y.props),p.ref=$s(v,L,y),p.return=v,v=p;break e}n(v,L);break}else t(v,L);L=L.sibling}y.type===Ai?(p=pi(y.props.children,v.mode,S,y.key),p.return=v,v=p):(S=Vo(y.type,y.key,y.props,null,v.mode,S),S.ref=$s(v,p,y),S.return=v,v=S)}return a(v);case Di:e:{for(L=y.key;p!==null;){if(p.key===L)if(p.tag===4&&p.stateNode.containerInfo===y.containerInfo&&p.stateNode.implementation===y.implementation){n(v,p.sibling),p=i(p,y.children||[]),p.return=v,v=p;break e}else{n(v,p);break}else t(v,p);p=p.sibling}p=Du(y,v.mode,S),p.return=v,v=p}return a(v);case or:return L=y._init,T(v,p,L(y._payload),S)}if(Ks(y))return g(v,p,y,S);if(Ls(y))return x(v,p,y,S);xo(v,y)}return typeof y=="string"&&y!==""||typeof y=="number"?(y=""+y,p!==null&&p.tag===6?(n(v,p.sibling),p=i(p,y),p.return=v,v=p):(n(v,p),p=Ou(y,v.mode,S),p.return=v,v=p),a(v)):n(v,p)}return T}var ms=Wv(!0),Zv=Wv(!1),ul=zr(null),cl=null,Vi=null,Vd=null;function Qd(){Vd=Vi=cl=null}function Wd(e){var t=ul.current;je(ul),e._currentValu)DELIM" -R"DELIM(e=t}function Cc(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Yi(e,t){cl=e,Vd=Vi=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(bt=!0),e.firstContext=null)}function qt(e){var t=e._currentValue;if(Vd!==e)if(e={context:e,memoizedValue:t,next:null},Vi===null){if(cl===null)throw Error(O(308));Vi=e,cl.dependencies={lanes:0,firstContext:e}}else Vi=Vi.next=e;return t}var ei=null;function Zd(e){ei===null?ei=[e]:ei.push(e)}function Kv(e,t,n,r){var i=t.interleaved;return i===null?(n.next=n,Zd(t)):(n.next=i.next,i.next=n),t.interleaved=n,Qn(e,r)}function Qn(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var lr=!1;function Kd(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Gv(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Bn(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Nr(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,ve&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,Qn(e,n)}return i=r.interleaved,i===null?(t.next=t,Zd(r)):(t.next=i.next,i.next=t),r.interleaved=t,Qn(e,n)}function $o(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ld(e,n)}}function $h(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,s=null;if(n=n.firstBaseUpdate,n!==null){do{var a={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};s===null?i=s=a:s=s.next=a,n=n.next}while(n!==null);s===null?i=s=t:s=s.next=t}else i=s=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:s,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function dl(e,t,n,r){var i=e.updateQueue;lr=!1;var s=i.firstBaseUpdate,a=i.lastBaseUpdate,o=i.shared.pending;if(o!==null){i.shared.pending=null;var l=o,u=l.next;l.next=null,a===null?s=u:a.next=u,a=l;var d=e.alternate;d!==null&&(d=d.updateQueue,o=d.lastBaseUpdate,o!==a&&(o===null?d.firstBaseUpdate=u:o.next=u,d.lastBaseUpdate=l))}if(s!==null){var c=i.baseState;a=0,d=u=l=null,o=s;do{var f=o.lane,m=o.eventTime;if((r&f)===f){d!==null&&(d=d.next={eventTime:m,lane:0,tag:o.tag,payload:o.payload,callback:o.callback,next:null});e:{var g=e,x=o;switch(f=t,m=n,x.tag){case 1:if(g=x.payload,typeof g=="function"){c=g.call(m,c,f);break e}c=g;break e;case 3:g.flags=g.flags&-65537|128;case 0:if(g=x.payload,f=typeof g=="function"?g.call(m,c,f):g,f==null)break e;c=Ae({},c,f);break e;case 2:lr=!0}}o.callback!==null&&o.lane!==0&&(e.flags|=64,f=i.effects,f===null?i.effects=[o]:f.push(o))}else m={eventTime:m,lane:f,tag:o.tag,payload:o.payload,callback:o.callback,next:null},d===null?(u=d=m,l=c):d=d.next=m,a|=f;if(o=o.next,o===null){if(o=i.shared.pending,o===null)break;f=o,o=f.next,f.next=null,i.lastBaseUpdate=f,i.shared.pending=null}}while(!0);if(d===null&&(l=c),i.baseState=l,i.firstBaseUpdate=u,i.lastBaseUpdate=d,t=i.shared.interleaved,t!==null){i=t;do a|=i.lane,i=i.next;while(i!==t)}else s===null&&(i.shared.lanes=0);wi|=a,e.lanes=a,e.memoizedState=c}}function Fh(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Pu.transition;Pu.transition={};try{e(!1),t()}finally{Ce=n,Pu.transition=r}}function fy(){return Yt().)DELIM" -R"DELIM(memoizedState}function k1(e,t,n){var r=Rr(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},hy(e))py(t,n);else if(n=Kv(e,t,n,r),n!==null){var i=xt();dn(n,e,r,i),my(n,t,r)}}function _1(e,t,n){var r=Rr(e),i={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(hy(e))py(t,i);else{var s=e.alternate;if(e.lanes===0&&(s===null||s.lanes===0)&&(s=t.lastRenderedReducer,s!==null))try{var a=t.lastRenderedState,o=s(a,n);if(i.hasEagerState=!0,i.eagerState=o,fn(o,a)){var l=t.interleaved;l===null?(i.next=i,Zd(t)):(i.next=l.next,l.next=i),t.interleaved=i;return}}catch{}finally{}n=Kv(e,t,i,r),n!==null&&(i=xt(),dn(n,e,r,i),my(n,t,r))}}function hy(e){var t=e.alternate;return e===De||t!==null&&t===De}function py(e,t){aa=hl=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function my(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ld(e,n)}}var pl={readContext:qt,useCallback:dt,useContext:dt,useEffect:dt,useImperativeHandle:dt,useInsertionEffect:dt,useLayoutEffect:dt,useMemo:dt,useReducer:dt,useRef:dt,useState:dt,useDebugValue:dt,useDeferredValue:dt,useTransition:dt,useMutableSource:dt,useSyncExternalStore:dt,useId:dt,unstable_isNewReconciler:!1},S1={readContext:qt,useCallback:function(e,t){return gn().memoizedState=[e,t===void 0?null:t],e},useContext:qt,useEffect:Bh,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,zo(4194308,4,oy.bind(null,t,e),n)},useLayoutEffect:function(e,t){return zo(4194308,4,e,t)},useInsertionEffect:function(e,t){return zo(4,2,e,t)},useMemo:function(e,t){var n=gn();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=gn();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=k1.bind(null,De,e),[r.memoizedState,e]},useRef:function(e){var t=gn();return e={current:e},t.memoizedState=e},useState:zh,useDebugValue:nf,useDeferredValue:function(e){return gn().memoizedState=e},useTransition:function(){var e=zh(!1),t=e[0];return e=w1.bind(null,e[1]),gn().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=De,i=gn();if(Le){if(n===void 0)throw Error(O(407));n=n()}else{if(n=t(),nt===null)throw Error(O(349));xi&30||Jv(r,t,n)}i.memoizedState=n;var s={value:n,getSnapshot:t};return i.queue=s,Bh(ty.bind(null,r,s,e),[e]),r.flags|=2048,Ta(9,ey.bind(null,r,s,n,t),void 0,null),n},useId:function(){var e=gn(),t=nt.identifierPrefix;if(Le){var n=zn,r=Fn;n=(r&~(1<<32-cn(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Ea++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=a.createElement(n,{is:r.is}):(e=a.createElement(n),n==="select"&&(a=e,r.multiple?a.multiple=!0:r.size&&(a.size=r.size))):e=a.createElementNS(e,n),e[_n]=t,e[ka]=r,Cy(e,t,!1,!1),t.stateNode=e;e:{switch(a=oc(n,r),n){case"dialog":Re("cancel",e),Re("close",e),i=r;break;case"iframe":case"object":case"embed":Re("load",e),i=r;break;case"video":case"audio":for(i=0;igs&&(t.flags|=128,r=!0,Fs(s,!1),t.lanes=4194304)}else{if(!r)if(e=fl(a),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Fs(s,!0),s.tail===null&&s.tailMode==="hidden"&&!a.alternate&&!Le)return ft(t),null}else 2*He()-s.renderingStartTime>gs&&n!==1073741824&&(t.flags|=128,r=!0,Fs(s,!1),t.lanes=4194304);s.isBackwards?(a.sibling=t.child,t.child=a):(n=s.last,n!==null?n.sibling=a:t.child=a,s.last=a)}return s.tail!==null?(t=s.tail,s.rendering=t,s.tail=t.sibling,s.renderingStartTime=He(),t.sibling=null,n=Oe.current,Pe(Oe,r?n&1|2:n&1),t):(ft(t),null);case 22:case 23:return uf(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?jt&1073741824&&(ft(t),t.subtreeFlags&6&&(t.flags|=8192)):ft(t),null;case 24:return null;case 25:return null}throw Error(O(156,t.tag))}function j1(e,t){switch(Ud(t),t.tag){case 1:return Pt(t.type)&&sl(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return vs(),je(Nt),je(pt),Yd(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return qd(t),null;case 13:if(je(Oe),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(O(340));ps()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return je(Oe),null;case 4:return vs(),null;case 10:return Wd(t.type._context),null;case 22:case 23:return uf(),null;case 24:return null;default:return null}}var ko=!1,ht=!1,M1=typeof WeakSet=="function"?WeakSet:Set,V=null;function Qi(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Be(e,t,r)}else n.current=null}function Oc(e,t,n){try{n()}catch(r){Be(e,t,r)}}var Xh=!1;function L1(e,t){if(yc=tl,e=Mv(),zd(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var i=r.anchorOffset,s=r.focusNode;r=r.focusOffset;try{n.nodeType,s.nodeType}catch{n=null;break e}var a=0,o=-1,l=-1,u=0,d=0,c=e,f=null;t:for(;;){for(var m;c!==n||i!==0&&c.nodeType!==3||(o=a+i),c!==s||r!==0&&c.nodeType!==3||(l=a+r),c.nodeType===3&&(a+=c.nodeValue.length),(m=c.firstChild)!==null;)f=c,c=m;for(;;){if(c===e)break t;if(f===n&&++u===i&&(o=a),f===s&&++d===r&&(l=a),(m=c.nextSibling)!==null)break;c=f,f=c.parentNode}c=m}n=o===-1||l===-1?null:{start:o,end:l}}else n=null}n=n||{start:0,end:0}}else n=null;for(gc={focusedElem:e,selectionRange:n},tl=!1,V=t;V!==null;)if(t=V,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,V=e;else for(;V!==null;){t=V;try{var g=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(g!==null){var x=g.memoizedProps,T=g.memoizedState,v=t.stateNode,p=v.getSnapshotBeforeUpdate(t.elementType===t.type?x:nn(t.type,x),T);v.__reactInternalSnapshotBeforeUpdate=p}break;case 3:var y=t.stateNode.containerInfo;y.nodeType===1?y.textContent="":y.nodeType===9&&y.documentElement&&y.removeChil)DELIM" -R"DELIM(d(y.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(O(163))}}catch(S){Be(t,t.return,S)}if(e=t.sibling,e!==null){e.return=t.return,V=e;break}V=t.return}return g=Xh,Xh=!1,g}function oa(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var i=r=r.next;do{if((i.tag&e)===e){var s=i.destroy;i.destroy=void 0,s!==void 0&&Oc(t,n,s)}i=i.next}while(i!==r)}}function Hl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Dc(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Ny(e){var t=e.alternate;t!==null&&(e.alternate=null,Ny(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[_n],delete t[ka],delete t[kc],delete t[m1],delete t[v1])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Py(e){return e.tag===5||e.tag===3||e.tag===4}function Jh(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Py(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ac(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=il));else if(r!==4&&(e=e.child,e!==null))for(Ac(e,t,n),e=e.sibling;e!==null;)Ac(e,t,n),e=e.sibling}function Ic(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ic(e,t,n),e=e.sibling;e!==null;)Ic(e,t,n),e=e.sibling}var st=null,an=!1;function er(e,t,n){for(n=n.child;n!==null;)Ry(e,t,n),n=n.sibling}function Ry(e,t,n){if(Cn&&typeof Cn.onCommitFiberUnmount=="function")try{Cn.onCommitFiberUnmount(Dl,n)}catch{}switch(n.tag){case 5:ht||Qi(n,t);case 6:var r=st,i=an;st=null,er(e,t,n),st=r,an=i,st!==null&&(an?(e=st,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):st.removeChild(n.stateNode));break;case 18:st!==null&&(an?(e=st,n=n.stateNode,e.nodeType===8?Tu(e.parentNode,n):e.nodeType===1&&Tu(e,n),va(e)):Tu(st,n.stateNode));break;case 4:r=st,i=an,st=n.stateNode.containerInfo,an=!0,er(e,t,n),st=r,an=i;break;case 0:case 11:case 14:case 15:if(!ht&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){i=r=r.next;do{var s=i,a=s.destroy;s=s.tag,a!==void 0&&(s&2||s&4)&&Oc(n,t,a),i=i.next}while(i!==r)}er(e,t,n);break;case 1:if(!ht&&(Qi(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(o){Be(n,t,o)}er(e,t,n);break;case 21:er(e,t,n);break;case 22:n.mode&1?(ht=(r=ht)||n.memoizedState!==null,er(e,t,n),ht=r):er(e,t,n);break;default:er(e,t,n)}}function ep(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new M1),t.forEach(function(r){var i=U1.bind(null,e,r);n.has(r)||(n.add(r),r.then(i,i))})}}function en(e,t){var n=t.deletions;if(n!==null)for(var r=0;ri&&(i=a),r&=~s}if(r=i,r=He()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*D1(r/1960))-r,10e?16:e,kr===null)var r=!1;else{if(e=kr,kr=null,yl=0,ve&6)throw Error(O(331));var i=ve;for(ve|=4,V=e.current;V!==null;){var s=V,a=s.child;if(V.flags&16){var o=s.deletions;if(o!==null){for(var l=0;lHe()-of?hi(e,0):af|=n),Rt(e,t)}function $y(e,t){t===0&&(e.mode&1?(t=fo,fo<<=1,!(fo&130023424)&&(fo=4194304)):t=1);var n=xt();e=Qn(e,t),e!==null&&(Ha(e,t,n),Rt(e,n))}function B1(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),$y(e,n)}function U1(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,i=e.memoizedState;i!==null&&(n=i.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(O(314))}r!==null&&r.delete(t),$y(e,n)}var Fy;Fy=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Nt.current)bt=!0;e)DELIM" -R"DELIM(lse{if(!(e.lanes&n)&&!(t.flags&128))return bt=!1,P1(e,t,n);bt=!!(e.flags&131072)}else bt=!1,Le&&t.flags&1048576&&Hv(t,ll,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Bo(e,t),e=t.pendingProps;var i=hs(t,pt.current);Yi(t,n),i=Jd(null,t,r,e,i,n);var s=ef();return t.flags|=1,typeof i=="object"&&i!==null&&typeof i.render=="function"&&i.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Pt(r)?(s=!0,al(t)):s=!1,t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,Kd(t),i.updater=Ul,t.stateNode=i,i._reactInternals=t,bc(t,r,e,n),t=Rc(null,t,r,!0,s,n)):(t.tag=0,Le&&s&&Bd(t),yt(null,t,i,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Bo(e,t),e=t.pendingProps,i=r._init,r=i(r._payload),t.type=r,i=t.tag=V1(r),e=nn(r,e),i){case 0:t=Pc(null,t,r,e,n);break e;case 1:t=Gh(null,t,r,e,n);break e;case 11:t=Zh(null,t,r,e,n);break e;case 14:t=Kh(null,t,r,nn(r.type,e),n);break e}throw Error(O(306,r,""))}return t;case 0:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:nn(r,i),Pc(e,t,r,i,n);case 1:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:nn(r,i),Gh(e,t,r,i,n);case 3:e:{if(_y(t),e===null)throw Error(O(387));r=t.pendingProps,s=t.memoizedState,i=s.element,Gv(e,t),dl(t,r,null,n);var a=t.memoizedState;if(r=a.element,s.isDehydrated)if(s={element:r,isDehydrated:!1,cache:a.cache,pendingSuspenseBoundaries:a.pendingSuspenseBoundaries,transitions:a.transitions},t.updateQueue.baseState=s,t.memoizedState=s,t.flags&256){i=ys(Error(O(423)),t),t=qh(e,t,r,n,i);break e}else if(r!==i){i=ys(Error(O(424)),t),t=qh(e,t,r,n,i);break e}else for(Lt=br(t.stateNode.containerInfo.firstChild),Ot=t,Le=!0,ln=null,n=Zv(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(ps(),r===i){t=Wn(e,t,n);break e}yt(e,t,r,n)}t=t.child}return t;case 5:return qv(t),e===null&&Ec(t),r=t.type,i=t.pendingProps,s=e!==null?e.memoizedProps:null,a=i.children,xc(r,i)?a=null:s!==null&&xc(r,s)&&(t.flags|=32),ky(e,t),yt(e,t,a,n),t.child;case 6:return e===null&&Ec(t),null;case 13:return Sy(e,t,n);case 4:return Gd(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=ms(t,null,r,n):yt(e,t,r,n),t.child;case 11:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:nn(r,i),Zh(e,t,r,i,n);case 7:return yt(e,t,t.pendingProps,n),t.child;case 8:return yt(e,t,t.pendingProps.children,n),t.child;case 12:return yt(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,i=t.pendingProps,s=t.memoizedProps,a=i.value,Pe(ul,r._currentValue),r._currentValue=a,s!==null)if(fn(s.value,a)){if(s.children===i.children&&!Nt.current){t=Wn(e,t,n);break e}}else for(s=t.child,s!==null&&(s.return=t);s!==null;){var o=s.dependencies;if(o!==null){a=s.child;for(var l=o.firstContext;l!==null;){if(l.context===r){if(s.tag===1){l=Bn(-1,n&-n),l.tag=2;var u=s.updateQueue;if(u!==null){u=u.shared;var d=u.pending;d===null?l.next=l:(l.next=d.next,d.next=l),u.pending=l}}s.lanes|=n,l=s.alternate,l!==null&&(l.lanes|=n),Cc(s.return,n,t),o.lanes|=n;break}l=l.next}}else if(s.tag===10)a=s.type===t.type?null:s.child;else if(s.tag===18){if(a=s.return,a===null)throw Error(O(341));a.lanes|=n,o=a.alternate,o!==null&&(o.lanes|=n),Cc(a,n,t),a=s.sibling}else a=s.child;if(a!==null)a.return=s;else for(a=s;a!==null;){if(a===t){a=null;break}if(s=a.sibling,s!==null){s.return=a.return,a=s;break}a=a.return}s=a}yt(e,t,i.children,n),t=t.child}return t;case 9:return i=t.type,r=t.pendingProps.children,Yi(t,n),i=qt(i),r=r(i),t.flags|=1,yt(e,t,r,n),t.child;case 14:return r=t.type,i=nn(r,t.pendingProps),i=nn(r.type,i),Kh(e,t,r,i,n);case 15:return xy(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:nn(r,i),Bo(e,t),t.tag=1,Pt(r)?(e=!0,al(t)):e=!1,Yi(t,n),vy(t,r,i),bc(t,r,i,n),Rc(null,t,r,!0,e,n);case 19:return Ey(e,t,n);case 22:return wy(e,t,n)}throw Error(O(156,t.tag))};function zy(e,t){return hv(e,t)}function H1(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Kt(e,t,n,r){return new H1(e,t,n,r)}function df(e){return e=e.prototype,!(!e||!e.isReactComponent)}function V1(e){if(typeof e=="function")return df(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Pd)return 11;if(e===Rd)return 14}return 2}function jr(e,t){var n=e.alternate;return n===null?(n=Kt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.ind)DELIM" -R"DELIM(ex,n.ref=e.ref,n}function Vo(e,t,n,r,i,s){var a=2;if(r=e,typeof e=="function")df(e)&&(a=1);else if(typeof e=="string")a=5;else e:switch(e){case Ai:return pi(n.children,i,s,t);case Nd:a=8,i|=8;break;case Yu:return e=Kt(12,n,t,i|2),e.elementType=Yu,e.lanes=s,e;case Xu:return e=Kt(13,n,t,i),e.elementType=Xu,e.lanes=s,e;case Ju:return e=Kt(19,n,t,i),e.elementType=Ju,e.lanes=s,e;case qm:return Ql(n,i,s,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Km:a=10;break e;case Gm:a=9;break e;case Pd:a=11;break e;case Rd:a=14;break e;case or:a=16,r=null;break e}throw Error(O(130,e==null?e:typeof e,""))}return t=Kt(a,n,t,i),t.elementType=e,t.type=r,t.lanes=s,t}function pi(e,t,n,r){return e=Kt(7,e,r,t),e.lanes=n,e}function Ql(e,t,n,r){return e=Kt(22,e,r,t),e.elementType=qm,e.lanes=n,e.stateNode={isHidden:!1},e}function Ou(e,t,n){return e=Kt(6,e,null,t),e.lanes=n,e}function Du(e,t,n){return t=Kt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Q1(e,t,n,r,i){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=mu(0),this.expirationTimes=mu(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=mu(0),this.identifierPrefix=r,this.onRecoverableError=i,this.mutableSourceEagerHydrationData=null}function ff(e,t,n,r,i,s,a,o,l){return e=new Q1(e,t,n,o,l),t===1?(t=1,s===!0&&(t|=8)):t=0,s=Kt(3,null,null,t),e.current=s,s.stateNode=e,s.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Kd(s),e}function W1(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(Vy)}catch(e){console.error(e)}}Vy(),Vm.exports=It;var vf=Vm.exports;const Y1=Ll(vf),X1=jm({__proto__:null,default:Y1},[vf]);var lp=vf;Gu.createRoot=lp.createRoot,Gu.hydrateRoot=lp.hydrateRoot;var Za=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},J1={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},fr,wd,wm,ew=(wm=class{constructor(){ne(this,fr,J1);ne(this,wd,!1)}setTimeoutProvider(e){H(this,fr,e)}setTimeout(e,t){return _(this,fr).setTimeout(e,t)}clearTimeout(e){_(this,fr).clearTimeout(e)}setInterval(e,t){return _(this,fr).setInterval(e,t)}clearInterval(e){_(this,fr).clearInterval(e)}},fr=new WeakMap,wd=new WeakMap,wm),ni=new ew;function tw(e){setTimeout(e,0)}var _i=typeof window>"u"||"Deno"in globalThis;function Et(){}function nw(e,t){return typeof e=="function"?e(t):e}function Uc(e){return typeof e=="number"&&e>=0&&e!==1/0}function Qy(e,t){return Math.max(e+(t||0)-Date.no)DELIM" -R"DELIM(w(),0)}function Mr(e,t){return typeof e=="function"?e(t):e}function Vt(e,t){return typeof e=="function"?e(t):e}function up(e,t){const{type:n="all",exact:r,fetchStatus:i,predicate:s,queryKey:a,stale:o}=e;if(a){if(r){if(t.queryHash!==yf(a,t.options))return!1}else if(!Pa(t.queryKey,a))return!1}if(n!=="all"){const l=t.isActive();if(n==="active"&&!l||n==="inactive"&&l)return!1}return!(typeof o=="boolean"&&t.isStale()!==o||i&&i!==t.state.fetchStatus||s&&!s(t))}function cp(e,t){const{exact:n,status:r,predicate:i,mutationKey:s}=e;if(s){if(!t.options.mutationKey)return!1;if(n){if(Na(t.options.mutationKey)!==Na(s))return!1}else if(!Pa(t.options.mutationKey,s))return!1}return!(r&&t.state.status!==r||i&&!i(t))}function yf(e,t){return((t==null?void 0:t.queryKeyHashFn)||Na)(e)}function Na(e){return JSON.stringify(e,(t,n)=>Vc(n)?Object.keys(n).sort().reduce((r,i)=>(r[i]=n[i],r),{}):n)}function Pa(e,t){return e===t?!0:typeof e!=typeof t?!1:e&&t&&typeof e=="object"&&typeof t=="object"?Object.keys(t).every(n=>Pa(e[n],t[n])):!1}var rw=Object.prototype.hasOwnProperty;function Wy(e,t){if(e===t)return e;const n=dp(e)&&dp(t);if(!n&&!(Vc(e)&&Vc(t)))return t;const i=(n?e:Object.keys(e)).length,s=n?t:Object.keys(t),a=s.length,o=n?new Array(a):{};let l=0;for(let u=0;u{ni.setTimeout(t,e)})}function Qc(e,t,n){return typeof n.structuralSharing=="function"?n.structuralSharing(e,t):n.structuralSharing!==!1?Wy(e,t):t}function sw(e,t,n=0){const r=[...e,t];return n&&r.length>n?r.slice(1):r}function aw(e,t,n=0){const r=[t,...e];return n&&r.length>n?r.slice(0,-1):r}var gf=Symbol();function Zy(e,t){return!e.queryFn&&(t!=null&&t.initialPromise)?()=>t.initialPromise:!e.queryFn||e.queryFn===gf?()=>Promise.reject(new Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function ow(e,t){return typeof e=="function"?e(...t):!!e}var ii,hr,es,km,lw=(km=class extends Za{constructor(){super();ne(this,ii);ne(this,hr);ne(this,es);H(this,es,t=>{if(!_i&&window.addEventListener){const n=()=>t();return window.addEventListener("visibilitychange",n,!1),()=>{window.removeEventListener("visibilitychange",n)}}})}onSubscribe(){_(this,hr)||this.setEventListener(_(this,es))}onUnsubscribe(){var t;this.hasListeners()||((t=_(this,hr))==null||t.call(this),H(this,hr,void 0))}setEventListener(t){var n;H(this,es,t),(n=_(this,hr))==null||n.call(this),H(this,hr,t(r=>{typeof r=="boolean"?this.setFocused(r):this.onFocus()}))}setFocused(t){_(this,ii)!==t&&(H(this,ii,t),this.onFocus())}onFocus(){const t=this.isFocused();this.listeners.forEach(n=>{n(t)})}isFocused(){var t;return typeof _(this,ii)=="boolean"?_(this,ii):((t=globalThis.document)==null?void 0:t.visibilityState)!=="hidden"}},ii=new WeakMap,hr=new WeakMap,es=new WeakMap,km),xf=new lw;function Wc(){let e,t;const n=new Promise((i,s)=>{e=i,t=s});n.status="pending",n.catch(()=>{});function r(i){Object.assign(n,i),delete n.resolve,delete n.reject}return n.resolve=i=>{r({status:"fulfilled",value:i}),e(i)},n.reject=i=>{r({status:"rejected",reason:i}),t(i)},n}var uw=tw;function cw(){let e=[],t=0,n=o=>{o()},r=o=>{o()},i=uw;const s=o=>{t?e.push(o):i(()=>{n(o)})},a=()=>{const o=e;e=[],o.length&&i(()=>{r(()=>{o.forEach(l=>{n(l)})})})};return{batch:o=>{let l;t++;try{l=o()}finally{t--,t||a()}return l},batchCalls:o=>(...l)=>{s(()=>{o(...l)})},schedule:s,setNotifyFunction:o=>{n=o},setBatchNotifyFunction:o=>{r=o},setScheduler:o=>{i=o}}}var ot=cw(),ts,pr,ns,_m,dw=(_m=class extends Za{constructor(){super();ne(this,ts,!0);ne(this,pr);ne(this,ns);H(this,ns,t=>{if(!_i&&window.addEventListener){const n=()=>t(!0),r=()=>t(!1);return window.addEventListener("online",n,!1),window.addEventListener("offline",r,!1),()=>{window.removeEventListener("online",n),window.removeEventListener("offline",r)}}})}onSubscribe(){_(this,pr)||this.setEventListener(_(this,ns))}onUnsubscribe(){var t;this.hasListeners()||((t=_(this,pr))==null||t.call(this),H(this,pr,void 0))}setEventListener(t){var n;H(this,ns,t),(n=_(this,pr))==null||n.call(this),H(this,pr,t(this.setOnline.bind(this)))}setOnline(t){_(this,ts)!==t&&(H(this,ts,t),this.listeners.forEach(r=>{r(t)}))}isOnline(){return _(this,ts)}},ts=new WeakMap,pr=new WeakMap,ns=new WeakMap,_m),wl=new dw;function fw(e){return Math.min(1e3*2**e,3e4)}function Ky(e){return(e??"online")==="online"?wl.)DELIM" -R"DELIM(isOnline():!0}var Zc=class extends Error{constructor(e){super("CancelledError"),this.revert=e==null?void 0:e.revert,this.silent=e==null?void 0:e.silent}};function Gy(e){let t=!1,n=0,r;const i=Wc(),s=()=>i.status!=="pending",a=x=>{var T;if(!s()){const v=new Zc(x);f(v),(T=e.onCancel)==null||T.call(e,v)}},o=()=>{t=!0},l=()=>{t=!1},u=()=>xf.isFocused()&&(e.networkMode==="always"||wl.isOnline())&&e.canRun(),d=()=>Ky(e.networkMode)&&e.canRun(),c=x=>{s()||(r==null||r(),i.resolve(x))},f=x=>{s()||(r==null||r(),i.reject(x))},m=()=>new Promise(x=>{var T;r=v=>{(s()||u())&&x(v)},(T=e.onPause)==null||T.call(e)}).then(()=>{var x;r=void 0,s()||(x=e.onContinue)==null||x.call(e)}),g=()=>{if(s())return;let x;const T=n===0?e.initialPromise:void 0;try{x=T??e.fn()}catch(v){x=Promise.reject(v)}Promise.resolve(x).then(c).catch(v=>{var L;if(s())return;const p=e.retry??(_i?0:3),y=e.retryDelay??fw,S=typeof y=="function"?y(n,v):y,R=p===!0||typeof p=="number"&&nu()?void 0:m()).then(()=>{t?f(v):g()})})};return{promise:i,status:()=>i.status,cancel:a,continue:()=>(r==null||r(),i),cancelRetry:o,continueRetry:l,canStart:d,start:()=>(d()?g():m().then(g),i)}}var si,Sm,qy=(Sm=class{constructor(){ne(this,si)}destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),Uc(this.gcTime)&&H(this,si,ni.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(_i?1/0:5*60*1e3))}clearGcTimeout(){_(this,si)&&(ni.clearTimeout(_(this,si)),H(this,si,void 0))}},si=new WeakMap,Sm),ai,rs,Ut,oi,et,Ia,li,rn,Ln,Em,hw=(Em=class extends qy{constructor(t){super();ne(this,rn);ne(this,ai);ne(this,rs);ne(this,Ut);ne(this,oi);ne(this,et);ne(this,Ia);ne(this,li);H(this,li,!1),H(this,Ia,t.defaultOptions),this.setOptions(t.options),this.observers=[],H(this,oi,t.client),H(this,Ut,_(this,oi).getQueryCache()),this.queryKey=t.queryKey,this.queryHash=t.queryHash,H(this,ai,pp(this.options)),this.state=t.state??_(this,ai),this.scheduleGc()}get meta(){return this.options.meta}get promise(){var t;return(t=_(this,et))==null?void 0:t.promise}setOptions(t){if(this.options={..._(this,Ia),...t},this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){const n=pp(this.options);n.data!==void 0&&(this.setState(hp(n.data,n.dataUpdatedAt)),H(this,ai,n))}}optionalRemove(){!this.observers.length&&this.state.fetchStatus==="idle"&&_(this,Ut).remove(this)}setData(t,n){const r=Qc(this.state.data,t,this.options);return fe(this,rn,Ln).call(this,{data:r,type:"success",dataUpdatedAt:n==null?void 0:n.updatedAt,manual:n==null?void 0:n.manual}),r}setState(t,n){fe(this,rn,Ln).call(this,{type:"setState",state:t,setStateOptions:n})}cancel(t){var r,i;const n=(r=_(this,et))==null?void 0:r.promise;return(i=_(this,et))==null||i.cancel(t),n?n.then(Et).catch(Et):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}reset(){this.destroy(),this.setState(_(this,ai))}isActive(){return this.observers.some(t=>Vt(t.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===gf||this.state.dataUpdateCount+this.state.errorUpdateCount===0}isStatic(){return this.getObserversCount()>0?this.observers.some(t=>Mr(t.options.staleTime,this)==="static"):!1}isStale(){return this.getObserversCount()>0?this.observers.some(t=>t.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(t=0){return this.state.data===void 0?!0:t==="static"?!1:this.state.isInvalidated?!0:!Qy(this.state.dataUpdatedAt,t)}onFocus(){var n;const t=this.observers.find(r=>r.shouldFetchOnWindowFocus());t==null||t.refetch({cancelRefetch:!1}),(n=_(this,et))==null||n.continue()}onOnline(){var n;const t=this.observers.find(r=>r.shouldFetchOnReconnect());t==null||t.refetch({cancelRefetch:!1}),(n=_(this,et))==null||n.continue()}addObserver(t){this.observers.includes(t)||(this.observers.push(t),this.clearGcTimeout(),_(this,Ut).notify({type:"observerAdded",query:this,observer:t}))}removeObserver(t){this.observers.includes(t)&&(this.observers=this.observers.filter(n=>n!==t),this.observers.length||(_(this,et)&&(_(this,li)?_(this,et).cancel({revert:!0}):_(this,et).cancelRetry()),this.scheduleGc()),_(this,Ut).notify({type:"observerRemoved",query:this,observer:t}))}getObserversCount(){return this.observers.length}invalidate(){this.state.isInvalidated||fe(this,rn,Ln).call(this,{type:"invalidate"})}async fetch(t,n){var l,u,d,c,f,m,g,x,T,v,p,y;if(this.state.fetchStatus!=="idle"&&((l=_(this,et))==null?void 0:l.status())!=="rejected"){if(this.state.data!==void 0&&(n!=null&&n.cancelRefetch))this.cancel({silent:!0});else if(_(this,et))return _(this,et).continueRetry(),_(this,et).promise}if(t&&this.setOptions(t),!this.options.queryFn){const S=this.observers.find(R=>R.options.queryFn);S&&this.setOptions(S.options)}const r=new AbortController,i=S=>{Object.defineProperty(S,"signal",{enumerable:!0,get:)DELIM" -R"DELIM(()=>(H(this,li,!0),r.signal)})},s=()=>{const S=Zy(this.options,n),L=(()=>{const k={client:_(this,oi),queryKey:this.queryKey,meta:this.meta};return i(k),k})();return H(this,li,!1),this.options.persister?this.options.persister(S,L,this):S(L)},o=(()=>{const S={fetchOptions:n,options:this.options,queryKey:this.queryKey,client:_(this,oi),state:this.state,fetchFn:s};return i(S),S})();(u=this.options.behavior)==null||u.onFetch(o,this),H(this,rs,this.state),(this.state.fetchStatus==="idle"||this.state.fetchMeta!==((d=o.fetchOptions)==null?void 0:d.meta))&&fe(this,rn,Ln).call(this,{type:"fetch",meta:(c=o.fetchOptions)==null?void 0:c.meta}),H(this,et,Gy({initialPromise:n==null?void 0:n.initialPromise,fn:o.fetchFn,onCancel:S=>{S instanceof Zc&&S.revert&&this.setState({..._(this,rs),fetchStatus:"idle"}),r.abort()},onFail:(S,R)=>{fe(this,rn,Ln).call(this,{type:"failed",failureCount:S,error:R})},onPause:()=>{fe(this,rn,Ln).call(this,{type:"pause"})},onContinue:()=>{fe(this,rn,Ln).call(this,{type:"continue"})},retry:o.options.retry,retryDelay:o.options.retryDelay,networkMode:o.options.networkMode,canRun:()=>!0}));try{const S=await _(this,et).start();if(S===void 0)throw new Error(`${this.queryHash} data is undefined`);return this.setData(S),(m=(f=_(this,Ut).config).onSuccess)==null||m.call(f,S,this),(x=(g=_(this,Ut).config).onSettled)==null||x.call(g,S,this.state.error,this),S}catch(S){if(S instanceof Zc){if(S.silent)return _(this,et).promise;if(S.revert){if(this.state.data===void 0)throw S;return this.state.data}}throw fe(this,rn,Ln).call(this,{type:"error",error:S}),(v=(T=_(this,Ut).config).onError)==null||v.call(T,S,this),(y=(p=_(this,Ut).config).onSettled)==null||y.call(p,this.state.data,S,this),S}finally{this.scheduleGc()}}},ai=new WeakMap,rs=new WeakMap,Ut=new WeakMap,oi=new WeakMap,et=new WeakMap,Ia=new WeakMap,li=new WeakMap,rn=new WeakSet,Ln=function(t){const n=r=>{switch(t.type){case"failed":return{...r,fetchFailureCount:t.failureCount,fetchFailureReason:t.error};case"pause":return{...r,fetchStatus:"paused"};case"continue":return{...r,fetchStatus:"fetching"};case"fetch":return{...r,...Yy(r.data,this.options),fetchMeta:t.meta??null};case"success":const i={...r,...hp(t.data,t.dataUpdatedAt),dataUpdateCount:r.dataUpdateCount+1,...!t.manual&&{fetchStatus:"idle",fetchFailureCount:0,fetchFailureReason:null}};return H(this,rs,t.manual?i:void 0),i;case"error":const s=t.error;return{...r,error:s,errorUpdateCount:r.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:r.fetchFailureCount+1,fetchFailureReason:s,fetchStatus:"idle",status:"error"};case"invalidate":return{...r,isInvalidated:!0};case"setState":return{...r,...t.state}}};this.state=n(this.state),ot.batch(()=>{this.observers.forEach(r=>{r.onQueryUpdate()}),_(this,Ut).notify({query:this,type:"updated",action:t})})},Em);function Yy(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:Ky(t.networkMode)?"fetching":"paused",...e===void 0&&{error:null,status:"pending"}}}function hp(e,t){return{data:e,dataUpdatedAt:t??Date.now(),error:null,isInvalidated:!1,status:"success"}}function pp(e){const t=typeof e.initialData=="function"?e.initialData():e.initialData,n=t!==void 0,r=n?typeof e.initialDataUpdatedAt=="function"?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?r??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?"success":"pending",fetchStatus:"idle"}}var _t,he,$a,mt,ui,is,In,mr,Fa,ss,as,ci,di,vr,os,Se,Ys,Kc,Gc,qc,Yc,Xc,Jc,ed,Xy,Cm,pw=(Cm=class extends Za{constructor(t,n){super();ne(this,Se);ne(this,_t);ne(this,he);ne(this,$a);ne(this,mt);ne(this,ui);ne(this,is);ne(this,In);ne(this,mr);ne(this,Fa);ne(this,ss);ne(this,as);ne(this,ci);ne(this,di);ne(this,vr);ne(this,os,new Set);this.options=n,H(this,_t,t),H(this,mr,null),H(this,In,Wc()),this.bindMethods(),this.setOptions(n)}bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(_(this,he).addObserver(this),mp(_(this,he),this.options)?fe(this,Se,Ys).call(this):this.updateResult(),fe(this,Se,Yc).call(this))}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return td(_(this,he),this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return td(_(this,he),this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,fe(this,Se,Xc).call(this),fe(this,Se,Jc).call(this),_(this,he).removeObserver(this)}setOptions(t){const n=this.options,r=_(this,he);if(this.options=_(this,_t).defaultQueryOptions(t),this.options.enabled!==void 0&&typeof this.options.enabled!="boolean"&&typeof this.options.enabled!="function"&&typeof Vt(this.options.enabled,_(this,he))!="boolean")throw new Error("Expected enabled to be a boolean or a callback that returns a boolean");fe(this,Se,ed).call(this),_(this,he).setOptions(this.options),n._defaulted&&!Hc(this.options,n)&&_(this,_t).getQueryCac)DELIM" -R"DELIM(he().notify({type:"observerOptionsUpdated",query:_(this,he),observer:this});const i=this.hasListeners();i&&vp(_(this,he),r,this.options,n)&&fe(this,Se,Ys).call(this),this.updateResult(),i&&(_(this,he)!==r||Vt(this.options.enabled,_(this,he))!==Vt(n.enabled,_(this,he))||Mr(this.options.staleTime,_(this,he))!==Mr(n.staleTime,_(this,he)))&&fe(this,Se,Kc).call(this);const s=fe(this,Se,Gc).call(this);i&&(_(this,he)!==r||Vt(this.options.enabled,_(this,he))!==Vt(n.enabled,_(this,he))||s!==_(this,vr))&&fe(this,Se,qc).call(this,s)}getOptimisticResult(t){const n=_(this,_t).getQueryCache().build(_(this,_t),t),r=this.createResult(n,t);return vw(this,r)&&(H(this,mt,r),H(this,is,this.options),H(this,ui,_(this,he).state)),r}getCurrentResult(){return _(this,mt)}trackResult(t,n){return new Proxy(t,{get:(r,i)=>(this.trackProp(i),n==null||n(i),i==="promise"&&(this.trackProp("data"),!this.options.experimental_prefetchInRender&&_(this,In).status==="pending"&&_(this,In).reject(new Error("experimental_prefetchInRender feature flag is not enabled"))),Reflect.get(r,i))})}trackProp(t){_(this,os).add(t)}getCurrentQuery(){return _(this,he)}refetch({...t}={}){return this.fetch({...t})}fetchOptimistic(t){const n=_(this,_t).defaultQueryOptions(t),r=_(this,_t).getQueryCache().build(_(this,_t),n);return r.fetch().then(()=>this.createResult(r,n))}fetch(t){return fe(this,Se,Ys).call(this,{...t,cancelRefetch:t.cancelRefetch??!0}).then(()=>(this.updateResult(),_(this,mt)))}createResult(t,n){var W;const r=_(this,he),i=this.options,s=_(this,mt),a=_(this,ui),o=_(this,is),u=t!==r?t.state:_(this,$a),{state:d}=t;let c={...d},f=!1,m;if(n._optimisticResults){const P=this.hasListeners(),B=!P&&mp(t,n),ue=P&&vp(t,r,n,i);(B||ue)&&(c={...c,...Yy(d.data,t.options)}),n._optimisticResults==="isRestoring"&&(c.fetchStatus="idle")}let{error:g,errorUpdatedAt:x,status:T}=c;m=c.data;let v=!1;if(n.placeholderData!==void 0&&m===void 0&&T==="pending"){let P;s!=null&&s.isPlaceholderData&&n.placeholderData===(o==null?void 0:o.placeholderData)?(P=s.data,v=!0):P=typeof n.placeholderData=="function"?n.placeholderData((W=_(this,as))==null?void 0:W.state.data,_(this,as)):n.placeholderData,P!==void 0&&(T="success",m=Qc(s==null?void 0:s.data,P,n),f=!0)}if(n.select&&m!==void 0&&!v)if(s&&m===(a==null?void 0:a.data)&&n.select===_(this,Fa))m=_(this,ss);else try{H(this,Fa,n.select),m=n.select(m),m=Qc(s==null?void 0:s.data,m,n),H(this,ss,m),H(this,mr,null)}catch(P){H(this,mr,P)}_(this,mr)&&(g=_(this,mr),m=_(this,ss),x=Date.now(),T="error");const p=c.fetchStatus==="fetching",y=T==="pending",S=T==="error",R=y&&p,L=m!==void 0,j={status:T,fetchStatus:c.fetchStatus,isPending:y,isSuccess:T==="success",isError:S,isInitialLoading:R,isLoading:R,data:m,dataUpdatedAt:c.dataUpdatedAt,error:g,errorUpdatedAt:x,failureCount:c.fetchFailureCount,failureReason:c.fetchFailureReason,errorUpdateCount:c.errorUpdateCount,isFetched:c.dataUpdateCount>0||c.errorUpdateCount>0,isFetchedAfterMount:c.dataUpdateCount>u.dataUpdateCount||c.errorUpdateCount>u.errorUpdateCount,isFetching:p,isRefetching:p&&!y,isLoadingError:S&&!L,isPaused:c.fetchStatus==="paused",isPlaceholderData:f,isRefetchError:S&&L,isStale:wf(t,n),refetch:this.refetch,promise:_(this,In),isEnabled:Vt(n.enabled,t)!==!1};if(this.options.experimental_prefetchInRender){const P=be=>{j.status==="error"?be.reject(j.error):j.data!==void 0&&be.resolve(j.data)},B=()=>{const be=H(this,In,j.promise=Wc());P(be)},ue=_(this,In);switch(ue.status){case"pending":t.queryHash===r.queryHash&&P(ue);break;case"fulfilled":(j.status==="error"||j.data!==ue.value)&&B();break;case"rejected":(j.status!=="error"||j.error!==ue.reason)&&B();break}}return j}updateResult(){const t=_(this,mt),n=this.createResult(_(this,he),this.options);if(H(this,ui,_(this,he).state),H(this,is,this.options),_(this,ui).data!==void 0&&H(this,as,_(this,he)),Hc(n,t))return;H(this,mt,n);const r=()=>{if(!t)return!0;const{notifyOnChangeProps:i}=this.options,s=typeof i=="function"?i():i;if(s==="all"||!s&&!_(this,os).size)return!0;const a=new Set(s??_(this,os));return this.options.throwOnError&&a.add("error"),Object.keys(_(this,mt)).some(o=>{const l=o;return _(this,mt)[l]!==t[l]&&a.has(l)})};fe(this,Se,Xy).call(this,{listeners:r()})}onQueryUpdate(){this.updateResult(),this.hasListeners()&&fe(this,Se,Yc).call(this)}},_t=new WeakMap,he=new WeakMap,$a=new WeakMap,mt=new WeakMap,ui=new WeakMap,is=new WeakMap,In=new WeakMap,mr=new WeakMap,Fa=new WeakMap,ss=new WeakMap,as=new WeakMap,ci=new WeakMap,di=new WeakMap,vr=new WeakMap,os=new WeakMap,Se=new WeakSet,Ys=function(t){fe(this,Se,ed).call(this);let n=_(this,he).fetch(this.options,t);return t!=null&&t.throwOnError||(n=n.catch(Et)),n},Kc=function(){fe(this,Se,Xc).call(this);const t=Mr(this.options.staleTime,_(this,he));if(_i||_(this,mt).isStale||!Uc(t))return;const r=Qy(_(this,mt).dataUpdatedAt,t)+1;H(this,ci,ni.setTimeout(()=>{_(this,mt).isStale||this.updateResult()},r))},Gc=function(){return(typeof this.options.refetchInterval=="function"?this.options.refetchIn)DELIM" -R"DELIM(terval(_(this,he)):this.options.refetchInterval)??!1},qc=function(t){fe(this,Se,Jc).call(this),H(this,vr,t),!(_i||Vt(this.options.enabled,_(this,he))===!1||!Uc(_(this,vr))||_(this,vr)===0)&&H(this,di,ni.setInterval(()=>{(this.options.refetchIntervalInBackground||xf.isFocused())&&fe(this,Se,Ys).call(this)},_(this,vr)))},Yc=function(){fe(this,Se,Kc).call(this),fe(this,Se,qc).call(this,fe(this,Se,Gc).call(this))},Xc=function(){_(this,ci)&&(ni.clearTimeout(_(this,ci)),H(this,ci,void 0))},Jc=function(){_(this,di)&&(ni.clearInterval(_(this,di)),H(this,di,void 0))},ed=function(){const t=_(this,_t).getQueryCache().build(_(this,_t),this.options);if(t===_(this,he))return;const n=_(this,he);H(this,he,t),H(this,$a,t.state),this.hasListeners()&&(n==null||n.removeObserver(this),t.addObserver(this))},Xy=function(t){ot.batch(()=>{t.listeners&&this.listeners.forEach(n=>{n(_(this,mt))}),_(this,_t).getQueryCache().notify({query:_(this,he),type:"observerResultsUpdated"})})},Cm);function mw(e,t){return Vt(t.enabled,e)!==!1&&e.state.data===void 0&&!(e.state.status==="error"&&t.retryOnMount===!1)}function mp(e,t){return mw(e,t)||e.state.data!==void 0&&td(e,t,t.refetchOnMount)}function td(e,t,n){if(Vt(t.enabled,e)!==!1&&Mr(t.staleTime,e)!=="static"){const r=typeof n=="function"?n(e):n;return r==="always"||r!==!1&&wf(e,t)}return!1}function vp(e,t,n,r){return(e!==t||Vt(r.enabled,e)===!1)&&(!n.suspense||e.state.status!=="error")&&wf(e,n)}function wf(e,t){return Vt(t.enabled,e)!==!1&&e.isStaleByTime(Mr(t.staleTime,e))}function vw(e,t){return!Hc(e.getCurrentResult(),t)}function yp(e){return{onFetch:(t,n)=>{var d,c,f,m,g;const r=t.options,i=(f=(c=(d=t.fetchOptions)==null?void 0:d.meta)==null?void 0:c.fetchMore)==null?void 0:f.direction,s=((m=t.state.data)==null?void 0:m.pages)||[],a=((g=t.state.data)==null?void 0:g.pageParams)||[];let o={pages:[],pageParams:[]},l=0;const u=async()=>{let x=!1;const T=y=>{Object.defineProperty(y,"signal",{enumerable:!0,get:()=>(t.signal.aborted?x=!0:t.signal.addEventListener("abort",()=>{x=!0}),t.signal)})},v=Zy(t.options,t.fetchOptions),p=async(y,S,R)=>{if(x)return Promise.reject();if(S==null&&y.pages.length)return Promise.resolve(y);const k=(()=>{const B={client:t.client,queryKey:t.queryKey,pageParam:S,direction:R?"backward":"forward",meta:t.options.meta};return T(B),B})(),j=await v(k),{maxPages:W}=t.options,P=R?aw:sw;return{pages:P(y.pages,j,W),pageParams:P(y.pageParams,S,W)}};if(i&&s.length){const y=i==="backward",S=y?yw:gp,R={pages:s,pageParams:a},L=S(r,R);o=await p(R,L,y)}else{const y=e??s.length;do{const S=l===0?a[0]??r.initialPageParam:gp(r,o);if(l>0&&S==null)break;o=await p(o,S),l++}while(l{var x,T;return(T=(x=t.options).persister)==null?void 0:T.call(x,u,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n)}:t.fetchFn=u}}}function gp(e,{pages:t,pageParams:n}){const r=t.length-1;return t.length>0?e.getNextPageParam(t[r],t,n[r],n):void 0}function yw(e,{pages:t,pageParams:n}){var r;return t.length>0?(r=e.getPreviousPageParam)==null?void 0:r.call(e,t[0],t,n[0],n):void 0}var za,xn,vt,fi,wn,sr,Tm,gw=(Tm=class extends qy{constructor(t){super();ne(this,wn);ne(this,za);ne(this,xn);ne(this,vt);ne(this,fi);H(this,za,t.client),this.mutationId=t.mutationId,H(this,vt,t.mutationCache),H(this,xn,[]),this.state=t.state||xw(),this.setOptions(t.options),this.scheduleGc()}setOptions(t){this.options=t,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(t){_(this,xn).includes(t)||(_(this,xn).push(t),this.clearGcTimeout(),_(this,vt).notify({type:"observerAdded",mutation:this,observer:t}))}removeObserver(t){H(this,xn,_(this,xn).filter(n=>n!==t)),this.scheduleGc(),_(this,vt).notify({type:"observerRemoved",mutation:this,observer:t})}optionalRemove(){_(this,xn).length||(this.state.status==="pending"?this.scheduleGc():_(this,vt).remove(this))}continue(){var t;return((t=_(this,fi))==null?void 0:t.continue())??this.execute(this.state.variables)}async execute(t){var a,o,l,u,d,c,f,m,g,x,T,v,p,y,S,R,L,k,j,W;const n=()=>{fe(this,wn,sr).call(this,{type:"continue"})},r={client:_(this,za),meta:this.options.meta,mutationKey:this.options.mutationKey};H(this,fi,Gy({fn:()=>this.options.mutationFn?this.options.mutationFn(t,r):Promise.reject(new Error("No mutationFn found")),onFail:(P,B)=>{fe(this,wn,sr).call(this,{type:"failed",failureCount:P,error:B})},onPause:()=>{fe(this,wn,sr).call(this,{type:"pause"})},onContinue:n,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>_(this,vt).canRun(this)}));const i=this.state.status==="pending",s=!_(this,fi).canStart();try{if(i)n();else{fe(this,wn,sr).call(this,{type:"pending",variables:t,isPaused:s}),await((o=(a=_(this,vt).config).onMutate)==null?void 0:o.call(a,t,this,r));const B=await((u=(l=this.options).onMutate)==null?void 0:u.call(l,t,r));B!==this.state.context&&fe(this,wn,sr).call(this,{type:"pending",context:B,variables:t,isPa)DELIM" -R"DELIM(used:s})}const P=await _(this,fi).start();return await((c=(d=_(this,vt).config).onSuccess)==null?void 0:c.call(d,P,t,this.state.context,this,r)),await((m=(f=this.options).onSuccess)==null?void 0:m.call(f,P,t,this.state.context,r)),await((x=(g=_(this,vt).config).onSettled)==null?void 0:x.call(g,P,null,this.state.variables,this.state.context,this,r)),await((v=(T=this.options).onSettled)==null?void 0:v.call(T,P,null,t,this.state.context,r)),fe(this,wn,sr).call(this,{type:"success",data:P}),P}catch(P){try{throw await((y=(p=_(this,vt).config).onError)==null?void 0:y.call(p,P,t,this.state.context,this,r)),await((R=(S=this.options).onError)==null?void 0:R.call(S,P,t,this.state.context,r)),await((k=(L=_(this,vt).config).onSettled)==null?void 0:k.call(L,void 0,P,this.state.variables,this.state.context,this,r)),await((W=(j=this.options).onSettled)==null?void 0:W.call(j,void 0,P,t,this.state.context,r)),P}finally{fe(this,wn,sr).call(this,{type:"error",error:P})}}finally{_(this,vt).runNext(this)}}},za=new WeakMap,xn=new WeakMap,vt=new WeakMap,fi=new WeakMap,wn=new WeakSet,sr=function(t){const n=r=>{switch(t.type){case"failed":return{...r,failureCount:t.failureCount,failureReason:t.error};case"pause":return{...r,isPaused:!0};case"continue":return{...r,isPaused:!1};case"pending":return{...r,context:t.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:t.isPaused,status:"pending",variables:t.variables,submittedAt:Date.now()};case"success":return{...r,data:t.data,failureCount:0,failureReason:null,error:null,status:"success",isPaused:!1};case"error":return{...r,data:void 0,error:t.error,failureCount:r.failureCount+1,failureReason:t.error,isPaused:!1,status:"error"}}};this.state=n(this.state),ot.batch(()=>{_(this,xn).forEach(r=>{r.onMutationUpdate(t)}),_(this,vt).notify({mutation:this,type:"updated",action:t})})},Tm);function xw(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:"idle",variables:void 0,submittedAt:0}}var $n,sn,Ba,bm,ww=(bm=class extends Za{constructor(t={}){super();ne(this,$n);ne(this,sn);ne(this,Ba);this.config=t,H(this,$n,new Set),H(this,sn,new Map),H(this,Ba,0)}build(t,n,r){const i=new gw({client:t,mutationCache:this,mutationId:++so(this,Ba)._,options:t.defaultMutationOptions(n),state:r});return this.add(i),i}add(t){_(this,$n).add(t);const n=Eo(t);if(typeof n=="string"){const r=_(this,sn).get(n);r?r.push(t):_(this,sn).set(n,[t])}this.notify({type:"added",mutation:t})}remove(t){if(_(this,$n).delete(t)){const n=Eo(t);if(typeof n=="string"){const r=_(this,sn).get(n);if(r)if(r.length>1){const i=r.indexOf(t);i!==-1&&r.splice(i,1)}else r[0]===t&&_(this,sn).delete(n)}}this.notify({type:"removed",mutation:t})}canRun(t){const n=Eo(t);if(typeof n=="string"){const r=_(this,sn).get(n),i=r==null?void 0:r.find(s=>s.state.status==="pending");return!i||i===t}else return!0}runNext(t){var r;const n=Eo(t);if(typeof n=="string"){const i=(r=_(this,sn).get(n))==null?void 0:r.find(s=>s!==t&&s.state.isPaused);return(i==null?void 0:i.continue())??Promise.resolve()}else return Promise.resolve()}clear(){ot.batch(()=>{_(this,$n).forEach(t=>{this.notify({type:"removed",mutation:t})}),_(this,$n).clear(),_(this,sn).clear()})}getAll(){return Array.from(_(this,$n))}find(t){const n={exact:!0,...t};return this.getAll().find(r=>cp(n,r))}findAll(t={}){return this.getAll().filter(n=>cp(t,n))}notify(t){ot.batch(()=>{this.listeners.forEach(n=>{n(t)})})}resumePausedMutations(){const t=this.getAll().filter(n=>n.state.isPaused);return ot.batch(()=>Promise.all(t.map(n=>n.continue().catch(Et))))}},$n=new WeakMap,sn=new WeakMap,Ba=new WeakMap,bm);function Eo(e){var t;return(t=e.options.scope)==null?void 0:t.id}var kn,Nm,kw=(Nm=class extends Za{constructor(t={}){super();ne(this,kn);this.config=t,H(this,kn,new Map)}build(t,n,r){const i=n.queryKey,s=n.queryHash??yf(i,n);let a=this.get(s);return a||(a=new hw({client:t,queryKey:i,queryHash:s,options:t.defaultQueryOptions(n),state:r,defaultOptions:t.getQueryDefaults(i)}),this.add(a)),a}add(t){_(this,kn).has(t.queryHash)||(_(this,kn).set(t.queryHash,t),this.notify({type:"added",query:t}))}remove(t){const n=_(this,kn).get(t.queryHash);n&&(t.destroy(),n===t&&_(this,kn).delete(t.queryHash),this.notify({type:"removed",query:t}))}clear(){ot.batch(()=>{this.getAll().forEach(t=>{this.remove(t)})})}get(t){return _(this,kn).get(t)}getAll(){return[..._(this,kn).values()]}find(t){const n={exact:!0,...t};return this.getAll().find(r=>up(n,r))}findAll(t={}){const n=this.getAll();return Object.keys(t).length>0?n.filter(r=>up(t,r)):n}notify(t){ot.batch(()=>{this.listeners.forEach(n=>{n(t)})})}onFocus(){ot.batch(()=>{this.getAll().forEach(t=>{t.onFocus()})})}onOnline(){ot.batch(()=>{this.getAll().forEach(t=>{t.onOnline()})})}},kn=new WeakMap,Nm),ze,yr,gr,ls,us,xr,cs,ds,Pm,_w=(Pm=class{constructor(e={}){ne(this,ze);ne(this,yr);ne(this,gr);ne(this,ls);ne(this,us);ne(this,xr);ne(this,cs);ne(this,ds);H(this,ze,e.queryCache||new kw),H(this,yr,e.mutationCache|)DELIM" -R"DELIM(|new ww),H(this,gr,e.defaultOptions||{}),H(this,ls,new Map),H(this,us,new Map),H(this,xr,0)}mount(){so(this,xr)._++,_(this,xr)===1&&(H(this,cs,xf.subscribe(async e=>{e&&(await this.resumePausedMutations(),_(this,ze).onFocus())})),H(this,ds,wl.subscribe(async e=>{e&&(await this.resumePausedMutations(),_(this,ze).onOnline())})))}unmount(){var e,t;so(this,xr)._--,_(this,xr)===0&&((e=_(this,cs))==null||e.call(this),H(this,cs,void 0),(t=_(this,ds))==null||t.call(this),H(this,ds,void 0))}isFetching(e){return _(this,ze).findAll({...e,fetchStatus:"fetching"}).length}isMutating(e){return _(this,yr).findAll({...e,status:"pending"}).length}getQueryData(e){var n;const t=this.defaultQueryOptions({queryKey:e});return(n=_(this,ze).get(t.queryHash))==null?void 0:n.state.data}ensureQueryData(e){const t=this.defaultQueryOptions(e),n=_(this,ze).build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(Mr(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return _(this,ze).findAll(e).map(({queryKey:t,state:n})=>{const r=n.data;return[t,r]})}setQueryData(e,t,n){const r=this.defaultQueryOptions({queryKey:e}),i=_(this,ze).get(r.queryHash),s=i==null?void 0:i.state.data,a=nw(t,s);if(a!==void 0)return _(this,ze).build(this,r).setData(a,{...n,manual:!0})}setQueriesData(e,t,n){return ot.batch(()=>_(this,ze).findAll(e).map(({queryKey:r})=>[r,this.setQueryData(r,t,n)]))}getQueryState(e){var n;const t=this.defaultQueryOptions({queryKey:e});return(n=_(this,ze).get(t.queryHash))==null?void 0:n.state}removeQueries(e){const t=_(this,ze);ot.batch(()=>{t.findAll(e).forEach(n=>{t.remove(n)})})}resetQueries(e,t){const n=_(this,ze);return ot.batch(()=>(n.findAll(e).forEach(r=>{r.reset()}),this.refetchQueries({type:"active",...e},t)))}cancelQueries(e,t={}){const n={revert:!0,...t},r=ot.batch(()=>_(this,ze).findAll(e).map(i=>i.cancel(n)));return Promise.all(r).then(Et).catch(Et)}invalidateQueries(e,t={}){return ot.batch(()=>(_(this,ze).findAll(e).forEach(n=>{n.invalidate()}),(e==null?void 0:e.refetchType)==="none"?Promise.resolve():this.refetchQueries({...e,type:(e==null?void 0:e.refetchType)??(e==null?void 0:e.type)??"active"},t)))}refetchQueries(e,t={}){const n={...t,cancelRefetch:t.cancelRefetch??!0},r=ot.batch(()=>_(this,ze).findAll(e).filter(i=>!i.isDisabled()&&!i.isStatic()).map(i=>{let s=i.fetch(void 0,n);return n.throwOnError||(s=s.catch(Et)),i.state.fetchStatus==="paused"?Promise.resolve():s}));return Promise.all(r).then(Et)}fetchQuery(e){const t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);const n=_(this,ze).build(this,t);return n.isStaleByTime(Mr(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(Et).catch(Et)}fetchInfiniteQuery(e){return e.behavior=yp(e.pages),this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(Et).catch(Et)}ensureInfiniteQueryData(e){return e.behavior=yp(e.pages),this.ensureQueryData(e)}resumePausedMutations(){return wl.isOnline()?_(this,yr).resumePausedMutations():Promise.resolve()}getQueryCache(){return _(this,ze)}getMutationCache(){return _(this,yr)}getDefaultOptions(){return _(this,gr)}setDefaultOptions(e){H(this,gr,e)}setQueryDefaults(e,t){_(this,ls).set(Na(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){const t=[..._(this,ls).values()],n={};return t.forEach(r=>{Pa(e,r.queryKey)&&Object.assign(n,r.defaultOptions)}),n}setMutationDefaults(e,t){_(this,us).set(Na(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){const t=[..._(this,us).values()],n={};return t.forEach(r=>{Pa(e,r.mutationKey)&&Object.assign(n,r.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;const t={..._(this,gr).queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||(t.queryHash=yf(t.queryKey,t)),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!=="always"),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode="offlineFirst"),t.queryFn===gf&&(t.enabled=!1),t}defaultMutationOptions(e){return e!=null&&e._defaulted?e:{..._(this,gr).mutations,...(e==null?void 0:e.mutationKey)&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){_(this,ze).clear(),_(this,yr).clear()}},ze=new WeakMap,yr=new WeakMap,gr=new WeakMap,ls=new WeakMap,us=new WeakMap,xr=new WeakMap,cs=new WeakMap,ds=new WeakMap,Pm),Jy=C.createContext(void 0),eg=e=>{const t=C.useContext(Jy);if(!t)throw new Error("No QueryClient set, use QueryClientProvider to set one");return t},Sw=({client:e,children:t})=>(C.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),h.jsx(Jy.Provider,{value:e,children:t})),tg=C.createContext(!1),Ew=()=>C.useContext(tg);tg.Provider;function Cw(){let e=!1;return{clearReset:()=>{e=!1},reset:()=>{e=!0},isReset:()=>e}}var Tw=C.createContext(Cw()),bw=()=>C.useContext(Tw),Nw=(e,t)=>{(e.suspense||e.throwOnError||e.experimental_prefetchInRender)&&(t.isReset()||(e.retryOnMount)DELIM" -R"DELIM(=!1))},Pw=e=>{C.useEffect(()=>{e.clearReset()},[e])},Rw=({result:e,errorResetBoundary:t,throwOnError:n,query:r,suspense:i})=>e.isError&&!t.isReset()&&!e.isFetching&&r&&(i&&e.data===void 0||ow(n,[e.error,r])),jw=e=>{if(e.suspense){const n=i=>i==="static"?i:Math.max(i??1e3,1e3),r=e.staleTime;e.staleTime=typeof r=="function"?(...i)=>n(r(...i)):n(r),typeof e.gcTime=="number"&&(e.gcTime=Math.max(e.gcTime,1e3))}},Mw=(e,t)=>e.isLoading&&e.isFetching&&!t,Lw=(e,t)=>(e==null?void 0:e.suspense)&&t.isPending,xp=(e,t,n)=>t.fetchOptimistic(e).catch(()=>{n.clearReset()});function Ow(e,t,n){var c,f,m,g,x;const r=Ew(),i=bw(),s=eg(),a=s.defaultQueryOptions(e);(f=(c=s.getDefaultOptions().queries)==null?void 0:c._experimental_beforeQuery)==null||f.call(c,a),a._optimisticResults=r?"isRestoring":"optimistic",jw(a),Nw(a,i),Pw(i);const o=!s.getQueryCache().get(a.queryHash),[l]=C.useState(()=>new t(s,a)),u=l.getOptimisticResult(a),d=!r&&e.subscribed!==!1;if(C.useSyncExternalStore(C.useCallback(T=>{const v=d?l.subscribe(ot.batchCalls(T)):Et;return l.updateResult(),v},[l,d]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),C.useEffect(()=>{l.setOptions(a)},[a,l]),Lw(a,u))throw xp(a,l,i);if(Rw({result:u,errorResetBoundary:i,throwOnError:a.throwOnError,query:s.getQueryCache().get(a.queryHash),suspense:a.suspense}))throw u.error;if((g=(m=s.getDefaultOptions().queries)==null?void 0:m._experimental_afterQuery)==null||g.call(m,a,u),a.experimental_prefetchInRender&&!_i&&Mw(u,r)){const T=o?xp(a,l,i):(x=s.getQueryCache().get(a.queryHash))==null?void 0:x.promise;T==null||T.catch(Et).finally(()=>{l.updateResult()})}return a.notifyOnChangeProps?u:l.trackResult(u)}function ql(e,t){return Ow(e,pw)}const Dw="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let wp=(e=21)=>{let t="",n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+=Dw[n[e]&63];return t};var ng={exports:{}};(function(e,t){(function(n,r){e.exports=r()})(E0,function(){var n=1e3,r=6e4,i=36e5,s="millisecond",a="second",o="minute",l="hour",u="day",d="week",c="month",f="quarter",m="year",g="date",x="Invalid Date",T=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,v=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,p={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(Z){var F=["th","st","nd","rd"],$=Z%100;return"["+Z+(F[($-20)%10]||F[$]||F[0])+"]"}},y=function(Z,F,$){var K=String(Z);return!K||K.length>=F?Z:""+Array(F+1-K.length).join($)+Z},S={s:y,z:function(Z){var F=-Z.utcOffset(),$=Math.abs(F),K=Math.floor($/60),b=$%60;return(F<=0?"+":"-")+y(K,2,"0")+":"+y(b,2,"0")},m:function Z(F,$){if(F.date()<$.date())return-Z($,F);var K=12*($.year()-F.year())+($.month()-F.month()),b=F.clone().add(K,c),D=$-b<0,I=F.clone().add(K+(D?-1:1),c);return+(-(K+($-b)/(D?b-I:I-b))||0)},a:function(Z){return Z<0?Math.ceil(Z)||0:Math.floor(Z)},p:function(Z){return{M:c,y:m,w:d,d:u,D:g,h:l,m:o,s:a,ms:s,Q:f}[Z]||String(Z||"").toLowerCase().replace(/s$/,"")},u:function(Z){return Z===void 0}},R="en",L={};L[R]=p;var k="$isDayjsObject",j=function(Z){return Z instanceof ue||!(!Z||!Z[k])},W=function Z(F,$,K){var b;if(!F)return R;if(typeof F=="string"){var D=F.toLowerCase();L[D]&&(b=D),$&&(L[D]=$,b=D);var I=F.split("-");if(!b&&I.length>1)return Z(I[0])}else{var ee=F.name;L[ee]=F,b=ee}return!K&&b&&(R=b),b||!K&&R},P=function(Z,F){if(j(Z))return Z.clone();var $=typeof F=="object"?F:{};return $.date=Z,$.args=arguments,new ue($)},B=S;B.l=W,B.i=j,B.w=function(Z,F){return P(Z,{locale:F.$L,utc:F.$u,x:F.$x,$offset:F.$offset})};var ue=function(){function Z($){this.$L=W($.locale,null,!0),this.parse($),this.$x=this.$x||$.x||{},this[k]=!0}var F=Z.prototype;return F.parse=function($){this.$d=function(K){var b=K.date,D=K.utc;if(b===null)return new Date(NaN);if(B.u(b))return new Date;if(b instanceof Date)return new Date(b);if(typeof b=="string"&&!/Z$/i.test(b)){var I=b.match(T);if(I){var ee=I[2]-1||0,re=(I[7]||"0").substring(0,3);return D?new Date(Date.UTC(I[1],ee,I[3]||1,I[4]||0,I[5]||0,I[6]||0,re)):new Date(I[1],ee,I[3]||1,I[4]||0,I[5]||0,I[6]||0,re)}}return new Date(b)}($),this.init()},F.init=function(){var $=this.$d;this.$y=$.getFullYear(),this.$M=$.getMonth(),this.$D=$.getDate(),this.$W=$.getDay(),this.$H=$.getHours(),this.$m=$.getMinutes(),this.$s=$.getSeconds(),this.$ms=$.getMilliseconds()},F.$utils=function(){return B},F.isValid=function(){return this.$d.toString()!==x},F.isSame=function($,K){var b=P($);return this.startOf(K)<=b&&b<=this.endOf(K)},F.isAfter=function($,K){return P($)-1&&e%1==0&&e0?t[Yw(0,n-1)]:void 0}function tk(e){return rk(e)&&kf.call(e,"callee")&&(!Qw.call(e,"callee")||sg.call(e)==Iw)}var nk=Array.isArray;function _f(e){return e!=null&&sk(e.length)&&!ik(e)}function rk(e){return ok(e)&&_f(e)}function ik(e){var t=ak(e)?sg.call(e):"";return t==$w||t==Fw}function sk(e){return typeof e=="number"&&e>-1&&e%1==0&&e<=ig}function ak(e){var t=typeof e;return!!e&&(t=="object"||t=="function")}function ok(e){return!!e&&typeof e=="object"}function lk(e){return _f(e)?Gw(e):qw(e)}function uk(e){return e?Hw(e,lk(e)):[]}var ck=ek;const dk=Ll(ck);function Xl(e){if(!e)return;const t=e.toLowerCase(),n=t.indexOf("1");if(n>0){const i=t.slice(0,n);if(["bc","tb","bcrt","ltc","sb"].some(s=>i===s)){const s=t.slice(n+1),a=s.charAt(0);return a==="p"?"p2tr":a==="q"?s.length===39||s.length===40||e.length===42?"p2wpkh":s.length===59||s.length===60||e.length===62?"p2wsh":"segwit":"segwit"}}switch(t[0]){case"1":case"m":case"n":return"p2pkh";case"3":case"2":return"p2sh";default:return}}const Xs=new Set;let Au=0;function kp(e){Xs.forEach(t=>t(e))}const ag=rg().subtract(2,"hour").unix(),Ka=Array.from({length:20}).map((e,t)=>{const n=`mock-tx-${t.toString().padStart(4,"0")}`,r=[{index:0,address:`bcrt1qmock${t}`,valueSats:1e6+t*1234,scriptType:"p2wpkh",spent:!1,script:{hex:`0014mockoutput${t}`,mnemonic:"OP_0 mock-output-pubkey"}}];return{txid:n,hash:n,status:"confirmed",confirmations:10-Math.floor(t/2),blockHeight:8e5-t,blockHash:`mock-block-${Math.floor(t/2)}`,receivedTime:ag+t*60,feeSats:250+t,feeRate:12+t*.1,size:225+t,weight:900+t*4,version:2,locktime:0,totalInput:1005e3,totalOutput:r.reduce((i,s)=>i+s.valueSats,0),inputs:[{index:0,prevTxId:`mock-prev-${t}`,prevOutputIndex:0,address:`bcrt1qprev${t}`,valueSats:1005e3,scriptType:"p2wpkh",script:{hex:`160014mockinputscriptsig${t}`,mnemonic:"OP_PUSHBYTES_22 mock-input-scriptsig"},witness:["3045022100cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe","02deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbe"],sequence:4294967293}],outputs:r}}),gt=[];var Rm;for(let e=0;e<6;e+=1){const t=8e5+e,n=`mock-block-${t}`,r=Ka.slice(e*3,e*3+3);gt.push({hash:n,height:t,timestamp:ag+e*600,txCount:r.length,size:12e5,weight:4e6,difficulty:5e10,previousBlockHash:e===0?null:(Rm=gt[e-1])==null?void 0:Rm.hash,merkleRoot:`mock-merkle-${e}`,nonce:1e3+e,bits:486604799,version:2,nextBlockHash:null,transactions:r}),e>0&&(gt[e-1].nextBlockHash=n)}const fk={bcrt1qmock0:{address:"bcrt1qmock0",addressType:Xl("bcrt1qmock0"),balance:2345678,totalReceived:5e6,totalSent:2654322,txCount:5,utxoCount:3,transactions:Ka.slice(0,5).map(e=>{var t;return{txid:e.txid,timestamp:e.receivedTime,valueChange:((t=e.outputs[0])==null?void 0:t.valueSats)??0,blockHeight:e.blockHeight}})}};function Iu(){return{metadata:{id:"mock",label:"Mock data (offline)",type:"mock",connectionState:"connected",supportsWebsocket:!0,description:"Generates synthetic blockchain data for development."},async getLatestBlocks(e=10){return gt.slice().reverse().slice(0,e).map(t=>({hash:t.hash,height:t.height,timestamp:t.timestamp,txCount:t.txCount,size:t.size,weight:t.weight,difficulty:t.difficulty,previousBlockHash:t.previousBlockHash}))},async getBlock(e){if(e==="latest")return gt[gt.length-1];const t=gt.find(n=>n.hash===e||n.height.toString()===e);if(!t)throw new Error("Block not found");return t},async getBlocksBefore(e,t=10){return gt.filter(r=>r.height<=e).slice().reverse().slice(0,t).map(r=>({hash:r.hash,height:r.height,timestamp:r.timestamp,txCount:r.txCount,size:r.size,weight:r.weight,difficulty:r.difficulty,previousBlockHash:r.previousBlockHash}))},async getBlockTransactions(e,t=0){return(await this.getBlock(e)).transactions.slice(t,t+25)},async getTransaction(e){const t=Ka.find(n=>n.txid===e||n.hash===e);if(!t)throw new Error("Transaction not found");return t},async getAddress(e){return fk[e]??{address:e,addressType:Xl(e),balance:0,totalReceived:0,totalSent:0,txCount:0,utxoCount:0,transactions:[]}},async getAddressTransactions(e,t){const n=await this.getAddress(e),r=10,i=t?n.transactions.findIndex(s=>s.txid===t)+1:0;return i<=0&&t?[]:n.transactions.slice(i,i+r)},subscribeToEvents(e){return Xs.add(e),Xs.size===1&&hk(),()=>{Xs.delete(e),Xs.size===0&&pk()}}}}let ca=null;function hk(){ca||(ca=setInterval(()=>{const e=vk();gt.push(e),kp({type:"block.new",payload:mk(e)});const t=dk(Ka);t&&kp({type:"tx.new",payload:t})},15e3))}function pk(){ca&&(clearInterval(ca),ca=null)}function mk(e){return{hash:e.hash,height:e.height,timestamp:e.timestamp,txCount:e.txCount,size:e.size,weight:e.weight,difficulty:e.difficulty,previousBlockHash:e.previousBlockHash}}function vk(){Au+=1;const e=gt[gt.length-1].height+1,t=`mock-block-${e}-${wp(6)}`,n=rg().unix(),r=Ka.slice(0,2).map(a=>({...a,blockHash:t,blockHeight:e})),i={hash:t,height:e,timestamp:n,txCount:r.length,size:1)DELIM" -R"DELIM(2e5+Au*1e3,weight:4e6,difficulty:5e10,previousBlockHash:gt[gt.length-1].hash,merkleRoot:`mock-merkle-${wp(10)}`,nonce:1e3+Au,bits:486604799,version:2,nextBlockHash:null,transactions:r},s=gt[gt.length-1];return s.nextBlockHash=t,i}var xe;(function(e){e.assertEqual=i=>{};function t(i){}e.assertIs=t;function n(i){throw new Error}e.assertNever=n,e.arrayToEnum=i=>{const s={};for(const a of i)s[a]=a;return s},e.getValidEnumValues=i=>{const s=e.objectKeys(i).filter(o=>typeof i[i[o]]!="number"),a={};for(const o of s)a[o]=i[o];return e.objectValues(a)},e.objectValues=i=>e.objectKeys(i).map(function(s){return i[s]}),e.objectKeys=typeof Object.keys=="function"?i=>Object.keys(i):i=>{const s=[];for(const a in i)Object.prototype.hasOwnProperty.call(i,a)&&s.push(a);return s},e.find=(i,s)=>{for(const a of i)if(s(a))return a},e.isInteger=typeof Number.isInteger=="function"?i=>Number.isInteger(i):i=>typeof i=="number"&&Number.isFinite(i)&&Math.floor(i)===i;function r(i,s=" | "){return i.map(a=>typeof a=="string"?`'${a}'`:a).join(s)}e.joinValues=r,e.jsonStringifyReplacer=(i,s)=>typeof s=="bigint"?s.toString():s})(xe||(xe={}));var _p;(function(e){e.mergeShapes=(t,n)=>({...t,...n})})(_p||(_p={}));const q=xe.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),ur=e=>{switch(typeof e){case"undefined":return q.undefined;case"string":return q.string;case"number":return Number.isNaN(e)?q.nan:q.number;case"boolean":return q.boolean;case"function":return q.function;case"bigint":return q.bigint;case"symbol":return q.symbol;case"object":return Array.isArray(e)?q.array:e===null?q.null:e.then&&typeof e.then=="function"&&e.catch&&typeof e.catch=="function"?q.promise:typeof Map<"u"&&e instanceof Map?q.map:typeof Set<"u"&&e instanceof Set?q.set:typeof Date<"u"&&e instanceof Date?q.date:q.object;default:return q.unknown}},A=xe.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]);class Zn extends Error{get errors(){return this.issues}constructor(t){super(),this.issues=[],this.addIssue=r=>{this.issues=[...this.issues,r]},this.addIssues=(r=[])=>{this.issues=[...this.issues,...r]};const n=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,n):this.__proto__=n,this.name="ZodError",this.issues=t}format(t){const n=t||function(s){return s.message},r={_errors:[]},i=s=>{for(const a of s.issues)if(a.code==="invalid_union")a.unionErrors.map(i);else if(a.code==="invalid_return_type")i(a.returnTypeError);else if(a.code==="invalid_arguments")i(a.argumentsError);else if(a.path.length===0)r._errors.push(n(a));else{let o=r,l=0;for(;ln.message){const n={},r=[];for(const i of this.issues)if(i.path.length>0){const s=i.path[0];n[s]=n[s]||[],n[s].push(t(i))}else r.push(t(i));return{formErrors:r,fieldErrors:n}}get formErrors(){return this.flatten()}}Zn.create=e=>new Zn(e);const nd=(e,t)=>{let n;switch(e.code){case A.invalid_type:e.received===q.undefined?n="Required":n=`Expected ${e.expected}, received ${e.received}`;break;case A.invalid_literal:n=`Invalid literal value, expected ${JSON.stringify(e.expected,xe.jsonStringifyReplacer)}`;break;case A.unrecognized_keys:n=`Unrecognized key(s) in object: ${xe.joinValues(e.keys,", ")}`;break;case A.invalid_union:n="Invalid input";break;case A.invalid_union_discriminator:n=`Invalid discriminator value. Expected ${xe.joinValues(e.options)}`;break;case A.invalid_enum_value:n=`Invalid enum value. Expected ${xe.joinValues(e.options)}, received '${e.received}'`;break;case A.invalid_arguments:n="Invalid function arguments";break;case A.invalid_return_type:n="Invalid function return type";break;case A.invalid_date:n="Invalid date";break;case A.invalid_string:typeof e.validation=="object"?"includes"in e.validation?(n=`Invalid input: must include "${e.validation.includes}"`,typeof e.validation.position=="number"&&(n=`${n} at one or more positions greater than or equal to ${e.validation.position}`)):"startsWith"in e.validation?n=`Invalid input: must start with "${e.validation.startsWith}"`:"endsWith"in e.validation?n=`Invalid input: must end with "${e.validation.endsWith}"`:xe.assertNever(e.validation):e.validation!=="regex"?n=`Invalid ${e.validation}`:n="Invalid";break;case A.too_small:e.type==="array"?n=`Array must contain ${e.exact?"exactly":e.in)DELIM" -R"DELIM(clusive?"at least":"more than"} ${e.minimum} element(s)`:e.type==="string"?n=`String must contain ${e.exact?"exactly":e.inclusive?"at least":"over"} ${e.minimum} character(s)`:e.type==="number"?n=`Number must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${e.minimum}`:e.type==="bigint"?n=`Number must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${e.minimum}`:e.type==="date"?n=`Date must be ${e.exact?"exactly equal to ":e.inclusive?"greater than or equal to ":"greater than "}${new Date(Number(e.minimum))}`:n="Invalid input";break;case A.too_big:e.type==="array"?n=`Array must contain ${e.exact?"exactly":e.inclusive?"at most":"less than"} ${e.maximum} element(s)`:e.type==="string"?n=`String must contain ${e.exact?"exactly":e.inclusive?"at most":"under"} ${e.maximum} character(s)`:e.type==="number"?n=`Number must be ${e.exact?"exactly":e.inclusive?"less than or equal to":"less than"} ${e.maximum}`:e.type==="bigint"?n=`BigInt must be ${e.exact?"exactly":e.inclusive?"less than or equal to":"less than"} ${e.maximum}`:e.type==="date"?n=`Date must be ${e.exact?"exactly":e.inclusive?"smaller than or equal to":"smaller than"} ${new Date(Number(e.maximum))}`:n="Invalid input";break;case A.custom:n="Invalid input";break;case A.invalid_intersection_types:n="Intersection results could not be merged";break;case A.not_multiple_of:n=`Number must be a multiple of ${e.multipleOf}`;break;case A.not_finite:n="Number must be finite";break;default:n=t.defaultError,xe.assertNever(e)}return{message:n}};let yk=nd;function gk(){return yk}const xk=e=>{const{data:t,path:n,errorMaps:r,issueData:i}=e,s=[...n,...i.path||[]],a={...i,path:s};if(i.message!==void 0)return{...i,path:s,message:i.message};let o="";const l=r.filter(u=>!!u).slice().reverse();for(const u of l)o=u(a,{data:t,defaultError:o}).message;return{...i,path:s,message:o}};function U(e,t){const n=gk(),r=xk({issueData:t,data:e.data,path:e.path,errorMaps:[e.common.contextualErrorMap,e.schemaErrorMap,n,n===nd?void 0:nd].filter(i=>!!i)});e.common.issues.push(r)}class At{constructor(){this.value="valid"}dirty(){this.value==="valid"&&(this.value="dirty")}abort(){this.value!=="aborted"&&(this.value="aborted")}static mergeArray(t,n){const r=[];for(const i of n){if(i.status==="aborted")return se;i.status==="dirty"&&t.dirty(),r.push(i.value)}return{status:t.value,value:r}}static async mergeObjectAsync(t,n){const r=[];for(const i of n){const s=await i.key,a=await i.value;r.push({key:s,value:a})}return At.mergeObjectSync(t,r)}static mergeObjectSync(t,n){const r={};for(const i of n){const{key:s,value:a}=i;if(s.status==="aborted"||a.status==="aborted")return se;s.status==="dirty"&&t.dirty(),a.status==="dirty"&&t.dirty(),s.value!=="__proto__"&&(typeof a.value<"u"||i.alwaysSet)&&(r[s.value]=a.value)}return{status:t.value,value:r}}}const se=Object.freeze({status:"aborted"}),Js=e=>({status:"dirty",value:e}),Xt=e=>({status:"valid",value:e}),Sp=e=>e.status==="aborted",Ep=e=>e.status==="dirty",xs=e=>e.status==="valid",kl=e=>typeof Promise<"u"&&e instanceof Promise;var X;(function(e){e.errToObj=t=>typeof t=="string"?{message:t}:t||{},e.toString=t=>typeof t=="string"?t:t==null?void 0:t.message})(X||(X={}));class Ir{constructor(t,n,r,i){this._cachedPath=[],this.parent=t,this.data=n,this._path=r,this._key=i}get path(){return this._cachedPath.length||(Array.isArray(this._key)?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}}const Cp=(e,t)=>{if(xs(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,get error(){if(this._error)return this._error;const n=new Zn(e.common.issues);return this._error=n,this._error}}};function ce(e){if(!e)return{};const{errorMap:t,invalid_type_error:n,required_error:r,description:i}=e;if(t&&(n||r))throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);return t?{errorMap:t,description:i}:{errorMap:(a,o)=>{const{message:l}=e;return a.code==="invalid_enum_value"?{message:l??o.defaultError}:typeof o.data>"u"?{message:l??r??o.defaultError}:a.code!=="invalid_type"?{message:o.defaultError}:{message:l??n??o.defaultError}},description:i}}class ye{get description(){return this._def.description}_getType(t){return ur(t.data)}_getOrReturnCtx(t,n){return n||{common:t.parent.common,data:t.data,parsedType:ur(t.data),schemaErrorMap:this._def.errorMap,path:t.path,parent:t.parent}}_processInputParams(t){return{status:new At,ctx:{common:t.parent.common,data:t.data,parsedType:ur(t.data),schemaErrorMap:this._def.errorMap,path:t.path,parent:t.parent}}}_parseSync(t){const n=this._parse(t);if(kl(n))throw new Error("Synchronous parse encountered promise.");return n}_parseAsync(t){const n=this._parse(t);return Promise.resolve(n)}parse(t,n){const r=this.safeParse(t,n);if(r.success)return r.data;throw r.error}safePar)DELIM" -R"DELIM(se(t,n){const r={common:{issues:[],async:(n==null?void 0:n.async)??!1,contextualErrorMap:n==null?void 0:n.errorMap},path:(n==null?void 0:n.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:t,parsedType:ur(t)},i=this._parseSync({data:t,path:r.path,parent:r});return Cp(r,i)}"~validate"(t){var r,i;const n={common:{issues:[],async:!!this["~standard"].async},path:[],schemaErrorMap:this._def.errorMap,parent:null,data:t,parsedType:ur(t)};if(!this["~standard"].async)try{const s=this._parseSync({data:t,path:[],parent:n});return xs(s)?{value:s.value}:{issues:n.common.issues}}catch(s){(i=(r=s==null?void 0:s.message)==null?void 0:r.toLowerCase())!=null&&i.includes("encountered")&&(this["~standard"].async=!0),n.common={issues:[],async:!0}}return this._parseAsync({data:t,path:[],parent:n}).then(s=>xs(s)?{value:s.value}:{issues:n.common.issues})}async parseAsync(t,n){const r=await this.safeParseAsync(t,n);if(r.success)return r.data;throw r.error}async safeParseAsync(t,n){const r={common:{issues:[],contextualErrorMap:n==null?void 0:n.errorMap,async:!0},path:(n==null?void 0:n.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:t,parsedType:ur(t)},i=this._parse({data:t,path:r.path,parent:r}),s=await(kl(i)?i:Promise.resolve(i));return Cp(r,s)}refine(t,n){const r=i=>typeof n=="string"||typeof n>"u"?{message:n}:typeof n=="function"?n(i):n;return this._refinement((i,s)=>{const a=t(i),o=()=>s.addIssue({code:A.custom,...r(i)});return typeof Promise<"u"&&a instanceof Promise?a.then(l=>l?!0:(o(),!1)):a?!0:(o(),!1)})}refinement(t,n){return this._refinement((r,i)=>t(r)?!0:(i.addIssue(typeof n=="function"?n(r,i):n),!1))}_refinement(t){return new _s({schema:this,typeName:ae.ZodEffects,effect:{type:"refinement",refinement:t}})}superRefine(t){return this._refinement(t)}constructor(t){this.spa=this.safeParseAsync,this._def=t,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.readonly=this.readonly.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this),this["~standard"]={version:1,vendor:"zod",validate:n=>this["~validate"](n)}}optional(){return Lr.create(this,this._def)}nullable(){return Ss.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return bn.create(this)}promise(){return Cl.create(this,this._def)}or(t){return Sl.create([this,t],this._def)}and(t){return El.create(this,t,this._def)}transform(t){return new _s({...ce(this._def),schema:this,typeName:ae.ZodEffects,effect:{type:"transform",transform:t}})}default(t){const n=typeof t=="function"?t:()=>t;return new sd({...ce(this._def),innerType:this,defaultValue:n,typeName:ae.ZodDefault})}brand(){return new Uk({typeName:ae.ZodBranded,type:this,...ce(this._def)})}catch(t){const n=typeof t=="function"?t:()=>t;return new ad({...ce(this._def),innerType:this,catchValue:n,typeName:ae.ZodCatch})}describe(t){const n=this.constructor;return new n({...this._def,description:t})}pipe(t){return Sf.create(this,t)}readonly(){return od.create(this)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}const wk=/^c[^\s-]{8,}$/i,kk=/^[0-9a-z]+$/,_k=/^[0-9A-HJKMNP-TV-Z]{26}$/i,Sk=/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i,Ek=/^[a-z0-9_-]{21}$/i,Ck=/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/,Tk=/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/,bk=/^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i,Nk="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";let $u;const Pk=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,Rk=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/,jk=/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([)DELIM" -R"DELIM(0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,Mk=/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,Lk=/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,Ok=/^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,og="((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))",Dk=new RegExp(`^${og}$`);function lg(e){let t="[0-5]\\d";e.precision?t=`${t}\\.\\d{${e.precision}}`:e.precision==null&&(t=`${t}(\\.\\d+)?`);const n=e.precision?"+":"?";return`([01]\\d|2[0-3]):[0-5]\\d(:${t})${n}`}function Ak(e){return new RegExp(`^${lg(e)}$`)}function Ik(e){let t=`${og}T${lg(e)}`;const n=[];return n.push(e.local?"Z?":"Z"),e.offset&&n.push("([+-]\\d{2}:?\\d{2})"),t=`${t}(${n.join("|")})`,new RegExp(`^${t}$`)}function $k(e,t){return!!((t==="v4"||!t)&&Pk.test(e)||(t==="v6"||!t)&&jk.test(e))}function Fk(e,t){if(!Ck.test(e))return!1;try{const[n]=e.split(".");if(!n)return!1;const r=n.replace(/-/g,"+").replace(/_/g,"/").padEnd(n.length+(4-n.length%4)%4,"="),i=JSON.parse(atob(r));return!(typeof i!="object"||i===null||"typ"in i&&(i==null?void 0:i.typ)!=="JWT"||!i.alg||t&&i.alg!==t)}catch{return!1}}function zk(e,t){return!!((t==="v4"||!t)&&Rk.test(e)||(t==="v6"||!t)&&Mk.test(e))}class _r extends ye{_parse(t){if(this._def.coerce&&(t.data=String(t.data)),this._getType(t)!==q.string){const s=this._getOrReturnCtx(t);return U(s,{code:A.invalid_type,expected:q.string,received:s.parsedType}),se}const r=new At;let i;for(const s of this._def.checks)if(s.kind==="min")t.data.lengths.value&&(i=this._getOrReturnCtx(t,i),U(i,{code:A.too_big,maximum:s.value,type:"string",inclusive:!0,exact:!1,message:s.message}),r.dirty());else if(s.kind==="length"){const a=t.data.length>s.value,o=t.data.lengtht.test(i),{validation:n,code:A.invalid_string,...X.errToObj(r)})}_addCheck(t){return new _r({...this._def,checks:[...this._def.checks,t]})}email(t){return this._addCheck({kind:"email",...X.errToObj(t)})}url(t){return this._addCheck({kind:"url",...X.errToObj(t)})}emoji(t){return this._addCheck({kind:"emoji",...X.errToObj(t)})}uuid(t){return this._addCheck({kind:"uuid",...X.errToObj(t)})}nanoid(t){return this._addCheck({kind:"nanoid",...X.errToObj(t)})}cuid(t){return this._addCheck({kind:"cuid",...X.errToObj(t)})}cuid2(t){return this._addCheck({kind:"cuid2",...X.errToObj(t)})}ulid(t){return this._addCheck({kind:"ulid",...X.errToObj(t)})}base64(t){return this._addCheck({kind:"base64",...X.errToObj(t)})}base64url(t){return this._addCheck({kind:"base64url",...X.errToObj(t)})}jwt(t){return this._addCheck({kind:"jwt",...X.errToObj(t)})}ip(t){return this._addCheck({kind:"ip",...X.errToObj(t)})}cidr(t){return this._addCheck({kind:"cidr",...X.errToObj(t)})}datetime(t){return typeof t=="string"?this._addCheck({kind:"datetime",precision:null,offset:!1,local:!1,message:t}):this._addCheck({kind:"datetime",precision:typeof(t==null?void 0:t.precision)>"u"?null:t==null?void 0:t.precision,offset:(t==null?void 0:t.offset)??!1,local:(t==null?void 0:t.local)??!1,...X.errToObj(t==null?void 0:t.message)})}date(t){return this._addCheck({kind:"date",message:t})}time(t){return typeof t=="string"?this._addCheck({kind:"time",precision:null,message:t}):this._addCheck({kind:"time",precision:typeof(t==null?void 0:t.precision)>"u"?null:t==null?void 0:t.precision,...X.errToObj(t==null?void 0:t.message)})}duration(t){return this._addCheck({kind:"duration",...X.errToObj(t)})}regex(t,n){return this._addCheck({kind:"regex",regex:t,...X.errToObj(n)})}includes(t,n){return this._addCheck({kind:"includes",value:t,position:n==null?void 0:n.position,...X.errToObj(n==null?void 0:n.message)})}startsWith(t,n){return this._addCheck({kind:"startsWith",value:t,...X.errToObj(n)})}endsWith(t,n){return this._addCheck({kind:"endsWith",value:t,...X.errToObj(n)})}min(t,n){return this._addCheck({kind:"min",value:t,...X.errToObj(n)})}max(t,n){return this._addCheck({kind:"max",value:t,...X.errToObj(n)})}length(t,n){return this._addCheck({kind:"length",value:t,...X.errToObj(n)})}nonempty(t){return this.min(1,X.errToObj(t))}trim(){return new _r({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}toLowerCase(){return new _r({...this._def,checks:[...this._def.checks,{kind:"toLowerCase"}]})}toUpperCase(){return new _r({...this._def,checks:[...this._def.checks,{kind:"toUpperCase"}]})}get isDatetime(){return!!this._def.checks.find(t=>t.kind==="datetime")}get isDate(){return!!this._def.checks.find(t=>t.kind==="date")}get isTime(){return!!this._def.checks.find(t=>t.kind==="time")}get isDuration(){return!!this._def.checks.find(t=>t.kind==="duration")}get isEmail(){return!!this._def.checks.find(t=>t.kind==="email")}get isURL(){return!!this._def.checks.find(t=>t.kind==="url")}get isEmoji(){return!!this._def.checks.find(t=>t.kind==="emoji")}get isUUID(){return!!this._def.checks.find(t=>t.kind==="uuid")}get isNANOID(){return!!this._def.checks.find(t=>t.kind==="nanoid")}get isCUID(){return!!this._def.checks.find(t=>t.kind==="cuid")}get isCUID2())DELIM" -R"DELIM({return!!this._def.checks.find(t=>t.kind==="cuid2")}get isULID(){return!!this._def.checks.find(t=>t.kind==="ulid")}get isIP(){return!!this._def.checks.find(t=>t.kind==="ip")}get isCIDR(){return!!this._def.checks.find(t=>t.kind==="cidr")}get isBase64(){return!!this._def.checks.find(t=>t.kind==="base64")}get isBase64url(){return!!this._def.checks.find(t=>t.kind==="base64url")}get minLength(){let t=null;for(const n of this._def.checks)n.kind==="min"&&(t===null||n.value>t)&&(t=n.value);return t}get maxLength(){let t=null;for(const n of this._def.checks)n.kind==="max"&&(t===null||n.valuenew _r({checks:[],typeName:ae.ZodString,coerce:(e==null?void 0:e.coerce)??!1,...ce(e)});function Bk(e,t){const n=(e.toString().split(".")[1]||"").length,r=(t.toString().split(".")[1]||"").length,i=n>r?n:r,s=Number.parseInt(e.toFixed(i).replace(".","")),a=Number.parseInt(t.toFixed(i).replace(".",""));return s%a/10**i}class ws extends ye{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(t){if(this._def.coerce&&(t.data=Number(t.data)),this._getType(t)!==q.number){const s=this._getOrReturnCtx(t);return U(s,{code:A.invalid_type,expected:q.number,received:s.parsedType}),se}let r;const i=new At;for(const s of this._def.checks)s.kind==="int"?xe.isInteger(t.data)||(r=this._getOrReturnCtx(t,r),U(r,{code:A.invalid_type,expected:"integer",received:"float",message:s.message}),i.dirty()):s.kind==="min"?(s.inclusive?t.datas.value:t.data>=s.value)&&(r=this._getOrReturnCtx(t,r),U(r,{code:A.too_big,maximum:s.value,type:"number",inclusive:s.inclusive,exact:!1,message:s.message}),i.dirty()):s.kind==="multipleOf"?Bk(t.data,s.value)!==0&&(r=this._getOrReturnCtx(t,r),U(r,{code:A.not_multiple_of,multipleOf:s.value,message:s.message}),i.dirty()):s.kind==="finite"?Number.isFinite(t.data)||(r=this._getOrReturnCtx(t,r),U(r,{code:A.not_finite,message:s.message}),i.dirty()):xe.assertNever(s);return{status:i.value,value:t.data}}gte(t,n){return this.setLimit("min",t,!0,X.toString(n))}gt(t,n){return this.setLimit("min",t,!1,X.toString(n))}lte(t,n){return this.setLimit("max",t,!0,X.toString(n))}lt(t,n){return this.setLimit("max",t,!1,X.toString(n))}setLimit(t,n,r,i){return new ws({...this._def,checks:[...this._def.checks,{kind:t,value:n,inclusive:r,message:X.toString(i)}]})}_addCheck(t){return new ws({...this._def,checks:[...this._def.checks,t]})}int(t){return this._addCheck({kind:"int",message:X.toString(t)})}positive(t){return this._addCheck({kind:"min",value:0,inclusive:!1,message:X.toString(t)})}negative(t){return this._addCheck({kind:"max",value:0,inclusive:!1,message:X.toString(t)})}nonpositive(t){return this._addCheck({kind:"max",value:0,inclusive:!0,message:X.toString(t)})}nonnegative(t){return this._addCheck({kind:"min",value:0,inclusive:!0,message:X.toString(t)})}multipleOf(t,n){return this._addCheck({kind:"multipleOf",value:t,message:X.toString(n)})}finite(t){return this._addCheck({kind:"finite",message:X.toString(t)})}safe(t){return this._addCheck({kind:"min",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:X.toString(t)})._addCheck({kind:"max",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:X.toString(t)})}get minValue(){let t=null;for(const n of this._def.checks)n.kind==="min"&&(t===null||n.value>t)&&(t=n.value);return t}get maxValue(){let t=null;for(const n of this._def.checks)n.kind==="max"&&(t===null||n.valuet.kind==="int"||t.kind==="multipleOf"&&xe.isInteger(t.value))}get isFinite(){let t=null,n=null;for(const r of this._def.checks){if(r.kind==="finite"||r.kind==="int"||r.kind==="multipleOf")return!0;r.kind==="min"?(n===null||r.value>n)&&(n=r.value):r.kind==="max"&&(t===null||r.valuenew ws({checks:[],typeName:ae.ZodNumber,coerce:(e==null?void 0:e.coerce)||!1,...ce(e)});class Ra extends ye{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(t){if(this._def.coerce)try{t.data=BigInt(t.data)}catch{return this._getInvalidInput(t)}if(this._getType(t)!==q.bigint)return this._getInvalidInput(t);let r;const i=new At;for(const s of this._def.checks)s.kind==="min"?(s.inclusive?t.datas.value:t.data>=s.value)&&(r=this._getOrReturnCtx(t,r),U(r,{code:A.too_big,type:"bigint",maximum:s.value,inclusive:s.inclusive,message:s.message}),i.dirty()):s.kind==="multipleOf"?t.data%s.value!==BigInt(0)&&(r=this._getOrReturnCtx(t,r),U(r,{code:A.not_multiple_of,multipleOf:s.value,message:s.message}),i.dirty()):xe.as)DELIM" -R"DELIM(sertNever(s);return{status:i.value,value:t.data}}_getInvalidInput(t){const n=this._getOrReturnCtx(t);return U(n,{code:A.invalid_type,expected:q.bigint,received:n.parsedType}),se}gte(t,n){return this.setLimit("min",t,!0,X.toString(n))}gt(t,n){return this.setLimit("min",t,!1,X.toString(n))}lte(t,n){return this.setLimit("max",t,!0,X.toString(n))}lt(t,n){return this.setLimit("max",t,!1,X.toString(n))}setLimit(t,n,r,i){return new Ra({...this._def,checks:[...this._def.checks,{kind:t,value:n,inclusive:r,message:X.toString(i)}]})}_addCheck(t){return new Ra({...this._def,checks:[...this._def.checks,t]})}positive(t){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!1,message:X.toString(t)})}negative(t){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!1,message:X.toString(t)})}nonpositive(t){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!0,message:X.toString(t)})}nonnegative(t){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!0,message:X.toString(t)})}multipleOf(t,n){return this._addCheck({kind:"multipleOf",value:t,message:X.toString(n)})}get minValue(){let t=null;for(const n of this._def.checks)n.kind==="min"&&(t===null||n.value>t)&&(t=n.value);return t}get maxValue(){let t=null;for(const n of this._def.checks)n.kind==="max"&&(t===null||n.valuenew Ra({checks:[],typeName:ae.ZodBigInt,coerce:(e==null?void 0:e.coerce)??!1,...ce(e)});class rd extends ye{_parse(t){if(this._def.coerce&&(t.data=!!t.data),this._getType(t)!==q.boolean){const r=this._getOrReturnCtx(t);return U(r,{code:A.invalid_type,expected:q.boolean,received:r.parsedType}),se}return Xt(t.data)}}rd.create=e=>new rd({typeName:ae.ZodBoolean,coerce:(e==null?void 0:e.coerce)||!1,...ce(e)});class _l extends ye{_parse(t){if(this._def.coerce&&(t.data=new Date(t.data)),this._getType(t)!==q.date){const s=this._getOrReturnCtx(t);return U(s,{code:A.invalid_type,expected:q.date,received:s.parsedType}),se}if(Number.isNaN(t.data.getTime())){const s=this._getOrReturnCtx(t);return U(s,{code:A.invalid_date}),se}const r=new At;let i;for(const s of this._def.checks)s.kind==="min"?t.data.getTime()s.value&&(i=this._getOrReturnCtx(t,i),U(i,{code:A.too_big,message:s.message,inclusive:!0,exact:!1,maximum:s.value,type:"date"}),r.dirty()):xe.assertNever(s);return{status:r.value,value:new Date(t.data.getTime())}}_addCheck(t){return new _l({...this._def,checks:[...this._def.checks,t]})}min(t,n){return this._addCheck({kind:"min",value:t.getTime(),message:X.toString(n)})}max(t,n){return this._addCheck({kind:"max",value:t.getTime(),message:X.toString(n)})}get minDate(){let t=null;for(const n of this._def.checks)n.kind==="min"&&(t===null||n.value>t)&&(t=n.value);return t!=null?new Date(t):null}get maxDate(){let t=null;for(const n of this._def.checks)n.kind==="max"&&(t===null||n.valuenew _l({checks:[],coerce:(e==null?void 0:e.coerce)||!1,typeName:ae.ZodDate,...ce(e)});class Tp extends ye{_parse(t){if(this._getType(t)!==q.symbol){const r=this._getOrReturnCtx(t);return U(r,{code:A.invalid_type,expected:q.symbol,received:r.parsedType}),se}return Xt(t.data)}}Tp.create=e=>new Tp({typeName:ae.ZodSymbol,...ce(e)});class bp extends ye{_parse(t){if(this._getType(t)!==q.undefined){const r=this._getOrReturnCtx(t);return U(r,{code:A.invalid_type,expected:q.undefined,received:r.parsedType}),se}return Xt(t.data)}}bp.create=e=>new bp({typeName:ae.ZodUndefined,...ce(e)});class Np extends ye{_parse(t){if(this._getType(t)!==q.null){const r=this._getOrReturnCtx(t);return U(r,{code:A.invalid_type,expected:q.null,received:r.parsedType}),se}return Xt(t.data)}}Np.create=e=>new Np({typeName:ae.ZodNull,...ce(e)});class Pp extends ye{constructor(){super(...arguments),this._any=!0}_parse(t){return Xt(t.data)}}Pp.create=e=>new Pp({typeName:ae.ZodAny,...ce(e)});class Rp extends ye{constructor(){super(...arguments),this._unknown=!0}_parse(t){return Xt(t.data)}}Rp.create=e=>new Rp({typeName:ae.ZodUnknown,...ce(e)});class $r extends ye{_parse(t){const n=this._getOrReturnCtx(t);return U(n,{code:A.invalid_type,expected:q.never,received:n.parsedType}),se}}$r.create=e=>new $r({typeName:ae.ZodNever,...ce(e)});class jp extends ye{_parse(t){if(this._getType(t)!==q.undefined){const r=this._getOrReturnCtx(t);return U(r,{code:A.invalid_type,expected:q.void,received:r.parsedType}),se}return Xt(t.data)}}jp.create=e=>new jp({typeName:ae.ZodVoid,...ce(e)});class bn extends ye{_parse(t){const{ctx:n,status:r}=this._processInputParams(t),i=this._def;if(n.parsedType!==q.array)return U(n,{code:A.invalid_type,expected:q.array,received:n.parsedType}),se;if(i.exactLength!==null){const a=n.data.length>i.exactLength.value,o=n.data.lengthi.maxLength.value&&(U(n,{code:A.too_big,maximum:i.maxLength.value,type:"array",inclusive:!0,exact:!1,message:i.maxLength.message}),r.dirty()),n.common.async)return Promise.all([...n.data].map((a,o)=>i.type._parseAsync(new Ir(n,a,n.path,o)))).then(a=>At.mergeArray(r,a));const s=[...n.data].map((a,o)=>i.type._parseSync(new Ir(n,a,n.path,o)));return At.mergeArray(r,s)}get element(){return this._def.type}min(t,n){return new bn({...this._def,minLength:{value:t,message:X.toString(n)}})}max(t,n){return new bn({...this._def,maxLength:{value:t,message:X.toString(n)}})}length(t,n){return new bn({...this._def,exactLength:{value:t,message:X.toString(n)}})}nonempty(t){return this.min(1,t)}}bn.create=(e,t)=>new bn({type:e,minLength:null,maxLength:null,exactLength:null,typeName:ae.ZodArray,...ce(t)});function Oi(e){if(e instanceof Ze){const t={};for(const n in e.shape){const r=e.shape[n];t[n]=Lr.create(Oi(r))}return new Ze({...e._def,shape:()=>t})}else return e instanceof bn?new bn({...e._def,type:Oi(e.element)}):e instanceof Lr?Lr.create(Oi(e.unwrap())):e instanceof Ss?Ss.create(Oi(e.unwrap())):e instanceof Si?Si.create(e.items.map(t=>Oi(t))):e}class Ze extends ye{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(this._cached!==null)return this._cached;const t=this._def.shape(),n=xe.objectKeys(t);return this._cached={shape:t,keys:n},this._cached}_parse(t){if(this._getType(t)!==q.object){const u=this._getOrReturnCtx(t);return U(u,{code:A.invalid_type,expected:q.object,received:u.parsedType}),se}const{status:r,ctx:i}=this._processInputParams(t),{shape:s,keys:a}=this._getCached(),o=[];if(!(this._def.catchall instanceof $r&&this._def.unknownKeys==="strip"))for(const u in i.data)a.includes(u)||o.push(u);const l=[];for(const u of a){const d=s[u],c=i.data[u];l.push({key:{status:"valid",value:u},value:d._parse(new Ir(i,c,i.path,u)),alwaysSet:u in i.data})}if(this._def.catchall instanceof $r){const u=this._def.unknownKeys;if(u==="passthrough")for(const d of o)l.push({key:{status:"valid",value:d},value:{status:"valid",value:i.data[d]}});else if(u==="strict")o.length>0&&(U(i,{code:A.unrecognized_keys,keys:o}),r.dirty());else if(u!=="strip")throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{const u=this._def.catchall;for(const d of o){const c=i.data[d];l.push({key:{status:"valid",value:d},value:u._parse(new Ir(i,c,i.path,d)),alwaysSet:d in i.data})}}return i.common.async?Promise.resolve().then(async()=>{const u=[];for(const d of l){const c=await d.key,f=await d.value;u.push({key:c,value:f,alwaysSet:d.alwaysSet})}return u}).then(u=>At.mergeObjectSync(r,u)):At.mergeObjectSync(r,l)}get shape(){return this._def.shape()}strict(t){return X.errToObj,new Ze({...this._def,unknownKeys:"strict",...t!==void 0?{errorMap:(n,r)=>{var s,a;const i=((a=(s=this._def).errorMap)==null?void 0:a.call(s,n,r).message)??r.defaultError;return n.code==="unrecognized_keys"?{message:X.errToObj(t).message??i}:{message:i}}}:{}})}strip(){return new Ze({...this._def,unknownKeys:"strip"})}passthrough(){return new Ze({...this._def,unknownKeys:"passthrough"})}extend(t){return new Ze({...this._def,shape:()=>({...this._def.shape(),...t})})}merge(t){return new Ze({unknownKeys:t._def.unknownKeys,catchall:t._def.catchall,shape:()=>({...this._def.shape(),...t._def.shape()}),typeName:ae.ZodObject})}setKey(t,n){return this.augment({[t]:n})}catchall(t){return new Ze({...this._def,catchall:t})}pick(t){const n={};for(const r of xe.objectKeys(t))t[r]&&this.shape[r]&&(n[r]=this.shape[r]);return new Ze({...this._def,shape:()=>n})}omit(t){const n={};for(const r of xe.objectKeys(this.shape))t[r]||(n[r]=this.shape[r]);return new Ze({...this._def,shape:()=>n})}deepPartial(){return Oi(this)}partial(t){const n={};for(const r of xe.objectKeys(this.shape)){const i=this.shape[r];t&&!t[r]?n[r]=i:n[r]=i.optional()}return new Ze({...this._def,shape:()=>n})}required(t){const n={};for(const r of xe.objectKeys(this.shape))if(t&&!t[r])n[r]=this.shape[r];else{let s=this.shape[r];for(;s instanceof Lr;)s=s._def.innerType;n[r]=s}return new Ze({...this._def,shape:()=>n})}keyof(){return ug(xe.objectKeys(this.shape))}}Ze.create=(e,t)=>new Ze({shape:()=>e,unknownKeys:"strip",catchall:$r.create(),typeName:ae.ZodObject,...ce(t)});Ze.strictCreate=(e,t)=>new Ze({shape:()=>e,unknownKeys:"strict",catchall:$r.create(),typeName:ae.ZodObject,...ce(t)});Ze.lazycreate=(e,t)=>new Ze({shape:e,unknownKeys:"strip",catchall:$r.create(),typeName:ae.ZodObject,...ce(t)});class Sl extends ye{_parse(t){const{ctx:n}=this._processInputParams(t),r=this._def.options;function i(s){for(const o of s)i)DELIM" -R"DELIM(f(o.result.status==="valid")return o.result;for(const o of s)if(o.result.status==="dirty")return n.common.issues.push(...o.ctx.common.issues),o.result;const a=s.map(o=>new Zn(o.ctx.common.issues));return U(n,{code:A.invalid_union,unionErrors:a}),se}if(n.common.async)return Promise.all(r.map(async s=>{const a={...n,common:{...n.common,issues:[]},parent:null};return{result:await s._parseAsync({data:n.data,path:n.path,parent:a}),ctx:a}})).then(i);{let s;const a=[];for(const l of r){const u={...n,common:{...n.common,issues:[]},parent:null},d=l._parseSync({data:n.data,path:n.path,parent:u});if(d.status==="valid")return d;d.status==="dirty"&&!s&&(s={result:d,ctx:u}),u.common.issues.length&&a.push(u.common.issues)}if(s)return n.common.issues.push(...s.ctx.common.issues),s.result;const o=a.map(l=>new Zn(l));return U(n,{code:A.invalid_union,unionErrors:o}),se}}get options(){return this._def.options}}Sl.create=(e,t)=>new Sl({options:e,typeName:ae.ZodUnion,...ce(t)});function id(e,t){const n=ur(e),r=ur(t);if(e===t)return{valid:!0,data:e};if(n===q.object&&r===q.object){const i=xe.objectKeys(t),s=xe.objectKeys(e).filter(o=>i.indexOf(o)!==-1),a={...e,...t};for(const o of s){const l=id(e[o],t[o]);if(!l.valid)return{valid:!1};a[o]=l.data}return{valid:!0,data:a}}else if(n===q.array&&r===q.array){if(e.length!==t.length)return{valid:!1};const i=[];for(let s=0;s{if(Sp(s)||Sp(a))return se;const o=id(s.value,a.value);return o.valid?((Ep(s)||Ep(a))&&n.dirty(),{status:n.value,value:o.data}):(U(r,{code:A.invalid_intersection_types}),se)};return r.common.async?Promise.all([this._def.left._parseAsync({data:r.data,path:r.path,parent:r}),this._def.right._parseAsync({data:r.data,path:r.path,parent:r})]).then(([s,a])=>i(s,a)):i(this._def.left._parseSync({data:r.data,path:r.path,parent:r}),this._def.right._parseSync({data:r.data,path:r.path,parent:r}))}}El.create=(e,t,n)=>new El({left:e,right:t,typeName:ae.ZodIntersection,...ce(n)});class Si extends ye{_parse(t){const{status:n,ctx:r}=this._processInputParams(t);if(r.parsedType!==q.array)return U(r,{code:A.invalid_type,expected:q.array,received:r.parsedType}),se;if(r.data.lengththis._def.items.length&&(U(r,{code:A.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),n.dirty());const s=[...r.data].map((a,o)=>{const l=this._def.items[o]||this._def.rest;return l?l._parse(new Ir(r,a,r.path,o)):null}).filter(a=>!!a);return r.common.async?Promise.all(s).then(a=>At.mergeArray(n,a)):At.mergeArray(n,s)}get items(){return this._def.items}rest(t){return new Si({...this._def,rest:t})}}Si.create=(e,t)=>{if(!Array.isArray(e))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new Si({items:e,typeName:ae.ZodTuple,rest:null,...ce(t)})};class Mp extends ye{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(t){const{status:n,ctx:r}=this._processInputParams(t);if(r.parsedType!==q.map)return U(r,{code:A.invalid_type,expected:q.map,received:r.parsedType}),se;const i=this._def.keyType,s=this._def.valueType,a=[...r.data.entries()].map(([o,l],u)=>({key:i._parse(new Ir(r,o,r.path,[u,"key"])),value:s._parse(new Ir(r,l,r.path,[u,"value"]))}));if(r.common.async){const o=new Map;return Promise.resolve().then(async()=>{for(const l of a){const u=await l.key,d=await l.value;if(u.status==="aborted"||d.status==="aborted")return se;(u.status==="dirty"||d.status==="dirty")&&n.dirty(),o.set(u.value,d.value)}return{status:n.value,value:o}})}else{const o=new Map;for(const l of a){const u=l.key,d=l.value;if(u.status==="aborted"||d.status==="aborted")return se;(u.status==="dirty"||d.status==="dirty")&&n.dirty(),o.set(u.value,d.value)}return{status:n.value,value:o}}}}Mp.create=(e,t,n)=>new Mp({valueType:t,keyType:e,typeName:ae.ZodMap,...ce(n)});class ja extends ye{_parse(t){const{status:n,ctx:r}=this._processInputParams(t);if(r.parsedType!==q.set)return U(r,{code:A.invalid_type,expected:q.set,received:r.parsedType}),se;const i=this._def;i.minSize!==null&&r.data.sizei.maxSize.value&&(U(r,{code:A.too_big,maximum:i.maxSize.value,type:"set",inclusive:!0,exact:!1,message:i.maxSize.message}),n.dirty());const s=this._def.valueType;function a(l){const u=new Set;for(const d of l){if(d.status==="aborted")return se;d.status==="dirty"&&n.dirty(),u.add(d.value)}return{status:n.value,value:u}}const o=[...r.data.values()].map((l,u)=>s._parse(new Ir(r,l,r.path,u)));retur)DELIM" -R"DELIM(n r.common.async?Promise.all(o).then(l=>a(l)):a(o)}min(t,n){return new ja({...this._def,minSize:{value:t,message:X.toString(n)}})}max(t,n){return new ja({...this._def,maxSize:{value:t,message:X.toString(n)}})}size(t,n){return this.min(t,n).max(t,n)}nonempty(t){return this.min(1,t)}}ja.create=(e,t)=>new ja({valueType:e,minSize:null,maxSize:null,typeName:ae.ZodSet,...ce(t)});class Lp extends ye{get schema(){return this._def.getter()}_parse(t){const{ctx:n}=this._processInputParams(t);return this._def.getter()._parse({data:n.data,path:n.path,parent:n})}}Lp.create=(e,t)=>new Lp({getter:e,typeName:ae.ZodLazy,...ce(t)});class Op extends ye{_parse(t){if(t.data!==this._def.value){const n=this._getOrReturnCtx(t);return U(n,{received:n.data,code:A.invalid_literal,expected:this._def.value}),se}return{status:"valid",value:t.data}}get value(){return this._def.value}}Op.create=(e,t)=>new Op({value:e,typeName:ae.ZodLiteral,...ce(t)});function ug(e,t){return new ks({values:e,typeName:ae.ZodEnum,...ce(t)})}class ks extends ye{_parse(t){if(typeof t.data!="string"){const n=this._getOrReturnCtx(t),r=this._def.values;return U(n,{expected:xe.joinValues(r),received:n.parsedType,code:A.invalid_type}),se}if(this._cache||(this._cache=new Set(this._def.values)),!this._cache.has(t.data)){const n=this._getOrReturnCtx(t),r=this._def.values;return U(n,{received:n.data,code:A.invalid_enum_value,options:r}),se}return Xt(t.data)}get options(){return this._def.values}get enum(){const t={};for(const n of this._def.values)t[n]=n;return t}get Values(){const t={};for(const n of this._def.values)t[n]=n;return t}get Enum(){const t={};for(const n of this._def.values)t[n]=n;return t}extract(t,n=this._def){return ks.create(t,{...this._def,...n})}exclude(t,n=this._def){return ks.create(this.options.filter(r=>!t.includes(r)),{...this._def,...n})}}ks.create=ug;class Dp extends ye{_parse(t){const n=xe.getValidEnumValues(this._def.values),r=this._getOrReturnCtx(t);if(r.parsedType!==q.string&&r.parsedType!==q.number){const i=xe.objectValues(n);return U(r,{expected:xe.joinValues(i),received:r.parsedType,code:A.invalid_type}),se}if(this._cache||(this._cache=new Set(xe.getValidEnumValues(this._def.values))),!this._cache.has(t.data)){const i=xe.objectValues(n);return U(r,{received:r.data,code:A.invalid_enum_value,options:i}),se}return Xt(t.data)}get enum(){return this._def.values}}Dp.create=(e,t)=>new Dp({values:e,typeName:ae.ZodNativeEnum,...ce(t)});class Cl extends ye{unwrap(){return this._def.type}_parse(t){const{ctx:n}=this._processInputParams(t);if(n.parsedType!==q.promise&&n.common.async===!1)return U(n,{code:A.invalid_type,expected:q.promise,received:n.parsedType}),se;const r=n.parsedType===q.promise?n.data:Promise.resolve(n.data);return Xt(r.then(i=>this._def.type.parseAsync(i,{path:n.path,errorMap:n.common.contextualErrorMap})))}}Cl.create=(e,t)=>new Cl({type:e,typeName:ae.ZodPromise,...ce(t)});class _s extends ye{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===ae.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(t){const{status:n,ctx:r}=this._processInputParams(t),i=this._def.effect||null,s={addIssue:a=>{U(r,a),a.fatal?n.abort():n.dirty()},get path(){return r.path}};if(s.addIssue=s.addIssue.bind(s),i.type==="preprocess"){const a=i.transform(r.data,s);if(r.common.async)return Promise.resolve(a).then(async o=>{if(n.value==="aborted")return se;const l=await this._def.schema._parseAsync({data:o,path:r.path,parent:r});return l.status==="aborted"?se:l.status==="dirty"||n.value==="dirty"?Js(l.value):l});{if(n.value==="aborted")return se;const o=this._def.schema._parseSync({data:a,path:r.path,parent:r});return o.status==="aborted"?se:o.status==="dirty"||n.value==="dirty"?Js(o.value):o}}if(i.type==="refinement"){const a=o=>{const l=i.refinement(o,s);if(r.common.async)return Promise.resolve(l);if(l instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return o};if(r.common.async===!1){const o=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});return o.status==="aborted"?se:(o.status==="dirty"&&n.dirty(),a(o.value),{status:n.value,value:o.value})}else return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(o=>o.status==="aborted"?se:(o.status==="dirty"&&n.dirty(),a(o.value).then(()=>({status:n.value,value:o.value}))))}if(i.type==="transform")if(r.common.async===!1){const a=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});if(!xs(a))return se;const o=i.transform(a.value,s);if(o instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:n.value,value:o}}else return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(a=>xs(a)?Promise.resolve(i.transform(a.value,s)).then(o=>({status:n.value,value:o})):se);xe.assertNever(i)}}_s.create=(e,t,n)=>new _s({schema:e,typeName:ae.ZodEffects,effe)DELIM" -R"DELIM(ct:t,...ce(n)});_s.createWithPreprocess=(e,t,n)=>new _s({schema:t,effect:{type:"preprocess",transform:e},typeName:ae.ZodEffects,...ce(n)});class Lr extends ye{_parse(t){return this._getType(t)===q.undefined?Xt(void 0):this._def.innerType._parse(t)}unwrap(){return this._def.innerType}}Lr.create=(e,t)=>new Lr({innerType:e,typeName:ae.ZodOptional,...ce(t)});class Ss extends ye{_parse(t){return this._getType(t)===q.null?Xt(null):this._def.innerType._parse(t)}unwrap(){return this._def.innerType}}Ss.create=(e,t)=>new Ss({innerType:e,typeName:ae.ZodNullable,...ce(t)});class sd extends ye{_parse(t){const{ctx:n}=this._processInputParams(t);let r=n.data;return n.parsedType===q.undefined&&(r=this._def.defaultValue()),this._def.innerType._parse({data:r,path:n.path,parent:n})}removeDefault(){return this._def.innerType}}sd.create=(e,t)=>new sd({innerType:e,typeName:ae.ZodDefault,defaultValue:typeof t.default=="function"?t.default:()=>t.default,...ce(t)});class ad extends ye{_parse(t){const{ctx:n}=this._processInputParams(t),r={...n,common:{...n.common,issues:[]}},i=this._def.innerType._parse({data:r.data,path:r.path,parent:{...r}});return kl(i)?i.then(s=>({status:"valid",value:s.status==="valid"?s.value:this._def.catchValue({get error(){return new Zn(r.common.issues)},input:r.data})})):{status:"valid",value:i.status==="valid"?i.value:this._def.catchValue({get error(){return new Zn(r.common.issues)},input:r.data})}}removeCatch(){return this._def.innerType}}ad.create=(e,t)=>new ad({innerType:e,typeName:ae.ZodCatch,catchValue:typeof t.catch=="function"?t.catch:()=>t.catch,...ce(t)});class Ap extends ye{_parse(t){if(this._getType(t)!==q.nan){const r=this._getOrReturnCtx(t);return U(r,{code:A.invalid_type,expected:q.nan,received:r.parsedType}),se}return{status:"valid",value:t.data}}}Ap.create=e=>new Ap({typeName:ae.ZodNaN,...ce(e)});class Uk extends ye{_parse(t){const{ctx:n}=this._processInputParams(t),r=n.data;return this._def.type._parse({data:r,path:n.path,parent:n})}unwrap(){return this._def.type}}class Sf extends ye{_parse(t){const{status:n,ctx:r}=this._processInputParams(t);if(r.common.async)return(async()=>{const s=await this._def.in._parseAsync({data:r.data,path:r.path,parent:r});return s.status==="aborted"?se:s.status==="dirty"?(n.dirty(),Js(s.value)):this._def.out._parseAsync({data:s.value,path:r.path,parent:r})})();{const i=this._def.in._parseSync({data:r.data,path:r.path,parent:r});return i.status==="aborted"?se:i.status==="dirty"?(n.dirty(),{status:"dirty",value:i.value}):this._def.out._parseSync({data:i.value,path:r.path,parent:r})}}static create(t,n){return new Sf({in:t,out:n,typeName:ae.ZodPipeline})}}class od extends ye{_parse(t){const n=this._def.innerType._parse(t),r=i=>(xs(i)&&(i.value=Object.freeze(i.value)),i);return kl(n)?n.then(i=>r(i)):r(n)}unwrap(){return this._def.innerType}}od.create=(e,t)=>new od({innerType:e,typeName:ae.ZodReadonly,...ce(t)});var ae;(function(e){e.ZodString="ZodString",e.ZodNumber="ZodNumber",e.ZodNaN="ZodNaN",e.ZodBigInt="ZodBigInt",e.ZodBoolean="ZodBoolean",e.ZodDate="ZodDate",e.ZodSymbol="ZodSymbol",e.ZodUndefined="ZodUndefined",e.ZodNull="ZodNull",e.ZodAny="ZodAny",e.ZodUnknown="ZodUnknown",e.ZodNever="ZodNever",e.ZodVoid="ZodVoid",e.ZodArray="ZodArray",e.ZodObject="ZodObject",e.ZodUnion="ZodUnion",e.ZodDiscriminatedUnion="ZodDiscriminatedUnion",e.ZodIntersection="ZodIntersection",e.ZodTuple="ZodTuple",e.ZodRecord="ZodRecord",e.ZodMap="ZodMap",e.ZodSet="ZodSet",e.ZodFunction="ZodFunction",e.ZodLazy="ZodLazy",e.ZodLiteral="ZodLiteral",e.ZodEnum="ZodEnum",e.ZodEffects="ZodEffects",e.ZodNativeEnum="ZodNativeEnum",e.ZodOptional="ZodOptional",e.ZodNullable="ZodNullable",e.ZodDefault="ZodDefault",e.ZodCatch="ZodCatch",e.ZodPromise="ZodPromise",e.ZodBranded="ZodBranded",e.ZodPipeline="ZodPipeline",e.ZodReadonly="ZodReadonly"})(ae||(ae={}));const Ke=_r.create,me=ws.create,Ef=rd.create;$r.create;const Ma=bn.create,Nn=Ze.create;Sl.create;El.create;Si.create;ks.create;Cl.create;Lr.create;Ss.create;class Cf extends Error{constructor(t,n){super(t),this.status=n}}const cg=Nn({id:Ke(),height:me(),timestamp:me().optional(),mediantime:me().optional(),tx_count:me().optional(),size:me().optional(),weight:me().optional(),difficulty:me().optional(),bits:me().optional(),merkle_root:Ke().optional(),previousblockhash:Ke().nullable().optional(),nonce:me().optional(),version:me().optional()}).passthrough(),Hk=Nn({confirmed:Ef(),block_height:me().optional(),block_hash:Ke().optional(),block_time:me().optional()}),Vk=Nn({txid:Ke().optional().nullable(),vout:me().optional().nullable(),sequence:me().optional(),scriptsig:Ke().optional().nullable(),scriptsig_asm:Ke().optional().nullable(),witness:Ma(Ke()).optional(),is_coinbase:Ef().optional(),prevout:Nn({scriptpubkey_type:Ke().optional(),scriptpubkey_address:Ke().optional(),scriptpubkey:Ke().optional(),scriptpubkey_asm:Ke().optional(),value:me().optional()}).partial().nullable().optional()}).passthrough(),Qk=Nn({scriptpubkey_type:Ke().optional(),scriptpubke)DELIM" -R"DELIM(y_address:Ke().optional(),scriptpubkey:Ke().optional(),scriptpubkey_asm:Ke().optional(),value:me(),n:me().optional()}).passthrough(),dg=Nn({txid:Ke(),hash:Ke().optional(),fee:me().optional(),version:me(),locktime:me(),vin:Ma(Vk),vout:Ma(Qk),size:me(),weight:me(),status:Hk}).passthrough(),Qo=Ma(cg),Tl=Ma(dg),Wk=Nn({in_best_chain:Ef(),next_best:Ke().optional(),height:me().optional()}),Zk=Nn({address:Ke(),chain_stats:Nn({funded_txo_count:me().optional(),funded_txo_sum:me().optional(),spent_txo_count:me().optional(),spent_txo_sum:me().optional(),tx_count:me().optional()}),mempool_stats:Nn({funded_txo_count:me().optional(),funded_txo_sum:me().optional(),spent_txo_count:me().optional(),spent_txo_sum:me().optional(),tx_count:me().optional()})});let Co=null,Ct=null;async function Ht(e,t,n){const r=t.startsWith("/")?t:`/${t}`,i=await fetch(`${e}${r}`);if(!i.ok)throw new Cf(`Request to ${t} failed with status ${i.status}`,i.status);const s=await i.json();return n.parse(s)}async function Ip(e){const t=await fetch(`${e}/blocks/tip/height`);if(!t.ok)throw new Cf("Unable to fetch tip height",t.status);const n=Number((await t.text()).trim());if(!Number.isFinite(n))throw new Error("Invalid tip height response");return Ct=n,n}async function Bs(e,t){if(Ct===null){await Ip(e);return}typeof t=="number"&&Cts.scriptpubkey_address===e?i+s.value:i,0),r=t.vin.reduce((i,s)=>{const a=s.prevout;return(a==null?void 0:a.scriptpubkey_address)===e&&typeof a.value=="number"?i+a.value:i},0);return n-r}async function $p(e,t){if(t==="latest"||t==="tip"){const[n]=await Ht(e,"/blocks",Qo);if(!n)throw new Error("No blocks returned from API");return n.id}if(/^\d+$/.test(t)){const n=await fetch(`${e}/block-height/${t}`);if(!n.ok)throw new Cf("Unable to resolve block height",n.status);return(await n.text()).trim()}return t}async function Gk(e,t){if(Ct==null||t>=Ct)return null;const n=t+1;try{const r=await fetch(`${e}/block-height/${n}`);return r.ok&&(await r.text()).trim()||null}catch(r){return console.warn(`Failed to resolve hash for height ${n}`,r),null}}function To(e){return{hash:e.id,height:e.height,timestamp:e.timestamp??e.mediantime??0,txCount:e.tx_count??0,size:e.size??0,weight:e.weight??0,difficulty:e.difficulty,previousBlockHash:e.previousblockhash??null}}function Fu(e){const t=e.status.confirmed?"confirmed":"unconfirmed",n=e.vin.map((o,l)=>{var d,c,f;const u=o.is_coinbase||o.txid==null?void 0:typeof o.vout=="number"&&o.vout!==4294967295?o.vout:void 0;return{index:l,prevTxId:o.txid??void 0,prevOutputIndex:u,address:((d=o.prevout)==null?void 0:d.scriptpubkey_address)??void 0,valueSats:(c=o.prevout)==null?void 0:c.value,scriptType:(f=o.prevout)==null?void 0:f.scriptpubkey_type,isCoinbase:o.is_coinbase??!1,script:o.scriptsig||o.scriptsig_asm?{hex:o.scriptsig??void 0,mnemonic:o.scriptsig_asm??void 0}:void 0,witness:o.witness??void 0,sequence:o.sequence}}),r=e.vout.map((o,l)=>({index:l,address:o.scriptpubkey_address??void 0,valueSats:o.value,scriptType:o.scriptpubkey_type,spent:void 0,script:o.scriptpubkey||o.scriptpubkey_asm?{hex:o.scriptpubkey??void 0,mnemonic:o.scriptpubkey_asm??void 0}:void 0})),i=r.reduce((o,l)=>o+l.valueSats,0),s=Ct;let a=0;return e.status.block_height&&t==="confirmed"&&(s&&s>=e.status.block_height?a=s-e.status.block_height+1:a=1),{txid:e.txid,hash:e.hash??e.txid,status:t,confirmations:a,blockHeight:e.status.block_height,blockHash:e.status.block_hash,receivedTime:e.status.block_time,feeSats:e.fee??null,feeRate:e.fee?e.fee/e.weight*4:null,size:e.size,weight:e.weight,version:e.version,locktime:e.locktime,totalInput:null,totalOutput:i,inputs:n,outputs:r}}function qk(e,t){const n=e.chain_stats,r=e.mempool_stats,i=(n.funded_txo_sum??0)-(n.spent_txo_sum??0),s=fg(e.address,t.slice(0,25));return{address:e.address,addressType:Xl(e.address),balance:i,totalReceived:(n.funded_txo_sum??0)+(r.funded_txo_sum??0),totalSent:(n.spent_txo_sum??0)+(r.spent_txo_sum??0),txCount:(n.tx_count??0)+(r.tx_count??0),utxoCount:(n.funded_txo_count??0)-(n.spent_txo_count??0),transactions:s}}function fg(e,t){return t.map(n=>({txid:n.txid,timestamp:n.status.block_time,valueChange:Kk(e,n),blockHeight:n.status.block_height}))}async function Fp(e,t,n=0){const r=n>0?`/block/${t}/txs/${n}`:`/block/${t}/txs`;return Ht(e,r,Tl)}async function Yk(e,t,n){return n?Ht(e,`/address/${t}/txs/chain/${n}`,Tl):Ht(e,`/address/${t}/txs`,Tl)}function hg({baseUrl:e}){const t=new Set;let n=null;async function r(){n||t.size===0||(n=setInterval(async()=>{try{const[s]=await Ht(e,"/blocks",Qo);if(!s)return;if(s.id!==Co){Co=s.id,Ct=Math.max(s.height,Ct??0);const a=To(s);t.forEach(o=>o({type:"block.new",payload:a}))}}catch(s){console.warn("Block polling failed",s)}},15e3))}function i(){n&&(clearInterval(n),n=null)}return{metadata:{id:"blockstream",label:"Blockstream.info",type:"external",connectionState:"connected",supportsWebsocket:!1,description:"Read-only adapter using Blockstream public REST API."},async getLatestBlock)DELIM" -R"DELIM(s(s=10){const a=await Ht(e,"/blocks",Qo);return a.length>0&&(Co=a[0].id,Ct=Math.max(a[0].height,Ct??0)),a.slice(0,s).map(To)},async getBlocksBefore(s,a=10){const o=Math.max(s,0),l=await Ht(e,`/blocks/${o}`,Qo);return l.length>0&&(Ct=Math.max(l[0].height,Ct??0)),l.slice(0,a).map(To)},async getBlock(s){const a=await $p(e,s);await Bs(e);const o=await Ht(e,`/block/${a}`,cg),[l,u]=await Promise.allSettled([Fp(e,a),Ht(e,`/block/${a}/status`,Wk)]);l.status==="rejected"&&console.warn("Failed to load block transactions, defaulting to empty set",l.reason),u.status==="rejected"&&console.warn("Failed to load block status, defaulting to unknown",u.reason);const d=To(o);Co=d.hash,Ct=Math.max(d.height,Ct??d.height);const c=l.status==="fulfilled"?l.value:[],f=u.status==="fulfilled"?u.value:null;let m=(f==null?void 0:f.next_best)??null;return m||(m=await Gk(e,d.height)),{...d,merkleRoot:o.merkle_root,nonce:o.nonce,bits:o.bits,version:o.version,nextBlockHash:m,transactions:c.map(Fu)}},async getBlockTransactions(s,a=0){const o=await $p(e,s),l=await Fp(e,o,a),u=l.reduce((d,c)=>c.status.block_height?d===null?c.status.block_height:Math.max(d,c.status.block_height):d,null);return typeof u=="number"?await Bs(e,u):await Bs(e),l.map(Fu)},async getTransaction(s){const a=await Ht(e,`/tx/${s}`,dg);return a.status.block_height?await Bs(e,a.status.block_height):await Bs(e),Fu(a)},async getAddress(s){const a=await Ht(e,`/address/${s}`,Zk),o=await Ht(e,`/address/${s}/txs`,Tl);return qk(a,o)},async getAddressTransactions(s,a){const o=await Yk(e,s,a);return fg(s,o)},subscribeToEvents(s){return t.add(s),r(),()=>{t.delete(s),t.size===0&&i()}}}}function Xk({baseUrl:e}){const t=hg({baseUrl:e});return{...t,metadata:{...t.metadata,id:"mempool",label:"mempool.space",description:"Adapter backed by mempool.space REST API."}}}/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */function La(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array"}function pg(e,t){return Array.isArray(t)?t.length===0?!0:e?t.every(n=>typeof n=="string"):t.every(n=>Number.isSafeInteger(n)):!1}function mg(e){if(typeof e!="function")throw new Error("function expected");return!0}function Oa(e,t){if(typeof t!="string")throw new Error(`${e}: string expected`);return!0}function Ga(e){if(!Number.isSafeInteger(e))throw new Error(`invalid integer: ${e}`)}function bl(e){if(!Array.isArray(e))throw new Error("array expected")}function vg(e,t){if(!pg(!0,t))throw new Error(`${e}: array of strings expected`)}function Tf(e,t){if(!pg(!1,t))throw new Error(`${e}: array of numbers expected`)}function bf(...e){const t=s=>s,n=(s,a)=>o=>s(a(o)),r=e.map(s=>s.encode).reduceRight(n,t),i=e.map(s=>s.decode).reduce(n,t);return{encode:r,decode:i}}function yg(e){const t=typeof e=="string"?e.split(""):e,n=t.length;vg("alphabet",t);const r=new Map(t.map((i,s)=>[i,s]));return{encode:i=>(bl(i),i.map(s=>{if(!Number.isSafeInteger(s)||s<0||s>=n)throw new Error(`alphabet.encode: digit index outside alphabet "${s}". Allowed: ${e}`);return t[s]})),decode:i=>(bl(i),i.map(s=>{Oa("alphabet.decode",s);const a=r.get(s);if(a===void 0)throw new Error(`Unknown letter: "${s}". Allowed: ${e}`);return a}))}}function gg(e=""){return Oa("join",e),{encode:t=>(vg("join.decode",t),t.join(e)),decode:t=>(Oa("join.decode",t),t.split(e))}}function zp(e,t,n){if(t<2)throw new Error(`convertRadix: invalid from=${t}, base cannot be less than 2`);if(n<2)throw new Error(`convertRadix: invalid to=${n}, base cannot be less than 2`);if(bl(e),!e.length)return[];let r=0;const i=[],s=Array.from(e,o=>{if(Ga(o),o<0||o>=t)throw new Error(`invalid integer: ${o}`);return o}),a=s.length;for(;;){let o=0,l=!0;for(let u=r;ut===0?e:xg(t,e%t),Nl=(e,t)=>e+(t-xg(e,t)),Wo=(()=>{let e=[];for(let t=0;t<40;t++)e.push(2**t);return e})();function ld(e,t,n,r){if(bl(e),t<=0||t>32)throw new Error(`convertRadix2: wrong from=${t}`);if(n<=0||n>32)throw new Error(`convertRadix2: wrong to=${n}`);if(Nl(t,n)>32)throw new Error(`convertRadix2: carry overflow from=${t} to=${n} carryBits=${Nl(t,n)}`);let i=0,s=0;const a=Wo[t],o=Wo[n]-1,l=[];for(const u of e){if(Ga(u),u>=a)throw new Error(`convertRadix2: invalid data word=${u} from=${t}`);if(i=i<32)throw new Error(`convertRadix2: carry overflow pos=${s} from=${t}`);for(s+=t;s>=n;s-=n)l.push((i>>s-n&o)>>>0);const d=Wo[s];if(d===void 0)throw new Error("invalid carry");i&=d-1}if(i=i<=t)throw new Error("Excess padding");if(!r&&i>0)throw new Error(`Non-zero padding: ${i}`);return r&&s>0&&l.push(i>>>0),l}function Jk(e){Ga(e);const t=2**8;return{encode:n=>{if(!La(n))throw )DELIM" -R"DELIM(new Error("radix.encode input should be Uint8Array");return zp(Array.from(n),t,e)},decode:n=>(Tf("radix.decode",n),Uint8Array.from(zp(n,e,t)))}}function e_(e,t=!1){if(Ga(e),e<=0||e>32)throw new Error("radix2: bits should be in (0..32]");if(Nl(8,e)>32||Nl(e,8)>32)throw new Error("radix2: carry overflow");return{encode:n=>{if(!La(n))throw new Error("radix2.encode input should be Uint8Array");return ld(Array.from(n),8,e,!t)},decode:n=>(Tf("radix2.decode",n),Uint8Array.from(ld(n,e,8,t)))}}function Bp(e){return mg(e),function(...t){try{return e.apply(null,t)}catch{}}}function t_(e,t){return Ga(e),mg(t),{encode(n){if(!La(n))throw new Error("checksum.encode: input should be Uint8Array");const r=t(n).slice(0,e),i=new Uint8Array(n.length+e);return i.set(n),i.set(r,n.length),i},decode(n){if(!La(n))throw new Error("checksum.decode: input should be Uint8Array");const r=n.slice(0,-e),i=n.slice(-e),s=t(r).slice(0,e);for(let a=0;abf(Jk(58),yg(e),gg("")),r_=n_("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"),i_=e=>bf(t_(4,t=>e(e(t))),r_),s_=i_,ud=bf(yg("qpzry9x8gf2tvdw0s3jn54khce6mua7l"),gg("")),Up=[996825010,642813549,513874426,1027748829,705979059];function Us(e){const t=e>>25;let n=(e&33554431)<<5;for(let r=0;r>r&1)===1&&(n^=Up[r]);return n}function Hp(e,t,n=1){const r=e.length;let i=1;for(let s=0;s126)throw new Error(`Invalid prefix (${e})`);i=Us(i)^a>>5}i=Us(i);for(let s=0;sm)throw new TypeError(`Length ${x} exceeds limit ${m}`);const T=c.toLowerCase(),v=Hp(T,f,t);return`${T}1${ud.encode(f)}${v}`}function o(c,f=90){Oa("bech32.decode input",c);const m=c.length;if(m<8||f!==!1&&m>f)throw new TypeError(`invalid string length: ${m} (${c}). Expected (8..${f})`);const g=c.toLowerCase();if(c!==g&&c!==c.toUpperCase())throw new Error("String must be lowercase or uppercase");const x=g.lastIndexOf("1");if(x===0||x===-1)throw new Error('Letter "1" must be present between prefix and data only');const T=g.slice(0,x),v=g.slice(x+1);if(v.length<6)throw new Error("Data must be at least 6 characters long");const p=ud.decode(v).slice(0,-6),y=Hp(T,p,t);if(!v.endsWith(y))throw new Error(`Invalid checksum in ${c}: expected "${y}"`);return{prefix:T,words:p}}const l=Bp(o);function u(c){const{prefix:f,words:m}=o(c,!1);return{prefix:f,words:m,bytes:r(m)}}function d(c,f){return a(c,i(f))}return{encode:a,decode:o,encodeFromBytes:d,decodeToBytes:u,decodeUnsafe:l,fromWords:r,fromWordsUnsafe:s,toWords:i}}const cd=wg("bech32"),kg=wg("bech32m");/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */function a_(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array"}function Jl(e,...t){if(!a_(e))throw new Error("Uint8Array expected");if(t.length>0&&!t.includes(e.length))throw new Error("Uint8Array expected of length "+t+", got length="+e.length)}function Vp(e,t=!0){if(e.destroyed)throw new Error("Hash instance has been destroyed");if(t&&e.finished)throw new Error("Hash#digest() has already been called")}function o_(e,t){Jl(e);const n=t.outputLen;if(e.length>>t}const l_=typeof Uint8Array.from([]).toHex=="function"&&typeof Uint8Array.fromHex=="function",u_=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,"0"));function fd(e){if(Jl(e),l_)return e.toHex();let t="";for(let n=0;ne().update(_g(r)).digest(),n=e();return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=()=>e(),t}function h_(e,t,n,r){if(typeof e.setBigUint64=="function")return e.setBigUint64(t,n,r);const i=BigInt(32),s=BigInt(4294967295),a=Number(n>>i&s),o=Number(n&s),l=r?4:0,u=r?0:4;e.setUint32(t+l,a,r),e.setUint32(t+u,o,r)}function p_(e,t,n){return e&t^~e&n}function m_(e,t,n){return e&t^e&n^t&n}class v_ extends d_{constructor(t,n,r,i){super(),this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.blockLen=t,this.outputLen=n,this.padOffset=r,this.isLE=i,this.buffer=new Uint8Array(t),this.view=zu(this.buffer)}update(t){Vp(this),t=_g(t),Jl(t);const{view:n,buffer:r,bl)DELIM" -R"DELIM(ockLen:i}=this,s=t.length;for(let a=0;ai-a&&(this.process(r,0),a=0);for(let c=a;cd.length)throw new Error("_sha2: outputLen bigger than state");for(let c=0;c>>3,x=yn(m,17)^yn(m,19)^m>>>10;nr[c]=x+nr[c-7]+g+nr[c-16]|0}let{A:r,B:i,C:s,D:a,E:o,F:l,G:u,H:d}=this;for(let c=0;c<64;c++){const f=yn(o,6)^yn(o,11)^yn(o,25),m=d+f+p_(o,l,u)+y_[c]+nr[c]|0,x=(yn(r,2)^yn(r,13)^yn(r,22))+m_(r,i,s)|0;d=u,u=l,l=o,o=a+m|0,a=s,s=i,i=r,r=m+x|0}r=r+this.A|0,i=i+this.B|0,s=s+this.C|0,a=a+this.D|0,o=o+this.E|0,l=l+this.F|0,u=u+this.G|0,d=d+this.H|0,this.set(r,i,s,a,o,l,u,d)}roundClean(){dd(nr)}destroy(){this.set(0,0,0,0,0,0,0,0),dd(this.buffer)}}const x_=f_(()=>new g_),Sg=x_,Eg=s_(Sg),w_=BigInt("0x00000000FFFF0000000000000000000000000000000000000000000000000000"),Qp=5e3,Bu=new Map;async function Zt(e,t,n){const r=t.startsWith("/")?t:`/${t}`;let i=r.includes("?")?`${e}${r}`:`${e}${r}?format=json`;n&&(i+=i.includes("?")?"&turbo=true":"?turbo=true");const s=await fetch(i);if(!s.ok){const o=await s.text().catch(()=>"");throw new Error(`Request failed ${s.status}: ${o||s.statusText}`)}const a=await s.text();try{return JSON.parse(a)}catch{return a}}async function bo(e,t,n){try{return await Zt(e,t,n)}catch{return null}}function Nf(e){if(e.length%2!==0)throw new Error("Invalid hex string");const t=new Uint8Array(e.length/2);for(let n=0;n>>24,n=e&8388607;if(n===0||t<3)return;const r=BigInt(n)*(1n<n[1]).filter(Boolean);return t.length?t:[e]}function Pf(e){const t=e.toLowerCase();if(t.includes("dup hash160")&&t.includes("equalverify checksig"))return"p2pkh";if(t.startsWith("hash160")&&t.includes("equal"))return"p2sh";if(t.startsWith("zero [")){const n=t.match(/zero \[([0-9a-f]+)\]/),r=(n==null?void 0:n[1])??"";return r.length===40?"p2wpkh":r.length===64?"p2wsh":"segwit"}if(t.startsWith("1 ["))return"p2tr"}function S_(){return"mainnet"}function E_(e){return e==="testnet"||e==="regtest"?{p2pkh:111,p2sh:196,hrp:e==="regtest"?"bcrt":"tb"}:{p2pkh:0,p2sh:5,hrp:"bc"}}function Wp(e,t){const n=new Uint8Array(t.length/2+1);return n[0]=e,n.set(Nf(t),1),Eg.encode(n)}function Uu(e,t,n){const r=Nf(n),i=cd.toWords(r);return i.unshift(t),(t===0?cd:kg).encode(e,i)}function Cg(e,t){const n=e.toLowerCase(),r=n.match(/dup hash160 \[([0-9a-f]{40})\] equalverify checksig/);if(r)return{address:Wp(t.p2pkh,r[1]),type:"p2pkh"};const i=n.match(/^hash160 \[([0-9a-f]{40})\] equal$/);if(i)return{addr)DELIM" -R"DELIM(ess:Wp(t.p2sh,i[1]),type:"p2sh"};const s=n.match(/^zero \[([0-9a-f]{40})\]/);if(s)return{address:Uu(t.hrp,0,s[1]),type:"p2wpkh"};const a=n.match(/^zero \[([0-9a-f]{64})\]/);if(a)return{address:Uu(t.hrp,0,a[1]),type:"p2wsh"};const o=n.match(/^1 \[([0-9a-f]{64})\]/);return o?{address:Uu(t.hrp,1,o[1]),type:"p2tr"}:{type:Pf(e)}}function C_(e,t){try{const i=Eg.decode(e),s=i[0],a=fd(i.slice(1));if(a.length===40&&s===t.p2pkh)return{scriptHex:`76a914${a}88ac`,type:"p2pkh"};if(a.length===40&&s===t.p2sh)return{scriptHex:`a914${a}87`,type:"p2sh"}}catch{}const n=e.toLowerCase();if(n.indexOf("1")>0){const i=n,s=i.startsWith(`${t.hrp}1`)?cd:kg;try{const{prefix:a,words:o}=s.decode(i,90);if(a!==t.hrp)throw new Error("wrong hrp");const l=o[0],u=s.fromWords(o.slice(1)),d=fd(u),c=u.length.toString(16).padStart(2,"0");if(l===0&&(u.length===20||u.length===32))return{scriptHex:`00${c}${d}`,type:u.length===20?"p2wpkh":"p2wsh"};if(l===1&&u.length===32)return{scriptHex:`51${c}${d}`,type:"p2tr"}}catch{}}return null}function Zp(e,t){const n=e.toLowerCase();if(/^[0-9a-f]{64}$/.test(n))return{hash:n,type:"script-hash"};const r=C_(e,t);if(!r)return null;const i=Sg(Nf(r.scriptHex)),s=Uint8Array.from(i).reverse();return{hash:fd(s),type:r.type}}async function hd(e,t){const n=await Zt(e,"/v1/top?format=json",t),r=typeof n=="number"?n:Number(n);if(!Number.isFinite(r))throw new Error("Invalid top height");return r}async function Zo(e,t){const n=Date.now(),r=`${e}|${t?"1":"0"}`,i=Bu.get(r);if(i&&i.expiresAt>n)return i.promise;const s=hd(e,t).then(a=>(Bu.set(r,{promise:s,value:a,expiresAt:Date.now()+Qp}),a));return Bu.set(r,{promise:s,value:(i==null?void 0:i.value)??null,expiresAt:n+Qp}),s}async function Kp(e,t,n){if(t==="latest"||t==="tip"){const r=await Zo(e,n);return{path:`/v1/block/height/${r}`,height:r}}return/^\d+$/u.test(t)?{path:`/v1/block/height/${t}`}:{path:`/v1/block/hash/${t}`}}function No(e,t){return{hash:e.hash,height:e.height,timestamp:e.timestamp,txCount:t,size:void 0,weight:void 0,difficulty:k_(e.bits),previousBlockHash:e.previous??null}}function T_(e){const t=__(e);return t&&t.length>0?t:void 0}function b_(e,t,n,r){const i=/^0{64}$/u.test(e.point.hash),s=t?Cg(t.script,n):{address:void 0,type:void 0};return{index:r,prevTxId:i?void 0:e.point.hash,prevOutputIndex:i?void 0:e.point.index,address:s.address,valueSats:t==null?void 0:t.value,scriptType:s.type??Pf((t==null?void 0:t.script)??e.script),isCoinbase:i,script:e.script?{mnemonic:e.script}:void 0,witness:T_(e.witness),sequence:e.sequence}}function N_(e,t,n){const r=Cg(e.script,n);return{index:t,address:r.address,valueSats:e.value,scriptType:r.type??Pf(e.script),spent:void 0,script:e.script?{mnemonic:e.script}:void 0}}function P_(e,t){return typeof e!="number"||typeof t!="number"?0:Math.max(0,e-t+1)}async function R_(e,t){const n=new Map;return(r,i)=>{const s=`${r}:${i}`;if(n.has(s))return n.get(s);const a=Zt(e,`/v1/output/${r}/${i}?format=json`,t).catch(()=>{});return n.set(s,a),a}}async function Hu(e,t,n,r,i){const s=r?await Promise.all(e.inputs.map(c=>/^0{64}$/u.test(c.point.hash)?Promise.resolve(void 0):r(c.point.hash,c.point.index).catch(()=>{}))):[],a=e.outputs.map((c,f)=>N_(c,f,t)),o=a.reduce((c,f)=>c+f.valueSats,0),l=s.reduce((c,f)=>(f==null?void 0:f.value)===void 0?c:(c??0)+f.value,null),u=P_(n.tipHeight,n.blockHeight),d=typeof n.blockHeight=="number"?"confirmed":"unconfirmed";return{txid:e.hash,hash:e.hash,status:d,confirmations:u,blockHeight:n.blockHeight,blockHash:n.blockHash,receivedTime:n.timestamp,feeSats:Number.isFinite(i)?i:void 0,feeRate:null,size:void 0,weight:void 0,version:e.version,locktime:e.locktime,totalInput:l,totalOutput:o,inputs:e.inputs.map((c,f)=>b_(c,s[f],t,f)),outputs:a}}async function j_(e,t,n){return Zt(e,`${t}/txs?format=json`,n)}async function M_(e,t,n,r){return Zt(e,`${t}/tx/${n}?format=json`,r)}async function L_(e,t,n){return await Zt(e,`${t}?format=json`,n)}async function Gp(e,t,n){return await Zt(e,`/v1/address/${t}?format=json`,n).catch(()=>[])}function O_({baseUrl:e,pollIntervalMs:t=15e3,turbo:n=!1}){const r=new Set;let i=null,s=null;const a=E_(S_());async function o(){i||(i=setInterval(async()=>{try{const u=await hd(e,n);if(s===null){s=u;return}if(u>s){const d=await Zt(e,`/v1/block/height/${u}/header?format=json`,n),c=No(d);r.forEach(f=>f({type:"block.new",payload:c})),s=u}}catch(u){console.warn("Libbitcoin poller failed",u)}},t))}function l(){i&&(clearInterval(i),i=null)}return{metadata:{id:"libbitcoin",label:"Server",type:"local",connectionState:"connected",supportsWebsocket:!1,description:"Adapter for the libbitcoin embedded REST API."},async getLatestBlocks(u=10){const d=await Zo(e,n);s=d;const c=Array.from({length:u},(m,g)=>d-g).filter(m=>m>=0);return(await Promise.all(c.map(m=>Zt(e,`/v1/block/height/${m}/header?format=json`,n)))).map(m=>No(m))},async getBlocksBefore(u,d=10){const c=Array.from({length:d},(m,g)=>u-g).filter(m=>m>=0);return(await Promise.all(c.map(m=>Zt(e,`/v1/block/height/${m}/header?format=json`,n)))).map(m=>No(m)))DELIM" -R"DELIM(},async getBlock(u){var v;const{path:d}=await Kp(e,u,n),[c,f]=await Promise.all([L_(e,d,n),Zo(e,n)]);s=Math.max(s??0,f);let m=null;if(c.header.heightHu(p,a,{blockHeight:c.header.height,blockHash:c.header.hash,tipHeight:f,timestamp:c.header.timestamp})));return{...x,merkleRoot:c.header.merkle_root,nonce:c.header.nonce,bits:c.header.bits,version:c.header.version,nextBlockHash:m,transactions:T}},async getBlockTransactions(u,d=0){const{path:c}=await Kp(e,u,n),f=await Zt(e,`${c}/header?format=json`,n),[m,g]=await Promise.all([j_(e,c,n),Zo(e,n)]);s=Math.max(s??0,g);const x=m.slice(d);return await Promise.all(x.map((v,p)=>M_(e,c,d+p,n).then(y=>Hu(y,a,{blockHeight:f.height,blockHash:f.hash,tipHeight:g,timestamp:f.timestamp}))))},async getTransaction(u){const d=await Zt(e,`/v1/tx/${u}?format=json`,n),[c,f,m]=await Promise.all([bo(e,`/v1/tx/${u}/header?format=json`,n),hd(e,n).catch(()=>null),bo(e,`/v1/tx/${u}/fee?format=json`,n)]),g=await R_(e,n),x=typeof m=="number"?m:Number(m);return Hu(d,a,{blockHeight:c==null?void 0:c.height,blockHash:c==null?void 0:c.hash,tipHeight:typeof f=="number"?f:void 0,timestamp:c==null?void 0:c.timestamp},g,Number.isFinite(x)?x:void 0)},async getAddress(u){const d=Zp(u,a);if(!d)throw new Error("Unsupported address format for libbitcoin adapter");const[c,f]=await Promise.all([bo(e,`/v1/address/${d.hash}/balance?format=json`,n),Gp(e,d.hash,n)]),m=typeof c=="number"?c:Number(c);let g=0;for(const v of f)g+=v.value??0;const x=Number.isFinite(m)?m:g,T=f.map(v=>({txid:v.point.hash,blockHeight:void 0,timestamp:void 0,valueChange:v.value}));return{address:u,addressType:d.type,balance:x,totalReceived:void 0,totalSent:void 0,txCount:void 0,utxoCount:void 0,transactions:T}},async getAddressTransactions(u){const d=Zp(u,a);return d?(await Gp(e,d.hash,n)).map(f=>({txid:f.point.hash,blockHeight:void 0,timestamp:void 0,valueChange:f.value})):[]},subscribeToEvents(u){return r.add(u),o(),()=>{r.delete(u),r.size===0&&l()}}}}const Tg=C.createContext(null),bg=C.createContext(null),Ng=C.createContext(null);function D_({children:e,client:t}){return h.jsx(Tg.Provider,{value:t,children:e})}function A_({children:e,provider:t,setProvider:n,availableProviders:r}){return h.jsx(bg.Provider,{value:{provider:t,setProvider:n,availableProviders:r},children:e})}function I_({children:e,turbo:t,setTurbo:n}){return h.jsx(Ng.Provider,{value:{turbo:t,setTurbo:n},children:e})}function qn(){const e=C.useContext(Tg);if(!e)throw new Error("BlockchainClientProvider missing in component tree");return e}function $_(){return qn().metadata}function F_(){const e=C.useContext(bg);if(!e)throw new Error("ProviderSwitcherProvider missing in component tree");return e}function z_(){const e=C.useContext(Ng);if(!e)throw new Error("TurboProvider missing in component tree");return e}function B_(){var e;return typeof window<"u"&&((e=window.location)!=null&&e.origin)?window.location.origin:"http://localhost:8080"}function U_(e){return Pg("libbitcoin",e)}function Pg(e,t){if(e==="blockstream")return hg({baseUrl:"https://blockstream.info/api"});if(e==="mempool")return Xk({baseUrl:"https://mempool.space/api"});if(e==="libbitcoin"){const n=B_();return O_({baseUrl:n,turbo:t==null?void 0:t.turboEnabled})}return e==="external"?(console.warn("No external API URL provided, falling back to mock client"),Iu()):(e==="mock"||console.warn(`Unknown provider "${e}", falling back to mock`),Iu())}function H_(){const e=new Set;return e.add("libbitcoin"),e.add("blockstream"),e.add("mempool"),Array.from(e)}const qp=new _w({defaultOptions:{queries:{staleTime:3e4,refetchOnWindowFocus:!1,retry:1}}});function V_({children:e}){const t="libbitcoin",[n,r]=C.useState(()=>typeof window>"u"?!1:window.localStorage.getItem("turboEnabled")==="true"),[i,s]=C.useState(t),[a,o]=C.useState(()=>U_({turboEnabled:n})),l=C.useRef(!0);return C.useEffect(()=>{typeof window<"u"&&window.localStorage.setItem("turboEnabled",n?"true":"false")},[n]),C.useEffect(()=>{if(l.current){l.current=!1;return}o(Pg(i,{turboEnabled:n})),qp.clear()},[i,n]),h.jsx(A_,{provider:i,setProvider:s,availableProviders:H_(),children:h.jsx(I_,{turbo:n,setTurbo:r,children:h.jsx(D_,{client:a,children:h.jsxs(Sw,{client:qp,children:[e,null]})})})})}/** - * @remix-run/router v1.23.1 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */function Me(){return Me=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function Ei(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function W_(){return Math.random().toString(36).substr(2,8)}function Xp(e,t){return{usr:e.state,key:e.key,idx:t}}function Da(e,t,n,r){return n===void 0&&(n=null),Me({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?Ur(t):t,{state:n,key:t&&t.key||r||W_()})}function Ci(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function Ur(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function Z_(e,t,n,r){r===void 0&&(r={});let{window:i=document.defaultView,v5Compat:s=!1}=r,a=i.history,o=We.Pop,l=null,u=d();u==null&&(u=0,a.replaceState(Me({},a.state,{idx:u}),""));function d(){return(a.state||{idx:null}).idx}function c(){o=We.Pop;let T=d(),v=T==null?null:T-u;u=T,l&&l({action:o,location:x.location,delta:v})}function f(T,v){o=We.Push;let p=Da(x.location,T,v);u=d()+1;let y=Xp(p,u),S=x.createHref(p);try{a.pushState(y,"",S)}catch(R){if(R instanceof DOMException&&R.name==="DataCloneError")throw R;i.location.assign(S)}s&&l&&l({action:o,location:x.location,delta:1})}function m(T,v){o=We.Replace;let p=Da(x.location,T,v);u=d();let y=Xp(p,u),S=x.createHref(p);a.replaceState(y,"",S),s&&l&&l({action:o,location:x.location,delta:0})}function g(T){let v=i.location.origin!=="null"?i.location.origin:i.location.href,p=typeof T=="string"?T:Ci(T);return p=p.replace(/ $/,"%20"),le(v,"No window.location.(origin|href) available to create URL for href: "+p),new URL(p,v)}let x={get action(){return o},get location(){return e(i,a)},listen(T){if(l)throw new Error("A history only accepts one active listener");return i.addEventListener(Yp,c),l=T,()=>{i.removeEventListener(Yp,c),l=null}},createHref(T){return t(i,T)},createURL:g,encodeLocation(T){let v=g(T);return{pathname:v.pathname,search:v.search,hash:v.hash}},push:f,replace:m,go(T){return a.go(T)}};return x}var Ee;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(Ee||(Ee={}));const K_=new Set(["lazy","caseSensitive","path","id","index","children"]);function G_(e){return e.index===!0}function Pl(e,t,n,r){return n===void 0&&(n=[]),r===void 0&&(r={}),e.map((i,s)=>{let a=[...n,String(s)],o=typeof i.id=="string"?i.id:a.join("-");if(le(i.index!==!0||!i.children,"Cannot specify children on an index route"),le(!r[o],'Found a route id collision on id "'+o+`". Route id's must be globally unique within Data Router usages`),G_(i)){let l=Me({},i,t(i),{id:o});return r[o]=l,l}else{let l=Me({},i,t(i),{id:o,children:void 0});return r[o]=l,i.children&&(l.children=Pl(i.children,t,a,r)),l}})}function Yr(e,t,n){return n===void 0&&(n="/"),Ko(e,t,n,!1)}function Ko(e,t,n,r){let i=typeof t=="string"?Ur(t):t,s=Kn(i.pathname||"/",n);if(s==null)return null;let a=Rg(e);Y_(a);let o=null;for(let l=0;o==null&&l{let l={relativePath:o===void 0?s.path||"":o,caseSensitive:s.caseSensitive===!0,childrenIndex:a,route:s};l.relativePath.startsWith("/")&&(le(l.relativePath.startsWith(r),'Absolute route path "'+l.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),l.relativePath=l.relativePath.slice(r.length));let u=Un([r,l.relativePath]),d=n.concat(l);s.children&&s.children.length>0&&(le(s.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+u+'".')),Rg(s.children,t,d,u)),!(s.path==null&&!s.index)&&t.push({path:u,score:iS(u,s.index),routesMeta:d})};return e.forEach((s,a)=>{var o;if(s.path===""||!((o=s.path)!=null&&o.includes("?")))i(s,a);else for(let l of jg(s.path))i(s,a,l)}),t}function jg(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,i=n.endsWith("?"),s=n.replace(/\?$/,"");if(r.length===0)return i?[s,""]:[s];let a=jg(r.join("/")),o=[];return o.push(...a.map(l=>l===""?s:[s,l].join("/"))),i&&o.push(...a),o.map(l=>e.startsWith("/")&&l===""?"/":l)}function Y_(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:sS(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const X_=/^:[\w-]+$/,J_=3,eS=2,tS=1,nS=10,rS=-2,Jp=e=>e==="*";function iS(e,t){let n=e.split("/"),r=n.length;return n.some(Jp)DELIM" - R"DELIM()&&(r+=rS),t&&(r+=eS),n.filter(i=>!Jp(i)).reduce((i,s)=>i+(X_.test(s)?J_:s===""?tS:nS),r)}function sS(e,t){return e.length===t.length&&e.slice(0,-1).every((r,i)=>r===t[i])?e[e.length-1]-t[t.length-1]:0}function aS(e,t,n){n===void 0&&(n=!1);let{routesMeta:r}=e,i={},s="/",a=[];for(let o=0;o{let{paramName:f,isOptional:m}=d;if(f==="*"){let x=o[c]||"";a=s.slice(0,s.length-x.length).replace(/(.)\/+$/,"$1")}const g=o[c];return m&&!g?u[f]=void 0:u[f]=(g||"").replace(/%2F/g,"/"),u},{}),pathname:s,pathnameBase:a,pattern:e}}function oS(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),Ei(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],i="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(a,o,l)=>(r.push({paramName:o,isOptional:l!=null}),l?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),i+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?i+="\\/*$":e!==""&&e!=="/"&&(i+="(?:(?=\\/|$))"),[new RegExp(i,t?void 0:"i"),r]}function lS(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return Ei(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function Kn(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}const uS=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,cS=e=>uS.test(e);function dS(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:i=""}=typeof e=="string"?Ur(e):e,s;if(n)if(cS(n))s=n;else{if(n.includes("//")){let a=n;n=n.replace(/\/\/+/g,"/"),Ei(!1,"Pathnames cannot have embedded double slashes - normalizing "+(a+" -> "+n))}n.startsWith("/")?s=em(n.substring(1),"/"):s=em(n,t)}else s=t;return{pathname:s,search:hS(r),hash:pS(i)}}function em(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(i=>{i===".."?n.length>1&&n.pop():i!=="."&&n.push(i)}),n.length>1?n.join("/"):"/"}function Vu(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function Mg(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function Rf(e,t){let n=Mg(e);return t?n.map((r,i)=>i===n.length-1?r.pathname:r.pathnameBase):n.map(r=>r.pathnameBase)}function jf(e,t,n,r){r===void 0&&(r=!1);let i;typeof e=="string"?i=Ur(e):(i=Me({},e),le(!i.pathname||!i.pathname.includes("?"),Vu("?","pathname","search",i)),le(!i.pathname||!i.pathname.includes("#"),Vu("#","pathname","hash",i)),le(!i.search||!i.search.includes("#"),Vu("#","search","hash",i)));let s=e===""||i.pathname==="",a=s?"/":i.pathname,o;if(a==null)o=n;else{let c=t.length-1;if(!r&&a.startsWith("..")){let f=a.split("/");for(;f[0]==="..";)f.shift(),c-=1;i.pathname=f.join("/")}o=c>=0?t[c]:"/"}let l=dS(i,o),u=a&&a!=="/"&&a.endsWith("/"),d=(s||a===".")&&n.endsWith("/");return!l.pathname.endsWith("/")&&(u||d)&&(l.pathname+="/"),l}const Un=e=>e.join("/").replace(/\/\/+/g,"/"),fS=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),hS=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,pS=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;class jl{constructor(t,n,r,i){i===void 0&&(i=!1),this.status=t,this.statusText=n||"",this.internal=i,r instanceof Error?(this.data=r.toString(),this.error=r):this.data=r}}function Aa(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const Lg=["post","put","patch","delete"],mS=new Set(Lg),vS=["get",...Lg],yS=new Set(vS),gS=new Set([301,302,303,307,308]),xS=new Set([307,308]),Qu={state:"idle",location:void 0,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0},wS={state:"idle",data:void 0,formMethod:void 0,formAction:void 0,formEncType:void 0,formDat)DELIM" - R"DELIM(a:void 0,json:void 0,text:void 0},Hs={state:"unblocked",proceed:void 0,reset:void 0,location:void 0},Mf=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,kS=e=>({hasErrorBoundary:!!e.hasErrorBoundary}),Og="remix-router-transitions";function _S(e){const t=e.window?e.window:typeof window<"u"?window:void 0,n=typeof t<"u"&&typeof t.document<"u"&&typeof t.document.createElement<"u",r=!n;le(e.routes.length>0,"You must provide a non-empty routes array to createRouter");let i;if(e.mapRouteProperties)i=e.mapRouteProperties;else if(e.detectErrorBoundary){let w=e.detectErrorBoundary;i=E=>({hasErrorBoundary:w(E)})}else i=kS;let s={},a=Pl(e.routes,i,void 0,s),o,l=e.basename||"/",u=e.dataStrategy||TS,d=e.patchRoutesOnNavigation,c=Me({v7_fetcherPersist:!1,v7_normalizeFormMethod:!1,v7_partialHydration:!1,v7_prependBasename:!1,v7_relativeSplatPath:!1,v7_skipActionErrorRevalidation:!1},e.future),f=null,m=new Set,g=null,x=null,T=null,v=e.hydrationData!=null,p=Yr(a,e.history.location,l),y=!1,S=null;if(p==null&&!d){let w=St(404,{pathname:e.history.location.pathname}),{matches:E,route:N}=dm(a);p=E,S={[N.id]:w}}p&&!e.hydrationData&&to(p,a,e.history.location.pathname).active&&(p=null);let R;if(p)if(p.some(w=>w.route.lazy))R=!1;else if(!p.some(w=>w.route.loader))R=!0;else if(c.v7_partialHydration){let w=e.hydrationData?e.hydrationData.loaderData:null,E=e.hydrationData?e.hydrationData.errors:null;if(E){let N=p.findIndex(M=>E[M.route.id]!==void 0);R=p.slice(0,N+1).every(M=>!md(M.route,w,E))}else R=p.every(N=>!md(N.route,w,E))}else R=e.hydrationData!=null;else if(R=!1,p=[],c.v7_partialHydration){let w=to(null,a,e.history.location.pathname);w.active&&w.matches&&(y=!0,p=w.matches)}let L,k={historyAction:e.history.action,location:e.history.location,matches:p,initialized:R,navigation:Qu,restoreScrollPosition:e.hydrationData!=null?!1:null,preventScrollReset:!1,revalidation:"idle",loaderData:e.hydrationData&&e.hydrationData.loaderData||{},actionData:e.hydrationData&&e.hydrationData.actionData||null,errors:e.hydrationData&&e.hydrationData.errors||S,fetchers:new Map,blockers:new Map},j=We.Pop,W=!1,P,B=!1,ue=new Map,be=null,Z=!1,F=!1,$=[],K=new Set,b=new Map,D=0,I=-1,ee=new Map,re=new Set,ke=new Map,_e=new Map,Te=new Set,Ie=new Map,Ve=new Map,hn;function Pn(){if(f=e.history.listen(w=>{let{action:E,location:N,delta:M}=w;if(hn){hn(),hn=void 0;return}Ei(Ve.size===0||M!=null,"You are trying to use a blocker on a POP navigation to a location that was not created by @remix-run/router. This will fail silently in production. This can happen if you are navigating outside the router via `window.history.pushState`/`window.location.hash` instead of using router navigation APIs. This can also happen if you are using createHashRouter and the user manually changes the URL.");let z=Gf({currentLocation:k.location,nextLocation:N,historyAction:E});if(z&&M!=null){let te=new Promise(ie=>{hn=ie});e.history.go(M*-1),eo(z,{state:"blocked",location:N,proceed(){eo(z,{state:"proceeding",proceed:void 0,reset:void 0,location:N}),te.then(()=>e.history.go(M))},reset(){let ie=new Map(k.blockers);ie.set(z,Hs),Ue({blockers:ie})}});return}return Qr(E,N)}),n){zS(t,ue);let w=()=>BS(t,ue);t.addEventListener("pagehide",w),be=()=>t.removeEventListener("pagehide",w)}return k.initialized||Qr(We.Pop,k.location,{initialHydration:!0}),L}function ct(){f&&f(),be&&be(),m.clear(),P&&P.abort(),k.fetchers.forEach((w,E)=>Ja(E)),k.blockers.forEach((w,E)=>Kf(E))}function pn(w){return m.add(w),()=>m.delete(w)}function Ue(w,E){E===void 0&&(E={}),k=Me({},k,w);let N=[],M=[];c.v7_fetcherPersist&&k.fetchers.forEach((z,te)=>{z.state==="idle"&&(Te.has(te)?M.push(te):N.push(te))}),Te.forEach(z=>{!k.fetchers.has(z)&&!b.has(z)&&M.push(z)}),[...m].forEach(z=>z(k,{deletedFetchers:M,viewTransitionOpts:E.viewTransitionOpts,flushSync:E.flushSync===!0})),c.v7_fetcherPersist?(N.forEach(z=>k.fetchers.delete(z)),M.forEach(z=>Ja(z))):M.forEach(z=>Te.delete(z))}function Jt(w,E,N){var M,z;let{flushSync:te}=N===void 0?{}:N,ie=k.actionData!=null&&k.navigation.formMethod!=null&&on(k.navigation.formMethod)&&k.navigation.state==="loading"&&((M=w.state)==null?void 0:M._isRedirect)!==!0,G;E.actionData?Object.keys(E.actionData).length>0?G=E.actionData:G=null:ie?G=k.actionData:G=null;let Y=E.loaderData?um(k.loaderData,E.loaderData,E.matches||[],E.errors):k.loaderData,Q=k.blockers;Q.size>0&&(Q=new Map(Q),Q.forEach((pe,rt)=>Q.set(rt,Hs)));let J=W===!0||k.navigation.formMethod!=null&&on(k.navigation.formMethod)&&((z=w.state)==null?void 0:z._isRedirect)!==!0;o&&(a=o,o=void 0),Z||j===We.Pop||(j===We.Push?e.history.push(w,w.state):j===We.Replace&&e.history.replace(w,w.state));let oe;if(j===We.Pop){let pe=ue.get(k.location.pathname);pe&&pe.has(w.pathname)?oe={currentLocation:k.location,nextLocation:w}:ue.has(w.pathname)&&(oe={currentLocation:w,nextLocation:k.location})}else if(B){let pe=ue.get(k.location.pathname);pe?pe.add(w.pathname):(pe=new Set([w.pathname]),ue.set(k.location.pathname,pe)),oe={currentLocation:k.location,nextLocation:w}}Ue(Me({},E,{)DELIM" - R"DELIM(actionData:G,loaderData:Y,historyAction:j,location:w,initialized:!0,navigation:Qu,revalidation:"idle",restoreScrollPosition:Yf(w,E.matches||k.matches),preventScrollReset:J,blockers:Q}),{viewTransitionOpts:oe,flushSync:te===!0}),j=We.Pop,W=!1,B=!1,Z=!1,F=!1,$=[]}async function Bf(w,E){if(typeof w=="number"){e.history.go(w);return}let N=pd(k.location,k.matches,l,c.v7_prependBasename,w,c.v7_relativeSplatPath,E==null?void 0:E.fromRouteId,E==null?void 0:E.relative),{path:M,submission:z,error:te}=tm(c.v7_normalizeFormMethod,!1,N,E),ie=k.location,G=Da(k.location,M,E&&E.state);G=Me({},G,e.history.encodeLocation(G));let Y=E&&E.replace!=null?E.replace:void 0,Q=We.Push;Y===!0?Q=We.Replace:Y===!1||z!=null&&on(z.formMethod)&&z.formAction===k.location.pathname+k.location.search&&(Q=We.Replace);let J=E&&"preventScrollReset"in E?E.preventScrollReset===!0:void 0,oe=(E&&E.flushSync)===!0,pe=Gf({currentLocation:ie,nextLocation:G,historyAction:Q});if(pe){eo(pe,{state:"blocked",location:G,proceed(){eo(pe,{state:"proceeding",proceed:void 0,reset:void 0,location:G}),Bf(w,E)},reset(){let rt=new Map(k.blockers);rt.set(pe,Hs),Ue({blockers:rt})}});return}return await Qr(Q,G,{submission:z,pendingError:te,preventScrollReset:J,replace:E&&E.replace,enableViewTransition:E&&E.viewTransition,flushSync:oe})}function c0(){if(su(),Ue({revalidation:"loading"}),k.navigation.state!=="submitting"){if(k.navigation.state==="idle"){Qr(k.historyAction,k.location,{startUninterruptedRevalidation:!0});return}Qr(j||k.historyAction,k.navigation.location,{overrideNavigation:k.navigation,enableViewTransition:B===!0})}}async function Qr(w,E,N){P&&P.abort(),P=null,j=w,Z=(N&&N.startUninterruptedRevalidation)===!0,w0(k.location,k.matches),W=(N&&N.preventScrollReset)===!0,B=(N&&N.enableViewTransition)===!0;let M=o||a,z=N&&N.overrideNavigation,te=N!=null&&N.initialHydration&&k.matches&&k.matches.length>0&&!y?k.matches:Yr(M,E,l),ie=(N&&N.flushSync)===!0;if(te&&k.initialized&&!F&&MS(k.location,E)&&!(N&&N.submission&&on(N.submission.formMethod))){Jt(E,{matches:te},{flushSync:ie});return}let G=to(te,M,E.pathname);if(G.active&&G.matches&&(te=G.matches),!te){let{error:Ne,notFoundMatches:we,route:$e}=au(E.pathname);Jt(E,{matches:we,loaderData:{},errors:{[$e.id]:Ne}},{flushSync:ie});return}P=new AbortController;let Y=Mi(e.history,E,P.signal,N&&N.submission),Q;if(N&&N.pendingError)Q=[Xr(te).route.id,{type:Ee.error,error:N.pendingError}];else if(N&&N.submission&&on(N.submission.formMethod)){let Ne=await d0(Y,E,N.submission,te,G.active,{replace:N.replace,flushSync:ie});if(Ne.shortCircuited)return;if(Ne.pendingActionResult){let[we,$e]=Ne.pendingActionResult;if(Mt($e)&&Aa($e.error)&&$e.error.status===404){P=null,Jt(E,{matches:Ne.matches,loaderData:{},errors:{[we]:$e.error}});return}}te=Ne.matches||te,Q=Ne.pendingActionResult,z=Wu(E,N.submission),ie=!1,G.active=!1,Y=Mi(e.history,Y.url,Y.signal)}let{shortCircuited:J,matches:oe,loaderData:pe,errors:rt}=await f0(Y,E,te,G.active,z,N&&N.submission,N&&N.fetcherSubmission,N&&N.replace,N&&N.initialHydration===!0,ie,Q);J||(P=null,Jt(E,Me({matches:oe||te},cm(Q),{loaderData:pe,errors:rt})))}async function d0(w,E,N,M,z,te){te===void 0&&(te={}),su();let ie=$S(E,N);if(Ue({navigation:ie},{flushSync:te.flushSync===!0}),z){let Q=await no(M,E.pathname,w.signal);if(Q.type==="aborted")return{shortCircuited:!0};if(Q.type==="error"){let J=Xr(Q.partialMatches).route.id;return{matches:Q.partialMatches,pendingActionResult:[J,{type:Ee.error,error:Q.error}]}}else if(Q.matches)M=Q.matches;else{let{notFoundMatches:J,error:oe,route:pe}=au(E.pathname);return{matches:J,pendingActionResult:[pe.id,{type:Ee.error,error:oe}]}}}let G,Y=ea(M,E);if(!Y.route.action&&!Y.route.lazy)G={type:Ee.error,error:St(405,{method:w.method,pathname:E.pathname,routeId:Y.route.id})};else if(G=(await Ps("action",k,w,[Y],M,null))[Y.route.id],w.signal.aborted)return{shortCircuited:!0};if(ri(G)){let Q;return te&&te.replace!=null?Q=te.replace:Q=am(G.response.headers.get("Location"),new URL(w.url),l)===k.location.pathname+k.location.search,await Wr(w,G,!0,{submission:N,replace:Q}),{shortCircuited:!0}}if(Sr(G))throw St(400,{type:"defer-action"});if(Mt(G)){let Q=Xr(M,Y.route.id);return(te&&te.replace)!==!0&&(j=We.Push),{matches:M,pendingActionResult:[Q.route.id,G]}}return{matches:M,pendingActionResult:[Y.route.id,G]}}async function f0(w,E,N,M,z,te,ie,G,Y,Q,J){let oe=z||Wu(E,te),pe=te||ie||hm(oe),rt=!Z&&(!c.v7_partialHydration||!Y);if(M){if(rt){let Fe=Uf(J);Ue(Me({navigation:oe},Fe!==void 0?{actionData:Fe}:{}),{flushSync:Q})}let ge=await no(N,E.pathname,w.signal);if(ge.type==="aborted")return{shortCircuited:!0};if(ge.type==="error"){let Fe=Xr(ge.partialMatches).route.id;return{matches:ge.partialMatches,loaderData:{},errors:{[Fe]:ge.error}}}else if(ge.matches)N=ge.matches;else{let{error:Fe,notFoundMatches:Pi,route:Ms}=au(E.pathname);return{matches:Pi,loaderData:{},errors:{[Ms.id]:Fe}}}}let Ne=o||a,[we,$e]=rm(e.history,k,N,pe,E,c.v7_partialHydration&&Y===!0,c.v7_skipActionErrorRevalidation,F,$)DELIM" - R"DELIM(,K,Te,ke,re,Ne,l,J);if(ou(ge=>!(N&&N.some(Fe=>Fe.route.id===ge))||we&&we.some(Fe=>Fe.route.id===ge)),I=++D,we.length===0&&$e.length===0){let ge=Wf();return Jt(E,Me({matches:N,loaderData:{},errors:J&&Mt(J[1])?{[J[0]]:J[1].error}:null},cm(J),ge?{fetchers:new Map(k.fetchers)}:{}),{flushSync:Q}),{shortCircuited:!0}}if(rt){let ge={};if(!M){ge.navigation=oe;let Fe=Uf(J);Fe!==void 0&&(ge.actionData=Fe)}$e.length>0&&(ge.fetchers=h0($e)),Ue(ge,{flushSync:Q})}$e.forEach(ge=>{Jn(ge.key),ge.controller&&b.set(ge.key,ge.controller)});let Ni=()=>$e.forEach(ge=>Jn(ge.key));P&&P.signal.addEventListener("abort",Ni);let{loaderResults:Rs,fetcherResults:jn}=await Hf(k,N,we,$e,w);if(w.signal.aborted)return{shortCircuited:!0};P&&P.signal.removeEventListener("abort",Ni),$e.forEach(ge=>b.delete(ge.key));let mn=Po(Rs);if(mn)return await Wr(w,mn.result,!0,{replace:G}),{shortCircuited:!0};if(mn=Po(jn),mn)return re.add(mn.key),await Wr(w,mn.result,!0,{replace:G}),{shortCircuited:!0};let{loaderData:lu,errors:js}=lm(k,N,Rs,J,$e,jn,Ie);Ie.forEach((ge,Fe)=>{ge.subscribe(Pi=>{(Pi||ge.done)&&Ie.delete(Fe)})}),c.v7_partialHydration&&Y&&k.errors&&(js=Me({},k.errors,js));let Zr=Wf(),ro=Zf(I),io=Zr||ro||$e.length>0;return Me({matches:N,loaderData:lu,errors:js},io?{fetchers:new Map(k.fetchers)}:{})}function Uf(w){if(w&&!Mt(w[1]))return{[w[0]]:w[1].data};if(k.actionData)return Object.keys(k.actionData).length===0?null:k.actionData}function h0(w){return w.forEach(E=>{let N=k.fetchers.get(E.key),M=Vs(void 0,N?N.data:void 0);k.fetchers.set(E.key,M)}),new Map(k.fetchers)}function p0(w,E,N,M){if(r)throw new Error("router.fetch() was called during the server render, but it shouldn't be. You are likely calling a useFetcher() method in the body of your component. Try moving it to a useEffect or a callback.");Jn(w);let z=(M&&M.flushSync)===!0,te=o||a,ie=pd(k.location,k.matches,l,c.v7_prependBasename,N,c.v7_relativeSplatPath,E,M==null?void 0:M.relative),G=Yr(te,ie,l),Y=to(G,te,ie);if(Y.active&&Y.matches&&(G=Y.matches),!G){Rn(w,E,St(404,{pathname:ie}),{flushSync:z});return}let{path:Q,submission:J,error:oe}=tm(c.v7_normalizeFormMethod,!0,ie,M);if(oe){Rn(w,E,oe,{flushSync:z});return}let pe=ea(G,Q),rt=(M&&M.preventScrollReset)===!0;if(J&&on(J.formMethod)){m0(w,E,Q,pe,G,Y.active,z,rt,J);return}ke.set(w,{routeId:E,path:Q}),v0(w,E,Q,pe,G,Y.active,z,rt,J)}async function m0(w,E,N,M,z,te,ie,G,Y){su(),ke.delete(w);function Q(Qe){if(!Qe.route.action&&!Qe.route.lazy){let Ri=St(405,{method:Y.formMethod,pathname:N,routeId:E});return Rn(w,E,Ri,{flushSync:ie}),!0}return!1}if(!te&&Q(M))return;let J=k.fetchers.get(w);Xn(w,FS(Y,J),{flushSync:ie});let oe=new AbortController,pe=Mi(e.history,N,oe.signal,Y);if(te){let Qe=await no(z,new URL(pe.url).pathname,pe.signal,w);if(Qe.type==="aborted")return;if(Qe.type==="error"){Rn(w,E,Qe.error,{flushSync:ie});return}else if(Qe.matches){if(z=Qe.matches,M=ea(z,N),Q(M))return}else{Rn(w,E,St(404,{pathname:N}),{flushSync:ie});return}}b.set(w,oe);let rt=D,we=(await Ps("action",k,pe,[M],z,w))[M.route.id];if(pe.signal.aborted){b.get(w)===oe&&b.delete(w);return}if(c.v7_fetcherPersist&&Te.has(w)){if(ri(we)||Mt(we)){Xn(w,ar(void 0));return}}else{if(ri(we))if(b.delete(w),I>rt){Xn(w,ar(void 0));return}else return re.add(w),Xn(w,Vs(Y)),Wr(pe,we,!1,{fetcherSubmission:Y,preventScrollReset:G});if(Mt(we)){Rn(w,E,we.error);return}}if(Sr(we))throw St(400,{type:"defer-action"});let $e=k.navigation.location||k.location,Ni=Mi(e.history,$e,oe.signal),Rs=o||a,jn=k.navigation.state!=="idle"?Yr(Rs,k.navigation.location,l):k.matches;le(jn,"Didn't find any matches after fetcher action");let mn=++D;ee.set(w,mn);let lu=Vs(Y,we.data);k.fetchers.set(w,lu);let[js,Zr]=rm(e.history,k,jn,Y,$e,!1,c.v7_skipActionErrorRevalidation,F,$,K,Te,ke,re,Rs,l,[M.route.id,we]);Zr.filter(Qe=>Qe.key!==w).forEach(Qe=>{let Ri=Qe.key,Xf=k.fetchers.get(Ri),S0=Vs(void 0,Xf?Xf.data:void 0);k.fetchers.set(Ri,S0),Jn(Ri),Qe.controller&&b.set(Ri,Qe.controller)}),Ue({fetchers:new Map(k.fetchers)});let ro=()=>Zr.forEach(Qe=>Jn(Qe.key));oe.signal.addEventListener("abort",ro);let{loaderResults:io,fetcherResults:ge}=await Hf(k,jn,js,Zr,Ni);if(oe.signal.aborted)return;oe.signal.removeEventListener("abort",ro),ee.delete(w),b.delete(w),Zr.forEach(Qe=>b.delete(Qe.key));let Fe=Po(io);if(Fe)return Wr(Ni,Fe.result,!1,{preventScrollReset:G});if(Fe=Po(ge),Fe)return re.add(Fe.key),Wr(Ni,Fe.result,!1,{preventScrollReset:G});let{loaderData:Pi,errors:Ms}=lm(k,jn,io,void 0,Zr,ge,Ie);if(k.fetchers.has(w)){let Qe=ar(we.data);k.fetchers.set(w,Qe)}Zf(mn),k.navigation.state==="loading"&&mn>I?(le(j,"Expected pending action"),P&&P.abort(),Jt(k.navigation.location,{matches:jn,loaderData:Pi,errors:Ms,fetchers:new Map(k.fetchers)})):(Ue({errors:Ms,loaderData:um(k.loaderData,Pi,jn,Ms),fetchers:new Map(k.fetchers)}),F=!1)}async function v0(w,E,N,M,z,te,ie,G,Y){let Q=k.fetchers.get(w);Xn(w,Vs(Y,Q?Q.data:void 0),{flushSync:ie});let J=new AbortController,oe=Mi(e.history,N,J.signal);if(te){let we=await no(z,new URL(oe.url).pathname,o)DELIM" - R"DELIM(e.signal,w);if(we.type==="aborted")return;if(we.type==="error"){Rn(w,E,we.error,{flushSync:ie});return}else if(we.matches)z=we.matches,M=ea(z,N);else{Rn(w,E,St(404,{pathname:N}),{flushSync:ie});return}}b.set(w,J);let pe=D,Ne=(await Ps("loader",k,oe,[M],z,w))[M.route.id];if(Sr(Ne)&&(Ne=await Lf(Ne,oe.signal,!0)||Ne),b.get(w)===J&&b.delete(w),!oe.signal.aborted){if(Te.has(w)){Xn(w,ar(void 0));return}if(ri(Ne))if(I>pe){Xn(w,ar(void 0));return}else{re.add(w),await Wr(oe,Ne,!1,{preventScrollReset:G});return}if(Mt(Ne)){Rn(w,E,Ne.error);return}le(!Sr(Ne),"Unhandled fetcher deferred data"),Xn(w,ar(Ne.data))}}async function Wr(w,E,N,M){let{submission:z,fetcherSubmission:te,preventScrollReset:ie,replace:G}=M===void 0?{}:M;E.response.headers.has("X-Remix-Revalidate")&&(F=!0);let Y=E.response.headers.get("Location");le(Y,"Expected a Location header on the redirect Response"),Y=am(Y,new URL(w.url),l);let Q=Da(k.location,Y,{_isRedirect:!0});if(n){let we=!1;if(E.response.headers.has("X-Remix-Reload-Document"))we=!0;else if(Mf.test(Y)){const $e=e.history.createURL(Y);we=$e.origin!==t.location.origin||Kn($e.pathname,l)==null}if(we){G?t.location.replace(Y):t.location.assign(Y);return}}P=null;let J=G===!0||E.response.headers.has("X-Remix-Replace")?We.Replace:We.Push,{formMethod:oe,formAction:pe,formEncType:rt}=k.navigation;!z&&!te&&oe&&pe&&rt&&(z=hm(k.navigation));let Ne=z||te;if(xS.has(E.response.status)&&Ne&&on(Ne.formMethod))await Qr(J,Q,{submission:Me({},Ne,{formAction:Y}),preventScrollReset:ie||W,enableViewTransition:N?B:void 0});else{let we=Wu(Q,z);await Qr(J,Q,{overrideNavigation:we,fetcherSubmission:te,preventScrollReset:ie||W,enableViewTransition:N?B:void 0})}}async function Ps(w,E,N,M,z,te){let ie,G={};try{ie=await bS(u,w,E,N,M,z,te,s,i)}catch(Y){return M.forEach(Q=>{G[Q.route.id]={type:Ee.error,error:Y}}),G}for(let[Y,Q]of Object.entries(ie))if(LS(Q)){let J=Q.result;G[Y]={type:Ee.redirect,response:RS(J,N,Y,z,l,c.v7_relativeSplatPath)}}else G[Y]=await PS(Q);return G}async function Hf(w,E,N,M,z){let te=w.matches,ie=Ps("loader",w,z,N,E,null),G=Promise.all(M.map(async J=>{if(J.matches&&J.match&&J.controller){let pe=(await Ps("loader",w,Mi(e.history,J.path,J.controller.signal),[J.match],J.matches,J.key))[J.match.route.id];return{[J.key]:pe}}else return Promise.resolve({[J.key]:{type:Ee.error,error:St(404,{pathname:J.path})}})})),Y=await ie,Q=(await G).reduce((J,oe)=>Object.assign(J,oe),{});return await Promise.all([AS(E,Y,z.signal,te,w.loaderData),IS(E,Q,M)]),{loaderResults:Y,fetcherResults:Q}}function su(){F=!0,$.push(...ou()),ke.forEach((w,E)=>{b.has(E)&&K.add(E),Jn(E)})}function Xn(w,E,N){N===void 0&&(N={}),k.fetchers.set(w,E),Ue({fetchers:new Map(k.fetchers)},{flushSync:(N&&N.flushSync)===!0})}function Rn(w,E,N,M){M===void 0&&(M={});let z=Xr(k.matches,E);Ja(w),Ue({errors:{[z.route.id]:N},fetchers:new Map(k.fetchers)},{flushSync:(M&&M.flushSync)===!0})}function Vf(w){return _e.set(w,(_e.get(w)||0)+1),Te.has(w)&&Te.delete(w),k.fetchers.get(w)||wS}function Ja(w){let E=k.fetchers.get(w);b.has(w)&&!(E&&E.state==="loading"&&ee.has(w))&&Jn(w),ke.delete(w),ee.delete(w),re.delete(w),c.v7_fetcherPersist&&Te.delete(w),K.delete(w),k.fetchers.delete(w)}function y0(w){let E=(_e.get(w)||0)-1;E<=0?(_e.delete(w),Te.add(w),c.v7_fetcherPersist||Ja(w)):_e.set(w,E),Ue({fetchers:new Map(k.fetchers)})}function Jn(w){let E=b.get(w);E&&(E.abort(),b.delete(w))}function Qf(w){for(let E of w){let N=Vf(E),M=ar(N.data);k.fetchers.set(E,M)}}function Wf(){let w=[],E=!1;for(let N of re){let M=k.fetchers.get(N);le(M,"Expected fetcher: "+N),M.state==="loading"&&(re.delete(N),w.push(N),E=!0)}return Qf(w),E}function Zf(w){let E=[];for(let[N,M]of ee)if(M0}function g0(w,E){let N=k.blockers.get(w)||Hs;return Ve.get(w)!==E&&Ve.set(w,E),N}function Kf(w){k.blockers.delete(w),Ve.delete(w)}function eo(w,E){let N=k.blockers.get(w)||Hs;le(N.state==="unblocked"&&E.state==="blocked"||N.state==="blocked"&&E.state==="blocked"||N.state==="blocked"&&E.state==="proceeding"||N.state==="blocked"&&E.state==="unblocked"||N.state==="proceeding"&&E.state==="unblocked","Invalid blocker state transition: "+N.state+" -> "+E.state);let M=new Map(k.blockers);M.set(w,E),Ue({blockers:M})}function Gf(w){let{currentLocation:E,nextLocation:N,historyAction:M}=w;if(Ve.size===0)return;Ve.size>1&&Ei(!1,"A router only supports one blocker at a time");let z=Array.from(Ve.entries()),[te,ie]=z[z.length-1],G=k.blockers.get(te);if(!(G&&G.state==="proceeding")&&ie({currentLocation:E,nextLocation:N,historyAction:M}))return te}function au(w){let E=St(404,{pathname:w}),N=o||a,{matches:M,route:z}=dm(N);return ou(),{notFoundMatches:M,route:z,error:E}}function ou(w){let E=[];return Ie.forEach((N,M)=>{(!w||w(M))&&(N.cancel(),E.push(M),Ie.delete(M))}),E}function x0(w,E,N){if(g=w,T=E,x=N||null,!v&&k.navigation===Qu){v=!0;let M=Yf(k.location,k.matches);M!=null&&Ue({restoreScrol)DELIM" - R"DELIM(lPosition:M})}return()=>{g=null,T=null,x=null}}function qf(w,E){return x&&x(w,E.map(M=>q_(M,k.loaderData)))||w.key}function w0(w,E){if(g&&T){let N=qf(w,E);g[N]=T()}}function Yf(w,E){if(g){let N=qf(w,E),M=g[N];if(typeof M=="number")return M}return null}function to(w,E,N){if(d)if(w){if(Object.keys(w[0].params).length>0)return{active:!0,matches:Ko(E,N,l,!0)}}else return{active:!0,matches:Ko(E,N,l,!0)||[]};return{active:!1,matches:null}}async function no(w,E,N,M){if(!d)return{type:"success",matches:w};let z=w;for(;;){let te=o==null,ie=o||a,G=s;try{await d({signal:N,path:E,matches:z,fetcherKey:M,patch:(J,oe)=>{N.aborted||sm(J,oe,ie,G,i)}})}catch(J){return{type:"error",error:J,partialMatches:z}}finally{te&&!N.aborted&&(a=[...a])}if(N.aborted)return{type:"aborted"};let Y=Yr(ie,E,l);if(Y)return{type:"success",matches:Y};let Q=Ko(ie,E,l,!0);if(!Q||z.length===Q.length&&z.every((J,oe)=>J.route.id===Q[oe].route.id))return{type:"success",matches:null};z=Q}}function k0(w){s={},o=Pl(w,i,void 0,s)}function _0(w,E){let N=o==null;sm(w,E,o||a,s,i),N&&(a=[...a],Ue({}))}return L={get basename(){return l},get future(){return c},get state(){return k},get routes(){return a},get window(){return t},initialize:Pn,subscribe:pn,enableScrollRestoration:x0,navigate:Bf,fetch:p0,revalidate:c0,createHref:w=>e.history.createHref(w),encodeLocation:w=>e.history.encodeLocation(w),getFetcher:Vf,deleteFetcher:y0,dispose:ct,getBlocker:g0,deleteBlocker:Kf,patchRoutes:_0,_internalFetchControllers:b,_internalActiveDeferreds:Ie,_internalSetRoutes:k0},L}function SS(e){return e!=null&&("formData"in e&&e.formData!=null||"body"in e&&e.body!==void 0)}function pd(e,t,n,r,i,s,a,o){let l,u;if(a){l=[];for(let c of t)if(l.push(c),c.route.id===a){u=c;break}}else l=t,u=t[t.length-1];let d=jf(i||".",Rf(l,s),Kn(e.pathname,n)||e.pathname,o==="path");if(i==null&&(d.search=e.search,d.hash=e.hash),(i==null||i===""||i===".")&&u){let c=Of(d.search);if(u.route.index&&!c)d.search=d.search?d.search.replace(/^\?/,"?index&"):"?index";else if(!u.route.index&&c){let f=new URLSearchParams(d.search),m=f.getAll("index");f.delete("index"),m.filter(x=>x).forEach(x=>f.append("index",x));let g=f.toString();d.search=g?"?"+g:""}}return r&&n!=="/"&&(d.pathname=d.pathname==="/"?n:Un([n,d.pathname])),Ci(d)}function tm(e,t,n,r){if(!r||!SS(r))return{path:n};if(r.formMethod&&!DS(r.formMethod))return{path:n,error:St(405,{method:r.formMethod})};let i=()=>({path:n,error:St(400,{type:"invalid-body"})}),s=r.formMethod||"get",a=e?s.toUpperCase():s.toLowerCase(),o=Ig(n);if(r.body!==void 0){if(r.formEncType==="text/plain"){if(!on(a))return i();let f=typeof r.body=="string"?r.body:r.body instanceof FormData||r.body instanceof URLSearchParams?Array.from(r.body.entries()).reduce((m,g)=>{let[x,T]=g;return""+m+x+"="+T+` -`},""):String(r.body);return{path:n,submission:{formMethod:a,formAction:o,formEncType:r.formEncType,formData:void 0,json:void 0,text:f}}}else if(r.formEncType==="application/json"){if(!on(a))return i();try{let f=typeof r.body=="string"?JSON.parse(r.body):r.body;return{path:n,submission:{formMethod:a,formAction:o,formEncType:r.formEncType,formData:void 0,json:f,text:void 0}}}catch{return i()}}}le(typeof FormData=="function","FormData is not available in this environment");let l,u;if(r.formData)l=vd(r.formData),u=r.formData;else if(r.body instanceof FormData)l=vd(r.body),u=r.body;else if(r.body instanceof URLSearchParams)l=r.body,u=om(l);else if(r.body==null)l=new URLSearchParams,u=new FormData;else try{l=new URLSearchParams(r.body),u=om(l)}catch{return i()}let d={formMethod:a,formAction:o,formEncType:r&&r.formEncType||"application/x-www-form-urlencoded",formData:u,json:void 0,text:void 0};if(on(d.formMethod))return{path:n,submission:d};let c=Ur(n);return t&&c.search&&Of(c.search)&&l.append("index",""),c.search="?"+l,{path:Ci(c),submission:d}}function nm(e,t,n){n===void 0&&(n=!1);let r=e.findIndex(i=>i.route.id===t);return r>=0?e.slice(0,n?r+1:r):e}function rm(e,t,n,r,i,s,a,o,l,u,d,c,f,m,g,x){let T=x?Mt(x[1])?x[1].error:x[1].data:void 0,v=e.createURL(t.location),p=e.createURL(i),y=n;s&&t.errors?y=nm(n,Object.keys(t.errors)[0],!0):x&&Mt(x[1])&&(y=nm(n,x[0]));let S=x?x[1].statusCode:void 0,R=a&&S&&S>=400,L=y.filter((j,W)=>{let{route:P}=j;if(P.lazy)return!0;if(P.loader==null)return!1;if(s)return md(P,t.loaderData,t.errors);if(ES(t.loaderData,t.matches[W],j)||l.some(be=>be===j.route.id))return!0;let B=t.matches[W],ue=j;return im(j,Me({currentUrl:v,currentParams:B.params,nextUrl:p,nextParams:ue.params},r,{actionResult:T,actionStatus:S,defaultShouldRevalidate:R?!1:o||v.pathname+v.search===p.pathname+p.search||v.search!==p.search||Dg(B,ue)}))}),k=[];return c.forEach((j,W)=>{if(s||!n.some(Z=>Z.route.id===j.routeId)||d.has(W))return;let P=Yr(m,j.path,g);if(!P){k.push({key:W,routeId:j.routeId,path:j.path,matches:null,match:null,controller:null});return}let B=t.fetchers.get(W),ue=ea(P,j.path),be=!1;f.has(W)?be=!1:u.has(W)?(u.delete(W),be=!0):B&&B.state!=="idle"&&B.data===void 0?be=o:be=im(ue,Me({curren)DELIM" -R"DELIM(tUrl:v,currentParams:t.matches[t.matches.length-1].params,nextUrl:p,nextParams:n[n.length-1].params},r,{actionResult:T,actionStatus:S,defaultShouldRevalidate:R?!1:o})),be&&k.push({key:W,routeId:j.routeId,path:j.path,matches:P,match:ue,controller:new AbortController})}),[L,k]}function md(e,t,n){if(e.lazy)return!0;if(!e.loader)return!1;let r=t!=null&&t[e.id]!==void 0,i=n!=null&&n[e.id]!==void 0;return!r&&i?!1:typeof e.loader=="function"&&e.loader.hydrate===!0?!0:!r&&!i}function ES(e,t,n){let r=!t||n.route.id!==t.route.id,i=e[n.route.id]===void 0;return r||i}function Dg(e,t){let n=e.route.path;return e.pathname!==t.pathname||n!=null&&n.endsWith("*")&&e.params["*"]!==t.params["*"]}function im(e,t){if(e.route.shouldRevalidate){let n=e.route.shouldRevalidate(t);if(typeof n=="boolean")return n}return t.defaultShouldRevalidate}function sm(e,t,n,r,i){var s;let a;if(e){let u=r[e];le(u,"No route found to patch children into: routeId = "+e),u.children||(u.children=[]),a=u.children}else a=n;let o=t.filter(u=>!a.some(d=>Ag(u,d))),l=Pl(o,i,[e||"_","patch",String(((s=a)==null?void 0:s.length)||"0")],r);a.push(...l)}function Ag(e,t){return"id"in e&&"id"in t&&e.id===t.id?!0:e.index===t.index&&e.path===t.path&&e.caseSensitive===t.caseSensitive?(!e.children||e.children.length===0)&&(!t.children||t.children.length===0)?!0:e.children.every((n,r)=>{var i;return(i=t.children)==null?void 0:i.some(s=>Ag(n,s))}):!1}async function CS(e,t,n){if(!e.lazy)return;let r=await e.lazy();if(!e.lazy)return;let i=n[e.id];le(i,"No route found in manifest");let s={};for(let a in r){let l=i[a]!==void 0&&a!=="hasErrorBoundary";Ei(!l,'Route "'+i.id+'" has a static property "'+a+'" defined but its lazy function is also returning a value for this property. '+('The lazy route property "'+a+'" will be ignored.')),!l&&!K_.has(a)&&(s[a]=r[a])}Object.assign(i,s),Object.assign(i,Me({},t(i),{lazy:void 0}))}async function TS(e){let{matches:t}=e,n=t.filter(i=>i.shouldLoad);return(await Promise.all(n.map(i=>i.resolve()))).reduce((i,s,a)=>Object.assign(i,{[n[a].route.id]:s}),{})}async function bS(e,t,n,r,i,s,a,o,l,u){let d=s.map(m=>m.route.lazy?CS(m.route,l,o):void 0),c=s.map((m,g)=>{let x=d[g],T=i.some(p=>p.route.id===m.route.id);return Me({},m,{shouldLoad:T,resolve:async p=>(p&&r.method==="GET"&&(m.route.lazy||m.route.loader)&&(T=!0),T?NS(t,r,m,x,p,u):Promise.resolve({type:Ee.data,result:void 0}))})}),f=await e({matches:c,request:r,params:s[0].params,fetcherKey:a,context:u});try{await Promise.all(d)}catch{}return f}async function NS(e,t,n,r,i,s){let a,o,l=u=>{let d,c=new Promise((g,x)=>d=x);o=()=>d(),t.signal.addEventListener("abort",o);let f=g=>typeof u!="function"?Promise.reject(new Error("You cannot call the handler for a route which defines a boolean "+('"'+e+'" [routeId: '+n.route.id+"]"))):u({request:t,params:n.params,context:s},...g!==void 0?[g]:[]),m=(async()=>{try{return{type:"data",result:await(i?i(x=>f(x)):f())}}catch(g){return{type:"error",result:g}}})();return Promise.race([m,c])};try{let u=n.route[e];if(r)if(u){let d,[c]=await Promise.all([l(u).catch(f=>{d=f}),r]);if(d!==void 0)throw d;a=c}else if(await r,u=n.route[e],u)a=await l(u);else if(e==="action"){let d=new URL(t.url),c=d.pathname+d.search;throw St(405,{method:t.method,pathname:c,routeId:n.route.id})}else return{type:Ee.data,result:void 0};else if(u)a=await l(u);else{let d=new URL(t.url),c=d.pathname+d.search;throw St(404,{pathname:c})}le(a.result!==void 0,"You defined "+(e==="action"?"an action":"a loader")+" for route "+('"'+n.route.id+"\" but didn't return anything from your `"+e+"` ")+"function. Please return a value or `null`.")}catch(u){return{type:Ee.error,result:u}}finally{o&&t.signal.removeEventListener("abort",o)}return a}async function PS(e){let{result:t,type:n}=e;if($g(t)){let c;try{let f=t.headers.get("Content-Type");f&&/\bapplication\/json\b/.test(f)?t.body==null?c=null:c=await t.json():c=await t.text()}catch(f){return{type:Ee.error,error:f}}return n===Ee.error?{type:Ee.error,error:new jl(t.status,t.statusText,c),statusCode:t.status,headers:t.headers}:{type:Ee.data,data:c,statusCode:t.status,headers:t.headers}}if(n===Ee.error){if(fm(t)){var r,i;if(t.data instanceof Error){var s,a;return{type:Ee.error,error:t.data,statusCode:(s=t.init)==null?void 0:s.status,headers:(a=t.init)!=null&&a.headers?new Headers(t.init.headers):void 0}}return{type:Ee.error,error:new jl(((r=t.init)==null?void 0:r.status)||500,void 0,t.data),statusCode:Aa(t)?t.status:void 0,headers:(i=t.init)!=null&&i.headers?new Headers(t.init.headers):void 0}}return{type:Ee.error,error:t,statusCode:Aa(t)?t.status:void 0}}if(OS(t)){var o,l;return{type:Ee.deferred,deferredData:t,statusCode:(o=t.init)==null?void 0:o.status,headers:((l=t.init)==null?void 0:l.headers)&&new Headers(t.init.headers)}}if(fm(t)){var u,d;return{type:Ee.data,data:t.data,statusCode:(u=t.init)==null?void 0:u.status,headers:(d=t.init)!=null&&d.headers?new Headers(t.init.headers):void 0}}return{type:Ee.data,data:t}}function RS(e,t,n,r,i,s){let a=e.hea)DELIM" -R"DELIM(ders.get("Location");if(le(a,"Redirects returned/thrown from loaders/actions must have a Location header"),!Mf.test(a)){let o=r.slice(0,r.findIndex(l=>l.route.id===n)+1);a=pd(new URL(t.url),o,i,!0,a,s),e.headers.set("Location",a)}return e}function am(e,t,n){if(Mf.test(e)){let r=e,i=r.startsWith("//")?new URL(t.protocol+r):new URL(r),s=Kn(i.pathname,n)!=null;if(i.origin===t.origin&&s)return i.pathname+i.search+i.hash}return e}function Mi(e,t,n,r){let i=e.createURL(Ig(t)).toString(),s={signal:n};if(r&&on(r.formMethod)){let{formMethod:a,formEncType:o}=r;s.method=a.toUpperCase(),o==="application/json"?(s.headers=new Headers({"Content-Type":o}),s.body=JSON.stringify(r.json)):o==="text/plain"?s.body=r.text:o==="application/x-www-form-urlencoded"&&r.formData?s.body=vd(r.formData):s.body=r.formData}return new Request(i,s)}function vd(e){let t=new URLSearchParams;for(let[n,r]of e.entries())t.append(n,typeof r=="string"?r:r.name);return t}function om(e){let t=new FormData;for(let[n,r]of e.entries())t.append(n,r);return t}function jS(e,t,n,r,i){let s={},a=null,o,l=!1,u={},d=n&&Mt(n[1])?n[1].error:void 0;return e.forEach(c=>{if(!(c.route.id in t))return;let f=c.route.id,m=t[f];if(le(!ri(m),"Cannot handle redirect results in processLoaderData"),Mt(m)){let g=m.error;d!==void 0&&(g=d,d=void 0),a=a||{};{let x=Xr(e,f);a[x.route.id]==null&&(a[x.route.id]=g)}s[f]=void 0,l||(l=!0,o=Aa(m.error)?m.error.status:500),m.headers&&(u[f]=m.headers)}else Sr(m)?(r.set(f,m.deferredData),s[f]=m.deferredData.data,m.statusCode!=null&&m.statusCode!==200&&!l&&(o=m.statusCode),m.headers&&(u[f]=m.headers)):(s[f]=m.data,m.statusCode&&m.statusCode!==200&&!l&&(o=m.statusCode),m.headers&&(u[f]=m.headers))}),d!==void 0&&n&&(a={[n[0]]:d},s[n[0]]=void 0),{loaderData:s,errors:a,statusCode:o||200,loaderHeaders:u}}function lm(e,t,n,r,i,s,a){let{loaderData:o,errors:l}=jS(t,n,r,a);return i.forEach(u=>{let{key:d,match:c,controller:f}=u,m=s[d];if(le(m,"Did not find corresponding fetcher result"),!(f&&f.signal.aborted))if(Mt(m)){let g=Xr(e.matches,c==null?void 0:c.route.id);l&&l[g.route.id]||(l=Me({},l,{[g.route.id]:m.error})),e.fetchers.delete(d)}else if(ri(m))le(!1,"Unhandled fetcher revalidation redirect");else if(Sr(m))le(!1,"Unhandled fetcher deferred data");else{let g=ar(m.data);e.fetchers.set(d,g)}}),{loaderData:o,errors:l}}function um(e,t,n,r){let i=Me({},t);for(let s of n){let a=s.route.id;if(t.hasOwnProperty(a)?t[a]!==void 0&&(i[a]=t[a]):e[a]!==void 0&&s.route.loader&&(i[a]=e[a]),r&&r.hasOwnProperty(a))break}return i}function cm(e){return e?Mt(e[1])?{actionData:{}}:{actionData:{[e[0]]:e[1].data}}:{}}function Xr(e,t){return(t?e.slice(0,e.findIndex(r=>r.route.id===t)+1):[...e]).reverse().find(r=>r.route.hasErrorBoundary===!0)||e[0]}function dm(e){let t=e.length===1?e[0]:e.find(n=>n.index||!n.path||n.path==="/")||{id:"__shim-error-route__"};return{matches:[{params:{},pathname:"",pathnameBase:"",route:t}],route:t}}function St(e,t){let{pathname:n,routeId:r,method:i,type:s,message:a}=t===void 0?{}:t,o="Unknown Server Error",l="Unknown @remix-run/router error";return e===400?(o="Bad Request",i&&n&&r?l="You made a "+i+' request to "'+n+'" but '+('did not provide a `loader` for route "'+r+'", ')+"so there is no way to handle the request.":s==="defer-action"?l="defer() is not supported in actions":s==="invalid-body"&&(l="Unable to encode submission body")):e===403?(o="Forbidden",l='Route "'+r+'" does not match URL "'+n+'"'):e===404?(o="Not Found",l='No route matches URL "'+n+'"'):e===405&&(o="Method Not Allowed",i&&n&&r?l="You made a "+i.toUpperCase()+' request to "'+n+'" but '+('did not provide an `action` for route "'+r+'", ')+"so there is no way to handle the request.":i&&(l='Invalid request method "'+i.toUpperCase()+'"')),new jl(e||500,o,new Error(l),!0)}function Po(e){let t=Object.entries(e);for(let n=t.length-1;n>=0;n--){let[r,i]=t[n];if(ri(i))return{key:r,result:i}}}function Ig(e){let t=typeof e=="string"?Ur(e):e;return Ci(Me({},t,{hash:""}))}function MS(e,t){return e.pathname!==t.pathname||e.search!==t.search?!1:e.hash===""?t.hash!=="":e.hash===t.hash?!0:t.hash!==""}function LS(e){return $g(e.result)&&gS.has(e.result.status)}function Sr(e){return e.type===Ee.deferred}function Mt(e){return e.type===Ee.error}function ri(e){return(e&&e.type)===Ee.redirect}function fm(e){return typeof e=="object"&&e!=null&&"type"in e&&"data"in e&&"init"in e&&e.type==="DataWithResponseInit"}function OS(e){let t=e;return t&&typeof t=="object"&&typeof t.data=="object"&&typeof t.subscribe=="function"&&typeof t.cancel=="function"&&typeof t.resolveData=="function"}function $g(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.headers=="object"&&typeof e.body<"u"}function DS(e){return yS.has(e.toLowerCase())}function on(e){return mS.has(e.toLowerCase())}async function AS(e,t,n,r,i){let s=Object.entries(t);for(let a=0;a(f==null?void 0:f.route.id)===o);if(!u)continue;let d=r.find(f=>f.route.id===u.route.id)DELIM" -R"DELIM(),c=d!=null&&!Dg(d,u)&&(i&&i[u.route.id])!==void 0;Sr(l)&&c&&await Lf(l,n,!1).then(f=>{f&&(t[o]=f)})}}async function IS(e,t,n){for(let r=0;r(u==null?void 0:u.route.id)===s)&&Sr(o)&&(le(a,"Expected an AbortController for revalidating fetcher deferred result"),await Lf(o,a.signal,!0).then(u=>{u&&(t[i]=u)}))}}async function Lf(e,t,n){if(n===void 0&&(n=!1),!await e.deferredData.resolveData(t)){if(n)try{return{type:Ee.data,data:e.deferredData.unwrappedData}}catch(i){return{type:Ee.error,error:i}}return{type:Ee.data,data:e.deferredData.data}}}function Of(e){return new URLSearchParams(e).getAll("index").some(t=>t==="")}function ea(e,t){let n=typeof t=="string"?Ur(t).search:t.search;if(e[e.length-1].route.index&&Of(n||""))return e[e.length-1];let r=Mg(e);return r[r.length-1]}function hm(e){let{formMethod:t,formAction:n,formEncType:r,text:i,formData:s,json:a}=e;if(!(!t||!n||!r)){if(i!=null)return{formMethod:t,formAction:n,formEncType:r,formData:void 0,json:void 0,text:i};if(s!=null)return{formMethod:t,formAction:n,formEncType:r,formData:s,json:void 0,text:void 0};if(a!==void 0)return{formMethod:t,formAction:n,formEncType:r,formData:void 0,json:a,text:void 0}}}function Wu(e,t){return t?{state:"loading",location:e,formMethod:t.formMethod,formAction:t.formAction,formEncType:t.formEncType,formData:t.formData,json:t.json,text:t.text}:{state:"loading",location:e,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0}}function $S(e,t){return{state:"submitting",location:e,formMethod:t.formMethod,formAction:t.formAction,formEncType:t.formEncType,formData:t.formData,json:t.json,text:t.text}}function Vs(e,t){return e?{state:"loading",formMethod:e.formMethod,formAction:e.formAction,formEncType:e.formEncType,formData:e.formData,json:e.json,text:e.text,data:t}:{state:"loading",formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0,data:t}}function FS(e,t){return{state:"submitting",formMethod:e.formMethod,formAction:e.formAction,formEncType:e.formEncType,formData:e.formData,json:e.json,text:e.text,data:t?t.data:void 0}}function ar(e){return{state:"idle",formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0,data:e}}function zS(e,t){try{let n=e.sessionStorage.getItem(Og);if(n){let r=JSON.parse(n);for(let[i,s]of Object.entries(r||{}))s&&Array.isArray(s)&&t.set(i,new Set(s||[]))}}catch{}}function BS(e,t){if(t.size>0){let n={};for(let[r,i]of t)n[r]=[...i];try{e.sessionStorage.setItem(Og,JSON.stringify(n))}catch(r){Ei(!1,"Failed to save applied view transitions in sessionStorage ("+r+").")}}}/** - * React Router v6.30.2 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */function Ml(){return Ml=Object.assign?Object.assign.bind():function(e){for(var t=1;t{o.current=!0}),C.useCallback(function(u,d){if(d===void 0&&(d={}),!o.current)return;if(typeof u=="number"){r.go(u);return}let c=jf(u,JSON.parse(a),s,d.relative==="path");e==null&&t!=="/"&&(c.pathname=c.pathname==="/"?t:Un([t,c.pathname])),(d.replace?r.replace:r.push)(c,d.state,d)},[t,r,a,s,e])}const VS=C.createContext(null);function QS(e){let t=C.useContext(Yn).outlet;return t&&C.createElement(VS.Provider,{value:e},t)}function If(){let{matches:e}=C.useContext(Yn),t=e[e.length-1];return t?t.params:{}}function tu(e,t){let{relative:n}=t===void 0?{}:t,{future:r}=C.useContext(Hr),{matches:i}=C.useContext(Yn),{pathname:s}=Ns(),a=JSON.stringify(Rf(i,r.v7_relativeSplatPath));return C.useMemo(()=>jf(e,JSON.parse(a),s,n==="path"),[e,a,s,n])}function WS(e,t,n,r){Ya()||le(!1);let{navigator:i}=C.useContext(Hr),{matches:s}=C.useContext(Yn),a=s[s.length-1],o=a?a.params:{};a&&a.pathname;let l=a?a.pathnameBase:"/";a&&a.route;let u=Ns(),d;d=u;let c=d.pathname||)DELIM" - R"DELIM("/",f=c;if(l!=="/"){let x=l.replace(/^\//,"").split("/");f="/"+c.replace(/^\//,"").split("/").slice(x.length).join("/")}let m=Yr(e,{pathname:f});return YS(m&&m.map(x=>Object.assign({},x,{params:Object.assign({},o,x.params),pathname:Un([l,i.encodeLocation?i.encodeLocation(x.pathname).pathname:x.pathname]),pathnameBase:x.pathnameBase==="/"?l:Un([l,i.encodeLocation?i.encodeLocation(x.pathnameBase).pathname:x.pathnameBase])})),s,n,r)}function ZS(){let e=Vg(),t=Aa(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,i={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"};return C.createElement(C.Fragment,null,C.createElement("h2",null,"Unexpected Application Error!"),C.createElement("h3",{style:{fontStyle:"italic"}},t),n?C.createElement("pre",{style:i},n):null,null)}const KS=C.createElement(ZS,null);class GS extends C.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?C.createElement(Yn.Provider,{value:this.props.routeContext},C.createElement(Fg.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function qS(e){let{routeContext:t,match:n,children:r}=e,i=C.useContext(qa);return i&&i.static&&i.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(i.staticContext._deepestRenderedBoundaryId=n.route.id),C.createElement(Yn.Provider,{value:t},r)}function YS(e,t,n,r){var i;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var s;if(!n)return null;if(n.errors)e=n.matches;else if((s=r)!=null&&s.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let a=e,o=(i=n)==null?void 0:i.errors;if(o!=null){let d=a.findIndex(c=>c.route.id&&(o==null?void 0:o[c.route.id])!==void 0);d>=0||le(!1),a=a.slice(0,Math.min(a.length,d+1))}let l=!1,u=-1;if(n&&r&&r.v7_partialHydration)for(let d=0;d=0?a=a.slice(0,u+1):a=[a[0]];break}}}return a.reduceRight((d,c,f)=>{let m,g=!1,x=null,T=null;n&&(m=o&&c.route.id?o[c.route.id]:void 0,x=c.route.errorElement||KS,l&&(u<0&&f===0?(n2("route-fallback"),g=!0,T=null):u===f&&(g=!0,T=c.route.hydrateFallbackElement||null)));let v=t.concat(a.slice(0,f+1)),p=()=>{let y;return m?y=x:g?y=T:c.route.Component?y=C.createElement(c.route.Component,null):c.route.element?y=c.route.element:y=d,C.createElement(qS,{match:c,routeContext:{outlet:d,matches:v,isDataRoute:n!=null},children:y})};return n&&(c.route.ErrorBoundary||c.route.errorElement||f===0)?C.createElement(GS,{location:n.location,revalidation:n.revalidation,component:x,error:m,children:p(),routeContext:{outlet:null,matches:v,isDataRoute:!0}}):p()},null)}var Bg=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(Bg||{}),Ug=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(Ug||{});function XS(e){let t=C.useContext(qa);return t||le(!1),t}function JS(e){let t=C.useContext(Df);return t||le(!1),t}function e2(e){let t=C.useContext(Yn);return t||le(!1),t}function Hg(e){let t=e2(),n=t.matches[t.matches.length-1];return n.route.id||le(!1),n.route.id}function Vg(){var e;let t=C.useContext(Fg),n=JS(Ug.UseRouteError),r=Hg();return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}function t2(){let{router:e}=XS(Bg.UseNavigateStable),t=Hg(),n=C.useRef(!1);return zg(()=>{n.current=!0}),C.useCallback(function(i,s){s===void 0&&(s={}),n.current&&(typeof i=="number"?e.navigate(i):e.navigate(i,Ml({fromRouteId:t},s)))},[e,t])}const pm={};function n2(e,t,n){pm[e]||(pm[e]=!0)}function r2(e,t){e==null||e.v7_startTransition,(e==null?void 0:e.v7_relativeSplatPath)===void 0&&(!t||t.v7_relativeSplatPath),t&&(t.v7_fetcherPersist,t.v7_normalizeFormMethod,t.v7_partialHydration,t.v7_skipActionErrorRevalidation)}function i2(e){return QS(e.context)}function On(e){le(!1)}function s2(e){let{basename:t="/",children:n=null,location:r,navigationType:i=We.Pop,navigator:s,static:a=!1,future:o}=e;Ya()&&le(!1);let l=t.replace(/^\/*/,"/"),u=C.u)DELIM" - R"DELIM(seMemo(()=>({basename:l,navigator:s,static:a,future:Ml({v7_relativeSplatPath:!1},o)}),[l,o,s,a]);typeof r=="string"&&(r=Ur(r));let{pathname:d="/",search:c="",hash:f="",state:m=null,key:g="default"}=r,x=C.useMemo(()=>{let T=Kn(d,l);return T==null?null:{location:{pathname:T,search:c,hash:f,state:m,key:g},navigationType:i}},[l,d,c,f,m,g,i]);return x==null?null:C.createElement(Hr.Provider,{value:u},C.createElement(Af.Provider,{children:n,value:x}))}new Promise(()=>{});function yd(e,t){t===void 0&&(t=[]);let n=[];return C.Children.forEach(e,(r,i)=>{if(!C.isValidElement(r))return;let s=[...t,i];if(r.type===C.Fragment){n.push.apply(n,yd(r.props.children,s));return}r.type!==On&&le(!1),!r.props.index||!r.props.children||le(!1);let a={id:r.props.id||s.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(a.children=yd(r.props.children,s)),n.push(a)}),n}function a2(e){let t={hasErrorBoundary:e.ErrorBoundary!=null||e.errorElement!=null};return e.Component&&Object.assign(t,{element:C.createElement(e.Component),Component:void 0}),e.HydrateFallback&&Object.assign(t,{hydrateFallbackElement:C.createElement(e.HydrateFallback),HydrateFallback:void 0}),e.ErrorBoundary&&Object.assign(t,{errorElement:C.createElement(e.ErrorBoundary),ErrorBoundary:void 0}),t}/** - * React Router DOM v6.30.2 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */function Es(){return Es=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0)&&(n[i]=e[i]);return n}function o2(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function l2(e,t){return e.button===0&&(!t||t==="_self")&&!o2(e)}function gd(e){return e===void 0&&(e=""),new URLSearchParams(typeof e=="string"||Array.isArray(e)||e instanceof URLSearchParams?e:Object.keys(e).reduce((t,n)=>{let r=e[n];return t.concat(Array.isArray(r)?r.map(i=>[n,i]):[[n,r]])},[]))}function u2(e,t){let n=gd(e);return t&&t.forEach((r,i)=>{n.has(i)||t.getAll(i).forEach(s=>{n.append(i,s)})}),n}const c2=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],d2=["aria-current","caseSensitive","className","end","style","to","viewTransition","children"],f2="6";try{window.__reactRouterVersion=f2}catch{}function h2(e,t){return _S({basename:void 0,future:Es({},void 0,{v7_prependBasename:!0}),history:Q_({window:void 0}),hydrationData:p2(),routes:e,mapRouteProperties:a2,dataStrategy:void 0,patchRoutesOnNavigation:void 0,window:void 0}).initialize()}function p2(){var e;let t=(e=window)==null?void 0:e.__staticRouterHydrationData;return t&&t.errors&&(t=Es({},t,{errors:m2(t.errors)})),t}function m2(e){if(!e)return null;let t=Object.entries(e),n={};for(let[r,i]of t)if(i&&i.__type==="RouteErrorResponse")n[r]=new jl(i.status,i.statusText,i.data,i.internal===!0);else if(i&&i.__type==="Error"){if(i.__subType){let s=window[i.__subType];if(typeof s=="function")try{let a=new s(i.message);a.stack="",n[r]=a}catch{}}if(n[r]==null){let s=new Error(i.message);s.stack="",n[r]=s}}else n[r]=i;return n}const Wg=C.createContext({isTransitioning:!1}),v2=C.createContext(new Map),y2="startTransition",mm=z0[y2],g2="flushSync",vm=X1[g2];function x2(e){mm?mm(e):e()}function Qs(e){vm?vm(e):e()}class w2{constructor(){this.status="pending",this.promise=new Promise((t,n)=>{this.resolve=r=>{this.status==="pending"&&(this.status="resolved",t(r))},this.reject=r=>{this.status==="pending"&&(this.status="rejected",n(r))}})}}function k2(e){let{fallbackElement:t,router:n,future:r}=e,[i,s]=C.useState(n.state),[a,o]=C.useState(),[l,u]=C.useState({isTransitioning:!1}),[d,c]=C.useState(),[f,m]=C.useState(),[g,x]=C.useState(),T=C.useRef(new Map),{v7_startTransition:v}=r||{},p=C.useCallback(j=>{v?x2(j):j()},[v]),y=C.useCallback((j,W)=>{let{deletedFetchers:P,flushSync:B,viewTransitionOpts:ue}=W;j.fetchers.forEach((Z,F)=>{Z.data!==void 0&&T.current.set(F,Z.data)}),P.forEach(Z=>T.current.delete(Z));let be=n.window==null||n.window.document==null||typeof n.window.document.startViewTransition!="function";if(!ue||be){B?Qs(()=>s(j)):p(()=>s(j));return}if(B){Qs(()=>{f&&(d&&d.resolve(),f.skipTransition()),u({isTransitioning:!0,flushSync:!0,currentLocation:ue.currentLocation,nextLocation:ue.nextLocation})});let Z=n.window.document.startViewTransition(()=>{Qs(()DELIM" - R"DELIM()=>s(j))});Z.finished.finally(()=>{Qs(()=>{c(void 0),m(void 0),o(void 0),u({isTransitioning:!1})})}),Qs(()=>m(Z));return}f?(d&&d.resolve(),f.skipTransition(),x({state:j,currentLocation:ue.currentLocation,nextLocation:ue.nextLocation})):(o(j),u({isTransitioning:!0,flushSync:!1,currentLocation:ue.currentLocation,nextLocation:ue.nextLocation}))},[n.window,f,d,T,p]);C.useLayoutEffect(()=>n.subscribe(y),[n,y]),C.useEffect(()=>{l.isTransitioning&&!l.flushSync&&c(new w2)},[l]),C.useEffect(()=>{if(d&&a&&n.window){let j=a,W=d.promise,P=n.window.document.startViewTransition(async()=>{p(()=>s(j)),await W});P.finished.finally(()=>{c(void 0),m(void 0),o(void 0),u({isTransitioning:!1})}),m(P)}},[p,a,d,n.window]),C.useEffect(()=>{d&&a&&i.location.key===a.location.key&&d.resolve()},[d,f,i.location,a]),C.useEffect(()=>{!l.isTransitioning&&g&&(o(g.state),u({isTransitioning:!0,flushSync:!1,currentLocation:g.currentLocation,nextLocation:g.nextLocation}),x(void 0))},[l.isTransitioning,g]),C.useEffect(()=>{},[]);let S=C.useMemo(()=>({createHref:n.createHref,encodeLocation:n.encodeLocation,go:j=>n.navigate(j),push:(j,W,P)=>n.navigate(j,{state:W,preventScrollReset:P==null?void 0:P.preventScrollReset}),replace:(j,W,P)=>n.navigate(j,{replace:!0,state:W,preventScrollReset:P==null?void 0:P.preventScrollReset})}),[n]),R=n.basename||"/",L=C.useMemo(()=>({router:n,navigator:S,static:!1,basename:R}),[n,S,R]),k=C.useMemo(()=>({v7_relativeSplatPath:n.future.v7_relativeSplatPath}),[n.future.v7_relativeSplatPath]);return C.useEffect(()=>r2(r,n.future),[r,n.future]),C.createElement(C.Fragment,null,C.createElement(qa.Provider,{value:L},C.createElement(Df.Provider,{value:i},C.createElement(v2.Provider,{value:T.current},C.createElement(Wg.Provider,{value:l},C.createElement(s2,{basename:R,location:i.location,navigationType:i.historyAction,navigator:S,future:k},i.initialized||n.future.v7_partialHydration?C.createElement(_2,{routes:n.routes,future:n.future,state:i}):t))))),null)}const _2=C.memo(S2);function S2(e){let{routes:t,future:n,state:r}=e;return WS(t,void 0,r,n)}const E2=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",C2=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Ye=C.forwardRef(function(t,n){let{onClick:r,relative:i,reloadDocument:s,replace:a,state:o,target:l,to:u,preventScrollReset:d,viewTransition:c}=t,f=Qg(t,c2),{basename:m}=C.useContext(Hr),g,x=!1;if(typeof u=="string"&&C2.test(u)&&(g=u,E2))try{let y=new URL(window.location.href),S=u.startsWith("//")?new URL(y.protocol+u):new URL(u),R=Kn(S.pathname,m);S.origin===y.origin&&R!=null?u=R+S.search+S.hash:x=!0}catch{}let T=US(u,{relative:i}),v=b2(u,{replace:a,state:o,target:l,preventScrollReset:d,relative:i,viewTransition:c});function p(y){r&&r(y),y.defaultPrevented||v(y)}return C.createElement("a",Es({},f,{href:g||T,onClick:x||s?r:p,ref:n,target:l}))}),Zu=C.forwardRef(function(t,n){let{"aria-current":r="page",caseSensitive:i=!1,className:s="",end:a=!1,style:o,to:l,viewTransition:u,children:d}=t,c=Qg(t,d2),f=tu(l,{relative:c.relative}),m=Ns(),g=C.useContext(Df),{navigator:x,basename:T}=C.useContext(Hr),v=g!=null&&P2(f)&&u===!0,p=x.encodeLocation?x.encodeLocation(f).pathname:f.pathname,y=m.pathname,S=g&&g.navigation&&g.navigation.location?g.navigation.location.pathname:null;i||(y=y.toLowerCase(),S=S?S.toLowerCase():null,p=p.toLowerCase()),S&&T&&(S=Kn(S,T)||S);const R=p!=="/"&&p.endsWith("/")?p.length-1:p.length;let L=y===p||!a&&y.startsWith(p)&&y.charAt(R)==="/",k=S!=null&&(S===p||!a&&S.startsWith(p)&&S.charAt(p.length)==="/"),j={isActive:L,isPending:k,isTransitioning:v},W=L?r:void 0,P;typeof s=="function"?P=s(j):P=[s,L?"active":null,k?"pending":null,v?"transitioning":null].filter(Boolean).join(" ");let B=typeof o=="function"?o(j):o;return C.createElement(Ye,Es({},c,{"aria-current":W,className:P,ref:n,style:B,to:l,viewTransition:u}),typeof d=="function"?d(j):d)});var xd;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(xd||(xd={}));var ym;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(ym||(ym={}));function T2(e){let t=C.useContext(qa);return t||le(!1),t}function b2(e,t){let{target:n,replace:r,state:i,preventScrollReset:s,relative:a,viewTransition:o}=t===void 0?{}:t,l=eu(),u=Ns(),d=tu(e,{relative:a});return C.useCallback(c=>{if(l2(c,n)){c.preventDefault();let f=r!==void 0?r:Ci(u)===Ci(d);l(e,{replace:f,state:i,preventScrollReset:s,relative:a,viewTransition:o})}},[u,l,d,r,i,n,e,s,a,o])}function N2(e){let t=C.useRef(gd(e)),n=C.useRef(!1),r=Ns(),i=C.useMemo(()=>u2(r.search,n.current?null:t.current),[r.search]),s=eu(),a=C.useCallback((o,l)=>{const u=gd(typeof o=="function"?o(i):o);n.current=!0,s("?"+u,l)},[s,i]);return[i,a]}function P2(e,t){t===void 0&&(t={});let n=C.useContext(Wg);n==null&&le(!1);let{basename:r}=T2(xd.useViewTransitionState),i=tu(e,{relativ)DELIM" - R"DELIM(e:t.relative});if(!n.isTransitioning)return!1;let s=Kn(n.currentLocation.pathname,r)||n.currentLocation.pathname,a=Kn(n.nextLocation.pathname,r)||n.nextLocation.pathname;return Rl(i.pathname,a)!=null||Rl(i.pathname,s)!=null}function R2({title:e,titleId:t,...n},r){return C.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor","aria-hidden":"true","data-slot":"icon",ref:r,"aria-labelledby":t},n),e?C.createElement("title",{id:t},e):null,C.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"}))}const j2=C.forwardRef(R2);function M2(){const[e,t]=C.useState(""),n=eu(),r=i=>{i.preventDefault();const s=e.trim();s&&n({pathname:"/search",search:`?q=${encodeURIComponent(s)}`})};return h.jsxs("form",{onSubmit:r,className:"flex w-full items-center gap-2 rounded-xl border border-accent/30 bg-surface/80 px-3 py-2 text-sm shadow-inner md:w-auto",children:[h.jsx(j2,{className:"h-4 w-4 text-accent","aria-hidden":"true"}),h.jsx("input",{type:"search",placeholder:"Search blocks, transactions, addresses",className:"w-full bg-transparent text-textPrimary placeholder:text-textMuted focus:outline-none md:w-72",value:e,onChange:i=>t(i.target.value)})]})}const L2={connected:"bg-emerald-500/20 text-emerald-300 border-emerald-500/50",degraded:"bg-amber-500/20 text-amber-300 border-amber-500/50",offline:"bg-rose-500/20 text-rose-300 border-rose-500/50",unknown:"bg-slate-500/20 text-slate-300 border-slate-500/50"},O2={connected:"bg-emerald-400",degraded:"bg-amber-400",offline:"bg-rose-400",unknown:"bg-slate-400"};function D2({status:e,label:t,title:n}){return h.jsxs("span",{title:n,className:`flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium uppercase tracking-wide ${L2[e]}`,children:[h.jsx("span",{className:`h-2 w-2 rounded-full ${O2[e]}`,"aria-hidden":"true"}),t]})}function A2(){const e=$_(),{provider:t,setProvider:n,availableProviders:r}=F_(),{turbo:i,setTurbo:s}=z_();return h.jsxs("div",{className:"min-h-screen bg-background text-textPrimary",children:[h.jsx("header",{className:"border-b border-accent/30 bg-surface/90 backdrop-blur",children:h.jsx("div",{className:"mx-auto max-w-7xl px-4 py-4",children:h.jsxs("div",{className:"flex flex-wrap items-center gap-4",children:[h.jsx(Zu,{to:"/",className:"order-1 text-lg font-semibold tracking-tight text-accent",children:"libbitcoin Explorer"}),h.jsxs("nav",{className:"order-2 hidden items-center gap-3 text-sm text-textMuted md:flex",children:[h.jsx(Zu,{to:"/",className:({isActive:a})=>`rounded-md px-3 py-1 transition text-textMuted hover:text-accent ${a?"bg-accentSecondary/20 text-accent":""}`,end:!0,children:"Dashboard"}),h.jsx(Zu,{to:"/block",className:({isActive:a})=>`rounded-md px-3 py-1 transition text-textMuted hover:text-accent ${a?"bg-accentSecondary/20 text-accent":""}`,children:"Blocks"})]}),h.jsx("div",{className:"order-2 flex-1 min-w-[12rem] md:order-4 md:flex-none",children:h.jsx(M2,{})}),h.jsxs("div",{className:"order-3 flex w-full flex-wrap items-center gap-3 md:order-3 md:w-auto md:flex-nowrap md:ml-auto",children:[h.jsx(D2,{label:e.label,status:e.connectionState,title:e.description}),t==="libbitcoin"&&h.jsxs("label",{className:"flex items-center gap-2 text-xs uppercase tracking-wide text-textMuted",children:[h.jsx("input",{type:"checkbox",className:"h-4 w-4 rounded border-accent/50 bg-surface/80 text-accent focus:ring-accent",checked:i,onChange:a=>s(a.target.checked)}),"Turbo"]}),h.jsx("select",{value:t,onChange:a=>n(a.target.value),className:"rounded-md border border-accent/40 bg-surface/80 px-2 py-1 text-xs uppercase tracking-wide text-textPrimary hover:border-accent focus:border-accent focus:outline-none",title:"Switch data provider",children:r.map(a=>{const o=a==="libbitcoin"?"server":a;return h.jsx("option",{value:a,children:o},a)})})]})]})})}),h.jsx("main",{className:"mx-auto min-h-[calc(100vh-4rem)] max-w-7xl px-4 py-8",children:h.jsx(i2,{})})]})}function I2(e=10){const t=qn();return ql({queryKey:["blocks","latest",{limit:e}],queryFn:()=>t.getLatestBlocks(e)})}function Zg(e){const t=qn();return ql({queryKey:["block",e],queryFn:()=>t.getBlock(e)})}function $2(e){const t=qn();return ql({queryKey:["transaction",e],queryFn:()=>t.getTransaction(e)})}function F2(e){const t=qn();return ql({queryKey:["address",e],queryFn:()=>t.getAddress(e)})}function z2(e){const t=qn();C.useEffect(()=>{const n=t.subscribeToEvents(e);return()=>n()},[t,e])}function B2(e=10){const t=eg(),{data:n,...r}=I2(e);C.useEffect(()=>{t.getQueryData(["transactions","recent"])||t.setQueryData(["transactions","recent"],[])},[t]);const i=C.useCallback(a=>{a.type==="block.new"&&t.setQueryData(["blocks","latest",{limit:e}],(o=[])=>[a.payload,...o.filter(u=>u.hash!==a.payload.hash)].slice(0,e)),a.type==="tx.new"&&t.setQueryData(["transactions","recent"],(o=[])=>[a.payload,...o.filter(u=>u.txid!==a.payload.)DELIM" - R"DELIM(txid)].slice(0,e*2))},[e,t]);z2(i);const s=t.getQueryData(["transactions","recent"]);return{data:n,recentTransactions:s,...r}}function Xe(e,t={}){return e==null?"—":new Intl.NumberFormat("en-US",{maximumFractionDigits:2,...t}).format(e)}function U2(e,t=8){return e?e.length<=t*2?e:`${e.slice(0,t)}…${e.slice(-t)}`:"—"}function mi(e){if(e!=null)return e/1e8}function Ji(e){return e==null?"—":`${Xe(e)} sats`}function vi(e){return e==null?"—":`${Xe(e,{minimumFractionDigits:8,maximumFractionDigits:8})} BTC`}function $f(e){return e?new Date(e*1e3).toLocaleString():"—"}function nu(e){if(!e)return"—";const t=new Intl.RelativeTimeFormat("en",{numeric:"auto"}),n=Date.now(),r=Math.round((e*1e3-n)/1e3);if(Math.abs(r)<60)return t.format(r,"second");const s=Math.round(r/60);if(Math.abs(s)<60)return t.format(s,"minute");const a=Math.round(s/60);if(Math.abs(a)<24)return t.format(a,"hour");const o=Math.round(a/24);return t.format(o,"day")}function Ku(e){return e==null?"—":`0x${e.toString(16)}`}function Or({value:e,truncate:t=8,className:n}){return h.jsxs("span",{className:n,children:[h.jsx("span",{className:"inline md:hidden",children:U2(e,t)}),h.jsx("span",{className:"hidden md:inline break-all",children:e})]})}function H2(){const{data:e,isLoading:t,recentTransactions:n}=B2(8),{data:r,isLoading:i}=Zg("latest"),s=C.useMemo(()=>{var a;return n!=null&&n.length?n.slice(0,10):(a=r==null?void 0:r.transactions)!=null&&a.length?r.transactions.slice(0,10):[]},[r,n]);return h.jsxs("div",{className:"flex flex-col gap-10",children:[h.jsxs("section",{children:[h.jsx("header",{className:"mb-4 flex items-center justify-between",children:h.jsx("h2",{className:"text-lg font-semibold text-accent",children:"Latest Blocks"})}),h.jsx("div",{className:"overflow-hidden rounded-xl border border-accent/30",children:h.jsxs("table",{className:"min-w-full divide-y divide-accentSecondary/30 text-sm",children:[h.jsx("thead",{className:"bg-accentSecondary/20 text-xs uppercase text-textMuted",children:h.jsxs("tr",{children:[h.jsx("th",{className:"px-4 py-3 text-left",children:"Height"}),h.jsx("th",{className:"px-4 py-3 text-left",children:"Hash"}),h.jsx("th",{className:"px-4 py-3 text-left",children:"Age"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Tx"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Size"})]})}),h.jsxs("tbody",{children:[t&&h.jsx("tr",{children:h.jsx("td",{colSpan:5,className:"px-4 py-6 text-center text-textMuted",children:"Loading latest blocks…"})}),!t&&(e==null?void 0:e.length)===0&&h.jsx("tr",{children:h.jsx("td",{colSpan:5,className:"px-4 py-6 text-center text-textMuted",children:"No blocks available."})}),!t&&(e==null?void 0:e.map(a=>h.jsxs("tr",{className:"hover:bg-accentSecondary/10",children:[h.jsx("td",{className:"px-4 py-3",children:h.jsx(Ye,{to:`/block/${a.hash}`,className:"font-medium text-accent hover:text-accentSecondary",children:Xe(a.height,{maximumFractionDigits:0})})}),h.jsx("td",{className:"px-4 py-3 text-textPrimary",children:h.jsx(Or,{value:a.hash,className:"font-mono"})}),h.jsx("td",{className:"px-4 py-3 text-textMuted",children:nu(a.timestamp)}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:Xe(a.txCount)}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:a.size?`${Xe(a.size/1e6,{maximumFractionDigits:2})} MB`:"—"})]},a.hash)))]})]})})]}),h.jsxs("section",{children:[h.jsx("header",{className:"mb-4 flex items-center justify-between",children:h.jsx("h2",{className:"text-lg font-semibold text-accent",children:"Recent Transactions"})}),h.jsx("div",{className:"overflow-hidden rounded-xl border border-accent/30",children:h.jsxs("table",{className:"min-w-full divide-y divide-accentSecondary/30 text-sm",children:[h.jsx("thead",{className:"bg-accentSecondary/20 text-xs uppercase text-textMuted",children:h.jsxs("tr",{children:[h.jsx("th",{className:"px-4 py-3 text-left",children:"TxID"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Output"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Fee"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Weight"})]})}),h.jsxs("tbody",{children:[i&&h.jsx("tr",{children:h.jsx("td",{colSpan:4,className:"px-4 py-6 text-center text-textMuted",children:"Loading transactions…"})}),!i&&s.length===0&&h.jsx("tr",{children:h.jsx("td",{colSpan:4,className:"px-4 py-6 text-center text-textMuted",children:"No transactions available."})}),!i&&s.map(a=>h.jsxs("tr",{className:"hover:bg-accentSecondary/10",children:[h.jsx("td",{className:"px-4 py-3",children:h.jsx(Ye,{to:`/tx/${a.txid}`,className:"font-mono text-accent hover:text-accentSecondary",children:h.jsx(Or,{value:a.txid,truncate:10})})}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:vi(mi(a.totalOutput))}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:Ji(a.feeSats)}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:Xe(a.weight)})]},a.txid))]})]})})]})]})}function V2(){const{hash:e}=If(),)DELIM" - R"DELIM(t=e??"latest",{data:n,isLoading:r,error:i}=Zg(t),s=qn(),[a,o]=C.useState([]),[l,u]=C.useState(!1),[d,c]=C.useState(!1);C.useEffect(()=>{n&&(o(n.transactions??[]),c(!1))},[n]);const f=n?!d&&a.length<(n.txCount??a.length):!1,m=async()=>{if(!(!n||!f||l)){u(!0);try{const g=await s.getBlockTransactions(n.hash,a.length);if(g.length===0){c(!0);return}o(x=>{const T=new Set(x.map(p=>p.txid)),v=[...x];return g.forEach(p=>{T.has(p.txid)||v.push(p)}),v.length===x.length?(c(!0),x):v})}finally{u(!1)}}};return r?h.jsx("div",{className:"py-10 text-textMuted",children:"Loading block details…"}):i||!n?h.jsxs("div",{className:"space-y-4 py-10 text-textPrimary",children:[h.jsx("h1",{className:"text-2xl font-semibold text-accent",children:"Block not found"}),h.jsxs("p",{className:"text-textMuted",children:["We could not find a block matching identifier “",t,"”."]}),h.jsx(Ye,{to:"/",className:"text-accent hover:text-accentSecondary",children:"Return to dashboard"})]}):h.jsxs("div",{className:"space-y-8",children:[h.jsxs("header",{className:"flex flex-wrap items-end justify-between gap-4",children:[h.jsxs("div",{children:[h.jsx("p",{className:"text-sm uppercase tracking-wide text-accentSecondary",children:"Block"}),h.jsxs("h1",{className:"text-3xl font-semibold text-accent",children:["#",n.height]}),h.jsx("p",{className:"mt-2 font-mono text-xs text-textPrimary break-all",children:n.hash}),h.jsxs("p",{className:"text-sm text-textMuted",children:[nu(n.timestamp)," · ",$f(n.timestamp)]})]}),h.jsxs("div",{className:"flex items-center gap-3",children:[n.previousBlockHash&&h.jsx(Ye,{to:`/block/${n.previousBlockHash}`,className:"rounded-md border border-accent/40 px-3 py-2 text-sm text-textPrimary hover:border-accent hover:text-accent",children:"← Previous"}),n.nextBlockHash&&h.jsx(Ye,{to:`/block/${n.nextBlockHash}`,className:"rounded-md border border-accent/40 px-3 py-2 text-sm text-textPrimary hover:border-accent hover:text-accent",children:"Next →"})]})]}),h.jsxs("section",{className:"grid gap-4 rounded-xl border border-accent/30 bg-surface/80 p-6 text-sm sm:grid-cols-2 lg:grid-cols-3",children:[h.jsx(rr,{label:"Transactions",value:Xe(n.txCount,{maximumFractionDigits:0})}),h.jsx(rr,{label:"Size",value:n.size?`${Xe(n.size/1e6,{maximumFractionDigits:2})} MB`:"—"}),h.jsx(rr,{label:"Weight units",value:n.weight?`${Xe(n.weight,{maximumFractionDigits:0})} WU`:"—"}),h.jsx(rr,{label:"Difficulty",value:Xe(n.difficulty??void 0)}),h.jsx(rr,{label:"Nonce",value:Ku(n.nonce)}),h.jsx(rr,{label:"Bits",value:Ku(n.bits)}),h.jsx(rr,{label:"Version",value:Ku(n.version)}),h.jsx(rr,{label:"Merkle root",value:h.jsx("span",{className:"font-mono text-xs text-textPrimary break-all",children:n.merkleRoot??"—"})})]}),h.jsxs("section",{children:[h.jsxs("header",{className:"mb-4 flex items-center justify-between",children:[h.jsx("h2",{className:"text-lg font-semibold text-accent",children:"Transactions"}),h.jsxs("p",{className:"text-sm text-textMuted",children:["Showing ",a.length," transactions"]})]}),h.jsx("div",{className:"overflow-hidden rounded-xl border border-accent/30",children:h.jsxs("table",{className:"min-w-full divide-y divide-accentSecondary/30 text-sm",children:[h.jsx("thead",{className:"bg-accentSecondary/20 text-xs uppercase text-textMuted",children:h.jsxs("tr",{children:[h.jsx("th",{className:"px-4 py-3 text-left",children:"TxID"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Outputs"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Fee"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Size"})]})}),h.jsx("tbody",{children:a.map(g=>h.jsxs("tr",{className:"hover:bg-accentSecondary/10",children:[h.jsx("td",{className:"px-4 py-3",children:h.jsx(Ye,{to:`/tx/${g.txid}`,className:"font-mono text-accent hover:text-accentSecondary",children:h.jsx(Or,{value:g.txid,truncate:12})})}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:vi(mi(g.totalOutput))}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:Ji(g.feeSats)}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:g.size?`${Xe(g.size)} bytes`:"—"})]},g.txid))})]})}),f&&h.jsx("div",{className:"mt-4 flex justify-center",children:h.jsx("button",{type:"button",onClick:m,disabled:l,className:"rounded-md border border-accent px-4 py-2 text-sm font-medium text-accent hover:border-accentSecondary hover:text-accentSecondary disabled:cursor-not-allowed disabled:opacity-60",children:l?"Loading more…":"Load more transactions"})})]})]})}function rr({label:e,value:t}){return h.jsxs("div",{className:"space-y-1",children:[h.jsx("p",{className:"text-xs uppercase tracking-wide text-accentSecondary",children:e}),h.jsx("p",{className:"text-sm text-textPrimary",children:t})]})}function Q2(){const{txid:e}=If(),t=e??"",{data:n,isLoading:r,error:i}=$2(t),[s,a]=C.useState({}),[o,l]=C.useState({});if(C.useEffect(()=>{a({}),l({})},[n==null?void 0:n.txid]),!e)return h.jsxs("div",{className:"py-10 text-textPrimary",children:[h.jsx("p",{classNa)DELIM" - R"DELIM(me:"text-textMuted",children:"Transaction ID missing."}),h.jsx(Ye,{to:"/",className:"text-accent hover:text-accentSecondary",children:"Go back"})]});if(r)return h.jsx("div",{className:"py-10 text-textMuted",children:"Loading transaction…"});if(i||!n)return h.jsxs("div",{className:"space-y-4 py-10 text-textPrimary",children:[h.jsx("h1",{className:"text-2xl font-semibold text-accent",children:"Transaction not found"}),h.jsxs("p",{className:"text-textMuted",children:["We could not find a transaction with ID “",t,"”."]}),h.jsx(Ye,{to:"/",className:"text-accent hover:text-accentSecondary",children:"Return to dashboard"})]});const u=vi(mi(n.totalOutput)),d=n.totalInput?vi(mi(n.totalInput)):"—",c=n.feeRate?`${Xe(n.feeRate,{maximumFractionDigits:2})} sat/vB`:"—";return h.jsxs("div",{className:"space-y-8",children:[h.jsxs("header",{className:"space-y-2",children:[h.jsx("p",{className:"text-sm uppercase tracking-wide text-accentSecondary",children:"Transaction"}),h.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[h.jsx("h1",{className:"text-3xl font-semibold text-accent",children:h.jsx(Or,{value:n.txid,truncate:24})}),h.jsx("button",{type:"button",onClick:()=>navigator.clipboard.writeText(n.txid),className:"inline-flex items-center gap-2 rounded-md border border-accent/40 px-3 py-1 text-xs font-medium uppercase tracking-wide text-accent hover:border-accent hover:text-accentSecondary",children:"Copy TxID"})]}),h.jsx("p",{className:"text-sm text-textMuted",children:n.receivedTime?`${nu(n.receivedTime)} · ${$f(n.receivedTime)}`:"Pending"}),h.jsxs("span",{className:`inline-flex w-fit items-center gap-2 rounded-full px-3 py-1 text-xs font-medium uppercase tracking-wide ${n.status==="confirmed"?"bg-emerald-500/20 text-emerald-300":"bg-amber-500/20 text-amber-300"}`,children:[h.jsx("span",{className:"h-2 w-2 rounded-full bg-current","aria-hidden":!0}),n.status]})]}),h.jsxs("section",{className:"grid gap-4 rounded-xl border border-accent/30 bg-surface/80 p-6 text-sm sm:grid-cols-2 lg:grid-cols-3",children:[h.jsx(tn,{label:"Confirmations",value:n.confirmations??0}),h.jsx(tn,{label:"Block height",value:n.blockHeight??"—"}),h.jsx(tn,{label:"Block hash",value:n.blockHash?h.jsx(Ye,{to:`/block/${n.blockHash}`,children:h.jsx(Or,{value:n.blockHash})}):"—"}),h.jsx(tn,{label:"Total output",value:u}),h.jsx(tn,{label:"Total input",value:d}),h.jsx(tn,{label:"Fee",value:Ji(n.feeSats)}),h.jsx(tn,{label:"Fee rate",value:c}),h.jsx(tn,{label:"Size",value:n.size?`${Xe(n.size)} bytes`:"—"}),h.jsx(tn,{label:"Weight units",value:`${Xe(n.weight,{maximumFractionDigits:0})} WU`}),h.jsx(tn,{label:"Version",value:n.version}),h.jsx(tn,{label:"Locktime",value:n.locktime})]}),h.jsxs("section",{className:"grid gap-6 lg:grid-cols-2",children:[h.jsxs("div",{className:"rounded-xl border border-accent/30",children:[h.jsx("header",{className:"border-b border-accentSecondary/30 px-4 py-3",children:h.jsx("h2",{className:"text-sm font-semibold uppercase tracking-wide text-accent",children:"Inputs"})}),h.jsxs("div",{className:"divide-y divide-accentSecondary/30",children:[n.inputs.length===0&&h.jsx("p",{className:"px-4 py-4 text-sm text-textMuted",children:"No inputs (coinbase transaction)"}),n.inputs.map(f=>{var T,v;const m=W2(f),g=K2(f),x=!!s[m];return h.jsxs("div",{className:"px-4 py-3 text-sm",children:[h.jsxs("div",{className:"flex items-start justify-between gap-3",children:[h.jsx("p",{className:"font-mono text-xs text-textPrimary",children:f.isCoinbase?"Coinbase":f.prevTxId?h.jsx(Ye,{to:`/tx/${f.prevTxId}`,className:"text-accent hover:text-accentSecondary",children:h.jsx(Or,{value:f.prevTxId,truncate:16})}):"—"}),g&&h.jsx("button",{type:"button",onClick:()=>a(p=>({...p,[m]:!p[m]})),className:"inline-flex items-center gap-1 rounded-md border border-accent/40 px-2 py-1 text-[11px] font-medium uppercase tracking-wide text-accent hover:border-accent hover:text-accentSecondary",children:x?"Hide details":"Show details"})]}),h.jsxs("div",{className:"mt-1 flex flex-wrap items-center gap-3 text-xs text-textMuted",children:[h.jsxs("span",{children:["Input #",f.index]}),typeof f.prevOutputIndex=="number"&&h.jsxs("span",{children:["Prevout ",f.prevOutputIndex]}),f.address&&h.jsx(Ye,{to:`/address/${f.address}`,className:"text-accent hover:text-accentSecondary",children:f.address}),f.valueSats&&h.jsx("span",{children:Ji(f.valueSats)}),f.scriptType&&h.jsx("span",{children:f.scriptType})]}),x&&h.jsxs("div",{className:"mt-3 space-y-3 rounded-lg border border-accent/20 bg-surface/60 p-3 text-xs",children:[((T=f.script)==null?void 0:T.hex)&&h.jsx(Ws,{label:"Script (hex)",value:f.script.hex}),((v=f.script)==null?void 0:v.mnemonic)&&h.jsx(Ws,{label:"Script (mnemonic)",value:f.script.mnemonic}),f.witness&&f.witness.length>0&&h.jsxs("div",{className:"space-y-1",children:[h.jsx("p",{className:"text-[10px] uppercase tracking-wide text-accentSecondary",children:"Witness"}),h.jsx("div",{className:"space-y-1",children:f.witness.map((p,y)=>h.jsx("code",{className:"bl)DELIM" - R"DELIM(ock break-all rounded bg-black/30 px-2 py-1 font-mono text-[11px] text-textMuted",children:p},`${m}-witness-${y}`))})]}),typeof f.sequence=="number"&&h.jsx(Ws,{label:"Sequence",value:`0x${f.sequence.toString(16).toUpperCase()}`})]})]},m)})]})]}),h.jsxs("div",{className:"rounded-xl border border-accent/30",children:[h.jsx("header",{className:"border-b border-accentSecondary/30 px-4 py-3",children:h.jsx("h2",{className:"text-sm font-semibold uppercase tracking-wide text-accent",children:"Outputs"})}),h.jsx("div",{className:"divide-y divide-accentSecondary/30",children:n.outputs.map(f=>{var T,v;const m=Z2(f),g=G2(f),x=!!o[m];return h.jsxs("div",{className:"px-4 py-3 text-sm",children:[h.jsxs("div",{className:"flex items-start justify-between gap-2",children:[h.jsx("p",{className:"text-xs text-textMuted",children:f.address?h.jsx(Ye,{to:`/address/${f.address}`,className:"text-accent hover:text-accentSecondary",children:f.address}):"Unknown"}),h.jsx("p",{className:"font-medium text-textPrimary",children:Ji(f.valueSats)})]}),h.jsxs("div",{className:"mt-1 flex flex-wrap items-center gap-3 text-xs text-textMuted",children:[h.jsxs("span",{children:["Index ",f.index]}),f.scriptType&&h.jsx("span",{children:f.scriptType}),typeof f.spent=="boolean"&&h.jsx("span",{children:f.spent?"Spent":"Unspent"})]}),g&&h.jsxs(h.Fragment,{children:[h.jsx("button",{type:"button",onClick:()=>l(p=>({...p,[m]:!p[m]})),className:"mt-3 inline-flex items-center gap-1 rounded-md border border-accent/40 px-2 py-1 text-[11px] font-medium uppercase tracking-wide text-accent hover:border-accent hover:text-accentSecondary",children:x?"Hide details":"Show details"}),x&&h.jsxs("div",{className:"mt-3 space-y-3 rounded-lg border border-accent/20 bg-surface/60 p-3 text-xs",children:[((T=f.script)==null?void 0:T.hex)&&h.jsx(Ws,{label:"Script (hex)",value:f.script.hex}),((v=f.script)==null?void 0:v.mnemonic)&&h.jsx(Ws,{label:"Script (mnemonic)",value:f.script.mnemonic})]})]})]},m)})})]})]})]})}function tn({label:e,value:t}){return h.jsxs("div",{className:"space-y-1",children:[h.jsx("p",{className:"text-xs uppercase tracking-wide text-accentSecondary",children:e}),h.jsx("p",{className:"text-sm text-textPrimary",children:t})]})}function W2(e){return`${e.prevTxId??"coinbase"}-${e.index}`}function Z2(e){return`output-${e.index}`}function K2(e){return!!(e.script&&(e.script.hex||e.script.mnemonic)||e.witness&&e.witness.length>0||typeof e.sequence=="number")}function G2(e){return!!(e.script&&(e.script.hex||e.script.mnemonic))}function Ws({label:e,value:t}){return h.jsxs("div",{className:"space-y-1",children:[h.jsx("p",{className:"text-[10px] uppercase tracking-wide text-accentSecondary",children:e}),h.jsx("code",{className:"block break-all rounded bg-black/30 px-2 py-1 font-mono text-[11px] text-textMuted",children:t})]})}var ru={},Kg={exports:{}},q2="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED",Y2=q2,X2=Y2;function Gg(){}function qg(){}qg.resetWarningCache=Gg;var J2=function(){function e(r,i,s,a,o,l){if(l!==X2){var u=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw u.name="Invariant Violation",u}}e.isRequired=e;function t(){return e}var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:qg,resetWarningCache:Gg};return n.PropTypes=n,n};Kg.exports=J2();var Yg=Kg.exports,Xg={L:1,M:0,Q:3,H:2},Jg={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},e4=Jg;function e0(e){this.mode=e4.MODE_8BIT_BYTE,this.data=e}e0.prototype={getLength:function(e){return this.data.length},write:function(e){for(var t=0;t>>7-e%8&1)==1},put:function(e,t){for(var n=0;n>>t-n-1&1)==1)},getLengthInBits:function(){return this.length},putBit:function(e){var t=Math.floor(this.length/8);this.buffer.length<=t&&this.buffer.push(0),e&&(this.buffer[t]|=128>>>this.length%8),this.length++}};var r4=t0,un={glog:function(e){if(e<1)throw new Error("glog("+e+")");return un.LOG_TABLE[e]},gexp:function(e){for(;e<0;)e+=255;for(;e>=256;)e-=255;return un.EXP_TABLE[e]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var at=0;at<8;at++)un.EXP_TABLE[at]=1<=0;)t^=it.G15<=0;)t^=it.G18<>>=1;return t},getPatternPosition:function(e){return it.PATTERN_POSITION_TABLE[e-1]},getMask:function(e,t,n){switch(e){case ir.PATTERN000:return(t+n)%2==0;case ir.PATTERN001:return t%2==0;case ir.PATTERN010:return n%3==0;case ir.PATTERN011:return(t+n)%3==0;case ir.PATTERN100:return(Math.floor(t/2)+Math.floor(n/3))%2==0;case ir.PATTERN101:return t*n%2+t*n%3==0;case ir.PATTERN110:return(t*n%2+t*n%3)%2==0;case ir.PATTERN111:return(t*n%3+(t+n)%2)%2==0;default:throw new Error("bad maskPattern:"+e)}},getErrorCorrectPolynomial:function(e){for(var t=new gm([1],0),n=0;n5&&(n+=3+s-5)}for(var r=0;r=7&&this.setupTypeNumber(e),this.dataCache==null&&(this.dataCache=En.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,t)};Ft.setupPositionProbePattern=function(e,t){for(var n=-1;n<=7;n++)if(!(e+n<=-1||this.moduleCount<=e+n))for(var r=-1;r<=7;r++)t+r<=-1||this.moduleCount<=t+r||(0<=n&&n<=6&&(r==0||r==6)||0<=r&&r<=6&&(n==0||n==6)||2<=n&&n<=4&&2<=r&&r<=4?this.modules[e+n][t+r]=!0:this.modules[e+n][t+r]=!1)};Ft.getBestMaskPattern=function(){for(var e=0,t=0,n=0;n<8;n++){this.makeImpl(!0,n);var r=Vr.getLostPoint(this);(n==0||e>r)&&(e=r,t=n)}return t};Ft.createMovieClip=function(e,t,n){var r=e.createEmptyMovieClip(t,n),i=1;this.make();for(var s=0;s>n&1)==1;this.modules[Math.floor(n/3)][n%3+this.moduleCount-8-3]=r}for(var n=0;n<18;n++){var r=!e&&(t>>n&1)==1;this.modules[n%3+this.moduleCount-8-3][Math.floor(n/3)]=r}};Ft.setupTypeInfo=function(e,t){for(var n=this.errorCorrectLevel<<3|t,r=Vr.getBCHTypeInfo(n),i=0;i<15;i++){var s=!e&&(r>>i&1)==1;i<6?this.modules[i][8]=s:i<8?this.modules[i+1][8]=s:this.modules[this.moduleCount-15+i][8]=s}for(var i=0;i<15;i++){var s=!e&&(r>>i&1)==1;i<8?this.modules[8][this.moduleCount-i-1]=s:i<9?this.modules[8][15-i-1+1]=s:this.modules[8][15-i-1]=s}this.modules[this.moduleCount-8][8]=!e};Ft.mapData=function(e,t){for(var n=-1,r=this.moduleCount-1,i=7,s=0,a=this.moduleCount-1;a>0;a-=2)for(a==6&&a--;;){for(var o=0;o<2;o++)if(this.modules[r][a-o]==null){var l=!1;s>>i&1)==1);var u=Vr.getMask(t,r,a-o);u&&(l=!l),this.modules[r][a-o]=l,i--,i==-1&&(s++,i=7)}if(r+=n,r<0||this.moduleCount<=r){r-=n,n=-n;break}}};En.PAD0=236;En.PAD1=17;En.createData=function(e,t,n){for(var r=i0.getRSBlocks(e,t),i=new s0,s=0;so*8)throw new Error("code length overflow. ("+i.getLengthInBits()+">"+o*8+")");for(i.getLengthInBits()+4<=o*8&&i.put(0,4);i.getLengthInBits()%8!=0;)i.putBit(!1);for(;!(i.getLengthInBits()>=o*8||(i.put(En.PAD0,8),i.getLengthInBits()>=o*8));)i.put(En.PAD1,8);return En.createBytes(i,r)};En.createBytes=function(e,t){for(var n=0,r=0,i=0,s=new Array(t.length),a=new Array(t.length),o=0;o=0?m.get(g):0}}for(var x=0,d=0;d=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}var f4={bgColor:Bt.default.oneOfType([Bt.default.object,Bt.default.string]).isRequired,bgD:Bt.default.string.isRequired,fgColor:Bt.default.oneOfType([Bt.default.object,Bt.default.string]).isRequired,fgD:Bt.default.string.isRequired,size:Bt.default.number.isRequired,title:Bt.default.string,viewBoxSize:Bt.default.number.isRequired,xmlns:Bt.default.string},zf=(0,a0.forwardRef)(function(e,t){var n=e.bgColor,r=e.bgD,i=e.fgD,s=e.fgColor,a=e.size,o=e.title,l=e.viewBoxSize,u=e.xmlns,d=u===void 0?"http://www.w3.org/2000/svg":u,c=d4(e,["bgColor","bgD","fgD","fgColor","size","title","viewBoxSize","xmlns"]);return jo.default.createElement("svg",u4({},c,{height:a,ref:t,viewBox:"0 0 "+l+" "+l,width:a,xmlns:d}),o?jo.default.createElement("title",null,o):null,jo.default.createElement("path",{d:r,fill:n}),jo.default.createElement("path",{d:i,fill:s}))});zf.displayName="QRCodeSvg";zf.propTypes=f4;Ff.default=zf;Object.defineProperty(ru,"__esModule",{value:!0});ru.QRCode=void 0;var h4=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}var S4={bgColor:Mn.default.oneOfType([Mn.default.object,Mn.default.string]),fgColor:Mn.default.oneOfType([Mn.default.object,Mn.default.string]),level:Mn.default.string,size:Mn.default.number,value:Mn.default.string.isRequired},iu=(0,l0.forwardRef)(function(e,t){var n=e.bgColor,r=n===void 0?"#FFFFFF":n,i=e.fgColor,s=i===void 0?"#000000":i,a=e.level,o=a===void 0?"L":a,l=e.size,u=l===void 0?256:l,d=e.value,c=_4(e,["bgColor","fgColor","level","size","value"]),f=new g4.default(-1,v4.default[o]);f.addData(d),f.make();var m=f.modules;return x4.default.createElement(k4.default,h4({},c,{bgColor:r,bgD:m.map(function(g,x){return g.map(function(T,v){return T?"":"M "+v+" "+x+" l 1 0 0 1 -1 0 Z"}).join(" ")}).join(" "),fgColor:s,fgD:m.map(function(g,x){return g.map(function(T,v){return T?"M "+v+" "+x+" l 1 0 0 1 -1 0 Z":""}).join(" ")}).join(" "),ref:t,size:u,vi)DELIM" - R"DELIM(ewBoxSize:m.length}))});ru.QRCode=iu;iu.displayName="QRCode";iu.propTypes=S4;var E4=ru.default=iu;function C4({value:e,size:t=128}){return e.trim()?h.jsx("div",{className:"rounded-xl border border-accent/30 bg-surface/60 p-4 shadow-sm",children:h.jsx(E4,{value:e,size:t,style:{height:"auto",maxWidth:"100%",width:"100%"},bgColor:"transparent",fgColor:"#ffa022",level:"Q"})}):null}function T4(){const{address:e}=If(),t=e??"",{data:n,isLoading:r,error:i}=F2(t),s=qn(),[a,o]=C.useState([]),[l,u]=C.useState(null),[d,c]=C.useState(!1),[f,m]=C.useState(!1);C.useEffect(()=>{if(n){o(n.transactions);const p=n.transactions[n.transactions.length-1];u(p?p.txid:null),m(n.transactions.length<(n.txCount??n.transactions.length))}},[n]);const g=async()=>{if(!(!e||!f||d||!n)){if(!l){m(!1);return}c(!0);try{const p=await s.getAddressTransactions(e,l);if(p.length===0){m(!1);return}o(y=>{const S=new Set(y.map(k=>k.txid)),R=[...y];if(p.forEach(k=>{S.has(k.txid)||R.push(k)}),R.length===y.length)return m(!1),y;const L=R[R.length-1];return u(L?L.txid:null),m(R.length<((n==null?void 0:n.txCount)??R.length)),R})}finally{c(!1)}}};if(!e)return h.jsxs("div",{className:"py-10 text-textPrimary",children:[h.jsx("p",{className:"text-textMuted",children:"Address missing."}),h.jsx(Ye,{to:"/",className:"text-accent hover:text-accentSecondary",children:"Go back"})]});if(r)return h.jsx("div",{className:"py-10 text-textMuted",children:"Loading address…"});if(i||!n)return h.jsxs("div",{className:"space-y-4 py-10 text-textPrimary",children:[h.jsx("h1",{className:"text-2xl font-semibold text-accent",children:"Address not found"}),h.jsxs("p",{className:"text-textMuted",children:["We could not find data for address “",t,"”."]}),h.jsx(Ye,{to:"/",className:"text-accent hover:text-accentSecondary",children:"Return to dashboard"})]});const x=vi(mi(n.balance)),T=vi(mi(n.totalReceived)),v=vi(mi(n.totalSent));return h.jsxs("div",{className:"space-y-8",children:[h.jsxs("header",{className:"space-y-3",children:[h.jsx("p",{className:"text-sm uppercase tracking-wide text-accentSecondary",children:"Address"}),h.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[h.jsx("h1",{className:"break-all text-2xl font-semibold text-accent",children:n.address}),h.jsx("button",{type:"button",onClick:()=>navigator.clipboard.writeText(n.address),className:"inline-flex items-center gap-2 rounded-md border border-accent/40 px-3 py-1 text-xs font-medium uppercase tracking-wide text-accent hover:border-accent hover:text-accentSecondary",children:"Copy address"})]})]}),h.jsxs("section",{className:"grid gap-6 lg:grid-cols-[minmax(0,1fr)_auto]",children:[h.jsxs("div",{className:"grid gap-4 rounded-xl border border-accent/30 bg-surface/80 p-6 text-sm sm:grid-cols-2 lg:grid-cols-3",children:[h.jsx(Li,{label:"Balance",value:x}),h.jsx(Li,{label:"Total received",value:T}),h.jsx(Li,{label:"Total sent",value:v}),h.jsx(Li,{label:"Transaction count",value:Xe(n.txCount,{maximumFractionDigits:0})}),h.jsx(Li,{label:"UTXO count",value:Xe(n.utxoCount??void 0,{maximumFractionDigits:0})}),h.jsx(Li,{label:"Address type",value:n.addressType??"—"})]}),h.jsx("div",{className:"place-self-start",children:h.jsx(C4,{value:n.address})})]}),h.jsxs("section",{children:[h.jsxs("header",{className:"mb-4 flex items-center justify-between",children:[h.jsx("h2",{className:"text-lg font-semibold text-accent",children:"Recent Transactions"}),h.jsxs("p",{className:"text-sm text-textMuted",children:["Showing ",a.length," entries"]})]}),h.jsx("div",{className:"overflow-hidden rounded-xl border border-accent/30",children:h.jsxs("table",{className:"min-w-full divide-y divide-accentSecondary/30 text-sm",children:[h.jsx("thead",{className:"bg-accentSecondary/20 text-xs uppercase text-textMuted",children:h.jsxs("tr",{children:[h.jsx("th",{className:"px-4 py-3 text-left",children:"TxID"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Value change"}),h.jsx("th",{className:"px-4 py-3 text-right",children:"Block height"}),h.jsx("th",{className:"px-4 py-3 text-left",children:"Timestamp"})]})}),h.jsxs("tbody",{children:[a.length===0&&h.jsx("tr",{children:h.jsx("td",{colSpan:4,className:"px-4 py-6 text-center text-textMuted",children:"No transactions recorded."})}),a.map(p=>h.jsxs("tr",{className:"hover:bg-accentSecondary/10",children:[h.jsx("td",{className:"px-4 py-3",children:h.jsx(Ye,{to:`/tx/${p.txid}`,className:"font-mono text-accent hover:text-accentSecondary",children:h.jsx(Or,{value:p.txid,truncate:12})})}),h.jsxs("td",{className:"px-4 py-3 text-right text-textPrimary",children:[p.valueChange>=0?"+":"-",Ji(Math.abs(p.valueChange))]}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:p.blockHeight??"Unconfirmed"}),h.jsx("td",{className:"px-4 py-3 text-textPrimary",children:p.timestamp?$f(p.timestamp):"—"})]},`${p.txid}-${p.blockHeight??"mempool"}`))]})]})}),f&&h.jsx("div",{className:"mt-4 flex justify-center",children:h.jsx("button",{type:"button",onCli)DELIM" - R"DELIM(ck:g,disabled:d,className:"rounded-md border border-accent px-4 py-2 text-sm font-medium text-accent hover:border-accentSecondary hover:text-accentSecondary disabled:cursor-not-allowed disabled:opacity-60",children:d?"Loading more…":"Load more transactions"})})]})]})}function Li({label:e,value:t}){return h.jsxs("div",{className:"space-y-1",children:[h.jsx("p",{className:"text-xs uppercase tracking-wide text-accentSecondary",children:e}),h.jsx("p",{className:"text-sm text-textPrimary",children:t})]})}const b4=/^[0-9a-f]+$/,N4=/^[0-9a-fA-F]+$/;function P4(e){var r,i;const t=e.trim();if(!t)return{type:"unknown"};if(/^\d+$/.test(t))return{type:"block-height",value:t.replace(/^0+/,"")||"0"};if(t.length===64&&N4.test(t)){const s=t.toLowerCase();return(((i=(r=s.match(/^0+/))==null?void 0:r[0])==null?void 0:i.length)??0)>=8&&b4.test(s)?{type:"block-hash",value:s}:{type:"txid",value:s}}return Xl(t)?{type:"address",value:t}:{type:"unknown"}}function R4(){const[e]=N2(),t=e.get("q")??"",n=t.trim(),r=eu(),i=C.useMemo(()=>P4(n),[n]);return C.useEffect(()=>{if(!n||!i||i.type==="unknown")return;const s=i.type==="block-height"?`/block/${i.value}`:i.type==="block-hash"?`/block/${i.value}`:i.type==="txid"?`/tx/${i.value}`:`/address/${i.value}`;r(s,{replace:!0})},[i,r,n]),n?i.type==="unknown"?h.jsxs("div",{className:"space-y-4 py-10 text-textPrimary",children:[h.jsx("h1",{className:"text-2xl font-semibold text-accent",children:"Unrecognized query"}),h.jsxs("p",{className:"text-textMuted",children:["We couldn’t determine whether “",t,"” is a block, transaction, or address."]}),h.jsx("p",{className:"text-sm text-textMuted",children:"Try entering a block height (digits only), a 64-character hash, or a valid base58/bech32 address."}),h.jsx(Ye,{to:"/",className:"text-accent hover:text-accentSecondary",children:"Return to dashboard"})]}):h.jsx("div",{className:"py-10 text-textMuted",children:"Redirecting…"}):h.jsx("div",{className:"py-10 text-textPrimary",children:h.jsx("p",{className:"text-textMuted",children:"Enter a block hash, transaction ID, or address in the search bar."})})}function xm(){const e=Vg(),t=(e==null?void 0:e.message)??"This page could not be found.";return h.jsxs("div",{className:"space-y-4 py-10 text-textPrimary",children:[h.jsx("h1",{className:"text-2xl font-semibold text-accent",children:"Page not found"}),h.jsx("p",{className:"text-textMuted",children:t}),h.jsx(Ye,{to:"/",className:"text-accent hover:text-accentSecondary",children:"Return to dashboard"})]})}const Mo=10;function j4(){const e=qn(),[t,n]=C.useState([]),[r,i]=C.useState(!0),[s,a]=C.useState(!1),[o,l]=C.useState(null),[u,d]=C.useState(!0),[c,f]=C.useState(null);C.useEffect(()=>{let g=!0;return(async()=>{i(!0);try{f(null);const T=await e.getLatestBlocks(Mo);if(!g)return;if(n(T),T.length){const v=T[T.length-1].height;l(v-1),d(T.length===Mo)}else d(!1)}catch(T){if(!g)return;f(T instanceof Error?T.message:"Failed to load blocks")}finally{g&&i(!1)}})(),()=>{g=!1}},[e]);const m=async()=>{if(!(s||!u||o===null)){a(!0);try{f(null);const g=await e.getBlocksBefore(o,Mo);if(g.length===0){d(!1);return}n(T=>{const v=new Set(T.map(y=>y.hash)),p=[...T];return g.forEach(y=>{v.has(y.hash)||p.push(y)}),p});const x=g[g.length-1].height;l(x-1),g.lengthh.jsxs("tr",{className:"hover:bg-accentSecondary/10",children:[h.jsx("td",{className:"px-4 py-3",children:h.jsx(Ye,{to:`/block/${g.hash}`,className:"font-medium text-accent hover:text-accentSecondary",children:Xe(g.height,{maximumFractionDigits:0})})}),h.jsx("td",{class)DELIM" - R"DELIM(Name:"px-4 py-3 text-textPrimary",children:h.jsx(Or,{value:g.hash,className:"font-mono"})}),h.jsx("td",{className:"px-4 py-3 text-textMuted",children:nu(g.timestamp)}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:Xe(g.txCount)}),h.jsx("td",{className:"px-4 py-3 text-right text-textPrimary",children:g.size?`${Xe(g.size/1e6,{maximumFractionDigits:2})} MB`:"—"})]},g.hash))]})]})}),u&&!r&&!c&&h.jsx("div",{className:"flex justify-center",children:h.jsx("button",{type:"button",onClick:m,disabled:s,className:"rounded-md border border-accent px-4 py-2 text-sm font-medium text-accent hover:border-accentSecondary hover:text-accentSecondary disabled:cursor-not-allowed disabled:opacity-60",children:s?"Loading more…":"Load more blocks"})})]})}const M4=h2(yd(h.jsxs(On,{element:h.jsx(A2,{}),errorElement:h.jsx(xm,{}),children:[h.jsx(On,{index:!0,element:h.jsx(H2,{})}),h.jsx(On,{path:"block",element:h.jsx(j4,{})}),h.jsx(On,{path:"block/:hash",element:h.jsx(V2,{})}),h.jsx(On,{path:"tx/:txid",element:h.jsx(Q2,{})}),h.jsx(On,{path:"address/:address",element:h.jsx(T4,{})}),h.jsx(On,{path:"search",element:h.jsx(R4,{})}),h.jsx(On,{path:"*",element:h.jsx(xm,{})})]})));function L4(){return h.jsx(C.Suspense,{fallback:h.jsx("div",{className:"p-6",children:"Loading…"}),children:h.jsx(k2,{router:M4})})}const u0=document.getElementById("root");if(!u0)throw new Error("Root element not found");Gu.createRoot(u0).render(h.jsx(Um.StrictMode,{children:h.jsx(V_,{children:h.jsx(L4,{})})})); -)DELIM" - - - -) - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/explore_font.cpp b/console/embedded/explore_font.cpp deleted file mode 100644 index 552880e5f..000000000 --- a/console/embedded/explore_font.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -DEFINE_EMBEDDED_PAGE(explore_pages, uint8_t, font, -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}) - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/explore_html.cpp b/console/embedded/explore_html.cpp deleted file mode 100644 index 3c8e7a265..000000000 --- a/console/embedded/explore_html.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -// Simple test html for embedded page, links in css and page icon. -DEFINE_EMBEDDED_PAGE(explore_pages, char, html, - R"DELIM( - - - - - libbitcoin Explorer - - - - -
- - -)DELIM" - -) - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/explore_icon.cpp b/console/embedded/explore_icon.cpp deleted file mode 100644 index 35b939569..000000000 --- a/console/embedded/explore_icon.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -DEFINE_EMBEDDED_PAGE(explore_pages, uint8_t, icon, -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}) - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/web_css.cpp b/console/embedded/web_css.cpp deleted file mode 100644 index 50bac69a7..000000000 --- a/console/embedded/web_css.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -// Simple test css for embedded page, links in font. -DEFINE_EMBEDDED_PAGE(web_pages, char, css, -R"(@font-face -{ - font-family: 'Boston'; - src: url('boston.woff2'); -})") - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/web_ecma.cpp b/console/embedded/web_ecma.cpp deleted file mode 100644 index 2c9d31cad..000000000 --- a/console/embedded/web_ecma.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -// Simple test ecma script for embedded page. -DEFINE_EMBEDDED_PAGE(web_pages, char, ecma, -R"(document.addEventListener('DOMContentLoaded', function() -{ - console.log('ping'); -});)") - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/web_font.cpp b/console/embedded/web_font.cpp deleted file mode 100644 index 101a94d8a..000000000 --- a/console/embedded/web_font.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -DEFINE_EMBEDDED_PAGE(web_pages, uint8_t, font, -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}) - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/web_html.cpp b/console/embedded/web_html.cpp deleted file mode 100644 index 1e9057db7..000000000 --- a/console/embedded/web_html.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -// Simple test html for embedded page, links in css and page icon. -DEFINE_EMBEDDED_PAGE(web_pages, char, html, -R"( - - Libbitcoin Server - - - - - - - - -

Hello world!

- -)") - -} // namespace server -} // namespace libbitcoin diff --git a/console/embedded/web_icon.cpp b/console/embedded/web_icon.cpp deleted file mode 100644 index ca6940330..000000000 --- a/console/embedded/web_icon.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../embedded/embedded.hpp" - -namespace libbitcoin { -namespace server { - -DEFINE_EMBEDDED_PAGE(web_pages, uint8_t, icon, -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}) - -} // namespace server -} // namespace libbitcoin diff --git a/console/executor.cpp b/console/executor.cpp deleted file mode 100644 index 96e1eee6e..000000000 --- a/console/executor.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -#include -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -using boost::format; -using namespace std::placeholders; - -// non-const member static (global for blocking interrupt handling). -std::promise executor::stopping_{}; - -// non-const member static (global for non-blocking interrupt handling). -std::atomic_bool executor::cancel_{}; - -executor::executor(parser& metadata, std::istream& input, std::ostream& output, - std::ostream&) - : metadata_(metadata), - store_(metadata.configured.database), - query_(store_), - input_(input), - output_(output), - toggle_ - { - metadata.configured.log.application, - metadata.configured.log.news, - metadata.configured.log.session, - metadata.configured.log.protocol, - metadata.configured.log.proxy, - metadata.configured.log.remote, - metadata.configured.log.fault, - metadata.configured.log.quitting, - metadata.configured.log.objects, - metadata.configured.log.verbose - } -{ - initialize_stop(); -} - -// Stop signal. -// ---------------------------------------------------------------------------- - -// Capture . -void executor::initialize_stop() -{ - std::signal(SIGINT, handle_stop); - std::signal(SIGTERM, handle_stop); -} - -void executor::handle_stop(int) -{ - initialize_stop(); - stop(error::success); -} - -// Manage the race between console stop and server stop. -void executor::stop(const code& ec) -{ - static std::once_flag stop_mutex{}; - std::call_once(stop_mutex, [&]() - { - cancel_.store(true); - stopping_.set_value(ec); - }); -} - -// Event handlers. -// ---------------------------------------------------------------------------- - -void executor::handle_started(const code& ec) -{ - if (ec) - { - if (ec == error::store_uninitialized) - logger(format(BN_UNINITIALIZED_CHAIN) % - metadata_.configured.database.path); - else - logger(format(BN_NODE_START_FAIL) % ec.message()); - - stop(ec); - return; - } - - logger(BN_NODE_STARTED); - - node_->subscribe_close( - std::bind(&executor::handle_stopped, this, _1), - std::bind(&executor::handle_subscribed, this, _1, _2)); -} - -void executor::handle_subscribed(const code& ec, size_t) -{ - if (ec) - { - logger(format(BN_NODE_START_FAIL) % ec.message()); - stop(ec); - return; - } - - node_->run(std::bind(&executor::handle_running, this, _1)); -} - -void executor::handle_running(const code& ec) -{ - if (ec) - { - logger(format(BN_NODE_START_FAIL) % ec.message()); - stop(ec); - return; - } - - logger(BN_NODE_RUNNING); -} - -bool executor::handle_stopped(const code& ec) -{ - if (ec && ec != network::error::service_stopped) - logger(format(BN_NODE_STOP_CODE) % ec.message()); - - // Signal stop (simulates ). - stop(ec); - return false; -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor.hpp b/console/executor.hpp deleted file mode 100644 index 76933994a..000000000 --- a/console/executor.hpp +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_BN_EXECUTOR_HPP -#define LIBBITCOIN_BN_EXECUTOR_HPP - -#include -#include -#include -#include -#include - -// This class is just an ad-hoc user interface wrapper on the node. -// It will be factored and cleaned up for final release. - -namespace libbitcoin { -namespace node { - -class executor -{ -public: - DELETE_COPY(executor); - - executor(parser& metadata, std::istream&, std::ostream& output, - std::ostream& error); - - bool dispatch(); - -private: - void logger(const auto& message) const - { - if (log_.stopped()) - output_ << message << std::endl; - else - log_.write(network::levels::application) << message << std::endl; - } - - void stopper(const auto& message) - { - capture_.stop(); - log_.stop(message, network::levels::application); - stopped_.get_future().wait(); - } - - // Stop signal. - static void initialize_stop(); - static void stop(const system::code& ec); - static void handle_stop(int code); - - // Event handlers. - void handle_started(const system::code& ec); - void handle_subscribed(const system::code& ec, size_t key); - void handle_running(const system::code& ec); - bool handle_stopped(const system::code& ec); - - // Store dumps. - void dump_version() const; - void dump_hardware() const; - void dump_options() const; - void dump_body_sizes() const; - void dump_records() const; - void dump_buckets() const; - void dump_progress() const; - void dump_collisions() const; - void dump_sizes() const; - - // Store functions. - bool check_store_path(bool create=false) const; - bool create_store(bool details = false); - bool open_store(bool details=false); - code open_store_coded(bool details=false); - bool close_store(bool details=false); - bool reload_store(bool details=false); - bool restore_store(bool details=false); - bool hot_backup_store(bool details=false); - bool cold_backup_store(bool details=false); - - // Long-running queries (scans). - void scan_flags() const; - void scan_slabs() const; - void scan_buckets() const; - void scan_collisions() const; - - // Command line (defaults to do_run). - bool do_help(); - bool do_version(); - bool do_hardware(); - bool do_settings(); - bool do_new_store(); - bool do_backup(); - bool do_restore(); - bool do_flags(); - bool do_information(); - bool do_slabs(); - bool do_buckets(); - bool do_collisions(); - bool do_read(const system::hash_digest& hash); - bool do_write(const system::hash_digest& hash); - - // Runtime options. - void do_hot_backup(); - void do_close(); - void do_suspend(); - void do_resume(); - void do_report_work(); - void do_reload_store(); - void do_menu() const; - void do_test() const; - void do_info() const; - void do_report_condition() const; - void subscribe_capture(); - - // Built in tests. - void read_test(const system::hash_digest& hash) const; - void write_test(const system::hash_digest& hash); - - // Logging. - database::file::stream::out::rotator create_log_sink() const; - system::ofstream create_event_sink() const; - void subscribe_log(std::ostream& sink); - void subscribe_events(std::ostream& sink); - - // Runner. - void subscribe_connect(); - void subscribe_close(); - bool do_run(); - - // Other user-facing values. - static const std::string name_; - static const std::string close_; - - // Runtime options. - static const std::unordered_map options_; - static const std::unordered_map options_menu_; - - // Runtime toggles. - static const std::unordered_map toggles_; - static const std::unordered_map toggles_menu_; - static const std::unordered_map defined_; - - // Runtime events. - static const std::unordered_map fired_; - static std::promise stopping_; - static std::atomic_bool cancel_; - - parser& metadata_; - full_node::ptr node_{}; - full_node::store store_; - full_node::query query_; - std::promise stopped_{}; - count_t sequence_{}; - - std::istream& input_; - std::ostream& output_; - network::logger log_{}; - network::capture capture_{ input_, close_ }; - std_array toggle_; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/console/executor_commands.cpp b/console/executor_commands.cpp deleted file mode 100644 index e2721c3af..000000000 --- a/console/executor_commands.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -#include -#include - -namespace libbitcoin { -namespace node { - -using system::config::printer; - -const std::string executor::name_{ "bn" }; - -// Command line options. -// ---------------------------------------------------------------------------- - -// --[h]elp -bool executor::do_help() -{ - log_.stop(); - printer help(metadata_.load_options(), name_, BN_INFORMATION_MESSAGE); - help.initialize(); - help.commandline(output_); - return true; -} - -// --[d]hardware -bool executor::do_hardware() -{ - log_.stop(); - dump_hardware(); - return true; -} - -// --[s]ettings -bool executor::do_settings() -{ - log_.stop(); - printer print(metadata_.load_settings(), name_, BN_SETTINGS_MESSAGE); - print.initialize(); - print.settings(output_); - return true; -} - -// --[v]ersion -bool executor::do_version() -{ - log_.stop(); - dump_version(); - return true; -} - -// --[n]ewstore -bool executor::do_new_store() -{ - log_.stop(); - if (!check_store_path(true) || - !create_store(true)) - return false; - - // Records and sizes reflect genesis block only. - dump_body_sizes(); - dump_records(); - dump_buckets(); - - if (!close_store(true)) - return false; - - logger(BN_INITCHAIN_COMPLETE); - return true; -} - -// --[b]ackup -bool executor::do_backup() -{ - log_.stop(); - return check_store_path() - && open_store() - && cold_backup_store(true) - && close_store(); -} - -// --[r]estore -bool executor::do_restore() -{ - log_.stop(); - return check_store_path() - && restore_store(true) - && close_store(); -} - -// --[f]lags -bool executor::do_flags() -{ - log_.stop(); - if (!check_store_path() || - !open_store()) - return false; - - scan_flags(); - return close_store(); -} - -// --[i]nformation -bool executor::do_information() -{ - log_.stop(); - if (!check_store_path() || - !open_store()) - return false; - - dump_sizes(); - return close_store(); -} - -// --[a]slabs -bool executor::do_slabs() -{ - log_.stop(); - if (!check_store_path() || - !open_store()) - return false; - - scan_slabs(); - return close_store(); -} - -// --[k]buckets -bool executor::do_buckets() -{ - log_.stop(); - if (!check_store_path() || - !open_store()) - return false; - - scan_buckets(); - return close_store(); -} - -// --[l]collisions -bool executor::do_collisions() -{ - log_.stop(); - if (!check_store_path() || - !open_store()) - return false; - - scan_collisions(); - return close_store(); -} - -// --[t]read -bool executor::do_read(const system::hash_digest& hash) -{ - log_.stop(); - if (!check_store_path() || - !open_store()) - return false; - - read_test(hash); - return close_store(); -} - -// --[w]rite -bool executor::do_write(const system::hash_digest& hash) -{ - log_.stop(); - if (!check_store_path() || - !open_store()) - return false; - - write_test(hash); - return close_store(); -} - -// Command line dispatch. -// ---------------------------------------------------------------------------- - -bool executor::dispatch() -{ - const auto& config = metadata_.configured; - - if (config.help) - return do_help(); - - // Order below matches help output (alphabetical), so that first option is - // executed in the case where multiple options are parsed. - - if (config.slabs) - return do_slabs(); - - if (config.backup) - return do_backup(); - - if (config.restore) - return do_restore(); - - if (config.hardware) - return do_hardware(); - - if (config.flags) - return do_flags(); - - if (config.newstore) - return do_new_store(); - - if (config.buckets) - return do_buckets(); - - if (config.collisions) - return do_collisions(); - - if (config.information) - return do_information(); - - if (config.settings) - return do_settings(); - - if (config.version) - return do_version(); - - if (config.test != system::null_hash) - return do_read(config.test); - - if (config.write != system::null_hash) - return do_write(config.write); - - return do_run(); -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_dumps.cpp b/console/executor_dumps.cpp deleted file mode 100644 index 6eec512c4..000000000 --- a/console/executor_dumps.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -#include -#include - -namespace libbitcoin { -namespace node { - -using boost::format; - -constexpr double to_double(auto integer) -{ - return 1.0 * integer; -} - -// Store dumps. -// ---------------------------------------------------------------------------- - -// emit version information for libbitcoin libraries -void executor::dump_version() const -{ - logger(format(BN_VERSION_MESSAGE) - % LIBBITCOIN_NODE_VERSION - % LIBBITCOIN_DATABASE_VERSION - % LIBBITCOIN_NETWORK_VERSION - % LIBBITCOIN_SYSTEM_VERSION); -} - -// The "try" functions are safe for instructions not compiled in. -void executor::dump_hardware() const -{ - using namespace system; - - logger(BN_HARDWARE_HEADER); - logger(format("arm..... " BN_HARDWARE_TABLE1) % have_arm); - logger(format("intel... " BN_HARDWARE_TABLE1) % have_xcpu); - logger(format("avx512.. " BN_HARDWARE_TABLE2) % try_avx512() % have_512); - logger(format("avx2.... " BN_HARDWARE_TABLE2) % try_avx2() % have_256); - logger(format("sse41... " BN_HARDWARE_TABLE2) % try_sse41() % have_128); - logger(format("shani... " BN_HARDWARE_TABLE2) % try_shani() % have_sha); -} - -// logging compilation and initial values. -void executor::dump_options() const -{ - using namespace network; - - logger(BN_LOG_TABLE_HEADER); - logger(format("[a]pplication.. " BN_LOG_TABLE) % levels::application_defined % toggle_.at(levels::application)); - logger(format("[n]ews......... " BN_LOG_TABLE) % levels::news_defined % toggle_.at(levels::news)); - logger(format("[s]ession...... " BN_LOG_TABLE) % levels::session_defined % toggle_.at(levels::session)); - logger(format("[p]rotocol..... " BN_LOG_TABLE) % levels::protocol_defined % toggle_.at(levels::protocol)); - logger(format("[x]proxy....... " BN_LOG_TABLE) % levels::proxy_defined % toggle_.at(levels::proxy)); - logger(format("[r]emote....... " BN_LOG_TABLE) % levels::remote_defined % toggle_.at(levels::remote)); - logger(format("[f]ault........ " BN_LOG_TABLE) % levels::fault_defined % toggle_.at(levels::fault)); - logger(format("[q]uitting..... " BN_LOG_TABLE) % levels::quitting_defined % toggle_.at(levels::quitting)); - logger(format("[o]bjects...... " BN_LOG_TABLE) % levels::objects_defined % toggle_.at(levels::objects)); - logger(format("[v]erbose...... " BN_LOG_TABLE) % levels::verbose_defined % toggle_.at(levels::verbose)); -} - -void executor::dump_body_sizes() const -{ - logger(format(BN_MEASURE_SIZES) % - query_.header_body_size() % - query_.txs_body_size() % - query_.tx_body_size() % - query_.point_body_size() % - query_.input_body_size() % - query_.output_body_size() % - query_.ins_body_size() % - query_.outs_body_size() % - query_.candidate_body_size() % - query_.confirmed_body_size() % - query_.duplicate_body_size() % - query_.prevout_body_size() % - query_.strong_tx_body_size() % - query_.validated_bk_body_size() % - query_.validated_tx_body_size() % - query_.filter_bk_body_size() % - query_.filter_tx_body_size() % - query_.address_body_size()); -} - -void executor::dump_records() const -{ - logger(format(BN_MEASURE_RECORDS) % - query_.header_records() % - query_.tx_records() % - query_.point_records() % - query_.ins_records() % - query_.outs_records() % - query_.candidate_records() % - query_.confirmed_records() % - query_.duplicate_records() % - query_.strong_tx_records() % - query_.filter_bk_records() % - query_.address_records()); -} - -void executor::dump_buckets() const -{ - logger(format(BN_MEASURE_BUCKETS) % - query_.header_buckets() % - query_.txs_buckets() % - query_.tx_buckets() % - query_.point_buckets() % - query_.duplicate_buckets() % - query_.prevout_buckets() % - query_.strong_tx_buckets() % - query_.validated_bk_buckets() % - query_.validated_tx_buckets() % - query_.filter_bk_buckets() % - query_.filter_tx_buckets() % - query_.address_buckets()); -} - -void executor::dump_collisions() const -{ - logger(format(BN_MEASURE_COLLISION_RATES) % - (to_double(query_.header_records()) / query_.header_buckets()) % - (to_double(query_.tx_records()) / query_.tx_buckets()) % - (to_double(query_.point_records()) / query_.point_buckets()) % - (to_double(query_.strong_tx_records()) / query_.strong_tx_buckets()) % - (to_double(query_.tx_records()) / query_.validated_tx_buckets()) % - (query_.address_enabled() ? (to_double(query_.address_records()) / - query_.address_buckets()) : zero)); -} - -void executor::dump_progress() const -{ - using namespace system; - - logger(format(BN_MEASURE_PROGRESS) % - query_.get_fork() % - query_.get_top_confirmed() % - encode_hash(query_.get_top_confirmed_hash()) % - query_.get_top_candidate() % - encode_hash(query_.get_top_candidate_hash()) % - query_.get_top_associated() % - (query_.get_top_candidate() - query_.get_unassociated_count()) % - query_.get_confirmed_size() % - query_.get_candidate_size()); -} - -// file and logical sizes. -void executor::dump_sizes() const -{ - dump_body_sizes(); - dump_records(); - dump_buckets(); - dump_collisions(); - - // This one can take a few seconds on cold iron. - logger(BN_MEASURE_PROGRESS_START); - dump_progress(); -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_events.cpp b/console/executor_events.cpp deleted file mode 100644 index e1259210b..000000000 --- a/console/executor_events.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" - -#include -#include - -namespace libbitcoin { -namespace node { - -const std::unordered_map executor::fired_ -{ - { events::header_archived, "header_archived....." }, - { events::header_organized, "header_organized...." }, - { events::header_reorganized, "header_reorganized.." }, - - { events::block_archived, "block_archived......" }, - { events::block_buffered, "block_buffered......" }, - { events::block_validated, "block_validated....." }, - { events::block_confirmed, "block_confirmed....." }, - { events::block_unconfirmable, "block_unconfirmable." }, - { events::validate_bypassed, "validate_bypassed..." }, - { events::confirm_bypassed, "confirm_bypassed...." }, - - { events::tx_archived, "tx_archived........." }, - { events::tx_validated, "tx_validated........" }, - { events::tx_invalidated, "tx_invalidated......" }, - - { events::block_organized, "block_organized....." }, - { events::block_reorganized, "block_reorganized..." }, - - { events::template_issued, "template_issued....." }, - - { events::snapshot_secs, "snapshot_secs......." }, - { events::prune_msecs, "prune_msecs........." }, - { events::reload_msecs, "reload_msecs........" }, - { events::block_msecs, "block_msecs........." }, - { events::ancestry_msecs, "ancestry_msecs......" }, - { events::filter_msecs, "filter_msecs........" }, - { events::filterhashes_msecs, "filterhashes_msecs.." }, - { events::filterchecks_msecs, "filterchecks_msecs.." } -}; - -// Events. -// ---------------------------------------------------------------------------- - -// TODO: throws, handle failure. -system::ofstream executor::create_event_sink() const -{ - // Standard file name, within the [node].path directory. - return { metadata_.configured.log.events_file() }; -} - -void executor::subscribe_events(std::ostream& sink) -{ - using namespace network; - using namespace std::chrono; - - log_.subscribe_events - ( - [&sink, start = logger::now()](const code& ec, uint8_t event_, - uint64_t value, const logger::time& point) - { - if (ec) - return false; - - const auto time = duration_cast(point - start).count(); - sink << fired_.at(event_) << " " << value << " " << time << std::endl; - return true; - } - ); -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_logging.cpp b/console/executor_logging.cpp deleted file mode 100644 index 5c39df84b..000000000 --- a/console/executor_logging.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -#include -#include -#include - -namespace libbitcoin { -namespace node { - -using namespace network; - -const std::unordered_map executor::defined_ -{ - { levels::application, levels::application_defined }, - { levels::news, levels::news_defined }, - { levels::session, levels::session_defined }, - { levels::protocol, levels::protocol_defined }, - { levels::proxy, levels::proxy_defined }, - { levels::remote, levels::remote_defined }, - { levels::fault, levels::fault_defined }, - { levels::quitting, levels::quitting_defined }, - { levels::objects, levels::objects_defined }, - { levels::verbose, levels::verbose_defined }, -}; - -// Logging. -// ---------------------------------------------------------------------------- - -// TODO: verify construction failure handled. -database::file::stream::out::rotator executor::create_log_sink() const -{ - return - { - // Standard file names, within the [node].path directory. - metadata_.configured.log.log_file1(), - metadata_.configured.log.log_file2(), - to_half(metadata_.configured.log.maximum_size) - }; -} - -void executor::subscribe_log(std::ostream& sink) -{ - using namespace system; - - log_.subscribe_messages - ( - [&](const code& ec, uint8_t level, time_t time, const std::string& message) - { - if (level >= toggle_.size()) - { - sink << "Invalid log [" << serialize(level) << "] : " << message; - output_ << "Invalid log [" << serialize(level) << "] : " << message; - output_.flush(); - return true; - } - - // Write only selected logs. - if (!ec && !toggle_.at(level)) - return true; - - const auto prefix = format_zulu_time(time) + "." + - serialize(level) + " "; - - if (ec) - { - sink << prefix << message << std::endl; - output_ << prefix << message << std::endl; - sink << prefix << BN_NODE_FOOTER << std::endl; - output_ << prefix << BN_NODE_FOOTER << std::endl; - output_ << prefix << BN_NODE_TERMINATE << std::endl; - stopped_.set_value(ec); - return false; - } - else - { - sink << prefix << message; - output_ << prefix << message; - output_.flush(); - return true; - } - } - ); -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_options.cpp b/console/executor_options.cpp deleted file mode 100644 index 167e7b3d7..000000000 --- a/console/executor_options.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -#include -#include -#include - -namespace libbitcoin { -namespace node { - -using boost::format; -using namespace network; - -// local -enum menu : uint8_t -{ - backup, - close, - errors, - go, - hold, - info, - menu_, - test, - work, - zeroize -}; - -// for capture construct, defines [c]lose command key. -const std::string executor::close_{ "c" }; - -const std::unordered_map executor::options_ -{ - { "b", menu::backup }, - { "c", menu::close }, - { "e", menu::errors }, - { "g", menu::go }, - { "h", menu::hold }, - { "i", menu::info }, - { "m", menu::menu_ }, - { "t", menu::test }, - { "w", menu::work }, - { "z", menu::zeroize } -}; - -const std::unordered_map executor::options_menu_ -{ - { menu::backup, "[b]ackup the store" }, - { menu::close, "[c]lose the node" }, - { menu::errors, "[e]rrors in store" }, - { menu::go, "[g]o network communication" }, - { menu::hold, "[h]old network communication" }, - { menu::info, "[i]nfo about store" }, - { menu::menu_, "[m]enu of options and toggles" }, - { menu::test, "[t]est built-in case" }, - { menu::work, "[w]ork distribution" }, - { menu::zeroize, "[z]eroize disk full error" } -}; - -const std::unordered_map executor::toggles_ -{ - { "a", levels::application }, - { "n", levels::news }, - { "s", levels::session }, - { "p", levels::protocol }, - { "x", levels::proxy }, - { "r", levels::remote }, - { "f", levels::fault }, - { "q", levels::quitting }, - { "o", levels::objects }, - { "v", levels::verbose } -}; - -const std::unordered_map executor::toggles_menu_ -{ - { levels::application, "[a]pplication" }, - { levels::news, "[n]ews" }, - { levels::session, "[s]ession" }, - { levels::protocol, "[p]rotocol" }, - { levels::proxy, "[x]proxy" }, - { levels::remote, "[r]emote" }, - { levels::fault, "[f]ault" }, - { levels::quitting, "[q]uitting" }, - { levels::objects, "[o]bjects" }, - { levels::verbose, "[v]erbose" } -}; - -// Runtime options. -// ---------------------------------------------------------------------------- - -// [b]ackup -void executor::do_hot_backup() -{ - if (!node_) - { - logger(BN_NODE_UNAVAILABLE); - return; - } - - hot_backup_store(true); -} - -// [c]lose -void executor::do_close() -{ - logger("CONSOLE: Close"); - stop(error::success); -} - -// [e]rrors -void executor::do_report_condition() const -{ - store_.report([&](const auto& ec, auto table) - { - logger(format(BN_CONDITION) % full_node::store::tables.at(table) % - ec.message()); - }); - - if (query_.is_full()) - logger(format(BN_RELOAD_SPACE) % query_.get_space()); -} - -// [h]old -void executor::do_suspend() -{ - if (!node_) - { - logger(BN_NODE_UNAVAILABLE); - return; - } - - node_->suspend(error::suspended_service); -} - -// [g]o -void executor::do_resume() -{ - if (query_.is_full()) - { - logger(BN_NODE_DISK_FULL); - return; - } - - if (query_.is_fault()) - { - logger(BN_NODE_UNRECOVERABLE); - return; - } - - if (!node_) - { - logger(BN_NODE_UNAVAILABLE); - return; - } - - node_->resume(); -} - -// [i]nfo -void executor::do_info() const -{ - dump_body_sizes(); - dump_records(); - dump_buckets(); - dump_collisions(); - ////dump_progress(); -} - -// [m]enu -void executor::do_menu() const -{ - for (const auto& toggle: toggles_menu_) - logger(format("Toggle: %1%") % toggle.second); - - for (const auto& option: options_menu_) - logger(format("Option: %1%") % option.second); -} - -// [t]est -void executor::do_test() const -{ - read_test(system::null_hash); -} - -// [w]ork -void executor::do_report_work() -{ - if (!node_) - { - logger(BN_NODE_UNAVAILABLE); - return; - } - - logger(format(BN_NODE_REPORT_WORK) % sequence_); - node_->notify(error::success, chase::report, sequence_++); -} - -// [z]eroize -void executor::do_reload_store() -{ - // Use do_resume command to restart connections after resetting here. - if (query_.is_full()) - { - if (!node_) - { - logger(BN_NODE_UNAVAILABLE); - return; - } - - reload_store(true); - return; - } - - // Any table with any error code. - logger(query_.is_fault() ? BN_NODE_UNRECOVERABLE : BN_NODE_OK); -} - -// Runtime options/toggles dispatch. -// ---------------------------------------------------------------------------- - -void executor::subscribe_capture() -{ - using namespace system; - - // This is not on a network thread, so the node may call close() while this - // is running a backup (for example), resulting in a try_lock warning loop. - capture_.subscribe( - [&](const code& ec, const std::string& line) - { - // The only case in which false may be returned. - if (ec == network::error::service_stopped) - { - set_console_echo(); - return false; - } - - const auto token = trim_copy(line); - - // -c emits empty token on Win32. - if (token.empty()) - return true; - - // toggle log levels - if (toggles_.contains(token)) - { - const auto toggle = toggles_.at(token); - if (defined_.at(toggle)) - { - toggle_.at(toggle) = !toggle_.at(toggle); - logger(format("CONSOLE: toggle %1% logging (%2%).") % - toggles_menu_.at(toggle) % - (toggle_.at(toggle) ? "+" : "-")); - } - else - { - logger(format("CONSOLE: %1% logging is not compiled.") % - toggles_menu_.at(toggle)); - } - - return true; - } - - // dispatch options - if (options_.contains(token)) - { - switch (options_.at(token)) - { - case menu::backup: - { - do_hot_backup(); - return true; - } - case menu::close: - { - do_close(); - return true; - } - case menu::errors: - { - do_report_condition(); - return true; - } - case menu::go: - { - do_resume(); - return true; - } - case menu::hold: - { - do_suspend(); - return true; - } - case menu::info: - { - do_info(); - return true; - } - case menu::menu_: - { - do_menu(); - return true; - } - case menu::test: - { - do_test(); - return true; - } - case menu::work: - { - do_report_work(); - return true; - } - case menu::zeroize: - { - do_reload_store(); - return true; - } - default: - { - logger("CONSOLE: Unexpected option."); - return true; - } - } - } - - logger("CONSOLE: '" + line + "'"); - return true; - }, - [&](const code& ec) - { - // subscription completion handler. - if (!ec) - unset_console_echo(); - } - ); -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_runner.cpp b/console/executor_runner.cpp deleted file mode 100644 index ca59361ec..000000000 --- a/console/executor_runner.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -#include - -namespace libbitcoin { -namespace node { - -using namespace network; -using namespace std::placeholders; - -// Runner. -// ---------------------------------------------------------------------------- - -void executor::subscribe_connect() -{ - node_->subscribe_connect - ( - [&](const code&, const network::channel::ptr&) - { - log_.write(levels::verbose) << - "{in:" << node_->inbound_channel_count() << "}" - "{ch:" << node_->channel_count() << "}" - "{rv:" << node_->reserved_count() << "}" - "{nc:" << node_->nonces_count() << "}" - "{ad:" << node_->address_count() << "}" - "{ss:" << node_->stop_subscriber_count() << "}" - "{cs:" << node_->connect_subscriber_count() << "}." - << std::endl; - - return true; - }, - [&](const code&, uintptr_t) - { - // By not handling it is possible stop could fire before complete. - // But the handler is not required for termination, so this is ok. - // The error code in the handler can be used to differentiate. - } - ); -} - -void executor::subscribe_close() -{ - node_->subscribe_close - ( - [&](const code&) - { - log_.write(levels::verbose) << - "{in:" << node_->inbound_channel_count() << "}" - "{ch:" << node_->channel_count() << "}" - "{rv:" << node_->reserved_count() << "}" - "{nc:" << node_->nonces_count() << "}" - "{ad:" << node_->address_count() << "}" - "{ss:" << node_->stop_subscriber_count() << "}" - "{cs:" << node_->connect_subscriber_count() << "}." - << std::endl; - - return false; - }, - [&](const code&, size_t) - { - // By not handling it is possible stop could fire before complete. - // But the handler is not required for termination, so this is ok. - // The error code in the handler can be used to differentiate. - } - ); -} - -bool executor::do_run() -{ - if (!metadata_.configured.log.path.empty()) - database::file::create_directory(metadata_.configured.log.path); - - // Hold sinks in scope for the length of the run. - auto log = create_log_sink(); - auto events = create_event_sink(); - if (!log || !events) - { - logger(BN_LOG_INITIALIZE_FAILURE); - return false; - } - - subscribe_log(log); - subscribe_events(events); - subscribe_capture(); - logger(BN_LOG_HEADER); - - if (check_store_path()) - { - auto ec = open_store_coded(true); - if (ec == database::error::flush_lock) - { - ec = error::success; - if (!restore_store(true)) - ec = database::error::integrity; - } - - if (ec) - { - stopper(BN_NODE_STOPPED); - return false; - } - } - else if (!check_store_path(true) || !create_store(true)) - { - stopper(BN_NODE_STOPPED); - return false; - } - - dump_body_sizes(); - dump_records(); - dump_buckets(); - ////logger(BN_MEASURE_PROGRESS_START); - ////dump_progress(); - - // Stopped by stopper. - capture_.start(); - dump_version(); - dump_hardware(); - dump_options(); - logger(BN_NODE_INTERRUPT); - - // Create node. - metadata_.configured.network.manual.initialize(); - node_ = std::make_shared(query_, metadata_.configured, log_); - - // Subscribe node. - subscribe_connect(); - subscribe_close(); - - // Start network. - logger(BN_NETWORK_STARTING); - node_->start(std::bind(&executor::handle_started, this, _1)); - - // Wait on signal to stop node (). - stopping_.get_future().wait(); - toggle_.at(levels::protocol) = false; - logger(BN_NETWORK_STOPPING); - - // Stop network (if not already stopped by self). - node_->close(); - - // Sizes and records change, buckets don't. - dump_body_sizes(); - dump_records(); - ////logger(BN_MEASURE_PROGRESS_START); - ////dump_progress(); - - if (!close_store(true)) - { - stopper(BN_NODE_STOPPED); - return false; - } - - stopper(BN_NODE_STOPPED); - return true; -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_scans.cpp b/console/executor_scans.cpp deleted file mode 100644 index d30b9e219..000000000 --- a/console/executor_scans.cpp +++ /dev/null @@ -1,445 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -using boost::format; -using namespace network; -using namespace system; - -constexpr double to_double(auto integer) -{ - return 1.0 * integer; -} - -// fork flag transitions (candidate chain). -void executor::scan_flags() const -{ - const auto start = logger::now(); - constexpr auto flag_bits = to_bits(sizeof(chain::flags)); - const auto error = code{ database::error::integrity }.message(); - const auto top = query_.get_top_candidate(); - uint32_t flags{}; - - logger(BN_OPERATION_INTERRUPT); - - for (size_t height{}; !cancel_ && height <= top; ++height) - { - database::context ctx{}; - const auto link = query_.to_candidate(height); - if (!query_.get_context(ctx, link) || (ctx.height != height)) - { - logger(format("Error: %1%") % error); - return; - } - - if (ctx.flags != flags) - { - const binary prev{ flag_bits, to_big_endian(flags) }; - const binary next{ flag_bits, to_big_endian(ctx.flags) }; - logger(format("Forked from [%1%] to [%2%] at [%3%:%4%]") % prev % - next % encode_hash(query_.get_header_key(link)) % height); - flags = ctx.flags; - } - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - const auto span = duration_cast(logger::now() - start); - logger(format("Scanned %1% headers for rule forks in %2% ms.") % top % - span.count()); -} - -// input and output table slab counts. -void executor::scan_slabs() const -{ - logger(BN_MEASURE_SLABS); - logger(BN_OPERATION_INTERRUPT); - database::tx_link::integer link{}; - size_t inputs{}, outputs{}; - const auto start = logger::now(); - constexpr auto frequency = 100'000; - - // Tx (record) links are sequential and so iterable, however the terminal - // condition assumes all tx entries fully written (ok for stopped node). - // A running node cannot safely iterate over record links, but stopped can. - for (auto puts = query_.put_counts(link); to_bool(puts.first) && !cancel_; - puts = query_.put_counts(++link)) - { - inputs += puts.first; - outputs += puts.second; - if (is_zero(link % frequency)) - logger(format(BN_MEASURE_SLABS_ROW) % link % inputs % outputs); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - const auto span = duration_cast(logger::now() - start); - logger(format(BN_MEASURE_STOP) % inputs % outputs % span.count()); -} - -// hashmap bucket fill rates. -void executor::scan_buckets() const -{ - constexpr auto block_frequency = 10'000u; - constexpr auto tx_frequency = 1'000'000u; - constexpr auto put_frequency = 10'000'000u; - - logger(BN_OPERATION_INTERRUPT); - - auto filled = zero; - auto bucket = max_size_t; - auto start = logger::now(); - while (!cancel_ && (++bucket < query_.header_buckets())) - { - const auto top = query_.top_header(bucket); - if (!top.is_terminal()) - ++filled; - - if (is_zero(bucket % block_frequency)) - logger(format("header" BN_READ_ROW) % bucket % - duration_cast(logger::now() - start).count()); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - auto span = duration_cast(logger::now() - start); - logger(format("header" BN_READ_ROW) % (to_double(filled) / bucket) % - span.count()); - - // ------------------------------------------------------------------------ - - filled = zero; - bucket = max_size_t; - start = logger::now(); - while (!cancel_ && (++bucket < query_.tx_buckets())) - { - const auto top = query_.top_tx(bucket); - if (!top.is_terminal()) - ++filled; - - if (is_zero(bucket % tx_frequency)) - logger(format("tx" BN_READ_ROW) % bucket % - duration_cast(logger::now() - start).count()); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - span = duration_cast(logger::now() - start); - logger(format("tx" BN_READ_ROW) % (to_double(filled) / bucket) % - span.count()); - - // ------------------------------------------------------------------------ - - filled = zero; - bucket = max_size_t; - start = logger::now(); - while (!cancel_ && (++bucket < query_.point_buckets())) - { - const auto top = query_.top_point(bucket); - if (!top.is_terminal()) - ++filled; - - if (is_zero(bucket % put_frequency)) - logger(format("point" BN_READ_ROW) % bucket % - duration_cast(logger::now() - start).count()); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - span = duration_cast(logger::now() - start); - logger(format("point" BN_READ_ROW) % (to_double(filled) / bucket) % - span.count()); -} - -// hashmap collision distributions. -// BUGBUG: the vector allocations are exceessive and can result in sigkill. -// BUGBUG: must process each header independently as buckets may not coincide. -void executor::scan_collisions() const -{ - using namespace database; - using hint = header_link::integer; - constexpr auto empty = 0u; - constexpr auto block_frequency = 10'000u; - constexpr auto tx_frequency = 1'000'000u; - constexpr auto put_frequency = 10'000'000u; - constexpr auto count = [](const auto& list) - { - return std::accumulate(list.begin(), list.end(), zero, - [](size_t total, const auto& value) - { - return total + to_int(to_bool(value)); - }); - }; - constexpr auto dump = [&](const auto& list) - { - // map frequency to length. - std::map map{}; - for (const auto value: list) - ++map[value]; - - return map; - }; - - logger(BN_OPERATION_INTERRUPT); - - // header & txs (txs is a proxy for validated_bk) - // ------------------------------------------------------------------------ - - auto index = max_size_t; - auto start = logger::now(); - const auto header_buckets = query_.header_buckets(); - const auto header_records = query_.header_records(); - std_vector header(header_buckets, empty); - std_vector txs(header_buckets, empty); - while (!cancel_ && (++index < header_records)) - { - const header_link link{ possible_narrow_cast(index) }; - const auto key = query_.get_header_key(link.value); - ++header.at(database::keys::hash(key) % header_buckets); - ++txs.at(database::keys::hash( - link.operator data_array()) % - header_buckets); - - if (is_zero(index % block_frequency)) - logger(format("header/txs" BN_READ_ROW) % index % - duration_cast(logger::now() - start).count()); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - // ........................................................................ - - const auto header_count = count(header); - auto span = duration_cast(logger::now() - start); - logger(format("header: %1% in %2%s buckets %3% filled %4% rate %5% ") % - index % span.count() % header_buckets % header_count % - (to_double(header_count) / header_buckets)); - - for (const auto& entry: dump(header)) - logger(format("header: %1% frequency: %2%") % - entry.first % entry.second); - - header.clear(); - header.shrink_to_fit(); - - // ........................................................................ - - const auto txs_count = count(txs); - span = duration_cast(logger::now() - start); - logger(format("txs: %1% in %2%s buckets %3% filled %4% rate %5%") % - index % span.count() % header_buckets % txs_count % - (to_double(txs_count) / header_buckets)); - - for (const auto& entry: dump(txs)) - logger(format("txs: %1% frequency: %2%") % - entry.first % entry.second); - - txs.clear(); - txs.shrink_to_fit(); - - // tx & strong_tx (strong_tx is a proxy for validated_tx) - // ------------------------------------------------------------------------ - - index = max_size_t; - start = logger::now(); - const auto tx_buckets = query_.tx_buckets(); - const auto tx_records = query_.tx_records(); - std_vector tx(tx_buckets, empty); - std_vector strong_tx(tx_buckets, empty); - while (!cancel_ && (++index < tx_records)) - { - const tx_link link{ possible_narrow_cast(index) }; - const auto key = query_.get_tx_key(link.value); - ++tx.at(database::keys::hash(key) % tx_buckets); - ++strong_tx.at(database::keys::hash( - link.operator data_array()) % tx_buckets); - - if (is_zero(index % tx_frequency)) - logger(format("tx & strong_tx" BN_READ_ROW) % index % - duration_cast(logger::now() - start).count()); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - // ........................................................................ - - const auto tx_count = count(tx); - span = duration_cast(logger::now() - start); - logger(format("tx: %1% in %2%s buckets %3% filled %4% rate %5%") % - index % span.count() % tx_buckets % tx_count % - (to_double(tx_count) / tx_buckets)); - - for (const auto& entry: dump(tx)) - logger(format("tx: %1% frequency: %2%") % - entry.first % entry.second); - - tx.clear(); - tx.shrink_to_fit(); - - // ........................................................................ - - const auto strong_tx_count = count(strong_tx); - span = duration_cast(logger::now() - start); - logger(format("strong_tx: %1% in %2%s buckets %3% filled %4% rate %5%") % - index % span.count() % tx_buckets % strong_tx_count % - (to_double(strong_tx_count) / tx_buckets)); - - for (const auto& entry: dump(strong_tx)) - logger(format("strong_tx: %1% frequency: %2%") % - entry.first % entry.second); - - strong_tx.clear(); - strong_tx.shrink_to_fit(); - - // point - // ------------------------------------------------------------------------ - - index = max_size_t; - start = logger::now(); - const auto point_buckets = query_.point_buckets(); - std_vector spend(point_buckets, empty); - auto inserts = zero; - - // TODO: expose filter type from hashhead to table. - /////////////////////////////////////////////////////////////////////////// - constexpr size_t m = 32; - constexpr size_t k = floored_log2(m); - using bloom_t = bloom; - ////using sieve_t = sieve; - /////////////////////////////////////////////////////////////////////////// - - constexpr auto empty_bloom = unmask_right(m); - std_vector bloom_filter(point_buckets, empty_bloom); - size_t bloom_collisions{}; - size_t bloom_subtotal{}; - - ////constexpr auto empty_sieve = unmask_right(m); - ////std_vector sieve_filter(point_buckets, empty_sieve); - ////size_t sieve_collisions{}; - ////size_t sieve_subtotal{}; - - size_t coinbases{}; - size_t window{}; - - const auto top = query_.get_top_associated(); - for (index = zero; index <= top && !cancel_; ++index) - { - ++coinbases; - const auto link = query_.to_candidate(index); - const auto transactions = query_.to_transactions(link); - for (const auto& transaction: transactions) - { - const auto points = query_.to_points(transaction); - for (const auto& point: points) - { - // If and only if coinbase bucket is one. - const auto key = query_.get_point(point); - const auto bucket = database::keys::bucket(key, point_buckets); - const auto entropy = database::keys::thumb(key); - ++spend.at(bucket); - ++inserts; - ++window; - - auto prev = bloom_filter.at(bucket); - auto next = bloom_t::screen(prev, entropy); - bloom_filter.at(bucket) = next; - auto coll = to_int(bloom_t::is_collision(prev, next)); - bloom_collisions += coll; - bloom_subtotal += coll; - - ////prev = sieve_filter.at(bucket); - ////next = sieve_t::screen(prev, entropy); - ////sieve_filter.at(bucket) = next; - ////coll = to_int(sieve_t::is_collision(prev, next)); - ////sieve_collisions += coll; - ////sieve_subtotal += coll; - - if (is_zero(inserts % put_frequency)) - { - logger(format("point: %1% bloom fps %2% rate %3$.7f in %4% secs.") % - inserts % bloom_collisions % - (to_double(bloom_subtotal) / window) % - duration_cast(logger::now() - start).count()); - - ////logger(format("point: %1% sieve fps %2% rate %3$.7f in %4% secs.") % - //// inserts % sieve_collisions % - //// (to_double(sieve_subtotal) / window) % - //// duration_cast(logger::now() - start).count()); - - ////sieve_subtotal = zero; - bloom_subtotal = zero; - window = zero; - } - } - } - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - // ........................................................................ - - const auto point_count = count(spend); - span = duration_cast(logger::now() - start); - logger(format("point: %1% in %2%s buckets %3% filled %4% rate %5%") % - inserts % span.count() % point_buckets % point_count % - (to_double(point_count) / point_buckets)); - - const auto spends = inserts - coinbases; - const auto bloom_spend_collisions = bloom_collisions - coinbases; - logger(format("bloom: %1% fps of %2% spends (ex %3% cbs) rate %4%") % - bloom_spend_collisions % spends % coinbases % - (to_double(bloom_spend_collisions) / spends)); - - ////const auto sieve_spend_collisions = sieve_collisions - coinbases; - ////logger(format("sieve: %1% fps of %2% spends (ex %3% cbs) rate %4%") % - //// sieve_spend_collisions % spends % coinbases % - //// (to_double(sieve_spend_collisions) / spends)); - - for (const auto& entry: dump(spend)) - logger(format("point: %1% frequency: %2%") % - entry.first % entry.second); - - - ////spend.clear(); - ////spend.shrink_to_fit(); - ////bloom_filter.clear(); - ////bloom_filter.shrink_to_fit(); - ////sieve_filter.clear(); - ////sieve_filter.shrink_to_fit(); -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_store.cpp b/console/executor_store.cpp deleted file mode 100644 index ad7268f36..000000000 --- a/console/executor_store.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -#include -#include - -namespace libbitcoin { -namespace node { - -using boost::format; -using namespace network; - -// Store functions. -// ---------------------------------------------------------------------------- - -bool executor::check_store_path(bool create) const -{ - const auto& configuration = metadata_.configured.file; - if (configuration.empty()) - { - logger(BN_USING_DEFAULT_CONFIG); - } - else - { - logger(format(BN_USING_CONFIG_FILE) % configuration); - } - - const auto& store = metadata_.configured.database.path; - if (create) - { - logger(format(BN_INITIALIZING_CHAIN) % store); - if (const auto ec = database::file::create_directory_ex(store)) - { - logger(format(BN_INITCHAIN_DIRECTORY_ERROR) % store % ec.message()); - return false; - } - } - else - { - if (!database::file::is_directory(store)) - { - logger(format(BN_UNINITIALIZED_DATABASE) % store); - return false; - } - } - - return true; -} - -bool executor::create_store(bool details) -{ - logger(BN_INITCHAIN_CREATING); - const auto start = logger::now(); - if (const auto ec = store_.create([&](auto event_, auto table) - { - if (details) - logger(format(BN_CREATE) % - full_node::store::events.at(event_) % - full_node::store::tables.at(table)); - })) - { - logger(format(BN_INITCHAIN_DATABASE_CREATE_FAILURE) % ec.message()); - return false; - } - - // Create and confirm genesis block (store invalid without it). - logger(BN_INITCHAIN_DATABASE_INITIALIZE); - if (!query_.initialize(metadata_.configured.bitcoin.genesis_block)) - { - logger(BN_INITCHAIN_DATABASE_INITIALIZE_FAILURE); - close_store(details); - return false; - } - - const auto span = duration_cast(logger::now() - start); - logger(format(BN_INITCHAIN_CREATED) % span.count()); - return true; -} - -bool executor::open_store(bool details) -{ - return !open_store_coded(details); -} - -// not timed or announced (generally fast) -code executor::open_store_coded(bool details) -{ - ////logger(BN_DATABASE_STARTING); - if (const auto ec = store_.open([&](auto event_, auto table) - { - if (details) - logger(format(BN_OPEN) % - full_node::store::events.at(event_) % - full_node::store::tables.at(table)); - })) - { - logger(format(BN_DATABASE_START_FAIL) % ec.message()); - return ec; - } - - logger(store_.is_dirty() ? BN_DATABASE_STARTED_DIRTY : BN_DATABASE_STARTED); - return error::success; -} - -bool executor::close_store(bool details) -{ - logger(BN_DATABASE_STOPPING); - const auto start = logger::now(); - if (const auto ec = store_.close([&](auto event_, auto table) - { - if (details) - logger(format(BN_CLOSE) % - full_node::store::events.at(event_) % - full_node::store::tables.at(table)); - })) - { - logger(format(BN_DATABASE_STOP_FAIL) % ec.message()); - return false; - } - - const auto span = duration_cast(logger::now() - start); - logger(format(BN_DATABASE_TIMED_STOP) % span.count()); - return true; -} - -bool executor::reload_store(bool details) -{ - if (!node_) - { - logger(BN_NODE_UNAVAILABLE); - return false; - } - - if (const auto ec = store_.get_fault()) - { - logger(format(BN_RELOAD_INVALID) % ec.message()); - return false; - } - - logger(BN_NODE_RELOAD_STARTED); - const auto start = logger::now(); - if (const auto ec = node_->reload([&](auto event_, auto table) - { - if (details) - logger(format(BN_RELOAD) % - full_node::store::events.at(event_) % - full_node::store::tables.at(table)); - })) - { - logger(format(BN_NODE_RELOAD_FAIL) % ec.message()); - return false; - }; - - node_->resume(); - const auto span = duration_cast(logger::now() - start); - logger(format(BN_NODE_RELOAD_COMPLETE) % span.count()); - return true; -} - -bool executor::restore_store(bool details) -{ - logger(BN_RESTORING_CHAIN); - const auto start = logger::now(); - if (const auto ec = store_.restore([&](auto event_, auto table) - { - if (details) - logger(format(BN_RESTORE) % - full_node::store::events.at(event_) % - full_node::store::tables.at(table)); - })) - { - if (ec == database::error::flush_lock) - logger(BN_RESTORE_MISSING_FLUSH_LOCK); - else - logger(format(BN_RESTORE_FAILURE) % ec.message()); - - return false; - } - - const auto span = duration_cast(logger::now() - start); - logger(format(BN_RESTORE_COMPLETE) % span.count()); - return true; -} - -bool executor::hot_backup_store(bool details) -{ - if (!node_) - { - logger(BN_NODE_UNAVAILABLE); - return false; - } - - if (const auto ec = store_.get_fault()) - { - logger(format(BN_SNAPSHOT_INVALID) % ec.message()); - return false; - } - - logger(BN_NODE_BACKUP_STARTED); - const auto start = logger::now(); - if (const auto ec = node_->snapshot([&](auto event_, auto table) - { - if (details) - logger(format(BN_BACKUP) % - full_node::store::events.at(event_) % - full_node::store::tables.at(table)); - })) - { - // system::error::not_a_stream when disk is full. - logger(format(BN_NODE_BACKUP_FAIL) % ec.message()); - return false; - } - - node_->resume(); - const auto span = duration_cast(logger::now() - start); - logger(format(BN_NODE_BACKUP_COMPLETE) % span.count()); - return true; -} - -bool executor::cold_backup_store(bool details) -{ - logger(BN_NODE_BACKUP_STARTED); - const auto start = logger::now(); - if (const auto ec = store_.snapshot([&](auto event_, auto table) - { - if (details) - logger(format(BN_BACKUP) % - full_node::store::events.at(event_) % - full_node::store::tables.at(table)); - })) - { - // system::error::not_a_stream when disk is full. - logger(format(BN_NODE_BACKUP_FAIL) % ec.message()); - return false; - } - - const auto span = duration_cast(logger::now() - start); - logger(format(BN_NODE_BACKUP_COMPLETE) % span.count()); - return true; -} - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_test_reader.cpp b/console/executor_test_reader.cpp deleted file mode 100644 index 31633393f..000000000 --- a/console/executor_test_reader.cpp +++ /dev/null @@ -1,947 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -////#include -#include -#include - -namespace libbitcoin { -namespace node { - -using boost::format; -using namespace network; -using namespace system; - -// arbitrary testing (const). - -void executor::read_test(const hash_digest&) const -{ - logger("Wire size computation."); - const auto start = fine_clock::now(); - const auto top = query_.get_top_associated(); - const auto concurrency = metadata_.configured.node.maximum_concurrency_(); - - size_t size{}; - for (auto height = zero; !cancel_ && height <= top; ++height) - { - const auto link = query_.to_candidate(height); - if (link.is_terminal()) - { - logger(format("Max candidate height is (%1%).") % sub1(height)); - return; - } - - const auto bytes = query_.get_block_size(link); - if (is_zero(bytes)) - { - logger(format("Block (%1%) is not associated.") % height); - return; - } - - size += bytes; - if (is_zero(height % concurrency)) - { - const auto span = duration_cast(fine_clock::now() - start); - logger(format("Wire size (%1%) at (%2%) in (%3%) ms.") % - size % height % span.count()); - } - } - - const auto span = duration_cast(fine_clock::now() - start); - logger(format("Wire size (%1%) at (%2%) in (%3%) ms.") % - size % top % span.count()); -} - -#if defined(UNDEFINED) - -void executor::read_test(const hash_digest&) const -{ - logger(format("Point table body searches: %1% / (%2% + %1%)") % - store_.point.positive_search_count() % - store_.point.negative_search_count()); -} - -void executor::read_test(const hash_digest&) const -{ - using namespace database; - constexpr auto start_tx = 1'000'000_u32; - constexpr auto target_count = 100_size; - constexpr auto dump{ false }; - - // Set ensures unique addresses. - std::set keys{}; - auto tx = start_tx; - - logger(format("Getting first [%1%] output address hashes.") % target_count); - - auto start = fine_clock::now(); - while (!cancel_ && keys.size() < target_count) - { - const auto outputs = query_.get_outputs(tx++); - if (is_null(outputs)) - { - // fault, tx with no outputs. - return; - } - - for (const auto& put: *outputs) - { - keys.emplace(put->script().hash()); - if (cancel_ || keys.size() == target_count) - break; - } - } - - auto span = duration_cast(fine_clock::now() - start); - logger(format("Got first [%1%] unique addresses above tx [%2%] in [%3%] ms.") % - keys.size() % start_tx % span.count()); - - struct out - { - hash_digest address; - - uint32_t bk_fk; - uint32_t bk_height; - hash_digest bk_hash; - - uint32_t tx_fk; - size_t tx_position; - hash_digest tx_hash; - - uint32_t pt_tx_fk; - hash_digest pt_tx_hash; - - uint64_t input_fk; - chain::input::cptr input{}; - - uint64_t output_fk; - chain::output::cptr output{}; - }; - - std_vector outs{}; - outs.reserve(target_count); - - start = fine_clock::now(); - for (auto& key: keys) - { - if (cancel_) - return; - - ////size_t found{}; - auto address_it = store_.address.it(key); - if (address_it.get().is_terminal()) - { - // fault, missing address. - return; - } - - do - { - if (cancel_) - break; - - table::address::record address{}; - if (!store_.address.get(address_it.get(), address)) - { - // fault, missing address. - return; - } - - const auto out_fk = address.output_fk; - table::output::get_parent output{}; - if (!store_.output.get(out_fk, output)) - { - // fault, missing output. - return; - } - - const auto tx_fk = output.parent_fk; - if (!store_.tx.exists(query_.get_tx_key(tx_fk))) - { - // fault, missing tx. - return; - } - - // There may be not-strong txs but we just won't count those. - const auto block_fk = query_.to_block(tx_fk); - if (block_fk.is_terminal()) - continue; - - table::header::get_height header{}; - if (!store_.header.get(block_fk, header)) - { - // fault, missing block. - return; - } - - table::txs::get_position txs{ {}, tx_fk }; - if (!store_.txs.get(query_.to_txs(block_fk), txs)) - { - // fault, missing txs. - return; - } - - point_link pt_fk{}; - input_link in_fk{}; - tx_link pt_tx_fk{}; - - // Get first spender only (may or may not be confirmed). - const auto points = query_.to_spenders(out_fk); - if (!points.empty()) - { - pt_fk = points.front(); - table::ins::record ins{}; - if (!store_.ins.get(pt_fk, ins)) - { - // fault, missing ins. - return; - } - - in_fk = ins.input_fk; - pt_tx_fk = ins.parent_fk; - } - - ////++found; - outs.push_back(out - { - key, - - block_fk, - header.height, - query_.get_header_key(block_fk), - - tx_fk, - txs.position, - query_.get_tx_key(tx_fk), - - pt_tx_fk, - query_.get_tx_key(pt_tx_fk), - - in_fk, - query_.get_input(pt_fk), - - out_fk, - query_.get_output(out_fk) - }); - } - while (address_it.advance()); - - // This affects the clock, so disabled. - ////logger(format("Fetched [%1%] unique payments to address [%2%].") % - //// found % encode_hash(key)); - } - - span = duration_cast(fine_clock::now() - start); - logger(format("Got all [%1%] payments to [%2%] addresses in [%3%] ms.") % - outs.size() % keys.size() % span.count()); - - if (!dump) - return; - - // Write it all... - logger( - "output_script_hash, " - - "ouput_bk_fk, " - "ouput_bk_height, " - "ouput_bk_hash, " - - "ouput_tx_fk, " - "ouput_tx_position, " - "ouput_tx_hash, " - - "input_tx_fk, " - "input_tx_hash, " - - "output_fk, " - "output_script, " - - "input_fk, " - "input_script" - ); - - for (const auto& row: outs) - { - if (cancel_) - break; - - const auto output = !row.output ? "{error}" : - row.output->script().to_string(chain::flags::all_rules); - - const auto input = !row.input ? "{unspent}" : - row.input->script().to_string(chain::flags::all_rules); - - logger(format("%1%, %2%, %3%, %4%, %5%, %6%, %7%, %8%, %9%, %10%, %11%, %12%, %13%") % - encode_hash(row.address) % - - row.bk_fk % - row.bk_height % - encode_hash(row.bk_hash) % - - row.tx_fk % - row.tx_position % - encode_hash(row.tx_hash) % - - row.pt_tx_fk % - encode_hash(row.pt_tx_hash) % - - row.output_fk % - output % - - row.input_fk % - input); - } -} - -void executor::read_test(const hash_digest&) const -{ - database::header_link link{ 350'017_u32 }; - const auto ec = query_.block_confirmable(link); - logger(format("block_confirmable [%1%] at height [%2%].") % ec.message() % - query_.get_height(link)); -} - -void executor::read_test(const hash_digest&) const -{ - auto start = fine_clock::now(); - auto count = query_.header_records(); - uint32_t block{ one }; - - logger("Find strong blocks."); - while (!cancel_ && (block < count) && query_.is_strong_block(block)) - { - ++block; - } - - auto span = duration_cast(fine_clock::now() - start); - logger(format("Top strong block is [%1%] in [%2%] ms.") % sub1(block) % span.count()); - start = fine_clock::now(); - count = query_.header_records(); - uint32_t milestone{ 295'001 }; - - logger("Find milestone blocks."); - while (!cancel_ && (milestone < count) && query_.is_milestone(milestone)) - { - ++milestone; - } - - span = duration_cast(fine_clock::now() - start); - logger(format("Top milestone block is [%1%] in [%2%] ms.") % sub1(milestone) % span.count()); - start = fine_clock::now(); - uint32_t tx{ one }; - - logger("Find strong txs."); - count = query_.tx_records(); - while (!cancel_ && (tx < count) && query_.is_strong_tx(tx)) - { - ++tx; - } - - span = duration_cast(fine_clock::now() - start); - logger(format("Top strong tx is [%1%] in [%2%] ms.") % sub1(tx) % span.count()); -} - -void executor::read_test(const hash_digest&) const -{ - const auto from = 481'824_u32; - const auto top = 840'000_u32; ////query_.get_top_associated(); - const auto start = fine_clock::now(); - - // segwit activation - uint32_t block{ from }; - size_t total{}; - - logger("Get all coinbases."); - while (!cancel_ && (block <= top)) - { - const auto count = query_.get_tx_count(query_.to_candidate(block++)); - if (is_zero(count)) - return; - - total += system::ceilinged_log2(count); - } - - const auto average = total / (top - from); - const auto span = duration_cast(fine_clock::now() - start); - logger(format("Total block depths [%1%] to [%2%] avg [%3%] in [%4%] ms.") - % total % top % average % span.count()); -} - -void executor::read_test(const hash_digest&) const -{ - // Binance wallet address with 1,380,169 transaction count. - // blockstream.info/address/bc1qm34lsc65zpw79lxes69zkqmk6ee3ewf0j77s3h - const auto data = base16_array("0014dc6bf86354105de2fcd9868a2b0376d6731cb92f"); - const chain::script output_script{ data, false }; - const auto mnemonic = output_script.to_string(chain::flags::all_rules); - logger(format("Getting payments to {%1%}.") % mnemonic); - - const auto start = fine_clock::now(); - database::output_links outputs{}; - if (!query_.to_address_outputs(outputs, output_script.hash())) - return; - - const auto span = duration_cast(fine_clock::now() - start); - logger(format("Found [%1%] outputs of {%2%} in [%3%] ms.") % - outputs.size() % mnemonic % span.count()); -} - -// This was caused by concurrent redundant downloads at tail following restart. -// The earlier transactions were marked as confirmed and during validation the -// most recent are found via point.hash association priot to to_block() test. -void executor::read_test(const hash_digest&) const -{ - const auto height = 839'287_size; - const auto block = query_.to_confirmed(height); - if (block.is_terminal()) - { - logger("!block"); - return; - } - - const auto txs = query_.to_transactions(block); - if (txs.empty()) - { - logger("!txs"); - return; - } - - database::tx_link spender_link{}; - const auto hash_spender = system::base16_hash("1ff970ec310c000595929bd290bbc8f4603ee18b2b4e3239dfb072aaca012b28"); - for (auto position = zero; !cancel_ && position < txs.size(); ++position) - { - const auto temp = txs.at(position); - if (query_.get_tx_key(temp) == hash_spender) - { - spender_link = temp; - break; - } - } - - auto spenders = store_.tx.it(hash_spender); - if (spenders.get().is_terminal()) - return; - - // ...260, 261 - size_t spender_count{}; - do - { - const auto foo = spenders.get(); - ++spender_count; - } while(spenders.advance()); - - if (is_zero(spender_count)) - { - logger("is_zero(spender_count)"); - return; - } - - // ...260 - if (spender_link.is_terminal()) - { - logger("spender_link.is_terminal()"); - return; - } - - const auto spender_link1 = query_.to_tx(hash_spender); - if (spender_link != spender_link1) - { - logger("spender_link != spender_link1"); - ////return; - } - - database::tx_link spent_link{}; - const auto hash_spent = system::base16_hash("85f65b57b88b74fd945a66a6ba392a5f3c8a7c0f78c8397228dece885d788841"); - for (auto position = zero; !cancel_ && position < txs.size(); ++position) - { - const auto temp = txs.at(position); - if (query_.get_tx_key(temp) == hash_spent) - { - spent_link = temp; - break; - } - } - - auto spent = store_.tx.it(hash_spent); - if (spent.get().is_terminal()) - return; - - // ...255, 254 - size_t spent_count{}; - do - { - const auto bar = spent.get(); - ++spent_count; - } while (spent.advance()); - - if (is_zero(spent_count)) - { - logger("is_zero(spent_count)"); - return; - } - - // ...254 (not ...255) - if (spent_link.is_terminal()) - { - logger("spent_link.is_terminal()"); - return; - } - - const auto spent_link1 = query_.to_tx(hash_spent); - if (spent_link != spent_link1) - { - logger("spent_link != spent_link1"); - ////return; - } - - const auto tx = query_.to_tx(hash_spender); - if (tx.is_terminal()) - { - logger("!tx"); - return; - } - - if (tx != spender_link) - { - logger("tx != spender_link"); - return; - } - - if (spender_link <= spent_link) - { - logger("spender_link <= spent_link"); - return; - } - - // ...254 - const auto header1 = query_.to_block(spender_link); - if (header1.is_terminal()) - { - logger("header1.is_terminal()"); - return; - } - - // ...255 (the latter instance is not confirmed) - const auto header11 = query_.to_block(add1(spender_link)); - if (!header11.is_terminal()) - { - logger("!header11.is_terminal()"); - return; - } - - // ...260 - const auto header2 = query_.to_block(spent_link); - if (header2.is_terminal()) - { - logger("auto.is_terminal()"); - return; - } - - // ...261 (the latter instance is not confirmed) - const auto header22 = query_.to_block(add1(spent_link)); - if (!header22.is_terminal()) - { - logger("!header22.is_terminal()"); - return; - } - - if (header1 != header2) - { - logger("header1 != header2"); - return; - } - - if (header1 != block) - { - logger("header1 != block"); - return; - } - - const auto ec = query_.block_confirmable(query_.to_confirmed(height)); - logger(format("Confirm [%1%] test (%2%).") % height % ec.message()); -} - -void executor::read_test(const hash_digest&) const -{ - const auto bk_link = query_.to_candidate(804'001_size); - const auto block = query_.get_block(bk_link); - if (!block) - { - logger("!query_.get_block(link)"); - return; - } - - ////const auto tx = query_.get_transaction({ 980'984'671_u32 }); - ////if (!tx) - ////{ - //// logger("!query_.get_transaction(tx_link)"); - //// return; - ////} - //// - ////chain::context ctx{}; - ////if (!query_.get_context(ctx, bk_link)) - ////{ - //// logger("!query_.get_context(ctx, bk_link)"); - //// return; - ////} - //// - ////if (!query_.populate_with_metadata(*tx)) - ////{ - //// logger("!query_.populate_with_metadata(*tx)"); - //// return; - ////} - //// - ////if (const auto ec = tx->confirm(ctx)) - //// logger(format("Error confirming tx [980'984'671] %1%") % ec.message()); - //// - ////// Does not compute spent metadata, assumes coinbase spent and others not. - ////if (!query_.populate_with_metadata(*block)) - ////{ - //// logger("!query_.populate_with_metadata(*block)"); - //// return; - ////} - //// - ////const auto& txs = *block->transactions_ptr(); - ////if (txs.empty()) - ////{ - //// logger("txs.empty()"); - //// return; - ////} - //// - ////for (auto index = one; index < txs.size(); ++index) - //// if (const auto ec = txs.at(index)->confirm(ctx)) - //// logger(format("Error confirming tx [%1%] %2%") % index % ec.message()); - //// - ////logger("Confirm test 1 complete."); - - const auto ec = query_.block_confirmable(bk_link); - logger(format("Confirm test 2 complete (%1%).") % ec.message()); -} - -void executor::read_test(const hash_digest&) const -{ - using namespace database; - constexpr auto frequency = 100'000u; - const auto start = fine_clock::now(); - auto tx = 664'400'000_size; - - // Read all data except genesis (ie. for validation). - while (!cancel_ && (++tx < query_.tx_records())) - { - const tx_link link{ - system::possible_narrow_cast(tx) }; - - ////const auto ptr = query_.get_header(link); - ////if (!ptr) - ////{ - //// logger("Failure: get_header"); - //// break; - ////} - ////else if (is_zero(ptr->bits())) - ////{ - //// logger("Failure: zero bits"); - //// break; - ////} - - ////const auto txs = query_.to_transactions(link); - ////if (txs.empty()) - ////{ - //// logger("Failure: to_txs"); - //// break; - ////} - - const auto ptr = query_.get_transaction(link); - if (!ptr) - { - logger("Failure: get_transaction"); - break; - } - else if (!ptr->is_valid()) - { - logger("Failure: is_valid"); - break; - } - - if (is_zero(tx % frequency)) - logger(format("get_transaction" BN_READ_ROW) % tx % - duration_cast(fine_clock::now() - start).count()); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - const auto span = duration_cast(fine_clock::now() - start); - logger(format("get_transaction" BN_READ_ROW) % tx % span.count()); -} - -void executor::read_test(const hash_digest&) const -{ - constexpr auto hash492224 = base16_hash( - "0000000000000000003277b639e56dffe2b4e60d18aeedb1fe8b7e4256b2a526"); - - logger("HIT TO START"); - std::string line{}; - std::getline(input_, line); - const auto start = fine_clock::now(); - - for (size_t height = 492'224; (height <= 492'224) && !cancel_; ++height) - { - // 2s 0s - const auto link = query_.to_header(hash492224); - if (link.is_terminal()) - { - logger("to_header"); - return; - } - - ////const auto link = query_.to_confirmed(height); - ////if (link.is_terminal()) - ////{ - //// logger("to_confirmed"); - //// return; - ////} - - // 109s 111s - const auto block = query_.get_block(link); - if (!block || !block->is_valid() || block->hash() != hash492224) - { - logger("get_block"); - return; - } - - // 125s 125s - code ec{}; - if ((ec = block->check())) - { - logger(format("Block [%1%] check1: %2%") % height % ec.message()); - return; - } - - // 117s 122s - if (chain::checkpoint::is_conflict( - metadata_.configured.bitcoin.checkpoints, block->hash(), height)) - { - logger(format("Block [%1%] checkpoint conflict") % height); - return; - } - - ////// ???? 125s/128s - ////block->populate(); - - // 191s 215s/212s/208s [independent] - // ???? 228s/219s/200s [combined] - if (!query_.populate_with_metadata(*block)) - { - logger("populate"); - return; - } - - // 182s - database::context ctx{}; - if (!query_.get_context(ctx, link) || ctx.height != height) - { - logger("get_context"); - return; - } - - // Fabricate chain_state context from store context. - chain::context state{}; - state.flags = ctx.flags; - state.height = ctx.height; - state.median_time_past = ctx.mtp; - state.timestamp = block->header().timestamp(); - - // split from accept. - if ((ec = block->check(state))) - { - logger(format("Block [%1%] check2: %2%") % height % ec.message()); - return; - } - - // 199s - const auto& coin = metadata_.configured.bitcoin; - if ((ec = block->accept(state, coin.subsidy_interval_blocks, - coin.initial_subsidy()))) - { - logger(format("Block [%1%] accept: %2%") % height % ec.message()); - return; - } - - // 1410s - if ((ec = block->connect(state))) - { - logger(format("Block [%1%] connect: %2%") % height % ec.message()); - return; - } - - ////for (size_t index = one; index < block->transactions_ptr()->size(); ++index) - ////{ - //// const auto& tx = *block->transactions_ptr()->at(index); - //// if ((ec = tx.connect(state))) - //// { - //// logger(format("Tx (%1%) [%2%] %3%") - //// % index - //// % encode_hash(tx.hash(false)) - //// % ec.message()); - //// } - ////} - - // +10s for all. - logger(format("block:%1%") % height); - ////logger(format("block:%1% flags:%2% mtp:%3%") % - //// ctx.height % ctx.flags % ctx.mtp); - } - - const auto span = duration_cast(fine_clock::now() - start); - logger(format("STOP (%1% secs)") % span.count()); -} - -// TODO: create a block/tx dumper. -void executor::read_test(const hash_digest&) const -{ - constexpr auto link = 600'000_size; - const auto start = logger::now(); - - const auto height = query_.get_height(link); - if (height.is_terminal()) - { - logger("height.is_terminal()"); - return; - } - - if (height != link) - { - logger("height != link"); - return; - } - - code ec{}; - if ((ec = query_.block_confirmable(link))) - { - logger(format("query_.block_confirmable: %1%") % ec.message()); - return; - } - - const auto block = query_.get_block(link); - if (!block) - { - logger("!block"); - return; - } - - if (!block->is_valid()) - { - logger("!block->is_valid()"); - return; - } - - database::context ctx{}; - if (!query_.get_context(ctx, link)) - { - logger("!query_.get_context(ctx, link)"); - return; - } - - logger(format("flags:%1% height:%2% mtp:%3%") % - ctx.flags % ctx.height % ctx.mtp); - - // minimum_block_version and work_required are only for header validate. - chain::context state{}; - state.flags = ctx.flags; - state.height = ctx.height; - state.median_time_past = ctx.mtp; - state.timestamp = block->header().timestamp(); - state.minimum_block_version = 0; - state.work_required = 0; - - block->populate(); - - if (!query_.populate_with_metadata(*block)) - { - logger("!query_.populate(*block)"); - return; - } - - if ((ec = block->check())) - { - logger(format("Block check(): %1%") % ec.message()); - return; - } - - if ((ec = block->check(state))) - { - logger(format("Block check(state): %1%") % ec.message()); - return; - } - - if ((ec = block->accept(state, - metadata_.configured.bitcoin.subsidy_interval_blocks, - metadata_.configured.bitcoin.initial_subsidy()))) - { - logger(format("Block accept(state): %1%") % ec.message()); - return; - } - - if ((ec = block->connect(state))) - { - logger(format("Block connect: %1%") % ec.message()); - return; - } - - logger(format("segregated [%1%]") % to_int(block->is_segregated())); - logger(format("segregated count [%1%]") % block->segregated()); - - const auto span = duration_cast(logger::now() - start); - logger(format("Validated block [%1%] in %2% msec.") % link % span.count()); - - ////constexpr auto tx_hash = base16_hash( - //// "eb2179db6c40bceb02cebcc5c99cf783ed6385b00767c7a5419fe530eaba8bff"); - ////const auto tx_link = query_.to_tx(tx_hash); - ////const auto tx = query_.get_transaction(tx_link); - ////const auto result = query_.populate_without_metadata(*tx); - ////const auto size = tx->serialized_size(false); - ////const auto weight = tx->serialized_size(true); - ////const auto version = tx->version(); - ////const auto locktime = tx->locktime(); - ////const auto fee = tx->fee(); - ////const auto sequence = tx->inputs_ptr()->at(0)->sequence(); - ////const auto& in = tx->inputs_ptr()->at(0)->script(); - ////const auto& out = tx->inputs_ptr()->at(0)->prevout->script(); - ////const auto connect = tx->connect(state); - - ////logger(format("tx_hash [%1%]") % encode_hash(tx_hash)); - ////logger(format("tx_link [%1%]") % tx_link.value); - ////logger(format("result [%1%]") % to_int(result)); - ////logger(format("size [%1%]") % size); - ////logger(format("weight [%1%]") % weight); - ////logger(format("version [%1%]") % version); - ////logger(format("locktime [%1%]") % locktime); - ////logger(format("fee [%1%]") % fee); - ////logger(format("sequence [%1%]") % sequence); - ////logger(format("in [%1%]") % in.to_string(chain::flags::no_rules)); - ////logger(format("out [%1%]") % out.to_string(chain::flags::no_rules)); - ////logger(format("connect [%1%]") % connect.message()); -} - -#endif // UNDEFINED - -} // namespace node -} // namespace libbitcoin diff --git a/console/executor_test_writer.cpp b/console/executor_test_writer.cpp deleted file mode 100644 index 4e62edf7e..000000000 --- a/console/executor_test_writer.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "executor.hpp" -#include "localize.hpp" - -////#include -#include - -namespace libbitcoin { -namespace node { - -using boost::format; -using namespace network; -using namespace system; - -// arbitrary testing (non-const). -void executor::write_test(const hash_digest&) -{ - logger("No write test implemented."); -} - -#if defined(UNDEFINED) - -void executor::write_test(const system::hash_digest&) -{ - for (database::header_link link{ 793'008_u32 }; link < 885'000_u32; ++link) - { - if (!query_.set_block_unknown(link)) - { - logger(format("set_block_unknown fault [%1%].") % link.value); - return; - } - } - - logger(format("set_block_unknown complete.")); -} - -void executor::write_test(const system::hash_digest&) -{ - code ec{}; - size_t count{}; - const auto start = fine_clock::now(); - - const auto fork = query_.get_fork(); - const auto top_associated = query_.get_top_associated_from(fork); - - for (auto height = fork; !cancel_ && !ec && height <= top_associated; - ++height, ++count) - { - const auto block = query_.to_candidate(height); - if (!query_.set_strong(block)) - { - logger(format("set_strong [%1%] fault.") % height); - return; - } - - ////if (ec = query_.block_confirmable(block)) - ////{ - //// logger(format("block_confirmable [%1%] fault (%2%).") % height % ec.message()); - //// return; - ////} - //// - ////if (!query_.set_block_confirmable(block)) - ////{ - //// logger(format("set_block_confirmable [%1%] fault.") % height); - //// return; - ////} - - if (!query_.push_confirmed(block, true)) - { - logger(format("push_confirmed [%1%] fault.") % height); - return; - } - - if (is_zero(height % 1000_size)) - logger(format("write_test [%1%].") % height); - } - - const auto span = duration_cast(fine_clock::now() - start); - logger(format("Set confirmation of %1% blocks in %2% secs.") % count % - span.count()); -} - -void executor::write_test(const system::hash_digest&) -{ - using namespace database; - constexpr auto frequency = 10'000; - const auto start = fine_clock::now(); - logger(BN_OPERATION_INTERRUPT); - - auto height = query_.get_top_candidate(); - while (!cancel_ && (++height < query_.header_records())) - { - // Assumes height is header link. - const header_link link{ possible_narrow_cast(height) }; - - if (!query_.push_confirmed(link, true)) - { - logger("!query_.push_confirmed(link)"); - return; - } - - if (!query_.push_candidate(link)) - { - logger("!query_.push_candidate(link)"); - return; - } - - if (is_zero(height % frequency)) - logger(format("block" BN_WRITE_ROW) % height % - duration_cast(fine_clock::now() - start).count()); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - const auto span = duration_cast(fine_clock::now() - start); - logger(format("block" BN_WRITE_ROW) % height % span.count()); -} - -void executor::write_test(const system::hash_digest&) -{ - using namespace database; - ////constexpr uint64_t fees = 99; - constexpr auto frequency = 10'000; - const auto start = fine_clock::now(); - code ec{}; - - logger(BN_OPERATION_INTERRUPT); - - auto height = zero;//// query_.get_top_confirmed(); - const auto records = query_.header_records(); - while (!cancel_ && (++height < records)) - { - // Assumes height is header link. - const header_link link{ possible_narrow_cast(height) }; - - if (!query_.set_strong(link)) - { - // total sequential chain cost: 18.7 min (now 6.6). - logger("Failure: set_strong"); - break; - } - else if ((ec = query_.block_confirmable(link))) - { - // must set_strong before each (no push, verifies non-use). - logger(format("Failure: block_confirmed, %1%") % ec.message()); - break; - } - ////if (!query_.set_txs_connected(link)) - ////{ - //// // total sequential chain cost: 21 min. - //// logger("Failure: set_txs_connected"); - //// break; - ////} - ////if (!query_.set_block_confirmable(link, fees)) - ////{ - //// // total chain cost: 1 sec. - //// logger("Failure: set_block_confirmable"); - //// break; - //// break; - ////} - ////else if (!query_.push_candidate(link)) - ////{ - //// // total chain cost: 1 sec. - //// logger("Failure: push_candidate"); - //// break; - ////} - ////else if (!query_.push_confirmed(link)) - ////{ - //// // total chain cost: 1 sec. - //// logger("Failure: push_confirmed"); - //// break; - ////} - - if (is_zero(height % frequency)) - logger(format("block" BN_WRITE_ROW) % height % - duration_cast(fine_clock::now() - start).count()); - } - - if (cancel_) - logger(BN_OPERATION_CANCELED); - - const auto span = duration_cast(fine_clock::now() - start); - logger(format("block" BN_WRITE_ROW) % height % span.count()); -} - - -void executor::write_test(const system::hash_digest& hash) -{ - const auto id = encode_hash(hash); - const auto link = query_.to_header(hash); - if (link.is_terminal()) - { - logger(format("Block [%1%] not found.") % id); - } - else if (query_.set_block_unknown(link)) - { - logger(format("Successfully reset block [%1%].") % id); - } - else - { - logger(format("Failed to reset block [%1%].") % id); - } -} - -#endif // UNDEFINED - -} // namespace node -} // namespace libbitcoin diff --git a/console/libbitcoin.ico b/console/libbitcoin.ico deleted file mode 100644 index 79464a847da0b8a53490800a0c3cfde685500e28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370070 zcmeIbRdg&z(yqPdU*9_GT%MbAb-LT?*4}1jnlZCIWA>PtVQi0?8D?ycnVFfHnLTD^ zrY)%@`M&=5$?9xnS67qNl1fr&tyET5Wo0G25g8d7898g#pJx59S^ww%%+g=atiAqc z)~r3a>WP2fs{8-dy~P)={GKyw)?xp5)~r=mjepO-`m9-nm1fO4;e^WX|NW;~Yah!s ze?7D2@A;ou2b}t+S@Y``pXgEell!kclyCB{XP(|Dv!O75)SG)XD&_7|xL@J%sHd=I z>+%-nRl3;|CD<#r&zM?-%oXMWy`JQ89nC(*L6O?oqf~*Wc>?r3x45 z`bEWOq`u#q`^@EE%0292()Q*)uH3sz*S}Zp&y9+OEo<{AETZRgxfd$$zo+-^bJwNZy?XzhYMNqUyTtp;M7!?rTeRDb1JSPA z4@6u1tv~wnvVGAabK-XE&Ap`e->kmhQ`oHbePL*Wb#8U<9e-3gy5rWt=-R6Wqkrr( z5S?&zIofjL{^;QS2VBf`PhsP__sNqs>dpP=zO&eTebL&h^hay`r9awq!~SUJ?FOPF z{y7j`cwRaEeQ;b@S#7hc@`(GQH}_?=%)Pm9s@FY*P3pfN(m{c|)>c?o{e8B=e{{W$ z;gdPg^v_IsPy+~>T|;3`hBi&afjmTI{SWM3+3m>x8If5e)>H!M$*2e{Q0`~bX|Rm zg`JiD06qIg;b#TPt8qPlP}^ldE(!avl)q4UjEwQ|_b=1Ek#X0j2m8n<{g7HGbUm_e=R})V7b+q~lpJzpv89@{CXG<5KQ{*55BISoeM`cYRRvE$~*# z-&AvNxzEyL-l)KYmz^CzXG5-&hzc#IZR?1)B^83grKE02N z`F|?Umuv3Rmhd>A#>a8mC+qI>yp+GJ_4maueO^9}Wv^OH_2!z=APC2 z>Fw{vqBh2%h2}dedhFpLSBE=qAB+w=Xuy4+GQ9T6P;}`<<>=q1t9|wui1ylLAUbHj zINo#T0VkUiuf9@WG^bEowqpLcmhJD_qOel5Kq(&6cG`9z+Gn?c=F$=!RIr`gr{n5uC4ZHH5cTPDv_K323e%2Y~=#=Bj(K-JrN2i`x z7GB!?zS@X6)<|F1)?@yjK8K5~H|uxh+HcQ+Xvb{^Tzg!2ZaLaT^MOazem7n>811dH zzw}~!-O2Zhg{{?(G3I0ZzuT}pVL8FWBJ=h|t1RCaopDMz+F-5z=uq_^a~$Zk25YUb zP*_Uu9I0@UFnwWL-w(?YUe7Bmbo~9We1%QbC$)3UQtlD8(GPXy>dif+`}H!>n7;4J z;^yz0x;E)4EUYr!QTKghBKSbeBfe8vo>Kc=B3WXC&f4F9vssK>#hkCOn8FeYODW7z zV4nEr_`2Eq_N@DTVr@F5+B8rFOv)vC3RJ2)DOB|o(yb={;<2%$wpAz)$MetXHTV4e zD!okG`;SmzdKDkTXNpJ zQSbkgzQqT(X7E$|8$C?*J~|ocqzaqIb#P_od%pY)t~=?cPSdI^p}gMCHR~btM|B)( z=bPh%AJ)K-w`c!9=4y(=>&m@e;>0z2z2=w+G1e(2I!Rwy5A+yG#!38Cp1V2CMB&HH zD^^!~EuP-z(#yv4#Z!duH`~HfCwj@M4AR#4~O)H(dnY#FTyNk zW-Yfj_qxJc3NP#4elGOpp3`s5-<%%zYF$6B@VNr>y03))9lC;_c)tI<^8ByDX9`cn z?d){S0q+QK;qpHu2cQI5St}*P5W#rnsif_?~OK%5^b+qUvyZ-Tjd?b5$nR zBj7jj`#uj}c7H!!V_T>>9k_m=FMObHR{^eb*O07Vj?XSksTbU5HEh^YnbHCeO7#t2L?co#Zt- zwRoLrr2VUX>@ei})vqy9HebHvb)`Oz&ULN0m_OFFo%^oZsG7H~ojs83Q5xZ=l)p&* zw~;IV2xin5&}RiNJ%wdd{@bd*`Hp`UUSkgww{2Xm)N9`l)wrZKzC0Bh4i9ckRBMOT zzOTgz>m)se74^LKewZHqgN@V3y##K2-WoS+aZOuS+S`}6reETE!ef$s-tO;sT^;&N zADhU>+5I>gZ-t+i18ldCAJ#!>n-!Pr)4In%#G25e(*MVGsC<()s^B5SMtsfs^{?)E zTACioAFQeQXXKl9?c|k^Po`b_eyGN(XJJEE{$&^Ii-v}VqkXjI#yZp8cMiI>F4mcL z-C-cwX7m1NzIpnhg=c%L_!vAizGj|?T~sUlFouo>KhOy>%*I-S`r-R{4Qh>*`=eL2 zPPXk9aeTnu1JTVl3`VSFee?Bjbj;ypw`NAY4%~OZt>Lkzl!iO(#IV<^@`Zrsgni7~ zNKCUyo)coJLJ#x}eNc&6GiHtLzIz7UcUY_W?z`dWFU$2sKmR-&U7$6sxBolj%KYV* z!x8Iymt95i4Q{eQzr)I6^Y;aqOvg`qYvYx2*WibH z3kf6NxOGy#zu89p(XYP_JDb75!Qp7TE&HP%e;kfj)1_a)7i)tLKRD>h34YG`cf6jt z{+j*K+_}RMcs%dyaENoTdw?d<)Un0yi)EI z{^R*1+`1>_0z>Pr)*rp~=8&uB=_i+6 zd1(h|<;ll~qKjmgxL|Kz^u78N41W2=aKzf>M;{KmwcQt<8&di295sI8`bmy(b{8W& zr;6DG|5;>CU$o>xebGM;7;tN)M;o!zuG#D_jt_vzHo8SH~P(35c7$~{A7fW)Ti{?t!1yOzFB>x zerH>{v)cW3_1(qim!m5#9dvc1&UfE27#)51KwSQ6*}`%K*G=$4ow1$m&3*4^kp3r> zau4cWY`5y~jV0biHn{ZLTC#uL{IC6vU&F&X=Rc~K>l5l%xpu!77QfyU#T9je zMqH@J)yOlSrapG%ci*hEXWBJ&YAXc&0p<#8s83F)^vO>(`0CBQre~+s+)LsMTOG=m zydVC?p1rNI(Z0TKeC+SJx+Ti)>+92KJ_Vh?Z)2PN6;!|2hO!aHiuse3W?}W?D^<*R zEX6i9xsk3D!uz%DNFU4ey3gs z={^3bM_bpGJbha7AJsK=q#Tv9xP02p+rJaOKf%`y&njvWSVZ= z??N8czE@r~7~;3eT=(3k^KqD_(`%o{=;PQPh&@sKSv=A0fSuN=3MVLdJFQvT^P2o? zPRHv1rgU!4t$hpwh5^HXVZbn87#IZx1e5Pl;*Q21J5#6EE;O;X8%U zK8bbKXU4z&h4m$)9PHN4O8I}Q{92ciJ)duP?6RBp7xt0ZoNDb}{xBM)rOvT^20lHI zbM#KwcTWCUPvyXO%l-<7s2sc1rU}z`h2}7w*eO9Q#4LzRmjIs`RV2l{Wka zG}2S}OV`@K`@Rdm5njjmUqtnPqh(p~PlOM+@cl;DrTiVL$6{@zP3BRxZ|DO4;XBFv z!{6-B!G}R?_ZZtTh0nw5qI@M4^T#yJ&!@+y2mFin_4nG1v4QOl@0+*f_o2_Yob~>l zlI6vg1^74w)y+Ls&Q^TF)!*%fUA<+Vq-F0KedzswChOSjcb#8OY#tGLp>k+kQ{?)6AkA5DFe@4$naeTQegx#!iVt2lYJRa*AXApfJ^Yn2wA zALYSMW2>~u=g3*fx{T&`(vM&AJN`FgR6BB-vrSZ;(tH8>ynM_a*SP-w?5O_GR$FuK zRy;v-)Nbfze0_-Vu}Phn--Y+nuGy39>Yw7{E-k&k$Gph-RcckgR?nqd4`td7*Y1!0 zwr+olZ@K0&k1tmo%NE$}S#B*Wz|thu#$Gw-Ffi=)#^6hjy-(kMI~?7veGtdV*B|?d@ZrWj2e85(0(@XEu|S{O&(TiV!!+mw zoewgV!O!!<=Z&u29I!>dc7hG`RDA2Uo3(%A^LWn`d)Dw-*WWknaKN6BU$pmVwH5o^ zKCs=im*Uzh2cxgF|A;*z?`S{LN=x@e=l`c1;d767$#Ws?b7@o$?!(JsU#7MCuIBG_ zSr_W`vvZ^8pB-{-;J>}4e4B#@_7&Z5&0th64?CZaAG|*l9evn<+be>fL;R25b632F z1V4>0K0o9zK|U|OFy!|CtguAf4l8J1AebTc-{~~Q?`(T44#`|Bq z4@>-9{{Gu=wB&+)(bMwt$bO&oR_~9>Y7_Qhee~f_^vX*^5k4uoUUh{&=l7BQO}a_yBc1S^g`bgPr7G^2Z;BqkGlw_}rvT)?cGP>hF*DrqPCk&psWF z4*AD`tINxJ|L32E-F@mv``#*=BfsUvTiLfmAF$W$%{PXkm*uB&|Gnb9ee7L?|Kfvm zJnA3I?2Tns_6#nyaG&bk@9NI}&sSa^iY~dJ9K9rrQ{Fwa50Slk>`B6xKYQ~AMKhFr zJ+;jbYOmK+Ki{A5m_G5B`|ghSDNkeAq{Llz0gI7QV~9la4Jr9<$w6 zF^+cMX~6j_eM@bN?^0+HK0IFaN3R&~8Ei~@joP5meZ~Ryrp0GO^hJA$R;a^W;u*WE z4d4%8i1(k+KFSB~8;t(GX}sr?_uw55-aqJYM116-<5+*;fOVEeb!oP*ywN+2?nD3V zF@{GhvxxYP#shrTa^?m5VHtPev7BiEj-V0Ri{Inc|L{Sr_oVrbK}H(QJgrgvo84b# z(Y|Wk|0;ja$XVcr{lWO}#s5ajF|9(=qe`dwJAT`l!^HC=?I+frI?nazL{4i~$I(0k zD{jC2c-KE{JNfs=-+Ao!ZE3Z~h;7bj>d=jM#=ZZ;`iIv}2hN!Tt@&fTj@O<{8_U37 zx0Ns7hCWAy>##1Pz2>Y3ox@Pd|4ZdyO}icsPCk*os9m39`0ZjIlswa*Q9Dm+{gdq` ze>C{BU@e#VZ0tuc*3Geo-lz`YeZN;e=Dqdp8K#YWgS{hzFV_}kep2EOQ|qijbH z9IoZCuH|`byaxWN+V*<2@zLVRTL>pQZzTL?makoV$fuorj2(tp+#))s~V!+>GHFkl!k3>XFs1BQV~#X!;@fc>6X zEr6eh6R7f^0zVNt3qAQaW%LThPbQV07*0lKYijLg?PeG-3>XFs1BL;^fMLKeU>GnA z7zPXjh5^HXVZbn87%&VN1`Gp+0mFb{z%XDKFbo(5CJ+OK4YmJ3eg@tC1o>Uo-f(OG z32gt#wCzw|kN5+^F9T=YKB@gTmummb!NS6Z3iy2O!1o6DaeKzJmqmV|IR84{_p*s_ zwxjahH@-ia^|o)vG|t)IO?~l!iSHF&5hiX@-yN?$l)pO{_SNsB6wc5y&ON+YSmLZx z_6WYI@Ug;o+S^s{7uERzliyV1Gn30_iun`U+OHVyN9@+M^M%F!IHSc$YlC+8>vpTp zllkC_&7FB+7?^kr$Op6Ecbn{&iTuqt(W9~$ADGy&fOCo`vY(rM3%Rpc*xE2KQGC$( zfBzyg{SP*fGrQ^I+xSUieo`##rS@*mS4lIv?dG%iyqi^w2TTADzz=&j`>q>##pnIV zXT}87f2Mx}ALwH}pQsys9_HbEJWVBEFH;?-g>{92);^2FJ{r$!&I0U_Tr$&Xb3*Ii z!P!0I+4tlfpE+S`4-?w|@G$u^E9LH~nD_t39u5jKSXSs~qIkC={-MYfTo#GAkYdGEPd-7ua z$od|BJSXl`cQ)hxYM1}05AG8!zMt9;_@4v|oY&H7?Y^jUCrWO;?q7<}j`P;LoCg=L z;X(gbDqJmn+zxSB+!(RG%5aa$@awpZ6Yw1Cf6mdw&uZuAqv5YPK4TM~nJIFVll3^m zw6!nWIE{QDyK|^F8p)5d?i?058|Qb0pA^1T_(I_=J$qb1=X<;H17FH-D{!`H>v4p7 zPp|&R4g!0cMHJ-2)SY{}vf_0V8tXa6etz`t@wY;`fU{}aIQJ$#X8q`%`4?hPx*gs=gg_JehTmP6ofP1cKDZ78~bmE={mWN z#~GdV)QOK;?1o6MIYx|s{Nb*qpt{Sav^yVKXD1f(|J3hIN6PHWK9yq0(8#duwyE>! zFCW;|exk00wbcKe^Zw*FdkSleUu~Wvo|Z>=ucxq8rT?$& zTEF=Gi-q7kcG>xsa*uZX8`zsVnV|{IWr9aI`|0-1e)sK{7IS_y^`YKr&pW+WENt)c z!#20mcUz?|7PfJDGsk3J&?^0CpT{=B8D9@_=l*^$<~CqGHtzrD#+(=J?a#`FuZ*_+ z8@*S>R$;fU=bii}I%v0-Agg@9oqOF{w~?$@_!~WnAFnwdJbX6VYq!TjZN`~aqs^ny zd*lgL*)!BAO}FkNPot0QX#dGXVny@VvDTIOUQc0d*QTA!ADex*m_Mk(qp|UAKA-FK zd6l>5Y@SZv9Zh=ZgL9QKG2SPz#W_%;sZ;n4b8~k_kn)D#h0jKN?bd}Tt8~7j&7;wK z*bchB(44PPnr`2Z^=0#C4g=v?+M}&=Z1)!Lg<=hCwE2YZIen_@|Aj_9FAehxuZxBK zD*eAySIQdZ-RRoc2F;VQrjek>iRKaAkbh&n*MhE1OZjWN(x%1ysg*vOU;W?S`Gt+z zK6#(D=eV8csH|g?eUf=M`W@Q_iJwh!e<#1+n|pf9{S}_=+O0hq)$!7uU-{~o%NX{H z)74k{0o_&jY`m^}b1!DD@4*84+p*R;#C9ov!I<--JkB1V(nqD-BVEbU*OPP8JstGs z-so!H{u{}-lzXE3P5c?qlCW5!$_#@@I9WJ>et1eMOwzKyaHqq9wxb2Pt8`CF@X!v=FSc_iNnZRv{nW4n^4{{}t-d>?d6Uh#Q1ia8J1 ze={pFwkwUw+sXY>{)%edn74QGZlkpRd}-RMnBS)>dHQda{M-WiABXh?|D7-n$Fr>T ze<^=MSK1Z(PG2W{?u7Lo?R7X_7PS^Q+I-U9E9K7&%G({hrhO;8SIVDP{ibiL$&Pz- z&vvC$){V;B$^Bw} z?`qw!hwS9tMrobCgnb0)0AzpMEv)jrlJ*Kz`#`oYjmpr?`=QRE`fchqSwS{%tV@l( zj&rq+x?Qy%-5RU0$EvQG%0>q}8IF+uHv&%b~?SeXS$Lv{S?kudSvt5Ek4)`1FW-VssFn-@4>&~$7<74 zM6dYCM5ojZo#IEzYrblmb!%NY?*9?p61F+ru={P+_dN49M_IuDzA#&rxzp#Y1I77{ zeO!s{%174A+1J}?c^aj!(Zy(gM>lPM)4w@aSk|7lfd5YRb#LzZuC-@+oyTUg!=601 z_pH)prTpn#D{rVUzj~m$|G1+~OT-A+rKVQG`h#8X^n|)icX4|dr zu)odcOU$0rZsnbpPwW>gLwiD;&*UG*wa#hn(AhnB=ExqvVqvGQmA90aO>V%8kq0{~ zTW7qDJ;rX&VDQcG`xAaGf9P4K|F}PpEnkyNae)H+KRavpM(NrA9kx09wi~7G#{J&h z59 zscq4%?}he}lhN0y-_#!dwEcdYZPXuq|J`u(#~**V@Y`?0(XmIAqemVZiq1W|9Br~e zf3*DKebGuw^*L-TJbUWlqgVUA!ZzvFI(Q?zuy1(WeB6v-Oks^Bt;}z{HdO8ZCm$b* zw%@8hdiLp|Xn6RK=&2`$qHVY6k3RfhD0=&?q3ELzhoY;m7>wW>f8Vq}+Iic7XqiQ` zjR|}k)bxMh@2TyO);6QL*PHuF-y6|ogx{Xb>teos?Q6kOU-Z?NBlN%i8vU+q7V7Pb zo_K61y7`8|Xw5(ON3RPbzyCfQ4Gs=R_uo4ht-E@E^x3Dw4kPf7o30;>cHXW(y5iEo z=7E&t~t3_OX-M-*w+k(S5DBWMA~l&vF0L zkMqql(pT4CGZVH-goz);~Ou(I2673{7|&jCjHU7?+iJN zJpRZ~G$-E|?XulK^vTD=(R0rXMJF9sj+T{tvDPa6j(_-knzdy&o;hFbS_5`_7CFPE zu$=mT>^jRvW$VqoTf=Ya`XR*P1Ycuk9hUpA>-M|;V*G#S?V*}y8?M#w`fIfn`=Y+N zasM+e@c#TgeJ(xk(Z74|HsJ2>x933g&;x_fk%z>;kuUoCV!rYDXT#A+$Cjga{u}d> zmtGi({<2(*w}bW@a9G)N!~SSF$s1uAyLHXpyx6xB_T^*OSvD#cbC@jkc}-a~#}56q z9CF}5R4&JT``9CcsqMbAXqZ0y^_Su3vWv?u&C(0^MUP5GL`Hxo(9g8T#pjo!Gfyo? zH(oawfvt_!iSacsFzjRr?jbKc{NPaZ=_kX{4qFdIj5QBGIOur9(9m%7!TZC}f6j`( zAG z@A{s5LqmT=8;DozxywLw=WTiE`HOwg z*=Lp`XrHvqDe3<;gdKSQqYo1XUH_jedM6F<@3VV-Ij*{V&^?3SeEiXHw1vJGmUAKT z2gVTO)oV0AL9W?thZtL7x{0{XdjGS|!af+rYUFnM2N{kzU0VA*CRvU?{!ui|T!1u3 z9~SrjufN9pdWi-5qT`Q>??L-J{5?(wjnl7$hb0&A-VfgoM{m9{kY>9Opv&E~bw*ZfO=^v3J)SOi8dk<7Z&Hu1OyPYdPD6{W}c*J^%3bFQ5yff|GpVx zj5+3lk_VFQe(H&3hZ%6VSTMKbns>>MfbN2K8BXTX0-hsw+=>2Ez}o1Dct~l#eH}#@4-*Lku1nP+7!7C8IRwDV~>n! zo$;IB@PTz!?T=`0cnf&?;QiV;2>H^N@F3nnHVn%MKVi(^UCOtB#vK2A(Rurv>;Wdw zVfr~N^6r8A4miy6ZN?(0QGZ^x&+#okogBESctGd-;pvYh#RI7Srdl6ZW|6*#cBWsSe|9K3RO1T$ z_uzdayn}Xx|L&;y-h1y3xv>V>0a`*wU=#P-~!3nY)x+m`cr2b{D;lCZm^dDmt zdHgJ0=PAe6ri0cQKVB1^)5nX>*B5C^{o*tVTs!xuJKvp=fJWYQ75FMa_3W?W|Ou;bSKj{fMAlhs##{z)6} zr1ho?&n-K;Tvc-2cXlMOR5$=BRH=AL(VBV-7Dn z{WQPPcOj$tJQ?TEbum{$7X|O}V+7cRzmaw-^*_GQS+DSQoUC{;(H#69Sq=RlYYFg< zv(6|-M@W}EAbFiWVeSSlX!oTx?(DnAKy-`f4xT|@`tqZ*23PM%CSdIvIt63ovH4#| zok$LmiUky8Xmpo4tU-I9cQvENU z!2Sqd_AJKqE%S8N7~r{#0m!2866AewL(Cju*X`qVgzdJBY5p;d)6|LnV%`azV$=Hn4FUq=RI{ANCiEJk2{&-WOwPdiEL64wqoe!@NGzM*~!`IYw z>y932qWZL3-=VLVKad~m&ESD+#sT`4{)f&P>z7}mFIsoCen;P=Lnq351!E0!Mdlq# zXfEi>HWm9n-WT^twGO^6t>RMdwzl+0t@6prbH;FF&tr}laBJO+>+q26w~FVo`|ma2 z=pCKUQqn7cgG;rB=Iy4aN2b^Ro5&aJv$Jd@ZE7*W%Fi&Cq za<;C~L!c)@enh{3z06eZ|Ehc{8!2q0(G8{9499i+P3y>ex^<3X{?NAYl9hh!b~(@w zt*mu1zg{#|>A%|lA&-!)WPDE0`@({^nZn2Ujq3Z^asRu$I-Sc7-R_61WyX{AuIjW?Q%@Z{`Y-N%o&k2zyGZM|8Yi-0U?H_ zegB7j@9?mw`hV)p0mDA)<~6oeHT02euW9%j|9$Lx?N&Q7o^XyzBl&YI-*IQOI3LsF zBeTrJ{*TW9Usm&mJ=JeltKZ%g7Dm^;u7C1pyKx3tdMtgN_HMjClX+`*{Y?9B&B*>w z@-%$;=uB69Z^!3vf8S<5GPk%^@5a91O2UC}*KWn`oVMz?)jdB`zI?s8H)kCGZ#mv+ z?-@RE=uh@Xu-=rWZ+3h213yqbg}*A@uU*9ezGbTAb-pp)?`rypI(oUQW;(6;>W4AXuIe*o6@Cneq-uqXj<$Unx)Yn(&-9uD%onI_lnq=AS zcN@H*6+IL-Q(Jd#r<#V9QtqCHeH8o0ua#Njaq2_KFpsFO&r@ITqkdjTVbO;9Sf6*H z&qGW&{;;Ond|ie06*g3me>F#Qn=0O1L1#S6uS1W{`bAHP|HWcqM}?ggc2&RauAub_ z@(A+|uO)Lr&+Lx`3;3YwDXgN9@iTqHI@opN{GHFkl!k3>XFs1BL;^fMLKeU>GnA7zPXjh5^HXVZbn87%&VN1`Gp+0mFb{ zz%XDKFbo(53GHFkl!k3>XH+5d*XA&sxA*z*@jsz*@jsV8*q;{M7hQ3jZHb z55H#3>iM5I4*oaX<35hEiG8!neh;j#Y%XCKFbo(53GHFkl!k3>XFs1BL;^ zfMLKeU>GnA7zPXjh5^HXVZbn87%&VN1`Gp+0mFb{z%XDKFbo(53GHFkl!k z3>XFs1BL;^fMLKeU>GnA7zPXjh5^HXVZbn87%&VN1`Gp+0mFb{z%XDKFbo(53GHFkl!k3>XFs1BL;^fMLKeU>GnA7zPXjh5^HXVZbn87%&VN1`Gp+0mFb{z%XDK zFbo(53GHFkl!k3>XFs1BL;^fMLKeU>GnA7zPXjh5^HXVZbn87%&VN1`Gp+ zf$m|Tu$1zezkB&v-&k832BroE3QI(#-0e|s?%}AEzbGo^4~lvU>nbd%u&}~>Q==Y+ zH|r0f*iAfP4Ta?u7E_pa9O^h*`D$)bSg7(1 z%~cA^Dy*pR7lqXoHV}W>M&;RC;c!>BQvO1f^G3-w_bWW8G@Ghy%FA$QZD1IPF;G}W z^!!;1+V?ylEk;iGQQ=L|*uA2+^F@1yiPpAISfLG^iC?&R#-A0hCmP;T@9(T|fWnc= z?=*#T6|PYJH!IwyZ#|{(io*K}U#jfCw86jnl=dCvzpcJwJivHhZyVU0{%MMZ1KXmV z+POg*AMp3!2frwMqwuN1%c9*I6i!$8kHXaocPTusXRj-Kpzw*pX9`~`@ZR?dKPf=h zzjv{GlNH^kvi* zGlmAH)_C1TdD5-E4W99jd`e}~v{g}~y&0dEj zA7Fj(W$^&*f$FGjO=ht6$~XpOtB;MHjr~cQum4>v>|^wA{BIgzV2*TLXNvxRGx~2j z{=*A?)n0*}rcvK#d|Wa=pW6^uP7^_le=&+IWI60G?-St=nY( zdRaf&ChQZ6_Y2#&X>8=K!AqhW+V*ed|Z{V__r7wa*z3 zXiNV8ui9%(qyGt#O^rS$fY0LhqOg+o{N67l?KJ{zZBU@=0R&w>Fq=7?7{7 zQvO<_|0eveH}|aQ-)w=6|4qQSJQ@5&dtOWVb0h~O{eGHEfJ}gml=Eje8GU2lG?`-} zFd*L-_}IgLpXmi_`5tqmo_Q8998Lrd?HiMaGwB3k|KIQ1d?1()a2|rmzSC6pHGY%8 z_8iH&TT2djQvrXQwPWE-{Ebg^qkp6S8HItu+M@rbjQ+*{u$7omeQ#r;^*0zOtm^#i z$Nsp#5I^wYb6xxJm9D4v9)e>2M8kvegqeu}cg~K^$|-DB30o@mVGHRCwo^DyJm9-& zD=QT9hs?~rH<};6e&@^y?ZN3Otfjprn<-vLVT$UQ@tLc=#6_L4)tmcBd|-O%4T^=` z#}5ZaV>6>|-I;akh(`9(eDdE4w~AJtRCvdYoxQpDbnj}>(fVo+>ENa!E(&wR%QklP zn5{E=ri-r7_WqgfuPu5k=q%jAYNCP7Mdy2q4s^~{4my8Jw9xKeyWjQB6MBDt<<&)- zGkn;&b23*`yoQ48*`?!)<&O0fi|-URRM<%2Zwec$9-BJZq^R@z+*y2@13(MzY|Vn? zes_MJWSBHwrFJOguTVxi&Gx^Z>aTYjd^L?XwKdv(FrSdIU-|yTLFNtNHucs^@ z=qapRUpJ%knb{_ejvf*AIVX2w#x=&!_*{yOMNc~Wx>M{)E-B@&QF+H}oW)1pJ(34@ zP}$PX&tb381}ei1O7~Tl%E6k$4-?P_b5(YoW!`CzUx#r<`OAA7T>m0WuGL=qix!dHC$gN#M$TrYq1?=RfpiJ^lNF9rI7l?TtM0L`xURyA zs@o#%)vtyQku#Vpp}W{e`JSqBT&wy&qrlmw$ZPNf=4R+8;W^A@nA>pfICCH7Ld=cc zP=NnDsQglRS+=)7+Z91o9ln!r|=gA^jzSHv0@VibXU8_c+3Ao zznAP7V_DfN>*~}BgKmHSW5V&o%u_-hgG~@)cnRAX?J@3|hklx1I&nS@w5IBGHSmR% zDgk_~prCTOAU&(Y4ePOlr4^P^5I)?y6I||zZl@{py?I5yJBfaeR*-Hzf4;(13O9;R z->L9`!V|*X3kt6*z{}BLv9GoEjEj+T-lWTGyZ#F*ucwt)BN=$&?mL@-7UeE!J)U!H zCbztE)hAD>-wseaPtkSF7VX?VevFTdx~<&aw8KPy=$#o`kmDHFMnATZ2RbRo^V{iT z=P&>5Nv5Zm#}xD1H0E2Z!)u<5-`dHgea{b>=l`nkgxXqj_IBF3<1~`d3z{>yF`MyR zI(v6^7disuzu1Pc=+-TFiRTA}L)1>_6wVY5E>yT$yy`YLj=<-8bFV2pr+3#Fr!pHq zXd4T{O+1J5nCea(%K^8jk6YW`vG$FQD+@GL%s25zU*%Jy=Z)i4$725A&S^<@5XHjI zqSO5q4i}wjy)}Q9!o{N1>lJQSxKH6>^$+qNdccA9)?P} zhlCIF|J63FcFen5@IkG!$G$4t>#xyZmo@5mp4zx9#r&bd&ScBeaT^r#d#2No#y@mc zqiKVQc&C^@EWO-~FyqcCz)$J;^?f^eV>{vW0*%UP_dDy?cH2wiaw&ha=zo0a4tt$a z{toqV1D&8`YQ~#(urd*8oPA)j4c%GZr>U*7YkSN3N!#NHyi9fr-B`yPYoEk(#cPZQ zBr!LhzqRhd{A|2w-(w^0<2%#k0MQn*rB6HAaq6Blt%YZ2&YwPB7xTxqN&omHyg=|J zKx)gks>=65X82R1x;T60uwB!x#cQcw7I_)6RZ;u5de|48R(8ABop#aw6znh>;b=VX z$2{OB^|5RUe1A%(HlE+?H1DKO3iJWVHO@}+oQU+r{3#=C>f1IEv!^0Uyf)b`=n!N6 zI|()oiMFcZb2{C}C%x=GC&O;V_jO6@KhE}}(yqnAz8RzcGivAv+xw~DgQJwMEq4um zoxRRhsz>(yk776IbeYl%)zo1+(cz@jg?ZokqBYiN#x5t1HjjACNBemzZ4jTAH+khr z)}ttY{l09(>^YA2BxR?J$ujdB-v?p2Yp!LJht2I+Y&z1uIbQd$8!qOzZCsy8p`XU< zS2b;&(Xz#ME9~ozq_mZuC~!!J71F^0$-38FuhlDq~t%>d(?){J$gf#d5%D>Z3G&sLjX0WWUao->hj(MBlXfhO_O$$7X<2 zeAKmiZz7+wzdebqVqyDCl`*Y7+#buIU1{T@e6gjao$B{`b1zB;ke~QOeeHJ||C>)b zt%ov!1MIqbIFl>rw_<*uOq9*jL~rg@l`pXeMK(bGc~)b$w<>5?NB`orSuO8a%H@0q zCHp8*_Yh+<`r7$(2z|T;84%Y~uDi(IjMAm#a9$bGubw!ynKL$?dq-~m$Oo_C#lXcIPG3k@i`6ziRQ{_yyPf2HSq~U(H^UFE;eV;7vSbpP6IIXUB zZ-nlBTj0|*D`NnDV?*0tS4N!yI~FW-``viY$?CQEa=sGp%v3p(^)BU}uPt*$7MKcq z{*!fS_1oUnP(MlS4p=wlnjzt`alUsRlPH4!{$G~aM~iRAn6bpye^^ehhg7$?4_S!UR z!^ybMT0~txX)o98+z**4Cw4xZWm#X&O-28oWu~0T{_D+ss5004%q`{*RGG6Q118Ho zn%~93{`LKn!u#s_aE7HR=1XuBCDxVqs*V2~r|CX(_pVWSH^#5Ll=wD~h=uDD2{_5lu zeCTJ>mX39vo_R*jRj8J~ut}!)Yd6}jSlG0_e~kWX+eGK=VQ($ehqyLvOL|6DI{AYbR#RsroXokzld=}d+EL~r2cYeX^nJxEd+N`Ir zT75kOK0V`UUu_YeTlIZr`W@dtJ@P|Z*EgI|p}i}k!NK^v^SkK(i28Cm`p?E0DrxnO zeJd3J4s19zU^#MFVc$-1-E9Zhh)`V*NgcjxW-gX+0LYXA6tvKV{SY@PiQ z@2mfFeZM*S&(=8=$ukN(o;X9XbG%L@{in3=JX!~uMtcByb6<-7Q{)^Q|7&OqfACqt zYJ84U3g32T6W*1nvKHN0+VyA9vzC^nHVw-X+byqBg8|RCO1T>|Tkfz9?Oexl_EeEcY4h^mOVPpuC8y4{zuwG{;RUKSA{e5dJ2n-l+pK>##zp;>=Xy%nZA_2 zOl6%frL5RgWXD!1Y=d}SoU*>^=$}0R6QhS1@9&gy-x#u1Mvi`71xXKtKgA9{~$ z{p$F(qyGytg-_1S!!D`5tjnrhvt+9oa3~<@pYZ)v2-J)T(<0K;xk*%5pPP-|Ea%< z`J*$1&5~sN9{D9oqoq>*Un*<%#?qMnw@NKDXJuyDcS*anPdfIrr^;qCo$h`8-M+t< zGeu9(3-eQWcf5DA)=v$7K{~;}c&Ei@aw8cN+u+{ZubC<@?cw~ElV-o29x2T>Yo^n^ufID_>$l9|Gufw%SEbyYk_UDWZVys< zvoogl=3c4trsN!;nBPwH|6S(V*ZD2V@6>?)7gSr{ow+h6G11QNqUK+9XGnSMTP#}I ze@gW;x3g$NdG!qB38j3t z_R>sE`u|k&|KjPiG|wU$|Fd(Zl5byh5~==GD>M+Vv1V`G!k53Dm~*U)`NJCYOKj^n zv-!YmVKYlU|3>Un+v(q|q=|hMHMTKlevp;2`f|kdzh6VzV2y=!Sf73>#+<*@$T#E6 zruz!-)7n4hKuyKI%~qN5G1Hj-OBwX1(~35V`F(_q?5$gL((i7LQ`oy9kDKv4!1;Pb zmo_!W-RzY6H_?CNJ*HWlW5s@p4{k^stg|53WT%Zg$7p;ui`I>2;Jm(_I^$n|s`qhL z@ag8#4P<~)K3n@0X|tIA4{1yrh0T;EdwR=G`a7PNrJT)X{2=akch z`8d1qAE$V%VE zJ`VrZxQ;WO{#E~?^dp^~r@i#TebF&T3`BeHHV|#Het)$55`B^JgQ>+2WBNa~F>Sa# zKvVl1kob&)bsN{w=wCAGO5*dKol|$nKL(Oks5{u|A^lC?@)B$z5~&pm+gyAI<_1gefU7M^=AFiB6Ex< zOdNf;YU|(C@251SjZ*HBDNg?%ivCw_TE}+ubt}2Zo|^&nidd@^CaTFdY5-({S|R2gA`lcMe7yuh$@f08b-%U7r;T zyGRCT-`?xK2IK#$FAr%P?~CT|>5CR9_C;GuAMnC+L($Ms%m)SshociUCt&Qg#OQJO8-U84@RGVr{(E>dUuAL)?cS9&33h3&JWOYZT0v3qHAb6O$YGr)5~>v zFpfX**ibY)JnUoxbOVdb+ZSDV>0tEBFVZ9aHXOb8?nr*j7v6nmID!ux@y~&1wH5mu zKY(}eP39Lr{1Eeqo30;>)?c&V=^L3pZ2s5&Xeq5vEG(XZu3{?Vcf8A9%KxV+ZDh^Q zQm5_Fn|q^a{klp&(?Fgm=8tQOURdw@?^`4E8u9_?FRh>cra6Cq|8UgbHyo|E`be7n z_Zo2P2w@(5N^^yH!$JEEI5}aJ<@=%+H7|gFgz5bCy?2QRNQdz5+i{uT56BL;-ZU6( zx@=;@-E-*rm2PjBu8qyJ9$lFmtG&qOO4Sa-Gl z=;M#-X_0Yzan0+~-iJP+e`tG+m1Fv6jqt(y2ctph3p_0ofAjTlv~aLifX)&5AH9N4 z_uaR{?p?n5fb<1k#`s;bgy$2)pM5s$<{-XIE(Pk1`M;ts5dHJ7otR2>%z(Y%EIqb{(c+(JLGBDt+!eW+-LWJ zI{JZz?vPzTqkhLO2)#cxgXjiHx9Nuc5#zY0Qy*V+UabycKBd3@n!)P0PCC{R9)ENw zy5f>p#`oXdNb87~Up(k^3qOeuTztNsZ`5Da1wh-3`DMus{(EtLv0eea2J#6!19=5r zf;{s2YeUg~dk)mdC47_hO7xS+4ZdE!EK@DU1_YaV_JsH5?h{YANd5Fv!#>S={Yv@U zjQ%^MfA+C*_I9Jb1?Q)qTy{FRkbbznNAvJT@1@<}N4x+V!el;-=VAIK7h&&fPqK#a@{2>!GK-EpCmno;@!o$I9psgl4LUs}^#7OT zVt&Ki!lxnrRq0Mu-u_$Y7ax9b(CHcAIoDl17_l~ieIzo{%FFi8sQH9%m#oC4{B=hE zozlP37xVwDVm#TGV4m^wC)x9aw81slPo{75n|#hc2R;(#^V%yhePa)>h zzZ=J~IfCx_7PnW{5f2!^E?20WZY{P&*XE% zwS!K!W}NkO{qaXb5&Fe2A9RN=X&it5y;`0Fuh@R8+O*q>=Amb3@$yT`&eq^{(cuZl zl-(L3@(c0-bn44a%-X>jrXO<2!tv$MR&*;CgZI@r; zNsXQV*9(N%`ZG4br?=(#uCm7V<{Lv!cHVV|<~R@gby2PVGv+c5`~0B8S2T97v~=y5 zu&r!;!Ze<4p~?L|2|VRe9e`EZcIi8h&<1D%zbDdSs|oTpBG~_vIKSmK0oS; zJp=FFch8_(^FS{+C)nEr|3U8=u1%09JPjQsbCh+1^-iC+k6ABa-r#kc)brC%hMZ0l zp1@qhr?t4VwkYMVvhja3<3DqN!q$=j>V1QtyZ1En(T773`Zhm~Zz;UCi&1QJ-jGhs z=jCJO)8Txc{LsmJ`M{_1G3$cp2e21_4jKDzxiOxLBfFEXPiq9&8ITwHKjtX}p9k@~ z?+iJ80X)Lj0X~F%A~s0aHNfY1?<@t@3Q30?bFB74W7iPo_sPexY_j~~&Bya&jwrh| z(Xf8ZiO@+PL!dJa=QqA=7LW8(qqZ>mALaP(cm}>f9;(uUZ{sZ_|Ash+-mvk1PxQ0K z%B}mv=fiq?nht^avQOh<_5y_8^w*4)8)|Kiagw>Yzt32V-VfP=>x(ZaN6U#0$&YzG zx<5as_wQggfE|GUCh@8(^f^1j5469TJqiDm-2go6j8n>0If3yxZ9eqqLqpM$k{`nS z(yqx9TKDBh#<$3Zi880Xo4mK|Vtrb#?2q;nPxO1M;AiM5khhZQr~7ZC|FO_Ndcgf9 z1Jup|{<>~|^oGVnFRy}EXa^fSJ^Y9{D`^*q|G0lX={6+sM3{vGL0CxRK^Fv>M`bp)QeaF}?rp+bb6X+xPHgf>v6Z9GA5Rl=SL%gjyK0GDVIU?Jm zhY0DGGG4BANw05A)+hYE+KTMiDu^uo!2NoF=nnm6}^3Zb4y4}s1H+Mq& zU=F$6)65Uw4@aMT9PbhO$3C@VFlo{K`Lxg$V=%OjUXO8|ISJz^`v`qHV)9Mu2PuD; zCcI``XD)&L0qg&a%f!qlkS9nBjh=MuNEumUNb((c3Hru%Xp6nj*cedO8?PI5`U>*9 zQM%Dq_Q?x~wvRe2u6MEwl=VsNxu6W#Sn?a41aeGLzmir??4>rAZ>Iw^Z(=P4{|)dO z(!!6zc~aW@S-9uU{vPH0H&rh-)ADk9z}PnkIA=|Z@ddg7Z`krZA~~0_1=*RgrrmzO zAdu<7|A!yM^o5;Jm}2kJ+QZ{)jC# zw_;Be?;m+c^Y@YGnb(B#9=?q|Bsx#@9mqy}`{d)wReLSgJ|`6Jq4>ybQ@ zdOvszdP2ruc+ale%O)w%CHQZV&v8dd*O-{&ur5Fv##Z!oT*E8SZ-n(|biKCL{P^bK z(lIixq0I2t4Ya4R(L3CSKk!XoJ|91%u|7$Y=qgV*dV~k?o#YxTdD515s!c=NX`e4K z{#6%&nT~GW>hpiaEpBKR4d-xdj^g()f1^W=f{FzgbCQL`# zYp;swpK>F&`tOA?x&-Wfe16El$i6??}BQ0`}e-2GvE1L<=$9}bbi_CWU#|#PJ*n6o)|qV?Tg;Vze5?gZd7ky z)=pxh|FP4)bP_&) z^nlMjJLGhN-VXrjklXjzIZg*{cs*X27uR4k)L}AbK<-GU`?tnC|IJlbNFRq88-1E) zFD$}ht1xn4zy8NF6fVa6s*NI`yTXlm*frJy{@v(N*U1IB=aNJ7r@$r?Ju>?_jyiO}@pf#jm`9D4Cm8*A zi~i?mE%4;JwLp)BPGZ*9u&W0f2MKrBzA>M`eiofR^9jZWbasts72JV0#$xv7vZoN; zIrDa8Y5yMMSE&C(9{ul|L(U#39IwfbF*U@Q+asWPc!+VX*>mXY3qC1FmH)t98fw|2SkB-}c ze(f0OaQFu0V$R`o&f;PIyJ2kf-zEBYdcYM$|KFyzbJ*siy=IRudL(2*bO_8ZSOY^> zh5eq#hL4fOv8_W7}-?W;jRS zIcx68aS)uQ=|#}tGw%q?O}QG4xqSDW#2N?beYjhE)R(PMjJ*M}H?j+9jyzhPUC$2Iaecw*dU zKP~!w`kA$LI{qFp@3VH#T*#M! z82Up88m9HvymQB`>G})aP5Mpn?Fkhfd*mZ0oJuvAgJ8K_Fn=5=s zu~#cBr_p~G$A9sW*f+>`HEkTW_hekdA6Uy`U5c|tSd&2RWL)vOC47kAuXVcQnD6mR zj4Y2GF*^R6G^Q}-AY(i${^Du;EzKRUwWFTk)z77QA56nrz$LuNx5Y6!`vm@kA7p=z zn6jbUOv*3Jl{b_PLYUTH;}ebfM_BIgnlssa+IC|2>X(5Z1b7c)DP=(S!G6R>`5>c& z-}cwE9dyroem&7Y^D*|0d^^k=J8FEa;134hjijZ&s4q5WhOq&<^5dJw7r&7&u~R}f$a)`R5;Th}z}y+Se&D`%j>`Ds@#lF4^HJu! ziImbW`f{f1l$i>*~B2OSoBM%{C z`|tTOQV;l5vw4FrPZ+QHXVJgrS3W=L%|0k(m=7e+G+WnCp8tRx!MY_r8N3fj><~E5 z!RPN|)(%(`49f!jvzAJKpm(G`*fBGI@aa~UZ>_W#2gBYH#ZkXgfoHnAsS>|xCy#1_1QwN1}kIM0mzPROQVId4n`$Fgs{TVdMdPLgzPu}ns=KlV@cKc(m zgdY^j0o}p#n9HzNqETKU{ST`huvo}p zY&?%-p2N48OP#0n9X~hoWuq*Y$%h(ixTN`1`$6C*obkdw2ljFCKJpSeSpPjA!yAy1 ze1616|KmabbJVU^Xf0sk^nhWXX5kt=0eFLEm^&aJFh78v;S1mwdnjy$v3L3;SldFT zMJKqL2ppW(}BjeD>)-%`qyKTBf2RlBO7uT{ zWdM(#c4BM_@lUpu#ya*Ep@W9Lwv%5V=E2O#v1ed?n>_~LpRtoM($lxcGBNXAZ1Y0i zz`K4w0J;Z$Gv~)ILOZ?znESA1;$>&{Y(b-x5qkx`3mtz_Sqq?yoYB^({`go$&Vl~@ z`@Rm?E_wYQHbUqU!!=T9o4roSylImGJ;SCDA5i4S`U-kNk!QQ zp?UOS*w$bx6YgP#pCD(1b7=NLJ#@d$UR`zMo5%|6yTOMT`wXCcXo%mw9PkEDn?7bO z{gi|ccAuX3XxS}zSpuIZ$R_ASphZ6qfgU}N@c9x$=V4x4!w)|fy+8Co%u|?KAU9pC z{R45CW8dq@C|*Cw_pu*B*ND8t+CVbjQvL=T|10#`bvdZ8tmXi3)wOxD5A62{JvM3! z66o|-(s-L3-$Gp%dip1|rUp%5vw)8#Y^7K$W9^Xn$z6BEd;5^> znQQy9aBdoFe#q$T(ZLp(@fRO2K1~?IbC4gPOX!(3Klm4V%W&^DIs)ZF@$Vxip*!I>>7IOi(CIvHlKpU_Ifc>x_>KQP&Ns-p>dRXH zV9CC<->vC?tZl^jiLWs1q|p^(Kgga*=!Eq{0yF~c5PMrXz6<6!x{U3IZ zjIC+&3Ftb_o)?>8&T#j2VQ&U{Iljpn0BfqO>tCkx6?_^Wd-^9W`UB=bp>(Fzxtn3?X)xNglTJUyo(%wUT;U)-bVl9JpPLZ6bn16KYpp})36`xIy*d|RbMP38ye&RXxXnh zFxFlzA3xkfPl2rwdoIu^qMJNaHV9r9iHv~H)uf#=bj%(>_Bik1@>K=zsj_KlTy)y!v#a?SV}15Mq5iuc3SVnR>s1 z&^kIw_BODG(9=F;z*YzSKuEWK3`akK?H+cB$Q!(e{Q^A2+YF)CWM4OWOKcvo>Bny3 zeXWPEmf`Ekc`x`lO`a9wpA#GXPXPVTGrQs&Wa7?=@70;q&CVzeF<{ptG&2!x0sSZU z=6d=^pU?aq*}$DuA~}J5S=a;leU+3E-hj*j-(jBwdjMG{_2tKY8T(7@AdnmU{bXFq z-(>Uuah?BH_)B3`(PRBtVafjJ#&7J7OSv1wbM|i7@7?&0^Y8_1rO_|44~l(P=qWi*h4WI-AuxAf4uRh=?h~_530Z-80&5$rU&1%Al}w%$ z9rl~i|3r-c@i$8O+a`n_dvl*?-Qh0^>rBY^LOj`Z=RCoW;q1GFFYKYUe)doJIRm;v zXqtTt*!bZqh5i1}JbnUB)LAR!LEB@;fGst01Zx=&KRD#x^K*w}{}}yG9R2qcHkJ(V zsKVFktMSnV^ya?Pxc`q>rYo$VZ~bUIKy;DB{Z##p4xc^4*gWCe8JV9wl)gUL4PzS_ z>K3p&U_Zpxo7d|VOZl5k{+|f>-+!a9nAQWfliuY5(dK7e=L3$P7Z$D1|1$ct;FfU?Xh8eUKHgR>nkXxDD2 zl)qf>&Obu?D2ejxV;-r$^N)F#b`Gem%Gxo@-y61SfpwHvG-^^&m^0PG&7y* zZ1g|Pd5E()GC!o#iDtW57i?0yG8T1vY_t5j(=Ub9H2!~7)faRVJC@hfpF{tyi+IbE@iCjsB-Q{o{Y= zA6h54O&I+^w3W@Zz}SBAXqA<7Q@)xK{ED%)H}{*?Vop{2UZgTloPUQ_<9fT#OZnTo zGtad7dbZn9=aKXjR&w+e?>%}(*!@@`&8BE#=e3lfm_JSB!w#A~OHWN9`u|F0-rAMB zl)Fh~&nACu#1D-Ar~mk$s^4|Awt?vGAdOvD3Ag{9l<})K_p82th3H58sWOI?^8cAa z^#6{^zGPhX!t!dzSEfwa;R#0nGnD>Ac@6s^?MudANHKq+FmaFL2cex|qw!;G_kVMH zAA9_MWnZSkMxvpqF^<;R&lUcvwtIcb@PJbOjw;{v{MX_c&J!Fzkd0ws7x9Agh3D6V z=Wfq!*|+ehc+N`o_$w@^XH#R%f<2qH^deu6g&oyiKTioBVDvx3Y2A;xtzz~f$*)i; ze~oDKC53OsfNyC38PR^=t04To_AnRS93b240JK3*VeJI{3s0{6?B)J$3~e+qI+#-a z&ie9)ePP$uJ~bF%fAY#2<2DyBI7t|PMBz&X{0+A!v$G!AQ}|occ8%?p$HHUApMGK< zwrqMmIR80+q1vR|a#MHfW%O_3f35sFiuNcht?_y{;r%?#=bv!=ppjmnH}|P%e}i1Ffs`ATEY+;Sobuf%u8;uqN@Ka8q3=8+<)cv zaN*Vj-kCsN=IQ-)(fREP|5p1Spnlj`p@Gh2wEb)EwTFQj-%ikd`?T-)fwKjAYXa$} zF})|}Go{>f?bio=U||Q1yWKwj<#*-zO=V1f-qAYiw|om7O;2Gd1^h)wE}QZAYbI;j zvrOWnFuyQ;WhP|+Y?L}}GiY<{_Lko(kEhixS7}T?LbSf4=v!+L*l!q(TN_L>3=|8y z3-8%lM|1YdI>Vu3dXrs4DSu{5e#V*sdQxl&@!!lkLOfT8&gJjL*=N|8-f{a+GuoID z^_TttJEUS^ABFuC4p2By;b4VB6b@6+IG8_5;TVPE6i!f(z9D~#!f6U;DEv#|EQNCv z&Q&;HbajEkMGBWFT&8e^!c_{_C|sv-gXr%jgghTDRj^O@0<%igheHVjM*1~fNk{xC9EUs2!fP&ZGnA7zPXjh5^HXVZbn87%&VN1`Gp+ z0mFb{z%XDKFbo(53GHFkl!k3>XFs1BL;^fMLKeU>GnA7zPXjh5^HXVZbn8 z7%&VN1`Gp+0mFb{z%XDKFbo(53GHFkl!k3>XFs1BL;^fMLKeU>GnA7zPXj zh5^HXVZbn87%&VN1`Gp+0mFb{z%XDKFbo(53GHFkl!k3>XFs1BL;^fMLKe zU>GnA7zPXjh5^HXVZbn87%&VN1`Gp+0mFb{z%XDKFbo(53GHFkl!k3>XFs z1BL;^fMLKeU>GnA7zPXjh5^HXVZbn87%&VN1`Gp+0mFb{z%XDKFbo(53GH zFkl!k3>XFs1BL;^fMLKeU>GnA7zPXjh5^HXVZbn87%&VN1`Gp+0mFb{z%XDKFbo(5 z3GHFkl!k3>XFs1BL;^fMLKeU>GnA7zPXjh5^HXVZbn87%&VN1`Gq^iGf-6 zXDwhYU@c%RU@c%RU@c%RU@c%RU@c%RU@c%RU@c%RU@c%RU@c%RU@c%RU@c%RU@c%R RU@c%RU@c%RFy&j|{|9kI0dfEU diff --git a/console/localize.hpp b/console/localize.hpp deleted file mode 100644 index c9121abbd..000000000 --- a/console/localize.hpp +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_LOCALIZE_HPP -#define LIBBITCOIN_NODE_LOCALIZE_HPP - -/// Localizable messages. - -namespace libbitcoin { -namespace node { - -#define BN_OPERATION_INTERRUPT \ - "Press CTRL-C to cancel operation." -#define BN_OPERATION_CANCELED \ - "CTRL-C detected, canceling operation..." - -// --settings -#define BN_SETTINGS_MESSAGE \ - "These are the configuration settings that can be set." -#define BN_INFORMATION_MESSAGE \ - "Runs a full bitcoin node." - -// --initchain -#define BN_INITIALIZING_CHAIN \ - "Initializing %1% directory..." -#define BN_INITCHAIN_DIRECTORY_ERROR \ - "Failed creating directory %1% with error '%2%'." -#define BN_INITCHAIN_CREATING \ - "Please wait while creating the database..." -#define BN_INITCHAIN_CREATED \ - "Created the database in %1% secs." -#define BN_INITCHAIN_COMPLETE \ - "Created and initialized the database." -#define BN_INITCHAIN_DATABASE_CREATE_FAILURE \ - "Database creation failed with error '%1%'." -#define BN_INITCHAIN_DATABASE_INITIALIZE \ - "Storing genesis block." -#define BN_INITCHAIN_DATABASE_INITIALIZE_FAILURE \ - "Failure storing genesis block." - -// --restore -#define BN_SNAPSHOT_INVALID \ - "Database snapshot disallowed due to corruption '%1%'." -#define BN_RESTORING_CHAIN \ - "Please wait while restoring from most recent snapshot..." -#define BN_RESTORE_MISSING_FLUSH_LOCK \ - "Database is not corrupted, flush lock file is absent." -#define BN_RESTORE_INVALID \ - "Database restore disallowed when corrupt '%1%'." -#define BN_RESTORE_FAILURE \ - "Database restore failed with error '%1%'." -#define BN_RESTORE_COMPLETE \ - "Restored the database in %1% secs." - -// --measure -#define BN_MEASURE_SIZES \ - "Body sizes...\n" \ - " header :%1%\n" \ - " txs :%2%\n" \ - " tx :%3%\n" \ - " point :%4%\n" \ - " input :%5%\n" \ - " output :%6%\n" \ - " ins :%7%\n" \ - " outs :%8%\n" \ - " candidate :%9%\n" \ - " confirmed :%10%\n" \ - " duplicate :%11%\n" \ - " prevout :%12%\n" \ - " strong_tx :%13%\n" \ - " valid_bk :%14%\n" \ - " valid_tx :%15%\n" \ - " filter_bk :%16%\n" \ - " filter_tx :%17%\n" \ - " address :%18%" -#define BN_MEASURE_RECORDS \ - "Table records...\n" \ - " header :%1%\n" \ - " tx :%2%\n" \ - " point :%3%\n" \ - " ins :%4%\n" \ - " outs :%5%\n" \ - " candidate :%6%\n" \ - " confirmed :%7%\n" \ - " duplicate :%8%\n" \ - " strong_tx :%9%\n" \ - " filter_bk :%10%\n" \ - " address :%11%" -#define BN_MEASURE_SLABS \ - "Table slabs..." -#define BN_MEASURE_SLABS_ROW \ - " @tx :%1%, inputs:%2%, outputs:%3%" -#define BN_MEASURE_STOP \ - " input :%1%\n" \ - " output :%2%\n" \ - " seconds :%3%" -#define BN_MEASURE_BUCKETS \ - "Head buckets...\n" \ - " header :%1%\n" \ - " txs :%2%\n" \ - " tx :%3%\n" \ - " point :%4%\n" \ - " duplicate :%5%\n" \ - " prevout :%6%\n" \ - " strong_tx :%7%\n" \ - " valid_bk :%8%\n" \ - " valid_tx :%9%\n" \ - " filter_bk :%10%\n" \ - " filter_tx :%11%\n" \ - " address :%12%" -#define BN_MEASURE_COLLISION_RATES \ - "Collision rates...\n" \ - " header :%1%\n" \ - " tx :%2%\n" \ - " point :%3%\n" \ - " strong_tx :%4%\n" \ - " valid_tx :%5%\n" \ - " address :%6%" -#define BN_MEASURE_PROGRESS_START \ - "Thinking..." -#define BN_MEASURE_PROGRESS \ - "Chain progress...\n" \ - " fork pt :%1%\n" \ - " top conf :%2%:%3%\n" \ - " top cand :%4%:%5%\n" \ - " top assoc :%6%\n" \ - " associated:%7%\n" \ - " wire conf :%8%\n" \ - " wire cand :%9%" - -// --read -#define BN_READ_ROW \ - ": %1% in %2% secs." - -// --write -#define BN_WRITE_ROW \ - ": %1% in %2% span." - -// run/general - -#define BN_CREATE \ - "create::%1%(%2%)" -#define BN_OPEN \ - "open::%1%(%2%)" -#define BN_CLOSE \ - "close::%1%(%2%)" -#define BN_BACKUP \ - "snapshot::%1%(%2%)" -#define BN_RESTORE \ - "restore::%1%(%2%)" -#define BN_RELOAD \ - "reload::%1%(%2%)" -#define BN_CONDITION \ - "condition::%1%(%2%)" - -#define BN_NODE_INTERRUPT \ - "Press CTRL-C to stop the node." -#define BN_DATABASE_STARTED \ - "Database is started (clean)." -#define BN_DATABASE_STARTED_DIRTY \ - "Database is started (dirty)." -#define BN_NETWORK_STARTING \ - "Please wait while network is starting..." -#define BN_NODE_START_FAIL \ - "Node failed to start with error '%1%'." -#define BN_NODE_UNAVAILABLE \ - "Command not available until node started." - -#define BN_NODE_BACKUP_STARTED \ - "Snapshot is started." -#define BN_NODE_BACKUP_FAIL \ - "Snapshot failed with error '%1%'." -#define BN_NODE_BACKUP_COMPLETE \ - "Snapshot complete in %1% secs." - -#define BN_RELOAD_SPACE \ - "Free [%1%] bytes of disk space to restart." -#define BN_RELOAD_INVALID \ - "Reload disallowed due to database corruption '%1%'." -#define BN_NODE_RELOAD_STARTED \ - "Reload from disk full is started." -#define BN_NODE_RELOAD_COMPLETE \ - "Reload from disk full in %1% secs." -#define BN_NODE_RELOAD_FAIL \ - "Reload failed with error '%1%'." - -#define BN_NODE_UNRECOVERABLE \ - "Node is not in recoverable condition." -#define BN_NODE_DISK_FULL \ - "Node cannot resume because its disk is full." -#define BN_NODE_OK \ - "Node is ok." - -#define BN_NODE_REPORT_WORK \ - "Requested channel work report [%1%]." - -#define BN_NODE_STARTED \ - "Node is started." -#define BN_NODE_RUNNING \ - "Node is running." - -#define BN_UNINITIALIZED_DATABASE \ - "The %1% database directory does not exist." -#define BN_UNINITIALIZED_CHAIN \ - "The %1% database is not initialized, delete and retry." -#define BN_DATABASE_START_FAIL \ - "Database failed to start with error '%1%'." -#define BN_DATABASE_STOPPING \ - "Please wait while database is stopping..." -#define BN_DATABASE_STOP_FAIL \ - "Database failed to stop with error '%1%'." -#define BN_DATABASE_TIMED_STOP \ - "Database stopped successfully in %1% secs." - -#define BN_NETWORK_STOPPING \ - "Please wait while network is stopping..." -#define BN_NODE_STOP_CODE \ - "Node stopped with code %1%." -#define BN_NODE_STOPPED \ - "Node stopped successfully." -#define BN_CHANNEL_LOG_PERIOD \ - "Log period: %1%" -#define BN_CHANNEL_STOP_TARGET \ - "Stop target: %1%" - -#define BN_VERSION_MESSAGE \ - "Version Information...\n" \ - "libbitcoin-node: %1%\n" \ - "libbitcoin-database: %2%\n" \ - "libbitcoin-network: %3%\n" \ - "libbitcoin-system: %4%" - -#define BN_HARDWARE_HEADER \ - "Hardware configuration..." -#define BN_HARDWARE_TABLE1 \ - "platform:%1%." -#define BN_HARDWARE_TABLE2 \ - "platform:%1% compiled:%2%." - -#define BN_LOG_TABLE_HEADER \ - "Log system configuration..." -#define BN_LOG_TABLE \ - "compiled:%1% enabled:%2%." - -#define BN_LOG_INITIALIZE_FAILURE \ - "Failed to initialize logging." -#define BN_USING_CONFIG_FILE \ - "Using config file: %1%" -#define BN_USING_DEFAULT_CONFIG \ - "Using default configuration settings." -#define BN_LOG_HEADER \ - "====================== startup =======================" -#define BN_NODE_FOOTER \ - "====================== shutdown ======================" -#define BN_NODE_TERMINATE \ - "Press to exit..." - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/console/main.cpp b/console/main.cpp deleted file mode 100644 index 0a4a45c0f..000000000 --- a/console/main.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "embedded/embedded.hpp" -#include "executor.hpp" - -// This is some experimental code to explore emission of win32 stack dump. -#ifdef HAVE_MSC -#include "stack_trace.hpp" - -namespace libbitcoin { -namespace system { - std::istream& cin = cin_stream(); - std::ostream& cout = cout_stream(); - std::ostream& cerr = cerr_stream(); - int main(int argc, char* argv[]); -} // namespace system -} // namespace libbitcoin - -namespace bc = libbitcoin; -std::filesystem::path symbols_path{}; - -int wmain(int argc, wchar_t* argv[]) -{ - __try - { - return bc::system::call_utf8_main(argc, argv, &bc::system::main); - } - __except (dump_stack_trace(GetExceptionCode(), GetExceptionInformation())) - { - return -1; - } -} - -// This is invoked by dump_stack_trace. -void handle_stack_trace(const std::string& trace) -{ - if (trace.empty()) - { - bc::system::cout << "<>" << std::endl; - return; - } - - bc::system::cout << "<>" << std::endl; - bc::system::cout << trace << std::endl; - bc::system::cout << "<>" << std::endl; -} - -// This is invoked by dump_stack_trace. -std::wstring pdb_path() -{ - return bc::system::extended_path(symbols_path); -} - -#else -BC_USE_LIBBITCOIN_MAIN -#endif - -/// Invoke this program with the raw arguments provided on the command line. -/// All console input and output streams for the application originate here. -int bc::system::main(int argc, char* argv[]) -{ - using namespace bc; - using namespace bc::system; - using namespace bc::network; - using namespace bc::node; - using namespace bc::server; - - // en.cppreference.com/w/cpp/io/ios_base/sync_with_stdio - std::ios_base::sync_with_stdio(false); - set_utf8_stdio(); - - const web_pages web_server{}; - const explore_pages block_explorer{}; - parser metadata(chain::selection::mainnet, block_explorer, web_server); - - const auto& args = const_cast(argv); - - if (!metadata.parse(argc, args, cerr)) - return -1; - -#if defined(HAVE_MSC) - symbols_path = metadata.configured.log.symbols; -#endif - - set_memory_priority(metadata.configured.node.memory_priority_()); - - executor host(metadata, cin, cout, cerr); - return host.dispatch() ? 0 : -1; -} diff --git a/console/stack_trace.cpp b/console/stack_trace.cpp deleted file mode 100644 index 760e570c5..000000000 --- a/console/stack_trace.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "stack_trace.hpp" - -#include - -// This is some experimental code to explore emission of win32 stack dump. -#if defined(HAVE_MSC) - -#include -#include -#include -#include -#include - -#include -#include -#include - -// This pulls in libs required for APIs used below. -#pragma comment(lib, "psapi.lib") -#pragma comment(lib, "dbghelp.lib") - -// Must define pdb_path() and handle_stack_trace when using dump_stack_trace. -extern std::wstring pdb_path(); -extern void handle_stack_trace(const std::string& trace); - -constexpr size_t depth_limit{ 10 }; - -using namespace bc; -using namespace system; - -struct module_data -{ - std::wstring image; - std::wstring module; - DWORD dll_size; - void* dll_base; -}; - -inline STACKFRAME64 get_stack_frame(const CONTEXT& context) -{ - STACKFRAME64 frame{}; - -#if defined(HAVE_X64) - frame.AddrPC.Offset = context.Rip; - frame.AddrPC.Mode = AddrModeFlat; - frame.AddrStack.Offset = context.Rsp; - frame.AddrStack.Mode = AddrModeFlat; - frame.AddrFrame.Offset = context.Rbp; - frame.AddrFrame.Mode = AddrModeFlat; -#else - frame.AddrPC.Offset = context.Eip; - frame.AddrPC.Mode = AddrModeFlat; - frame.AddrStack.Offset = context.Esp; - frame.AddrStack.Mode = AddrModeFlat; - frame.AddrFrame.Offset = context.Ebp; - frame.AddrFrame.Mode = AddrModeFlat; -#endif - - return frame; -} - -static std::string get_undecorated(HANDLE process, DWORD64 address) -{ - // Including null terminator. - constexpr DWORD maximum_characters{ 1024 }; - - // First character in the name is accounted for in structure size. - constexpr size_t struct_size{ sizeof(SYMBOL_INFOW) + - sub1(maximum_characters) * sizeof(wchar_t) }; - - BC_PUSH_WARNING(NO_NEW_OR_DELETE) - const auto symbol = std::unique_ptr( - pointer_cast(operator new(struct_size))); - BC_POP_WARNING() - - if (!symbol) - return {}; - - symbol->SizeOfStruct = sizeof(SYMBOL_INFOW); - symbol->TypeIndex = 0; - symbol->Reserved[0] = 0; - symbol->Reserved[1] = 0; - symbol->Index = 0; - symbol->Size = 0; - symbol->ModBase = 0; - symbol->Flags = 0; - symbol->Value = 0; - symbol->Address = 0; - symbol->Register = 0; - symbol->Scope = 0; - symbol->Tag = 0; - symbol->NameLen = 0; - symbol->MaxNameLen = maximum_characters; - symbol->Name[0] = wchar_t{ '\0' }; - - DWORD64 displace{}; - if (SymFromAddrW(process, address, &displace, symbol.get()) == FALSE || - is_zero(symbol->MaxNameLen)) - { - return {}; - } - - std::wstring undecorated{}; - undecorated.resize(maximum_characters); - undecorated.resize(UnDecorateSymbolNameW(symbol->Name, - undecorated.data(), maximum_characters, UNDNAME_COMPLETE)); - - return to_utf8(undecorated); -} - -inline DWORD get_machine(HANDLE process) THROWS -{ - constexpr size_t module_buffer_size{ 4096 }; - - // Get required bytes by passing zero. - DWORD bytes{}; - if (EnumProcessModules(process, NULL, 0u, &bytes) == FALSE) - throw(std::logic_error("EnumProcessModules")); - - std_vector handles{}; - handles.resize(bytes / sizeof(HMODULE)); - const auto handles_buffer_size = possible_narrow_cast( - handles.size() * sizeof(HMODULE)); - - // Get all elements into contiguous byte vector. - if (EnumProcessModules(process, &handles.front(), handles_buffer_size, - &bytes) == FALSE) - throw(std::logic_error("EnumProcessModules")); - - std_vector modules{}; - std::transform(handles.begin(), handles.end(), - std::back_inserter(modules), [process](const auto& handle) THROWS - { - MODULEINFO info{}; - if (GetModuleInformation(process, handle, &info, - sizeof(MODULEINFO)) == FALSE) - throw(std::logic_error("GetModuleInformation")); - - module_data data{}; - data.dll_base = info.lpBaseOfDll; - data.dll_size = info.SizeOfImage; - - data.image.resize(module_buffer_size); - if (GetModuleFileNameExW(process, handle, data.image.data(), - module_buffer_size) == FALSE) - throw(std::logic_error("GetModuleFileNameExA")); - - data.module.resize(module_buffer_size); - if (GetModuleBaseNameW(process, handle, data.module.data(), - module_buffer_size) == FALSE) - throw(std::logic_error("GetModuleBaseNameA")); - - SymLoadModuleExW(process, NULL, data.image.data(), data.module.data(), - reinterpret_cast(data.dll_base), data.dll_size, NULL, - SLMFLAG_NO_SYMBOLS); - - return data; - }); - - const auto header = ImageNtHeader(modules.front().dll_base); - - if (is_null(header)) - throw(std::logic_error("ImageNtHeader")); - - return header->FileHeader.Machine; -} - -DWORD dump_stack_trace(unsigned, EXCEPTION_POINTERS* exception) THROWS -{ - if (is_null(exception) || is_null(exception->ContextRecord)) - return EXCEPTION_EXECUTE_HANDLER; - - // NULL for defaults, otherwise semicolon seperated directories. - const auto search = pdb_path().empty() ? NULL : pdb_path().c_str(); - const auto process = GetCurrentProcess(); - const auto thread = GetCurrentThread(); - - if (SymInitializeW(process, search, TRUE) == FALSE) - throw(std::logic_error("SymInitialize")); - - SymSetOptions(SymGetOptions() - | SYMOPT_DEFERRED_LOADS - | SYMOPT_LOAD_LINES - | SYMOPT_UNDNAME); - - DWORD displace{}; - std::ostringstream tracer{}; - IMAGEHLP_LINE64 line{ sizeof(IMAGEHLP_LINE64) }; - const auto machine = get_machine(process); - const auto context = exception->ContextRecord; - auto it = get_stack_frame(*context); - auto iteration = zero; - - do - { - // Get undecorated function name. - const auto name = get_undecorated(process, it.AddrPC.Offset); - - // When this? - if (name == "main") - { - tracer << "((no symbols))"; - break; - } - - // Compiled in release mode. - if (name == "RaiseException") - { - tracer << "[[no symbols]]"; - break; - } - - // Get line. - if (SymGetLineFromAddr64(process, it.AddrPC.Offset, &displace, - &line) == FALSE) - break; - - // Write line. - tracer - << name << " -> " << line.FileName << "(" << line.LineNumber << ")" - << std::endl; - - // Advance iterator. - if (StackWalk64(machine, process, thread, &it, context, NULL, - SymFunctionTableAccess64, SymGetModuleBase64, NULL) == FALSE) - break; - - } while ((iteration++ < depth_limit) && !is_zero(it.AddrReturn.Offset)); - - handle_stack_trace(tracer.str()); - - if (SymCleanup(process) == FALSE) - throw(std::logic_error("SymCleanup")); - - return EXCEPTION_EXECUTE_HANDLER; -} - -#endif diff --git a/console/stack_trace.hpp b/console/stack_trace.hpp deleted file mode 100644 index c6ac8d60f..000000000 --- a/console/stack_trace.hpp +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_STACK_TRACE_HPP -#define LIBBITCOIN_STACK_TRACE_HPP - -#include - - // This is some temporary code to explore emission of win32 stack dump. -#if defined(HAVE_MSC) - -#include - -DWORD dump_stack_trace(unsigned code, EXCEPTION_POINTERS* exception) THROWS; - -#endif -#endif diff --git a/include/bitcoin/node.hpp b/include/bitcoin/node.hpp index 0f58e11ed..759d75b71 100644 --- a/include/bitcoin/node.hpp +++ b/include/bitcoin/node.hpp @@ -24,16 +24,10 @@ #include #include #include -#include #include #include #include -#include -#include #include -#include -#include -#include #include #include #include @@ -47,54 +41,27 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include #include #include #include -#include -#include -#include #include #include #include #include #include -#include -#include #include #include #include -#include -#include -#include #include #include -#include #include #include -#include #include #include #include #include -#include #include #endif diff --git a/include/bitcoin/node/channels/channel_electrum.hpp b/include/bitcoin/node/channels/channel_electrum.hpp deleted file mode 100644 index b000dde73..000000000 --- a/include/bitcoin/node/channels/channel_electrum.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_CHANNELS_CHANNEL_ELECTRUM_HPP -#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_ELECTRUM_HPP - -#include -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -// TODO: strip extraneous args before electrum version dispatch. -/// Channel for electrum channels (non-http json-rpc). -class BCN_API channel_electrum - : public node::channel, - public network::channel_rpc, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - using interface_t = interface::electrum; - using options_t = typename network::channel_rpc::options_t; - - inline channel_electrum(const network::logger& log, - const network::socket::ptr& socket, uint64_t identifier, - const node::configuration& config, const options_t& options) NOEXCEPT - : node::channel(log, socket, identifier, config), - network::channel_rpc(log, socket, identifier, - config.network, options), - network::tracker(log) - { - } - - /// Properties. - /// ----------------------------------------------------------------------- - - inline void set_client(const std::string& name) NOEXCEPT - { - name_ = name; - } - - inline const std::string& client() const NOEXCEPT - { - return name_; - } - - inline void set_version(electrum_version version) NOEXCEPT - { - version_ = version; - } - - inline electrum_version version() const NOEXCEPT - { - return version_; - } - -private: - // These are protected by strand. - electrum_version version_{ electrum_version::v0_0 }; - std::string name_{}; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/channels/channel_http.hpp b/include/bitcoin/node/channels/channel_http.hpp deleted file mode 100644 index a8d6f5864..000000000 --- a/include/bitcoin/node/channels/channel_http.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_CHANNELS_CHANNEL_HTTP_HPP -#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_HTTP_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API channel_http - : public node::channel, - public network::channel_http, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - - inline channel_http(const network::logger& log, - const network::socket::ptr& socket, uint64_t identifier, - const node::configuration& config, const options_t& options) NOEXCEPT - : node::channel(log, socket, identifier, config), - network::channel_http(log, socket, identifier, config.network, options), - network::tracker(log) - { - } -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/channels/channel_stratum_v1.hpp b/include/bitcoin/node/channels/channel_stratum_v1.hpp deleted file mode 100644 index 5fe39dde7..000000000 --- a/include/bitcoin/node/channels/channel_stratum_v1.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_CHANNELS_CHANNEL_STRATUM_V1_HPP -#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_STRATUM_V1_HPP - -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -/// Channel for stratum v1 channels (non-http json-rpc). -class BCN_API channel_stratum_v1 - : public node::channel, - public network::channel_rpc, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - using interface_t = interface::stratum_v1; - using options_t = typename network::channel_rpc::options_t; - - inline channel_stratum_v1(const network::logger& log, - const network::socket::ptr& socket, uint64_t identifier, - const node::configuration& config, const options_t& options) NOEXCEPT - : node::channel(log, socket, identifier, config), - network::channel_rpc(log, socket, identifier, - config.network, options), - network::tracker(log) - { - } -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/channels/channel_stratum_v2.hpp b/include/bitcoin/node/channels/channel_stratum_v2.hpp deleted file mode 100644 index faee09f08..000000000 --- a/include/bitcoin/node/channels/channel_stratum_v2.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_CHANNELS_CHANNEL_STRATUM_V2_HPP -#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_STRATUM_V2_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -/// Channel for stratum v2 (custom protocol, not implemented). -class BCN_API channel_stratum_v2 - : public node::channel, - public network::channel, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - - inline channel_stratum_v2(const network::logger& log, - const network::socket::ptr& socket, uint64_t identifier, - const node::configuration& config, const options_t& options) NOEXCEPT - : node::channel(log, socket, identifier, config), - network::channel(log, socket, identifier, config.network, options), - network::tracker(log) - { - } -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/channels/channel_ws.hpp b/include/bitcoin/node/channels/channel_ws.hpp deleted file mode 100644 index 88944b8c1..000000000 --- a/include/bitcoin/node/channels/channel_ws.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_CHANNELS_CHANNEL_WS_HPP -#define LIBBITCOIN_NODE_CHANNELS_CHANNEL_WS_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API channel_ws - : public node::channel, - public network::channel_ws, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - - /// Subscribe to messages post-upgrade (requires strand). - /// Event handler is always invoked on the channel strand. - template - inline void subscribe(auto&& ) NOEXCEPT - { - BC_ASSERT(stranded()); - ////using message_handler = distributor_ws::handler; - ////ws_distributor_.subscribe(std::forward(handler)); - } - - /// Serialize and write websocket message to peer (requires strand). - /// Completion handler is always invoked on the channel strand. - inline void send(system::data_chunk&& message, bool binary, - network::result_handler&& handler) NOEXCEPT - { - BC_ASSERT(stranded()); - using namespace std::placeholders; - - // TODO: Serialize message. - const auto ptr = system::move_shared(std::move(message)); - network::count_handler complete = std::bind(&channel_ws::handle_send_ws, - shared_from_base(), _1, _2, ptr, std::move(handler)); - - if (!ptr) - { - complete(network::error::bad_alloc, {}); - return; - } - - // TODO: serialize message to send. - // TODO: websocket is full duplex, so writes must be queued. - ws_write(network::asio::const_buffer{ ptr->data(), ptr->size() }, - binary, std::move(complete)); - } - - inline channel_ws(const network::logger& log, - const network::socket::ptr& socket, uint64_t identifier, - const node::configuration& config, const options_t& options) NOEXCEPT - : node::channel(log, socket, identifier, config), - network::channel_ws(log, socket, identifier, config.network, options), - network::tracker(log) - { - } - -protected: - /// Dispatch websocket buffer via derived handlers (override to handle). - /// Override to handle dispatch, must invoke read_request() on complete. - inline void dispatch_ws(const network::http::flat_buffer&, - size_t) NOEXCEPT override - { - const std::string welcome{ "Websocket libbitcoin/4.0" }; - send(system::to_chunk(welcome), false, [this](const code& ec) NOEXCEPT - { - // handle_send alread stops channel on ec. - // One and only one handler of message must restart read loop. - // In half duplex this happens only after send (ws full duplex). - if (!ec) receive(); - }); - } - - inline void handle_send_ws(const code& ec, size_t, const system::chunk_ptr&, - const network::result_handler& handler) NOEXCEPT - { - if (ec) stop(ec); - handler(ec); - } -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/channels/channels.hpp b/include/bitcoin/node/channels/channels.hpp index 10627939a..0ff4c624a 100644 --- a/include/bitcoin/node/channels/channels.hpp +++ b/include/bitcoin/node/channels/channels.hpp @@ -20,12 +20,6 @@ #define LIBBITCOIN_NODE_CHANNELS_CHANNELS_HPP #include -#include -#include #include -#include -#include -#include #endif - diff --git a/include/bitcoin/node/chasers/chaser.hpp b/include/bitcoin/node/chasers/chaser.hpp index af1ede51a..7489a6da7 100644 --- a/include/bitcoin/node/chasers/chaser.hpp +++ b/include/bitcoin/node/chasers/chaser.hpp @@ -120,12 +120,16 @@ class BCN_API chaser /// Properties. /// ----------------------------------------------------------------------- - /// Node configuration settings. - const node::configuration& config() const NOEXCEPT; - /// Thread safe synchronous archival interface. query& archive() const NOEXCEPT; + /// Configuration settings for all libraries. + const node::configuration& node_config() const NOEXCEPT; + const system::settings& system_settings() const NOEXCEPT; + const database::settings& database_settings() const NOEXCEPT; + const network::settings& network_settings() const NOEXCEPT; + const node::settings& node_settings() const NOEXCEPT; + /// Top candidate|confirmed is within configured span from current time. bool is_current(bool confirmed) const NOEXCEPT; diff --git a/include/bitcoin/node/configuration.hpp b/include/bitcoin/node/configuration.hpp index bb0377fae..6e5c86e81 100644 --- a/include/bitcoin/node/configuration.hpp +++ b/include/bitcoin/node/configuration.hpp @@ -29,42 +29,13 @@ namespace node { class BCN_API configuration { public: - configuration(system::chain::selection context, - const server::settings::embedded_pages& explore, - const server::settings::embedded_pages& web) NOEXCEPT; - - /// Environment. - std::filesystem::path file; - - /// Information. - bool help{}; - bool hardware{}; - bool settings{}; - bool version{}; - - /// Actions. - bool newstore{}; - bool backup{}; - bool restore{}; - - /// Chain scans. - bool flags{}; - bool information{}; - bool slabs{}; - bool buckets{}; - bool collisions{}; - - /// Ad-hoc Testing. - system::config::hash256 test{}; - system::config::hash256 write{}; + configuration(system::chain::selection context) NOEXCEPT; /// Settings. - log::settings log; - server::settings server; - node::settings node; - network::settings network; - database::settings database; system::settings bitcoin; + database::settings database; + network::settings network; + node::settings node; }; } // namespace node diff --git a/include/bitcoin/node/define.hpp b/include/bitcoin/node/define.hpp index 9f8a643e5..59f583d97 100644 --- a/include/bitcoin/node/define.hpp +++ b/include/bitcoin/node/define.hpp @@ -102,37 +102,6 @@ using type_id = network::messages::peer::inventory_item::type_id; } // namespace node } // namespace libbitcoin -/// Augment limited xcode placeholder defines (10 vs. common 20). -/// --------------------------------------------------------------------------- -#if defined (HAVE_XCODE) - -/// Define custom placeholder types in the global namespace to avoid conflicts. -struct placeholder_11 {}; -struct placeholder_12 {}; -struct placeholder_13 {}; - -/// Specialize std::is_placeholder within the std namespace. -namespace std -{ - template <> - struct is_placeholder<::placeholder_11> : integral_constant {}; - template <> - struct is_placeholder<::placeholder_12> : integral_constant {}; - template <> - struct is_placeholder<::placeholder_13> : integral_constant {}; -} - -/// Add instances to std::placeholders for standard usage syntax. -namespace std::placeholders -{ - inline constexpr ::placeholder_11 _11{}; - inline constexpr ::placeholder_12 _12{}; - inline constexpr ::placeholder_13 _13{}; -} - -#endif // HAVE_XCODE -/// --------------------------------------------------------------------------- - #endif // define.hpp is the common include for /node. diff --git a/include/bitcoin/node/error.hpp b/include/bitcoin/node/error.hpp index 1e21cfe37..7294aed93 100644 --- a/include/bitcoin/node/error.hpp +++ b/include/bitcoin/node/error.hpp @@ -98,30 +98,7 @@ enum error_t : uint8_t confirm10, confirm11, confirm12, - confirm13, - - /// server (url parse codes) - empty_path, - invalid_number, - invalid_hash, - missing_version, - missing_target, - invalid_target, - missing_hash, - missing_height, - missing_position, - missing_id_type, - invalid_id_type, - missing_type_id, - missing_component, - invalid_component, - invalid_subcomponent, - extra_segment, - - /// server (rpc response codes) - not_found, - invalid_argument, - not_implemented + confirm13 }; // No current need for error_code equivalence mapping. diff --git a/include/bitcoin/node/full_node.hpp b/include/bitcoin/node/full_node.hpp index d985aa191..684e77733 100644 --- a/include/bitcoin/node/full_node.hpp +++ b/include/bitcoin/node/full_node.hpp @@ -130,8 +130,14 @@ class BCN_API full_node /// Thread safe synchronous archival interface. virtual query& archive() const NOEXCEPT; + /// Configuration for all libraries. + virtual const node::configuration& node_config() const NOEXCEPT; + /// Configuration settings for all libraries. - virtual const configuration& config() const NOEXCEPT; + virtual const system::settings& system_settings() const NOEXCEPT; + virtual const database::settings& database_settings() const NOEXCEPT; + ////const network::settings& network_settings() const NOEXCEPT override; + virtual const node::settings& node_settings() const NOEXCEPT; /// The candidate|confirmed chain is current. virtual bool is_current(bool confirmed) const NOEXCEPT; @@ -161,14 +167,6 @@ class BCN_API full_node network::session_inbound::ptr attach_inbound_session() NOEXCEPT override; network::session_outbound::ptr attach_outbound_session() NOEXCEPT override; - /// Attach server sessions (base net doesn't specialize or start these). - virtual session_web::ptr attach_web_session() NOEXCEPT; - virtual session_explore::ptr attach_explore_session() NOEXCEPT; - virtual session_bitcoind::ptr attach_bitcoind_session() NOEXCEPT; - virtual session_electrum::ptr attach_electrum_session() NOEXCEPT; - virtual session_stratum_v1::ptr attach_stratum_v1_session() NOEXCEPT; - virtual session_stratum_v2::ptr attach_stratum_v2_session() NOEXCEPT; - /// Virtual handlers. /// ----------------------------------------------------------------------- void do_start(const result_handler& handler) NOEXCEPT override; @@ -176,13 +174,6 @@ class BCN_API full_node void do_close() NOEXCEPT override; private: - void start_web(const code& ec, const result_handler& handler) NOEXCEPT; - void start_explore(const code& ec, const result_handler& handler) NOEXCEPT; - void start_bitcoind(const code& ec, const result_handler& handler) NOEXCEPT; - void start_electrum(const code& ec, const result_handler& handler) NOEXCEPT; - void start_stratum_v1(const code& ec, const result_handler& handler) NOEXCEPT; - void start_stratum_v2(const code& ec, const result_handler& handler) NOEXCEPT; - void do_subscribe_events(const event_notifier& handler, const event_completer& complete) NOEXCEPT; void do_notify(const code& ec, chase event_, event_value value) NOEXCEPT; diff --git a/include/bitcoin/node/impl/chasers/chaser_organize.ipp b/include/bitcoin/node/impl/chasers/chaser_organize.ipp index 8b4b96036..2d0dc8601 100644 --- a/include/bitcoin/node/impl/chasers/chaser_organize.ipp +++ b/include/bitcoin/node/impl/chasers/chaser_organize.ipp @@ -33,8 +33,8 @@ namespace node { TEMPLATE CLASS::chaser_organize(full_node& node) NOEXCEPT : chaser(node), - settings_(config().bitcoin), - checkpoints_(config().bitcoin.checkpoints) + settings_(system_settings()), + checkpoints_(system_settings().checkpoints) { } diff --git a/include/bitcoin/node/interfaces/bitcoind_rest.hpp b/include/bitcoin/node/interfaces/bitcoind_rest.hpp deleted file mode 100644 index 314ee36f2..000000000 --- a/include/bitcoin/node/interfaces/bitcoind_rest.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_INTERFACES_BITCOIND_REST_HPP -#define LIBBITCOIN_NODE_INTERFACES_BITCOIND_REST_HPP - -#include -#include - -namespace libbitcoin { -namespace node { -namespace interface { - -struct bitcoind_rest_methods -{ - // github.com/bitcoin/bitcoin/blob/master/doc/REST-interface.md - static constexpr std::tuple methods - { - // blocks (block_part is bin|hex only) - method<"block", uint8_t, system::hash_cptr>{ "media", "hash" }, - method<"block_hash", uint8_t, uint32_t>{ "media", "height" }, - method<"block_txs", uint8_t, system::hash_cptr>{ "media", "hash" }, - method<"block_headers", uint8_t, system::hash_cptr, uint32_t>{ "media", "hash", "count" }, - method<"block_part", uint8_t, system::hash_cptr, uint32_t, uint32_t>{ "media", "hash", "offset", "size" }, - method<"block_spent_tx_outputs", uint8_t, system::hash_cptr>{ "media", "hash" }, - - // client filters - method<"block_filter", uint8_t, system::hash_cptr, uint8_t>{ "media", "hash", "type" }, - method<"block_filter_headers", uint8_t, system::hash_cptr, uint8_t>{ "media", "hash", "type" }, - - // unspent outputs - method<"get_utxos", uint8_t, system::hash_cptr, uint8_t>{ "media", "hash", "type" }, - method<"get_utxos_confirmed", uint8_t, system::hash_cptr, uint8_t>{ "media", "hash", "type" }, - - // mempool (json only) - method<"mempool", optional, optional>{ "verbose", "sequence" }, - - // info (json only) - method<"chain_information">{}, - method<"mempool_information">{}, - method<"fork_information", nullable>{ "hash" } - }; - - template - using subscriber = network::unsubscriber; - - template - using at = method_at; - - // Derive this from above in c++26 using reflection. - using block = at<0>; - using block_hash = at<1>; - using block_txs = at<2>; - using block_headers = at<3>; - using block_part = at<4>; - using block_spent_tx_outputs = at<5>; - using block_filter = at<6>; - using block_filter_headers = at<7>; - using get_utxos = at<8>; - using get_utxos_confirmed = at<9>; - using mempool = at<10>; - using chain_information = at<11>; - using mempool_information = at<12>; - using fork_information = at<13>; -}; - -} // namespace interface -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/interfaces/bitcoind_rpc.hpp b/include/bitcoin/node/interfaces/bitcoind_rpc.hpp deleted file mode 100644 index 579c75512..000000000 --- a/include/bitcoin/node/interfaces/bitcoind_rpc.hpp +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_INTERFACES_BITCOIND_RPC_HPP -#define LIBBITCOIN_NODE_INTERFACES_BITCOIND_RPC_HPP - -#include -#include - -namespace libbitcoin { -namespace node { -namespace interface { - -struct bitcoind_rpc_methods -{ - static constexpr std::tuple methods - { - /// Blockchain methods. - method<"getbestblockhash">{}, - method<"getblock", string_t, optional<1.0>>{ "blockhash", "verbosity" }, - method<"getblockchaininfo">{}, - method<"getblockcount">{}, - method<"getblockfilter", string_t, optional<"basic"_t>>{ "blockhash", "filtertype" }, - method<"getblockhash", number_t>{ "height" }, - method<"getblockheader", string_t, optional>{ "blockhash", "verbose" }, - method<"getblockstats", string_t, optional>{ "hash_or_height", "stats" }, - method<"getchaintxstats", optional<-1.0>, optional<""_t>>{ "nblocks", "blockhash" }, - method<"getchainwork">{}, - method<"gettxout", string_t, number_t, optional>{ "txid", "n", "include_mempool" }, - method<"gettxoutsetinfo">{}, - method<"pruneblockchain", number_t>{ "height" }, - method<"savemempool">{}, - method<"scantxoutset", string_t, optional>{ "action", "scanobjects" }, - method<"verifychain", optional<4.0>, optional<288.0>>{ "checklevel", "nblocks" }, - method<"verifytxoutset", string_t>{ "input_verify_flag" }, - - /////// Control methods. - ////method<"getmemoryinfo", optional<"stats"_t>>{ "mode" }, - ////method<"getrpcinfo">{}, - ////method<"help", optional<""_t>>{ "command" }, - ////method<"logging", optional<"*"_t>>{ "include" }, - ////method<"stop">{}, - ////method<"uptime">{}, - - /////// Mining methods. - ////method<"getblocktemplate", optional>{ "template_request" }, - ////method<"getmininginfo">{}, - ////method<"getnetworkhashps", optional<120_u32>, optional<-1_i32>>{ "nblocks", "height" }, - ////method<"prioritisetransaction", string_t, number_t, number_t>{ "txid", "dummy", "priority_delta" }, - ////method<"submitblock", string_t, optional<""_t>>{ "block", "parameters" }, - - /////// Network methods. - ////method<"addnode", string_t, string_t>{ "node", "command" }, - ////method<"clearbanned">{}, - ////method<"disconnectnode", string_t, optional<-1_i32>>{ "address", "nodeid" }, - ////method<"getaddednodeinfo", optional, optional, optional<""_t>>{ "include_chain_info", "dns", "addnode" }, - ////method<"getconnectioncount">{}, - ////method<"getnetworkinfo">{}, - ////method<"getpeerinfo">{}, - ////method<"listbanned">{}, - ////method<"ping">{}, - ////method<"setban", string_t, string_t, optional<86400_u32>, optional, optional<""_t>>{ "addr", "command", "bantime", "absolute", "reason" }, - ////method<"setnetworkactive", boolean_t>{ "state" }, - - /////// Rawtransactions methods. - ////method<"combinerawtransaction", array_t>{ "txs" }, - ////method<"createrawtransaction", array_t, object_t, optional<0_u32>, optional>{ "inputs", "outputs", "locktime", "replaceable" }, - ////method<"decoderawtransaction", string_t>{ "hexstring" }, - ////method<"fundrawtransaction", string_t, optional>{ "rawtx", "options" }, - ////method<"getrawtransaction", string_t, optional<0_u32>, optional<""_t>>{ "txid", "verbose", "blockhash" }, - ////method<"sendrawtransaction", string_t, optional<0_u32>>{ "hexstring", "maxfeerate" }, - ////method<"signrawtransactionwithkey", string_t, optional, optional, optional<"ALL|FORKID"_t>>{ "hexstring", "privkeys", "prevtxs", "sighashtype" }, - ////method<"testmempoolaccept", array_t, optional<0_u32>>{ "rawtxs", "maxfeerate" }, - ////method<"testrawtransaction", string_t>{ "rawtx" }, - - /////// Util methods (node-related). - ////method<"createmultisig", number_t, array_t>{ "nrequired", "keys" }, - ////method<"decodepsbt", string_t>{ "psbt" }, - ////method<"decodescript", string_t>{ "hex" }, - ////method<"estimaterawfee", number_t, optional<"unset"_t>>{ "conf_target", "estimate_mode" }, - ////method<"getdescriptorinfo", string_t>{ "descriptor" }, - ////method<"validateaddress", string_t>{ "address" }, - - /////// Wallet methods (unsupported). - ////method<"abandontransaction", string_t>{ "txid" }, - ////method<"addmultisigaddress", number_t, array_t, optional<""_t>, optional<"legacy"_t>>{ "nrequired", "keys", "label", "address_type" }, - ////method<"backupwallet", string_t>{ "destination" }, - ////method<"bumpfee", string_t, optional>{ "txid", "options" }, - ////method<"createwallet", string_t, optional, optional, optional, optional<""_t>, optional, optional, optional, optional<"set"_t>>{ "wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors", "load_on_startup", "external_signer", "change_type" }, - ////method<"dumpprivkey", string_t>{ "address" }, - ////method<"dumpwallet", string_t>{ "filename" }, - ////method<"encryptwallet", string_t>{ "passphrase" }, - ////method<"getaddressinfo", string_t>{ "address" }, - ////method<"getbalances">{}, - ////method<"getnewaddress", optional<""_t>, optional<"legacy"_t>>{ "label", "address_type" }, - ////method<"getreceivedbyaddress", string_t, optional<0>>{ "address", "minconf" }, - ////method<"getreceivedbylabel", string_t, optional<0>>{ "label", "minconf" }, - ////method<"gettransaction", string_t, optional, optional>{ "txid", "include_watchonly", "verbose" }, - ////method<"getunconfirmedbalance">{}, - ////method<"getwalletinfo">{}, - ////method<"importaddress", string_t, optional<""_t>, optional, optional>{ "address", "label", "rescan", "p2sh" }, - ////method<"importmulti", array_t, optional>{ "requests", "options" }, - ////method<"importprivkey", string_t, optional<""_t>, optional>{ "privkey", "label", "rescan" }, - ////method<"importprunedfunds", string_t, string_t>{ "rawtransaction", "txoutproof" }, - ////method<"importpubkey", string_t, optional<""_t>, optional>{ "pubkey", "label", "rescan" }, - ////method<"importwallet", string_t>{ "filename" }, - ////method<"keypoolrefill", optional<100>>{ "newsize" }, - ////method<"listaddressgroupings", optional<1>, optional>{ "minconf", "include_watchonly" }, - ////method<"listlabels", optional<"receive"_t>>{ "purpose" }, - ////method<"listlockunspent">{}, - ////method<"listreceivedbyaddress", optional<1>, optional, optional, optional<""_t>>{ "minconf", "include_empty", "include_watchonly", "address_filter" }, - ////method<"listreceivedbylabel", optional<1>, optional, optional>{ "minconf", "include_empty", "include_watchonly" }, - ////method<"listtransactions", optional<""_t>, optional<10>, optional<0>, optional>{ "label", "count", "skip", "include_watchonly" }, - ////method<"listunspent", optional<1>, optional, optional, optional>{ "minconf", "addresses", "include_unsafe", "query_options" }, - ////method<"loadwallet", string_t, optional>{ "filename", "load_on_startup" }, - ////method<"lockunspent", boolean_t, optional>{ "unlock", "transactions" }, - ////method<"removeprunedfunds", string_t>{ "txid" }, - ////method<"rescanblockchain", optional<0>>{ "start_height" }, - ////method<"send", object_t, optional>{ "outputs", "options" }, - ////method<"sendmany", string_t, object_t, optional<1>, optional<""_t>, optional<""_t>, optional, optional, optional<25>, optional<"unset"_t>, optional, optional<0>>{ "dummy", "outputs", "minconf", "comment", "comment_to", "subtractfeefrom", "replaceable", "conf_target", "estimate_mode", "avoid_reuse", "fee_rate" }, - ////method<"sendtoaddress", string_t, number_t, optional<""_t>, optional<""_t>, optional, optional<25>, optional<"unset"_t>, optional, optional<0>, optional>{ "address", "amount", "comment", "comment_to", "subtractfeefromamount", "conf_target", "estimate_mode", "avoid_reuse", "fee_rate", "verbose" }, - ////method<"setlabel", string_t, string_t>{ "address", "label" }, - ////method<"settxfee", number_t>{ "amount" }, - ////method<"signmessage", string_t, string_t>{ "address", "message" }, - ////method<"signmessagewithprivkey", string_t, string_t>{ "privkey", "message" }, - ////method<"syncwithvalidationinterfacequeue">{}, - ////method<"unloadwallet", optional<""_t>, optional>{ "wallet_name", "load_on_startup" }, - ////method<"walletcreatefundedpsbt", optional, optional, optional<0>, optional>{ "inputs", "outputs", "locktime", "options" }, - ////method<"walletlock">{}, - ////method<"walletpassphrase", string_t, number_t>{ "passphrase", "timeout" }, - ////method<"walletprocesspsbt", string_t, optional, optional, optional>{ "psbt", "sign", "bip32derivs", "complete" } - }; - - template - using subscriber = network::unsubscriber; - - template - using at = method_at; - - // Derive this from above in c++26 using reflection. - using get_best_block_hash = at<0>; - using get_block = at<1>; - using get_block_chain_info = at<2>; - using get_block_count = at<3>; - using get_block_filter = at<4>; - using get_block_hash = at<5>; - using get_block_header = at<6>; - using get_block_stats = at<7>; - using get_chain_tx_stats = at<8>; - using get_chain_work = at<9>; - using get_tx_out = at<10>; - using get_tx_out_set_info = at<11>; - using prune_block_chain = at<12>; - using save_mem_pool = at<13>; - using scan_tx_out_set = at<14>; - using verify_chain = at<15>; - using verify_tx_out_set = at<16>; - - ////using get_memory_info = at<17>; - ////using get_rpc_info = at<18>; - ////using help = at<19>; - ////using logging = at<20>; - ////using stop = at<21>; - ////using uptime = at<22>; - ////using get_block_template = at<23>; - ////using get_mining_info = at<24>; - ////using get_network_hash_ps = at<25>; - ////using prioritise_transaction = at<26>; - ////using submit_block = at<27>; - ////using add_node = at<28>; - ////using clear_banned = at<29>; - ////using disconnect_node = at<30>; - ////using get_added_node_info = at<31>; - ////using get_connection_count = at<32>; - ////using get_network_info = at<33>; - ////using get_peer_info = at<34>; - ////using list_banned = at<35>; - ////using ping = at<36>; - ////using set_ban = at<37>; - ////using set_network_active = at<38>; - ////using combine_raw_transaction = at<39>; - ////using create_raw_transaction = at<40>; - ////using decode_raw_transaction = at<41>; - ////using fund_raw_transaction = at<42>; - ////using get_raw_transaction = at<43>; - ////using send_raw_transaction = at<44>; - ////using sign_raw_transaction_with_key = at<45>; - ////using test_mem_pool_accept = at<46>; - ////using test_raw_transaction = at<47>; - ////using create_multisig = at<48>; - ////using decode_psbt = at<49>; - ////using decode_script = at<50>; - ////using estimate_raw_fee = at<51>; - ////using get_descriptor_info = at<52>; - ////using validate_address = at<53>; - ////using abandon_transaction = at<54>; - ////using add_multisig_address = at<55>; - ////using backup_wallet = at<56>; - ////using bump_fee = at<57>; - ////using create_wallet = at<58>; - ////using dump_priv_key = at<59>; - ////using dump_wallet = at<60>; - ////using encrypt_wallet = at<61>; - ////using get_address_info = at<62>; - ////using get_balances = at<63>; - ////using get_new_address = at<64>; - ////using get_received_by_address = at<65>; - ////using get_received_by_label = at<66>; - ////using get_transaction = at<67>; - ////using get_unconfirmed_balance = at<68>; - ////using get_wallet_info = at<69>; - ////using import_address = at<70>; - ////using import_multi = at<71>; - ////using import_priv_key = at<72>; - ////using import_pruned_funds = at<73>; - ////using import_pub_key = at<74>; - ////using import_wallet = at<75>; - ////using key_pool_refill = at<76>; - ////using list_address_groupings = at<77>; - ////using list_labels = at<78>; - ////using list_lock_unspent = at<79>; - ////using list_received_by_address = at<80>; - ////using list_received_by_label = at<81>; - ////using list_transactions = at<82>; - ////using list_unspent = at<83>; - ////using load_wallet = at<84>; - ////using lock_unspent = at<85>; - ////using remove_pruned_funds = at<86>; - ////using rescan_block_chain = at<87>; - ////using send = at<88>; - ////using send_many = at<89>; - ////using send_to_address = at<90>; - ////using set_label = at<91>; - ////using set_tx_fee = at<92>; - ////using sign_message = at<93>; - ////using sign_message_with_priv_key = at<94>; - ////using sync_with_validation_interface_queue = at<95>; - ////using unload_wallet = at<96>; - ////using wallet_create_funded_psbt = at<97>; - ////using wallet_lock = at<98>; - ////using wallet_passphrase = at<99>; - ////using wallet_process_psbt = at<100>; -}; - -} // namespace interface -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/interfaces/electrum.hpp b/include/bitcoin/node/interfaces/electrum.hpp deleted file mode 100644 index 2e2a32fd8..000000000 --- a/include/bitcoin/node/interfaces/electrum.hpp +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_INTERFACES_ELECTRUM_HPP -#define LIBBITCOIN_NODE_INTERFACES_ELECTRUM_HPP - -#include -#include - -namespace libbitcoin { -namespace node { -namespace interface { - -struct electrum_methods -{ - /// Electrum protocol version 1.4.2 - static constexpr std::tuple methods - { - /// Blockchain methods. - method<"blockchain.block.header", number_t, number_t>{ "height", "cp_height" }, - method<"blockchain.block.headers", number_t, number_t, number_t>{ "start_height", "count", "cp_height" }, - method<"blockchain.headers.subscribe">{}, - method<"blockchain.estimatefee", number_t>{ "number" }, - method<"blockchain.relayfee">{}, - method<"blockchain.scripthash.get_balance", string_t>{ "scripthash" }, - method<"blockchain.scripthash.get_history", string_t>{ "scripthash" }, - method<"blockchain.scripthash.get_mempool", string_t>{ "scripthash" }, - method<"blockchain.scripthash.listunspent", string_t>{ "scripthash" }, - method<"blockchain.scripthash.subscribe", string_t>{ "scripthash" }, - method<"blockchain.scripthash.unsubscribe", string_t>{ "scripthash" }, - method<"blockchain.transaction.broadcast", string_t>{ "raw_tx" }, - method<"blockchain.transaction.get", string_t, boolean_t>{ "tx_hash", "verbose" }, - method<"blockchain.transaction.get_merkle", string_t, number_t>{ "tx_hash", "height" }, - method<"blockchain.transaction.id_from_pos", number_t, number_t, boolean_t>{ "height", "tx_pos", "merkle" }, - - /// Server methods. - method<"server.add_peer", object_t>{ "features" }, - method<"server.banner">{}, - method<"server.donation_address">{}, - method<"server.features">{}, - method<"server.peers.subscribe">{}, - method<"server.ping">{}, - method<"server.version", string_t, optional>{ "client_name", "protocol_version" }, - - /// Mempool methods. - method<"mempool.get_fee_histogram">{} - }; - - template - using subscriber = network::subscriber; - - template - using at = method_at; - - // Derive this from above in c++26 using reflection. - using blockchain_block_header = at<0>; - using blockchain_block_headers = at<1>; - using blockchain_headers_subscribe = at<2>; - using blockchain_estimatefee = at<3>; - using blockchain_relayfee = at<4>; - using blockchain_scripthash_get_balance = at<5>; - using blockchain_scripthash_get_history = at<6>; - using blockchain_scripthash_get_mempool = at<7>; - using blockchain_scripthash_listunspent = at<8>; - using blockchain_scripthash_subscribe = at<9>; - using blockchain_scripthash_unsubscribe = at<10>; - using blockchain_transaction_broadcast = at<11>; - using blockchain_transaction_get = at<12>; - using blockchain_transaction_get_merkle = at<13>; - using blockchain_transaction_id_from_pos = at<14>; - using server_add_peer = at<15>; - using server_banner = at<16>; - using server_donation_address = at<17>; - using server_features = at<18>; - using server_peers_subscribe = at<19>; - using server_ping = at<20>; - using server_version = at<21>; - using mempool_get_fee_histogram = at<22>; -}; - -} // namespace interface -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/interfaces/explore.hpp b/include/bitcoin/node/interfaces/explore.hpp deleted file mode 100644 index b487924c8..000000000 --- a/include/bitcoin/node/interfaces/explore.hpp +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_INTERFACES_EXPLORE_HPP -#define LIBBITCOIN_NODE_INTERFACES_EXPLORE_HPP - -#include -#include - -namespace libbitcoin { -namespace node { -namespace interface { - -struct explore_methods -{ - static constexpr std::tuple methods - { - method<"top", uint8_t, uint8_t>{ "version", "media" }, - method<"block", uint8_t, uint8_t, nullable, nullable, optional>{ "version", "media", "hash", "height", "witness" }, - method<"block_header", uint8_t, uint8_t, nullable, nullable>{ "version", "media", "hash", "height" }, - method<"block_header_context", uint8_t, uint8_t, nullable, nullable>{ "version", "media", "hash", "height" }, - method<"block_details", uint8_t, uint8_t, nullable, nullable>{ "version", "media", "hash", "height" }, - method<"block_txs", uint8_t, uint8_t, nullable, nullable>{ "version", "media", "hash", "height" }, - method<"block_filter", uint8_t, uint8_t, uint8_t, nullable, nullable>{ "version", "media", "type", "hash", "height" }, - method<"block_filter_hash", uint8_t, uint8_t, uint8_t, nullable, nullable>{ "version", "media", "type", "hash", "height" }, - method<"block_filter_header", uint8_t, uint8_t, uint8_t, nullable, nullable>{ "version", "media", "type", "hash", "height" }, - method<"block_tx", uint8_t, uint8_t, uint32_t, nullable, nullable, optional>{ "version", "media", "position", "hash", "height", "witness" }, - - method<"tx", uint8_t, uint8_t, system::hash_cptr, optional>{ "version", "media", "hash", "witness" }, - method<"tx_header", uint8_t, uint8_t, system::hash_cptr>{ "version", "media", "hash" }, - method<"tx_details", uint8_t, uint8_t, system::hash_cptr>{ "version", "media", "hash" }, - - method<"inputs", uint8_t, uint8_t, system::hash_cptr, optional>{ "version", "media", "hash", "witness" }, - method<"input", uint8_t, uint8_t, system::hash_cptr, uint32_t, optional>{ "version", "media", "hash", "index", "witness" }, - method<"input_script", uint8_t, uint8_t, system::hash_cptr, uint32_t>{ "version", "media", "hash", "index" }, - method<"input_witness", uint8_t, uint8_t, system::hash_cptr, uint32_t>{ "version", "media", "hash", "index" }, - - method<"outputs", uint8_t, uint8_t, system::hash_cptr>{ "version", "media", "hash" }, - method<"output", uint8_t, uint8_t, system::hash_cptr, uint32_t>{ "version", "media", "hash", "index" }, - method<"output_script", uint8_t, uint8_t, system::hash_cptr, uint32_t>{ "version", "media", "hash", "index" }, - method<"output_spender", uint8_t, uint8_t, system::hash_cptr, uint32_t>{ "version", "media", "hash", "index" }, - method<"output_spenders", uint8_t, uint8_t, system::hash_cptr, uint32_t>{ "version", "media", "hash", "index" }, - - method<"address", uint8_t, uint8_t, system::hash_cptr, optional>{ "version", "media", "hash", "turbo" }, - method<"address_confirmed", uint8_t, uint8_t, system::hash_cptr, optional>{ "version", "media", "hash", "turbo" }, - method<"address_unconfirmed", uint8_t, uint8_t, system::hash_cptr, optional>{ "version", "media", "hash", "turbo" }, - method<"address_balance", uint8_t, uint8_t, system::hash_cptr, optional>{ "version", "media", "hash", "turbo" } - }; - - template - using subscriber = network::unsubscriber; - - template - using at = method_at; - - // Derive this from above in c++26 using reflection. - - using top = at<0>; - - using block = at<1>; - using block_header = at<2>; - using block_header_context = at<3>; - using block_details = at<4>; - using block_txs = at<5>; - using block_filter = at<6>; - using block_filter_hash = at<7>; - using block_filter_header = at<8>; - using block_tx = at<9>; - - using tx = at<10>; - using tx_header = at<11>; - using tx_details = at<12>; - - using inputs = at<13>; - using input = at<14>; - using input_script = at<15>; - using input_witness = at<16>; - - using outputs = at<17>; - using output = at<18>; - using output_script = at<19>; - using output_spender = at<20>; - using output_spenders = at<21>; - - using address = at<22>; - using address_confirmed = at<23>; - using address_unconfirmed = at<24>; - using address_balance = at<25>; -}; - -/// ?format=data|text|json (via query string). -/// ----------------------------------------------------------------------- - -/// /v1/top {1} - -/// /v1/block/hash/[bkhash] {1} -/// /v1/block/height/[height] {1} - -/// /v1/block/hash/[bkhash]/header {1} -/// /v1/block/height/[height]/header {1} - -/// /v1/block/hash/[bkhash]/fees {1} -/// /v1/block/height/[height]/fees {1} - -/// /v1/block/hash/[bkhash]/filter/[type] {1} -/// /v1/block/height/[height]/filter/[type] {1} - -/// /v1/block/hash/[bkhash]/filter/[type]/hash {1} -/// /v1/block/height/[height]/filter/[type]/hash {1} - -/// /v1/block/hash/[bkhash]/filter/[type]/header {1} -/// /v1/block/height/[height]/filter/[type]/header {1} - -/// /v1/block/hash/[bkhash]/txs {all txs in the block} -/// /v1/block/height/[height]/txs {all txs in the block} - -/// /v1/block/hash/[bkhash]/tx/[position] {1} -/// /v1/block/height/[height]/tx/[position] {1} - -/// ----------------------------------------------------------------------- - -/// /v1/tx/[txhash] {1} -/// /v1/tx/[txhash]/header {1 - if confirmed} -/// /v1/tx/[txhash]/fee {1} - -/// ----------------------------------------------------------------------- - -/// /v1/input/[txhash] {all inputs in the tx} -/// /v1/input/[txhash]/[index] {1} -/// /v1/input/[txhash]/[index]/script {1} -/// /v1/input/[txhash]/[index]/witness {1} - -/// ----------------------------------------------------------------------- - -/// /v1/output/[txhash] {all outputs in the tx} -/// /v1/output/[txhash]/[index] {1} -/// /v1/output/[txhash]/[index]/script {1} -/// /v1/output/[txhash]/[index]/spender {1 - if confirmed} -/// /v1/output/[txhash]/[index]/spenders {all} - -/// ----------------------------------------------------------------------- - -/// /v1/address/[output-script-hash] {all} -/// /v1/address/[output-script-hash]/unconfirmed {all unconfirmed} -/// /v1/address/[output-script-hash]/confirmed {all unconfirmed} -/// /v1/address/[output-script-hash]/balance {all confirmed} - -} // namespace interface -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/interfaces/interfaces.hpp b/include/bitcoin/node/interfaces/interfaces.hpp deleted file mode 100644 index eda164813..000000000 --- a/include/bitcoin/node/interfaces/interfaces.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_INTERFACES_HPP -#define LIBBITCOIN_NODE_INTERFACES_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { -namespace interface { - -using bitcoind_rest = publish; -using bitcoind_rpc = publish; -using electrum = publish; -using explore = publish; -using stratum_v1 = publish; -using stratum_v2 = publish; - -} // namespace interface -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/interfaces/stratum_v1.hpp b/include/bitcoin/node/interfaces/stratum_v1.hpp deleted file mode 100644 index d76e405ee..000000000 --- a/include/bitcoin/node/interfaces/stratum_v1.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_INTERFACES_STRATUM_V1_HPP -#define LIBBITCOIN_NODE_INTERFACES_STRATUM_V1_HPP - -#include -#include - -namespace libbitcoin { -namespace node { -namespace interface { - -struct stratum_v1_methods -{ - static constexpr std::tuple methods - { - /// Client requests. - method<"mining.subscribe", optional<""_t>, optional<0.0>>{ "user_agent", "extranonce1_size" }, - method<"mining.authorize", string_t, string_t>{ "username", "password" }, - method<"mining.submit", string_t, string_t, string_t, number_t, string_t>{ "worker_name", "job_id", "extranonce2", "ntime", "nonce" }, - method<"mining.extranonce.subscribe">{}, - method<"mining.extranonce.unsubscribe", number_t>{ "id" }, - - /// Server notifications. - method<"mining.configure", object_t>{ "extensions" }, - method<"mining.set_difficulty", optional<1.0>>{ "difficulty" }, - method<"mining.notify", string_t, string_t, string_t, string_t, array_t, number_t, number_t, number_t, boolean_t, boolean_t, boolean_t>{ "job_id", "prevhash", "coinb1", "coinb2", "merkle_branch", "version", "nbits", "ntime", "clean_jobs", "hash1", "hash2" }, - method<"client.reconnect", string_t, number_t, number_t>{ "url", "port", "id" }, - method<"client.hello", object_t>{ "protocol" }, - method<"client.rejected", string_t, string_t>{ "job_id", "reject_reason" } - }; - - template - using subscriber = network::unsubscriber; - - template - using at = method_at; - - // Derive this from above in c++26 using reflection. - using mining_subscribe = at<0>; - using mining_authorize = at<1>; - using mining_submit = at<2>; - using mining_extranonce_subscribe = at<3>; - using mining_extranonce_unsubscribe = at<4>; - using mining_configure = at<5>; - using mining_set_difficulty = at<6>; - using mining_notify = at<7>; - using client_reconnect = at<8>; - using client_hello = at<9>; - using client_rejected = at<10>; -}; - -} // namespace interface -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/interfaces/stratum_v2.hpp b/include/bitcoin/node/interfaces/stratum_v2.hpp deleted file mode 100644 index a266d0e5e..000000000 --- a/include/bitcoin/node/interfaces/stratum_v2.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_INTERFACES_STRATUM_V2_HPP -#define LIBBITCOIN_NODE_INTERFACES_STRATUM_V2_HPP - -#include -#include - -namespace libbitcoin { -namespace node { -namespace interface { - -struct stratum_v2_methods -{ - static constexpr std::tuple methods - { - /// Common setup messages. - method<"setup_connection", number_t, number_t, number_t, number_t, string_t, string_t, string_t>{ "min_version", "max_version", "flags", "endpoint_port", "endpoint_host", "vendor", "user_agent" }, - method<"setup_connection_success", number_t, number_t>{ "used_version", "flags" }, - method<"setup_connection_error", string_t>{ "code" }, - - /// Standard mining channel (header-only, primary for devices/firmware). - method<"open_standard_mining_channel", string_t, number_t, string_t, number_t>{ "user_identity", "nominal_hash_rate", "max_target", "min_extranonce_size" }, - method<"open_standard_mining_channel_success", number_t, string_t, string_t, number_t>{ "channel_id", "target", "extranonce_prefix", "extranonce_size" }, - method<"open_standard_mining_channel_error", string_t>{ "code" }, - - /// Extended mining channel. - method<"open_extended_mining_channel", string_t, number_t, string_t, number_t>{ "user_identity", "nominal_hash_rate", "max_target", "min_extranonce_size" }, - method<"open_extended_mining_channel_success", number_t, string_t, string_t, number_t, number_t, number_t>{ "channel_id", "target", "extranonce_prefix", "extranonce_size", "max_coinbase_size", "coinbase_output_max_additional_size" }, - method<"open_extended_mining_channel_error", string_t>{ "code" }, - - /// Channel management. - method<"update_channel", number_t, number_t, optional<""_t>>{ "channel_id", "nominal_hash_rate", "max_target" }, - method<"update_channel_error", number_t, string_t>{ "channel_id", "code" }, - method<"close_channel", number_t, optional<""_t>>{ "channel_id", "reason" }, - method<"set_target", number_t, string_t>{ "channel_id", "maximum_target" }, - - /// Header-only job declaration. - method<"new_mining_job", number_t, boolean_t, number_t, string_t, string_t, number_t>{ "job_id", "future_job", "version", "prev_hash", "merkle_root", "ntime_offset" }, - method<"set_new_prev_hash", number_t, number_t, string_t, number_t, number_t>{ "channel_id", "job_id", "prev_hash", "min_ntime", "nbits" }, - - /// Extended / custom jobs. - method<"new_extended_mining_job", number_t, boolean_t, number_t, string_t, string_t, array_t>{ "job_id", "future_job", "version", "coinbase_prefix", "coinbase_suffix", "merkle_path" }, - method<"set_custom_mining_job", number_t, boolean_t, number_t, string_t, number_t, number_t, array_t, array_t>{ "channel_id", "future_job", "job_id", "prev_hash", "version", "ntime", "merkle_branch", "transactions" }, - method<"set_custom_mining_job_success", number_t>{ "job_id" }, - method<"set_custom_mining_job_error", number_t, string_t>{ "job_id", "code" }, - - /// Share submission. - method<"submit_shares", number_t, number_t, number_t, number_t, number_t, number_t>{ "channel_id", "sequence_number", "job_id", "nonce", "ntime", "version" }, - method<"submit_shares_success", number_t, optional<""_t>>{ "sequence_number", "new_target" }, - method<"submit_shares_error", number_t, string_t>{ "sequence_number", "code" }, - - /// Miscellaneous. - method<"reconnect", string_t, number_t>{ "new_host", "new_port" } - }; - - template - using subscriber = network::unsubscriber; - - template - using at = method_at; - - // Derive this from above in c++26 using reflection. - using setup_connection = at<0>; - using setup_connection_success = at<1>; - using setup_connection_error = at<2>; - using open_standard_mining_channel = at<3>; - using open_standard_mining_channel_success = at<4>; - using open_standard_mining_channel_error = at<5>; - using open_extended_mining_channel = at<6>; - using open_extended_mining_channel_success = at<7>; - using open_extended_mining_channel_error = at<8>; - using update_channel = at<9>; - using update_channel_error = at<10>; - using close_channel = at<11>; - using set_target = at<12>; - using new_mining_job = at<13>; - using set_new_prev_hash = at<14>; - using new_extended_mining_job = at<15>; - using set_custom_mining_job = at<16>; - using set_custom_mining_job_success = at<17>; - using set_custom_mining_job_error = at<18>; - using submit_shares = at<19>; - using submit_shares_success = at<20>; - using submit_shares_error = at<21>; - using reconnect = at<22>; -}; - -} // namespace interface -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/interfaces/types.hpp b/include/bitcoin/node/interfaces/types.hpp deleted file mode 100644 index 6510acb87..000000000 --- a/include/bitcoin/node/interfaces/types.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_INTERFACES_TYPES_HPP -#define LIBBITCOIN_NODE_INTERFACES_TYPES_HPP - -#include - -namespace libbitcoin { -namespace node { -namespace interface { - -/// Alias network::rpc names within interface::. - -template -using method = network::rpc::method; -template -using method_at = network::rpc::method_at; -template -using publish = network::rpc::publish; - -template -using optional = network::rpc::optional; -template -using nullable = network::rpc::nullable; -using boolean_t = network::rpc::boolean_t; -using string_t = network::rpc::string_t; -using number_t = network::rpc::number_t; -using object_t = network::rpc::object_t; -using array_t = network::rpc::array_t; -using value_t = network::rpc::value_t; -using null_t = network::rpc::null_t; - -namespace empty { constexpr auto array = network::rpc::empty::array; }; -namespace empty { constexpr auto object = network::rpc::empty::object; }; -namespace empty { constexpr auto value = network::rpc::empty::value; }; - -} // namespace interface -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/parser.hpp b/include/bitcoin/node/parser.hpp deleted file mode 100644 index 2d253e82f..000000000 --- a/include/bitcoin/node/parser.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_PARSER_HPP -#define LIBBITCOIN_NODE_PARSER_HPP - -#include -#include -#include - -// Not localizable. -#define BN_HELP_VARIABLE "help" -#define BN_HARDWARE_VARIABLE "hardware" -#define BN_SETTINGS_VARIABLE "settings" -#define BN_VERSION_VARIABLE "version" -#define BN_NEWSTORE_VARIABLE "newstore" -#define BN_BACKUP_VARIABLE "backup" -#define BN_RESTORE_VARIABLE "restore" - -#define BN_FLAGS_VARIABLE "flags" -#define BN_SLABS_VARIABLE "slabs" -#define BN_BUCKETS_VARIABLE "buckets" -#define BN_COLLISIONS_VARIABLE "collisions" -#define BN_INFORMATION_VARIABLE "information" - -#define BN_READ_VARIABLE "test" -#define BN_WRITE_VARIABLE "write" - -// This must be lower case but the env var part can be any case. -#define BN_CONFIG_VARIABLE "config" - -// This must match the case of the env var. -#define BN_ENVIRONMENT_VARIABLE_PREFIX "BN_" - -namespace libbitcoin { -namespace node { - -/// Parse configurable values from environment variables, settings file, and -/// command line positional and non-positional options. -class BCN_API parser - : public system::config::parser -{ -public: - parser(system::chain::selection context, - const server::settings::embedded_pages& explore, - const server::settings::embedded_pages& web) NOEXCEPT; - - /// Load command line options (named). - virtual options_metadata load_options() THROWS; - - /// Load command line arguments (positional). - virtual arguments_metadata load_arguments() THROWS; - - /// Load environment variable settings. - virtual options_metadata load_environment() THROWS; - - /// Load configuration file settings. - virtual options_metadata load_settings() THROWS; - - /// Parse all configuration into member settings. - virtual bool parse(int argc, const char* argv[], - std::ostream& error) THROWS; - - /// The populated configuration settings values. - configuration configured; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/parsers/bitcoind_query.hpp b/include/bitcoin/node/parsers/bitcoind_query.hpp deleted file mode 100644 index 47bd9f3a8..000000000 --- a/include/bitcoin/node/parsers/bitcoind_query.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_PARSERS_BITCOIND_QUERY_HPP -#define LIBBITCOIN_NODE_PARSERS_BITCOIND_QUERY_HPP - -#include - -namespace libbitcoin { -namespace node { - -BCN_API bool bitcoind_query(network::rpc::request_t& out, - const network::http::request& request) NOEXCEPT; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/parsers/bitcoind_target.hpp b/include/bitcoin/node/parsers/bitcoind_target.hpp deleted file mode 100644 index faa34a3b3..000000000 --- a/include/bitcoin/node/parsers/bitcoind_target.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_PARSERS_BITCOIND_TARGET_HPP -#define LIBBITCOIN_NODE_PARSERS_BITCOIND_TARGET_HPP - -#include - -namespace libbitcoin { -namespace node { - -BCN_API code bitcoind_target(network::rpc::request_t& out, - const std::string_view& path) NOEXCEPT; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/parsers/electrum_version.hpp b/include/bitcoin/node/parsers/electrum_version.hpp deleted file mode 100644 index 71e30a9c3..000000000 --- a/include/bitcoin/node/parsers/electrum_version.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_PARSERS_ELECTRUM_VERSION_HPP -#define LIBBITCOIN_NODE_PARSERS_ELECTRUM_VERSION_HPP - -#include - -namespace libbitcoin { -namespace node { - -enum class electrum_version -{ - /// Invalid version. - v0_0, - - /// 2011, initial protocol negotiation. - v0_6, - - /// 2012, enhanced protocol negotiation. - v0_8, - - /// 2012, added pruning limits and transport indicators. - v0_9, - - /// 2013, baseline for core methods in the official specification. - v0_10, - - /// 2014, 1.x series, deprecations of utxo and block number methods. - v1_0, - - /// 2015, updated version response and introduced scripthash methods. - v1_1, - - /// 2017, added optional parameters for transactions and headers. - v1_2, - - /// 2018, defaulted raw headers and introduced new block methods. - v1_3, - - /// 2019, removed deserialized headers and added merkle proof features. - v1_4, - - /// 2019, modifications for auxiliary proof-of-work handling. - v1_4_1, - - /// 2020, added scripthash unsubscribe functionality. - v1_4_2, - - /// 2022, updated response formats and added fee estimation modes. - v1_6 -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/parsers/explore_query.hpp b/include/bitcoin/node/parsers/explore_query.hpp deleted file mode 100644 index 659f70670..000000000 --- a/include/bitcoin/node/parsers/explore_query.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_PARSERS_EXPLORE_QUERY_HPP -#define LIBBITCOIN_NODE_PARSERS_EXPLORE_QUERY_HPP - -#include - -namespace libbitcoin { -namespace node { - -BCN_API bool explore_query(network::rpc::request_t& out, - const network::http::request& request) NOEXCEPT; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/parsers/explore_target.hpp b/include/bitcoin/node/parsers/explore_target.hpp deleted file mode 100644 index 5d78d6dde..000000000 --- a/include/bitcoin/node/parsers/explore_target.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_PARSERS_EXPLORE_TARGET_HPP -#define LIBBITCOIN_NODE_PARSERS_EXPLORE_TARGET_HPP - -#include - -namespace libbitcoin { -namespace node { - -BCN_API code explore_target(network::rpc::request_t& out, - const std::string_view& path) NOEXCEPT; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/parsers/parsers.hpp b/include/bitcoin/node/parsers/parsers.hpp deleted file mode 100644 index 5f4ba2055..000000000 --- a/include/bitcoin/node/parsers/parsers.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_PARSERS_PARSERS_HPP -#define LIBBITCOIN_NODE_PARSERS_PARSERS_HPP - -#include -#include -#include -#include -#include - -#endif diff --git a/include/bitcoin/node/protocols/protocol.hpp b/include/bitcoin/node/protocols/protocol.hpp index 9ad86a2fd..77d76f67f 100644 --- a/include/bitcoin/node/protocols/protocol.hpp +++ b/include/bitcoin/node/protocols/protocol.hpp @@ -54,7 +54,11 @@ class BCN_API protocol query& archive() const NOEXCEPT; /// Configuration settings for all libraries. - const configuration& config() const NOEXCEPT; + virtual const node::configuration& node_config() const NOEXCEPT; + virtual const system::settings& system_settings() const NOEXCEPT; + virtual const database::settings& database_settings() const NOEXCEPT; + ////const network::settings& network_settings() const NOEXCEPT override; + virtual const node::settings& node_settings() const NOEXCEPT; /// The candidate|confirmed chain is current. virtual bool is_current(bool confirmed) const NOEXCEPT; diff --git a/include/bitcoin/node/protocols/protocol_bitcoind_rest.hpp b/include/bitcoin/node/protocols/protocol_bitcoind_rest.hpp deleted file mode 100644 index 6825ee3cf..000000000 --- a/include/bitcoin/node/protocols/protocol_bitcoind_rest.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_BITCOIND_REST_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BITCOIND_REST_HPP - -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API protocol_bitcoind_rest - : public node::protocol_bitcoind_rpc, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - using rest_interface = interface::bitcoind_rest; - using rest_dispatcher = network::rpc::dispatcher; - - inline protocol_bitcoind_rest(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol_bitcoind_rpc(session, channel, options), - network::tracker(session->log) - { - } - - void start() NOEXCEPT override; - void stopping(const code& ec) NOEXCEPT override; - -protected: - using get = network::http::method::get; - - /// Dispatch. - void handle_receive_get(const code& ec, - const get::cptr& get) NOEXCEPT override; - - /// REST interface handlers. - bool handle_get_block(const code& ec, rest_interface::block, - uint8_t media, system::hash_cptr hash) NOEXCEPT; - -private: - template - inline void subscribe(Method&& method, Args&&... args) NOEXCEPT - { - rest_dispatcher_.subscribe(BIND_SHARED(method, args)); - } - - // This is protected by strand. - rest_dispatcher rest_dispatcher_{}; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_bitcoind_rpc.hpp b/include/bitcoin/node/protocols/protocol_bitcoind_rpc.hpp deleted file mode 100644 index ce60a0463..000000000 --- a/include/bitcoin/node/protocols/protocol_bitcoind_rpc.hpp +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_BITCOIND_RPC_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BITCOIND_RPC_HPP - -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API protocol_bitcoind_rpc - : public node::protocol_http, - public network::protocol_http, - protected network::tracker -{ -public: - // Replace base class channel_t (network::channel_http). - using channel_t = node::channel_http; - - typedef std::shared_ptr ptr; - using rpc_interface = interface::bitcoind_rpc; - using rpc_dispatcher = network::rpc::dispatcher; - - inline protocol_bitcoind_rpc(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol_http(session, channel), - network::protocol_http(session, channel, options), - network::tracker(session->log) - { - } - - void start() NOEXCEPT override; - void stopping(const code& ec) NOEXCEPT override; - -protected: - using post = network::http::method::post; - using options = network::http::method::options; - - /// Dispatch. - void handle_receive_options(const code& ec, - const network::http::method::options::cptr& options) NOEXCEPT override; - void handle_receive_post(const code& ec, - const post::cptr& post) NOEXCEPT override; - - /// Handlers. - bool handle_get_best_block_hash(const code& ec, - rpc_interface::get_best_block_hash) NOEXCEPT; - bool handle_get_block(const code& ec, - rpc_interface::get_block, const std::string&, - double verbosity) NOEXCEPT; - bool handle_get_block_chain_info(const code& ec, - rpc_interface::get_block_chain_info) NOEXCEPT; - bool handle_get_block_count(const code& ec, - rpc_interface::get_block_count) NOEXCEPT; - bool handle_get_block_filter(const code& ec, - rpc_interface::get_block_filter, const std::string&, - const std::string&) NOEXCEPT; - bool handle_get_block_hash(const code& ec, - rpc_interface::get_block_hash, double) NOEXCEPT; - bool handle_get_block_header(const code& ec, - rpc_interface::get_block_header, const std::string&, bool) NOEXCEPT; - bool handle_get_block_stats(const code& ec, - rpc_interface::get_block_stats, const std::string&, - const network::rpc::array_t&) NOEXCEPT; - bool handle_get_chain_tx_stats(const code& ec, - rpc_interface::get_chain_tx_stats, double, - const std::string&) NOEXCEPT; - bool handle_get_chain_work(const code& ec, - rpc_interface::get_chain_work) NOEXCEPT; - bool handle_get_tx_out(const code& ec, - rpc_interface::get_tx_out, const std::string&, double, bool) NOEXCEPT; - bool handle_get_tx_out_set_info(const code& ec, - rpc_interface::get_tx_out_set_info) NOEXCEPT; - bool handle_prune_block_chain(const code& ec, - rpc_interface::prune_block_chain, double) NOEXCEPT; - bool handle_save_mem_pool(const code& ec, - rpc_interface::save_mem_pool) NOEXCEPT; - bool handle_scan_tx_out_set(const code& ec, - rpc_interface::scan_tx_out_set, const std::string&, - const network::rpc::array_t&) NOEXCEPT; - bool handle_verify_chain(const code& ec, - rpc_interface::verify_chain, double, double) NOEXCEPT; - bool handle_verify_tx_out_set(const code& ec, - rpc_interface::verify_tx_out_set, const std::string&) NOEXCEPT; - - /// Senders. - void send_error(const code& ec) NOEXCEPT; - void send_error(const code& ec, size_t size_hint) NOEXCEPT; - void send_error(const code& ec, network::rpc::value_option&& error, - size_t size_hint) NOEXCEPT; - void send_text(std::string&& hexidecimal) NOEXCEPT; - void send_result(network::rpc::value_option&& result, - size_t size_hint) NOEXCEPT; - -private: - template - inline void subscribe(Method&& method, Args&&... args) NOEXCEPT - { - rpc_dispatcher_.subscribe(BIND_SHARED(method, args)); - } - - // Senders. - void send_rpc(network::rpc::response_t&& model, - size_t size_hint) NOEXCEPT; - - // Cache request for serialization (requires strand). - void set_rpc_request(network::rpc::version version, - const network::rpc::id_option& id, - const network::http::request_cptr& request) NOEXCEPT; - - // Obtain cached request and clear cache (requires strand). - network::http::request_cptr reset_rpc_request() NOEXCEPT; - - // These are protected by strand. - rpc_dispatcher rpc_dispatcher_{}; - network::rpc::version version_{}; - network::rpc::id_option id_{}; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_block_in_106.hpp b/include/bitcoin/node/protocols/protocol_block_in_106.hpp index 1f5af47dd..4ef9ad882 100644 --- a/include/bitcoin/node/protocols/protocol_block_in_106.hpp +++ b/include/bitcoin/node/protocols/protocol_block_in_106.hpp @@ -36,7 +36,7 @@ class BCN_API protocol_block_in_106 protocol_block_in_106(const auto& session, const network::channel::ptr& channel) NOEXCEPT : node::protocol_peer(session, channel), - block_type_(session->config().network.witness_node() ? + block_type_(session->network_settings().witness_node() ? type_id::witness_block : type_id::block), network::tracker(session->log) { diff --git a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp index f79abd156..bd70527db 100644 --- a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp +++ b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp @@ -38,8 +38,8 @@ class BCN_API protocol_block_in_31800 const network::channel::ptr& channel, bool performance_enabled=true) NOEXCEPT : protocol_performer(session, channel, performance_enabled), top_checkpoint_height_( - session->config().bitcoin.top_checkpoint().height()), - block_type_(session->config().network.witness_node() ? + session->system_settings().top_checkpoint().height()), + block_type_(session->network_settings().witness_node() ? type_id::witness_block : type_id::block), map_(chaser_check::empty_map()), network::tracker(session->log) diff --git a/include/bitcoin/node/protocols/protocol_block_out_106.hpp b/include/bitcoin/node/protocols/protocol_block_out_106.hpp index 5877c5822..26217b93e 100644 --- a/include/bitcoin/node/protocols/protocol_block_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out_106.hpp @@ -35,7 +35,7 @@ class BCN_API protocol_block_out_106 protocol_block_out_106(const auto& session, const network::channel::ptr& channel) NOEXCEPT : node::protocol_peer(session, channel), - node_witness_(session->config().network.witness_node()), + node_witness_(session->network_settings().witness_node()), network::tracker(session->log) { } diff --git a/include/bitcoin/node/protocols/protocol_electrum.hpp b/include/bitcoin/node/protocols/protocol_electrum.hpp deleted file mode 100644 index c489212cb..000000000 --- a/include/bitcoin/node/protocols/protocol_electrum.hpp +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_ELECTRUM_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_ELECTRUM_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API protocol_electrum - : public node::protocol_rpc, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - using rpc_interface = interface::electrum; - - inline protocol_electrum(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol_rpc(session, channel, options), - channel_(std::dynamic_pointer_cast(channel)), - network::tracker(session->log) - { - } - - void start() NOEXCEPT override; - -protected: - /// Handlers (blockchain). - void handle_blockchain_block_header(const code& ec, - rpc_interface::blockchain_block_header, double height, - double cp_height) NOEXCEPT; - void handle_blockchain_block_headers(const code& ec, - rpc_interface::blockchain_block_headers, double start_height, - double count, double cp_height) NOEXCEPT; - void handle_blockchain_headers_subscribe(const code& ec, - rpc_interface::blockchain_headers_subscribe) NOEXCEPT; - void handle_blockchain_estimatefee(const code& ec, - rpc_interface::blockchain_estimatefee, double) NOEXCEPT; - void handle_blockchain_relayfee(const code& ec, - rpc_interface::blockchain_relayfee) NOEXCEPT; - void handle_blockchain_scripthash_get_balance(const code& ec, - rpc_interface::blockchain_scripthash_get_balance, - const std::string& scripthash) NOEXCEPT; - void handle_blockchain_scripthash_get_history(const code& ec, - rpc_interface::blockchain_scripthash_get_history, - const std::string& scripthash) NOEXCEPT; - void handle_blockchain_scripthash_get_mempool(const code& ec, - rpc_interface::blockchain_scripthash_get_mempool, - const std::string& scripthash) NOEXCEPT; - void handle_blockchain_scripthash_listunspent(const code& ec, - rpc_interface::blockchain_scripthash_listunspent, - const std::string& scripthash) NOEXCEPT; - void handle_blockchain_scripthash_subscribe(const code& ec, - rpc_interface::blockchain_scripthash_subscribe, - const std::string& scripthash) NOEXCEPT; - void handle_blockchain_scripthash_unsubscribe(const code& ec, - rpc_interface::blockchain_scripthash_unsubscribe, - const std::string& scripthash) NOEXCEPT; - void handle_blockchain_transaction_broadcast(const code& ec, - rpc_interface::blockchain_transaction_broadcast, - const std::string& raw_tx) NOEXCEPT; - void handle_blockchain_transaction_get(const code& ec, - rpc_interface::blockchain_transaction_get, const std::string& tx_hash, - bool verbose) NOEXCEPT; - void handle_blockchain_transaction_get_merkle(const code& ec, - rpc_interface::blockchain_transaction_get_merkle, - const std::string& tx_hash, double height) NOEXCEPT; - void handle_blockchain_transaction_id_from_pos(const code& ec, - rpc_interface::blockchain_transaction_id_from_pos, double height, - double tx_pos, bool merkle) NOEXCEPT; - - /// Handlers (server). - void handle_server_add_peer(const code& ec, - rpc_interface::server_add_peer, - const interface::object_t& features) NOEXCEPT; - void handle_server_banner(const code& ec, - rpc_interface::server_banner) NOEXCEPT; - void handle_server_donation_address(const code& ec, - rpc_interface::server_donation_address) NOEXCEPT; - void handle_server_features(const code& ec, - rpc_interface::server_features) NOEXCEPT; - void handle_server_peers_subscribe(const code& ec, - rpc_interface::server_peers_subscribe) NOEXCEPT; - void handle_server_ping(const code& ec, - rpc_interface::server_ping) NOEXCEPT; - ////void handle_server_version(const code& ec, - //// rpc_interface::server_version, const std::string& client_name, - //// const interface::value_t& protocol_version) NOEXCEPT; - - /// Handlers (mempool). - void handle_mempool_get_fee_histogram(const code& ec, - rpc_interface::mempool_get_fee_histogram) NOEXCEPT; - -protected: - inline bool is_version(electrum_version version) const NOEXCEPT - { - return channel_->version() >= version; - } - -private: - // This is mostly thread safe, and used in a thread safe manner. - const channel_t::ptr channel_; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_electrum_version.hpp b/include/bitcoin/node/protocols/protocol_electrum_version.hpp deleted file mode 100644 index 0356508ce..000000000 --- a/include/bitcoin/node/protocols/protocol_electrum_version.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_ELECTRUM_VERSION_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_ELECTRUM_VERSION_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API protocol_electrum_version - : public node::protocol_rpc, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - using rpc_interface = interface::electrum; - - inline protocol_electrum_version(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol_rpc(session, channel, options), - channel_(std::dynamic_pointer_cast(channel)), - network::tracker(session->log) - { - } - - virtual void shake(network::result_handler&& handler) NOEXCEPT; - virtual void complete(const code& ec, const code& shake) NOEXCEPT; - -protected: - static constexpr electrum_version minimum = electrum_version::v1_4; - static constexpr electrum_version maximum = electrum_version::v1_4_2; - static constexpr size_t max_client_name_length = 1024; - - void handle_server_version(const code& ec, - rpc_interface::server_version, const std::string& client_name, - const interface::value_t& protocol_version) NOEXCEPT; - - electrum_version version() const NOEXCEPT; - std::string_view negotiated_version() const NOEXCEPT; - bool set_version(const interface::value_t& version) NOEXCEPT; - bool get_versions(electrum_version& min, electrum_version& max, - const interface::value_t& version) NOEXCEPT; - - std::string_view server_name() const NOEXCEPT; - std::string_view client_name() const NOEXCEPT; - std::string escape_client(const std::string& in) NOEXCEPT; - bool set_client(const std::string& name) NOEXCEPT; - -private: - static std::string_view version_to_string( - electrum_version version) NOEXCEPT; - static electrum_version version_from_string( - const std::string_view& version) NOEXCEPT; - - // This is mostly thread safe, and used in a thread safe manner. - const channel_t::ptr channel_; - - // This is protected by strand. - std::shared_ptr handler_{}; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_explore.hpp b/include/bitcoin/node/protocols/protocol_explore.hpp deleted file mode 100644 index 2ba17082c..000000000 --- a/include/bitcoin/node/protocols/protocol_explore.hpp +++ /dev/null @@ -1,184 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_EXPLORE_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_EXPLORE_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API protocol_explore - : public node::protocol_html, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - using interface = node::interface::explore; - using dispatcher = network::rpc::dispatcher; - - inline protocol_explore(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol_html(session, channel, options), - network::tracker(session->log) - { - } - - void start() NOEXCEPT override; - void stopping(const code& ec) NOEXCEPT override; - -protected: - template - inline void subscribe(Method&& method, Args&&... args) NOEXCEPT - { - dispatcher_.subscribe(BIND_SHARED(method, args)); - } - - /// Dispatch. - bool try_dispatch_object( - const network::http::request& request) NOEXCEPT override; - - /// REST interface handlers. - - bool handle_get_top(const code& ec, interface::top, - uint8_t version, uint8_t media) NOEXCEPT; - - bool handle_get_block(const code& ec, interface::block, - uint8_t version, uint8_t media, std::optional hash, - std::optional height, bool witness) NOEXCEPT; - bool handle_get_block_header(const code& ec, interface::block_header, - uint8_t version, uint8_t media, std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_block_header_context(const code& ec, interface::block_header_context, - uint8_t version, uint8_t media, std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_block_details(const code& ec, interface::block_details, - uint8_t version, uint8_t media, std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_block_txs(const code& ec, interface::block_txs, - uint8_t version, uint8_t media, std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_block_filter(const code& ec, interface::block_filter, - uint8_t version, uint8_t media, uint8_t type, - std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_block_filter_hash(const code& ec, - interface::block_filter_hash, uint8_t version, uint8_t media, - uint8_t type, std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_block_filter_header(const code& ec, - interface::block_filter_header, uint8_t version, uint8_t media, - uint8_t type, std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_block_tx(const code& ec, interface::block_tx, - uint8_t version, uint8_t media, uint32_t position, - std::optional hash, - std::optional height, bool witness) NOEXCEPT; - - bool handle_get_tx(const code& ec, interface::tx, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - bool witness) NOEXCEPT; - bool handle_get_tx_header(const code& ec, interface::tx_header, - uint8_t version, uint8_t media, - const system::hash_cptr& hash) NOEXCEPT; - bool handle_get_tx_details(const code& ec, interface::tx_details, - uint8_t version, uint8_t media, - const system::hash_cptr& hash) NOEXCEPT; - - bool handle_get_inputs(const code& ec, interface::inputs, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - bool witness) NOEXCEPT; - bool handle_get_input(const code& ec, interface::input, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - uint32_t index, bool witness) NOEXCEPT; - bool handle_get_input_script(const code& ec, interface::input_script, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - uint32_t index) NOEXCEPT; - bool handle_get_input_witness(const code& ec, interface::input_witness, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - uint32_t index) NOEXCEPT; - - bool handle_get_outputs(const code& ec, interface::outputs, - uint8_t version, uint8_t media, - const system::hash_cptr& hash) NOEXCEPT; - bool handle_get_output(const code& ec, interface::output, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - uint32_t index) NOEXCEPT; - bool handle_get_output_script(const code& ec, interface::output_script, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - uint32_t index) NOEXCEPT; - bool handle_get_output_spender(const code& ec, interface::output_spender, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - uint32_t index) NOEXCEPT; - bool handle_get_output_spenders(const code& ec, interface::output_spenders, - uint8_t version, uint8_t media, const system::hash_cptr& hash, - uint32_t index) NOEXCEPT; - - bool handle_get_address(const code& ec, interface::address, - uint8_t version, uint8_t media, - const system::hash_cptr& hash, bool turbo) NOEXCEPT; - bool handle_get_address_confirmed(const code& ec, - interface::address_confirmed, uint8_t version, uint8_t media, - const system::hash_cptr& hash, bool turbo) NOEXCEPT; - bool handle_get_address_unconfirmed(const code& ec, - interface::address_unconfirmed, uint8_t version, uint8_t media, - const system::hash_cptr& hash, bool turbo) NOEXCEPT; - bool handle_get_address_balance(const code& ec, - interface::address_balance, uint8_t version, uint8_t media, - const system::hash_cptr& hash, bool turbo) NOEXCEPT; - -private: - void do_get_address(uint8_t media, bool turbo, - const system::hash_cptr& hash) NOEXCEPT; - void do_get_address_confirmed(uint8_t media, bool turbo, - const system::hash_cptr& hash) NOEXCEPT; - ////void do_get_address_unconfirmed(uint8_t media, bool turbo, - //// const system::hash_cptr& hash) NOEXCEPT; - void complete_get_address(const code& ec, uint8_t media, - const database::outpoints& set) NOEXCEPT; - - void do_get_address_balance(uint8_t media, bool turbo, - const system::hash_cptr& hash) NOEXCEPT; - void complete_get_address_balance(const code& ec, uint8_t media, - const uint64_t balance) NOEXCEPT; - - void inject(boost::json::value& out, std::optional height, - const database::header_link& link) const NOEXCEPT; - - database::header_link to_header(const std::optional& height, - const std::optional& hash) NOEXCEPT; - - // This is thread safe. - std::atomic_bool stopping_{}; - - // This is protected by strand. - dispatcher dispatcher_{}; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_html.hpp b/include/bitcoin/node/protocols/protocol_html.hpp deleted file mode 100644 index 1d28ea5dd..000000000 --- a/include/bitcoin/node/protocols/protocol_html.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_HTML_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_HTML_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -/// Abstract base for HTML protocols, thread safe. -/// To keep inheritance simple this derives from protocol_ws, which in turn -/// derives from protocol_http (as required). So any html server is able to -/// operate as a websocket server. -class BCN_API protocol_html - : public node::protocol_http, - public network::protocol_ws, - protected network::tracker -{ -public: - using options_t = server::settings::html_server; - using channel_t = node::channel_ws; - -protected: - inline protocol_html(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol_http(session, channel), - network::protocol_ws(session, channel, options), - options_(options), - network::tracker(session->log) - { - } - - /// Message handlers by http method. - void handle_receive_get(const code& ec, - const network::http::method::get::cptr& get) NOEXCEPT override; - - /// Dispatch. - virtual bool try_dispatch_object( - const network::http::request& request) NOEXCEPT; - virtual void dispatch_file( - const network::http::request& request) NOEXCEPT; - virtual void dispatch_embedded( - const network::http::request& request) NOEXCEPT; - - /// Senders. - virtual void send_json(boost::json::value&& model, size_t size_hint, - const network::http::request& request={}) NOEXCEPT; - virtual void send_text(std::string&& hexidecimal, - const network::http::request& request={}) NOEXCEPT; - virtual void send_chunk(system::data_chunk&& bytes, - const network::http::request& request={}) NOEXCEPT; - virtual void send_file(network::http::file&& file, - network::http::media_type type, - const network::http::request& request={}) NOEXCEPT; - virtual void send_span(network::http::span_body::value_type&& span, - network::http::media_type type, - const network::http::request& request={}) NOEXCEPT; - virtual void send_buffer(network::http::buffer_body::value_type&& buffer, - network::http::media_type type, - const network::http::request& request={}) NOEXCEPT; - - /// Utilities. - std::filesystem::path to_path( - const std::string& target = "/") const NOEXCEPT; - std::filesystem::path to_local_path( - const std::string& target = "/") const NOEXCEPT; - -private: - // This is thread safe. - const options_t& options_; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_http.hpp b/include/bitcoin/node/protocols/protocol_http.hpp deleted file mode 100644 index 177cef496..000000000 --- a/include/bitcoin/node/protocols/protocol_http.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_HTTP_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_HTTP_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -/// Abstract base for HTTP protocols, thread safe. -class BCN_API protocol_http - : public node::protocol -{ -protected: - inline protocol_http(const auto& session, - const network::channel::ptr& channel) NOEXCEPT - : node::protocol(session, channel) - { - } - - /// Cache request for serialization (requires strand). - void set_request(const network::http::request_cptr& request) NOEXCEPT; - - /// Obtain cached request and clear cache (requires strand). - network::http::request_cptr reset_request() NOEXCEPT; - -private: - // This is protected by strand. - network::http::request_cptr request_{}; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_observer.hpp b/include/bitcoin/node/protocols/protocol_observer.hpp index 4ef8bc58b..c464fafc6 100644 --- a/include/bitcoin/node/protocols/protocol_observer.hpp +++ b/include/bitcoin/node/protocols/protocol_observer.hpp @@ -43,9 +43,9 @@ class BCN_API protocol_observer ( std::dynamic_pointer_cast(channel)-> is_negotiated(network::messages::peer::level::bip37) && - !session->config().network.enable_relay + !session->network_settings().enable_relay ), - node_witness_(session->config().network.witness_node()), + node_witness_(session->network_settings().witness_node()), network::tracker(session->log) { } diff --git a/include/bitcoin/node/protocols/protocol_performer.hpp b/include/bitcoin/node/protocols/protocol_performer.hpp index 59a6ac864..a27c47f1c 100644 --- a/include/bitcoin/node/protocols/protocol_performer.hpp +++ b/include/bitcoin/node/protocols/protocol_performer.hpp @@ -40,10 +40,10 @@ class BCN_API protocol_performer protocol_performer(const auto& session, const network::channel::ptr& channel, bool enabled) NOEXCEPT : node::protocol_peer(session, channel), - deviation_(session->config().node.allowed_deviation > 0.0), - enabled_(enabled && to_bool(session->config().node.sample_period_seconds)), + deviation_(session->node_settings().allowed_deviation > 0.0), + enabled_(enabled && to_bool(session->node_settings().sample_period_seconds)), performance_timer_(std::make_shared(session->log, - channel->strand(), session->config().node.sample_period())), + channel->strand(), session->node_settings().sample_period())), network::tracker(session->log) { } diff --git a/include/bitcoin/node/protocols/protocol_rpc.hpp b/include/bitcoin/node/protocols/protocol_rpc.hpp deleted file mode 100644 index e0e874336..000000000 --- a/include/bitcoin/node/protocols/protocol_rpc.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_RPC_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_RPC_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -/// Abstract base for RPC protocols, thread safe. -template -class BCN_API protocol_rpc - : public node::protocol, - public network::protocol_rpc -{ -public: - using channel_t = Channel; - using options_t = channel_t::options_t; - -protected: - inline protocol_rpc(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol(session, channel), - network::protocol_rpc( - session, channel, options) - { - } -}; - -#define SUBSCRIBE_RPC(...) SUBSCRIBE_CHANNEL(void, __VA_ARGS__) -#define SEND_RPC(message, size_hint, method, ...) \ - send(message, size_hint, &CLASS::method, __VA_ARGS__) - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_stratum_v1.hpp b/include/bitcoin/node/protocols/protocol_stratum_v1.hpp deleted file mode 100644 index 7d0ac220f..000000000 --- a/include/bitcoin/node/protocols/protocol_stratum_v1.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_STRATUM_V1_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_STRATUM_V1_HPP - -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API protocol_stratum_v1 - : public node::protocol_rpc, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - using rpc_interface = interface::stratum_v1; - - inline protocol_stratum_v1(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol_rpc(session, channel, options), - network::tracker(session->log) - { - } - - void start() NOEXCEPT override; - -protected: - /// Handlers (client requests). - bool handle_mining_subscribe(const code& ec, - rpc_interface::mining_subscribe, const std::string& user_agent, - double extranonce1_size) NOEXCEPT; - bool handle_mining_authorize(const code& ec, - rpc_interface::mining_authorize, const std::string& username, - const std::string& password) NOEXCEPT; - bool handle_mining_submit(const code& ec, - rpc_interface::mining_submit, const std::string& worker_name, - const std::string& job_id, const std::string& extranonce2, - double ntime, const std::string& nonce) NOEXCEPT; - bool handle_mining_extranonce_subscribe(const code& ec, - rpc_interface::mining_extranonce_subscribe) NOEXCEPT; - bool handle_mining_extranonce_unsubscribe(const code& ec, - rpc_interface::mining_extranonce_unsubscribe, double id) NOEXCEPT; - - /// Handlers (server notifications). - bool handle_mining_configure(const code& ec, - rpc_interface::mining_configure, - const interface::object_t& extensions) NOEXCEPT; - bool handle_mining_set_difficulty(const code& ec, - rpc_interface::mining_set_difficulty, double difficulty) NOEXCEPT; - bool handle_mining_notify(const code& ec, - rpc_interface::mining_notify, const std::string& job_id, - const std::string& prevhash, const std::string& coinb1, - const std::string& coinb2, const interface::array_t& merkle_branch, - double version, double nbits, double ntime, bool clean_jobs, - bool hash1, bool hash2) NOEXCEPT; - bool handle_client_reconnect(const code& ec, - rpc_interface::client_reconnect, const std::string& url, double port, - double id) NOEXCEPT; - bool handle_client_hello(const code& ec, - rpc_interface::client_hello, - const interface::object_t& protocol) NOEXCEPT; - bool handle_client_rejected(const code& ec, - rpc_interface::client_rejected, const std::string& job_id, - const std::string& reject_reason) NOEXCEPT; -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_stratum_v2.hpp b/include/bitcoin/node/protocols/protocol_stratum_v2.hpp deleted file mode 100644 index ad265c906..000000000 --- a/include/bitcoin/node/protocols/protocol_stratum_v2.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_STRATUM_V2_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_STRATUM_V2_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -class BCN_API protocol_stratum_v2 - : public node::protocol, - public network::protocol, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - using channel_t = node::channel_stratum_v2; - - inline protocol_stratum_v2(const auto& session, - const network::channel::ptr& channel, const options_t&) NOEXCEPT - : node::protocol(session, channel), - network::protocol(session, channel), - network::tracker(session->log) - { - } - - // TODO: subscribe to and handle messages. - inline void start() NOEXCEPT override - { - network::protocol::start(); - } -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocol_transaction_in_106.hpp b/include/bitcoin/node/protocols/protocol_transaction_in_106.hpp index 15670a56c..102d7d8bf 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_in_106.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_in_106.hpp @@ -35,7 +35,7 @@ class BCN_API protocol_transaction_in_106 protocol_transaction_in_106(const auto& session, const network::channel::ptr& channel) NOEXCEPT : node::protocol_peer(session, channel), - ////tx_type_(session->config().network.witness_node() ? + ////tx_type_(session->network_settings().witness_node() ? //// type_id::witness_tx : type_id::transaction), network::tracker(session->log) { diff --git a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp index 1f8f518df..75a0f6596 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp @@ -35,7 +35,7 @@ class BCN_API protocol_transaction_out_106 protocol_transaction_out_106(const auto& session, const network::channel::ptr& channel) NOEXCEPT : node::protocol_peer(session, channel), - node_witness_(session->config().network.witness_node()), + node_witness_(session->network_settings().witness_node()), network::tracker(session->log) { } diff --git a/include/bitcoin/node/protocols/protocol_web.hpp b/include/bitcoin/node/protocols/protocol_web.hpp deleted file mode 100644 index a877f3c7d..000000000 --- a/include/bitcoin/node/protocols/protocol_web.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_WEB_HPP -#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_WEB_HPP - -#include -#include -#include - -namespace libbitcoin { -namespace node { - -/// Administrative web site for the node (currently just page server). -class BCN_API protocol_web - : public node::protocol_html, - protected network::tracker -{ -public: - typedef std::shared_ptr ptr; - - inline protocol_web(const auto& session, - const network::channel::ptr& channel, - const options_t& options) NOEXCEPT - : node::protocol_html(session, channel, options), - network::tracker(session->log) - { - } -}; - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/protocols/protocols.hpp b/include/bitcoin/node/protocols/protocols.hpp index 4d96db343..c52f1efb5 100644 --- a/include/bitcoin/node/protocols/protocols.hpp +++ b/include/bitcoin/node/protocols/protocols.hpp @@ -19,14 +19,7 @@ #ifndef LIBBITCOIN_NODE_PROTOCOLS_PROTOCOLS_HPP #define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOLS_HPP -/// base #include -#include -#include -#include -#include - -/// peer #include #include #include @@ -37,18 +30,9 @@ #include #include #include +#include #include #include #include -/// server -#include -#include -#include -#include -#include -#include -#include -#include - #endif diff --git a/include/bitcoin/node/sessions/session.hpp b/include/bitcoin/node/sessions/session.hpp index 5198e4249..bd059ed0f 100644 --- a/include/bitcoin/node/sessions/session.hpp +++ b/include/bitcoin/node/sessions/session.hpp @@ -95,7 +95,11 @@ class BCN_API session node::query& archive() const NOEXCEPT; /// Configuration settings for all libraries. - const configuration& config() const NOEXCEPT; + virtual const node::configuration& node_config() const NOEXCEPT; + virtual const system::settings& system_settings() const NOEXCEPT; + virtual const database::settings& database_settings() const NOEXCEPT; + ////const network::settings& network_settings() const NOEXCEPT override; + virtual const node::settings& node_settings() const NOEXCEPT; /// The candidate|confirmed chain is current. virtual bool is_current(bool confirmed) const NOEXCEPT; diff --git a/include/bitcoin/node/sessions/session_handshake.hpp b/include/bitcoin/node/sessions/session_handshake.hpp deleted file mode 100644 index b1b56cda1..000000000 --- a/include/bitcoin/node/sessions/session_handshake.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_SESSIONS_SESSION_HANDSHAKE_HPP -#define LIBBITCOIN_NODE_SESSIONS_SESSION_HANDSHAKE_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -class full_node; - -/// This is session_server<> with added support for single handshake protocol. -template -class session_handshake - : public node::session_server, - protected network::tracker> -{ -public: - typedef std::shared_ptr> ptr; - using base = node::session_server; - - /// Construct an instance (network should be started). - inline session_handshake(full_node& node, uint64_t identifier, - const base::options_t& options) NOEXCEPT - : node::session_server(node, identifier, options), - network::tracker>(node) - { - } - -protected: - /// Overridden to implement a connection handshake. Handshake protocol(s) - /// must invoke handler one time at completion. Use - /// std::dynamic_pointer_cast(channel) to obtain channel_t. - inline void attach_handshake(const base::channel_ptr& channel, - network::result_handler&& handler) NOEXCEPT override - { - using own = session_handshake; - const auto self = this->template shared_from_base(); - channel->template attach(self, this->options_)-> - shake(std::move(handler)); - } - - /// Enable handshake (network::session_server disables by default). - inline void do_attach_handshake(const base::channel_ptr& channel, - const network::result_handler& handshake) NOEXCEPT override - { - network::session::do_attach_handshake(channel, handshake); - } -}; - -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/sessions/session_peer.hpp b/include/bitcoin/node/sessions/session_peer.hpp index 8dac21a72..5b611621e 100644 --- a/include/bitcoin/node/sessions/session_peer.hpp +++ b/include/bitcoin/node/sessions/session_peer.hpp @@ -62,7 +62,7 @@ class session_peer const auto channel = std::make_shared( this->get_memory(), this->log, socket, this->create_key(), - this->config(), this->options()); + this->node_config(), this->options()); return std::static_pointer_cast(channel); } @@ -93,17 +93,17 @@ class session_peer using base = session_peer; const auto self = this->template shared_from_base(); - const auto relay = this->config().network.enable_relay; - const auto delay = this->config().node.delay_inbound; - const auto headers = this->config().node.headers_first; + const auto relay = this->network_settings().enable_relay; + const auto delay = this->node_settings().delay_inbound; + const auto headers = this->node_settings().headers_first; const auto node_network = to_bool(bit_and ( - this->config().network.services_maximum, + this->network_settings().services_maximum, service::node_network )); const auto node_client_filters = to_bool(bit_and ( - this->config().network.services_maximum, + this->network_settings().services_maximum, service::node_client_filters )); diff --git a/include/bitcoin/node/sessions/session_server.hpp b/include/bitcoin/node/sessions/session_server.hpp deleted file mode 100644 index 18029e676..000000000 --- a/include/bitcoin/node/sessions/session_server.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright (c) 2011-2025 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_SESSIONS_SESSION_SERVER_HPP -#define LIBBITCOIN_NODE_SESSIONS_SESSION_SERVER_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace node { - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -class full_node; - -/// Declare a concrete instance of this type for client-server protocols built -/// on tcp/ip. session_base processing performs all connection management and -/// session tracking. This includes start/stop/disable/enable/black/whitelist. -/// First protocol must declare options_t and channel_t. Each of the protocols -/// are constructed and attached to a constructed instance of channel_t. The -/// protocol construct and attachment can be overridden and/or augmented with -/// other protocols. -template -class session_server - : public node::session, - public network::session_server, - protected network::tracker> -{ -public: - typedef std::shared_ptr> ptr; - - /// Extract the first protocol type from the pack. - template - struct first_protocol { using type = Protocol; }; - - /// The first protocol must define these public types. - using first = typename first_protocol::type; - using options_t = typename first::options_t; - using channel_t = typename first::channel_t; - - /// Construct an instance (network should be started). - inline session_server(full_node& node, uint64_t identifier, - const options_t& options) NOEXCEPT - : node::session(node), - network::session_server((network::net&)node, identifier, options), - options_(options), - network::tracker>(node) - { - } - -protected: - using socket_ptr = network::socket::ptr; - using channel_ptr = network::channel::ptr; - - /// Inbound connection attempts are dropped unless confirmed is current. - /// Used instead of suspension because that has independent start/stop. - inline bool enabled() const NOEXCEPT override - { - return !this->config().node.delay_inbound || this->is_recent(); - } - - /// Override to construct channel. This allows the implementation to pass - /// other values to protocol construction and/or select the desired channel - /// based on available factors (e.g. a distinct protocol version). - inline channel_ptr create_channel( - const socket_ptr& socket) NOEXCEPT override - { - const auto channel = std::make_shared(log, socket, - this->create_key(), this->config(), this->options_); - - return std::static_pointer_cast(channel); - } - - template = true> - inline void attach_rest(const channel_ptr&, const ptr&) NOEXCEPT{} - - template - inline void attach_rest(const channel_ptr& channel, const ptr& self) NOEXCEPT - { - channel->template attach(self, this->options_)->start(); - attach_rest(channel, self); - } - - /// Overridden to set channel protocols. This allows the implementation to - /// pass other values to protocol construction and/or select the desired - /// protocol based on available factors (e.g. a distinct protocol version). - /// Use std::dynamic_pointer_cast(channel) to obtain channel_t. - inline void attach_protocols(const channel_ptr& channel) NOEXCEPT override - { - using own = session_server; - const auto self = this->template shared_from_base(); - attach_rest(channel, self); - } - -protected: - // This is thread safe. - const options_t& options_; -}; - -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/node/sessions/sessions.hpp b/include/bitcoin/node/sessions/sessions.hpp index 2fe747f3f..471abffe3 100644 --- a/include/bitcoin/node/sessions/sessions.hpp +++ b/include/bitcoin/node/sessions/sessions.hpp @@ -20,28 +20,9 @@ #define LIBBITCOIN_NODE_SESSIONS_SESSIONS_HPP #include -#include #include #include #include #include -#include - -#include - -namespace libbitcoin { -namespace node { - -/// Alias server sessions, all derived from node::session. -using session_web = session_server; -using session_explore = session_server; -using session_bitcoind = session_server; -using session_stratum_v1 = session_server; -using session_stratum_v2 = session_server; -using session_electrum = session_handshake; - -} // namespace node -} // namespace libbitcoin #endif diff --git a/include/bitcoin/node/settings.hpp b/include/bitcoin/node/settings.hpp index d8800acc2..8a5ea16ff 100644 --- a/include/bitcoin/node/settings.hpp +++ b/include/bitcoin/node/settings.hpp @@ -23,42 +23,6 @@ #include namespace libbitcoin { -namespace log { - -/// [log] settings. -class BCN_API settings -{ -public: - DEFAULT_COPY_MOVE_DESTRUCT(settings); - - settings() NOEXCEPT; - settings(system::chain::selection context) NOEXCEPT; - - bool application; - bool news; - bool session; - bool protocol; - bool proxy; - bool remote; - bool fault; - bool quitting; - bool objects; - bool verbose; - - uint32_t maximum_size; - std::filesystem::path path; - -#if defined (HAVE_MSC) - std::filesystem::path symbols; -#endif - - virtual std::filesystem::path log_file1() const NOEXCEPT; - virtual std::filesystem::path log_file2() const NOEXCEPT; - virtual std::filesystem::path events_file() const NOEXCEPT; -}; - -} // namespace log - namespace node { /// [node] settings. @@ -99,86 +63,6 @@ class BCN_API settings }; } // namespace node - -namespace server { - -// HACK: must cast writer to non-const. -using span_value = network::http::span_body::value_type; - -/// [server] settings. -class BCN_API settings -{ -public: - /// References to process embeded resources for html_server. - struct embedded_pages - { - DEFAULT_COPY_MOVE_DESTRUCT(embedded_pages); - embedded_pages() = default; - - virtual span_value css() const NOEXCEPT; - virtual span_value html() const NOEXCEPT; - virtual span_value ecma() const NOEXCEPT; - virtual span_value font() const NOEXCEPT; - virtual span_value icon() const NOEXCEPT; - - /// At least the html page is required to load embedded site. - virtual bool enabled() const NOEXCEPT; - }; - - /// html (http/s) document server settings (has directory/default). - /// This is for web servers that expose a local file system directory. - struct html_server - : public network::settings::websocket_server - { - // embedded_pages reference precludes copy. - DELETE_COPY(html_server); - - html_server(const std::string_view& logging_name, - const embedded_pages& embedded) NOEXCEPT; - - /// Embeded single page with html, css, js, favicon resource. - /// This is a reference to the caller's resource (retained instance). - const embedded_pages& pages; - - /// Set false to disable http->websocket http upgrade processing. - bool websocket{ true }; - - /// Directory to serve. - std::filesystem::path path{}; - - /// Default page for default URL (recommended). - std::string default_{ "index.html" }; - - /// !path.empty() && http_server::enabled() [hidden, not virtual] - virtual bool enabled() const NOEXCEPT; - }; - - // html_server precludes copy. - DELETE_COPY(settings); - - settings(system::chain::selection context, const embedded_pages& explore, - const embedded_pages& web) NOEXCEPT; - - /// native admin web interface, isolated (http/s, stateless html) - server::settings::html_server web; - - /// native RESTful block explorer (http/s, stateless html/websocket) - server::settings::html_server explore; - - /// bitcoind compat interface (http/s, stateless json-rpc-v2) - network::settings::http_server bitcoind{ "bitcoind" }; - - /// electrum compat interface (tcp/s, json-rpc-v2) - network::settings::tcp_server electrum{ "electrum" }; - - /// stratum v1 compat interface (tcp/s, json-rpc-v1, auth handshake) - network::settings::tcp_server stratum_v1{ "stratum_v1" }; - - /// stratum v2 compat interface (tcp[/s], binary, auth/privacy handshake) - network::settings::tcp_server stratum_v2{ "stratum_v2" }; -}; - -} // namespace server } // namespace libbitcoin #endif diff --git a/src/chasers/chaser.cpp b/src/chasers/chaser.cpp index c13168b81..13c8190e1 100644 --- a/src/chasers/chaser.cpp +++ b/src/chasers/chaser.cpp @@ -34,7 +34,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser::chaser(full_node& node) NOEXCEPT : node_(node), strand_(node.service().get_executor()), - top_checkpoint_height_(node.config().bitcoin.top_checkpoint().height()), + top_checkpoint_height_(node.system_settings().top_checkpoint().height()), reporter(node.log) { } @@ -122,14 +122,34 @@ bool chaser::stranded() const NOEXCEPT // Properties. // ---------------------------------------------------------------------------- -const node::configuration& chaser::config() const NOEXCEPT +query& chaser::archive() const NOEXCEPT { - return node_.config(); + return node_.archive(); } -query& chaser::archive() const NOEXCEPT +const node::configuration& chaser::node_config() const NOEXCEPT { - return node_.archive(); + return node_.node_config(); +} + +const system::settings& chaser::system_settings() const NOEXCEPT +{ + return node_.system_settings(); +} + +const database::settings& chaser::database_settings() const NOEXCEPT +{ + return node_.database_settings(); +} + +const network::settings& chaser::network_settings() const NOEXCEPT +{ + return node_.network_settings(); +} + +const node::settings& chaser::node_settings() const NOEXCEPT +{ + return node_.node_settings(); } bool chaser::is_current(bool confirmed) const NOEXCEPT diff --git a/src/chasers/chaser_block.cpp b/src/chasers/chaser_block.cpp index 0851a70ae..9c14ab166 100644 --- a/src/chasers/chaser_block.cpp +++ b/src/chasers/chaser_block.cpp @@ -31,7 +31,7 @@ using namespace system::chain; chaser_block::chaser_block(full_node& node) NOEXCEPT : chaser_organize(node), - node_witness_(node.config().network.witness_node()) + node_witness_(node.network_settings().witness_node()) { } diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index 8602102ac..4f5c4520f 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -44,10 +44,10 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser_check::chaser_check(full_node& node) NOEXCEPT : chaser(node), - maximum_concurrency_(node.config().node.maximum_concurrency_()), - maximum_height_(node.config().node.maximum_height_()), - connections_(node.config().network.outbound.connections), - allowed_deviation_(node.config().node.allowed_deviation) + maximum_concurrency_(node.node_settings().maximum_concurrency_()), + maximum_height_(node.node_settings().maximum_height_()), + connections_(node.network_settings().outbound.connections), + allowed_deviation_(node.node_settings().allowed_deviation) { } @@ -518,7 +518,7 @@ size_t chaser_check::set_unassociated() NOEXCEPT size_t chaser_check::get_inventory_size() const NOEXCEPT { // Either condition means blocks shouldn't be getting downloaded (yet). - const size_t peers = config().network.outbound.connections; + const size_t peers = network_settings().outbound.connections; if (is_zero(peers) || !is_current(false)) return zero; diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index e75f49e15..37b79ba3c 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -36,7 +36,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser_confirm::chaser_confirm(full_node& node) NOEXCEPT : chaser(node), filter_(node.archive().filter_enabled()), - defer_(node.config().node.defer_confirmation) + defer_(node.node_settings().defer_confirmation) { } diff --git a/src/chasers/chaser_header.cpp b/src/chasers/chaser_header.cpp index 544d9f9e4..6b26a5f61 100644 --- a/src/chasers/chaser_header.cpp +++ b/src/chasers/chaser_header.cpp @@ -29,7 +29,7 @@ using namespace system::chain; chaser_header::chaser_header(full_node& node) NOEXCEPT : chaser_organize
(node), - milestone_(config().bitcoin.milestone) + milestone_(system_settings().milestone) { } diff --git a/src/chasers/chaser_snapshot.cpp b/src/chasers/chaser_snapshot.cpp index bac455d9b..d399a60f2 100644 --- a/src/chasers/chaser_snapshot.cpp +++ b/src/chasers/chaser_snapshot.cpp @@ -38,9 +38,9 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser_snapshot::chaser_snapshot(full_node& node) NOEXCEPT : chaser(node) - ////snapshot_bytes_(node.config().node.snapshot_bytes), - ////snapshot_valid_(node.config().node.snapshot_valid), - ////snapshot_confirm_(node.config().node.snapshot_confirm), + ////snapshot_bytes_(node.node_settings().snapshot_bytes), + ////snapshot_valid_(node.node_settings().snapshot_valid), + ////snapshot_confirm_(node.node_settings().snapshot_confirm), ////enabled_bytes_(to_bool(snapshot_bytes_)), ////enabled_valid_(to_bool(snapshot_valid_)), ////enabled_confirm_(to_bool(snapshot_confirm_)) diff --git a/src/chasers/chaser_storage.cpp b/src/chasers/chaser_storage.cpp index 88250f8da..c0c7de4a7 100644 --- a/src/chasers/chaser_storage.cpp +++ b/src/chasers/chaser_storage.cpp @@ -38,7 +38,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) chaser_storage::chaser_storage(full_node& node) NOEXCEPT : chaser(node), - store_(node.config().database.path) + store_(node.database_settings().path) { } diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index 0f484b8c6..8dded3450 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -37,21 +37,21 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) // Independent threadpool and strand (base class strand uses network pool). chaser_validate::chaser_validate(full_node& node) NOEXCEPT : chaser(node), - threadpool_(node.config().node.threads_(), - node.config().node.thread_priority_()), + threadpool_(node.node_settings().threads_(), + node.node_settings().thread_priority_()), independent_strand_(threadpool_.service().get_executor()), - subsidy_interval_(node.config().bitcoin.subsidy_interval_blocks), - initial_subsidy_(node.config().bitcoin.initial_subsidy()), - maximum_backlog_(node.config().node.maximum_concurrency_()), - node_witness_(node.config().network.witness_node()), - defer_(node.config().node.defer_validation), + subsidy_interval_(node.system_settings().subsidy_interval_blocks), + initial_subsidy_(node.system_settings().initial_subsidy()), + maximum_backlog_(node.node_settings().maximum_concurrency_()), + node_witness_(node.network_settings().witness_node()), + defer_(node.node_settings().defer_validation), filter_(!defer_ && node.archive().filter_enabled()) { } code chaser_validate::start() NOEXCEPT { - if (!config().node.headers_first) + if (!node_settings().headers_first) return error::success; const auto& query = archive(); diff --git a/src/configuration.cpp b/src/configuration.cpp index 0da66230b..9b602b6d2 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -23,18 +23,12 @@ namespace libbitcoin { namespace node { -using namespace bc::system; - // Construct with defaults derived from given context. -configuration::configuration(system::chain::selection context, - const server::settings::embedded_pages& explore, - const server::settings::embedded_pages& web) NOEXCEPT - : log(context), - server(context, explore, web), - node(context), - network(context), +configuration::configuration(system::chain::selection context) NOEXCEPT + : bitcoin(context), database(context), - bitcoin(context) + network(context), + node(context) { } diff --git a/src/error.cpp b/src/error.cpp index 7e0c6f7b5..10e189464 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -88,30 +88,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { confirm10, "confirm10" }, { confirm11, "confirm11" }, { confirm12, "confirm12" }, - { confirm13, "confirm13" }, - - // server (url parse codes) - { empty_path, "empty_path" }, - { invalid_number, "invalid_number" }, - { invalid_hash, "invalid_hash" }, - { missing_version, "missing_version" }, - { missing_target, "missing_target" }, - { invalid_target, "invalid_target" }, - { missing_hash, "missing_hash" }, - { missing_height, "missing_height" }, - { missing_position, "missing_position" }, - { missing_id_type, "missing_id_type" }, - { invalid_id_type, "invalid_id_type" }, - { missing_type_id, "missing_type_id" }, - { missing_component, "missing_component" }, - { invalid_component, "invalid_component" }, - { invalid_subcomponent, "invalid_subcomponent" }, - { extra_segment, "extra_segment" }, - - // server (rpc response codes) - { not_found, "not_found" }, - { invalid_argument, "invalid_argument" }, - { not_implemented, "not_implemented" } + { confirm13, "confirm13" } }; DEFINE_ERROR_T_CATEGORY(error, "node", "node code") diff --git a/src/full_node.cpp b/src/full_node.cpp index 4f3be1db1..017efa5a6 100644 --- a/src/full_node.cpp +++ b/src/full_node.cpp @@ -76,7 +76,7 @@ void full_node::do_start(const result_handler& handler) NOEXCEPT BC_ASSERT(stranded()); code ec{}; - if (((ec = (config().node.headers_first ? + if (((ec = (config_.node.headers_first ? chaser_header_.start() : chaser_block_.start()))) || ((ec = chaser_check_.start())) || @@ -115,96 +115,7 @@ void full_node::do_run(const result_handler& handler) NOEXCEPT do_notify(error::success, chase::start, height_t{}); // Start services after network is running. - net::do_run(std::bind(&full_node::start_web, this, _1, handler)); -} - -void full_node::start_web(const code& ec, - const result_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (ec) - { - handler(ec); - return; - } - - attach_web_session()->start( - std::bind(&full_node::start_explore, this, _1, handler)); -} - -void full_node::start_explore(const code& ec, - const result_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (ec) - { - handler(ec); - return; - } - - attach_explore_session()->start( - std::bind(&full_node::start_bitcoind, this, _1, handler)); -} - -void full_node::start_bitcoind(const code& ec, - const result_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (ec) - { - handler(ec); - return; - } - - attach_bitcoind_session()->start( - std::bind(&full_node::start_electrum, this, _1, handler)); -} - -void full_node::start_electrum(const code& ec, - const result_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (ec) - { - handler(ec); - return; - } - - attach_electrum_session()->start( - std::bind(&full_node::start_stratum_v1, this, _1, handler)); -} - -void full_node::start_stratum_v1(const code& ec, - const result_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (ec) - { - handler(ec); - return; - } - - attach_stratum_v1_session()->start( - std::bind(&full_node::start_stratum_v2, this, _1, handler)); -} - -void full_node::start_stratum_v2(const code& ec, - const result_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (ec) - { - handler(ec); - return; - } - - attach_stratum_v2_session()->start(move_copy(handler)); + net::do_run(handler); } void full_node::close() NOEXCEPT @@ -458,11 +369,31 @@ query& full_node::archive() const NOEXCEPT return query_; } -const configuration& full_node::config() const NOEXCEPT +const node::configuration& full_node::node_config() const NOEXCEPT { return config_; } +const system::settings& full_node::system_settings() const NOEXCEPT +{ + return config_.bitcoin; +} + +const database::settings& full_node::database_settings() const NOEXCEPT +{ + return config_.database; +} + +////const network::settings& full_node::network_settings() const NOEXCEPT +////{ +//// return network_.network_settings(); +////} + +const node::settings& full_node::node_settings() const NOEXCEPT +{ + return config_.node; +} + bool full_node::is_current(bool confirmed) const NOEXCEPT { if (is_zero(config_.node.currency_window_minutes)) @@ -525,36 +456,6 @@ network::session_outbound::ptr full_node::attach_outbound_session() NOEXCEPT return net::attach(*this); } -session_web::ptr full_node::attach_web_session() NOEXCEPT -{ - return net::attach(*this, config_.server.web); -} - -session_explore::ptr full_node::attach_explore_session() NOEXCEPT -{ - return net::attach(*this, config_.server.explore); -} - -session_bitcoind::ptr full_node::attach_bitcoind_session() NOEXCEPT -{ - return net::attach(*this, config_.server.bitcoind); -} - -session_electrum::ptr full_node::attach_electrum_session() NOEXCEPT -{ - return net::attach(*this, config_.server.electrum); -} - -session_stratum_v1::ptr full_node::attach_stratum_v1_session() NOEXCEPT -{ - return net::attach(*this, config_.server.stratum_v1); -} - -session_stratum_v2::ptr full_node::attach_stratum_v2_session() NOEXCEPT -{ - return net::attach(*this, config_.server.stratum_v2); -} - BC_POP_WARNING() } // namespace node diff --git a/src/parser.cpp b/src/parser.cpp deleted file mode 100644 index e2e12f96e..000000000 --- a/src/parser.cpp +++ /dev/null @@ -1,1618 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 - -////std::filesystem::path config_default_path() NOEXCEPT -////{ -//// return { "libbitcoin/bn.cfg" }; -////} - -namespace libbitcoin { -namespace node { - -using namespace bc::system; -using namespace bc::system::config; -using namespace boost::program_options; - -// Initialize configuration using defaults of the given context. -parser::parser(system::chain::selection context, - const server::settings::embedded_pages& explore, - const server::settings::embedded_pages& web) NOEXCEPT - : configured(context, explore, web) -{ - // node - - configured.node.threads = 32; - - // network - - using level = network::messages::peer::level; - using service = network::messages::peer::service; - - configured.network.threads = 16; - configured.network.enable_relay = true; - configured.network.enable_address = true; - configured.network.enable_address_v2 = false; - configured.network.enable_witness_tx = false; - configured.network.enable_compact = false; - configured.network.outbound.host_pool_capacity = 10000; - configured.network.outbound.connections = 100; - configured.network.inbound.connections = 100; - configured.network.maximum_skew_minutes = 120; - configured.network.protocol_minimum = level::headers_protocol; - configured.network.protocol_maximum = level::bip130; - - // services_minimum must be node_witness to be a witness node. - configured.network.services_minimum = service::node_network | service::node_witness; - configured.network.services_maximum = service::node_network | service::node_witness; - - // TODO: from bitcoind, revert to defaults when seeds are up. - configured.network.outbound.seeds.clear(); - configured.network.outbound.seeds.emplace_back("seed.bitcoin.sipa.be", 8333_u16); - configured.network.outbound.seeds.emplace_back("dnsseed.bluematt.me", 8333_u16); - ////configured.network.outbound.seeds.emplace_back("dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us", 8333_u16); - configured.network.outbound.seeds.emplace_back("seed.bitcoin.jonasschnelli.ch", 8333_u16); - configured.network.outbound.seeds.emplace_back("seed.btc.petertodd.net", 8333_u16); - configured.network.outbound.seeds.emplace_back("seed.bitcoin.sprovoost.nl", 8333_u16); - configured.network.outbound.seeds.emplace_back("dnsseed.emzy.de", 8333_u16); - configured.network.outbound.seeds.emplace_back("seed.bitcoin.wiz.biz", 8333_u16); - configured.network.outbound.seeds.emplace_back("seed.mainnet.achownodes.xyz", 8333_u16); - - // admin - configured.server.web.binds.emplace_back(asio::address{}, 8080_u16); - configured.server.explore.binds.emplace_back(asio::address{}, 8180_u16); - configured.server.bitcoind.binds.emplace_back(asio::address{}, 8380_u16); - configured.server.electrum.binds.emplace_back(asio::address{}, 8480_u16); - configured.server.stratum_v1.binds.emplace_back(asio::address{}, 8580_u16); - configured.server.stratum_v2.binds.emplace_back(asio::address{}, 8680_u16); - - // SCALE: LF2.2 @ 850K. - - // database (archive) - - configured.database.header_buckets = 386'364; - configured.database.header_size = 21'000'000; - configured.database.header_rate = 5; - - configured.database.input_size = 92'500'000'000; - configured.database.input_rate = 5; - - configured.database.output_size = 25'300'000'000; - configured.database.output_rate = 5; - - // point table set to 2.2LF @ ~900k. - configured.database.point_buckets = 1'365'977'136; - configured.database.point_size = 25'700'000'000; - configured.database.point_rate = 5; - - configured.database.ins_size = 8'550'000'000; - configured.database.ins_rate = 5; - - configured.database.outs_size = 3'700'000'000; - configured.database.outs_rate = 5; - - configured.database.tx_buckets = 469'222'525; - configured.database.tx_size = 17'000'000'000; - configured.database.tx_rate = 5; - - configured.database.txs_buckets = 900'001; - configured.database.txs_size = 1'050'000'000; - configured.database.txs_rate = 5; - - // database (indexes) - - configured.database.candidate_size = 2'575'500; - configured.database.candidate_rate = 5; - - configured.database.confirmed_size = 2'575'500; - configured.database.confirmed_rate = 5; - - configured.database.strong_tx_buckets = 469'222'525; - configured.database.strong_tx_size = 2'900'000'000; - configured.database.strong_tx_rate = 5; - - // database (caches) - - configured.database.duplicate_buckets = 1024; - configured.database.duplicate_size = 44; - configured.database.duplicate_rate = 5; - - configured.database.prevout_buckets = 0; - configured.database.prevout_size = 1; - configured.database.prevout_rate = 5; - - configured.database.validated_bk_buckets = 900'001; - configured.database.validated_bk_size = 1'700'000; - configured.database.validated_bk_rate = 5; - - configured.database.validated_tx_buckets = 1; - configured.database.validated_tx_size = 1; - configured.database.validated_tx_rate = 5; - - // database (optionals) - - configured.database.address_buckets = 1; - configured.database.address_size = 1; - configured.database.address_rate = 5; - - // also disabled by filter_tx - configured.database.filter_bk_buckets = 0; - configured.database.filter_bk_size = 1; - configured.database.filter_bk_rate = 5; - - // also disabled by filter_bk - configured.database.filter_tx_buckets = 0; - configured.database.filter_tx_size = 1; - configured.database.filter_tx_rate = 5; -} - -options_metadata parser::load_options() THROWS -{ - options_metadata description("options"); - description.add_options() - ( - BN_CONFIG_VARIABLE ",c", - value(&configured.file), - "Specify path to a configuration settings file." - ) - // Information. - ( - BN_HELP_VARIABLE ",h", - value(&configured.help)-> - default_value(false)->zero_tokens(), - "Display command line options." - ) - ( - BN_HARDWARE_VARIABLE ",d", - value(&configured.hardware)-> - default_value(false)->zero_tokens(), - "Display hardware compatibility." - ) - ( - BN_SETTINGS_VARIABLE ",s", - value(&configured.settings)-> - default_value(false)->zero_tokens(), - "Display all configuration settings." - ) - ( - BN_VERSION_VARIABLE ",v", - value(&configured.version)-> - default_value(false)->zero_tokens(), - "Display version information." - ) - // Actions. - ( - BN_NEWSTORE_VARIABLE ",n", - value(&configured.newstore)-> - default_value(false)->zero_tokens(), - "Create new store in configured directory." - ) - ( - BN_BACKUP_VARIABLE ",b", - value(&configured.backup)-> - default_value(false)->zero_tokens(), - "Backup to a snapshot (can also do live)." - ) - ( - BN_RESTORE_VARIABLE ",r", - value(&configured.restore)-> - default_value(false)->zero_tokens(), - "Restore from most recent snapshot." - ) - // Chain scans. - ( - BN_FLAGS_VARIABLE ",f", - value(&configured.flags)-> - default_value(false)->zero_tokens(), - "Scan and display all flag transitions." - ) - ( - BN_SLABS_VARIABLE ",a", - value(&configured.slabs)-> - default_value(false)->zero_tokens(), - "Scan and display store slab measures." - ) - ( - BN_BUCKETS_VARIABLE ",k", - value(&configured.buckets)-> - default_value(false)->zero_tokens(), - "Scan and display all bucket densities." - ) - ( - BN_COLLISIONS_VARIABLE ",l", - value(&configured.collisions)-> - default_value(false)->zero_tokens(), - "Scan and display hashmap collision stats (may exceed RAM and result in SIGKILL)." - ) - ( - BN_INFORMATION_VARIABLE ",i", - value(&configured.information)-> - default_value(false)->zero_tokens(), - "Scan and display store information." - ) - // Ad-hoc Testing. - ( - BN_READ_VARIABLE ",t", - value(&configured.test)-> - default_value(system::null_hash), - "Run built-in read test and display." - ) - ( - BN_WRITE_VARIABLE ",w", - value(&configured.write)-> - default_value(system::null_hash), - "Run built-in write test and display." - ); - - return description; -} - -arguments_metadata parser::load_arguments() THROWS -{ - arguments_metadata description; - return description - .add(BN_CONFIG_VARIABLE, 1); -} - -options_metadata parser::load_environment() THROWS -{ - options_metadata description("environment"); - description.add_options() - ( - // For some reason po requires this to be a lower case name. - // The case must match the other declarations for it to compose. - // This composes with the cmdline options and inits to default path. - BN_CONFIG_VARIABLE, - value(&configured.file)->composing() - /*->default_value(config_default_path())*/, - "The path to the configuration settings file." - ); - - return description; -} - -options_metadata parser::load_settings() THROWS -{ - options_metadata description("settings"); - description.add_options() - - /* [forks] */ - ( - "forks.difficult", - value(&configured.bitcoin.forks.difficult), - "Require difficult blocks, defaults to 'true' (use false for testnet)." - ) - ( - "forks.retarget", - value(&configured.bitcoin.forks.retarget), - "Retarget difficulty, defaults to 'true'." - ) - ( - "forks.bip16", - value(&configured.bitcoin.forks.bip16), - "Add pay-to-script-hash processing, defaults to 'true' (soft fork)." - ) - ( - "forks.bip30", - value(&configured.bitcoin.forks.bip30), - "Disallow collision of unspent transaction hashes, defaults to 'true' (soft fork)." - ) - ( - "forks.bip34", - value(&configured.bitcoin.forks.bip34), - "Require coinbase input includes block height, defaults to 'true' (soft fork)." - ) - ( - "forks.bip42", - value(&configured.bitcoin.forks.bip42), - "Finite monetary supply, defaults to 'true' (soft fork)." - ) - ( - "forks.bip66", - value(&configured.bitcoin.forks.bip66), - "Require strict signature encoding, defaults to 'true' (soft fork)." - ) - ( - "forks.bip65", - value(&configured.bitcoin.forks.bip65), - "Add check-locktime-verify op code, defaults to 'true' (soft fork)." - ) - ( - "forks.bip90", - value(&configured.bitcoin.forks.bip90), - "Assume bip34, bip65, and bip66 activation if enabled, defaults to 'true' (hard fork)." - ) - ( - "forks.bip68", - value(&configured.bitcoin.forks.bip68), - "Add relative locktime enforcement, defaults to 'true' (soft fork)." - ) - ( - "forks.bip112", - value(&configured.bitcoin.forks.bip112), - "Add check-sequence-verify op code, defaults to 'true' (soft fork)." - ) - ( - "forks.bip113", - value(&configured.bitcoin.forks.bip113), - "Use median time past for locktime, defaults to 'true' (soft fork)." - ) - ( - "forks.bip141", - value(&configured.bitcoin.forks.bip141), - "Segregated witness consensus layer, defaults to 'true' (soft fork)." - ) - ( - "forks.bip143", - value(&configured.bitcoin.forks.bip143), - "Witness version 0 (segwit), defaults to 'true' (soft fork)." - ) - ( - "forks.bip147", - value(&configured.bitcoin.forks.bip147), - "Prevent dummy value malleability, defaults to 'true' (soft fork)." - ) - ( - "forks.bip341", - value(&configured.bitcoin.forks.bip341), - "Witness version 1 (taproot), defaults to 'true' (soft fork)." - ) - ( - "forks.bip342", - value(&configured.bitcoin.forks.bip342), - "Validation of taproot script, defaults to 'true' (soft fork)." - ) - ( - "forks.time_warp_patch", - value(&configured.bitcoin.forks.time_warp_patch), - "Fix time warp bug, defaults to 'false' (hard fork)." - ) - ( - "forks.retarget_overflow_patch", - value(&configured.bitcoin.forks.retarget_overflow_patch), - "Fix target overflow for very low difficulty, defaults to 'false' (hard fork)." - ) - ( - "forks.scrypt_proof_of_work", - value(&configured.bitcoin.forks.scrypt_proof_of_work), - "Use scrypt hashing for proof of work, defaults to 'false' (hard fork)." - ) - - /* [bitcoin] */ - ( - "bitcoin.initial_block_subsidy_bitcoin", - value(&configured.bitcoin.initial_subsidy_bitcoin), - "The initial block subsidy, defaults to '50'." - ) - ( - "bitcoin.subsidy_interval", - value(&configured.bitcoin.subsidy_interval_blocks), - "The subsidy halving period, defaults to '210000'." - ) - ( - "bitcoin.timestamp_limit_seconds", - value(&configured.bitcoin.timestamp_limit_seconds), - "The future timestamp allowance, defaults to '7200'." - ) - ( - "bitcoin.retargeting_factor", - value(&configured.bitcoin.retargeting_factor), - "The difficulty retargeting factor, defaults to '4'." - ) - ( - "bitcoin.retargeting_interval_seconds", - value(&configured.bitcoin.retargeting_interval_seconds), - "The difficulty retargeting period, defaults to '1209600'." - ) - ( - "bitcoin.block_spacing_seconds", - value(&configured.bitcoin.block_spacing_seconds), - "The target block period, defaults to '600'." - ) - ( - "bitcoin.proof_of_work_limit", - value(&configured.bitcoin.proof_of_work_limit), - "The proof of work limit, defaults to '486604799'." - ) - ( - "bitcoin.genesis_block", - value(&configured.bitcoin.genesis_block), - "The hexideciaml encoding of the genesis block, defaults to mainnet." - ) - ( - "bitcoin.checkpoint", - value(&configured.bitcoin.checkpoints), - "The blockchain checkpoints, defaults to the consensus set." - ) - // [version properties excluded here] - ( - "bitcoin.bip34_activation_threshold", - value(&configured.bitcoin.bip34_activation_threshold), - "The number of new version blocks required for bip34 style soft fork activation, defaults to '750'." - ) - ( - "bitcoin.bip34_enforcement_threshold", - value(&configured.bitcoin.bip34_enforcement_threshold), - "The number of new version blocks required for bip34 style soft fork enforcement, defaults to '950'." - ) - ( - "bitcoin.bip34_activation_sample", - value(&configured.bitcoin.bip34_activation_sample), - "The number of blocks considered for bip34 style soft fork activation, defaults to '1000'." - ) - ( - "bitcoin.bip65_freeze", - value(&configured.bitcoin.bip90_bip65_height), - "The block height to freeze the bip65 softfork for bip90, defaults to '388381'." - ) - ( - "bitcoin.bip66_freeze", - value(&configured.bitcoin.bip90_bip66_height), - "The block height to freeze the bip66 softfork for bip90, defaults to '363725'." - ) - ( - "bitcoin.bip34_freeze", - value(&configured.bitcoin.bip90_bip34_height), - "The block height to freeze the bip34 softfork for bip90, defaults to '227931'." - ) - ( - "bitcoin.bip16_activation_time", - value(&configured.bitcoin.bip16_activation_time), - "The activation time for bip16 in unix time, defaults to '1333238400'." - ) - ( - "bitcoin.bip9_bit0_active_checkpoint", - value(&configured.bitcoin.bip9_bit0_active_checkpoint), - "The hash:height checkpoint for bip9 bit0 activation, defaults to '000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5:419328'." - ) - ( - "bitcoin.milestone", - value(&configured.bitcoin.milestone), - "A block presumed to be valid but not required to be present, defaults to '000000000000000000010538edbfd2d5b809a33dd83f284aeea41c6d0d96968a:900000'." - ) - ( - "bitcoin.minimum_work", - value(&configured.bitcoin.minimum_work), - "The minimum work for any branch to be considered valid, defaults to '000000000000000000000000000000000000000052b2559353df4117b7348b64'." - ) - - /* [network] */ - ( - "network.threads", - value(&configured.network.threads), - "The minimum number of threads in the network threadpool, defaults to '16'." - ) - ( - "network.address_upper", - value(&configured.network.address_upper), - "The upper bound for address selection divisor, defaults to '10'." - ) - ( - "network.address_lower", - value(&configured.network.address_lower), - "The lower bound for address selection divisor, defaults to '5'." - ) - ( - "network.protocol_maximum", - value(&configured.network.protocol_maximum), - "The maximum network protocol version, defaults to '70012'." - ) - ( - "network.protocol_minimum", - value(&configured.network.protocol_minimum), - "The minimum network protocol version, defaults to '31800'." - ) - ( - "network.services_maximum", - value(&configured.network.services_maximum), - "The maximum services exposed by network connections, defaults to '9' (full node, witness)." - ) - ( - "network.services_minimum", - value(&configured.network.services_minimum), - "The minimum services exposed by network connections, defaults to '9' (full node, witness)." - ) - ( - "network.invalid_services", - value(&configured.network.invalid_services), - "The advertised services that cause a peer to be dropped, defaults to '176'." - ) - ( - "network.enable_address", - value(&configured.network.enable_address), - "Enable address messages, defaults to 'true'." - ) - ( - "network.enable_address_v2", - value(&configured.network.enable_address_v2), - "Enable address v2 messages, defaults to 'false'." - ) - ( - "network.enable_witness_tx", - value(&configured.network.enable_witness_tx), - "Enable witness transaction identifier relay, defaults to 'false'." - ) - ( - "network.enable_compact", - value(&configured.network.enable_compact), - "Enable enable compact block messages, defaults to 'false'." - ) - ( - "network.enable_alert", - value(&configured.network.enable_alert), - "Enable alert messages, defaults to 'false'." - ) - ( - "network.enable_reject", - value(&configured.network.enable_reject), - "Enable reject messages, defaults to 'false'." - ) - ( - "network.enable_relay", - value(&configured.network.enable_relay), - "Enable transaction relay, defaults to 'true'." - ) - ( - "network.validate_checksum", - value(&configured.network.validate_checksum), - "Validate the checksum of network messages, defaults to 'false'." - ) - ( - "network.identifier", - value(&configured.network.identifier), - "The magic number for message headers, defaults to '3652501241'." - ) - ( - "network.retry_timeout_seconds", - value(&configured.network.retry_timeout_seconds), - "The time delay for failed connection retry, defaults to '1'." - ) - ( - "network.connect_timeout_seconds", - value(&configured.network.connect_timeout_seconds), - "The time limit for connection establishment, defaults to '5'." - ) - ( - "network.handshake_timeout_seconds", - value(&configured.network.handshake_timeout_seconds), - "The time limit to complete the connection handshake, defaults to '15'." - ) - ( - "network.channel_heartbeat_minutes", - value(&configured.network.channel_heartbeat_minutes), - "The time between ping messages, defaults to '5'." - ) - ( - "network.maximum_skew_minutes", - value(&configured.network.maximum_skew_minutes), - "The maximum allowable channel clock skew, defaults to '120'." - ) - ( - "network.rate_limit", - value(&configured.network.rate_limit), - "The peer download rate limit in bytes per second, defaults to 1024 (not implemented)." - ) - ( - "network.user_agent", - value(&configured.network.user_agent), - "The node user agent string, defaults to '" BC_USER_AGENT "'." - ) - ( - "network.path", - value(&configured.network.path), - "The peer address cache file directory, defaults to empty." - ) - ( - "network.blacklist", - value(&configured.network.blacklists), - "IP address to disallow as a peer, multiple allowed." - ) - ( - "network.whitelist", - value(&configured.network.whitelists), - "IP address to allow as a peer, multiple allowed." - ) - - /* [outbound] */ - ////( - //// "outbound.secure", - //// value(&configured.network.outbound.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ////( - //// "outbound.bind", - //// value(&configured.network.outbound.binds), - //// "IP address to bind for load balancing, multiple allowed (not implemented)." - ////) - ( - "outbound.connections", - value(&configured.network.outbound.connections), - "The target number of outgoing network connections, defaults to '100'." - ) - ( - "outbound.inactivity_minutes", - value(&configured.network.outbound.inactivity_minutes), - "The inactivity time limit for any connection, defaults to '10'." - ) - ( - "outbound.expiration_minutes", - value(&configured.network.outbound.expiration_minutes), - "The age limit for any connection, defaults to '60'." - ) - ( - "outbound.minimum_buffer", - value(&configured.network.outbound.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "outbound.maximum_request", - value(&configured.network.outbound.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - ( - "outbound.use_ipv6", - value(&configured.network.outbound.use_ipv6), - "Use internet protocol version 6 (IPv6) addresses, defaults to 'false'." - ) - ( - "outbound.seed", - value(&configured.network.outbound.seeds), - "A seed node for initializing the host pool, multiple allowed." - ) - ( - "outbound.connect_batch_size", - value(&configured.network.outbound.connect_batch_size), - "The number of concurrent attempts to establish one connection, defaults to '5'." - ) - ( - "outbound.host_pool_capacity", - value(&configured.network.outbound.host_pool_capacity), - "The maximum number of peer hosts in the pool, defaults to '10000'." - ) - ( - "outbound.seeding_timeout_seconds", - value(&configured.network.outbound.seeding_timeout_seconds), - "The time limit for obtaining seed connections and addresses, defaults to '30'." - ) - - /* [inbound] */ - ////( - //// "inbound.secure", - //// value(&configured.network.inbound.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ( - "inbound.bind", - value(&configured.network.inbound.binds), - "IP address to bind for listening, multiple allowed, defaults to '0.0.0.0:8333' (all IPv4)." - ) - ( - "inbound.connections", - value(&configured.network.inbound.connections), - "The target number of incoming network connections, defaults to '100'." - ) - ( - "inbound.inactivity_minutes", - value(&configured.network.inbound.inactivity_minutes), - "The inactivity time limit for any connection, defaults to '10'." - ) - ( - "inbound.expiration_minutes", - value(&configured.network.inbound.expiration_minutes), - "The age limit for any connection, defaults to '60'." - ) - ( - "inbound.minimum_buffer", - value(&configured.network.inbound.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "inbound.maximum_request", - value(&configured.network.inbound.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - ( - "inbound.enable_loopback", - value(&configured.network.inbound.enable_loopback), - "Allow connections from the node to itself, defaults to 'false'." - ) - ( - "inbound.self", - value(&configured.network.inbound.selfs), - "IP address to advertise, multiple allowed." - ) - - /* [manual] */ - ////( - //// "manual.secure", - //// value(&configured.network.manual.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ////( - //// "manual.bind", - //// value(&configured.network.manual.binds), - //// "IP address to bind for load balancing, multiple allowed (not implemented)." - ////) - ////( - //// "manual.connections", - //// value(&configured.network.manual.connections), - //// "The target number of outgoing manual connections (not implemented)." - ////) - ( - "manual.inactivity_minutes", - value(&configured.network.manual.inactivity_minutes), - "The inactivity time limit for any connection, defaults to '10' (will attempt reconnect)." - ) - ( - "manual.expiration_minutes", - value(&configured.network.manual.expiration_minutes), - "The age limit for any connection, defaults to '60' (will attempt reconnect)." - ) - ( - "manual.minimum_buffer", - value(&configured.network.manual.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "manual.maximum_request", - value(&configured.network.manual.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - ( - "manual.peer", - value(&configured.network.manual.peers), - "A persistent peer node, multiple allowed." - ) - - /* [web] */ - ////( - //// "web.secure", - //// value(&configured.network.web.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ( - "web.bind", - value(&configured.server.web.binds), - "IP address to bind, multiple allowed, defaults to '0.0.0.0:8080' (all IPv4)." - ) - ( - "web.connections", - value(&configured.server.web.connections), - "The required maximum number of connections, defaults to '0'." - ) - ( - "web.inactivity_minutes", - value(&configured.server.web.inactivity_minutes), - "The idle timeout (http keep-alive), defaults to '10'." - ) - ( - "web.expiration_minutes", - value(&configured.server.web.expiration_minutes), - "The idle timeout (http keep-alive), defaults to '60'." - ) - ( - "web.minimum_buffer", - value(&configured.server.web.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "web.maximum_request", - value(&configured.server.web.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - ( - "web.server", - value(&configured.server.web.server), - "The server name (http header), defaults to '" BC_HTTP_SERVER_NAME "'." - ) - ( - "web.host", - value(&configured.server.web.hosts), - "The host name (http verification), multiple allowed, defaults to empty (disabled)." - ) - ( - "web.origin", - value(&configured.server.web.origins), - "The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)." - ) - ( - "web.allow_opaque_origin", - value(&configured.server.web.allow_opaque_origin), - "Allow requests from opaue origin (see CORS), multiple allowed, defaults to false." - ) - ( - "web.path", - value(&configured.server.web.path), - "The required root path of source files to be served, defaults to empty." - ) - ( - "web.default", - value(&configured.server.web.default_), - "The path of the default source page, defaults to 'index.html'." - ) - - /* [explore] */ - ////( - //// "explore.secure", - //// value(&configured.network.explore.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ( - "explore.bind", - value(&configured.server.explore.binds), - "IP address to bind, multiple allowed, defaults to '0.0.0.0:8180' (all IPv4)." - ) - ( - "explore.connections", - value(&configured.server.explore.connections), - "The required maximum number of connections, defaults to '0'." - ) - ( - "explore.inactivity_minutes", - value(&configured.server.explore.inactivity_minutes), - "The idle timeout (http keep-server), defaults to '60'." - ) - ( - "explore.expiration_minutes", - value(&configured.server.explore.expiration_minutes), - "The idle timeout (http keep-alive), defaults to '60'." - ) - ( - "explore.minimum_buffer", - value(&configured.server.explore.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "explore.maximum_request", - value(&configured.server.explore.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - ( - "explore.server", - value(&configured.server.explore.server), - "The server name (http header), defaults to '" BC_HTTP_SERVER_NAME "'." - ) - ( - "explore.host", - value(&configured.server.explore.hosts), - "The host name (http verification), multiple allowed, defaults to empty (disabled)." - ) - ( - "explore.origin", - value(&configured.server.explore.origins), - "The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)." - ) - ( - "explore.allow_opaque_origin", - value(&configured.server.explore.allow_opaque_origin), - "Allow requests from opaue origin (see CORS), multiple allowed, defaults to false." - ) - ( - "explore.path", - value(&configured.server.explore.path), - "The required root path of source files to be served, defaults to empty." - ) - ( - "explore.default", - value(&configured.server.explore.default_), - "The path of the default source page, defaults to 'index.html'." - ) - ( - "explore.websocket", - value(&configured.server.explore.websocket), - "Enable websocket interface, defaults to true." - ) - - /* [bitcoind] */ - ////( - //// "bitcoind.secure", - //// value(&configured.network.bitcoind.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ( - "bitcoind.bind", - value(&configured.server.bitcoind.binds), - "IP address to bind, multiple allowed, defaults to '0.0.0.0:8380' (all IPv4)." - ) - ( - "bitcoind.connections", - value(&configured.server.bitcoind.connections), - "The required maximum number of connections, defaults to '0'." - ) - ( - "bitcoind.inactivity_minutes", - value(&configured.server.bitcoind.inactivity_minutes), - "The idle timeout (http keep-alive), defaults to '10'." - ) - ( - "bitcoind.expiration_minutes", - value(&configured.server.bitcoind.expiration_minutes), - "The idle timeout (http keep-alive), defaults to '60'." - ) - ( - "bitcoind.minimum_buffer", - value(&configured.server.bitcoind.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "bitcoind.maximum_request", - value(&configured.server.bitcoind.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - ( - "bitcoind.server", - value(&configured.server.bitcoind.server), - "The server name (http header), defaults to '" BC_HTTP_SERVER_NAME "'." - ) - ( - "bitcoind.host", - value(&configured.server.bitcoind.hosts), - "The host name (http verification), multiple allowed, defaults to empty (disabled)." - ) - ( - "bitcoind.origin", - value(&configured.server.bitcoind.origins), - "The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)." - ) - ( - "bitcoind.allow_opaque_origin", - value(&configured.server.bitcoind.allow_opaque_origin), - "Allow requests from opaue origin (see CORS), multiple allowed, defaults to false." - ) - - /* [electrum] */ - ////( - //// "electrum.secure", - //// value(&configured.network.electrum.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ( - "electrum.bind", - value(&configured.server.electrum.binds), - "IP address to bind, multiple allowed, defaults to '0.0.0.0:8480' (all IPv4)." - ) - ( - "electrum.connections", - value(&configured.server.electrum.connections), - "The required maximum number of connections, defaults to '0'." - ) - ( - "electrum.inactivity_minutes", - value(&configured.server.electrum.inactivity_minutes), - "The idle timeout (http keep-alive), defaults to '10'." - ) - ( - "electrum.expiration_minutes", - value(&configured.server.electrum.expiration_minutes), - "The idle timeout (http keep-alive), defaults to '60'." - ) - ( - "electrum.minimum_buffer", - value(&configured.server.electrum.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "electrum.maximum_request", - value(&configured.server.electrum.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - - /* [stratum_v1] */ - ////( - //// "stratum_v1.secure", - //// value(&configured.network.stratum_v1.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ( - "stratum_v1.bind", - value(&configured.server.stratum_v1.binds), - "IP address to bind, multiple allowed, defaults to '0.0.0.0:8580' (all IPv4)." - ) - ( - "stratum_v1.connections", - value(&configured.server.stratum_v1.connections), - "The required maximum number of connections, defaults to '0'." - ) - ( - "stratum_v1.inactivity_minutes", - value(&configured.server.stratum_v1.inactivity_minutes), - "The idle timeout (http keep-alive), defaults to '10'." - ) - ( - "stratum_v1.expiration_minutes", - value(&configured.server.stratum_v1.expiration_minutes), - "The idle timeout (http keep-alive), defaults to '60'." - ) - ( - "stratum_v1.minimum_buffer", - value(&configured.server.stratum_v1.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "stratum_v1.maximum_request", - value(&configured.server.stratum_v1.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - - /* [stratum_v2] */ - ////( - //// "stratum_v2.secure", - //// value(&configured.network.stratum_v2.secure), - //// "Require transport layer security, defaults to 'false' (not implemented)." - ////) - ( - "stratum_v2.bind", - value(&configured.server.stratum_v2.binds), - "IP address to bind, multiple allowed, defaults to '0.0.0.0:8680' (all IPv4)." - ) - ( - "stratum_v2.connections", - value(&configured.server.stratum_v2.connections), - "The required maximum number of connections, defaults to '0'." - ) - ( - "stratum_v2.inactivity_minutes", - value(&configured.server.stratum_v2.inactivity_minutes), - "The idle timeout (http keep-alive), defaults to '10'." - ) - ( - "stratum_v2.expiration_minutes", - value(&configured.server.stratum_v2.expiration_minutes), - "The idle timeout (http keep-alive), defaults to '60'." - ) - ( - "stratum_v2.minimum_buffer", - value(&configured.server.stratum_v2.minimum_buffer), - "The minimum retained read buffer size, defaults to '4000000'." - ) - ( - "stratum_v2.maximum_request", - value(&configured.server.stratum_v2.maximum_request), - "The maximum allowed request size, defaults to '4000000'." - ) - - /* [node] */ - ( - "node.threads", - value(&configured.node.threads), - "The number of threads in the validation threadpool, defaults to '32'." - ) - ( - "node.thread_priority", - value(&configured.node.thread_priority), - "Set validation threads to high processing priority, defaults to 'true'." - ) - ( - "node.memory_priority", - value(&configured.node.memory_priority), - "Set the process to high memory priority, defaults to 'true'." - ) - ( - "node.delay_inbound", - value(&configured.node.delay_inbound), - "Delay accepting inbound connections until node is current, defaults to 'true'." - ) - ( - "node.defer_validation", - value(&configured.node.defer_validation), - "Defer validation, defaults to 'false'." - ) - ( - "node.defer_confirmation", - value(&configured.node.defer_confirmation), - "Defer confirmation, defaults to 'false'." - ) - ////( - //// "node.headers_first", - //// value(&configured.node.headers_first), - //// "Obtain current header chain before obtaining associated blocks, defaults to 'true'." - ////) - ( - "node.allowed_deviation", - value(&configured.node.allowed_deviation), - "Allowable underperformance standard deviation, defaults to '1.5' (0 disables)." - ) - ( - "node.announcement_cache", - value(&configured.node.announcement_cache), - "Limit of per channel cached peer block and tx announcements, to avoid replaying, defaults to '42'." - ) - ( - "node.allocation_multiple", - value(&configured.node.allocation_multiple), - "Block deserialization buffer multiple of wire size, defaults to '20' (0 disables)." - ) - ( - "node.maximum_height", - value(&configured.node.maximum_height), - "Maximum block height to populate, defaults to 0 (unlimited)." - ) - ( - "node.maximum_concurrency", - value(&configured.node.maximum_concurrency), - "Maximum number of blocks to download concurrently, defaults to '50000' (0 disables)." - ) - ////( - //// "node.snapshot_bytes", - //// value(&configured.node.snapshot_bytes), - //// "Downloaded bytes that triggers snapshot, defaults to '0' (0 disables)." - ////) - ////( - //// "node.snapshot_valid", - //// value(&configured.node.snapshot_valid), - //// "Completed validations that trigger snapshot, defaults to '0' (0 disables)." - ////) - ////( - //// "node.snapshot_confirm", - //// value(&configured.node.snapshot_confirm), - //// "Completed confirmations that trigger snapshot, defaults to '0' (0 disables)." - ////) - ( - "node.sample_period_seconds", - value(&configured.node.sample_period_seconds), - "Sampling period for drop of stalled channels, defaults to '10' (0 disables)." - ) - ( - "node.currency_window_minutes", - value(&configured.node.currency_window_minutes), - "Time from present that blocks are considered current, defaults to '60' (0 disables)." - ) - // ####################### - ////( - //// "node.notify_limit_hours", - //// value(&configured.node.notify_limit_hours), - //// "Disable relay when top block age exceeds, defaults to '24' (0 disables)." - ////) - ////( - //// "node.byte_fee_satoshis", - //// value(&configured.node.byte_fee_satoshis), - //// "The minimum fee per byte, cumulative for conflicts, defaults to '1'." - ////) - ////( - //// "node.sigop_fee_satoshis", - //// value(&configured.node.sigop_fee_satoshis), - //// "The minimum fee per sigop, additional to byte fee, defaults to' 100'." - ////) - ////( - //// "node.minimum_output_satoshis", - //// value(&configured.node.minimum_output_satoshis), - //// "The minimum output value, defaults to '500'." - ////) - - /* [database] */ - ( - "database.path", - value(&configured.database.path), - "The blockchain database directory, defaults to 'blockchain'." - ) - ( - "database.turbo", - value(&configured.database.turbo), - "Allow indiviudal non-validation queries to use all CPUs, defaults to false." - ) - - /* header */ - ( - "database.header_buckets", - value(&configured.database.header_buckets), - "The number of buckets in the header table head, defaults to '386364'." - ) - ( - "database.header_size", - value(&configured.database.header_size), - "The minimum allocation of the header table body, defaults to '21000000'." - ) - ( - "database.header_rate", - value(&configured.database.header_rate), - "The percentage expansion of the header table body, defaults to '5'." - ) - - /* input */ - ( - "database.input_size", - value(&configured.database.input_size), - "The minimum allocation of the input table body, defaults to '92500000000'." - ) - ( - "database.input_rate", - value(&configured.database.input_rate), - "The percentage expansion of the input table body, defaults to '5'." - ) - - /* output */ - ( - "database.output_size", - value(&configured.database.output_size), - "The minimum allocation of the output table body, defaults to '25300000000'." - ) - ( - "database.output_rate", - value(&configured.database.output_rate), - "The percentage expansion of the output table body, defaults to '5'." - ) - - /* point */ - ( - "database.point_buckets", - value(&configured.database.point_buckets), - "The number of buckets in the spend table head, defaults to '1365977136'." - ) - ( - "database.point_size", - value(&configured.database.point_size), - "The minimum allocation of the point table body, defaults to '25700000000'." - ) - ( - "database.point_rate", - value(&configured.database.point_rate), - "The percentage expansion of the point table body, defaults to '5'." - ) - - /* ins */ - ( - "database.ins_size", - value(&configured.database.ins_size), - "The minimum allocation of the point table body, defaults to '8550000000'." - ) - ( - "database.ins_rate", - value(&configured.database.ins_rate), - "The percentage expansion of the ins table body, defaults to '5'." - ) - - /* outs */ - ( - "database.outs_size", - value(&configured.database.outs_size), - "The minimum allocation of the puts table body, defaults to '3700000000'." - ) - ( - "database.outs_rate", - value(&configured.database.outs_rate), - "The percentage expansion of the puts table body, defaults to '5'." - ) - - /* tx */ - ( - "database.tx_buckets", - value(&configured.database.tx_buckets), - "The number of buckets in the tx table head, defaults to '469222525'." - ) - ( - "database.tx_size", - value(&configured.database.tx_size), - "The minimum allocation of the tx table body, defaults to '17000000000'." - ) - ( - "database.tx_rate", - value(&configured.database.tx_rate), - "The percentage expansion of the tx table body, defaults to '5'." - ) - - /* txs */ - ( - "database.txs_buckets", - value(&configured.database.txs_buckets), - "The number of buckets in the txs table head, defaults to '900001'." - ) - ( - "database.txs_size", - value(&configured.database.txs_size), - "The minimum allocation of the txs table body, defaults to '1050000000'." - ) - ( - "database.txs_rate", - value(&configured.database.txs_rate), - "The percentage expansion of the txs table body, defaults to '5'." - ) - - /* candidate */ - ( - "database.candidate_size", - value(&configured.database.candidate_size), - "The minimum allocation of the candidate table body, defaults to '2575500'." - ) - ( - "database.candidate_rate", - value(&configured.database.candidate_rate), - "The percentage expansion of the candidate table body, defaults to '5'." - ) - - /* confirmed */ - ( - "database.confirmed_size", - value(&configured.database.confirmed_size), - "The minimum allocation of the candidate table body, defaults to '2575500'." - ) - ( - "database.confirmed_rate", - value(&configured.database.confirmed_rate), - "The percentage expansion of the candidate table body, defaults to '5'." - ) - - /* strong_tx */ - ( - "database.strong_tx_buckets", - value(&configured.database.strong_tx_buckets), - "The number of buckets in the strong_tx table head, defaults to '469222525'." - ) - ( - "database.strong_tx_size", - value(&configured.database.strong_tx_size), - "The minimum allocation of the strong_tx table body, defaults to '2900000000'." - ) - ( - "database.strong_tx_rate", - value(&configured.database.strong_tx_rate), - "The percentage expansion of the strong_tx table body, defaults to '5'." - ) - - /* duplicate */ - ( - "database.duplicate_buckets", - value(&configured.database.duplicate_buckets), - "The minimum number of buckets in the duplicate table head, defaults to '1024'." - ) - ( - "database.duplicate_size", - value(&configured.database.duplicate_size), - "The minimum allocation of the duplicate table body, defaults to '44'." - ) - ( - "database.duplicate_rate", - value(&configured.database.duplicate_rate), - "The percentage expansion of the duplicate table, defaults to '5'." - ) - - /* prevout */ - ( - "database.prevout_buckets", - value(&configured.database.prevout_buckets), - "The minimum number of buckets in the prevout table head, defaults to '0'." - ) - ( - "database.prevout_size", - value(&configured.database.prevout_size), - "The minimum allocation of the prevout table body, defaults to '1'." - ) - ( - "database.prevout_rate", - value(&configured.database.prevout_rate), - "The percentage expansion of the prevout table, defaults to '5'." - ) - - /* validated_bk */ - ( - "database.validated_bk_buckets", - value(&configured.database.validated_bk_buckets), - "The number of buckets in the validated_bk table head, defaults to '900001'." - ) - ( - "database.validated_bk_size", - value(&configured.database.validated_bk_size), - "The minimum allocation of the validated_bk table body, defaults to '1700000'." - ) - ( - "database.validated_bk_rate", - value(&configured.database.validated_bk_rate), - "The percentage expansion of the validated_bk table body, defaults to '5'." - ) - - /* validated_tx */ - ( - "database.validated_tx_buckets", - value(&configured.database.validated_tx_buckets), - "The number of buckets in the validated_tx table head, defaults to '1'." - ) - ( - "database.validated_tx_size", - value(&configured.database.validated_tx_size), - "The minimum allocation of the validated_tx table body, defaults to '1'." - ) - ( - "database.validated_tx_rate", - value(&configured.database.validated_tx_rate), - "The percentage expansion of the validated_tx table body, defaults to '5'." - ) - - /* address */ - ( - "database.address_buckets", - value(&configured.database.address_buckets), - "The number of buckets in the address table head, defaults to '1' (0|1 disables)." - ) - ( - "database.address_size", - value(&configured.database.address_size), - "The minimum allocation of the address table body, defaults to '1'." - ) - ( - "database.address_rate", - value(&configured.database.address_rate), - "The percentage expansion of the address table body, defaults to '5'." - ) - - /* filter_bk */ - ( - "database.filter_bk_buckets", - value(&configured.database.filter_bk_buckets), - "The number of buckets in the filter_bk table head, defaults to '0' (0 disables)." - ) - ( - "database.filter_bk_size", - value(&configured.database.filter_bk_size), - "The minimum allocation of the filter_bk table body, defaults to '1'." - ) - ( - "database.filter_bk_rate", - value(&configured.database.filter_bk_rate), - "The percentage expansion of the filter_bk table body, defaults to '5'." - ) - - /* filter_tx */ - ( - "database.filter_tx_buckets", - value(&configured.database.filter_tx_buckets), - "The number of buckets in the filter_tx table head, defaults to '0' (0 disables)." - ) - ( - "database.filter_tx_size", - value(&configured.database.filter_tx_size), - "The minimum allocation of the filter_tx table body, defaults to '1'." - ) - ( - "database.filter_tx_rate", - value(&configured.database.filter_tx_rate), - "The percentage expansion of the filter_tx table body, defaults to '5'." - ) - - /* [log] */ -#if defined(HAVE_LOGA) - ( - "log.application", - value(&configured.log.application), - "Enable application logging, defaults to 'true'." - ) -#endif -#if defined(HAVE_LOGN) - ( - "log.news", - value(&configured.log.news), - "Enable news logging, defaults to 'true'." - ) -#endif -#if defined(HAVE_LOGS) - ( - "log.session", - value(&configured.log.session), - "Enable session logging, defaults to 'true'." - ) -#endif -#if defined(HAVE_LOGP) - ( - "log.protocol", - value(&configured.log.protocol), - "Enable protocol logging, defaults to 'false'." - ) -#endif -#if defined(HAVE_LOGX) - ( - "log.proxy", - value(&configured.log.proxy), - "Enable proxy logging, defaults to 'false'." - ) -#endif -#if defined(HAVE_LOGR) - ( - "log.remote", - value(&configured.log.remote), - "Enable remote fault logging, defaults to 'true'." - ) -#endif -#if defined(HAVE_LOGF) - ( - "log.fault", - value(&configured.log.fault), - "Enable local fault logging, defaults to 'true'." - ) -#endif -#if defined(HAVE_LOGQ) - ( - "log.quitting", - value(&configured.log.quitting), - "Enable quitting logging, defaults to 'false'." - ) -#endif -#if defined(HAVE_LOGO) - ( - "log.objects", - value(&configured.log.objects), - "Enable objects logging, defaults to 'false'." - ) -#endif -#if defined(HAVE_LOGV) - ( - "log.verbose", - value(&configured.log.verbose), - "Enable verbose logging, defaults to 'false'." - ) -#endif - ( - "log.maximum_size", - value(&configured.log.maximum_size), - "The maximum byte size of each pair of rotated log files, defaults to 1000000." - ) -#if defined (HAVE_MSC) - ( - "log.symbols", - value(&configured.log.symbols), - "Path to windows debug build symbols file (.pdb)." - ) -#endif - ( - "log.path", - value(&configured.log.path), - "The log files directory, defaults to empty." - ); - - return description; -} - -bool parser::parse(int argc, const char* argv[], std::ostream& error) THROWS -{ - try - { - auto file = false; - variables_map variables; - load_command_variables(variables, argc, argv); - load_environment_variables(variables, BN_ENVIRONMENT_VARIABLE_PREFIX); - - // Don't load config file if any of these options are specified. - if (!get_option(variables, BN_VERSION_VARIABLE) && - !get_option(variables, BN_SETTINGS_VARIABLE) && - !get_option(variables, BN_HELP_VARIABLE)) - { - // Returns true if the settings were loaded from a file. - file = load_configuration_variables(variables, BN_CONFIG_VARIABLE); - } - - // Update bound variables in metadata.settings. - notify(variables); - - // Clear the config file path if it wasn't used. - if (!file) - configured.file.clear(); - } - catch (const boost::program_options::error& e) - { - // This is obtained from boost, which circumvents our localization. - error << format_invalid_parameter(e.what()) << std::endl; - return false; - } - - return true; -} - -} // namespace node -} // namespace libbitcoin diff --git a/src/parsers/bitcoind_query.cpp b/src/parsers/bitcoind_query.cpp deleted file mode 100644 index 01c6e046d..000000000 --- a/src/parsers/bitcoind_query.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 - -namespace libbitcoin { -namespace node { - -using namespace system; -using namespace network; -using namespace network::http; - -BC_PUSH_WARNING(NO_ARRAY_INDEXING) -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -bool bitcoind_query(rpc::request_t& , const request& ) NOEXCEPT -{ - return false; -} - -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/parsers/bitcoind_target.cpp b/src/parsers/bitcoind_target.cpp deleted file mode 100644 index a3525c284..000000000 --- a/src/parsers/bitcoind_target.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 { - -using namespace system; -using namespace network::rpc; - -BC_PUSH_WARNING(NO_ARRAY_INDEXING) -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -code bitcoind_target(request_t& , const std::string_view& ) NOEXCEPT -{ - return {}; -} - -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/parsers/explore_query.cpp b/src/parsers/explore_query.cpp deleted file mode 100644 index 4c1d73cff..000000000 --- a/src/parsers/explore_query.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 - -namespace libbitcoin { -namespace node { - -using namespace system; -using namespace network; -using namespace network::http; - -BC_PUSH_WARNING(NO_ARRAY_INDEXING) -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -bool explore_query(rpc::request_t& out, const request& request) NOEXCEPT -{ - wallet::uri uri{}; - if (!uri.decode(request.target())) - return false; - - constexpr auto text = media_type::text_plain; - constexpr auto json = media_type::application_json; - constexpr auto data = media_type::application_octet_stream; - - auto query = uri.decode_query(); - const auto format = query["format"]; - - if (!out.params.has_value() || - !std::holds_alternative(out.params.value())) - return false; - - auto& params = std::get(out.params.value()); - - // Validate proper witness bool value if set. - const auto witness = query.find("witness"); - if (witness != query.end()) - { - if (witness->second != "true" && witness->second != "false") - return false; - - // Witness is optional (where applicable), so only set if false. - if (witness->second == "false") - params["witness"] = false; - } - - // Validate proper turbo bool value if set. - const auto turbo = query.find("turbo"); - if (turbo != query.end()) - { - if (turbo->second != "true" && turbo->second != "false") - return false; - - // Turbo is optional (where applicable), so only set if false. - if (turbo->second == "false") - params["turbo"] = false; - } - - const auto accepts = to_media_types((request)[field::accept]); - - if (contains(accepts, text) || format == "text") - { - params["media"] = to_value(text); - return true; - } - - if (contains(accepts, data) || format == "data") - { - params["media"] = to_value(data); - return true; - } - - ////// Default format to json. - ////params["media"] = to_value(json); - ////return true; - - if (contains(accepts, json) || format == "json") - { - params["media"] = to_value(json); - return true; - } - - return false; -} - -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/parsers/explore_target.cpp b/src/parsers/explore_target.cpp deleted file mode 100644 index 6f643266c..000000000 --- a/src/parsers/explore_target.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 { - -using namespace system; -using namespace network::rpc; - -BC_PUSH_WARNING(NO_ARRAY_INDEXING) -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -template -static bool to_number(Number& out, const std::string_view& token) NOEXCEPT -{ - return !token.empty() && is_ascii_numeric(token) && (is_one(token.size()) || - token.front() != '0') && deserialize(out, token); -} - -static hash_cptr to_hash(const std::string_view& token) NOEXCEPT -{ - hash_digest out{}; - return decode_hash(out, token) ? - emplace_shared(std::move(out)) : hash_cptr{}; -} - -code explore_target(request_t& out, const std::string_view& path) NOEXCEPT -{ - const auto clean = split(path, "?", false, false).front(); - if (clean.empty()) - return error::empty_path; - - // Avoid conflict with node type. - using object_t = network::rpc::object_t; - - // Initialize json-rpc.v2 named params message. - out = request_t - { - .jsonrpc = version::v2, - .id = null_t{}, - .method = {}, - .params = object_t{} - }; - - auto& method = out.method; - auto& params = std::get(out.params.value()); - const auto segments = split(clean, "/", false, true); - BC_ASSERT(!segments.empty()); - - size_t segment{}; - if (!segments[segment].starts_with('v')) - return error::missing_version; - - uint8_t version{}; - if (!to_number(version, segments[segment++].substr(one))) - return error::invalid_number; - - params["version"] = version; - if (segment == segments.size()) - return error::missing_target; - - // transaction, address, inputs, and outputs are identical excluding names; - // input and output are identical excluding names; block is unique. - const auto target = segments[segment++]; - if (target == "top") - { - method = "top"; - } - else if (target == "address") - { - if (segment == segments.size()) - return error::missing_hash; - - // address hash is a single sha256, in reversed display endianness. - const auto base16 = to_hash(segments[segment++]); - if (!base16) return error::invalid_hash; - - params["hash"] = base16; - if (segment == segments.size()) - { - method = "address"; - } - else - { - const auto subcomponent = segments[segment++]; - if (subcomponent == "confirmed") - method = "address_confirmed"; - else if (subcomponent == "unconfirmed") - method = "address_unconfirmed"; - else if (subcomponent == "balance") - method = "address_balance"; - else - return error::invalid_subcomponent; - } - } - else if (target == "input") - { - if (segment == segments.size()) - return error::missing_hash; - - const auto hash = to_hash(segments[segment++]); - if (!hash) return error::invalid_hash; - - params["hash"] = hash; - if (segment == segments.size()) - { - method = "inputs"; - } - else - { - const auto component = segments[segment++]; - uint32_t index{}; - if (!to_number(index, component)) - return error::invalid_number; - - params["index"] = index; - if (segment == segments.size()) - { - method = "input"; - } - else - { - const auto subcomponent = segments[segment++]; - if (subcomponent == "script") - method = "input_script"; - else if (subcomponent == "witness") - method = "input_witness"; - else - return error::invalid_subcomponent; - } - } - } - else if (target == "output") - { - if (segment == segments.size()) - return error::missing_hash; - - const auto hash = to_hash(segments[segment++]); - if (!hash) return error::invalid_hash; - - params["hash"] = hash; - if (segment == segments.size()) - { - method = "outputs"; - } - else - { - const auto component = segments[segment++]; - uint32_t index{}; - if (!to_number(index, component)) - return error::invalid_number; - - params["index"] = index; - if (segment == segments.size()) - { - method = "output"; - } - else - { - const auto subcomponent = segments[segment++]; - if (subcomponent == "script") - method = "output_script"; - else if (subcomponent == "spender") - method = "output_spender"; - else if (subcomponent == "spenders") - method = "output_spenders"; - else - return error::invalid_subcomponent; - } - } - } - else if (target == "tx") - { - if (segment == segments.size()) - return error::missing_hash; - - const auto hash = to_hash(segments[segment++]); - if (!hash) return error::invalid_hash; - - params["hash"] = hash; - if (segment == segments.size()) - { - method = "tx"; - } - else - { - const auto component = segments[segment++]; - if (component == "header") - method = "tx_header"; - else if (component == "details") - method = "tx_details"; - else - return error::invalid_component; - } - } - else if (target == "block") - { - if (segment == segments.size()) - return error::missing_id_type; - - const auto by = segments[segment++]; - if (by == "hash") - { - if (segment == segments.size()) - return error::missing_hash; - - const auto hash = to_hash(segments[segment++]); - if (!hash) return error::invalid_hash; - - params["hash"] = hash; - } - else if (by == "height") - { - if (segment == segments.size()) - return error::missing_height; - - uint32_t height{}; - if (!to_number(height, segments[segment++])) - return error::invalid_number; - - params["height"] = height; - } - else - { - return error::invalid_id_type; - } - - if (segment == segments.size()) - { - method = "block"; - } - else - { - const auto component = segments[segment++]; - if (component == "tx") - { - if (segment == segments.size()) - return error::missing_position; - - uint32_t position{}; - if (!to_number(position, segments[segment++])) - return error::invalid_number; - - params["position"] = position; - method = "block_tx"; - } - else if (component == "header") - { - if (segment == segments.size()) - { - method = "block_header"; - } - else - { - const auto subcomponent = segments[segment++]; - if (subcomponent == "context") - method = "block_header_context"; - else - return error::invalid_subcomponent; - } - } - else if (component == "txs") - method = "block_txs"; - else if (component == "details") - method = "block_details"; - else if (component == "filter") - { - if (segment == segments.size()) - return error::missing_type_id; - - uint8_t type{}; - if (!to_number(type, segments[segment++])) - return error::invalid_number; - - params["type"] = type; - if (segment == segments.size()) - { - method = "block_filter"; - } - else - { - const auto subcomponent = segments[segment++]; - if (subcomponent == "hash") - method = "block_filter_hash"; - else if (subcomponent == "header") - method = "block_filter_header"; - else - return error::invalid_subcomponent; - } - } - else - return error::invalid_component; - } - } - else - { - return error::invalid_target; - } - - return segment == segments.size() ? error::success : error::extra_segment; -} - -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/protocols/protocol.cpp b/src/protocols/protocol.cpp index a08af5274..964d2e1fd 100644 --- a/src/protocols/protocol.cpp +++ b/src/protocols/protocol.cpp @@ -32,9 +32,29 @@ query& protocol::archive() const NOEXCEPT return session_->archive(); } -const configuration& protocol::config() const NOEXCEPT +const node::configuration& protocol::node_config() const NOEXCEPT { - return session_->config(); + return session_->node_config(); +} + +const system::settings& protocol::system_settings() const NOEXCEPT +{ + return session_->system_settings(); +} + +const database::settings& protocol::database_settings() const NOEXCEPT +{ + return session_->database_settings(); +} + +////const network::settings& protocol::network_settings() const NOEXCEPT +////{ +//// return session_->network_settings(); +////} + +const node::settings& protocol::node_settings() const NOEXCEPT +{ + return session_->node_settings(); } bool protocol::is_current(bool confirmed) const NOEXCEPT diff --git a/src/protocols/protocol_bitcoind_rest.cpp b/src/protocols/protocol_bitcoind_rest.cpp deleted file mode 100644 index 4aaafa4ac..000000000 --- a/src/protocols/protocol_bitcoind_rest.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 - -namespace libbitcoin { -namespace node { - -#define CLASS protocol_bitcoind_rest -#define SUBSCRIBE_BITCOIND(method, ...) \ - subscribe(&CLASS::method, __VA_ARGS__) - -using namespace system; -using namespace network::rpc; -using namespace network::http; -using namespace std::placeholders; -using namespace boost::json; - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) -BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) - -// Start. -// ---------------------------------------------------------------------------- - -void protocol_bitcoind_rest::start() NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (started()) - return; - - SUBSCRIBE_BITCOIND(handle_get_block, _1, _2, _3, _4); - protocol_bitcoind_rpc::start(); -} - -void protocol_bitcoind_rest::stopping(const code& ec) NOEXCEPT -{ - BC_ASSERT(stranded()); - rest_dispatcher_.stop(ec); - protocol_bitcoind_rpc::stopping(ec); -} - -// Dispatch. -// ---------------------------------------------------------------------------- - -// Base rpc protocol handles options and post, this derived handles get. -void protocol_bitcoind_rest::handle_receive_get(const code& ec, - const get::cptr& get) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return; - - // Enforce http host header (if any hosts are configured). - if (!is_allowed_host(*get, get->version())) - { - send_bad_host(*get); - return; - } - - // Enforce http origin policy (if any origins are configured). - if (!is_allowed_origin(*get, get->version())) - { - send_forbidden(*get); - return; - } - - // The post is saved off during asynchonous handling and used in send_json - // to formulate response headers, isolating handlers from http semantics. - set_request(get); - - ////if (const auto code = rest_dispatcher_.notify({})) - //// stop(code); - protocol_bitcoind_rpc::handle_receive_get(ec, get); -} - -// Handlers. -// ---------------------------------------------------------------------------- - -////constexpr auto data = to_value(media_type::application_octet_stream); -////constexpr auto json = to_value(media_type::application_json); -////constexpr auto text = to_value(media_type::text_plain); - -bool protocol_bitcoind_rest::handle_get_block(const code& ec, - rest_interface::block, uint8_t , system::hash_cptr ) NOEXCEPT -{ - if (stopped(ec)) - return false; - - ////const auto& query = archive(); - ////if (const auto block = query.get_block(query.to_header(*hash), true)) - ////{ - //// const auto size = block->serialized_size(true); - //// switch (media) - //// { - //// case data: - //// send_chunk(to_bin(*block, size, true)); - //// return true; - //// case text: - //// send_text(to_hex(*block, size, true)); - //// return true; - //// case json: - //// send_json(value_from(block), two * size); - //// return true; - //// } - ////} - - send_not_found(); - return true; -} - -// private -// ---------------------------------------------------------------------------- - -BC_POP_WARNING() -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/protocols/protocol_bitcoind_rpc.cpp b/src/protocols/protocol_bitcoind_rpc.cpp deleted file mode 100644 index 6ae0a04c0..000000000 --- a/src/protocols/protocol_bitcoind_rpc.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 - -namespace libbitcoin { -namespace node { - -#define CLASS protocol_bitcoind_rpc -#define SUBSCRIBE_BITCOIND(method, ...) \ - subscribe(&CLASS::method, __VA_ARGS__) - -using namespace system; -using namespace network; -using namespace network::json; -using namespace std::placeholders; - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) -BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) - -// Start. -// ---------------------------------------------------------------------------- - -void protocol_bitcoind_rpc::start() NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (started()) - return; - - SUBSCRIBE_BITCOIND(handle_get_best_block_hash, _1, _2); - SUBSCRIBE_BITCOIND(handle_get_block, _1, _2, _3, _4); - SUBSCRIBE_BITCOIND(handle_get_block_chain_info, _1, _2); - SUBSCRIBE_BITCOIND(handle_get_block_count, _1, _2); - SUBSCRIBE_BITCOIND(handle_get_block_filter, _1, _2, _3, _4); - SUBSCRIBE_BITCOIND(handle_get_block_hash, _1, _2, _3); - SUBSCRIBE_BITCOIND(handle_get_block_header, _1, _2, _3, _4); - SUBSCRIBE_BITCOIND(handle_get_block_stats, _1, _2, _3, _4); - SUBSCRIBE_BITCOIND(handle_get_chain_tx_stats, _1, _2, _3, _4); - SUBSCRIBE_BITCOIND(handle_get_chain_work, _1, _2); - SUBSCRIBE_BITCOIND(handle_get_tx_out, _1, _2, _3, _4, _5); - SUBSCRIBE_BITCOIND(handle_get_tx_out_set_info, _1, _2); - SUBSCRIBE_BITCOIND(handle_prune_block_chain, _1, _2, _3); - SUBSCRIBE_BITCOIND(handle_save_mem_pool, _1, _2); - SUBSCRIBE_BITCOIND(handle_scan_tx_out_set, _1, _2, _3, _4); - SUBSCRIBE_BITCOIND(handle_verify_chain, _1, _2, _3, _4); - SUBSCRIBE_BITCOIND(handle_verify_tx_out_set, _1, _2, _3); - network::protocol_http::start(); -} - -void protocol_bitcoind_rpc::stopping(const code& ec) NOEXCEPT -{ - BC_ASSERT(stranded()); - rpc_dispatcher_.stop(ec); - network::protocol_http::stopping(ec); -} - -// Dispatch. -// ---------------------------------------------------------------------------- - -// Handled here for rpc and derived rest protocol. -void protocol_bitcoind_rpc::handle_receive_options(const code& ec, - const options::cptr& options) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return; - - // Enforce http host header (if any hosts are configured). - if (!is_allowed_host(*options, options->version())) - { - send_bad_host(*options); - return; - } - - // Enforce http origin policy (if any origins are configured). - if (!is_allowed_origin(*options, options->version())) - { - send_forbidden(*options); - return; - } - - send_ok(*options); -} - -// Derived rest protocol handles get and rpc handles post. -void protocol_bitcoind_rpc::handle_receive_post(const code& ec, - const post::cptr& post) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return; - - // Enforce http host header (if any hosts are configured). - if (!is_allowed_host(*post, post->version())) - { - send_bad_host(*post); - return; - } - - // Enforce http origin policy (if any origins are configured). - if (!is_allowed_origin(*post, post->version())) - { - send_forbidden(*post); - return; - } - - // Endpoint accepts only json-rpc posts. - if (!post->body().contains()) - { - send_bad_request(*post); - return; - } - - // Get the parsed json-rpc request object. - // v1 or v2 both supported, batch not yet supported. - // v1 null id and v2 missing id implies notification and no response. - const auto& message = post->body().get().message; - - // The post is saved off during asynchonous handling and used in send_json - // to formulate response headers, isolating handlers from http semantics. - set_rpc_request(message.jsonrpc, message.id, post); - - // Dispatch the request to subscribers. - if (const auto code = rpc_dispatcher_.notify(message)) - stop(code); -} - -template -std::string to_hex(const Object& object, size_t size, Args&&... args) NOEXCEPT -{ - std::string out(two * size, '\0'); - stream::out::fast sink{ out }; - write::base16::fast writer{ sink }; - object.to_data(writer, std::forward(args)...); - BC_ASSERT(writer); - return out; -} - -// Handlers. -// ---------------------------------------------------------------------------- -// github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md -// TODO: precompute size for buffer hints. - -bool protocol_bitcoind_rpc::handle_get_best_block_hash(const code& ec, - rpc_interface::get_best_block_hash) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto hash = archive().get_top_confirmed_hash(); - send_result(encode_hash(hash), two * system::hash_size); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_block(const code& ec, - rpc_interface::get_block, const std::string& blockhash, - double verbosity) NOEXCEPT -{ - if (stopped(ec)) - return false; - - hash_digest hash{}; - if (!decode_hash(hash, blockhash)) - { - send_error(error::not_found, blockhash, blockhash.size()); - return true; - } - - constexpr auto witness = true; - const auto& query = archive(); - const auto link = query.to_header(hash); - - if (verbosity == 0.0) - { - const auto block = query.get_block(link, witness); - if (is_null(block)) - { - send_error(error::not_found, blockhash, blockhash.size()); - return true; - } - - send_text(to_hex(*block, block->serialized_size(witness), witness)); - return true; - } - - if (verbosity == 1.0) - { - send_error(error::not_implemented); - return true; - } - - if (verbosity == 2.0) - { - send_error(error::not_implemented); - return true; - } - - send_error(error::invalid_argument); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_block_chain_info(const code& ec, - rpc_interface::get_block_chain_info) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_block_count(const code& ec, - rpc_interface::get_block_count) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_block_filter(const code& ec, - rpc_interface::get_block_filter, const std::string&, - const std::string&) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_block_hash(const code& ec, - rpc_interface::get_block_hash, network::rpc::number_t) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_block_header(const code& ec, - rpc_interface::get_block_header, const std::string&, bool) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_block_stats(const code& ec, - rpc_interface::get_block_stats, const std::string&, - const network::rpc::array_t&) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_chain_tx_stats(const code& ec, - rpc_interface::get_chain_tx_stats, double, const std::string&) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_chain_work(const code& ec, - rpc_interface::get_chain_work) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_tx_out(const code& ec, - rpc_interface::get_tx_out, const std::string&, double, bool) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_get_tx_out_set_info(const code& ec, - rpc_interface::get_tx_out_set_info) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_prune_block_chain(const code& ec, - rpc_interface::prune_block_chain, double) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_save_mem_pool(const code& ec, - rpc_interface::save_mem_pool) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_scan_tx_out_set(const code& ec, - rpc_interface::scan_tx_out_set, const std::string&, - const network::rpc::array_t&) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_verify_chain(const code& ec, - rpc_interface::verify_chain, double, double) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -bool protocol_bitcoind_rpc::handle_verify_tx_out_set(const code& ec, - rpc_interface::verify_tx_out_set, const std::string&) NOEXCEPT -{ - if (stopped(ec)) return false; - send_error(error::not_implemented); - return true; -} - -// Senders. -// ---------------------------------------------------------------------------- - -void protocol_bitcoind_rpc::send_error(const code& ec) NOEXCEPT -{ - send_error(ec, two * ec.message().size()); -} - -void protocol_bitcoind_rpc::send_error(const code& ec, - size_t size_hint) NOEXCEPT -{ - send_error(ec, {}, size_hint); -} - -void protocol_bitcoind_rpc::send_error(const code& ec, - rpc::value_option&& error, size_t size_hint) NOEXCEPT -{ - BC_ASSERT(stranded()); - send_rpc( - { - .jsonrpc = version_, - .id = id_, - .error = rpc::result_t - { - .code = ec.value(), - .message = ec.message(), - .data = std::move(error) - } - }, size_hint); -} - -void protocol_bitcoind_rpc::send_text(std::string&& hexidecimal) NOEXCEPT -{ - BC_ASSERT(stranded()); - send_result(hexidecimal, hexidecimal.size()); -} - -void protocol_bitcoind_rpc::send_result(rpc::value_option&& result, - size_t size_hint) NOEXCEPT -{ - BC_ASSERT(stranded()); - send_rpc( - { - .jsonrpc = version_, - .id = id_, - .result = std::move(result) - }, size_hint); -} - -// private -void protocol_bitcoind_rpc::send_rpc(rpc::response_t&& model, - size_t size_hint) NOEXCEPT -{ - BC_ASSERT(stranded()); - using namespace http; - static const auto json = from_media_type(media_type::application_json); - const auto request = reset_rpc_request(); - http::response message{ status::ok, request->version() }; - add_common_headers(message, *request); - add_access_control_headers(message, *request); - message.set(field::content_type, json); - message.body() = rpc::response - { - { .size_hint = size_hint }, std::move(model), - }; - message.prepare_payload(); - SEND(std::move(message), handle_complete, _1, error::success); -} - -// private -void protocol_bitcoind_rpc::set_rpc_request(rpc::version version, - const rpc::id_option& id, const http::request_cptr& request) NOEXCEPT -{ - BC_ASSERT(stranded()); - id_ = id; - version_ = version; - set_request(request); -} - -// private -http::request_cptr protocol_bitcoind_rpc::reset_rpc_request() NOEXCEPT -{ - BC_ASSERT(stranded()); - id_.reset(); - version_ = rpc::version::undefined; - return reset_request(); -} - -BC_POP_WARNING() -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp deleted file mode 100644 index 55fb4654e..000000000 --- a/src/protocols/protocol_electrum.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 -#include - -namespace libbitcoin { -namespace node { - -#define CLASS protocol_electrum - -using namespace interface; -using namespace std::placeholders; - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) -BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) - -// Start. -// ---------------------------------------------------------------------------- - -void protocol_electrum::start() NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (started()) - return; - - // Blockchain methods. - SUBSCRIBE_RPC(handle_blockchain_block_header, _1, _2, _3, _4); - SUBSCRIBE_RPC(handle_blockchain_block_headers, _1, _2, _3, _4, _5); - SUBSCRIBE_RPC(handle_blockchain_headers_subscribe, _1, _2); - SUBSCRIBE_RPC(handle_blockchain_estimatefee, _1, _2, _3); - SUBSCRIBE_RPC(handle_blockchain_relayfee, _1, _2); - SUBSCRIBE_RPC(handle_blockchain_scripthash_get_balance, _1, _2, _3); - SUBSCRIBE_RPC(handle_blockchain_scripthash_get_history, _1, _2, _3); - SUBSCRIBE_RPC(handle_blockchain_scripthash_get_mempool, _1, _2, _3); - SUBSCRIBE_RPC(handle_blockchain_scripthash_listunspent, _1, _2, _3); - SUBSCRIBE_RPC(handle_blockchain_scripthash_subscribe, _1, _2, _3); - SUBSCRIBE_RPC(handle_blockchain_scripthash_unsubscribe, _1, _2, _3); - SUBSCRIBE_RPC(handle_blockchain_transaction_broadcast, _1, _2, _3); - SUBSCRIBE_RPC(handle_blockchain_transaction_get, _1, _2, _3, _4); - SUBSCRIBE_RPC(handle_blockchain_transaction_get_merkle, _1, _2, _3, _4); - SUBSCRIBE_RPC(handle_blockchain_transaction_id_from_pos, _1, _2, _3, _4, _5); - - // Server methods - SUBSCRIBE_RPC(handle_server_add_peer, _1, _2, _3); - SUBSCRIBE_RPC(handle_server_banner, _1, _2); - SUBSCRIBE_RPC(handle_server_donation_address, _1, _2); - SUBSCRIBE_RPC(handle_server_features, _1, _2); - SUBSCRIBE_RPC(handle_server_peers_subscribe, _1, _2); - SUBSCRIBE_RPC(handle_server_ping, _1, _2); - ////SUBSCRIBE_RPC(handle_server_version, _1, _2, _3, _4); - - // Mempool methods. - SUBSCRIBE_RPC(handle_mempool_get_fee_histogram, _1, _2); - node::protocol_rpc::start(); -} - -// Handlers (blockchain). -// ---------------------------------------------------------------------------- - -void protocol_electrum::handle_blockchain_block_header(const code& ec, - rpc_interface::blockchain_block_header, double , - double ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_block_headers(const code& ec, - rpc_interface::blockchain_block_headers, double , - double , double ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_headers_subscribe(const code& ec, - rpc_interface::blockchain_headers_subscribe) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_estimatefee(const code& ec, - rpc_interface::blockchain_estimatefee, double ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_relayfee(const code& ec, - rpc_interface::blockchain_relayfee) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_scripthash_get_balance(const code& ec, - rpc_interface::blockchain_scripthash_get_balance, - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_scripthash_get_history(const code& ec, - rpc_interface::blockchain_scripthash_get_history, - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_scripthash_get_mempool(const code& ec, - rpc_interface::blockchain_scripthash_get_mempool, - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_scripthash_listunspent(const code& ec, - rpc_interface::blockchain_scripthash_listunspent, - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_scripthash_subscribe(const code& ec, - rpc_interface::blockchain_scripthash_subscribe, - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_scripthash_unsubscribe(const code& ec, - rpc_interface::blockchain_scripthash_unsubscribe, - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_transaction_broadcast(const code& ec, - rpc_interface::blockchain_transaction_broadcast, - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_transaction_get(const code& ec, - rpc_interface::blockchain_transaction_get, const std::string& , - bool ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_transaction_get_merkle(const code& ec, - rpc_interface::blockchain_transaction_get_merkle, const std::string& , - double ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_blockchain_transaction_id_from_pos(const code& ec, - rpc_interface::blockchain_transaction_id_from_pos, double , - double , bool ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -// Handlers (server). -// ---------------------------------------------------------------------------- - -void protocol_electrum::handle_server_add_peer(const code& ec, - rpc_interface::server_add_peer, const interface::object_t& ) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_server_banner(const code& ec, - rpc_interface::server_banner) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_server_donation_address(const code& ec, - rpc_interface::server_donation_address) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_server_features(const code& ec, - rpc_interface::server_features) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_server_peers_subscribe(const code& ec, - rpc_interface::server_peers_subscribe) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -void protocol_electrum::handle_server_ping(const code& ec, - rpc_interface::server_ping) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -// Handlers (mempool). -// ---------------------------------------------------------------------------- - -void protocol_electrum::handle_mempool_get_fee_histogram(const code& ec, - rpc_interface::mempool_get_fee_histogram) NOEXCEPT -{ - if (stopped(ec)) return; - send_code(error::not_implemented); -} - -BC_POP_WARNING() -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/protocols/protocol_electrum_version.cpp b/src/protocols/protocol_electrum_version.cpp deleted file mode 100644 index c32ba1a1a..000000000 --- a/src/protocols/protocol_electrum_version.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 -#include -#include - -namespace libbitcoin { -namespace node { - -#define CLASS protocol_electrum_version - -using namespace network; -using namespace interface; -using namespace std::placeholders; - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) -BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) - -// Start/complete (handshake). -// ---------------------------------------------------------------------------- - -// Session resumes the channel following return from start(). -// Sends are not precluded, but no messages can be received while paused. -void protocol_electrum_version::shake(result_handler&& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (started()) - { - handler(network::error::operation_failed); - return; - } - - handler_ = system::move_shared(std::move(handler)); - - SUBSCRIBE_RPC(handle_server_version, _1, _2, _3, _4); - protocol_rpc::start(); -} - -void protocol_electrum_version::complete(const code& ec, - const code& shake) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return; - - // Calls after handshake completion are allowed and will skip this. - if (handler_) - { - // Invoke handshake completion, error will result in stopped channel. - (*handler_)(shake); - handler_.reset(); - } -} - -// Handler. -// ---------------------------------------------------------------------------- - -// Changed in version 1.6: server must tolerate and ignore extraneous args. -void protocol_electrum_version::handle_server_version(const code& ec, - rpc_interface::server_version, const std::string& client_name, - const value_t& protocol_version) NOEXCEPT -{ - if (stopped(ec)) - return; - - // v0_0 implies version has not been set (first call). - if ((channel_->version() == electrum_version::v0_0) && - (!set_client(client_name) || !set_version(protocol_version))) - { - const auto reason = error::invalid_argument; - send_code(reason, BIND(complete, _1, reason)); - } - else - { - send_result(value_t - { - array_t - { - { string_t{ server_name() } }, - { string_t{ negotiated_version() } } - } - }, 70, BIND(complete, _1, error::success)); - } - - // Handshake must leave channel paused, before leaving stranded handler. - if (handler_) pause(); -} - -// Client/server names. -// ---------------------------------------------------------------------------- - -std::string_view protocol_electrum_version::server_name() const NOEXCEPT -{ - return settings().user_agent; -} - -std::string_view protocol_electrum_version::client_name() const NOEXCEPT -{ - return channel_->client(); -} - -bool protocol_electrum_version::set_client(const std::string& name) NOEXCEPT -{ - // Avoid excess, empty name is allowed. - if (name.size() > max_client_name_length) - return false; - - // Do not put to log without escaping. - channel_->set_client(escape_client(name)); - return true; -} - -std::string protocol_electrum_version::escape_client( - const std::string& in) NOEXCEPT -{ - std::string out(in.size(), '*'); - std::transform(in.begin(), in.end(), out.begin(), [](char c) NOEXCEPT - { - using namespace system; - return is_ascii_character(c) && !is_ascii_whitespace(c) ? c : '*'; - }); - - return out; -} - -// Negotiated version. -// ---------------------------------------------------------------------------- - -std::string_view protocol_electrum_version::negotiated_version() const NOEXCEPT -{ - return version_to_string(channel_->version()); -} - -bool protocol_electrum_version::set_version(const value_t& version) NOEXCEPT -{ - electrum_version client_min{}; - electrum_version client_max{}; - if (!get_versions(client_min, client_max, version)) - return false; - - const auto lower = std::max(client_min, minimum); - const auto upper = std::min(client_max, maximum); - if (lower > upper) - return false; - - LOGA("Electrum [" << authority() << "] version (" - << version_to_string(client_max) << ") " << client_name()); - - channel_->set_version(upper); - return true; -} - -bool protocol_electrum_version::get_versions(electrum_version& min, - electrum_version& max, const interface::value_t& version) NOEXCEPT -{ - // Optional value_t can be string_t or array_t of two string_t. - const auto& value = version.value(); - - // Default version (null_t is the default of value_t). - if (std::holds_alternative(value)) - { - // An interface default can't be set for optional. - max = min = electrum_version::v1_4; - return true; - } - - // One version. - if (std::holds_alternative(value)) - { - // A single value implies minimum is the same as maximum. - max = min = version_from_string(std::get(value)); - return min != electrum_version::v0_0; - } - - // Two versions. - if (std::holds_alternative(value)) - { - const auto& versions = std::get(value); - if (versions.size() != two) - return false; - - // First string is mimimum, second is maximum. - const auto& min_version = versions.at(0).value(); - const auto& max_version = versions.at(1).value(); - if (!std::holds_alternative(min_version) || - !std::holds_alternative(max_version)) - return false; - - min = version_from_string(std::get(min_version)); - max = version_from_string(std::get(max_version)); - return min != electrum_version::v0_0 - && max != electrum_version::v0_0; - } - - return false; -} - -// private/static -std::string_view protocol_electrum_version::version_to_string( - electrum_version version) NOEXCEPT -{ - static const std::unordered_map map - { - { electrum_version::v0_6, "0.6" }, - { electrum_version::v0_8, "0.8" }, - { electrum_version::v0_9, "0.9" }, - { electrum_version::v0_10, "0.10" }, - { electrum_version::v1_0, "1.0" }, - { electrum_version::v1_1, "1.1" }, - { electrum_version::v1_2, "1.2" }, - { electrum_version::v1_3, "1.3" }, - { electrum_version::v1_4, "1.4" }, - { electrum_version::v1_4_1, "1.4.1" }, - { electrum_version::v1_4_2, "1.4.2" }, - { electrum_version::v1_6, "1.6" }, - { electrum_version::v0_0, "0.0" } - }; - - const auto it = map.find(version); - return it != map.end() ? it->second : "0.0"; -} - -// private/static -electrum_version protocol_electrum_version::version_from_string( - const std::string_view& version) NOEXCEPT -{ - static const std::unordered_map map - { - { "0.6", electrum_version::v0_6 }, - { "0.8", electrum_version::v0_8 }, - { "0.9", electrum_version::v0_9 }, - { "0.10", electrum_version::v0_10 }, - { "1.0", electrum_version::v1_0 }, - { "1.1", electrum_version::v1_1 }, - { "1.2", electrum_version::v1_2 }, - { "1.3", electrum_version::v1_3 }, - { "1.4", electrum_version::v1_4 }, - { "1.4.1", electrum_version::v1_4_1 }, - { "1.4.2", electrum_version::v1_4_2 }, - { "1.6", electrum_version::v1_6 }, - { "0.0", electrum_version::v0_0 } - }; - - const auto it = map.find(version); - return it != map.end() ? it->second : electrum_version::v0_0; -} - -BC_POP_WARNING() -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/protocols/protocol_explore.cpp b/src/protocols/protocol_explore.cpp deleted file mode 100644 index 4e1cc2bc5..000000000 --- a/src/protocols/protocol_explore.cpp +++ /dev/null @@ -1,1232 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 -#include -#include - -namespace libbitcoin { -namespace node { - -#define CLASS protocol_explore -#define SUBSCRIBE_EXPLORE(method, ...) \ - subscribe(&CLASS::method, __VA_ARGS__) - -using namespace system; -using namespace network; -using namespace network::messages::peer; -using namespace std::placeholders; -using namespace boost::json; - -using inpoint = database::inpoint; -using outpoint = database::outpoint; -using inpoints = database::inpoints; -using outpoints = database::outpoints; - -DEFINE_JSON_TO_TAG(inpoints) -{ - inpoints out{}; - for (const auto& point: value.as_array()) - out.insert(value_to(point)); - - return out; -} - -DEFINE_JSON_TO_TAG(outpoints) -{ - outpoints out{}; - for (const auto& point: value.as_array()) - out.insert(value_to(point)); - - return out; -} - -// Avoiding namespace conflict. -using object_type = network::rpc::object_t; - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -BC_PUSH_WARNING(NO_INCOMPLETE_SWITCH) -BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) -BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) - -// Start. -// ---------------------------------------------------------------------------- - -void protocol_explore::start() NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (started()) - return; - - SUBSCRIBE_EXPLORE(handle_get_top, _1, _2, _3, _4); - - SUBSCRIBE_EXPLORE(handle_get_block, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_EXPLORE(handle_get_block_header, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_block_header_context, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_block_details, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_block_txs, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_block_filter, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_EXPLORE(handle_get_block_filter_hash, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_EXPLORE(handle_get_block_filter_header, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_EXPLORE(handle_get_block_tx, _1, _2, _3, _4, _5, _6, _7, _8); - - SUBSCRIBE_EXPLORE(handle_get_tx, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_tx_header, _1, _2, _3, _4, _5); - SUBSCRIBE_EXPLORE(handle_get_tx_details, _1, _2, _3, _4, _5); - - SUBSCRIBE_EXPLORE(handle_get_inputs, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_input, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_EXPLORE(handle_get_input_script, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_input_witness, _1, _2, _3, _4, _5, _6); - - SUBSCRIBE_EXPLORE(handle_get_outputs, _1, _2, _3, _4, _5); - SUBSCRIBE_EXPLORE(handle_get_output, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_output_script, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_output_spender, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_output_spenders, _1, _2, _3, _4, _5, _6); - - SUBSCRIBE_EXPLORE(handle_get_address, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_address_confirmed, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_address_unconfirmed, _1, _2, _3, _4, _5, _6); - SUBSCRIBE_EXPLORE(handle_get_address_balance, _1, _2, _3, _4, _5, _6); - protocol_html::start(); -} - -void protocol_explore::stopping(const code& ec) NOEXCEPT -{ - BC_ASSERT(stranded()); - stopping_.store(true); - dispatcher_.stop(ec); - protocol_html::stopping(ec); -} - -// Dispatch. -// ---------------------------------------------------------------------------- - -bool protocol_explore::try_dispatch_object(const http::request& request) NOEXCEPT -{ - BC_ASSERT(stranded()); - - rpc::request_t model{}; - if (LOG_ONLY(const auto ec =) explore_target(model, request.target())) - { - LOGA("Request parse [" << request.target() << "] " << ec.message()); - return false; - } - - if (!explore_query(model, request)) - { - send_not_acceptable(request); - return true; - } - - if (const auto ec = dispatcher_.notify(model)) - send_internal_server_error(ec, request); - - return true; -} - -// Handlers. -// ---------------------------------------------------------------------------- - -constexpr auto data = to_value(http::media_type::application_octet_stream); -constexpr auto json = to_value(http::media_type::application_json); -constexpr auto text = to_value(http::media_type::text_plain); - -template -data_chunk to_bin(const Object& object, size_t size, Args&&... args) NOEXCEPT -{ - data_chunk out(size); - stream::out::fast sink{ out }; - write::bytes::fast writer{ sink }; - object.to_data(writer, std::forward(args)...); - BC_ASSERT(writer); - return out; -} - -template -std::string to_hex(const Object& object, size_t size, Args&&... args) NOEXCEPT -{ - std::string out(two * size, '\0'); - stream::out::fast sink{ out }; - write::base16::fast writer{ sink }; - object.to_data(writer, std::forward(args)...); - BC_ASSERT(writer); - return out; -} - -template -data_chunk to_bin_array(const Collection& collection, size_t size, - Args&&... args) NOEXCEPT -{ - data_chunk out(size); - stream::out::fast sink{ out }; - write::bytes::fast writer{ sink }; - for (const auto& element: collection) - element.to_data(writer, std::forward(args)...); - - BC_ASSERT(writer); - return out; -} - -template -std::string to_hex_array(const Collection& collection, size_t size, - Args&&... args) NOEXCEPT -{ - std::string out(two * size, '\0'); - stream::out::fast sink{ out }; - write::base16::fast writer{ sink }; - for (const auto& element: collection) - element.to_data(writer, std::forward(args)...); - - BC_ASSERT(writer); - return out; -} - -template -data_chunk to_bin_ptr_array(const Collection& collection, size_t size, - Args&&... args) NOEXCEPT -{ - data_chunk out(size); - stream::out::fast sink{ out }; - write::bytes::fast writer{ sink }; - for (const auto& ptr: collection) - ptr->to_data(writer, std::forward(args)...); - - BC_ASSERT(writer); - return out; -} - -template -std::string to_hex_ptr_array(const Collection& collection, size_t size, - Args&&... args) NOEXCEPT -{ - std::string out(two * size, '\0'); - stream::out::fast sink{ out }; - write::base16::fast writer{ sink }; - for (const auto& ptr: collection) - ptr->to_data(writer, std::forward(args)...); - - BC_ASSERT(writer); - return out; -} - -bool protocol_explore::handle_get_top(const code& ec, interface::top, - uint8_t, uint8_t media) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto height = archive().get_top_confirmed(); - switch (media) - { - case data: - send_chunk(to_little_endian_size(height)); - return true; - case text: - send_text(encode_base16(to_little_endian_size(height))); - return true; - case json: - send_json(height, two * sizeof(height)); - return true; - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block(const code& ec, interface::block, - uint8_t, uint8_t media, std::optional hash, - std::optional height, bool witness) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto link = to_header(height, hash); - if (const auto block = archive().get_block(link, witness)) - { - const auto size = block->serialized_size(witness); - switch (media) - { - case data: - send_chunk(to_bin(*block, size, witness)); - return true; - case text: - send_text(to_hex(*block, size, witness)); - return true; - case json: - auto model = value_from(block); - inject(model.at("header"), height, link); - send_json(std::move(model), two * size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block_header(const code& ec, - interface::block_header, uint8_t, uint8_t media, - std::optional hash, std::optional height) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto link = to_header(height, hash); - if (const auto header = archive().get_header(link)) - { - constexpr auto size = chain::header::serialized_size(); - switch (media) - { - case data: - send_chunk(to_bin(*header, size)); - return true; - case text: - send_text(to_hex(*header, size)); - return true; - case json: - auto model = value_from(header); - inject(model, height, link); - send_json(std::move(model), two * size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block_header_context(const code& ec, - interface::block_header_context, uint8_t, uint8_t media, - std::optional hash, std::optional height) NOEXCEPT -{ - if (stopped(ec)) - return false; - - // states: - // block_valid - // block_confirmable - // block_unconfirmable - // get_header_state->unvalidated can be no header or no txs. - ////const auto state = query.get_header_state(link); - ////if (state == database::error::unvalidated) - ////{ - //// send_not_found(); - //// return true; - ////} - - const auto& query = archive(); - const auto link = to_header(height, hash); - database::context context{}; - if (query.get_context(context, link)) - { - switch (media) - { - case data: - send_chunk(to_little_endian_size(context.flags)); - return true; - case text: - send_text(encode_base16(to_little_endian_size(context.flags))); - return true; - case json: - send_json(context.flags, two * sizeof(context.flags)); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block_details(const code& ec, - interface::block_details, uint8_t, uint8_t media, - std::optional hash, std::optional height) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - const auto link = to_header(height, hash); - const auto state = query.get_block_state(link); - - // get_block_state->unassociated can be no header or no txs. - if (state == database::error::unassociated) - { - send_not_found(); - return true; - } - - // states: - // unvalidated - // block_valid - // block_confirmable - // block_unconfirmable - - // both txs table (can get from details) - //const auto size = query.get_block_size(link); - //const auto count = query.get_tx_count(link); - - // TODO: - // query (whole block and all prevouts, same as get_block_fees) - // fees, claim, reward, subsidy, weight, size, count. - - if (const auto fees = query.get_block_fees(link); fees != max_uint64) - { - switch (media) - { - case data: - send_chunk(to_little_endian_size(fees)); - return true; - case text: - send_text(encode_base16(to_little_endian_size(fees))); - return true; - case json: - send_json(fees, two * sizeof(fees)); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block_txs(const code& ec, - interface::block_txs, uint8_t, uint8_t media, - std::optional hash, std::optional height) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (const auto hashes = query.get_tx_keys(to_header(height, hash)); - !hashes.empty()) - { - const auto size = hashes.size() * hash_size; - switch (media) - { - case data: - { - const auto data = pointer_cast(hashes.data()); - send_chunk(to_chunk({ data, std::next(data, size) })); - return true; - } - case text: - { - const auto data = pointer_cast(hashes.data()); - send_text(encode_base16({ data, std::next(data, size) })); - return true; - } - case json: - { - array out(hashes.size()); - std::ranges::transform(hashes, out.begin(), - [](const auto& hash) { return encode_hash(hash); }); - send_json(out, two * size); - return true; - } - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block_filter(const code& ec, - interface::block_filter, uint8_t, uint8_t media, uint8_t type, - std::optional hash, std::optional height) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (!query.filter_enabled() || type != client_filter::type_id::neutrino) - { - send_not_implemented(); - return true; - } - - data_chunk filter{}; - if (query.get_filter_body(filter, to_header(height, hash))) - { - switch (media) - { - case data: - send_chunk(std::move(filter)); - return true; - case text: - send_text(encode_base16(filter)); - return true; - case json: - send_json(value_from(encode_base16(filter)), - two * filter.size()); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block_filter_hash(const code& ec, - interface::block_filter_hash, uint8_t, uint8_t media, uint8_t type, - std::optional hash, std::optional height) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (!query.filter_enabled() || type != client_filter::type_id::neutrino) - { - send_not_implemented(); - return true; - } - - hash_digest filter_hash{ hash_size }; - if (query.get_filter_hash(filter_hash, to_header(height, hash))) - { - switch (media) - { - case data: - send_chunk(to_chunk(filter_hash)); - return true; - case text: - send_text(encode_base16(filter_hash)); - return true; - case json: - send_json(value_from(encode_hash(filter_hash)), - two * hash_size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block_filter_header(const code& ec, - interface::block_filter_header, uint8_t, uint8_t media, uint8_t type, - std::optional hash, std::optional height) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (!query.filter_enabled() || type != client_filter::type_id::neutrino) - { - send_not_implemented(); - return true; - } - - hash_digest filter_head{ hash_size }; - if (query.get_filter_head(filter_head, to_header(height, hash))) - { - switch (media) - { - case data: - send_chunk(to_chunk(filter_head)); - return true; - case text: - send_text(encode_base16(filter_head)); - return true; - case json: - send_json(value_from(encode_hash(filter_head)), - two * hash_size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_block_tx(const code& ec, interface::block_tx, - uint8_t, uint8_t media, uint32_t position, std::optional hash, - std::optional height, bool witness) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (const auto tx = query.get_transaction(query.to_transaction( - to_header(height, hash), position), witness)) - { - const auto size = tx->serialized_size(witness); - switch (media) - { - case data: - send_chunk(to_bin(*tx, size, witness)); - return true; - case text: - send_text(to_hex(*tx, size, witness)); - return true; - case json: - send_json(value_from(tx), two * size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_tx(const code& ec, interface::tx, uint8_t, - uint8_t media, const hash_cptr& hash, bool witness) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (const auto tx = query.get_transaction(query.to_tx(*hash), witness)) - { - const auto size = tx->serialized_size(witness); - switch (media) - { - case data: - send_chunk(to_bin(*tx, size, witness)); - return true; - case text: - send_text(to_hex(*tx, size, witness)); - return true; - case json: - send_json(value_from(tx), two * size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_tx_header(const code& ec, - interface::tx_header, uint8_t, uint8_t media, - const hash_cptr& hash) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - const auto link = query.to_confirmed_block(*hash); - if (link.is_terminal()) - { - send_not_found(); - return true; - } - - const auto header = query.get_header(link); - if (!header) - { - send_internal_server_error(database::error::integrity); - return true; - } - - constexpr auto size = chain::header::serialized_size(); - switch (media) - { - case data: - send_chunk(to_bin(*header, size)); - return true; - case text: - send_text(to_hex(*header, size)); - return true; - case json: - auto model = value_from(header); - inject(model, {}, link); - send_json(std::move(model), two * size); - return true; - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_tx_details(const code& ec, - interface::tx_details, uint8_t, uint8_t media, - const hash_cptr& hash) NOEXCEPT -{ - if (stopped(ec)) - return false; - - // TODO: expand details to include tx.size and tx.weight. - const auto& query = archive(); - if (const auto fee = query.get_tx_fee(query.to_tx(*hash)); - fee != max_uint64) - { - switch (media) - { - case data: - send_chunk(to_little_endian_size(fee)); - return true; - case text: - send_text(encode_base16(to_little_endian_size(fee))); - return true; - case json: - send_json(fee, two * sizeof(fee)); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_inputs(const code& ec, interface::inputs, - uint8_t, uint8_t media, const hash_cptr& hash, bool witness) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - const auto tx = query.to_tx(*hash); - if (tx.is_terminal()) - { - send_not_found(); - return true; - } - - const auto inputs = query.get_inputs(tx, witness); - if (!inputs || inputs->empty()) - { - send_internal_server_error(database::error::integrity); - return true; - } - - // Wire serialization of input does not include witness. - const auto size = std::accumulate(inputs->begin(), inputs->end(), zero, - [&](size_t total, const auto& input) NOEXCEPT - { return total + input->serialized_size(false); }); - - switch (media) - { - case data: - send_chunk(to_bin_ptr_array(*inputs, size)); - return true; - case text: - send_text(to_hex_ptr_array(*inputs, size)); - return true; - case json: - // Json input serialization includes witness. - send_json(value_from(*inputs), two * size); - return true; - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_input(const code& ec, interface::input, - uint8_t, uint8_t media, const hash_cptr& hash, uint32_t index, - bool witness) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (const auto input = query.get_input(query.to_tx(*hash), index, witness)) - { - // Wire serialization of input does not include witness. - const auto size = input->serialized_size(false); - switch (media) - { - case data: - send_chunk(to_bin(*input, size)); - return true; - case text: - send_text(to_hex(*input, size)); - return true; - case json: - // Json input serialization includes witness. - send_json(value_from(input), - two * input->serialized_size(witness)); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_input_script(const code& ec, - interface::input_script, uint8_t, uint8_t media, const hash_cptr& hash, - uint32_t index) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (const auto script = query.get_input_script(query.to_point( - query.to_tx(*hash), index))) - { - const auto size = script->serialized_size(false); - switch (media) - { - case data: - send_chunk(to_bin(*script, size, false)); - return true; - case text: - send_text(to_hex(*script, size, false)); - return true; - case json: - send_json(value_from(script), two * size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_input_witness(const code& ec, - interface::input_witness, uint8_t, uint8_t media, const hash_cptr& hash, - uint32_t index) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (const auto witness = query.get_witness(query.to_point( - query.to_tx(*hash), index)); !is_null(witness) && witness->is_valid()) - { - const auto size = witness->serialized_size(false); - switch (media) - { - case data: - send_chunk(to_bin(*witness, size, false)); - return true; - case text: - send_text(to_hex(*witness, size, false)); - return true; - case json: - send_json(value_from(witness), - two * witness->serialized_size(false)); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_outputs(const code& ec, interface::outputs, - uint8_t, uint8_t media, const hash_cptr& hash) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - const auto tx = query.to_tx(*hash); - if (tx.is_terminal()) - { - send_not_found(); - return true; - } - - const auto outputs = query.get_outputs(tx); - if (!outputs || outputs->empty()) - { - send_internal_server_error(database::error::integrity); - return true; - } - - // Wire serialization size of outputs set. - const auto size = std::accumulate(outputs->begin(), outputs->end(), zero, - [](size_t total, const auto& output) NOEXCEPT - { return total + output->serialized_size(); }); - - switch (media) - { - case data: - send_chunk(to_bin_ptr_array(*outputs, size)); - return true; - case text: - send_text(to_hex_ptr_array(*outputs, size)); - return true; - case json: - send_json(value_from(*outputs), two * size); - return true; - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_output(const code& ec, interface::output, - uint8_t, uint8_t media, const hash_cptr& hash, - uint32_t index) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (const auto output = query.get_output(query.to_tx(*hash), index)) - { - const auto size = output->serialized_size(); - switch (media) - { - case data: - send_chunk(to_bin(*output, size)); - return true; - case text: - send_text(to_hex(*output, size)); - return true; - case json: - send_json(value_from(output), two * size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_output_script(const code& ec, - interface::output_script, uint8_t, uint8_t media, const hash_cptr& hash, - uint32_t index) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (const auto script = query.get_output_script(query.to_output( - query.to_tx(*hash), index))) - { - const auto size = script->serialized_size(false); - switch (media) - { - case data: - send_chunk(to_bin(*script, size, false)); - return true; - case text: - send_text(to_hex(*script, size, false)); - return true; - case json: - send_json(value_from(script), two * size); - return true; - } - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_output_spender(const code& ec, - interface::output_spender, uint8_t, uint8_t media, const hash_cptr& hash, - uint32_t index) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto& query = archive(); - const chain::point spent{ *hash, index }; - const auto spender = query.get_spender(query.to_confirmed_spender(spent)); - if (spender.index() == chain::point::null_index) - { - send_not_found(); - return true; - } - - constexpr auto size = chain::point::serialized_size(); - switch (media) - { - case data: - send_chunk(to_bin(spender, size)); - return true; - case text: - send_text(to_hex(spender, size)); - return true; - case json: - send_json(value_from(spender), two * size); - return true; - } - - send_not_found(); - return true; -} - -bool protocol_explore::handle_get_output_spenders(const code& ec, - interface::output_spenders, uint8_t, uint8_t media, const hash_cptr& hash, - uint32_t index) NOEXCEPT -{ - if (stopped(ec)) - return false; - - const auto ins = archive().get_spenders({ *hash, index }); - if (ins.empty()) - { - send_not_found(); - return true; - } - - const auto size = ins.size() * inpoint::serialized_size(); - switch (media) - { - case data: - send_chunk(to_bin_array(ins, size)); - return true; - case text: - send_text(to_hex_array(ins, size)); - return true; - case json: - send_json(value_from(ins), two * size); - return true; - } - - send_not_found(); - return true; -} - -// handle_get_address -// ---------------------------------------------------------------------------- - -bool protocol_explore::handle_get_address(const code& ec, interface::address, - uint8_t, uint8_t media, const hash_cptr& hash, bool turbo) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return false; - - if (!archive().address_enabled()) - { - send_not_implemented(); - return true; - } - - // Monitor socket for close. - monitor(true); - - PARALLEL(do_get_address, media, turbo, hash); - return true; -} - -// private -void protocol_explore::do_get_address(uint8_t media, bool turbo, - const hash_cptr& hash) NOEXCEPT -{ - BC_ASSERT(!stranded()); - - outpoints set{}; - const auto& query = archive(); - const auto ec = query.get_address_outputs(stopping_, set, *hash, turbo); - POST(complete_get_address, ec, media, std::move(set)); -} - -// This is shared by the three get_address... methods. -void protocol_explore::complete_get_address(const code& ec, uint8_t media, - const outpoints& set) NOEXCEPT -{ - BC_ASSERT(stranded()); - - // Stop monitoring socket. - monitor(false); - - if (stopped()) - return; - - if (ec) - { - send_internal_server_error(ec); - return; - } - - if (set.empty()) - { - send_not_found(); - return; - } - - const auto size = set.size() * chain::outpoint::serialized_size(); - switch (media) - { - case data: - send_chunk(to_bin_array(set, size)); - return; - case text: - send_text(to_hex_array(set, size)); - return; - case json: - send_json(value_from(set), two * size); - return; - } - - send_not_found(); -} - -// handle_get_address_confirmed -// ---------------------------------------------------------------------------- - -bool protocol_explore::handle_get_address_confirmed(const code& ec, - interface::address_confirmed, uint8_t, uint8_t media, - const hash_cptr& hash, bool turbo) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return false; - - if (!archive().address_enabled()) - { - send_not_implemented(); - return true; - } - - // Monitor socket for close. - monitor(true); - - PARALLEL(do_get_address_confirmed, media, turbo, hash); - return true; -} - -// private -void protocol_explore::do_get_address_confirmed(uint8_t media, bool turbo, - const hash_cptr& hash) NOEXCEPT -{ - BC_ASSERT(!stranded()); - - outpoints set{}; - const auto& query = archive(); - auto ec = query.get_confirmed_unspent_outputs(stopping_, set, *hash, turbo); - POST(complete_get_address, ec, media, std::move(set)); -} - -// handle_get_address_unconfirmed -// ---------------------------------------------------------------------------- - -bool protocol_explore::handle_get_address_unconfirmed(const code& ec, - interface::address_unconfirmed, uint8_t, uint8_t, - const hash_cptr&, bool) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return false; - - // TODO: there are currently no unconfirmed txs. - send_not_implemented(); - return true; -} - -// handle_get_address_balance -// ---------------------------------------------------------------------------- - -bool protocol_explore::handle_get_address_balance(const code& ec, - interface::address_balance, uint8_t, uint8_t media, - const hash_cptr& hash, bool turbo) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return false; - - const auto& query = archive(); - if (!query.address_enabled()) - { - send_not_implemented(); - return true; - } - - // Monitor socket for close. - monitor(true); - - PARALLEL(do_get_address_balance, media, turbo, hash); - return true; -} - -void protocol_explore::do_get_address_balance(uint8_t media, bool turbo, - const system::hash_cptr& hash) NOEXCEPT -{ - BC_ASSERT(!stranded()); - - uint64_t balance{}; - const auto& query = archive(); - auto ec = query.get_confirmed_balance(stopping_, balance, *hash, turbo); - POST(complete_get_address_balance, ec, media, balance); -} - -void protocol_explore::complete_get_address_balance(const code& ec, - uint8_t media, uint64_t balance) NOEXCEPT -{ - BC_ASSERT(stranded()); - - // Stop monitoring socket. - monitor(false); - - // Suppresses cancelation error response. - if (stopped()) - return; - - if (ec) - { - send_internal_server_error(ec); - return; - } - - switch (media) - { - case data: - send_chunk(to_little_endian_size(balance)); - return; - case text: - send_text(encode_base16(to_little_endian_size(balance))); - return; - case json: - send_json(balance, two * sizeof(balance)); - return; - } - - send_not_found(); -} - -// private -// ---------------------------------------------------------------------------- - -void protocol_explore::inject(value& out, std::optional height, - const database::header_link& link) const NOEXCEPT -{ - out.as_object().emplace("height", height.has_value() ? height.value() : - archive().get_height(link).value); -} - -database::header_link protocol_explore::to_header( - const std::optional& height, - const std::optional& hash) NOEXCEPT -{ - const auto& query = archive(); - - if (hash.has_value()) - return query.to_header(*(hash.value())); - - if (height.has_value()) - return query.to_confirmed(height.value()); - - return {}; -} - -BC_POP_WARNING() -BC_POP_WARNING() -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/protocols/protocol_html.cpp b/src/protocols/protocol_html.cpp deleted file mode 100644 index b2e853f1c..000000000 --- a/src/protocols/protocol_html.cpp +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 - -namespace libbitcoin { -namespace node { - -#define CLASS protocol_html - -using namespace network::http; -using namespace std::placeholders; - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -// Handle get method. -// ---------------------------------------------------------------------------- - -void protocol_html::handle_receive_get(const code& ec, - const method::get::cptr& get) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped(ec)) - return; - - // Enforce http origin form for get. - if (!is_origin_form(get->target())) - { - send_bad_target({}, *get); - return; - } - - // Enforce http origin policy (if any origins are configured). - if (!is_allowed_origin(*get, get->version())) - { - send_forbidden(*get); - return; - } - - // Enforce http host header (if any hosts are configured). - if (!is_allowed_host(*get, get->version())) - { - send_bad_host(*get); - return; - } - - // Always try API dispatch, false if unhandled. - if (try_dispatch_object(*get)) - return; - - // Require file system dispatch if path is configured (always handles). - if (!options_.path.empty()) - { - dispatch_file(*get); - return; - } - - // Require embedded dispatch if site is configured (always handles). - if (options_.pages.enabled()) - { - dispatch_embedded(*get); - return; - } - - // Neither site is enabled and object dispatch doesn't support. - send_not_implemented(*get); -} - -// Dispatch. -// ---------------------------------------------------------------------------- - -bool protocol_html::try_dispatch_object(const request&) NOEXCEPT -{ - return false; -} - -void protocol_html::dispatch_embedded(const request& request) NOEXCEPT -{ - const auto& pages = config().server.explore.pages; - switch (const auto media = file_media_type(to_path(request.target()))) - { - case media_type::text_css: - send_span(pages.css(), media, request); - break; - case media_type::text_html: - send_span(pages.html(), media, request); - break; - case media_type::application_javascript: - send_span(pages.ecma(), media, request); - break; - case media_type::font_woff: - case media_type::font_woff2: - send_span(pages.font(), media, request); - break; - case media_type::image_png: - case media_type::image_gif: - case media_type::image_jpeg: - send_span(pages.icon(), media, request); - break; - default: - send_not_found(request); - } -} - -void protocol_html::dispatch_file(const request& request) NOEXCEPT -{ - // Empty path implies malformed target (terminal). - auto path = to_local_path(request.target()); - if (path.empty()) - { - send_bad_target({}, request); - return; - } - - // If no file extension it's REST on the single/default html page. - if (!path.has_extension()) - { - path = to_local_path(); - - // Default html page (e.g. index.html) is not configured. - if (path.empty()) - { - send_not_implemented(request); - return; - } - } - - // Get the single/default or explicitly requested page. - auto file = get_file_body(path); - if (!file.is_open()) - { - send_not_found(request); - return; - } - - const auto octet_stream = media_type::application_octet_stream; - send_file(std::move(file), file_media_type(path, octet_stream), request); -} - -// Senders. -// ---------------------------------------------------------------------------- - -constexpr auto data = media_type::application_octet_stream; -constexpr auto json = media_type::application_json; -constexpr auto text = media_type::text_plain; - -void protocol_html::send_json(boost::json::value&& model, size_t size_hint, - const request& request) NOEXCEPT -{ - BC_ASSERT(stranded()); - response response{ status::ok, request.version() }; - add_common_headers(response, request); - add_access_control_headers(response, request); - response.set(field::content_type, from_media_type(json)); - response.body() = json_value{ std::move(model), size_hint }; - response.prepare_payload(); - SEND(std::move(response), handle_complete, _1, error::success); -} - -void protocol_html::send_text(std::string&& hexidecimal, - const request& request) NOEXCEPT -{ - BC_ASSERT(stranded()); - response response{ status::ok, request.version() }; - add_common_headers(response, request); - add_access_control_headers(response, request); - response.set(field::content_type, from_media_type(text)); - response.body() = std::move(hexidecimal); - response.prepare_payload(); - SEND(std::move(response), handle_complete, _1, error::success); -} - -void protocol_html::send_chunk(system::data_chunk&& bytes, - const request& request) NOEXCEPT -{ - BC_ASSERT(stranded()); - response response{ status::ok, request.version() }; - add_common_headers(response, request); - add_access_control_headers(response, request); - response.set(field::content_type, from_media_type(data)); - response.body() = std::move(bytes); - response.prepare_payload(); - SEND(std::move(response), handle_complete, _1, error::success); -} - -void protocol_html::send_file(file&& file, media_type type, - const request& request) NOEXCEPT -{ - BC_ASSERT(stranded()); - BC_ASSERT_MSG(file.is_open(), "sending closed file handle"); - response response{ status::ok, request.version() }; - add_common_headers(response, request); - add_access_control_headers(response, request); - response.set(field::content_type, from_media_type(type)); - response.body() = std::move(file); - response.prepare_payload(); - SEND(std::move(response), handle_complete, _1, error::success); -} - -void protocol_html::send_span(span_body::value_type&& span, - media_type type, const request& request) NOEXCEPT -{ - BC_ASSERT(stranded()); - response response{ status::ok, request.version() }; - add_common_headers(response, request); - add_access_control_headers(response, request); - response.set(field::content_type, from_media_type(type)); - response.body() = std::move(span); - response.prepare_payload(); - SEND(std::move(response), handle_complete, _1, error::success); -} - -void protocol_html::send_buffer(buffer_body::value_type&& buffer, - media_type type, const request& request) NOEXCEPT -{ - BC_ASSERT(stranded()); - response response{ status::ok, request.version() }; - add_common_headers(response, request); - add_access_control_headers(response, request); - response.set(field::content_type, from_media_type(type)); - response.body() = std::move(buffer); - response.prepare_payload(); - SEND(std::move(response), handle_complete, _1, error::success); -} - -// Utilities. -// ---------------------------------------------------------------------------- - -std::filesystem::path protocol_html::to_path( - const std::string& target) const NOEXCEPT -{ - return target == "/" ? target + options_.default_ : target; -} - -std::filesystem::path protocol_html::to_local_path( - const std::string& target) const NOEXCEPT -{ - return sanitize_origin(options_.path, to_path(target).string()); -} - -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/protocols/protocol_http.cpp b/src/protocols/protocol_http.cpp deleted file mode 100644 index f005c1c61..000000000 --- a/src/protocols/protocol_http.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 - -namespace libbitcoin { -namespace node { - -using namespace network::http; - -// Cache request for serialization, keeping it out of dispatch. -void protocol_http::set_request(const request_cptr& request) NOEXCEPT -{ - ////BC_ASSERT(stranded()); - BC_ASSERT(request); - request_ = request; -} - -// Returns default if not set, for safety (asserts correctness). -request_cptr protocol_http::reset_request() NOEXCEPT -{ - ////BC_ASSERT(stranded()); - BC_ASSERT(request_); - - if (request_) - { - auto copy = request_; - request_.reset(); - return copy; - } - - return system::to_shared(); -} - -} // namespace node -} // namespace libbitcoin diff --git a/src/protocols/protocol_stratum_v1.cpp b/src/protocols/protocol_stratum_v1.cpp deleted file mode 100644 index eb406d43f..000000000 --- a/src/protocols/protocol_stratum_v1.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 - -namespace libbitcoin { -namespace node { - -#define CLASS protocol_stratum_v1 - -using namespace interface; -using namespace std::placeholders; - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) -BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) - -// Start. -// ---------------------------------------------------------------------------- - -void protocol_stratum_v1::start() NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (started()) - return; - - // Client requests. - SUBSCRIBE_RPC(handle_mining_subscribe, _1, _2, _3, _4); - SUBSCRIBE_RPC(handle_mining_authorize, _1, _2, _3, _4); - SUBSCRIBE_RPC(handle_mining_submit, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_RPC(handle_mining_extranonce_subscribe, _1, _2); - SUBSCRIBE_RPC(handle_mining_extranonce_unsubscribe, _1, _2, _3); - - // Server notifications. - SUBSCRIBE_RPC(handle_mining_configure, _1, _2, _3); - SUBSCRIBE_RPC(handle_mining_set_difficulty, _1, _2, _3); - SUBSCRIBE_RPC(handle_mining_notify, _1, _2, _3, _4, _5, _6, _7, _8, _9, - _10, _11, _12, _13); - SUBSCRIBE_RPC(handle_client_reconnect, _1, _2, _3, _4, _5); - SUBSCRIBE_RPC(handle_client_hello, _1, _2, _3); - SUBSCRIBE_RPC(handle_client_rejected, _1, _2, _3, _4); - node::protocol_rpc::start(); -} - -// Handlers (client requests). -// ---------------------------------------------------------------------------- - -bool protocol_stratum_v1::handle_mining_subscribe(const code& ec, - rpc_interface::mining_subscribe, const std::string& , - double ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_mining_authorize(const code& ec, - rpc_interface::mining_authorize, const std::string& , - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_mining_submit(const code& ec, - rpc_interface::mining_submit, const std::string& , - const std::string& , const std::string& , - double , const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_mining_extranonce_subscribe(const code& ec, - rpc_interface::mining_extranonce_subscribe) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_mining_extranonce_unsubscribe(const code& ec, - rpc_interface::mining_extranonce_unsubscribe, double ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -// Handlers (server notifications). -// ---------------------------------------------------------------------------- - -bool protocol_stratum_v1::handle_mining_configure(const code& ec, - rpc_interface::mining_configure, - const interface::object_t& ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_mining_set_difficulty(const code& ec, - rpc_interface::mining_set_difficulty, double ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_mining_notify(const code& ec, - rpc_interface::mining_notify, const std::string& , - const std::string& , const std::string& , - const std::string& , const interface::array_t& , - double , double , double , bool , - bool , bool ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_client_reconnect(const code& ec, - rpc_interface::client_reconnect, const std::string& , double , - double ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_client_hello(const code& ec, - rpc_interface::client_hello, - const interface::object_t& ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -bool protocol_stratum_v1::handle_client_rejected(const code& ec, - rpc_interface::client_rejected, const std::string& , - const std::string& ) NOEXCEPT -{ - if (stopped(ec)) return false; - send_code(error::not_implemented); - return true; -} - -BC_POP_WARNING() -BC_POP_WARNING() -BC_POP_WARNING() - -} // namespace node -} // namespace libbitcoin diff --git a/src/sessions/session.cpp b/src/sessions/session.cpp index 166879247..77564e1de 100644 --- a/src/sessions/session.cpp +++ b/src/sessions/session.cpp @@ -124,9 +124,29 @@ full_node::query& session::archive() const NOEXCEPT return node_.archive(); } -const configuration& session::config() const NOEXCEPT +const node::configuration& session::node_config() const NOEXCEPT { - return node_.config(); + return node_.node_config(); +} + +const system::settings& session::system_settings() const NOEXCEPT +{ + return node_.system_settings(); +} + +const database::settings& session::database_settings() const NOEXCEPT +{ + return node_.database_settings(); +} + +////const network::settings& session::network_settings() const NOEXCEPT +////{ +//// return node_.network_settings(); +////} + +const node::settings& session::node_settings() const NOEXCEPT +{ + return node_.node_settings(); } bool session::is_current(bool confirmed) const NOEXCEPT diff --git a/src/sessions/session_inbound.cpp b/src/sessions/session_inbound.cpp index 9eeca953d..0e4ab67e8 100644 --- a/src/sessions/session_inbound.cpp +++ b/src/sessions/session_inbound.cpp @@ -25,7 +25,7 @@ namespace node { // Used instead of suspension because suspension has independent start/stop. bool session_inbound::enabled() const NOEXCEPT { - return !config().node.delay_inbound || is_recent(); + return !node_settings().delay_inbound || is_recent(); } } // namespace node diff --git a/src/settings.cpp b/src/settings.cpp index 10d0ceb26..4431e7635 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -25,59 +25,6 @@ using namespace bc::system; using namespace bc::network; namespace libbitcoin { - -// log -// ---------------------------------------------------------------------------- - -namespace log { - -// Log states default to network compiled states or explicit false. -settings::settings() NOEXCEPT - : application{ levels::application_defined }, - news{ levels::news_defined }, - session{ levels::session_defined }, - protocol{ false /*levels::protocol_defined*/ }, - proxy{ false /*levels::proxy_defined*/ }, - remote{ levels::remote_defined }, - fault{ levels::fault_defined }, - quitting{ false /*levels::quitting_defined*/ }, - objects{ false /*levels::objects_defined*/ }, - verbose{ false /*levels::verbose_defined*/ }, - maximum_size{ 1'000'000_u32 } -{ -} - -settings::settings(chain::selection) NOEXCEPT - : settings() -{ -} - -std::filesystem::path settings::log_file1() const NOEXCEPT -{ - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - return path / "bn_end.log"; - BC_POP_WARNING() -} - -std::filesystem::path settings::log_file2() const NOEXCEPT -{ - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - return path / "bn_begin.log"; - BC_POP_WARNING() -} - -std::filesystem::path settings::events_file() const NOEXCEPT -{ - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - return path / "events.log"; - BC_POP_WARNING() -} - -} // namespace log - -// node -// ---------------------------------------------------------------------------- - namespace node { settings::settings() NOEXCEPT @@ -149,46 +96,4 @@ network::memory_priority settings::memory_priority_() const NOEXCEPT } } // namespace node - -// server -// ---------------------------------------------------------------------------- - -namespace server { - -// settings::settings -settings::settings(system::chain::selection, const embedded_pages& explore, - const embedded_pages& web) NOEXCEPT - : explore("explore", explore), - web("web", web) -{ -} - -// settings::embedded_pages -span_value settings::embedded_pages::css() const NOEXCEPT { return {}; } -span_value settings::embedded_pages::html() const NOEXCEPT { return {}; } -span_value settings::embedded_pages::ecma() const NOEXCEPT { return {}; } -span_value settings::embedded_pages::font() const NOEXCEPT { return {}; } -span_value settings::embedded_pages::icon() const NOEXCEPT { return {}; } -bool settings::embedded_pages::enabled() const NOEXCEPT -{ - return !html().empty(); -} - -// settings::html_server -settings::html_server::html_server(const std::string_view& logging_name, - const embedded_pages& embedded) NOEXCEPT - : network::settings::websocket_server(logging_name), - pages(embedded) -{ -} - -bool settings::html_server::enabled() const NOEXCEPT -{ - return (!path.empty() || pages.enabled()) && http_server::enabled(); -} - -} // namespace server - -// ---------------------------------------------------------------------------- - } // namespace libbitcoin diff --git a/test/configuration.cpp b/test/configuration.cpp index 37b550359..a7ad11c4e 100644 --- a/test/configuration.cpp +++ b/test/configuration.cpp @@ -25,31 +25,12 @@ BOOST_AUTO_TEST_SUITE(configuration_tests) BOOST_AUTO_TEST_CASE(configuration__construct1__none_context__expected) { - const server::settings::embedded_pages web{}; - const server::settings::embedded_pages explorer{}; - const node::configuration instance(chain::selection::none, explorer, web); - - BOOST_REQUIRE(instance.file.empty()); - BOOST_REQUIRE(!instance.help); - BOOST_REQUIRE(!instance.hardware); - BOOST_REQUIRE(!instance.settings); - BOOST_REQUIRE(!instance.version); - BOOST_REQUIRE(!instance.newstore); - BOOST_REQUIRE(!instance.backup); - BOOST_REQUIRE(!instance.restore); - BOOST_REQUIRE(!instance.flags); - BOOST_REQUIRE(!instance.information); - BOOST_REQUIRE(!instance.slabs); - BOOST_REQUIRE(!instance.buckets); - BOOST_REQUIRE(!instance.collisions); - BOOST_REQUIRE_EQUAL(instance.test, null_hash); - BOOST_REQUIRE_EQUAL(instance.write, null_hash); + const node::configuration instance(chain::selection::none); // Just a sample of settings. BOOST_REQUIRE(instance.node.headers_first); BOOST_REQUIRE_EQUAL(instance.network.threads, 1_u32); BOOST_REQUIRE_EQUAL(instance.bitcoin.first_version, 1_u32); - BOOST_REQUIRE_EQUAL(instance.log.application, levels::application_defined); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/error.cpp b/test/error.cpp index a4e056b56..baa780177 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -225,179 +225,4 @@ BOOST_AUTO_TEST_CASE(error_t__code__confirm1__true_exected_message) BOOST_REQUIRE_EQUAL(ec.message(), "confirm1"); } -// server (url parse codes) - -BOOST_AUTO_TEST_CASE(error_t__code__empty_path__true_exected_message) -{ - constexpr auto value = error::empty_path; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "empty_path"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__invalid_number__true_exected_message) -{ - constexpr auto value = error::invalid_number; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_number"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__invalid_hash__true_exected_message) -{ - constexpr auto value = error::invalid_hash; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_hash"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__missing_version__true_exected_message) -{ - constexpr auto value = error::missing_version; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "missing_version"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__missing_target__true_exected_message) -{ - constexpr auto value = error::missing_target; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "missing_target"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__invalid_target__true_exected_message) -{ - constexpr auto value = error::invalid_target; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_target"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__missing_hash__true_exected_message) -{ - constexpr auto value = error::missing_hash; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "missing_hash"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__missing_height__true_exected_message) -{ - constexpr auto value = error::missing_height; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "missing_height"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__missing_position__true_exected_message) -{ - constexpr auto value = error::missing_position; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "missing_position"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__missing_id_type__true_exected_message) -{ - constexpr auto value = error::missing_id_type; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "missing_id_type"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__invalid_id_type__true_exected_message) -{ - constexpr auto value = error::invalid_id_type; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_id_type"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__missing_type_id__true_exected_message) -{ - constexpr auto value = error::missing_type_id; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "missing_type_id"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__missing_component__true_exected_message) -{ - constexpr auto value = error::missing_component; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "missing_component"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__invalid_component__true_exected_message) -{ - constexpr auto value = error::invalid_component; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_component"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__invalid_subcomponent__true_exected_message) -{ - constexpr auto value = error::invalid_subcomponent; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_subcomponent"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__extra_segment__true_exected_message) -{ - constexpr auto value = error::extra_segment; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "extra_segment"); -} - -// server (rpc response codes) - -BOOST_AUTO_TEST_CASE(error_t__code__not_found__true_exected_message) -{ - constexpr auto value = error::not_found; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "not_found"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__invalid_argument__true_exected_message) -{ - constexpr auto value = error::invalid_argument; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "invalid_argument"); -} - -BOOST_AUTO_TEST_CASE(error_t__code__not_implemented__true_exected_message) -{ - constexpr auto value = error::not_implemented; - const auto ec = code(value); - BOOST_REQUIRE(ec); - BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "not_implemented"); -} - BOOST_AUTO_TEST_SUITE_END() diff --git a/test/parsers/bitcoind_query.cpp b/test/parsers/bitcoind_query.cpp deleted file mode 100644 index 23d806c31..000000000 --- a/test/parsers/bitcoind_query.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../test.hpp" - -BOOST_AUTO_TEST_SUITE(bitcoind_query_tests) - -BOOST_AUTO_TEST_CASE(bitcoind_query_test) -{ - BOOST_REQUIRE(true); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/parsers/bitcoind_target.cpp b/test/parsers/bitcoind_target.cpp deleted file mode 100644 index e77d9f49c..000000000 --- a/test/parsers/bitcoind_target.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../test.hpp" - -BOOST_AUTO_TEST_SUITE(bitcoind_target_tests) - -BOOST_AUTO_TEST_CASE(bitcoind_target_test) -{ - BOOST_REQUIRE(true); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/parsers/explore_query.cpp b/test/parsers/explore_query.cpp deleted file mode 100644 index 1418e666e..000000000 --- a/test/parsers/explore_query.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../test.hpp" - -BOOST_AUTO_TEST_SUITE(explore_query_tests) - -using namespace network::http; - -BOOST_AUTO_TEST_CASE(parsers__explore_query__empty__false) -{ - network::rpc::request_t out{}; - BOOST_REQUIRE(!explore_query(out, network::http::request{})); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/parsers/explore_target.cpp b/test/parsers/explore_target.cpp deleted file mode 100644 index f8ca6af99..000000000 --- a/test/parsers/explore_target.cpp +++ /dev/null @@ -1,1289 +0,0 @@ -/** - * Copyright (c) 2011-2025 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 "../test.hpp" - -BOOST_AUTO_TEST_SUITE(explore_target_tests) - -using namespace system; -using namespace network::rpc; -using object_t = network::rpc::object_t; - -// General errors - -BOOST_AUTO_TEST_CASE(parsers__explore_target__empty_path__empty_path) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "?foo=bar"), node::error::empty_path); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__missing_version__missing_version) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/"), node::error::missing_version); - BOOST_REQUIRE_EQUAL(explore_target(out, "/block/height/123"), node::error::missing_version); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__invalid_version__invalid_number) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/vinvalid/block/height/123"), node::error::invalid_number); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__version_leading_zero__invalid_number) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v01/block/height/123"), node::error::invalid_number); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__missing_target__missing_target) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3"), node::error::missing_target); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__invalid_target__invalid_target) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/invalid"), node::error::invalid_target); -} - -// block/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_height_valid__expected) -{ - const std::string path = "/v42/block/height/123456"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_height_missing_height__missing_height) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height"), node::error::missing_height); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_height_invalid_height__invalid_number) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/invalid"), node::error::invalid_number); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_height_invalid_component__invalid_component) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/invalid"), node::error::invalid_component); -} - -// block/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_hash_valid__expected) -{ - const std::string path = "//v42//block//hash//0000000000000000000000000000000000000000000000000000000000000042//?foo=bar"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_hash_missing_hash__missing_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash"), node::error::missing_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_hash_invalid_hash__invalid_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/invalidhex"), node::error::invalid_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_hash_invalid_component__invalid_component) -{ - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_component); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_invalid_id_type__invalid_id_type) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/invalid/123"), node::error::invalid_id_type); -} - -// block_header/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_height_valid__expected) -{ - request_t request{}; - BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/header/")); - BOOST_REQUIRE_EQUAL(request.method, "block_header"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_height_invalid_subcomponent__invalid_subcomponent) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/header/invalid"), node::error::invalid_subcomponent); -} - -// block_header/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_hash_valid__expected) -{ - const std::string path = "v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/header"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block_header"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_hash_invalid_subcomponent__invalid_subcomponent) -{ - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/header/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_subcomponent); -} - -// block_header_context/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_context_height_valid__expected) -{ - request_t request{}; - BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/header/context")); - BOOST_REQUIRE_EQUAL(request.method, "block_header_context"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_context_height_extra_segment__extra_segment) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/header/context/extra"), node::error::extra_segment); -} - -// block_header_context/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_context_hash_valid__expected) -{ - const std::string path = "v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/header/context"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block_header_context"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_context_hash_extra_segment__extra_segment) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/header/context/extra"), node::error::extra_segment); -} - -// block_txs/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_txs_height_valid__expected) -{ - request_t request{}; - BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/txs")); - BOOST_REQUIRE_EQUAL(request.method, "block_txs"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_txs_height_extra_segment__extra_segment) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/txs/extra"), node::error::extra_segment); -} - -// block_txs/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_txs_hash_valid__expected) -{ - const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/txs"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block_txs"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_txs_hash_extra_segment__extra_segment) -{ - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/txs/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// block_tx/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_height_valid__expected) -{ - request_t request{}; - BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/tx/7")); - BOOST_REQUIRE_EQUAL(request.method, "block_tx"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); - - const auto position = std::get(object.at("position").value()); - BOOST_REQUIRE_EQUAL(position, 7u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_height_missing_position__missing_position) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/tx"), node::error::missing_position); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_height_invalid_position__invalid_number) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/tx/invalid"), node::error::invalid_number); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_height_extra_segment__extra_segment) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/tx/7/extra"), node::error::extra_segment); -} - -// block_tx/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_hash_valid__expected) -{ - const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/tx/7"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block_tx"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto position = std::get(object.at("position").value()); - BOOST_REQUIRE_EQUAL(position, 7u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_hash_missing_position__missing_position) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx"), node::error::missing_position); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_hash_invalid_position__invalid_number) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx/invalid"), node::error::invalid_number); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_hash_extra_segment__extra_segment) -{ - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx/7/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// tx - -BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_valid__expected) -{ - const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "tx"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_missing_hash__missing_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/tx"), node::error::missing_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__transaction_invalid_hash__invalid_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/tx/invalidhex"), node::error::invalid_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_invalid_component__invalid_component) -{ - const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_component); -} - -// tx_header - -BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_header_valid__expected) -{ - const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042/header"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "tx_header"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_header_invalid_component__invalid_component) -{ - const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_component); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_header_extra_segment__extra_segment) -{ - const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/header/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// inputs - -BOOST_AUTO_TEST_CASE(parsers__explore_target__inputs_valid__expected) -{ - const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "inputs"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__inputs_missing_hash__missing_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/input"), node::error::missing_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__inputs_invalid_hash__invalid_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/input/invalidhex"), node::error::invalid_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__inputs_invalid_number__invalid_number) -{ - const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_number); -} - -// input - -BOOST_AUTO_TEST_CASE(parsers__explore_target__input_valid__expected) -{ - const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042/3"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "input"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto index = std::get(object.at("index").value()); - BOOST_REQUIRE_EQUAL(index, 3u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__input_missing_hash__missing_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/input"), node::error::missing_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__input_invalid_hash__invalid_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/input/invalidhex/3"), node::error::invalid_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__input_invalid_number__invalid_number) -{ - const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_number); -} - -// input_script - -BOOST_AUTO_TEST_CASE(parsers__explore_target__input_script_valid__expected) -{ - const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042/3/script"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "input_script"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto index = std::get(object.at("index").value()); - BOOST_REQUIRE_EQUAL(index, 3u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__input_script_extra_segment__extra_segment) -{ - const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/3/script/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// input_witness - -BOOST_AUTO_TEST_CASE(parsers__explore_target__input_witness_valid__expected) -{ - const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042/3/witness"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "input_witness"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto index = std::get(object.at("index").value()); - BOOST_REQUIRE_EQUAL(index, 3u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__input_witness_extra_segment__extra_segment) -{ - const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/3/witness/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// outputs - -BOOST_AUTO_TEST_CASE(parsers__explore_target__outputs_valid__expected) -{ - const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "outputs"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__outputs_missing_hash__missing_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/output"), node::error::missing_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__outputs_invalid_hash__invalid_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/output/invalidhex"), node::error::invalid_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__outputs_invalid_number__invalid_number) -{ - const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_number); -} - -// output - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_valid__expected) -{ - const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/3"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "output"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto index = std::get(object.at("index").value()); - BOOST_REQUIRE_EQUAL(index, 3u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_invalid_number__invalid_number) -{ - const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_number); -} - -// output_script - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_script_valid__expected) -{ - const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/3/script"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "output_script"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto index = std::get(object.at("index").value()); - BOOST_REQUIRE_EQUAL(index, 3u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_script_invalid_subcomponent__invalid_subcomponent) -{ - const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/3/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_subcomponent); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_script_extra_segment__extra_segment) -{ - const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/3/script/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// output_spender - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_spender_valid__expected) -{ - const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/3/spender"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "output_spender"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto index = std::get(object.at("index").value()); - BOOST_REQUIRE_EQUAL(index, 3u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_spender_extra_segment__extra_segment) -{ - const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/3/spender/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// output_spenders - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_spenders_valid__expected) -{ - const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/1/spenders"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "output_spenders"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto index = std::get(object.at("index").value()); - BOOST_REQUIRE_EQUAL(index, 1u); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__output_spenders_extra_segment__extra_segment) -{ - const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/1/spenders/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// address - -BOOST_AUTO_TEST_CASE(parsers__explore_target__address_valid__reversed_expected) -{ - const std::string path = "/v255/address/0000000000000000000000000000000000000000000000000000000000000042"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "address"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 255u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__address_missing_hash__missing_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/address"), node::error::missing_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__address_invalid_hash__invalid_hash) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/address/invalidhex"), node::error::invalid_hash); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__address_invalid_subcomponent__invalid_subcomponent) -{ - const std::string path = "/v3/address/0000000000000000000000000000000000000000000000000000000000000000/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_subcomponent); -} - -// TODO: -// address/confirmed -// address/unconfirmed -// address/balance - -// block_filter/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_height_valid__expected) -{ - request_t request{}; - BOOST_REQUIRE(!explore_target(request, "v42/block/height/123456/filter/255")); - BOOST_REQUIRE_EQUAL(request.method, "block_filter"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); - - const auto type = std::get(object.at("type").value()); - BOOST_REQUIRE_EQUAL(type, 255u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_height_invalid_subcomponent__invalid_subcomponent) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/42/invalid"), node::error::invalid_subcomponent); -} - -// block_filter/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_valid__expected) -{ - const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block_filter"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto type = std::get(object.at("type").value()); - BOOST_REQUIRE_EQUAL(type, 255u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_invalid_subcomponent__invalid_subcomponent) -{ - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::invalid_subcomponent); -} - -// block_filter_hash/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_height_valid__expected) -{ - request_t request{}; - BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/filter/255/hash")); - BOOST_REQUIRE_EQUAL(request.method, "block_filter_hash"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); - - const auto type = std::get(object.at("type").value()); - BOOST_REQUIRE_EQUAL(type, 255u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_height_extra_segment__extra_segment) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/42/hash/extra"), node::error::extra_segment); -} - -// block_filter_hash/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_hash_valid__expected) -{ - const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255/hash"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block_filter_hash"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto type = std::get(object.at("type").value()); - BOOST_REQUIRE_EQUAL(type, 255u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_hash_extra_segment__extra_segment) -{ - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/hash/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// block_filter_header/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_header_height_valid__expected) -{ - request_t request{}; - BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/filter/255/header")); - BOOST_REQUIRE_EQUAL(request.method, "block_filter_header"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); - - const auto type = std::get(object.at("type").value()); - BOOST_REQUIRE_EQUAL(type, 255u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_header_height_extra_segment__extra_segment) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/42/header/extra"), node::error::extra_segment); -} - -// block_filter_header/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_header_hash_valid__expected) -{ - const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255/header"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block_filter_header"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 3u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); - - const auto type = std::get(object.at("type").value()); - BOOST_REQUIRE_EQUAL(type, 255u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_header_hash_extra_segment__extra_segment) -{ - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/header/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_missing_type_id__missing_type_id) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter"), node::error::missing_type_id); - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter"), node::error::missing_type_id); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_invalid_type__invalid_number) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/invalid"), node::error::invalid_number); - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/invalid"), node::error::invalid_number); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_invalid_subcomponent__invalid_subcomponent) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/42/invalid"), node::error::invalid_subcomponent); - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/invalid"), node::error::invalid_subcomponent); -} - -// tx_details - -BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_details_valid__expected) -{ - const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042/details"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "tx_details"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_details_extra_segment__extra_segment) -{ - const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/details/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - -// block_details/height - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_details_height_valid__expected) -{ - request_t request{}; - BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/details")); - BOOST_REQUIRE_EQUAL(request.method, "block_details"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto height = std::get(object.at("height").value()); - BOOST_REQUIRE_EQUAL(height, 123456u); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_details_height_extra_segment__extra_segment) -{ - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/details/extra"), node::error::extra_segment); -} - -// block_details/hash - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_details_hash_valid__expected) -{ - const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/details"; - - request_t request{}; - BOOST_REQUIRE(!explore_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "block_details"); - BOOST_REQUIRE(request.params.has_value()); - - const auto& params = request.params.value(); - BOOST_REQUIRE(std::holds_alternative(params)); - - const auto& object = std::get(request.params.value()); - BOOST_REQUIRE_EQUAL(object.size(), 2u); - - const auto version = std::get(object.at("version").value()); - BOOST_REQUIRE_EQUAL(version, 42u); - - const auto& any = std::get(object.at("hash").value()); - BOOST_REQUIRE(any.holds_alternative()); - - const auto& hash_cptr = any.get(); - BOOST_REQUIRE(hash_cptr); - BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -} - -BOOST_AUTO_TEST_CASE(parsers__explore_target__block_details_hash_extra_segment__extra_segment) -{ - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/details/extra"; - request_t out{}; - BOOST_REQUIRE_EQUAL(explore_target(out, path), node::error::extra_segment); -} - - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/settings.cpp b/test/settings.cpp index 8bb4e1a75..fac0b17d7 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -23,31 +23,6 @@ BOOST_AUTO_TEST_SUITE(settings_tests) using namespace bc::network; using namespace bc::system::chain; -// [log] - -BOOST_AUTO_TEST_CASE(settings__log__default_context__expected) -{ - const log::settings log{}; - BOOST_REQUIRE_EQUAL(log.application, levels::application_defined); - BOOST_REQUIRE_EQUAL(log.news, levels::news_defined); - BOOST_REQUIRE_EQUAL(log.session, levels::session_defined); - BOOST_REQUIRE_EQUAL(log.protocol, false /*levels::protocol_defined*/); - BOOST_REQUIRE_EQUAL(log.proxy, false /*levels::proxy_defined*/); - BOOST_REQUIRE_EQUAL(log.remote, levels::remote_defined); - BOOST_REQUIRE_EQUAL(log.fault, levels::fault_defined); - BOOST_REQUIRE_EQUAL(log.quitting, false /*levels::quitting_defined*/); - BOOST_REQUIRE_EQUAL(log.objects, false /*levels::objects_defined*/); - BOOST_REQUIRE_EQUAL(log.verbose, false /*levels::verbose_defined*/); - BOOST_REQUIRE_EQUAL(log.maximum_size, 1'000'000_u32); - BOOST_REQUIRE_EQUAL(log.path, ""); - BOOST_REQUIRE_EQUAL(log.log_file1(), "bn_end.log"); - BOOST_REQUIRE_EQUAL(log.log_file2(), "bn_begin.log"); - BOOST_REQUIRE_EQUAL(log.events_file(), "events.log"); -#if defined(HAVE_MSC) - BOOST_REQUIRE_EQUAL(log.symbols, ""); -#endif -} - // [node] BOOST_AUTO_TEST_CASE(settings__node__default_context__expected) @@ -83,192 +58,4 @@ BOOST_AUTO_TEST_CASE(settings__node__default_context__expected) BOOST_REQUIRE(node.memory_priority_() == network::memory_priority::highest); } -// [server] -// ---------------------------------------------------------------------------- - -BOOST_AUTO_TEST_CASE(server__html_server__defaults__expected) -{ - const auto undefined = server::settings::embedded_pages{}; - const server::settings::html_server instance{ "test", undefined }; - - // tcp_server - BOOST_REQUIRE_EQUAL(instance.name, "test"); - BOOST_REQUIRE(!instance.secure); - BOOST_REQUIRE(instance.binds.empty()); - BOOST_REQUIRE_EQUAL(instance.connections, 0u); - BOOST_REQUIRE_EQUAL(instance.inactivity_minutes, 10u); - BOOST_REQUIRE_EQUAL(instance.expiration_minutes, 60u); - BOOST_REQUIRE(!instance.enabled()); - BOOST_REQUIRE(instance.inactivity() == minutes(10)); - BOOST_REQUIRE(instance.expiration() == minutes(60)); - - // http_server - BOOST_REQUIRE_EQUAL(instance.server, "libbitcoin/4.0"); - BOOST_REQUIRE(instance.hosts.empty()); - BOOST_REQUIRE(instance.host_names().empty()); - - // html_server - BOOST_REQUIRE(!instance.pages.enabled()); - BOOST_REQUIRE(instance.pages.css().empty()); - BOOST_REQUIRE(instance.pages.html().empty()); - BOOST_REQUIRE(instance.pages.ecma().empty()); - BOOST_REQUIRE(instance.pages.font().empty()); - BOOST_REQUIRE(instance.pages.icon().empty()); - BOOST_REQUIRE(instance.websocket); - BOOST_REQUIRE(instance.path.empty()); - BOOST_REQUIRE_EQUAL(instance.default_, "index.html"); -} - -BOOST_AUTO_TEST_CASE(server__web_server__defaults__expected) -{ - const server::settings::embedded_pages web{}; - const server::settings::embedded_pages explorer{}; - const server::settings instance{ selection::none, explorer, web }; - const auto& server = instance.web; - - // tcp_server - BOOST_REQUIRE_EQUAL(server.name, "web"); - BOOST_REQUIRE(!server.secure); - BOOST_REQUIRE(server.binds.empty()); - BOOST_REQUIRE_EQUAL(server.connections, 0u); - BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); - BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); - BOOST_REQUIRE(!server.enabled()); - BOOST_REQUIRE(server.inactivity() == minutes(10)); - BOOST_REQUIRE(server.expiration() == minutes(60)); - - // http_server - BOOST_REQUIRE_EQUAL(server.server, "libbitcoin/4.0"); - BOOST_REQUIRE(server.hosts.empty()); - BOOST_REQUIRE(server.host_names().empty()); - - // html_server - BOOST_REQUIRE(!server.pages.enabled()); - BOOST_REQUIRE(server.pages.css().empty()); - BOOST_REQUIRE(server.pages.html().empty()); - BOOST_REQUIRE(server.pages.ecma().empty()); - BOOST_REQUIRE(server.pages.font().empty()); - BOOST_REQUIRE(server.pages.icon().empty()); - BOOST_REQUIRE(server.path.empty()); - BOOST_REQUIRE(server.websocket); - BOOST_REQUIRE_EQUAL(server.default_, "index.html"); -} - -BOOST_AUTO_TEST_CASE(server__explore_server__defaults__expected) -{ - const server::settings::embedded_pages web{}; - const server::settings::embedded_pages explorer{}; - const server::settings instance{ selection::none, explorer, web }; - const auto& server = instance.explore; - - // tcp_server - BOOST_REQUIRE_EQUAL(server.name, "explore"); - BOOST_REQUIRE(!server.secure); - BOOST_REQUIRE(server.binds.empty()); - BOOST_REQUIRE_EQUAL(server.connections, 0u); - BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); - BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); - BOOST_REQUIRE(!server.enabled()); - BOOST_REQUIRE(server.inactivity() == minutes(10)); - BOOST_REQUIRE(server.expiration() == minutes(60)); - - // http_server - BOOST_REQUIRE_EQUAL(server.server, "libbitcoin/4.0"); - BOOST_REQUIRE(server.hosts.empty()); - BOOST_REQUIRE(server.host_names().empty()); - - // html_server - BOOST_REQUIRE(!server.pages.enabled()); - BOOST_REQUIRE(server.pages.css().empty()); - BOOST_REQUIRE(server.pages.html().empty()); - BOOST_REQUIRE(server.pages.ecma().empty()); - BOOST_REQUIRE(server.pages.font().empty()); - BOOST_REQUIRE(server.pages.icon().empty()); - BOOST_REQUIRE(server.path.empty()); - BOOST_REQUIRE(server.websocket); - BOOST_REQUIRE_EQUAL(server.default_, "index.html"); -} - -// TODO: could add websocket under bitcoind as a custom property. -BOOST_AUTO_TEST_CASE(server__bitcoind_server__defaults__expected) -{ - const server::settings::embedded_pages web{}; - const server::settings::embedded_pages explorer{}; - const server::settings instance{ selection::none, explorer, web }; - const auto& server = instance.bitcoind; - - // tcp_server - BOOST_REQUIRE_EQUAL(server.name, "bitcoind"); - BOOST_REQUIRE(!server.secure); - BOOST_REQUIRE(server.binds.empty()); - BOOST_REQUIRE_EQUAL(server.connections, 0u); - BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); - BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); - BOOST_REQUIRE(!server.enabled()); - BOOST_REQUIRE(server.inactivity() == minutes(10)); - BOOST_REQUIRE(server.expiration() == minutes(60)); - - // http_server - BOOST_REQUIRE_EQUAL(server.server, "libbitcoin/4.0"); - BOOST_REQUIRE(server.hosts.empty()); - BOOST_REQUIRE(server.host_names().empty()); -} - -BOOST_AUTO_TEST_CASE(server__electrum_server__defaults__expected) -{ - const server::settings::embedded_pages web{}; - const server::settings::embedded_pages explorer{}; - const server::settings instance{ selection::none, explorer, web }; - const auto& server = instance.electrum; - - // tcp_server - BOOST_REQUIRE_EQUAL(server.name, "electrum"); - BOOST_REQUIRE(!server.secure); - BOOST_REQUIRE(server.binds.empty()); - BOOST_REQUIRE_EQUAL(server.connections, 0u); - BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); - BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); - BOOST_REQUIRE(!server.enabled()); - BOOST_REQUIRE(server.inactivity() == minutes(10)); - BOOST_REQUIRE(server.expiration() == minutes(60)); -} - -BOOST_AUTO_TEST_CASE(server__stratum_v1_server__defaults__expected) -{ - const server::settings::embedded_pages web{}; - const server::settings::embedded_pages explorer{}; - const server::settings instance{ selection::none, explorer, web }; - const auto& server = instance.stratum_v1; - - // tcp_server - BOOST_REQUIRE_EQUAL(server.name, "stratum_v1"); - BOOST_REQUIRE(!server.secure); - BOOST_REQUIRE(server.binds.empty()); - BOOST_REQUIRE_EQUAL(server.connections, 0u); - BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); - BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); - BOOST_REQUIRE(!server.enabled()); - BOOST_REQUIRE(server.inactivity() == minutes(10)); - BOOST_REQUIRE(server.expiration() == minutes(60)); -} - -BOOST_AUTO_TEST_CASE(server__stratum_v2_server__defaults__expected) -{ - const server::settings::embedded_pages web{}; - const server::settings::embedded_pages explorer{}; - const server::settings instance{ selection::none, explorer, web }; - const auto& server = instance.stratum_v2; - - // tcp_server - BOOST_REQUIRE_EQUAL(server.name, "stratum_v2"); - BOOST_REQUIRE(!server.secure); - BOOST_REQUIRE(server.binds.empty()); - BOOST_REQUIRE_EQUAL(server.connections, 0u); - BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); - BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); - BOOST_REQUIRE(!server.enabled()); - BOOST_REQUIRE(server.inactivity() == minutes(10)); - BOOST_REQUIRE(server.expiration() == minutes(60)); -} - BOOST_AUTO_TEST_SUITE_END()