Skip to content

Commit

Permalink
Merge pull request #527 from thecodefactory/electrum-seed
Browse files Browse the repository at this point in the history
Update seed command to support electrum mnemonics
  • Loading branch information
evoskuil authored Feb 20, 2018
2 parents 54df66a + 6300c1e commit 6847180
Show file tree
Hide file tree
Showing 17 changed files with 73 additions and 37 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ console/bx
/release/bx
/release/bx.exe
*.zpl

build-libbitcoin-explorer
libbitcoin_explorer_test_runner.sh.log
libbitcoin_explorer_test_runner.sh.trs
test-suite.log
test.log
test/libbitcoin_explorer_test
6 changes: 4 additions & 2 deletions include/bitcoin/explorer/commands/electrum-new.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ namespace commands {
/**
* Various localizable strings.
*/
#define BX_EC_ELECTRUM_NEW_UNSUPPORTED \
"The electrum-new command requires an ICU build."
#define BX_ELECTRUM_NEW_INVALID_SEED \
"The seed size is not supported."
#define BX_ELECTRUM_REQUIRES_ICU \
"The command requires an ICU build."

/**
* Class to implement the electrum-new command.
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/explorer/commands/electrum-to-seed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ namespace commands {
/**
* Various localizable strings.
*/
#define BX_EC_ELECTRUM_TO_SEED_PASSPHRASE_UNSUPPORTED \
#define BX_ELECTRUM_TO_SEED_REQUIRES_ICU \
"The passphrase option requires an ICU build."

/**
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/explorer/commands/mnemonic-decode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ namespace commands {
* Various localizable strings.
*/
#define BX_MNEMONIC_DECODE_OBSOLETE \
"Electrum style key functions are obsolete. Use mnemonic-to-seed (BIP39) command instead."
"Electrum version 1 functions are obsolete. Use electrum-to-seed or mnemonic-to-seed (BIP39) command instead."

/**
* Class to implement the mnemonic-decode command.
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/explorer/commands/mnemonic-encode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ namespace commands {
* Various localizable strings.
*/
#define BX_MNEMONIC_ENCODE_OBSOLETE \
"Electrum style key functions are obsolete. Use mnemonic-new (BIP39) command instead."
"Electrum version 1 functions are obsolete. Use electrum-new or mnemonic-new (BIP39) command instead."

/**
* Class to implement the mnemonic-encode command.
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/explorer/commands/mnemonic-new.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ namespace commands {
/**
* Various localizable strings.
*/
#define BX_EC_MNEMONIC_NEW_INVALID_ENTROPY \
#define BX_EC_MNEMONIC_NEW_INVALID_SEED \
"The seed length in bytes is not evenly divisible by 32 bits."

/**
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/explorer/commands/mnemonic-to-seed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ namespace commands {
*/
#define BX_EC_MNEMONIC_TO_SEED_LENGTH_INVALID_SENTENCE \
"The number of words must be divisible by 3."
#define BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED \
#define BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU \
"The passphrase option requires an ICU build."
#define BX_EC_MNEMONIC_TO_SEED_INVALID_IN_LANGUAGE \
"The specified words are not a valid mnemonic in the specified dictionary."
Expand Down
4 changes: 2 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ fi
# Set the prefix-based package config directory.
PREFIX_PKG_CONFIG_DIR="$PREFIX/lib/pkgconfig"

# Augment PKG_CONFIG_PATH search path with our prefix.
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$PREFIX_PKG_CONFIG_DIR"
# Prioritize prefix package config in PKG_CONFIG_PATH search path.
export PKG_CONFIG_PATH="$PREFIX_PKG_CONFIG_DIR:$PKG_CONFIG_PATH"

# Set a package config save path that can be passed via our builds.
with_pkgconfigdir="--with-pkgconfigdir=$PREFIX_PKG_CONFIG_DIR"
Expand Down
13 changes: 7 additions & 6 deletions model/generate.xml
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,15 @@
<option name="prefix" type="electrum" description="The electrum seed type identifier to use. Options are 'standard', 'witness', and 'dual' (for two factor authentication), defaults to 'standard'." />
<option name="language" type="language" description="The language identifier of the mnemonic dictionary to use. Options are 'en', 'es', 'pt', 'ja', 'zh_Hans' and 'any', defaults to 'en'." />
<argument name="SEED" stdin="true" type="base16" description="The Base16 entropy from which the mnemonic is created. If not specified the entropy is read from STDIN." />
<define name="BX_EC_ELECTRUM_NEW_UNSUPPORTED" value="The electrum-new command requires an ICU build." />
<define name="BX_ELECTRUM_NEW_INVALID_SEED" value="The seed size is not supported." />
<define name="BX_ELECTRUM_REQUIRES_ICU" value="The command requires an ICU build." />
</command>

<command symbol="electrum-to-seed" output="base16" category="WALLET" description="Convert a mnemonic seed (Electrum) to its numeric representation.">
<option name="language" type="language" description="The language identifier of the dictionary of the mnemonic. Options are 'en', 'es', 'ja', 'pt', 'zh_Hans' and 'any', defaults to 'any'." />
<option name="passphrase" type="string" description="An optional passphrase for converting the mnemonic to a seed." />
<argument name="WORD" stdin="true" limit="-1" type="string" description="The set of words that that make up the mnemonic. If not specified the words are read from STDIN." />
<define name="BX_EC_ELECTRUM_TO_SEED_PASSPHRASE_UNSUPPORTED" value="The passphrase option requires an ICU build." />
<define name="BX_ELECTRUM_TO_SEED_REQUIRES_ICU" value="The passphrase option requires an ICU build." />
</command>

<command symbol="ek-address" output="payment_address" category="KEY_ENCRYPTION" description="Create a payment address derived from an intermediate passphrase token (BIP38).">
Expand Down Expand Up @@ -412,25 +413,25 @@
</command>

<command symbol="mnemonic-decode" output="base16" category="ELECTRUM" obsolete="true" description="Convert a seed to its Electrum mnemonic.">
<define name="BX_MNEMONIC_DECODE_OBSOLETE" value="Electrum style key functions are obsolete. Use mnemonic-to-seed (BIP39) command instead." />
<define name="BX_MNEMONIC_DECODE_OBSOLETE" value="Electrum version 1 functions are obsolete. Use electrum-to-seed or mnemonic-to-seed (BIP39) command instead." />
</command>

<command symbol="mnemonic-encode" formerly="mnemonic" output="string" multipleX="true" category="ELECTRUM" obsolete="true" description="Convert an Electrum mnemonic to its seed.">
<define name="BX_MNEMONIC_ENCODE_OBSOLETE" value="Electrum style key functions are obsolete. Use mnemonic-new (BIP39) command instead." />
<define name="BX_MNEMONIC_ENCODE_OBSOLETE" value="Electrum version 1 functions are obsolete. Use electrum-new or mnemonic-new (BIP39) command instead." />
</command>

<command symbol="mnemonic-new" output="string" multipleX="true" category="WALLET" description="Create a mnemonic seed (BIP39) from entropy. WARNING: mnemonic should be created from properly generated entropy.">
<option name="language" type="language" description="The language identifier of the mnemonic dictionary to use. Options are 'en', 'es', 'fr', 'it', 'ja', 'cs', 'ru', 'uk', 'zh_Hans', 'zh_Hant' and 'any', defaults to 'en'." />
<argument name="SEED" stdin="true" type="base16" description="The Base16 entropy from which the mnemonic is created. The length must be evenly divisible by 32 bits. If not specified the entropy is read from STDIN." />
<define name="BX_EC_MNEMONIC_NEW_INVALID_ENTROPY" value="The seed length in bytes is not evenly divisible by 32 bits." />
<define name="BX_EC_MNEMONIC_NEW_INVALID_SEED" value="The seed length in bytes is not evenly divisible by 32 bits." />
</command>

<command symbol="mnemonic-to-seed" output="base16" category="WALLET" description="Convert a mnemonic seed (BIP39) to its numeric representation.">
<option name="language" type="language" description="The language identifier of the dictionary of the mnemonic. Options are 'en', 'es', 'fr', 'it', 'ja', 'cs', 'ru', 'uk', 'zh_Hans', 'zh_Hant' and 'any', defaults to 'any'." />
<option name="passphrase" type="string" description="An optional passphrase for converting the mnemonic to a seed." />
<argument name="WORD" stdin="true" limit="-1" type="string" description="The set of words that that make up the mnemonic. If not specified the words are read from STDIN." />
<define name="BX_EC_MNEMONIC_TO_SEED_LENGTH_INVALID_SENTENCE" value="The number of words must be divisible by 3." />
<define name="BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED" value="The passphrase option requires an ICU build." />
<define name="BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU" value="The passphrase option requires an ICU build." />
<define name="BX_EC_MNEMONIC_TO_SEED_INVALID_IN_LANGUAGE" value="The specified words are not a valid mnemonic in the specified dictionary." />
<define name="BX_EC_MNEMONIC_TO_SEED_INVALID_IN_LANGUAGES" value="WARNING: The specified words are not a valid mnemonic in any supported dictionary." />
</command>
Expand Down
31 changes: 24 additions & 7 deletions src/commands/electrum-new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,50 @@
*/
#include <bitcoin/explorer/commands/electrum-new.hpp>

#include <iostream>
#include <cmath>
#include <bitcoin/bitcoin.hpp>
#include <bitcoin/bitcoin/math/hash.hpp>
#include <bitcoin/explorer/define.hpp>

namespace libbitcoin {
namespace explorer {
namespace commands {
using namespace bc::wallet;

console_result electrum_new::invoke(std::ostream& output,
std::ostream& error)
// Requires a seed of at least 17 bytes (136 bits).
static const size_t minimum_electrum_words = 12;

console_result electrum_new::invoke(std::ostream& output, std::ostream& error)
{
#ifdef WITH_ICU
// Bound parameters.
const dictionary_list& language = get_language_option();
const data_chunk& seed = get_seed_argument();
const auto prefix = get_prefix_option();

// trunc(log2(2048)) = 11
const auto word_bits = static_cast<size_t>(std::log2(dictionary_size));

// 17 * 8 = 136
const auto seed_bits = seed.size() * byte_bits;

// 136 / 11 = 12
const auto words = seed_bits / word_bits;

if (words < minimum_electrum_words)
{
error << BX_ELECTRUM_NEW_INVALID_SEED << std::endl;
return console_result::failure;
}

// If 'any' default to first ('en'), otherwise the one specified.
const auto dictionary = language.front();
const auto words = electrum::create_mnemonic(seed, *dictionary, prefix);

output << join(words) << std::endl;
auto mnemonic = electrum::create_mnemonic(seed, *dictionary, prefix);

output << join(mnemonic) << std::endl;
return console_result::okay;
#else
error << BX_EC_ELECTRUM_NEW_UNSUPPORTED << std::endl;
error << BX_ELECTRUM_REQUIRES_ICU << std::endl;
return console_result::failure;
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/electrum-to-seed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ console_result electrum_to_seed::invoke(std::ostream& output,
// The passphrase requires ICU normalization.
if (!passphrase.empty())
{
error << BX_EC_ELECTRUM_TO_SEED_PASSPHRASE_UNSUPPORTED << std::endl;
error << BX_ELECTRUM_TO_SEED_REQUIRES_ICU << std::endl;
return console_result::failure;
}

Expand Down
2 changes: 1 addition & 1 deletion src/commands/mnemonic-new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ console_result mnemonic_new::invoke(std::ostream& output,

if ((entropy_size % bc::wallet::mnemonic_seed_multiple) != 0)
{
error << BX_EC_MNEMONIC_NEW_INVALID_ENTROPY << std::endl;
error << BX_EC_MNEMONIC_NEW_INVALID_SEED << std::endl;
return console_result::failure;
}

Expand Down
2 changes: 1 addition & 1 deletion src/commands/mnemonic-to-seed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ console_result mnemonic_to_seed::invoke(std::ostream& output,
#else
if (!passphrase.empty())
{
error << BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED << std::endl;
error << BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU << std::endl;
return console_result::failure;
}

Expand Down
1 change: 0 additions & 1 deletion src/commands/seed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
#include <bitcoin/explorer/commands/seed.hpp>

#include <iostream>
#include <bitcoin/bitcoin.hpp>
#include <bitcoin/explorer/define.hpp>
#include <bitcoin/explorer/utility.hpp>
Expand Down
12 changes: 11 additions & 1 deletion test/commands/electrum-new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(electrum_new__invoke__17_bytes__okay_output)
BX_REQUIRE_OUTPUT("giggle crush argue inflict wear defy combine evolve tiger spatial crumble fury\n");
}

BOOST_AUTO_TEST_CASE(electrum_new__invoke__dictionary_prefix__okay_output)
BOOST_AUTO_TEST_CASE(electrum_new__invoke__en_dictionary_prefix__okay_output)
{
BX_DECLARE_COMMAND(electrum_new);
command.set_seed_argument({ "05e669b4270f4e25bce6fc3736170d423c" });
Expand All @@ -44,6 +44,16 @@ BOOST_AUTO_TEST_CASE(electrum_new__invoke__dictionary_prefix__okay_output)
BX_REQUIRE_OUTPUT("giggle crush argue inflict wear defy combine evolve tiger spatial crumble fury\n");
}

BOOST_AUTO_TEST_CASE(electrum_new__invoke__es_dictionary_prefix__okay_output)
{
BX_DECLARE_COMMAND(electrum_new);
command.set_seed_argument({ "05e669b4270f4e25bce6fc3736170d423c" });
command.set_language_option({ "es" });
command.set_prefix_option({ "standard" });
BX_REQUIRE_OKAY(command.invoke(output, error));
BX_REQUIRE_OUTPUT("gigante codo ámbar insecto verbo cráter celoso entrar tarjeta sala coco frito\n");
}

BOOST_AUTO_TEST_CASE(electrum_new__invoke__32_bytes__okay_output)
{
BX_DECLARE_COMMAND(electrum_new);
Expand Down
2 changes: 1 addition & 1 deletion test/commands/mnemonic-new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_new__invoke__136_bits__failure_error)
BX_DECLARE_COMMAND(mnemonic_new);
command.set_seed_argument({ "baadf00dbaadf00dbaadf00dbaadf00dff" });
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_NEW_INVALID_ENTROPY "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_NEW_INVALID_SEED "\n");
}

BOOST_AUTO_TEST_CASE(mnemonic_new__invoke__128_bits__okay_output)
Expand Down
18 changes: 9 additions & 9 deletions test/commands/mnemonic-to-seed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__standard__okay_output)
BX_REQUIRE_OUTPUT("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand All @@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__non_ascii_passphrase__okay_output
BX_REQUIRE_OUTPUT("3e52585ea1275472a82fa0dcd84121e742140f64a302eca7c390832ba428c707a7ebf449267ae592c51f1740259226e31520de39fd8f33e08788fd21221c6f4e\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand All @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__non_ascii_passphrase_and_words__o
BX_REQUIRE_OUTPUT("e72505021b97e15171fe09e996898888579c4196c445d7629762c5b09586e3fb3d68380120b8d8a6ed6f9a73306dab7bf54127f3a610ede2a2d5b4e59916ac73\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand All @@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__non_ascii_passphrase_and_words_an
BX_REQUIRE_OUTPUT("e72505021b97e15171fe09e996898888579c4196c445d7629762c5b09586e3fb3d68380120b8d8a6ed6f9a73306dab7bf54127f3a610ede2a2d5b4e59916ac73\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand All @@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__non_ascii_passphrase_and_words_es
BX_REQUIRE_OUTPUT("e72505021b97e15171fe09e996898888579c4196c445d7629762c5b09586e3fb3d68380120b8d8a6ed6f9a73306dab7bf54127f3a610ede2a2d5b4e59916ac73\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand All @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__non_ascii_passphrase_and_words_an
BX_REQUIRE_OUTPUT("a92538e2827914d13ead2b426aa45ebd0b16590318e04b6ef78780c8eb803269f08662dd74bc4982e7cbb71f15c71f310168457d570ad5fd89c98a6095bac560\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand All @@ -191,7 +191,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__non_ascii_passphrase_and_words_fr
BX_REQUIRE_OUTPUT("a92538e2827914d13ead2b426aa45ebd0b16590318e04b6ef78780c8eb803269f08662dd74bc4982e7cbb71f15c71f310168457d570ad5fd89c98a6095bac560\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand All @@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__non_ascii_passphrase_and_words_an
BX_REQUIRE_OUTPUT("b145e13882dc52b64a868ba35c3a95de5f468f2963d9feca9a8b345e3a60ef02af42347f99bc35a72d88bdabe8d63a4f5b61a63d6cd549461b5dd11027b66cf7\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand All @@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE(mnemonic_to_seed__invoke__non_ascii_passphrase_and_words_it
BX_REQUIRE_OUTPUT("b145e13882dc52b64a868ba35c3a95de5f468f2963d9feca9a8b345e3a60ef02af42347f99bc35a72d88bdabe8d63a4f5b61a63d6cd549461b5dd11027b66cf7\n");
#else
BX_REQUIRE_FAILURE(command.invoke(output, error));
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_PASSPHRASE_UNSUPPORTED "\n");
BX_REQUIRE_ERROR(BX_EC_MNEMONIC_TO_SEED_REQUIRES_ICU "\n");
#endif
}

Expand Down

0 comments on commit 6847180

Please sign in to comment.