From 16c2e13fb48c3e7cdab945310280766e12e2f260 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 16 Dec 2024 08:31:46 +0000 Subject: [PATCH] test: add functional tests for `coinjoinsalt` RPC --- test/functional/rpc_coinjoin.py | 73 +++++++++++++++++++++++++++++++++ test/functional/test_runner.py | 2 - 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/test/functional/rpc_coinjoin.py b/test/functional/rpc_coinjoin.py index 8ca9c865fa..70f9955377 100755 --- a/test/functional/rpc_coinjoin.py +++ b/test/functional/rpc_coinjoin.py @@ -3,13 +3,17 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +import random + from test_framework.test_framework import BitcoinTestFramework from test_framework.messages import ( COIN, MAX_MONEY, + uint256_to_string, ) from test_framework.util import ( assert_equal, + assert_is_hex_string, assert_raises_rpc_error, ) @@ -36,9 +40,22 @@ class CoinJoinTest(BitcoinTestFramework): node.createwallet(wallet_name='w1', blank=True, disable_private_keys=False) w1 = node.get_wallet_rpc('w1') + self.test_salt_presence(w1) self.test_coinjoin_start_stop(w1) self.test_setcoinjoinamount(w1) self.test_setcoinjoinrounds(w1) + self.test_coinjoinsalt(w1) + w1.unloadwallet() + + node.createwallet(wallet_name='w2', blank=True, disable_private_keys=True) + w2 = node.get_wallet_rpc('w2') + self.test_coinjoinsalt_disabled(w2) + w2.unloadwallet() + + def test_salt_presence(self, node): + self.log.info('Salt should be automatically generated in new wallet') + # Will raise exception if no salt generated + assert_is_hex_string(node.coinjoinsalt('get')) def test_coinjoin_start_stop(self, node): self.log.info('"coinjoin" subcommands should update mixing status') @@ -81,5 +98,61 @@ class CoinJoinTest(BitcoinTestFramework): for value in [COINJOIN_ROUNDS_MIN - 1, COINJOIN_ROUNDS_MAX + 1]: assert_raises_rpc_error(-8, "Invalid number of rounds", node.setcoinjoinrounds, value) + def test_coinjoinsalt(self, node): + self.log.info('"coinjoinsalt generate" should fail if salt already present') + assert_raises_rpc_error(-32600, 'Wallet "w1" already has set CoinJoin salt!', node.coinjoinsalt, 'generate') + + self.log.info('"coinjoinsalt" subcommands should succeed if no balance and not mixing') + # 'coinjoinsalt generate' should return a new salt if overwrite enabled + s1 = node.coinjoinsalt('get') + assert_equal(node.coinjoinsalt('generate', True), True) + s2 = node.coinjoinsalt('get') + assert s1 != s2 + + # 'coinjoinsalt get' should fetch newly generated value (i.e. new salt should persist) + node.unloadwallet('w1') + node.loadwallet('w1') + node = self.nodes[0].get_wallet_rpc('w1') + assert_equal(s2, node.coinjoinsalt('get')) + + # 'coinjoinsalt set' should work with random hashes + s1 = uint256_to_string(random.getrandbits(256)) + node.coinjoinsalt('set', s1) + assert_equal(s1, node.coinjoinsalt('get')) + assert s1 != s2 + + # 'coinjoinsalt set' shouldn't work with nonsense values + s2 = format(0, '064x') + assert_raises_rpc_error(-8, "Invalid CoinJoin salt value", node.coinjoinsalt, 'set', s2, True) + s2 = s2[0:63] + 'h' + assert_raises_rpc_error(-8, "salt must be hexadecimal string (not '%s')" % s2, node.coinjoinsalt, 'set', s2, True) + + self.log.info('"coinjoinsalt generate" and "coinjoinsalt set" should fail if mixing') + # Start mix session + node.coinjoin('start') + assert_equal(node.getcoinjoininfo()['running'], True) + + # 'coinjoinsalt generate' and 'coinjoinsalt set' should fail when mixing + assert_raises_rpc_error(-4, 'Wallet "w1" is currently mixing, cannot change salt!', node.coinjoinsalt, 'generate', True) + assert_raises_rpc_error(-4, 'Wallet "w1" is currently mixing, cannot change salt!', node.coinjoinsalt, 'set', s1, True) + + # 'coinjoinsalt get' should still work + assert_equal(node.coinjoinsalt('get'), s1) + + # Stop mix session + node.coinjoin('stop') + assert_equal(node.getcoinjoininfo()['running'], False) + + # 'coinjoinsalt generate' and 'coinjoinsalt set' should start working again + assert_equal(node.coinjoinsalt('generate', True), True) + assert_equal(node.coinjoinsalt('set', s1, True), True) + + def test_coinjoinsalt_disabled(self, node): + self.log.info('"coinjoinsalt" subcommands should fail if private keys disabled') + for subcommand in ['generate', 'get']: + assert_raises_rpc_error(-32600, 'Wallet "w2" has private keys disabled, cannot perform CoinJoin!', node.coinjoinsalt, subcommand) + s1 = uint256_to_string(random.getrandbits(256)) + assert_raises_rpc_error(-32600, 'Wallet "w2" has private keys disabled, cannot perform CoinJoin!', node.coinjoinsalt, 'set', s1) + if __name__ == '__main__': CoinJoinTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 2b311fca34..e1c2b7680a 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -892,8 +892,6 @@ class RPCCoverage(): # Consider RPC generate covered, because it is overloaded in # test_framework/test_node.py and not seen by the coverage check. covered_cmds = set({'generate'}) - # TODO: implement functional tests for coinjoinsalt - covered_cmds.add('coinjoinsalt') # TODO: implement functional tests for voteraw covered_cmds.add('voteraw') # TODO: implement functional tests for getmerkleblocks