From 17b7bbb02f6bdf5e2efef63857f333fa5e5f184d Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Mon, 12 Aug 2024 22:38:14 +0300 Subject: [PATCH 1/5] feat: remove misleading and unused `spork active` and its test, let `spork` accept anything and act like `spork show` --- src/rpc/node.cpp | 26 +++--------- test/functional/feature_sporks.py | 66 ------------------------------- 2 files changed, 5 insertions(+), 87 deletions(-) delete mode 100755 test/functional/feature_sporks.py diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp index 97d6188828b9..96b887cc3346 100644 --- a/src/rpc/node.cpp +++ b/src/rpc/node.cpp @@ -151,7 +151,7 @@ static RPCHelpMan spork() return RPCHelpMan{"spork", "\nShows information about current state of sporks\n", { - {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'show' to show all current spork values, 'active' to show which sporks are active"}, + {"command", RPCArg::Type::STR, /* default*/ "\"show\"", "DEPRECATED. 'show' to show all current spork values"}, }, { RPCResult{"For 'show'", @@ -159,11 +159,6 @@ static RPCHelpMan spork() { {RPCResult::Type::NUM, "SPORK_NAME", "The value of the specific spork with the name SPORK_NAME"}, }}, - RPCResult{"For 'active'", - RPCResult::Type::OBJ_DYN, "", "keys are the sporks, and values indicates its status", - { - {RPCResult::Type::BOOL, "SPORK_NAME", "'true' for time-based sporks if spork is active and 'false' otherwise"}, - }}, }, RPCExamples { HelpExampleCli("spork", "show") @@ -173,24 +168,13 @@ static RPCHelpMan spork() { // basic mode, show info - std:: string strCommand = request.params[0].get_str(); const NodeContext& node = EnsureAnyNodeContext(request.context); CHECK_NONFATAL(node.sporkman); - if (strCommand == "show") { - UniValue ret(UniValue::VOBJ); - for (const auto& sporkDef : sporkDefs) { - ret.pushKV(std::string(sporkDef.name), node.sporkman->GetSporkValue(sporkDef.sporkId)); - } - return ret; - } else if(strCommand == "active"){ - UniValue ret(UniValue::VOBJ); - for (const auto& sporkDef : sporkDefs) { - ret.pushKV(std::string(sporkDef.name), node.sporkman->IsSporkActive(sporkDef.sporkId)); - } - return ret; + UniValue ret(UniValue::VOBJ); + for (const auto& sporkDef : sporkDefs) { + ret.pushKV(std::string(sporkDef.name), node.sporkman->GetSporkValue(sporkDef.sporkId)); } - - return NullUniValue; + return ret; }, }; } diff --git a/test/functional/feature_sporks.py b/test/functional/feature_sporks.py deleted file mode 100755 index b6b5fdf7b817..000000000000 --- a/test/functional/feature_sporks.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2018-2024 The Dash Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from test_framework.test_framework import BitcoinTestFramework - -''' -''' - -class SporkTest(BitcoinTestFramework): - def set_test_params(self): - self.num_nodes = 3 - self.setup_clean_chain = True - self.disable_mocktime = True - self.extra_args = [["-sporkkey=cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"], [], []] - - def setup_network(self): - self.setup_nodes() - # connect only 2 first nodes at start - self.connect_nodes(0, 1) - - def get_test_spork_state(self, node): - info = node.spork('active') - # use InstantSend spork for tests - return info['SPORK_2_INSTANTSEND_ENABLED'] - - def set_test_spork_state(self, node, state): - if state: - value = 0 - else: - value = 4070908800 - # use InstantSend spork for tests - node.sporkupdate("SPORK_2_INSTANTSEND_ENABLED", value) - - def run_test(self): - spork_default_state = self.get_test_spork_state(self.nodes[0]) - # check test spork default state matches on all nodes - assert self.get_test_spork_state(self.nodes[1]) == spork_default_state - assert self.get_test_spork_state(self.nodes[2]) == spork_default_state - - # check spork propagation for connected nodes - spork_new_state = not spork_default_state - self.set_test_spork_state(self.nodes[0], spork_new_state) - self.wait_until(lambda: self.get_test_spork_state(self.nodes[1]), timeout=10) - - # restart nodes to check spork persistence - self.stop_node(0) - self.stop_node(1) - self.start_node(0) - self.start_node(1) - assert self.get_test_spork_state(self.nodes[0]) == spork_new_state - assert self.get_test_spork_state(self.nodes[1]) == spork_new_state - - # Generate one block to kick off masternode sync, which also starts sporks syncing for node2 - self.generate(self.nodes[1], 1, sync_fun=self.no_op) - - # connect new node and check spork propagation after restoring from cache - self.connect_nodes(1, 2) - self.wait_until(lambda: self.get_test_spork_state(self.nodes[2]), timeout=10) - - assert "" not in self.nodes[0].spork('show').keys() - - -if __name__ == '__main__': - SporkTest().main() From ba4acc637b76718a9dcef24ac9ba6a30fbaf0ee8 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Mon, 12 Aug 2024 22:44:33 +0300 Subject: [PATCH 2/5] test: update tests to use `spork` with no args --- test/functional/feature_multikeysporks.py | 2 +- test/functional/test_framework/test_framework.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/functional/feature_multikeysporks.py b/test/functional/feature_multikeysporks.py index 999622b1e6d4..06c7f9b71c8d 100755 --- a/test/functional/feature_multikeysporks.py +++ b/test/functional/feature_multikeysporks.py @@ -69,7 +69,7 @@ def setup_network(self): def get_test_spork_value(self, node, spork_name): self.bump_mocktime(5) # advance ProcessTick - info = node.spork('show') + info = node.spork() # use InstantSend spork for tests return info[spork_name] diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 8c8ac38b011f..4a05e814c912 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1527,7 +1527,7 @@ def activate_by_name(self, name, expected_activation_height=None, slow_mode=True self.log.info("Wait for " + name + " activation") # disable spork17 while mining blocks to activate "name" to prevent accidental quorum formation - spork17_value = self.nodes[0].spork('show')['SPORK_17_QUORUM_DKG_ENABLED'] + spork17_value = self.nodes[0].spork()['SPORK_17_QUORUM_DKG_ENABLED'] self.bump_mocktime(1) self.nodes[0].sporkupdate("SPORK_17_QUORUM_DKG_ENABLED", 4070908800) self.wait_for_sporks_same() @@ -1951,8 +1951,8 @@ def wait_for_best_chainlock(self, node, block_hash, timeout=15): def wait_for_sporks_same(self, timeout=30): def check_sporks_same(): self.bump_mocktime(1) - sporks = self.nodes[0].spork('show') - return all(node.spork('show') == sporks for node in self.nodes[1:]) + sporks = self.nodes[0].spork() + return all(node.spork() == sporks for node in self.nodes[1:]) self.wait_until(check_sporks_same, timeout=timeout, sleep=1) def wait_for_quorum_connections(self, quorum_hash, expected_connections, mninfos, llmq_type_name="llmq_test", timeout = 60, wait_proc=None): @@ -2095,8 +2095,8 @@ def move_blocks(self, nodes, num_blocks): self.generate(self.nodes[0], num_blocks, sync_fun=lambda: self.sync_blocks(nodes)) def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connections=None, expected_members=None, expected_contributions=None, expected_complaints=0, expected_justifications=0, expected_commitments=None, mninfos_online=None, mninfos_valid=None, skip_maturity=False): - spork21_active = self.nodes[0].spork('show')['SPORK_21_QUORUM_ALL_CONNECTED'] <= 1 - spork23_active = self.nodes[0].spork('show')['SPORK_23_QUORUM_POSE'] <= 1 + spork21_active = self.nodes[0].spork()['SPORK_21_QUORUM_ALL_CONNECTED'] <= 1 + spork23_active = self.nodes[0].spork()['SPORK_23_QUORUM_POSE'] <= 1 if expected_connections is None: expected_connections = (self.llmq_size - 1) if spork21_active else 2 @@ -2185,8 +2185,8 @@ def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connec return new_quorum def mine_cycle_quorum(self): - spork21_active = self.nodes[0].spork('show')['SPORK_21_QUORUM_ALL_CONNECTED'] <= 1 - spork23_active = self.nodes[0].spork('show')['SPORK_23_QUORUM_POSE'] <= 1 + spork21_active = self.nodes[0].spork()['SPORK_21_QUORUM_ALL_CONNECTED'] <= 1 + spork23_active = self.nodes[0].spork()['SPORK_23_QUORUM_POSE'] <= 1 llmq_type_name="llmq_test_dip0024" llmq_type=103 From aa80a709201d6d6f303fe8e8deae678ef93e2dd6 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Mon, 12 Aug 2024 23:06:54 +0300 Subject: [PATCH 3/5] chore: update help text, mention that spork state/updates are irrelevant on mainnet --- src/rpc/node.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp index 96b887cc3346..8d40f845e782 100644 --- a/src/rpc/node.cpp +++ b/src/rpc/node.cpp @@ -149,20 +149,20 @@ static RPCHelpMan spork() { // default help, for basic mode return RPCHelpMan{"spork", - "\nShows information about current state of sporks\n", + "\nShows information about current state of sporks for non-mainnet networks. Mainnet values are hardcoded.\n", { - {"command", RPCArg::Type::STR, /* default*/ "\"show\"", "DEPRECATED. 'show' to show all current spork values"}, + {"command", RPCArg::Type::STR, RPCArg::Default{""}, "Deprecated and ignored"}, }, { - RPCResult{"For 'show'", + RPCResult{ RPCResult::Type::OBJ_DYN, "", "keys are the sporks, and values indicates its value", { {RPCResult::Type::NUM, "SPORK_NAME", "The value of the specific spork with the name SPORK_NAME"}, }}, }, RPCExamples { - HelpExampleCli("spork", "show") - + HelpExampleRpc("spork", "\"show\"") + HelpExampleCli("spork", "") + + HelpExampleRpc("spork", "") }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { @@ -182,7 +182,7 @@ static RPCHelpMan spork() static RPCHelpMan sporkupdate() { return RPCHelpMan{"sporkupdate", - "\nUpdate the value of the specific spork. Requires \"-sporkkey\" to be set to sign the message.\n", + "\nUpdate the value of the specific spork on non-mainnet networks. Requires \"-sporkkey\" to be set to sign the message.\n", { {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the spork to update"}, {"value", RPCArg::Type::NUM, RPCArg::Optional::NO, "The new desired value of the spork"}, From 35fc2ded903ec59aea478ff0824608b7d735f554 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 16 Oct 2024 13:05:44 +0300 Subject: [PATCH 4/5] test: revive feature_sporks.py --- test/functional/feature_sporks.py | 61 +++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100755 test/functional/feature_sporks.py diff --git a/test/functional/feature_sporks.py b/test/functional/feature_sporks.py new file mode 100755 index 000000000000..0183d90e7322 --- /dev/null +++ b/test/functional/feature_sporks.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018-2024 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework + +''' +''' + +class SporkTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 3 + self.setup_clean_chain = True + self.disable_mocktime = True + self.extra_args = [["-sporkkey=cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"], [], []] + + def setup_network(self): + self.setup_nodes() + # connect only 2 first nodes at start + self.connect_nodes(0, 1) + + def get_test_spork_value(self, node): + # use InstantSend spork for tests + return node.spork()['SPORK_2_INSTANTSEND_ENABLED'] + + def set_test_spork_value(self, node, value): + # use InstantSend spork for tests + node.sporkupdate("SPORK_2_INSTANTSEND_ENABLED", value) + + def run_test(self): + spork_default_value = self.get_test_spork_value(self.nodes[0]) + # check test spork default state matches on all nodes + assert self.get_test_spork_value(self.nodes[1]) == spork_default_value + assert self.get_test_spork_value(self.nodes[2]) == spork_default_value + + # check spork propagation for connected nodes + spork_new_value = spork_default_value + 1 + self.set_test_spork_value(self.nodes[0], spork_new_value) + self.wait_until(lambda: self.get_test_spork_value(self.nodes[1]) == spork_new_value, timeout=10) + + # restart nodes to check spork persistence + self.stop_node(0) + self.stop_node(1) + self.start_node(0) + self.start_node(1) + assert self.get_test_spork_value(self.nodes[0]) == spork_new_value + assert self.get_test_spork_value(self.nodes[1]) == spork_new_value + + # Generate one block to kick off masternode sync, which also starts sporks syncing for node2 + self.generate(self.nodes[1], 1, sync_fun=self.no_op) + + # connect new node and check spork propagation after restoring from cache + self.connect_nodes(1, 2) + self.wait_until(lambda: self.get_test_spork_value(self.nodes[2]) == spork_new_value, timeout=10) + + assert "" not in self.nodes[0].spork().keys() + + +if __name__ == '__main__': + SporkTest().main() From 30fd8b62adca62a5b751f4a614762e2082a6424e Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 23 Sep 2025 22:14:36 +0300 Subject: [PATCH 5/5] docs: partial release notes --- doc/release-notes-6207.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 doc/release-notes-6207.md diff --git a/doc/release-notes-6207.md b/doc/release-notes-6207.md new file mode 100644 index 000000000000..f49cfc940d88 --- /dev/null +++ b/doc/release-notes-6207.md @@ -0,0 +1,4 @@ +RPC changes +----------- + +- `spork` no longer have `active` and `show` commands, it always shows actual raw values now. `command` argument is deprecated and ignored.