diff --git a/doc/release-notes-dogmode-user-agent.md b/doc/release-notes-dogmode-user-agent.md new file mode 100644 index 000000000000..0ee194f511b7 --- /dev/null +++ b/doc/release-notes-dogmode-user-agent.md @@ -0,0 +1,12 @@ +$DOG Mode user agent +==================== + +P2P +--- + +- The user agent names $DOG Mode after Bitcoin Core's own component, for + example `/Satoshi:31.1.0/DOGMode:20260902/`, the way Bitcoin Knots names + itself. A peer, a DNS seed's crawler, `getpeerinfo` and `getnetworkinfo` can + now tell a $DOG Mode node, and which release it runs, from plain Bitcoin + Core. The Bitcoin Core component is unchanged, and `-uacomment` comments + still attach to it. diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 86610112ba0a..5c6957768de8 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -23,6 +23,15 @@ using util::Join; */ const std::string UA_NAME("Satoshi"); +/** + * $DOG Mode's name and version, reported in the 'version' message as a second + * BIP 14 component after the Bitcoin Core one above, the way Bitcoin Knots + * reports itself: "/Satoshi:31.1.0/DOGMode:20260902/". The Bitcoin Core + * component stays first and unchanged. The version is a date (YYYYMMDD), as + * BIP 14 recommends; set it to the release date when a release is tagged. + */ +const std::string UA_DOGMODE("DOGMode:20260902"); + #include // The , which is generated by the build environment (cmake/script/GenerateBuildInfo.cmake), diff --git a/src/clientversion.h b/src/clientversion.h index 9d09e502accc..727e27e8cc09 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -29,6 +29,7 @@ static const int CLIENT_VERSION = + 1 * CLIENT_VERSION_BUILD; extern const std::string UA_NAME; +extern const std::string UA_DOGMODE; std::string FormatFullVersion(); diff --git a/src/init.cpp b/src/init.cpp index 2d6915d00096..98a9ebc242fc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1679,7 +1679,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); uacomments.push_back(cmt); } - strSubVersion = FormatSubVersion(UA_NAME, CLIENT_VERSION, uacomments); + strSubVersion = FormatSubVersion(UA_NAME, CLIENT_VERSION, uacomments) + UA_DOGMODE + "/"; if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."), strSubVersion.size(), MAX_SUBVERSION_LENGTH)); diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py index 2ebb01fde438..f7732d53edc2 100755 --- a/test/functional/feature_includeconf.py +++ b/test/functional/feature_includeconf.py @@ -33,10 +33,10 @@ def run_test(self): f.write("uacomment=main\nincludeconf=relative.conf\n") self.restart_node(0) - self.log.info("-includeconf works from config file. subversion should end with 'main; relative)/'") + self.log.info("-includeconf works from config file. subversion should contain 'main; relative)/'") subversion = self.nodes[0].getnetworkinfo()["subversion"] - assert subversion.endswith("main; relative)/") + assert "main; relative)/" in subversion self.log.info("-includeconf cannot be used as command-line arg") self.stop_node(0) @@ -49,13 +49,13 @@ def run_test(self): expected_msg='Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf="relative2.conf"', ) - self.log.info("-includeconf cannot be used recursively. subversion should end with 'main; relative)/'") + self.log.info("-includeconf cannot be used recursively. subversion should contain 'main; relative)/'") with open(self.nodes[0].datadir_path / "relative.conf", "a") as f: f.write("includeconf=relative2.conf\n") self.start_node(0) subversion = self.nodes[0].getnetworkinfo()["subversion"] - assert subversion.endswith("main; relative)/") + assert "main; relative)/" in subversion self.stop_node(0, expected_stderr="warning: -includeconf cannot be used from included files; ignoring -includeconf=relative2.conf") self.log.info("-includeconf cannot contain invalid arg") @@ -69,7 +69,7 @@ def run_test(self): (self.nodes[0].datadir_path / "relative.conf").unlink() self.nodes[0].assert_start_raises_init_error(expected_msg="Error: Error reading configuration file: Failed to include configuration file relative.conf") - self.log.info("multiple -includeconf args can be used from the base config file. subversion should end with 'main; relative; relative2)/'") + self.log.info("multiple -includeconf args can be used from the base config file. subversion should contain 'main; relative; relative2)/'") with open(self.nodes[0].datadir_path / "relative.conf", "w") as f: # Restore initial file contents f.write("uacomment=relative\n") @@ -80,7 +80,7 @@ def run_test(self): self.start_node(0) subversion = self.nodes[0].getnetworkinfo()["subversion"] - assert subversion.endswith("main; relative; relative2)/") + assert "main; relative; relative2)/" in subversion if __name__ == '__main__': IncludeConfTest(__file__).main() diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py index 12fcd129c9ef..dfd54798d14f 100755 --- a/test/functional/feature_uacomment.py +++ b/test/functional/feature_uacomment.py @@ -2,10 +2,11 @@ # Copyright (c) 2017-present The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test the -uacomment option.""" +"""Test the -uacomment option, and the $DOG Mode component of the user agent.""" import re +from test_framework.p2p import P2PInterface from test_framework.test_framework import BitcoinTestFramework from test_framework.test_node import ErrorMatch from test_framework.util import assert_equal @@ -17,13 +18,17 @@ def set_test_params(self): self.setup_clean_chain = True def run_test(self): - self.log.info("test multiple -uacomment") - test_uacomment = self.nodes[0].getnetworkinfo()["subversion"][-12:-1] - assert_equal(test_uacomment, "(testnode0)") + self.log.info("test multiple -uacomment, on the Bitcoin Core component, followed by the $DOG Mode one") + test_uacomment = self.nodes[0].getnetworkinfo()["subversion"] + assert re.fullmatch(r"/Satoshi:\d+\.\d+\.\d+\(testnode0\)/DOGMode:[^/:()]+/", test_uacomment), test_uacomment self.restart_node(0, ["-uacomment=foo"]) - foo_uacomment = self.nodes[0].getnetworkinfo()["subversion"][-17:-1] - assert_equal(foo_uacomment, "(testnode0; foo)") + foo_uacomment = self.nodes[0].getnetworkinfo()["subversion"] + assert re.fullmatch(r"/Satoshi:\d+\.\d+\.\d+\(testnode0; foo\)/DOGMode:[^/:()]+/", foo_uacomment), foo_uacomment + + self.log.info("test peers receive the same user agent") + peer = self.nodes[0].add_p2p_connection(P2PInterface()) + assert_equal(peer.last_message["version"].strSubVer, foo_uacomment) self.log.info("test -uacomment max length") self.stop_node(0)