Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/hdchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ bool CHDChain::SetMnemonic(const SecureString& ssMnemonic, const SecureString& s

// empty mnemonic i.e. "generate a new one"
if (ssMnemonic.empty()) {
ssMnemonicTmp = CMnemonic::Generate(256);
ssMnemonicTmp = CMnemonic::Generate(gArgs.GetArg("-mnemonicbits", DEFAULT_MNEMONIC_BITS));
}
// NOTE: default mnemonic passphrase is an empty string

Expand Down
2 changes: 2 additions & 0 deletions src/hdchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class CHDChain
std::map<uint32_t, CHDAccount> GUARDED_BY(cs) mapAccounts;

public:
/** Default for -mnemonicbits */
static constexpr int DEFAULT_MNEMONIC_BITS = 128; // 128 bits == 12 words

CHDChain() = default;
CHDChain(const CHDChain& other) :
Expand Down
7 changes: 7 additions & 0 deletions src/wallet/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
#include <wallet/wallet.h>
#include <walletinitinterface.h>

#include <bip39.h>
#include <coinjoin/client.h>
#include <coinjoin/options.h>
#include <hdchain.h>

class WalletInit : public WalletInitInterface
{
Expand Down Expand Up @@ -85,6 +87,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const

argsman.AddArg("-hdseed=<hex>", "User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::WALLET_HD);
argsman.AddArg("-mnemonic=<text>", "User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::WALLET_HD);
argsman.AddArg("-mnemonicbits=<n>", strprintf("User defined mnemonic security for HD wallet in bits (BIP39). Only has effect during wallet creation/first start (allowed values: 128, 192, 256; default: %u)", CHDChain::DEFAULT_MNEMONIC_BITS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_HD);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't 160 also accepted as a correct value?

    // check number of words
    if (nWordCount != 12 && nWordCount != 18 && nWordCount != 24) {
        return false;
    }

but when it's generated:

    if (strength % 32 || strength < 128 || strength > 256) {

Probably should be updated 32 to 64: otherwise Generate() is happy with 160 bits, 224 bits but CMnemonic::Check later fails with error: mnemonic with 15 words are invalid mnemonic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it with modified script:

--- a/test/functional/wallet_mnemonicbits.py
+++ b/test/functional/wallet_mnemonicbits.py
@@ -28,6 +28,8 @@ class WalletMnemonicbitsTest(BitcoinTestFramework):
         assert_equal(len(self.nodes[0].dumphdinfo()["mnemonic"].split()), 12)  # 12 words by default
 
         self.log.info("Can have multiple wallets with different mnemonic length loaded at the same time")
+        self.restart_node(0, extra_args=self.extra_args[0] + ["-mnemonicbits=160"])
+        self.nodes[0].createwallet("wallet_160")
         self.restart_node(0, extra_args=self.extra_args[0] + ["-mnemonicbits=192"])
         self.nodes[0].createwallet("wallet_192")
         self.restart_node(0, extra_args=self.extra_args[0] + ["-mnemonicbits=256"])
@@ -35,6 +37,7 @@ class WalletMnemonicbitsTest(BitcoinTestFramework):
         self.nodes[0].createwallet("wallet_256", False, True)  # blank
         self.nodes[0].get_wallet_rpc("wallet_256").upgradetohd()
         assert_equal(len(self.nodes[0].get_wallet_rpc(self.default_wallet_name).dumphdinfo()["mnemonic"].split()), 12)  # 12 words by default
+        assert_equal(len(self.nodes[0].get_wallet_rpc("wallet_160").dumphdinfo()["mnemonic"].split()), 15)              # 15 words

...

test_framework.authproxy.JSONRPCException: SetMnemonic: invalid mnemonic: `noise glow drift unveil intact fruit mind add claim ankle pumpkin cheap come tuna win` (-1)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

argsman.AddArg("-mnemonicpassphrase=<text>", "User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::WALLET_HD);
argsman.AddArg("-usehd", strprintf("Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start (default: %u)", DEFAULT_USE_HD_WALLET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET_HD);

Expand Down Expand Up @@ -168,6 +171,10 @@ bool WalletInit::ParameterInteraction() const
return InitError(strprintf(_("%s can't be lower than %s"), "-coinjoindenomshardcap", "-coinjoindenomsgoal"));
}

if (CMnemonic::Generate(gArgs.GetArg("-mnemonicbits", CHDChain::DEFAULT_MNEMONIC_BITS)) == SecureString()) {
return InitError(strprintf(_("Invalid '%s'. Allowed values: 128, 192, 256."), "-mnemonicbits"));
}

return true;
}

Expand Down
1 change: 1 addition & 0 deletions test/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
'wallet_import_rescan.py',
'wallet_import_with_label.py',
'wallet_upgradewallet.py',
'wallet_mnemonicbits.py',
'rpc_bind.py --ipv4',
'rpc_bind.py --ipv6',
'rpc_bind.py --nonloopback',
Expand Down
43 changes: 43 additions & 0 deletions test/functional/wallet_mnemonicbits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing executable flag on wallet_mnemonicbits.py: chmod +x wallet_mnemonicbits.py

# Copyright (c) 2023 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test -mnemonicbits wallet option."""

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)

class WalletMnemonicbitsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [['-usehd=1']]

def skip_test_if_missing_module(self):
self.skip_if_no_wallet()

def run_test(self):
self.log.info("Test -mnemonicbits")

self.log.info("Invalid -mnemonicbits should rise an error")
self.stop_node(0)
self.nodes[0].assert_start_raises_init_error(self.extra_args[0] + ['-mnemonicbits=123'], "Error: Invalid '-mnemonicbits'. Allowed values: 128, 192, 256.")
self.start_node(0)
assert_equal(len(self.nodes[0].dumphdinfo()["mnemonic"].split()), 12) # 12 words by default

self.log.info("Can have multiple wallets with different mnemonic length loaded at the same time")
self.restart_node(0, extra_args=self.extra_args[0] + ["-mnemonicbits=192"])
self.nodes[0].createwallet("wallet_192")
self.restart_node(0, extra_args=self.extra_args[0] + ["-mnemonicbits=256"])
self.nodes[0].loadwallet("wallet_192")
self.nodes[0].createwallet("wallet_256", False, True) # blank
self.nodes[0].get_wallet_rpc("wallet_256").upgradetohd()
assert_equal(len(self.nodes[0].get_wallet_rpc(self.default_wallet_name).dumphdinfo()["mnemonic"].split()), 12) # 12 words by default
assert_equal(len(self.nodes[0].get_wallet_rpc("wallet_192").dumphdinfo()["mnemonic"].split()), 18) # 18 words
assert_equal(len(self.nodes[0].get_wallet_rpc("wallet_256").dumphdinfo()["mnemonic"].split()), 24) # 24 words


if __name__ == '__main__':
WalletMnemonicbitsTest().main ()