diff --git a/data/bn.cfg b/data/bn.cfg index 7741b7934..083371193 100644 --- a/data/bn.cfg +++ b/data/bn.cfg @@ -27,8 +27,8 @@ threads = 0 protocol_maximum = 70013 # The minimum network protocol version, defaults to 31402. protocol_minimum = 31402 -# The services exposed by network connections, defaults to 1 (full node). -services = 1 +# The services exposed by network connections, defaults to 9 (full node, witness). +services = 9 # The advertised services that cause a peer to be dropped, defaults to 0 (none). invalid_services = 0 # The magic number for message headers, defaults to 3652501241 (use 118034699 for testnet). @@ -156,6 +156,12 @@ bip68 = true bip112 = true # Use median time past for locktime, defaults to true (soft fork). bip113 = true +# Segregated witness consensus layer, defaults to true (soft fork). +bip141 = true +# Version 0 transaction digest, defaults to true (soft fork). +bip143 = true +# Prevent dummy value malleability, defaults to true (soft fork). +bip147 = true [node] # The time to wait for a requested block, defaults to 60. diff --git a/include/bitcoin/node/protocols/protocol_block_in.hpp b/include/bitcoin/node/protocols/protocol_block_in.hpp index b0b49c78e..b88382ab9 100644 --- a/include/bitcoin/node/protocols/protocol_block_in.hpp +++ b/include/bitcoin/node/protocols/protocol_block_in.hpp @@ -70,6 +70,8 @@ class BCN_API protocol_block_in const asio::duration block_latency_; const bool headers_from_peer_; const bool blocks_from_peer_; + const bool require_witness_; + const bool peer_witness_; // This is protected by mutex. hash_queue backlog_; diff --git a/include/bitcoin/node/protocols/protocol_block_out.hpp b/include/bitcoin/node/protocols/protocol_block_out.hpp index d2f8be26a..266d00caa 100644 --- a/include/bitcoin/node/protocols/protocol_block_out.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out.hpp @@ -50,11 +50,11 @@ class BCN_API protocol_block_out void send_next_data(inventory_ptr inventory); void send_block(const code& ec, block_const_ptr message, - uint64_t height, inventory_ptr inventory); + size_t height, inventory_ptr inventory); void send_merkle_block(const code& ec, merkle_block_const_ptr message, - uint64_t height, inventory_ptr inventory); + size_t height, inventory_ptr inventory); void send_compact_block(const code& ec, compact_block_const_ptr message, - uint64_t height, inventory_ptr inventory); + size_t height, inventory_ptr inventory); bool handle_receive_get_data(const code& ec, get_data_const_ptr message); @@ -76,11 +76,13 @@ class BCN_API protocol_block_out block_const_ptr_list_const_ptr incoming, block_const_ptr_list_const_ptr outgoing); + // These are thread safe. full_node& node_; blockchain::safe_chain& chain_; bc::atomic last_locator_top_; std::atomic compact_to_peer_; std::atomic headers_to_peer_; + const bool enable_witness_; }; } // namespace node diff --git a/include/bitcoin/node/protocols/protocol_transaction_in.hpp b/include/bitcoin/node/protocols/protocol_transaction_in.hpp index e75b0dfaf..e243e96e9 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_in.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_in.hpp @@ -55,10 +55,13 @@ class BCN_API protocol_transaction_in void handle_stop(const code&); + // These are thread safe. blockchain::safe_chain& chain_; + const uint64_t minimum_relay_fee_; const bool relay_from_peer_; const bool refresh_pool_; - const uint64_t minimum_relay_fee_; + const bool require_witness_; + const bool peer_witness_; }; } // namespace node diff --git a/include/bitcoin/node/protocols/protocol_transaction_out.hpp b/include/bitcoin/node/protocols/protocol_transaction_out.hpp index b73b51f8e..e36e994e3 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_out.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_out.hpp @@ -46,8 +46,8 @@ class BCN_API protocol_transaction_out private: void send_next_data(inventory_ptr inventory); - void send_transaction(const code& ec, transaction_const_ptr transaction, - size_t height, size_t position, inventory_ptr inventory); + void send_transaction(const code& ec, transaction_const_ptr message, + size_t position, size_t height, inventory_ptr inventory); bool handle_receive_get_data(const code& ec, get_data_const_ptr message); @@ -63,10 +63,12 @@ class BCN_API protocol_transaction_out bool handle_transaction_pool(const code& ec, transaction_const_ptr message); + // These are thread safe. blockchain::safe_chain& chain_; std::atomic minimum_peer_fee_; ////std::atomic compact_to_peer_; const bool relay_to_peer_; + const bool enable_witness_; }; } // namespace node diff --git a/src/parser.cpp b/src/parser.cpp index 43713d664..f1af30e21 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -49,6 +49,8 @@ parser::parser(const configuration& defaults) parser::parser(config::settings context) : configured(context) { + using serve = message::version::service; + // A node doesn't use history, and history is expensive. configured.database.index_start_height = max_uint32; @@ -61,8 +63,8 @@ parser::parser(config::settings context) // A node allows 1000 host names by default. configured.network.host_pool_capacity = 1000; - // A node exposes full node (1) network services by default. - configured.network.services = message::version::service::node_network; + // Expose full node (1) and witness (8) network services by default. + configured.network.services = serve::node_network | serve::node_witness; } options_metadata parser::load_options() @@ -195,7 +197,7 @@ options_metadata parser::load_settings() ( "network.services", value(&configured.network.services), - "The services exposed by network connections, defaults to 1 (full node)." + "The services exposed by network connections, defaults to 9 (full node, witness)." ) ( "network.invalid_services", @@ -413,6 +415,21 @@ options_metadata parser::load_settings() value(&configured.chain.bip113), "Use median time past for locktime, defaults to true (soft fork)." ) + ( + "fork.bip141", + value(&configured.chain.bip141), + "Segregated witness consensus layer, defaults to true (soft fork)." + ) + ( + "fork.bip143", + value(&configured.chain.bip143), + "Version 0 transaction digest, defaults to true (soft fork)." + ) + ( + "fork.bip147", + value(&configured.chain.bip147), + "Prevent dummy value malleability, defaults to true (soft fork)." + ) /* [node] */ ////( diff --git a/src/protocols/protocol_block_in.cpp b/src/protocols/protocol_block_in.cpp index 3a3ea5db0..8459edd3a 100644 --- a/src/protocols/protocol_block_in.cpp +++ b/src/protocols/protocol_block_in.cpp @@ -44,6 +44,11 @@ using namespace bc::network; using namespace std::chrono; using namespace std::placeholders; +inline bool is_witness(uint64_t services) +{ + return (services & version::service::node_witness) != 0; +} + protocol_block_in::protocol_block_in(full_node& node, channel::ptr channel, safe_chain& chain) : protocol_timer(node, channel, false, NAME), @@ -58,6 +63,10 @@ protocol_block_in::protocol_block_in(full_node& node, channel::ptr channel, blocks_from_peer_( negotiated_version() > version::level::no_blocks_end || negotiated_version() < version::level::no_blocks_start), + + // Witness must be requested if possibly enforced. + require_witness_(is_witness(node.network_settings().services)), + peer_witness_(is_witness(channel->peer_version()->services())), CONSTRUCT_TRACK(protocol_block_in) { } @@ -70,6 +79,11 @@ void protocol_block_in::start() // Use timer to drop slow peers. protocol_timer::start(block_latency_, BIND1(handle_timeout, _1)); + // Do not process incoming blocks if required witness is unavailable. + // The channel will remain active outbound unless node becomes stale. + if (require_witness_ && !peer_witness_) + return; + // TODO: move headers to a derived class protocol_block_in_31800. SUBSCRIBE2(headers, handle_receive_headers, _1, _2); @@ -225,6 +239,10 @@ void protocol_block_in::send_get_data(const code& ec, get_data_ptr message) mutex.unlock(); /////////////////////////////////////////////////////////////////////////// + // Convert requested message types to corresponding witness types. + if (require_witness_) + message->to_witness(); + // There was no backlog so the timer must be started now. if (fresh) reset_timer(); @@ -312,6 +330,15 @@ bool protocol_block_in::handle_receive_block(const code& ec, return false; } + if (!require_witness_ && message->is_segregated()) + { + LOG_DEBUG(LOG_NODE) + << "Block [" << encode_hash(message->hash()) + << "] contains unrequested witness from [" << authority() << "]"; + stop(error::channel_stopped); + return false; + } + message->validation.originator = nonce(); chain_.organize(message, BIND2(handle_store_block, _1, message)); @@ -432,6 +459,9 @@ void protocol_block_in::handle_timeout(const code& ec) // an announcement. There is no sense pinging a broken peer, so we either // drop the peer after a certain mount of time (above 10 minutes) or rely // on other peers to keep us moving and periodically age out connections. + // Note that this allows a non-witness peer to hang on indefinately to our + // witness-requiring node until the node becomes stale. Allowing this then + // depends on requiring witness peers for explicitly outbound connections. } void protocol_block_in::handle_stop(const code&) diff --git a/src/protocols/protocol_block_out.cpp b/src/protocols/protocol_block_out.cpp index a1c684c71..018b263e1 100644 --- a/src/protocols/protocol_block_out.cpp +++ b/src/protocols/protocol_block_out.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -38,8 +39,14 @@ namespace node { using namespace bc::blockchain; using namespace bc::message; using namespace bc::network; +using namespace boost::adaptors; using namespace std::placeholders; +inline bool is_witness(uint64_t services) +{ + return (services & version::service::node_witness) != 0; +} + protocol_block_out::protocol_block_out(full_node& node, channel::ptr channel, safe_chain& chain) : protocol_events(node, channel, NAME), @@ -53,6 +60,8 @@ protocol_block_out::protocol_block_out(full_node& node, channel::ptr channel, // TODO: move send_headers to a derived class protocol_block_out_70012. headers_to_peer_(false), + // Witness requests must be allowed if advertising the service. + enable_witness_(is_witness(node.network_settings().services)), CONSTRUCT_TRACK(protocol_block_out) { } @@ -276,7 +285,6 @@ bool protocol_block_out::handle_receive_get_data(const code& ec, //// return true; // Create a copy because message is const because it is shared. - const auto& inventories = message->inventories(); const auto response = std::make_shared(); // TODO: convert all compact_block elements to block unless block is, @@ -285,9 +293,9 @@ bool protocol_block_out::handle_receive_get_data(const code& ec, // Peer may request compact only after receipt of a send_compact message. // Reverse copy the block elements of the const inventory. - for (auto it = inventories.rbegin(); it != inventories.rend(); ++it) - if (it->is_block_type()) - response->inventories().push_back(*it); + for (const auto inventory: reverse(message->inventories())) + if (inventory.is_block_type()) + response->inventories().push_back(inventory); send_next_data(response); return true; @@ -303,9 +311,21 @@ void protocol_block_out::send_next_data(inventory_ptr inventory) switch (entry.type()) { + case inventory::type_id::witness_block: + { + if (!enable_witness_) + { + stop(error::channel_stopped); + return; + } + + chain_.fetch_block(entry.hash(), true, + BIND4(send_block, _1, _2, _3, inventory)); + break; + } case inventory::type_id::block: { - chain_.fetch_block(entry.hash(), + chain_.fetch_block(entry.hash(), false, BIND4(send_block, _1, _2, _3, inventory)); break; } @@ -329,7 +349,7 @@ void protocol_block_out::send_next_data(inventory_ptr inventory) } void protocol_block_out::send_block(const code& ec, block_const_ptr message, - uint64_t, inventory_ptr inventory) + size_t, inventory_ptr inventory) { if (stopped(ec)) return; @@ -361,7 +381,7 @@ void protocol_block_out::send_block(const code& ec, block_const_ptr message, // TODO: move merkle_block to derived class protocol_block_out_70001. void protocol_block_out::send_merkle_block(const code& ec, - merkle_block_const_ptr message, uint64_t, inventory_ptr inventory) + merkle_block_const_ptr message, size_t, inventory_ptr inventory) { if (stopped(ec)) return; @@ -393,7 +413,7 @@ void protocol_block_out::send_merkle_block(const code& ec, // TODO: move merkle_block to derived class protocol_block_out_70014. void protocol_block_out::send_compact_block(const code& ec, - compact_block_const_ptr message, uint64_t, inventory_ptr inventory) + compact_block_const_ptr message, size_t, inventory_ptr inventory) { if (stopped(ec)) return; diff --git a/src/protocols/protocol_transaction_in.cpp b/src/protocols/protocol_transaction_in.cpp index 5a7111ead..46fc0ddee 100644 --- a/src/protocols/protocol_transaction_in.cpp +++ b/src/protocols/protocol_transaction_in.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -38,7 +39,12 @@ using namespace bc::message; using namespace bc::network; using namespace std::placeholders; -uint64_t to_relay_fee(float minimum_byte_fee) +inline bool is_witness(uint64_t services) +{ + return (services & version::service::node_witness) != 0; +} + +inline uint64_t to_relay_fee(float minimum_byte_fee) { // Spending one standard prevout with one output is nominally 189 bytes. static const size_t small_transaction_size = 189; @@ -50,6 +56,10 @@ protocol_transaction_in::protocol_transaction_in(full_node& node, : protocol_events(node, channel, NAME), chain_(chain), + // TODO: move fee_filter to a derived class protocol_transaction_in_70013. + minimum_relay_fee_(negotiated_version() >= version::level::bip133 ? + to_relay_fee(node.chain_settings().byte_fee_satoshis) : 0), + // TODO: move relay to a derived class protocol_transaction_in_70001. // In the mean time, restrict by negotiated protocol level. // Because of inconsistent implementation by version we must allow relay @@ -61,9 +71,9 @@ protocol_transaction_in::protocol_transaction_in(full_node& node, refresh_pool_(negotiated_version() >= version::level::bip35 && node.node_settings().refresh_transactions), - // TODO: move fee_filter to a derived class protocol_transaction_in_70013. - minimum_relay_fee_(negotiated_version() >= version::level::bip133 ? - to_relay_fee(node.chain_settings().byte_fee_satoshis) : 0), + // Witness must be requested if possibly enforced. + require_witness_(is_witness(node.network_settings().services)), + peer_witness_(is_witness(channel->peer_version()->services())), CONSTRUCT_TRACK(protocol_transaction_in) { } @@ -73,6 +83,11 @@ protocol_transaction_in::protocol_transaction_in(full_node& node, void protocol_transaction_in::start() { + // Do not process incoming transactions if required witness is unavailable. + // The channel will remain active outbound unless node becomes stale. + if (require_witness_ && !peer_witness_) + return; + protocol_events::start(BIND1(handle_stop, _1)); SUBSCRIBE2(inventory, handle_receive_inventory, _1, _2); @@ -144,6 +159,10 @@ void protocol_transaction_in::send_get_data(const code& ec, return; } + // Convert requested message types to corresponding witness types. + if (require_witness_) + message->to_witness(); + // inventory->get_data[transaction] SEND2(*message, handle_send, _1, message->command); } @@ -168,6 +187,15 @@ bool protocol_transaction_in::handle_receive_transaction(const code& ec, return false; } + if (!require_witness_ && message->is_segregated()) + { + LOG_DEBUG(LOG_NODE) + << "Transaction [" << encode_hash(message->hash()) + << "] contains unrequested witness from [" << authority() << "]"; + stop(error::channel_stopped); + return false; + } + // TODO: manage channel relay at the service layer. // Do not process transactions while chain is stale. if (chain_.is_stale()) diff --git a/src/protocols/protocol_transaction_out.cpp b/src/protocols/protocol_transaction_out.cpp index 6857cdacb..792fb0cfc 100644 --- a/src/protocols/protocol_transaction_out.cpp +++ b/src/protocols/protocol_transaction_out.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -36,8 +37,14 @@ using namespace bc::chain; using namespace bc::database; using namespace bc::message; using namespace bc::network; +using namespace boost::adaptors; using namespace std::placeholders; +inline bool is_witness(uint64_t services) +{ + return (services & version::service::node_witness) != 0; +} + protocol_transaction_out::protocol_transaction_out(full_node& network, channel::ptr channel, safe_chain& chain) : protocol_events(network, channel, NAME), @@ -48,6 +55,9 @@ protocol_transaction_out::protocol_transaction_out(full_node& network, // TODO: move relay to a derived class protocol_transaction_out_70001. relay_to_peer_(peer_version()->relay()), + + // Witness requests must be allowed if advertising the service. + enable_witness_(is_witness(network.network_settings().services)), CONSTRUCT_TRACK(protocol_transaction_out) { } @@ -148,13 +158,12 @@ bool protocol_transaction_out::handle_receive_get_data(const code& ec, } // Create a copy because message is const because it is shared. - const auto& inventories = message->inventories(); const auto response = std::make_shared(); // Reverse copy the transaction elements of the const inventory. - for (auto it = inventories.rbegin(); it != inventories.rend(); ++it) - if (it->is_transaction_type()) - response->inventories().push_back(*it); + for (const auto inventory: reverse(message->inventories())) + if (inventory.is_transaction_type()) + response->inventories().push_back(inventory); send_next_data(response); return true; @@ -168,15 +177,36 @@ void protocol_transaction_out::send_next_data(inventory_ptr inventory) // The order is reversed so that we can pop from the back. const auto& entry = inventory->inventories().back(); - // This allows confirmed and unconfirmed transactions and will return the - // first match of either that it finds (by hash). - chain_.fetch_transaction(entry.hash(), false, - BIND5(send_transaction, _1, _2, _3, _4, inventory)); + switch (entry.type()) + { + case inventory::type_id::witness_transaction: + { + if (!enable_witness_) + { + stop(error::channel_stopped); + return; + } + + chain_.fetch_transaction(entry.hash(), false, true, + BIND5(send_transaction, _1, _2, _3, _4, inventory)); + break; + } + case inventory::type_id::transaction: + { + chain_.fetch_transaction(entry.hash(), false, false, + BIND5(send_transaction, _1, _2, _3, _4, inventory)); + break; + } + default: + { + BITCOIN_ASSERT_MSG(false, "improperly-filtered inventory"); + } + } } // TODO: send block_transaction message as applicable. void protocol_transaction_out::send_transaction(const code& ec, - transaction_const_ptr transaction, size_t position, size_t /*height*/, + transaction_const_ptr message, size_t position, size_t /*height*/, inventory_ptr inventory) { if (stopped(ec)) @@ -207,7 +237,7 @@ void protocol_transaction_out::send_transaction(const code& ec, return; } - SEND2(*transaction, handle_send_next, _1, inventory); + SEND2(*message, handle_send_next, _1, inventory); } void protocol_transaction_out::handle_send_next(const code& ec,