Skip to content

Commit

Permalink
Encode restore height into seed extra data
Browse files Browse the repository at this point in the history
  • Loading branch information
fireice-uk committed Aug 15, 2020
1 parent 29a1d43 commit 7139cfa
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 58 deletions.
6 changes: 3 additions & 3 deletions src/cryptonote_basic/account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void account_base::forget_spend_key()
crypto::secret_key_16 account_base::generate()
{
using namespace crypto;
if(m_acc_opt == ACC_OPT_KURZ_ADDRESS)
if(m_options.get_keying_type() == acc_options::ACC_OPT_KURZ_ADDRESS)
{
crypto::generate_wallet_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, m_keys.m_short_seed, KEY_VARIANT_KURZ);
m_keys.m_account_address.m_view_public_key = m_keys.m_account_address.m_spend_public_key;
Expand Down Expand Up @@ -129,12 +129,12 @@ bool account_base::has_25word_seed() const
bool account_base::has_14word_seed() const
{
using namespace crypto;
if(m_acc_opt == ACC_OPT_UNKNOWN)
if(m_options.get_keying_type() == acc_options::ACC_OPT_UNKNOWN)
return false;

public_key pview, pspend;
secret_key view, spend;
if(m_acc_opt == ACC_OPT_KURZ_ADDRESS)
if(m_options.get_keying_type() == acc_options::ACC_OPT_KURZ_ADDRESS)
{
generate_wallet_keys(pspend, spend, m_keys.m_short_seed, KEY_VARIANT_KURZ);
return spend == m_keys.m_spend_secret_key;
Expand Down
34 changes: 22 additions & 12 deletions src/cryptonote_basic/account.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,11 @@

#include "crypto/crypto.h"
#include "cryptonote_basic.h"
#include "account_opt.h"
#include "serialization/keyvalue_serialization.h"

namespace cryptonote
{
static constexpr uint8_t ACC_OPT_UNKNOWN = 0;
static constexpr uint8_t ACC_OPT_KURZ_ADDRESS = 1;
static constexpr uint8_t ACC_OPT_LONG_ADDRESS = 2;

struct account_keys
{
account_public_address m_account_address;
Expand Down Expand Up @@ -86,18 +83,18 @@ class account_base
{
public:

inline crypto::secret_key_16 generate_new(uint8_t acc_opt)
inline crypto::secret_key_16 generate_new(acc_options acc_opt)
{
crypto::generate_wallet_secret(m_keys.m_short_seed);
m_acc_opt = acc_opt;
m_options = acc_opt;
m_creation_timestamp = time(NULL);
return generate();
}

inline void recover(const crypto::secret_key_16 &recovery_seed, uint8_t acc_opt)
inline void recover(const crypto::secret_key_16 &recovery_seed, acc_options acc_opt)
{
m_keys.m_short_seed = recovery_seed;
m_acc_opt = acc_opt;
m_options = acc_opt;
m_creation_timestamp = EARLIEST_TIMESTAMP;
generate();
}
Expand All @@ -119,7 +116,7 @@ class account_base
uint64_t get_createtime() const { return m_creation_timestamp; }
void set_createtime(uint64_t val) { m_creation_timestamp = val; }

uint8_t get_account_options() const { return m_acc_opt; }
const acc_options& get_account_options() const { return m_options; }
bool is_kurz() const { return m_keys.m_spend_secret_key == m_keys.m_view_secret_key; }

bool has_25word_seed() const;
Expand All @@ -140,17 +137,30 @@ class account_base

static constexpr uint64_t EARLIEST_TIMESTAMP = 1483228800; // 01-01-2017 00:00


BEGIN_KV_SERIALIZE_MAP(account_base)
KV_SERIALIZE(m_keys)
KV_SERIALIZE(m_creation_timestamp)
KV_SERIALIZE(m_acc_opt)
do {
uint8_t acc_opt;
if(!is_store)
{
if(epee::serialization::selector<is_store>::serialize(acc_opt, stg, hparent_section, "m_acc_opt"))
const_cast<account_base&>(this_ref).m_options.set_opt_value(acc_opt);
}
else
{
acc_opt = this_ref.m_options.get_opt_value();
epee::serialization::selector<is_store>::serialize(acc_opt, stg, hparent_section, "m_acc_opt");
}
} while (0);
END_KV_SERIALIZE_MAP()

private:
private:
crypto::secret_key_16 generate();

account_keys m_keys = account_keys();
uint64_t m_creation_timestamp;
uint8_t m_acc_opt;
acc_options m_options;
};
}
124 changes: 124 additions & 0 deletions src/cryptonote_basic/account_opt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright (c) 2020, Ryo Currency Project
// Portions copyright (c) 2014-2018, The Monero Project
//
// Portions of this file are available under BSD-3 license. Please see ORIGINAL-LICENSE for details
// All rights reserved.
//
// Authors and copyright holders give permission for following:
//
// 1. Redistribution and use in source and binary forms WITHOUT modification.
//
// 2. Modification of the source form for your own personal use.
//
// As long as the following conditions are met:
//
// 3. You must not distribute modified copies of the work to third parties. This includes
// posting the work online, or hosting copies of the modified work for download.
//
// 4. Any derivative version of this work is also covered by this license, including point 8.
//
// 5. Neither the name of the copyright holders nor the names of the authors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// 6. You agree that this licence is governed by and shall be construed in accordance
// with the laws of England and Wales.
//
// 7. You agree to submit all disputes arising out of or in connection with this licence
// to the exclusive jurisdiction of the Courts of England and Wales.
//
// Authors and copyright holders agree that:
//
// 8. This licence expires and the work covered by it is released into the
// public domain on 1st of February 2021
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers

#include <inttypes.h>

struct acc_options
{
static constexpr uint8_t ACC_OPT_UNKNOWN = 0;
static constexpr uint8_t ACC_OPT_KURZ_ADDRESS = 1;
static constexpr uint8_t ACC_OPT_LONG_ADDRESS = 2;

static constexpr uint64_t BC_HEIGHT_PERIOD = 100000;
static constexpr uint64_t BC_HEIGHT_ZERO_V = 300000;

acc_options() : acc_opt_val(0) {}

acc_options(uint8_t keying_type) : acc_opt_val(0)
{
set_keying_type(keying_type);
}

acc_options(uint8_t keying_type, uint64_t bc_height) : acc_opt_val(0)
{
set_keying_type(keying_type);
set_seed_restore_height(bc_height);
}

uint8_t get_keying_type() const
{
if(acc_opt_val == 0)
return ACC_OPT_UNKNOWN;
else if((acc_opt_val & 3) == 1)
return ACC_OPT_KURZ_ADDRESS;
else if((acc_opt_val & 3) == 2)
return ACC_OPT_LONG_ADDRESS;
else
return ACC_OPT_UNKNOWN;
}

void set_keying_type(uint8_t type)
{
acc_opt_val &= 0xfc;
if(type == ACC_OPT_KURZ_ADDRESS)
acc_opt_val |= 0x01;
else if(type == ACC_OPT_LONG_ADDRESS)
acc_opt_val |= 0x02;
}

void set_seed_restore_height(uint64_t bc_height)
{
if(bc_height <= BC_HEIGHT_ZERO_V)
return;

bc_height -= BC_HEIGHT_ZERO_V;
bc_height /= BC_HEIGHT_PERIOD;
bc_height = std::min<uint64_t>(bc_height, 15); // 4 bits
acc_opt_val |= bc_height << 4;
}

uint64_t get_seed_restore_height()
{
uint64_t height = acc_opt_val >> 4;
if(height == 0)
return 0;
else
return height * BC_HEIGHT_PERIOD + BC_HEIGHT_ZERO_V;
}

uint8_t get_opt_value() const
{
return acc_opt_val;
}

void set_opt_value(uint8_t v)
{
acc_opt_val = v;
}

private:
uint8_t acc_opt_val = 0;
};
45 changes: 28 additions & 17 deletions src/simplewallet/simplewallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3022,7 +3022,8 @@ bool simple_wallet::init(const boost::program_options::variables_map &vm)
if(m_electrum_seed.empty())
{
std::string kurz_addr = input_line(tr("Would you like to use a shorter address format without a viewkey? (Y/Yes/N/No): "));
r = new_wallet(vm, get_mnemonic_language(false), nullptr, command_line::is_yes(kurz_addr) ? cryptonote::ACC_OPT_KURZ_ADDRESS : cryptonote::ACC_OPT_LONG_ADDRESS);
uint8_t key_type = command_line::is_yes(kurz_addr) ? acc_options::ACC_OPT_KURZ_ADDRESS : acc_options::ACC_OPT_LONG_ADDRESS;
r = new_wallet(vm, get_mnemonic_language(false), acc_options(key_type), nullptr);
}
else
{
Expand All @@ -3042,20 +3043,17 @@ bool simple_wallet::init(const boost::program_options::variables_map &vm)
{
uint32_t version;
bool connected = try_connect_to_daemon(false, &version);
std::string prompt = fmt::format("Restore from specific blockchain height (optional, default {}),\n"
"or alternatively from specific date (YYYY-MM-DD): ", m_restore_height);

while(true)
{
std::string heightstr;
if(!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
heightstr = input_line("Restore from specific blockchain height (optional, default 0): ");
else
heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): ");
heightstr = input_line(prompt);
if(std::cin.eof())
return false;
if(heightstr.empty())
{
m_restore_height = 0;
break;
}
try
{
m_restore_height = boost::lexical_cast<uint64_t>(heightstr);
Expand Down Expand Up @@ -3302,21 +3300,24 @@ bool simple_wallet::new_wallet_from_seed(const boost::program_options::variables

std::string language;
crypto::secret_key_16 seed_14;
uint8_t seed_extra;
acc_options seed_extra;
crypto::secret_key seed_25;
bool decode_14 = false, decode_25 = false;
if(wseed.size() == 12 || wseed.size() == 14)
{
decode_14 = crypto::Electrum14Words::words_to_bytes(seed, seed_14, seed_extra, language);
uint8_t extra_val;
decode_14 = crypto::Electrum14Words::words_to_bytes(seed, seed_14, extra_val, language);

if(wseed.size() == 12)
{
std::string kurz_addr = input_line(tr("Was the wallet created as a kurz address? (Y/Yes/N/No): "));
if(command_line::is_yes(kurz_addr))
seed_extra = cryptonote::ACC_OPT_KURZ_ADDRESS;
seed_extra = acc_options(acc_options::ACC_OPT_KURZ_ADDRESS);
else
seed_extra = cryptonote::ACC_OPT_LONG_ADDRESS;
seed_extra = acc_options(acc_options::ACC_OPT_LONG_ADDRESS);
}
else
seed_extra.set_opt_value(extra_val);
}
else if(wseed.size() >= 24 && wseed.size() <= 26)
{
Expand All @@ -3335,7 +3336,16 @@ bool simple_wallet::new_wallet_from_seed(const boost::program_options::variables
}

if(decode_14)
return new_wallet(vm, language, &seed_14, seed_extra);
{
if(new_wallet(vm, language, seed_extra, &seed_14))
{
if(command_line::is_arg_defaulted(vm, arg_restore_height))
m_restore_height = seed_extra.get_seed_restore_height();
return true;
}
else
return false;
}
else
return restore_legacy_wallet(vm, language, seed_25);
}
Expand All @@ -3355,7 +3365,7 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> simple_wal
}

//----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const boost::program_options::variables_map &vm, const std::string& seed_lang, const crypto::secret_key_16 *seed, uint8_t seed_extra)
bool simple_wallet::new_wallet(const boost::program_options::variables_map &vm, const std::string& seed_lang, acc_options seed_extra, const crypto::secret_key_16 *seed)
{
auto rc = make_new_wrapped(vm, password_prompter);
m_wallet = std::move(rc.first);
Expand All @@ -3378,7 +3388,8 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map &vm,
crypto::secret_key_16 recovery_val;
try
{
recovery_val = m_wallet->generate_new(m_wallet_file, std::move(rc.second).password(), seed, seed_extra, create_address_file);
seed_extra.set_seed_restore_height(m_wallet->estimate_blockchain_height());
recovery_val = m_wallet->generate_new(m_wallet_file, std::move(rc.second).password(), seed_extra, seed, create_address_file);
GULPS_PRINT_BOLD(tr("Generated new wallet: "), m_wallet->get_account().get_public_address_str(m_wallet->nettype()));
if(!m_wallet->get_account().is_kurz())
GULPS_PRINT_SECRET(tr("View key: "), string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key));
Expand All @@ -3392,9 +3403,9 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map &vm,
// convert rng value to electrum-style word list
std::string electrum_words;
if(seed == nullptr)
crypto::Electrum14Words::bytes_to_words(recovery_val, seed_extra, electrum_words, m_wallet->get_seed_language());
crypto::Electrum14Words::bytes_to_words(recovery_val, seed_extra.get_opt_value(), electrum_words, m_wallet->get_seed_language());
else
crypto::Electrum14Words::bytes_to_words(*seed, seed_extra, electrum_words, m_wallet->get_seed_language());
crypto::Electrum14Words::bytes_to_words(*seed, seed_extra.get_opt_value(), electrum_words, m_wallet->get_seed_language());

GULPS_PRINT_OK("**********************************************************************\n",
tr("Your wallet has been generated!\n"
Expand Down
2 changes: 1 addition & 1 deletion src/simplewallet/simplewallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class simple_wallet : public tools::i_wallet2_callback
const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter);

bool new_wallet_from_seed(const boost::program_options::variables_map &vm, std::string seed);
bool new_wallet(const boost::program_options::variables_map &vm, const std::string& seed_lang, const crypto::secret_key_16 *seed = nullptr, uint8_t seed_extra = cryptonote::ACC_OPT_LONG_ADDRESS);
bool new_wallet(const boost::program_options::variables_map &vm, const std::string& seed_lang, acc_options seed_extra, const crypto::secret_key_16 *seed = nullptr);
bool restore_legacy_wallet(const boost::program_options::variables_map &vm, const std::string& seed_lang, const crypto::secret_key &seed_legacy);

bool new_wallet(const boost::program_options::variables_map &vm, const cryptonote::account_public_address &address,
Expand Down
12 changes: 6 additions & 6 deletions src/wallet/wallet2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ bool wallet2::get_seed(std::string &electrum_words, bool &short_seed) const
else
{
crypto::secret_key_16 key = get_account().get_keys().m_short_seed;
if(!crypto::Electrum14Words::bytes_to_words(key, get_account().get_account_options(), electrum_words, seed_language))
if(!crypto::Electrum14Words::bytes_to_words(key, get_account().get_account_options().get_opt_value(), electrum_words, seed_language))
{
std::cout << "Failed to create seed from key for language: " << seed_language << std::endl;
return false;
Expand Down Expand Up @@ -2979,7 +2979,7 @@ void wallet2::generate_legacy(const std::string &wallet_, const epee::wipeable_s
store();
}

crypto::secret_key_16 wallet2::generate_new(const std::string &wallet_, const epee::wipeable_string &password, const crypto::secret_key_16 *seed, uint8_t extra, bool create_address_file)
crypto::secret_key_16 wallet2::generate_new(const std::string &wallet_, const epee::wipeable_string &password, acc_options extra, const crypto::secret_key_16 *seed, bool create_address_file)
{
clear();
prepare_file_names(wallet_);
Expand Down Expand Up @@ -8195,10 +8195,10 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
uint64_t wallet2::get_approximate_blockchain_height() const
{
if (m_nettype == TESTNET) return 0;
// time of v4 fork
const time_t fork_time = 1530990884;
// v4 fork block
const uint64_t fork_block = 150000;
// time of v9 fork
const time_t fork_time = 1589775672;
// v9 fork block
const uint64_t fork_block = 388000;

// Calculated blockchain height
time_t current_time = time(NULL);
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/wallet2.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ class wallet2
*/
void generate_legacy(const std::string &wallet_, const epee::wipeable_string &password, const crypto::secret_key &seed_legacy, bool create_address_file = false);

crypto::secret_key_16 generate_new(const std::string &wallet_, const epee::wipeable_string &password, const crypto::secret_key_16 *seed = nullptr, uint8_t extra = cryptonote::ACC_OPT_LONG_ADDRESS, bool create_address_file = false);
crypto::secret_key_16 generate_new(const std::string &wallet_, const epee::wipeable_string &password, acc_options extra, const crypto::secret_key_16 *seed = nullptr, bool create_address_file = false);
/*!
* \brief Creates a wallet from a public address and a spend/view secret key pair.
* \param wallet_ Name of wallet file
Expand Down

0 comments on commit 7139cfa

Please sign in to comment.