1
0
Fork 0
forked from lthn/blockchain

Merge branch 'auditability' into predevelop

This commit is contained in:
sowle 2020-06-02 13:26:22 +03:00
commit ab1ef9cf5e
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
41 changed files with 2523 additions and 386 deletions

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2020 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Copyright (c) 2012-2013 The Boolberry developers
@ -37,3 +37,21 @@ namespace std { \
} \
}; \
}
namespace std
{
// this allows using std::pair<> as a key in unordered std containers
template <class T1, class T2>
struct hash<pair<T1, T2>>
{
size_t operator()(const pair<T1, T2>& p) const
{
auto hash1 = hash<T1>{}(p.first);
auto hash2 = hash<T2>{}(p.second);
return hash1 ^ hash2;
}
};
} // namespace std

View file

@ -48,8 +48,6 @@ namespace tools
{
using namespace std;
const int NUMWORDS = 1626;
const map<string,uint32_t> wordsMap = {
{"like", 0},
{"just", 1},
@ -3358,7 +3356,7 @@ namespace tools
throw runtime_error("Invalid binary data size for mnemonic encoding");
// 4 bytes -> 3 words. 8 digits base 16 -> 3 digits base 1626
string res;
for (unsigned int i=0; i < binary.size() / 4; i++, res += ' ')
for (unsigned int i=0; i < binary.size() / 4; i++)
{
const uint32_t* val =
reinterpret_cast<const uint32_t*>(&binary[i * 4]);
@ -3369,8 +3367,9 @@ namespace tools
res += wordsArray[w1] + " ";
res += wordsArray[w2] + " ";
res += wordsArray[w3];
res += wordsArray[w3] + " ";
}
res.erase(--res.end()); // remove trailing space
return res;
}
std::string word_by_num(uint32_t n)

View file

@ -40,6 +40,8 @@ namespace tools
{
namespace mnemonic_encoding
{
constexpr int NUMWORDS = 1626;
std::vector<unsigned char> text2binary(const std::string& text);
std::string binary2text(const std::vector<unsigned char>& binary);
std::string word_by_num(uint32_t n);

View file

@ -85,34 +85,31 @@ namespace crypto {
memcpy(&res, tmp, 32);
}
void crypto_ops::keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec, size_t brain_wallet_seed_size)
{
unsigned char tmp[64] = { 0 };
if (!(sizeof(tmp) >= brain_wallet_seed_size))
{
throw std::runtime_error("size mismatch");
}
memcpy(tmp, a_part, brain_wallet_seed_size);
cn_fast_hash(tmp, 32, (char*)&tmp[32]);
sc_reduce(tmp);
memcpy(&sec, tmp, 32);
ge_p3 point;
ge_scalarmult_base(&point, &sec);
ge_p3_tobytes(&pub, &point);
}
void crypto_ops::generate_brain_keys(public_key &pub, secret_key &sec, std::string& seed, size_t brain_wallet_seed_size)
void crypto_ops::keys_from_default(const unsigned char* a_part, public_key &pub, secret_key &sec, size_t keys_seed_binary_size)
{
std::vector<unsigned char> tmp_vector;
tmp_vector.resize(brain_wallet_seed_size, 0);
unsigned char *tmp = &tmp_vector[0];
generate_random_bytes(brain_wallet_seed_size, tmp);
seed.assign((const char*)tmp, brain_wallet_seed_size);
keys_from_default(tmp, pub, sec, brain_wallet_seed_size);
unsigned char tmp[64] = { 0 };
if (!(sizeof(tmp) >= keys_seed_binary_size))
{
throw std::runtime_error("size mismatch");
}
memcpy(tmp, a_part, keys_seed_binary_size);
cn_fast_hash(tmp, 32, (char*)&tmp[32]);
sc_reduce(tmp);
memcpy(&sec, tmp, 32);
ge_p3 point;
ge_scalarmult_base(&point, &sec);
ge_p3_tobytes(&pub, &point);
}
void crypto_ops::generate_seed_keys(public_key &pub, secret_key &sec, std::vector<unsigned char>& keys_seed_binary, size_t keys_seed_binary_size)
{
keys_seed_binary.resize(keys_seed_binary_size, 0);
generate_random_bytes(keys_seed_binary_size, keys_seed_binary.data());
keys_from_default(keys_seed_binary.data(), pub, sec, keys_seed_binary_size);
}
static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res)

View file

@ -74,10 +74,10 @@ namespace crypto {
static void generate_keys(public_key &, secret_key &);
friend void generate_keys(public_key &, secret_key &);
static void generate_brain_keys(public_key &, secret_key &, std::string& seed, size_t brain_wallet_seed_size);
friend void generate_brain_keys(public_key &, secret_key &, std::string& seed, size_t brain_wallet_seed_size);
static void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec, size_t brain_wallet_seed_size);
friend void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec, size_t brain_wallet_seed_size);
static void generate_seed_keys(public_key &pub, secret_key &sec, std::vector<unsigned char>& keys_seed_binary, size_t keys_seed_binary_size);
friend void generate_seed_keys(public_key &pub, secret_key &sec, std::vector<unsigned char>& keys_seed_binary, size_t keys_seed_binary_size);
static void keys_from_default(const unsigned char* a_part, public_key &pub, secret_key &sec, size_t keys_seed_binary_size);
friend void keys_from_default(const unsigned char* a_part, public_key &pub, secret_key &sec, size_t keys_seed_binary_size);
static void dependent_key(const secret_key& first, secret_key& second);
friend void dependent_key(const secret_key& first, secret_key& second);
static bool check_key(const public_key &);
@ -136,14 +136,14 @@ namespace crypto {
crypto_ops::generate_keys(pub, sec);
}
inline void generate_brain_keys(public_key &pub, secret_key &sec, std::string& seed, size_t brain_wallet_seed_size) {
crypto_ops::generate_brain_keys(pub, sec, seed, brain_wallet_seed_size);
inline void generate_seed_keys(public_key &pub, secret_key &sec, std::vector<unsigned char>& keys_seed_binary, size_t keys_seed_binary_size)
{
crypto_ops::generate_seed_keys(pub, sec, keys_seed_binary, keys_seed_binary_size);
}
inline void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec, size_t brain_wallet_seed_size)
inline void keys_from_default(const unsigned char* a_part, public_key &pub, secret_key &sec, size_t keys_seed_binary_size)
{
crypto_ops::keys_from_default(a_part, pub, sec, brain_wallet_seed_size);
crypto_ops::keys_from_default(a_part, pub, sec, keys_seed_binary_size);
}
inline void dependent_key(const secret_key& first, secret_key& second){
@ -290,8 +290,7 @@ namespace crypto {
bool m_ready;
};
}
} // namespace crypto
POD_MAKE_HASHABLE(crypto, public_key)
POD_MAKE_COMPARABLE(crypto, secret_key)

View file

@ -17,15 +17,10 @@
using namespace std;
DISABLE_VS_WARNINGS(4244 4345)
//DISABLE_VS_WARNINGS(4244 4345)
namespace currency
{
//-----------------------------------------------------------------
account_base::account_base()
{
@ -37,18 +32,22 @@ namespace currency
// fill sensitive data with random bytes
crypto::generate_random_bytes(sizeof m_keys.spend_secret_key, &m_keys.spend_secret_key);
crypto::generate_random_bytes(sizeof m_keys.view_secret_key, &m_keys.view_secret_key);
crypto::generate_random_bytes(m_seed.size(), &m_seed[0]);
if (m_keys_seed_binary.size())
crypto::generate_random_bytes(m_keys_seed_binary.size(), &m_keys_seed_binary[0]);
// clear
m_keys = account_keys();
m_creation_timestamp = 0;
m_seed.clear();
m_keys_seed_binary.clear();
}
//-----------------------------------------------------------------
void account_base::generate()
void account_base::generate(bool auditable /* = false */)
{
generate_brain_keys(m_keys.account_address.spend_public_key, m_keys.spend_secret_key, m_seed, BRAINWALLET_DEFAULT_SEED_SIZE);
dependent_key(m_keys.spend_secret_key, m_keys.view_secret_key);
if (auditable)
m_keys.account_address.flags = ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE;
crypto::generate_seed_keys(m_keys.account_address.spend_public_key, m_keys.spend_secret_key, m_keys_seed_binary, BRAINWALLET_DEFAULT_SEED_SIZE);
crypto::dependent_key(m_keys.spend_secret_key, m_keys.view_secret_key);
if (!crypto::secret_key_to_public_key(m_keys.view_secret_key, m_keys.account_address.view_public_key))
throw std::runtime_error("Failed to create public view key");
@ -61,68 +60,122 @@ namespace currency
return m_keys;
}
//-----------------------------------------------------------------
std::string account_base::get_restore_data() const
{
return m_seed;
}
//-----------------------------------------------------------------
std::string account_base::get_restore_braindata() const
{
std::string restore_buff = get_restore_data();
if (restore_buff.empty())
if (m_keys_seed_binary.empty())
return "";
std::vector<unsigned char> v;
v.assign((unsigned char*)restore_buff.data(), (unsigned char*)restore_buff.data() + restore_buff.size());
std::string seed_brain_data = tools::mnemonic_encoding::binary2text(v);
std::string keys_seed_text = tools::mnemonic_encoding::binary2text(m_keys_seed_binary);
std::string timestamp_word = currency::get_word_from_timstamp(m_creation_timestamp);
seed_brain_data = seed_brain_data + timestamp_word;
return seed_brain_data;
// floor creation time to WALLET_BRAIN_DATE_QUANTUM to make checksum calculation stable
uint64_t creation_timestamp_rounded = get_timstamp_from_word(timestamp_word);
constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum
crypto::hash h = crypto::cn_fast_hash(m_keys_seed_binary.data(), m_keys_seed_binary.size());
*reinterpret_cast<uint64_t*>(&h) = creation_timestamp_rounded;
h = crypto::cn_fast_hash(&h, sizeof h);
uint64_t h_64 = *reinterpret_cast<uint64_t*>(&h);
uint16_t checksum = h_64 % (checksum_max + 1);
uint8_t auditable_flag = 0;
if (m_keys.account_address.flags & ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE)
auditable_flag = 1;
uint64_t auditable_flag_and_checksum = (auditable_flag & 1) | (checksum << 1);
std::string auditable_flag_and_checksum_word = tools::mnemonic_encoding::word_by_num(auditable_flag_and_checksum);
return keys_seed_text + " " + timestamp_word + " " + auditable_flag_and_checksum_word;
}
//-----------------------------------------------------------------
bool account_base::restore_keys(const std::string& restore_data)
std::string account_base::get_awo_blob() const
{
//CHECK_AND_ASSERT_MES(restore_data.size() == ACCOUNT_RESTORE_DATA_SIZE, false, "wrong restore data size");
if (restore_data.size() == BRAINWALLET_DEFAULT_SEED_SIZE)
{
crypto::keys_from_default((unsigned char*)restore_data.data(), m_keys.account_address.spend_public_key, m_keys.spend_secret_key, BRAINWALLET_DEFAULT_SEED_SIZE);
}
else
{
LOG_ERROR("wrong restore data size=" << restore_data.size());
return false;
}
m_seed = restore_data;
return get_public_address_str() + ":" +
epee::string_tools::pod_to_hex(m_keys.view_secret_key) +
(m_creation_timestamp ? ":" : "") + (m_creation_timestamp ? epee::string_tools::num_to_string_fast(m_creation_timestamp) : "");
}
//-----------------------------------------------------------------
bool account_base::restore_keys(const std::vector<unsigned char>& keys_seed_binary)
{
CHECK_AND_ASSERT_MES(keys_seed_binary.size() == BRAINWALLET_DEFAULT_SEED_SIZE, false, "wrong restore data size: " << keys_seed_binary.size());
crypto::keys_from_default(keys_seed_binary.data(), m_keys.account_address.spend_public_key, m_keys.spend_secret_key, keys_seed_binary.size());
crypto::dependent_key(m_keys.spend_secret_key, m_keys.view_secret_key);
bool r = crypto::secret_key_to_public_key(m_keys.view_secret_key, m_keys.account_address.view_public_key);
CHECK_AND_ASSERT_MES(r, false, "failed to secret_key_to_public_key for view key");
set_createtime(0);
return true;
}
//-----------------------------------------------------------------
bool account_base::restore_keys_from_braindata(const std::string& restore_data_)
bool account_base::restore_from_braindata(const std::string& seed_phrase)
{
//cut the last timestamp word from restore_dats
std::list<std::string> words;
boost::split(words, restore_data_, boost::is_space());
CHECK_AND_ASSERT_MES(words.size() == BRAINWALLET_DEFAULT_WORDS_COUNT, false, "Words count missmatch: " << words.size());
std::string timestamp_word = words.back();
words.erase(--words.end());
std::string restore_data_local = boost::algorithm::join(words, " ");
boost::split(words, seed_phrase, boost::is_space());
std::vector<unsigned char> bin = tools::mnemonic_encoding::text2binary(restore_data_local);
if (!bin.size())
std::string keys_seed_text, timestamp_word, auditable_flag_and_checksum_word;
if (words.size() == SEED_PHRASE_V1_WORDS_COUNT)
{
// 24 seed words + one timestamp word = 25 total
timestamp_word = words.back();
words.erase(--words.end());
keys_seed_text = boost::algorithm::join(words, " ");
}
else if (words.size() == SEED_PHRASE_V2_WORDS_COUNT)
{
// 24 seed words + one timestamp word + one flags & checksum = 26 total
auditable_flag_and_checksum_word = words.back();
words.erase(--words.end());
timestamp_word = words.back();
words.erase(--words.end());
keys_seed_text = boost::algorithm::join(words, " ");
}
else
{
LOG_ERROR("Invalid seed words count: " << words.size());
return false;
}
uint64_t auditable_flag_and_checksum = UINT64_MAX;
if (!auditable_flag_and_checksum_word.empty())
auditable_flag_and_checksum = tools::mnemonic_encoding::num_by_word(auditable_flag_and_checksum_word);
std::vector<unsigned char> keys_seed_binary = tools::mnemonic_encoding::text2binary(keys_seed_text);
CHECK_AND_ASSERT_MES(keys_seed_binary.size(), false, "text2binary failed to convert the given text"); // don't prints event incorrect seed into the log for security
std::string restore_buff((const char*)&bin[0], bin.size());
bool r = restore_keys(restore_buff);
CHECK_AND_ASSERT_MES(r, false, "restore_keys failed");
m_creation_timestamp = get_timstamp_from_word(timestamp_word);
bool auditable_flag = false;
// check the checksum if checksum word provided
if (auditable_flag_and_checksum != UINT64_MAX)
{
auditable_flag = (auditable_flag_and_checksum & 1) != 0; // auditable flag is the lower 1 bit
uint16_t checksum = auditable_flag_and_checksum >> 1; // checksum -- everything else
constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum
crypto::hash h = crypto::cn_fast_hash(keys_seed_binary.data(), keys_seed_binary.size());
*reinterpret_cast<uint64_t*>(&h) = m_creation_timestamp;
h = crypto::cn_fast_hash(&h, sizeof h);
uint64_t h_64 = *reinterpret_cast<uint64_t*>(&h);
uint16_t checksum_calculated = h_64 % (checksum_max + 1);
CHECK_AND_ASSERT_MES(checksum == checksum_calculated, false, "seed phase has invalid checksum: " << checksum_calculated << ", while " << checksum << " is expected, check your words");
}
bool r = restore_keys(keys_seed_binary);
CHECK_AND_ASSERT_MES(r, false, "restore_keys failed");
m_keys_seed_binary = keys_seed_binary;
if (auditable_flag)
m_keys.account_address.flags |= ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE;
return true;
}
//-----------------------------------------------------------------
bool account_base::restore_from_awo_blob(const std::string& awo_blob)
{
set_null();
bool r = parse_awo_blob(awo_blob, m_keys.account_address, m_keys.view_secret_key, m_creation_timestamp);
return r;
}
//-----------------------------------------------------------------
std::string account_base::get_public_address_str() const
{
//TODO: change this code into base 58
@ -133,7 +186,7 @@ namespace currency
{
// keep only:
// timestamp
// view pub & spend pub (public address)
// view pub & spend pub + flags (public address)
// view sec
// store to local tmp

View file

@ -12,7 +12,8 @@
#define BRAINWALLET_DEFAULT_SEED_SIZE 32
#define ACCOUNT_RESTORE_DATA_SIZE BRAINWALLET_DEFAULT_SEED_SIZE
#define BRAINWALLET_DEFAULT_WORDS_COUNT 25
#define SEED_PHRASE_V1_WORDS_COUNT 25
#define SEED_PHRASE_V2_WORDS_COUNT 26
@ -47,15 +48,15 @@ namespace currency
{
public:
account_base();
void generate();
void generate(bool auditable = false);
const account_keys& get_keys() const;
const account_public_address& get_public_address() const { return m_keys.account_address; };
std::string get_public_address_str() const;
std::string get_restore_data() const;
std::string get_restore_braindata() const;
bool restore_keys(const std::string& restore_data);
bool restore_keys_from_braindata(const std::string& restore_data);
std::string get_awo_blob() const;
bool restore_from_braindata(const std::string& seed_phrase);
bool restore_from_awo_blob(const std::string& awo_blob);
uint64_t get_createtime() const { return m_creation_timestamp; }
void set_createtime(uint64_t val) { m_creation_timestamp = val; }
@ -70,20 +71,26 @@ namespace currency
{
a & m_keys;
a & m_creation_timestamp;
a & m_seed;
a & m_keys_seed_binary;
}
static std::string vector_of_chars_to_string(const std::vector<unsigned char>& v) { return std::string(v.begin(), v.end()); }
static std::vector<unsigned char> string_to_vector_of_chars(const std::string& v) { return std::vector<unsigned char>(v.begin(), v.end()); }
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_keys)
KV_SERIALIZE(m_creation_timestamp)
KV_SERIALIZE(m_seed)
KV_SERIALIZE_CUSTOM_N(m_keys_seed_binary, std::string, vector_of_chars_to_string, string_to_vector_of_chars, "m_seed")
END_KV_SERIALIZE_MAP()
private:
void set_null();
bool restore_keys(const std::vector<unsigned char>& keys_seed_binary);
account_keys m_keys;
uint64_t m_creation_timestamp;
std::string m_seed;
std::vector<unsigned char> m_keys_seed_binary;
};

View file

@ -4671,6 +4671,26 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti
return true;
}
if (block_height <= m_core_runtime_config.hard_fork_02_starts_after_height)
{
// before hardfork 2
auto check_lambda = [&](const std::vector<payload_items_v>& container) -> bool
{
for (const auto& el : container)
{
const auto& type = el.type();
CHECK_AND_ASSERT_MES(type != typeid(tx_payer), false, "tx " << tx_id << " contains tx_payer which is not allowed on height " << block_height);
CHECK_AND_ASSERT_MES(type != typeid(tx_receiver), false, "tx " << tx_id << " contains tx_receiver which is not allowed on height " << block_height);
CHECK_AND_ASSERT_MES(type != typeid(extra_alias_entry), false, "tx " << tx_id << " contains extra_alias_entry which is not allowed on height " << block_height);
}
return true;
};
return check_lambda(tx.extra) && check_lambda(tx.attachment);
}
return true;
}
//------------------------------------------------------------------

View file

@ -15,7 +15,7 @@
#include <boost/mpl/unique.hpp>
#include <boost/mpl/list.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/vector/vector30.hpp>
#include <boost/type_traits/is_same.hpp>
#include <vector>
@ -60,9 +60,9 @@ namespace currency
/* */
/************************************************************************/
//since structure used in blockchain as a key accessor, then be sure that there is no padding inside
//since structure used in blockchain as a key accessor, then be sure that there is no padding inside
#pragma pack(push, 1)
struct account_public_address
struct account_public_address_old
{
crypto::public_key spend_public_key;
crypto::public_key view_public_key;
@ -72,13 +72,63 @@ namespace currency
FIELD(view_public_key)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(spend_public_key, "m_spend_public_key")
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(view_public_key, "m_view_public_key")
END_KV_SERIALIZE_MAP()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(spend_public_key, "m_spend_public_key")
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(view_public_key, "m_view_public_key")
END_KV_SERIALIZE_MAP()
};
#pragma pack(pop)
#define ACCOUNT_PUBLIC_ADDRESS_SERIZALIZATION_VER 1
#define ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE 0x01 // auditable address
//since structure used in blockchain as a key accessor, then be sure that there is no padding inside
#pragma pack(push, 1)
struct account_public_address
{
crypto::public_key spend_public_key;
crypto::public_key view_public_key;
uint8_t flags;
DEFINE_SERIALIZATION_VERSION(ACCOUNT_PUBLIC_ADDRESS_SERIZALIZATION_VER)
BEGIN_SERIALIZE_OBJECT()
FIELD(spend_public_key)
FIELD(view_public_key)
FIELD(flags)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(spend_public_key, "m_spend_public_key")
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(view_public_key, "m_view_public_key")
KV_SERIALIZE(flags)
END_KV_SERIALIZE_MAP()
bool is_auditable() const
{
return (flags & ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE) != 0;
}
static account_public_address from_old(const account_public_address_old& rhs)
{
account_public_address result = AUTO_VAL_INIT(result);
result.spend_public_key = rhs.spend_public_key;
result.view_public_key = rhs.view_public_key;
return result;
}
account_public_address_old to_old() const
{
account_public_address_old result = AUTO_VAL_INIT(result);
result.spend_public_key = spend_public_key;
result.view_public_key = view_public_key;
return result;
}
};
#pragma pack(pop)
const static account_public_address null_pub_addr = AUTO_VAL_INIT(null_pub_addr);
typedef std::vector<crypto::signature> ring_signature;
@ -226,9 +276,30 @@ namespace currency
END_SERIALIZE()
};
struct tx_payer_old
{
account_public_address_old acc_addr;
BEGIN_SERIALIZE()
FIELD(acc_addr)
END_SERIALIZE()
};
struct tx_payer
{
account_public_address acc_addr;
tx_payer() = default;
tx_payer(const tx_payer_old& old) : acc_addr(account_public_address::from_old(old.acc_addr)) {}
account_public_address acc_addr{};
BEGIN_SERIALIZE()
FIELD(acc_addr)
END_SERIALIZE()
};
struct tx_receiver_old
{
account_public_address_old acc_addr;
BEGIN_SERIALIZE()
FIELD(acc_addr)
@ -237,7 +308,10 @@ namespace currency
struct tx_receiver
{
account_public_address acc_addr;
tx_receiver() = default;
tx_receiver(const tx_receiver_old& old) : acc_addr(account_public_address::from_old(old.acc_addr)) {}
account_public_address acc_addr{};
BEGIN_SERIALIZE()
FIELD(acc_addr)
@ -299,8 +373,42 @@ namespace currency
};
struct extra_alias_entry_base_old
{
account_public_address_old m_address;
std::string m_text_comment;
std::vector<crypto::secret_key> m_view_key; // only one or zero elments expected (std::vector is using as memory efficient container for such a case)
std::vector<crypto::signature> m_sign; // only one or zero elments expected (std::vector is using as memory efficient container for such a case)
BEGIN_SERIALIZE()
FIELD(m_address)
FIELD(m_text_comment)
FIELD(m_view_key)
FIELD(m_sign)
END_SERIALIZE()
};
struct extra_alias_entry_old : public extra_alias_entry_base_old
{
std::string m_alias;
BEGIN_SERIALIZE()
FIELD(m_alias)
FIELDS(*static_cast<extra_alias_entry_base_old*>(this))
END_SERIALIZE()
};
struct extra_alias_entry_base
{
extra_alias_entry_base() = default;
extra_alias_entry_base(const extra_alias_entry_base_old& old)
: m_address(account_public_address::from_old(old.m_address))
, m_text_comment(old.m_text_comment)
, m_view_key(old.m_view_key)
, m_sign(old.m_sign)
{
}
account_public_address m_address;
std::string m_text_comment;
std::vector<crypto::secret_key> m_view_key; // only one or zero elments expected (std::vector is using as memory efficient container for such a case)
@ -314,14 +422,32 @@ namespace currency
END_SERIALIZE()
};
struct extra_alias_entry: public extra_alias_entry_base
struct extra_alias_entry : public extra_alias_entry_base
{
extra_alias_entry() = default;
extra_alias_entry(const extra_alias_entry_old& old)
: extra_alias_entry_base(old)
, m_alias(old.m_alias)
{
}
std::string m_alias;
BEGIN_SERIALIZE()
FIELD(m_alias)
FIELDS(*static_cast<extra_alias_entry_base *>(this))
END_SERIALIZE()
FIELDS(*static_cast<extra_alias_entry_base*>(this))
END_SERIALIZE()
extra_alias_entry_old to_old() const
{
extra_alias_entry_old result = AUTO_VAL_INIT(result);
result.m_address = m_address.to_old();
result.m_text_comment = m_text_comment;
result.m_view_key = m_view_key;
result.m_sign = m_sign;
result.m_alias = m_alias;
return result;
}
};
@ -390,9 +516,10 @@ namespace currency
END_SERIALIZE()
};
typedef boost::mpl::vector<
tx_service_attachment, tx_comment, tx_payer, tx_receiver, tx_derivation_hint, std::string, tx_crypto_checksum, etc_tx_time, etc_tx_details_unlock_time, etc_tx_details_expiration_time,
etc_tx_details_flags, crypto::public_key, extra_attachment_info, extra_alias_entry, extra_user_data, extra_padding, etc_tx_uint16_t, etc_tx_details_unlock_time2
typedef boost::mpl::vector21<
tx_service_attachment, tx_comment, tx_payer_old, tx_receiver_old, tx_derivation_hint, std::string, tx_crypto_checksum, etc_tx_time, etc_tx_details_unlock_time, etc_tx_details_expiration_time,
etc_tx_details_flags, crypto::public_key, extra_attachment_info, extra_alias_entry_old, extra_user_data, extra_padding, etc_tx_uint16_t, etc_tx_details_unlock_time2,
tx_payer, tx_receiver, extra_alias_entry
> all_payload_types;
typedef boost::make_variant_over<all_payload_types>::type payload_items_v;
@ -604,7 +731,7 @@ SET_VARIANT_TAGS(currency::transaction, 5, "tx");
SET_VARIANT_TAGS(currency::block, 6, "block");
//attachment_v definitions
SET_VARIANT_TAGS(currency::tx_comment, 7, "comment");
SET_VARIANT_TAGS(currency::tx_payer, 8, "payer");
SET_VARIANT_TAGS(currency::tx_payer_old, 8, "payer");
SET_VARIANT_TAGS(std::string, 9, "string");
SET_VARIANT_TAGS(currency::tx_crypto_checksum, 10, "checksum");
SET_VARIANT_TAGS(currency::tx_derivation_hint, 11, "derivation_hint");
@ -618,7 +745,7 @@ SET_VARIANT_TAGS(currency::signed_parts, 17, "signed_outs");
//extra_v definitions
SET_VARIANT_TAGS(currency::extra_attachment_info, 18, "extra_attach_info");
SET_VARIANT_TAGS(currency::extra_user_data, 19, "user_data");
SET_VARIANT_TAGS(currency::extra_alias_entry, 20, "alias_entry");
SET_VARIANT_TAGS(currency::extra_alias_entry_old, 20, "alias_entry");
SET_VARIANT_TAGS(currency::extra_padding, 21, "extra_padding");
SET_VARIANT_TAGS(crypto::public_key, 22, "pub_key");
SET_VARIANT_TAGS(currency::etc_tx_uint16_t, 23, "etc_tx_uint16");
@ -629,7 +756,16 @@ SET_VARIANT_TAGS(uint64_t, 26, "uint64_t");
//etc
SET_VARIANT_TAGS(currency::etc_tx_time, 27, "etc_tx_time");
SET_VARIANT_TAGS(uint32_t, 28, "uint32_t");
SET_VARIANT_TAGS(currency::tx_receiver, 29, "payer");
SET_VARIANT_TAGS(currency::tx_receiver_old, 29, "payer"); // -- original
//SET_VARIANT_TAGS(currency::tx_receiver_old, 29, "receiver");
SET_VARIANT_TAGS(currency::etc_tx_details_unlock_time2, 30, "unlock_time2");
SET_VARIANT_TAGS(currency::tx_payer, 31, "payer2");
SET_VARIANT_TAGS(currency::tx_receiver, 32, "receiver2");
// @#@ TODO @#@
SET_VARIANT_TAGS(currency::extra_alias_entry, 33, "alias_entry2");
#undef SET_VARIANT_TAGS

View file

@ -29,10 +29,18 @@ namespace boost
template <class Archive>
inline void serialize(Archive &a, currency::account_public_address &x, const boost::serialization::version_type ver)
{
//a & x.version;
a & x.flags;
a & x.spend_public_key;
a & x.view_public_key;
}
template <class Archive>
inline void serialize(Archive &a, currency::account_public_address_old &x, const boost::serialization::version_type ver)
{
a & x.spend_public_key;
a & x.view_public_key;
}
template <class Archive>
inline void serialize(Archive &a, currency::txout_to_key &x, const boost::serialization::version_type ver)
@ -90,11 +98,24 @@ namespace boost
a & x.comment;
}
template <class Archive>
inline void serialize(Archive &a, currency::tx_payer_old &x, const boost::serialization::version_type ver)
{
a & x.acc_addr;
}
template <class Archive>
inline void serialize(Archive &a, currency::tx_payer &x, const boost::serialization::version_type ver)
{
a & x.acc_addr;
}
template <class Archive>
inline void serialize(Archive &a, currency::tx_receiver_old &x, const boost::serialization::version_type ver)
{
a & x.acc_addr;
}
template <class Archive>
inline void serialize(Archive &a, currency::tx_receiver &x, const boost::serialization::version_type ver)
{
@ -136,14 +157,6 @@ namespace boost
a & x.m_sign;
}
template <class Archive>
inline void serialize(Archive &a, currency::signed_parts &x, const boost::serialization::version_type ver)
{
a & x.n_outs;
a & x.n_extras;
}
template <class Archive>
inline void serialize(Archive &a, currency::extra_alias_entry &x, const boost::serialization::version_type ver)
{
@ -151,6 +164,29 @@ namespace boost
a & static_cast<currency::extra_alias_entry_base&>(x);
}
template <class Archive>
inline void serialize(Archive &a, currency::extra_alias_entry_base_old &x, const boost::serialization::version_type ver)
{
a & x.m_address;
a & x.m_text_comment;
a & x.m_view_key;
a & x.m_sign;
}
template <class Archive>
inline void serialize(Archive &a, currency::extra_alias_entry_old &x, const boost::serialization::version_type ver)
{
a & x.m_alias;
a & static_cast<currency::extra_alias_entry_base_old&>(x);
}
template <class Archive>
inline void serialize(Archive &a, currency::signed_parts &x, const boost::serialization::version_type ver)
{
a & x.n_outs;
a & x.n_extras;
}
template <class Archive>
inline void serialize(Archive &a, currency::extra_padding &x, const boost::serialization::version_type ver)
{

View file

@ -20,8 +20,11 @@
#define CURRENCY_MAX_BLOCK_NUMBER 500000000
#define CURRENCY_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used!
#define CURRENCY_TX_MAX_ALLOWED_OUTS 2000
#define CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX 197 // addresses start with 'Z'
#define CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX 0xc5 // addresses start with 'Zx'
#define CURRENCY_PUBLIC_INTEG_ADDRESS_BASE58_PREFIX 0x3678 // integrated addresses start with 'iZ'
#define CURRENCY_PUBLIC_INTEG_ADDRESS_V2_BASE58_PREFIX 0x36f8 // integrated addresses start with 'iZ' (new format)
#define CURRENCY_PUBLIC_AUDITABLE_ADDRESS_BASE58_PREFIX 0x98c8 // auditable addresses start with 'aZx'
#define CURRENCY_PUBLIC_AUDITABLE_INTEG_ADDRESS_BASE58_PREFIX 0x8a49 // auditable integrated addresses start with 'aiZX'
#define CURRENCY_MINED_MONEY_UNLOCK_WINDOW 10
#define CURRENT_TRANSACTION_VERSION 1
#define CURRENT_BLOCK_MAJOR_VERSION 1
@ -141,7 +144,8 @@
#define POS_MINIMUM_COINSTAKE_AGE 10 // blocks count
#define WALLET_FILE_SIGNATURE 0x1111012101101011LL //Bender's nightmare
#define WALLET_FILE_SIGNATURE_OLD 0x1111012101101011LL // Bender's nightmare
#define WALLET_FILE_SIGNATURE_V2 0x1111011201101011LL // another Bender's nightmare
#define WALLET_FILE_MAX_BODY_SIZE 0x88888888L //2GB
#define WALLET_FILE_MAX_KEYS_SIZE 10000 //
#define WALLET_BRAIN_DATE_OFFSET 1543622400
@ -216,7 +220,7 @@
#define BC_OFFERS_CURRENCY_MARKET_FILENAME "market.bin"
#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+66)
#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+67)
#define CURRENT_MEMPOOL_ARCHIVE_VER (CURRENCY_FORMATION_VERSION+31)

View file

@ -493,7 +493,17 @@ namespace currency
//-----------------------------------------------------------------------------------------------
bool core::add_new_block(const block& b, block_verification_context& bvc)
{
return m_blockchain_storage.add_new_block(b, bvc);
bool r = m_blockchain_storage.add_new_block(b, bvc);
if (r && bvc.m_added_to_main_chain)
{
uint64_t h = get_block_height(b);
auto& crc = m_blockchain_storage.get_core_runtime_config();
if (h == crc.hard_fork_01_starts_after_height + 1)
{ LOG_PRINT_GREEN("Hardfork 1 activated at height " << h, LOG_LEVEL_0); }
else if (h == crc.hard_fork_02_starts_after_height + 1)
{ LOG_PRINT_GREEN("Hardfork 2 activated at height " << h, LOG_LEVEL_0); }
}
return r;
}
//-----------------------------------------------------------------------------------------------
bool core::parse_block(const blobdata& block_blob, block& b, block_verification_context& bvc)

View file

@ -313,6 +313,46 @@ namespace currency
return string_tools::get_xtype_from_string(amount, str_amount);
}
//--------------------------------------------------------------------------------
bool parse_awo_blob(const std::string& awo_blob, account_public_address& address, crypto::secret_key& view_sec_key, uint64_t& creation_timestamp)
{
std::vector<std::string> parts;
boost::split(parts, awo_blob, [](char x){ return x == ':'; } );
if (parts.size() != 2 && parts.size() != 3)
return false;
if (!get_account_address_from_str(address, parts[0]))
return false;
if (!address.is_auditable())
return false;
if (!epee::string_tools::parse_tpod_from_hex_string(parts[1], view_sec_key))
return false;
crypto::public_key view_pub_key = AUTO_VAL_INIT(view_pub_key);
if (!crypto::secret_key_to_public_key(view_sec_key, view_pub_key))
return false;
if (view_pub_key != address.view_public_key)
return false;
creation_timestamp = 0;
if (parts.size() == 3)
{
// parse timestamp
int64_t ts = 0;
if (!epee::string_tools::string_to_num_fast(parts[2], ts))
return false;
if (ts < WALLET_BRAIN_DATE_OFFSET)
return false;
creation_timestamp = ts;
}
return true;
}
//--------------------------------------------------------------------------------
std::string print_stake_kernel_info(const stake_kernel& sk)
{
std::stringstream ss;
@ -421,13 +461,16 @@ namespace currency
rei.m_attachment_info = ai;
return true;
}
bool operator()(const extra_alias_entry& ae) const
{
ENSURE_ONETIME(was_alias, "alias");
rei.m_alias = ae;
return true;
}
bool operator()(const extra_alias_entry_old& ae) const
{
return operator()(static_cast<const extra_alias_entry&>(ae));
}
bool operator()(const extra_user_data& ud) const
{
ENSURE_ONETIME(was_userdata, "userdata");
@ -589,7 +632,12 @@ namespace currency
//out to key
txout_to_key tk;
tk.key = target_keys.back();
tk.mix_attr = tx_outs_attr;
if (de.addr.front().is_auditable()) // check only the first address because there's only one in this branch
tk.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX; // override mix_attr to 1 for auditable target addresses
else
tk.mix_attr = tx_outs_attr;
out.target = tk;
}
else
@ -1106,7 +1154,7 @@ namespace currency
//fill outputs
size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1
std::set<uint16_t> deriv_cache;
BOOST_FOREACH(const tx_destination_entry& dst_entr, shuffled_dsts)
for(const tx_destination_entry& dst_entr : shuffled_dsts)
{
CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount);
bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, tx_outs_attr);
@ -1251,7 +1299,7 @@ namespace currency
{
uint64_t date_offset = timestamp > WALLET_BRAIN_DATE_OFFSET ? timestamp - WALLET_BRAIN_DATE_OFFSET : 0;
uint64_t weeks_count = date_offset / WALLET_BRAIN_DATE_QUANTUM;
CHECK_AND_ASSERT_THROW_MES(weeks_count < std::numeric_limits<uint32_t>::max(), "internal error: unable to converto to uint32, val = " << weeks_count);
CHECK_AND_ASSERT_THROW_MES(weeks_count < std::numeric_limits<uint32_t>::max(), "internal error: unable to convert to uint32, val = " << weeks_count);
uint32_t weeks_count_32 = static_cast<uint32_t>(weeks_count);
return tools::mnemonic_encoding::word_by_num(weeks_count_32);
@ -2026,7 +2074,7 @@ namespace currency
//---------------------------------------------------------------
bool is_showing_sender_addres(const transaction& tx)
{
return have_type_in_variant_container<tx_payer>(tx.attachment);
return have_type_in_variant_container<tx_payer>(tx.attachment) || have_type_in_variant_container<tx_payer_old>(tx.attachment);
}
//---------------------------------------------------------------
bool is_mixin_tx(const transaction& tx)
@ -2181,6 +2229,10 @@ namespace currency
return true;
}
bool operator()(const extra_alias_entry_old& ee)
{
return operator()(static_cast<const extra_alias_entry&>(ee));
}
bool operator()(const extra_user_data& ee)
{
tv.type = "user_data";
@ -2214,7 +2266,13 @@ namespace currency
return true;
}
bool operator()(const tx_payer_old&)
{
tv.type = "payer_old";
tv.short_view = "(encrypted)";
return true;
}
bool operator()(const tx_receiver& ee)
{
//const tx_payer& ee = boost::get<tx_payer>(extra);
@ -2223,6 +2281,13 @@ namespace currency
return true;
}
bool operator()(const tx_receiver_old& ee)
{
tv.type = "receiver_old";
tv.short_view = "(encrypted)";
return true;
}
bool operator()(const tx_derivation_hint& ee)
{
tv.type = "derivation_hint";
@ -2511,12 +2576,24 @@ namespace currency
//-----------------------------------------------------------------------
std::string get_account_address_as_str(const account_public_address& addr)
{
return tools::base58::encode_addr(CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr));
if (addr.flags == 0)
return tools::base58::encode_addr(CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr.to_old())); // classic Zano address
if (addr.flags & ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE)
return tools::base58::encode_addr(CURRENCY_PUBLIC_AUDITABLE_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr)); // new format Zano address (auditable)
return tools::base58::encode_addr(CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr)); // new format Zano address (normal)
}
//-----------------------------------------------------------------------
std::string get_account_address_and_payment_id_as_str(const account_public_address& addr, const payment_id_t& payment_id)
{
return tools::base58::encode_addr(CURRENCY_PUBLIC_INTEG_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr) + payment_id);
if (addr.flags == 0)
return tools::base58::encode_addr(CURRENCY_PUBLIC_INTEG_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr.to_old()) + payment_id); // classic integrated Zano address
if (addr.flags & ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE)
return tools::base58::encode_addr(CURRENCY_PUBLIC_AUDITABLE_INTEG_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr) + payment_id); // new format integrated Zano address (auditable)
return tools::base58::encode_addr(CURRENCY_PUBLIC_INTEG_ADDRESS_V2_BASE58_PREFIX, t_serializable_object_to_blob(addr) + payment_id); // new format integrated Zano address (normal)
}
//-----------------------------------------------------------------------
bool get_account_address_from_str(account_public_address& addr, const std::string& str)
@ -2527,7 +2604,7 @@ namespace currency
//-----------------------------------------------------------------------
bool get_account_address_and_payment_id_from_str(account_public_address& addr, payment_id_t& payment_id, const std::string& str)
{
static const size_t addr_blob_size = sizeof(account_public_address);
payment_id.clear();
blobdata blob;
uint64_t prefix;
if (!tools::base58::decode_addr(str, prefix, blob))
@ -2536,42 +2613,88 @@ namespace currency
return false;
}
if (blob.size() < addr_blob_size)
if (blob.size() < sizeof(account_public_address_old))
{
LOG_PRINT_L1("Address " << str << " has invalid format: blob size is " << blob.size() << " which is less, than expected " << addr_blob_size);
LOG_PRINT_L1("Address " << str << " has invalid format: blob size is " << blob.size() << " which is less, than expected " << sizeof(account_public_address_old));
return false;
}
if (blob.size() > addr_blob_size + BC_PAYMENT_ID_SERVICE_SIZE_MAX)
if (blob.size() > sizeof(account_public_address) + BC_PAYMENT_ID_SERVICE_SIZE_MAX)
{
LOG_PRINT_L1("Address " << str << " has invalid format: blob size is " << blob.size() << " which is more, than allowed " << addr_blob_size + BC_PAYMENT_ID_SERVICE_SIZE_MAX);
LOG_PRINT_L1("Address " << str << " has invalid format: blob size is " << blob.size() << " which is more, than allowed " << sizeof(account_public_address) + BC_PAYMENT_ID_SERVICE_SIZE_MAX);
return false;
}
bool parse_as_old_format = false;
if (prefix == CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX)
{
// nothing
// normal address
if (blob.size() == sizeof(account_public_address_old))
{
parse_as_old_format = true;
}
else if (blob.size() == sizeof(account_public_address))
{
parse_as_old_format = false;
}
else
{
LOG_PRINT_L1("Account public address cannot be parsed from \"" << str << "\", incorrect size");
return false;
}
}
else if (prefix == CURRENCY_PUBLIC_AUDITABLE_ADDRESS_BASE58_PREFIX)
{
// auditable, parse as new format
parse_as_old_format = false;
}
else if (prefix == CURRENCY_PUBLIC_INTEG_ADDRESS_BASE58_PREFIX)
{
payment_id = blob.substr(addr_blob_size);
blob = blob.substr(0, addr_blob_size);
payment_id = blob.substr(sizeof(account_public_address_old));
blob = blob.substr(0, sizeof(account_public_address_old));
parse_as_old_format = true;
}
else if (prefix == CURRENCY_PUBLIC_AUDITABLE_INTEG_ADDRESS_BASE58_PREFIX || prefix == CURRENCY_PUBLIC_INTEG_ADDRESS_V2_BASE58_PREFIX)
{
payment_id = blob.substr(sizeof(account_public_address));
blob = blob.substr(0, sizeof(account_public_address));
parse_as_old_format = false;
}
else
{
LOG_PRINT_L1("Address " << str << " has wrong prefix " << prefix << ", expected " << CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX << " or " << CURRENCY_PUBLIC_INTEG_ADDRESS_BASE58_PREFIX);
LOG_PRINT_L1("Address " << str << " has wrong prefix " << prefix);
return false;
}
if (!::serialization::parse_binary(blob, addr))
if (parse_as_old_format)
{
LOG_PRINT_L1("Account public address keys can't be parsed for address \"" << str << "\"");
account_public_address_old addr_old = AUTO_VAL_INIT(addr_old);
if (!::serialization::parse_binary(blob, addr_old))
{
LOG_PRINT_L1("Account public address (old) cannot be parsed from \"" << str << "\"");
return false;
}
addr = account_public_address::from_old(addr_old);
}
else
{
if (!::serialization::parse_binary(blob, addr))
{
LOG_PRINT_L1("Account public address cannot be parsed from \"" << str << "\"");
return false;
}
}
if (payment_id.size() > BC_PAYMENT_ID_SERVICE_SIZE_MAX)
{
LOG_PRINT_L1("Failed to parse address from \"" << str << "\": payment id size exceeded: " << payment_id.size());
return false;
}
if (!crypto::check_key(addr.spend_public_key) || !crypto::check_key(addr.view_public_key))
{
LOG_PRINT_L1("Failed to validate address keys for address \"" << str << "\"");
LOG_PRINT_L1("Failed to validate address keys for public address \"" << str << "\"");
return false;
}

View file

@ -27,6 +27,7 @@
#include "blockchain_storage_basic.h"
#include "currency_format_utils_blocks.h"
#include "currency_format_utils_transactions.h"
#include "core_runtime_config.h"
// ------ get_tx_type_definition -------------
@ -238,6 +239,7 @@ namespace currency
bool check_inputs_types_supported(const transaction& tx);
bool check_outs_valid(const transaction& tx);
bool parse_amount(uint64_t& amount, const std::string& str_amount);
bool parse_awo_blob(const std::string& awo_blob, account_public_address& address, crypto::secret_key& view_sec_key, uint64_t& creation_timestamp);
@ -589,6 +591,51 @@ namespace currency
return boost::apply_visitor(input_amount_getter(), v);
}
//---------------------------------------------------------------
template <typename container_t>
void create_and_add_tx_payer_to_container_from_address(container_t& container, const account_public_address& addr, uint64_t top_block_height, const core_runtime_config& crc)
{
if (top_block_height > crc.hard_fork_02_starts_after_height)
{
// after hardfork 2
tx_payer result = AUTO_VAL_INIT(result);
result.acc_addr = addr;
container.push_back(result);
}
else
{
// before hardfork 2 -- add only if addr is not auditable
if (!addr.is_auditable())
{
tx_payer_old result = AUTO_VAL_INIT(result);
result.acc_addr = addr.to_old();
container.push_back(result);
}
}
}
//---------------------------------------------------------------
template <typename container_t>
void create_and_add_tx_receiver_to_container_from_address(container_t& container, const account_public_address& addr, uint64_t top_block_height, const core_runtime_config& crc)
{
if (top_block_height > crc.hard_fork_02_starts_after_height)
{
// after hardfork 2
tx_receiver result = AUTO_VAL_INIT(result);
result.acc_addr = addr;
container.push_back(result);
}
else
{
// before hardfork 2 -- add only if addr is not auditable
if (!addr.is_auditable())
{
tx_receiver_old result = AUTO_VAL_INIT(result);
result.acc_addr = addr.to_old();
container.push_back(result);
}
}
}
//---------------------------------------------------------------
//---------------------------------------------------------------
std::ostream& operator <<(std::ostream& o, const ref_by_id& r);
//---------------------------------------------------------------
#ifndef ANDROID_BUILD

View file

@ -107,8 +107,9 @@ namespace currency
return false;
}
//---------------------------------------------------------------
// callback should return true to continue iterating through the container
template <typename A, typename B, typename container_t, typename callback_t>
bool handle_2_alternative_types_in_variant_container(const container_t& container, callback_t& cb)
bool handle_2_alternative_types_in_variant_container(const container_t& container, callback_t cb)
{
bool found = false;
for (auto& item : container)
@ -122,7 +123,7 @@ namespace currency
else if (item.type() == typeid(B))
{
found = true;
if (!cb(boost::get<A>(item)))
if (!cb(boost::get<B>(item)))
break;
}
}

View file

@ -1587,7 +1587,7 @@ QString MainWindow::restore_wallet(const QString& param)
//return que_call2<view::restore_wallet_request>("restore_wallet", param, [this](const view::restore_wallet_request& owd, view::api_response& ar){
PREPARE_ARG_FROM_JSON(view::restore_wallet_request, owd);
PREPARE_RESPONSE(view::open_wallet_response, ar);
ar.error_code = m_backend.restore_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.restore_key, ar.response_data);
ar.error_code = m_backend.restore_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.restore_key, owd.auditable_watch_only, ar.response_data);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}

View file

@ -40,6 +40,7 @@ namespace
{
const command_line::arg_descriptor<std::string> arg_wallet_file = {"wallet-file", "Use wallet <arg>", ""};
const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", "Generate new wallet and save it to <arg> or <address>.wallet by default", ""};
const command_line::arg_descriptor<std::string> arg_generate_new_auditable_wallet = {"generate-new-auditable-wallet", "Generate new auditable wallet and store it to <arg>", ""};
const command_line::arg_descriptor<std::string> arg_daemon_address = {"daemon-address", "Use daemon instance at <host>:<port>", ""};
const command_line::arg_descriptor<std::string> arg_daemon_host = {"daemon-host", "Use daemon instance at host <arg> instead of localhost", ""};
const command_line::arg_descriptor<std::string> arg_password = {"password", "Wallet password", "", true};
@ -51,6 +52,7 @@ namespace
const command_line::arg_descriptor<bool> arg_do_pos_mining = { "do-pos-mining", "Do PoS mining", false, false };
const command_line::arg_descriptor<std::string> arg_pos_mining_reward_address = { "pos-mining-reward-address", "Block reward will be sent to the giving address if specified", "" };
const command_line::arg_descriptor<std::string> arg_restore_wallet = { "restore-wallet", "Restore wallet from the seed phrase and save it to <arg>", "" };
const command_line::arg_descriptor<std::string> arg_restore_awo_wallet = { "restore-awo-wallet", "Restore auditable watch-only wallet from address and view key. Use \"address:viewkey\" as argument", "" };
const command_line::arg_descriptor<bool> arg_offline_mode = { "offline-mode", "Don't connect to daemon, work offline (for cold-signing process)", false, true };
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
@ -219,6 +221,8 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), "Get transaction one-time secret key (r) for a given <txid>");
m_cmd_binder.set_handler("awo_blob", boost::bind(&simple_wallet::awo_blob, this, _1), "For auditable wallets: prints auditable watch-only blob for wallet's audit by a third party");
m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data");
m_cmd_binder.set_handler("save_watch_only", boost::bind(&simple_wallet::save_watch_only, this, _1), "save_watch_only <filename> <password> - save as watch-only wallet file.");
@ -270,9 +274,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
if (m_wallet_file.empty() && m_generate_new.empty() && m_restore_wallet.empty())
if (m_wallet_file.empty() && m_generate_new.empty() && m_restore_wallet.empty() && m_restore_awo_wallet.empty() && m_generate_new_aw.empty())
{
fail_msg_writer() << "you must specify --wallet-file, --generate-new-wallet or --restore-wallet";
fail_msg_writer() << "you must specify --wallet-file, --generate-new-wallet, --generate-new-auditable-wallet, --restore-wallet or --restore-awo-wallet";
return false;
}
@ -311,9 +315,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (!m_generate_new.empty())
{
bool r = new_wallet(m_generate_new, pwd_container.password());
CHECK_AND_ASSERT_MES(r, false, "account creation failed");
bool r = new_wallet(m_generate_new, pwd_container.password(), false);
CHECK_AND_ASSERT_MES(r, false, "failed to create new wallet");
}
else if (!m_generate_new_aw.empty())
{
bool r = new_wallet(m_generate_new_aw, pwd_container.password(), true);
CHECK_AND_ASSERT_MES(r, false, "failed to create new auditable wallet");
}
else if (!m_restore_wallet.empty())
{
@ -330,7 +338,25 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
bool r = restore_wallet(m_restore_wallet, restore_seed_container.password(), pwd_container.password());
bool r = restore_wallet(m_restore_wallet, restore_seed_container.password(), pwd_container.password(), false);
CHECK_AND_ASSERT_MES(r, false, "wallet restoring failed");
}
else if (!m_restore_awo_wallet.empty())
{
if (boost::filesystem::exists(m_restore_awo_wallet))
{
fail_msg_writer() << "file " << m_restore_awo_wallet << " already exists";
return false;
}
tools::password_container restore_addr_and_viewkey_container;
if (!restore_addr_and_viewkey_container.read_password("please, enter wallet auditable address, viewkey and timestamp, separated by a colon (\"address:viewkey:timestamp\"):\n"))
{
fail_msg_writer() << "failed to read seed phrase";
return false;
}
bool r = restore_wallet(m_restore_awo_wallet, restore_addr_and_viewkey_container.password(), pwd_container.password(), true);
CHECK_AND_ASSERT_MES(r, false, "wallet restoring failed");
}
else
@ -354,12 +380,14 @@ void simple_wallet::handle_command_line(const boost::program_options::variables_
{
m_wallet_file = command_line::get_arg(vm, arg_wallet_file);
m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
m_generate_new_aw = command_line::get_arg(vm, arg_generate_new_auditable_wallet);
m_daemon_address = command_line::get_arg(vm, arg_daemon_address);
m_daemon_host = command_line::get_arg(vm, arg_daemon_host);
m_daemon_port = command_line::get_arg(vm, arg_daemon_port);
m_do_not_set_date = command_line::get_arg(vm, arg_dont_set_date);
m_do_pos_mining = command_line::get_arg(vm, arg_do_pos_mining);
m_restore_wallet = command_line::get_arg(vm, arg_restore_wallet);
m_restore_awo_wallet = command_line::get_arg(vm, arg_restore_awo_wallet);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::try_connect_to_daemon()
@ -374,7 +402,7 @@ bool simple_wallet::try_connect_to_daemon()
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password)
bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password, bool create_auditable_wallet)
{
m_wallet_file = wallet_file;
@ -383,10 +411,10 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas
m_wallet->set_do_rise_transfer(false);
try
{
m_wallet->generate(epee::string_encoding::utf8_to_wstring(m_wallet_file), password);
message_writer(epee::log_space::console_color_white, true) << "Generated new wallet: " << m_wallet->get_account().get_public_address_str();
m_wallet->generate(epee::string_encoding::utf8_to_wstring(m_wallet_file), password, create_auditable_wallet);
message_writer(epee::log_space::console_color_white, true) << "Generated new " << (create_auditable_wallet ? "AUDITABLE" : "") << " wallet: " << m_wallet->get_account().get_public_address_str();
std::cout << "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().view_secret_key) << std::endl << std::flush;
if(m_do_not_set_date)
if (m_do_not_set_date)
m_wallet->reset_creation_time(0);
if (m_print_brain_wallet)
@ -407,16 +435,11 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas
success_msg_writer() <<
"**********************************************************************\n" <<
"Your wallet has been generated.\n" <<
"To start synchronizing with the daemon use \"refresh\" command.\n" <<
"Use \"help\" command to see the list of available commands.\n" <<
"Always use \"exit\" command when closing simplewallet to save\n" <<
"current session's state. Otherwise, you will possibly need to synchronize \n" <<
"your wallet again. Your wallet key is NOT under risk anyway.\n" <<
"**********************************************************************";
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::restore_wallet(const std::string &wallet_file, const std::string &restore_seed, const std::string& password)
bool simple_wallet::restore_wallet(const std::string &wallet_file, const std::string &seed_or_awo_blob, const std::string& password, bool auditable_watch_only)
{
m_wallet_file = wallet_file;
@ -425,15 +448,24 @@ bool simple_wallet::restore_wallet(const std::string &wallet_file, const std::st
m_wallet->set_do_rise_transfer(false);
try
{
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, restore_seed);
message_writer(epee::log_space::console_color_white, true) << "Wallet restored: " << m_wallet->get_account().get_public_address_str();
std::cout << "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().view_secret_key) << std::endl << std::flush;
if (auditable_watch_only)
{
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_awo_blob, true);
message_writer(epee::log_space::console_color_white, true) << "Auditable watch-only wallet restored: " << m_wallet->get_account().get_public_address_str();
}
else
{
// normal wallet
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_awo_blob, false);
message_writer(epee::log_space::console_color_white, true) << "Wallet restored: " << m_wallet->get_account().get_public_address_str();
std::cout << "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().view_secret_key) << std::endl << std::flush;
}
if (m_do_not_set_date)
m_wallet->reset_creation_time(0);
}
catch (const std::exception& e)
{
fail_msg_writer() << "failed to restore wallet: " << e.what();
fail_msg_writer() << "failed to restore wallet, check your " << (auditable_watch_only ? "awo blob!" : "seed phrase!") << ENDL << e.what();
return false;
}
@ -462,7 +494,7 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
try
{
m_wallet->load(epee::string_encoding::utf8_to_wstring(m_wallet_file), password);
message_writer(epee::log_space::console_color_white, true) << "Opened" << (m_wallet->is_watch_only() ? " watch-only" : "") << " wallet: " << m_wallet->get_account().get_public_address_str();
message_writer(epee::log_space::console_color_white, true) << "Opened" << (m_wallet->is_auditable() ? " auditable" : "") << (m_wallet->is_watch_only() ? " watch-only" : "") << " wallet: " << m_wallet->get_account().get_public_address_str();
if (m_print_brain_wallet)
std::cout << "Brain wallet: " << m_wallet->get_account().get_restore_braindata() << std::endl << std::flush;
@ -592,22 +624,40 @@ void simple_wallet::on_new_block(uint64_t height, const currency::block& block)
m_refresh_progress_reporter.update(height, false);
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::on_money_received(uint64_t height, const currency::transaction& tx, size_t out_index)
std::string print_money_trailing_zeros_replaced_with_spaces(uint64_t amount)
{
message_writer(epee::log_space::console_color_green, false) <<
"Height " << height <<
", transaction " << get_transaction_hash(tx) <<
", received " << print_money(tx.vout[out_index].amount);
m_refresh_progress_reporter.update(height, true);
std::string s = print_money(amount);
size_t p = s.find_last_not_of('0');
if (p != std::string::npos)
{
if (s[p] == '.')
++p;
size_t l = s.length() - p - 1;
return s.replace(p + 1, l, l, ' ');
}
return s;
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::on_money_spent(uint64_t height, const currency::transaction& in_tx, size_t out_index, const currency::transaction& spend_tx)
void simple_wallet::on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined)
{
message_writer(epee::log_space::console_color_magenta, false) <<
"Height " << height <<
", transaction " << get_transaction_hash(spend_tx) <<
", spent " << print_money(in_tx.vout[out_index].amount);
m_refresh_progress_reporter.update(height, true);
epee::log_space::console_colors color = wti.is_income ? epee::log_space::console_color_green : epee::log_space::console_color_magenta;
message_writer(color, false) <<
"height " << wti.height <<
", tx " << wti.tx_hash <<
" " << std::right << std::setw(18) << print_money_trailing_zeros_replaced_with_spaces(wti.amount) << (wti.is_income ? " received," : " spent, ") <<
" balance: " << print_money_brief(balance);
m_refresh_progress_reporter.update(wti.height, true);
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::on_message(i_wallet2_callback::message_severity severity, const std::string& m)
{
epee::log_space::console_colors color = epee::log_space::console_color_white;
if (severity == i_wallet2_callback::ms_red)
color = epee::log_space::console_color_red;
else if (severity == i_wallet2_callback::ms_yellow)
color = epee::log_space::console_color_yellow;
message_writer(color, true, std::string()) << m;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::refresh(const std::vector<std::string>& args)
@ -1322,7 +1372,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
bool simple_wallet::show_seed(const std::vector<std::string> &args)
{
success_msg_writer() << "Here's your wallet's seed phrase. Write it down and keep in a safe place.";
success_msg_writer(true) << "Anyone who knows the following 25 words can access your wallet:";
success_msg_writer(true) << "Anyone who knows the following 26 words can access your wallet:";
std::cout << m_wallet->get_account().get_restore_braindata() << std::endl << std::flush;
return true;
}
@ -1452,6 +1502,19 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
}
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::awo_blob(const std::vector<std::string> &args_)
{
if (!m_wallet->is_auditable())
{
fail_msg_writer() << "this command is allowed for auditable wallets only";
return true;
}
success_msg_writer() << "Auditable watch-only blob for this wallet is:";
std::cout << m_wallet->get_account().get_awo_blob() << ENDL;
return true;
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::set_offline_mode(bool offline_mode)
{
if (offline_mode && !m_offline_mode)
@ -1702,6 +1765,7 @@ int main(int argc, char* argv[])
po::options_description desc_params("Wallet options");
command_line::add_arg(desc_params, arg_wallet_file);
command_line::add_arg(desc_params, arg_generate_new_wallet);
command_line::add_arg(desc_params, arg_generate_new_auditable_wallet);
command_line::add_arg(desc_params, arg_password);
command_line::add_arg(desc_params, arg_daemon_address);
command_line::add_arg(desc_params, arg_daemon_host);
@ -1714,6 +1778,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_do_pos_mining);
command_line::add_arg(desc_params, arg_pos_mining_reward_address);
command_line::add_arg(desc_params, arg_restore_wallet);
command_line::add_arg(desc_params, arg_restore_awo_wallet);
command_line::add_arg(desc_params, arg_offline_mode);
command_line::add_arg(desc_params, command_line::arg_log_file);
command_line::add_arg(desc_params, command_line::arg_log_level);
@ -1848,7 +1913,7 @@ int main(int argc, char* argv[])
{
LOG_PRINT_L0("Initializing wallet...");
wal.init(daemon_address);
if (command_line::get_arg(vm, arg_generate_new_wallet).size())
if (command_line::get_arg(vm, arg_generate_new_wallet).size() || command_line::get_arg(vm, arg_generate_new_auditable_wallet).size())
return EXIT_FAILURE;
if (!offline_mode)
@ -1903,7 +1968,7 @@ int main(int argc, char* argv[])
sw->set_offline_mode(offline_mode);
r = sw->init(vm);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet");
if (command_line::get_arg(vm, arg_generate_new_wallet).size())
if (command_line::get_arg(vm, arg_generate_new_wallet).size() || command_line::get_arg(vm, arg_generate_new_auditable_wallet).size())
return EXIT_FAILURE;

View file

@ -44,9 +44,9 @@ namespace currency
bool run_console_handler();
bool new_wallet(const std::string &wallet_file, const std::string& password);
bool new_wallet(const std::string &wallet_file, const std::string& password, bool create_auditable_wallet);
bool open_wallet(const std::string &wallet_file, const std::string& password);
bool restore_wallet(const std::string &wallet_file, const std::string &restore_seed, const std::string& password);
bool restore_wallet(const std::string &wallet_file, const std::string &seed_or_awo_blob, const std::string& password, bool auditable_watch_only);
bool close_wallet();
bool help(const std::vector<std::string> &args = std::vector<std::string>());
@ -81,6 +81,7 @@ namespace currency
bool enable_console_logger(const std::vector<std::string> &args);
bool integrated_address(const std::vector<std::string> &args);
bool get_tx_key(const std::vector<std::string> &args_);
bool awo_blob(const std::vector<std::string> &args_);
bool save_watch_only(const std::vector<std::string> &args);
bool sign_transfer(const std::vector<std::string> &args);
bool submit_transfer(const std::vector<std::string> &args);
@ -93,9 +94,10 @@ namespace currency
bool try_connect_to_daemon();
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const currency::block& block);
virtual void on_money_received(uint64_t height, const currency::transaction& tx, size_t out_index);
virtual void on_money_spent(uint64_t height, const currency::transaction& in_tx, size_t out_index, const currency::transaction& spend_tx);
virtual void on_new_block(uint64_t height, const currency::block& block) override;
virtual void on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined) override;
virtual void on_message(i_wallet2_callback::message_severity severity, const std::string& m) override;
//----------------------------------------------------------
friend class refresh_progress_reporter_t;
@ -153,6 +155,7 @@ namespace currency
private:
std::string m_wallet_file;
std::string m_generate_new;
std::string m_generate_new_aw;
std::string m_import_path;
std::string m_daemon_address;
@ -164,6 +167,7 @@ namespace currency
bool m_do_pos_mining;
bool m_offline_mode;
std::string m_restore_wallet;
std::string m_restore_awo_wallet;
epee::console_handlers_binder m_cmd_binder;

View file

@ -365,7 +365,7 @@ namespace plain_wallet
std::string full_path = get_wallets_folder() + path;
epee::json_rpc::response<view::open_wallet_response, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, ok_response.result);
std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, false, ok_response.result);
if (rsp == API_RETURN_CODE_OK || rsp == API_RETURN_CODE_FILE_RESTORED)
{
if (rsp == API_RETURN_CODE_FILE_RESTORED)

View file

@ -213,16 +213,20 @@ public:
uint64_t balance;
uint64_t mined_total;
std::string address;
std::string tracking_hey;
std::string view_sec_key;
std::string path;
bool is_auditable;
bool is_watch_only;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(balance)
KV_SERIALIZE(mined_total)
KV_SERIALIZE(address)
KV_SERIALIZE(tracking_hey)
KV_SERIALIZE(view_sec_key)
KV_SERIALIZE(path)
KV_SERIALIZE(is_auditable);
KV_SERIALIZE(is_watch_only);
END_KV_SERIALIZE_MAP()
};
@ -416,11 +420,13 @@ public:
std::string pass;
std::string path;
std::string restore_key;
bool auditable_watch_only;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(pass)
KV_SERIALIZE(path)
KV_SERIALIZE(restore_key)
KV_SERIALIZE(auditable_watch_only)
END_KV_SERIALIZE_MAP()
};

View file

@ -263,11 +263,60 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
{
if (in.type() == typeid(currency::txin_to_key))
{
auto it = m_key_images.find(boost::get<currency::txin_to_key>(in).k_image);
if (it != m_key_images.end())
const currency::txin_to_key& intk = boost::get<currency::txin_to_key>(in);
// check if this input spends our output
transfer_details* p_td = nullptr;
uint64_t tid = UINT64_MAX;
if (is_auditable() && is_watch_only())
{
tx_money_spent_in_ins += boost::get<currency::txin_to_key>(in).amount;
transfer_details& td = m_transfers[it->second];
// auditable wallet
// try to find a reference among own UTXOs
std::vector<txout_v> abs_key_offsets = relative_output_offsets_to_absolute(intk.key_offsets); // potential speed-up: don't convert to abs offsets as we interested only in direct spends for auditable wallets. Now it's kind a bit paranoid.
for(auto v : abs_key_offsets)
{
if (v.type() != typeid(uint64_t))
continue;
uint64_t gindex = boost::get<uint64_t>(v);
auto it = m_amount_gindex_to_transfer_id.find(std::make_pair(intk.amount, gindex));
if (it != m_amount_gindex_to_transfer_id.end())
{
tid = it->second;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tid < m_transfers.size(), "invalid tid: " << tid << ", ref from input with amount: " << intk.amount << ", gindex: " << gindex);
auto& td = m_transfers[it->second];
if (intk.key_offsets.size() != 1)
{
// own output was used in non-direct transaction
// the core should not allow this to happen, the only way it may happen - mixing in own output that was sent without mix_attr == 1
// log strange situation
std::stringstream ss;
ss << "own transfer tid=" << tid << " tx=" << td.tx_hash() << " mix_attr=" << td.mix_attr() << ", is referenced by a transaction with mixins, ref from input with amount: " << intk.amount << ", gindex: " << gindex;
WLT_LOG_YELLOW(ss.str(), LOG_LEVEL_0);
if (m_wcallback)
m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str());
continue;
}
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(!td.is_spent(), "transfer is spent, tid: " << tid << ", ref from input with amount: " << intk.amount << ", gindex: " << gindex);
// own output is spent, handle it
break;
}
}
}
else
{
// wallet with spend secret key -- we can calculate own key images and then search by them
auto it = m_key_images.find(intk.k_image);
if (it != m_key_images.end())
{
tid = it->second;
}
}
if (tid != UINT64_MAX)
{
tx_money_spent_in_ins += intk.amount;
transfer_details& td = m_transfers[tid];
uint32_t flags_before = td.m_flags;
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT;
td.m_spent_height = height;
@ -275,10 +324,10 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
is_derived_from_coinbase = true;
else
is_derived_from_coinbase = false;
WLT_LOG_L0("Spent key out, transfer #" << it->second << ", amount: " << print_money(m_transfers[it->second].amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height <<
WLT_LOG_L0("Spent key out, transfer #" << tid << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height <<
"; flags: " << flags_before << " -> " << td.m_flags);
mtd.spent_indices.push_back(i);
remove_transfer_from_expiration_list(it->second);
remove_transfer_from_expiration_list(tid);
}
}
else if (in.type() == typeid(currency::txin_multisig))
@ -341,18 +390,21 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
crypto::key_image ki = currency::null_ki;
if (m_watch_only)
{
// don't have spend secret key, so we unable to calculate key image for an output
// look it up in special container instead
auto it = m_pending_key_images.find(otk.key);
if (it != m_pending_key_images.end())
if (!is_auditable())
{
ki = it->second;
WLT_LOG_L1("pending key image " << ki << " was found by out pub key " << otk.key);
}
else
{
ki = currency::null_ki;
WLT_LOG_L1("can't find pending key image by out pub key: " << otk.key << ", key image temporarily set to null");
// don't have spend secret key, so we unable to calculate key image for an output
// look it up in special container instead
auto it = m_pending_key_images.find(otk.key);
if (it != m_pending_key_images.end())
{
ki = it->second;
WLT_LOG_L1("pending key image " << ki << " was found by out pub key " << otk.key);
}
else
{
ki = currency::null_ki;
WLT_LOG_L1("can't find pending key image by out pub key: " << otk.key << ", key image temporarily set to null");
}
}
}
else
@ -365,20 +417,37 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
if (ki != currency::null_ki)
{
// make sure calculated key image for this own output has not been seen before
auto it = m_key_images.find(ki);
if (it != m_key_images.end())
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->second < m_transfers.size(), "m_key_images entry has wrong m_transfers index, it->second: " << it->second << ", m_transfers.size(): " << m_transfers.size());
const transfer_details& local_td = m_transfers[it->second];
WLT_LOG_YELLOW("tx " << get_transaction_hash(tx) << " @ block " << height << " has output #" << o << " with key image " << ki << " that has already been seen in output #" <<
std::stringstream ss;
ss << "tx " << get_transaction_hash(tx) << " @ block " << height << " has output #" << o << " with key image " << ki << " that has already been seen in output #" <<
local_td.m_internal_output_index << " in tx " << get_transaction_hash(local_td.m_ptx_wallet_info->m_tx) << " @ block " << local_td.m_spent_height <<
". This output can't ever be spent and will be skipped.", LOG_LEVEL_0);
". This output can't ever be spent and will be skipped.";
WLT_LOG_YELLOW(ss.str(), LOG_LEVEL_0);
if (m_wcallback)
m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str());
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tx_money_got_in_outs >= tx.vout[o].amount, "tx_money_got_in_outs: " << tx_money_got_in_outs << ", tx.vout[o].amount:" << tx.vout[o].amount);
tx_money_got_in_outs -= tx.vout[o].amount;
continue; // skip the output
}
}
if (is_auditable() && otk.mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
{
std::stringstream ss;
ss << "output #" << o << " from tx " << get_transaction_hash(tx) << " with amount " << print_money_brief(tx.vout[o].amount)
<< " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)otk.mix_attr << ". Output IGNORED.";
WLT_LOG_RED(ss.str(), LOG_LEVEL_0);
if (m_wcallback)
m_wcallback->on_message(i_wallet2_callback::ms_red, ss.str());
tx_money_got_in_outs -= tx.vout[o].amount;
continue; // skip the output
}
mtd.receive_indices.push_back(o);
m_transfers.push_back(boost::value_initialized<transfer_details>());
@ -399,6 +468,11 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
if (td.m_key_image != currency::null_ki)
m_key_images[td.m_key_image] = transfer_index;
add_transfer_to_transfers_cache(tx.vout[o].amount, transfer_index);
uint64_t amount = tx.vout[o].amount;
auto amount_gindex_pair = std::make_pair(amount, td.m_global_output_index);
WLT_CHECK_AND_ASSERT_MES_NO_RET(m_amount_gindex_to_transfer_id.count(amount_gindex_pair) == 0, "update m_amount_gindex_to_transfer_id: amount " << amount << ", gindex " << td.m_global_output_index << " already exists");
m_amount_gindex_to_transfer_id[amount_gindex_pair] = transfer_index;
if (max_out_unlock_time < get_tx_unlock_time(tx, o))
max_out_unlock_time = get_tx_unlock_time(tx, o);
@ -469,20 +543,25 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
void wallet2::prepare_wti_decrypted_attachments(wallet_public::wallet_transfer_info& wti, const std::vector<currency::payload_items_v>& decrypted_att)
{
PROFILE_FUNC("wallet2::prepare_wti_decrypted_attachments");
tx_payer tp = AUTO_VAL_INIT(tp);
wti.show_sender = get_type_in_variant_container(decrypted_att, tp);
if (wti.is_income)
{
if(wti.show_sender)
wti.remote_addresses.push_back(currency::get_account_address_as_str(tp.acc_addr));
account_public_address sender_address = AUTO_VAL_INIT(sender_address);
wti.show_sender = handle_2_alternative_types_in_variant_container<tx_payer, tx_payer_old>(decrypted_att, [&](const tx_payer& p) { sender_address = p.acc_addr; return false; /* <- continue? */ } );
if (wti.show_sender)
wti.remote_addresses.push_back(currency::get_account_address_as_str(sender_address));
}
else
{
//TODO: actually recipients could be more then one, handle it in future
tx_receiver tr = AUTO_VAL_INIT(tr);
if (!wti.remote_addresses.size() && get_type_in_variant_container(decrypted_att, tr))
wti.remote_addresses.push_back(currency::get_account_address_as_str(tr.acc_addr));
if (wti.remote_addresses.empty())
{
handle_2_alternative_types_in_variant_container<tx_receiver, tx_receiver_old>(decrypted_att, [&](const tx_receiver& p) {
std::string addr_str = currency::get_account_address_as_str(p.acc_addr);
wti.remote_addresses.push_back(addr_str);
LOG_PRINT_YELLOW("prepare_wti_decrypted_attachments, income=false, wti.amount = " << print_money_brief(wti.amount) << ", rem. addr = " << addr_str, LOG_LEVEL_0);
return true; // continue iterating through the container
});
}
}
currency::tx_comment cm;
@ -1163,7 +1242,6 @@ void wallet2::pull_blocks(size_t& blocks_added, std::atomic<bool>& stop)
bool r = m_core_proxy->call_COMMAND_RPC_GET_BLOCKS_DIRECT(req, res);
if (!r)
throw error::no_connection_to_daemon(LOCATION_STR, "getblocks.bin");
WLT_LOG_L0("COMMAND_RPC_GET_BLOCKS_DIRECT: " << epee::serialization::store_t_to_json(req));
if (res.status == API_RETURN_CODE_GENESIS_MISMATCH)
{
WLT_LOG_MAGENTA("Reseting genesis block...", LOG_LEVEL_0);
@ -1799,6 +1877,17 @@ uint64_t wallet2::detach_from_block_ids(uint64_t including_height)
return blocks_detached;
}
//----------------------------------------------------------------------------------------------------
void wallet2::remove_transfer_from_amount_gindex_map(uint64_t tid)
{
for (auto it = m_amount_gindex_to_transfer_id.begin(); it != m_amount_gindex_to_transfer_id.end(); )
{
if (it->second == tid)
it = m_amount_gindex_to_transfer_id.erase(it);
else
++it;
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::detach_blockchain(uint64_t including_height)
{
WLT_LOG_L0("Detaching blockchain on height " << including_height);
@ -1817,6 +1906,7 @@ void wallet2::detach_blockchain(uint64_t including_height)
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it_ki != m_key_images.end(), "key image " << m_transfers[i].m_key_image << " not found");
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(m_transfers[i].m_ptx_wallet_info->m_block_height >= including_height, "transfer #" << i << " block height is less than " << including_height);
m_key_images.erase(it_ki);
remove_transfer_from_amount_gindex_map(i);
++transfers_detached;
}
m_transfers.erase(it, m_transfers.end());
@ -1902,6 +1992,7 @@ bool wallet2::reset_all()
//m_blockchain.clear();
m_chain.clear();
m_transfers.clear();
m_amount_gindex_to_transfer_id.clear();
m_key_images.clear();
// m_pending_key_images is not cleared intentionally
m_unconfirmed_in_transfers.clear();
@ -2035,20 +2126,27 @@ bool wallet2::prepare_file_names(const std::wstring& file_path)
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::load_keys(const std::string& buff, const std::string& password)
void wallet2::load_keys(const std::string& buff, const std::string& password, uint64_t file_signature)
{
wallet2::keys_file_data keys_file_data;
// std::string buf;
// bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
// CHECK_AND_THROW_WALLET_EX(!r, error::file_read_error, keys_file_name);
bool r = ::serialization::parse_binary(buff, keys_file_data);
bool r = false;
wallet2::keys_file_data kf_data = AUTO_VAL_INIT(kf_data);
if (file_signature == WALLET_FILE_SIGNATURE_OLD)
{
wallet2::keys_file_data_old kf_data_old;
r = ::serialization::parse_binary(buff, kf_data_old);
kf_data = wallet2::keys_file_data::from_old(kf_data_old);
}
else if (file_signature == WALLET_FILE_SIGNATURE_V2)
{
r = ::serialization::parse_binary(buff, kf_data);
}
THROW_IF_TRUE_WALLET_EX(!r, error::wallet_internal_error, "internal error: failed to deserialize");
crypto::chacha8_key key;
crypto::generate_chacha8_key(password, key);
std::string account_data;
account_data.resize(keys_file_data.account_data.size());
crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
account_data.resize(kf_data.account_data.size());
crypto::chacha8(kf_data.account_data.data(), kf_data.account_data.size(), key, kf_data.iv, &account_data[0]);
const currency::account_keys& keys = m_account.get_keys();
r = epee::serialization::load_t_from_binary(m_account, account_data);
@ -2072,7 +2170,7 @@ void wallet2::assign_account(const currency::account_base& acc)
init_log_prefix();
}
//----------------------------------------------------------------------------------------------------
void wallet2::generate(const std::wstring& path, const std::string& pass)
void wallet2::generate(const std::wstring& path, const std::string& pass, bool auditable_wallet)
{
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(validate_password(pass), "new wallet generation failed: password contains forbidden characters")
clear();
@ -2081,11 +2179,11 @@ void wallet2::generate(const std::wstring& path, const std::string& pass)
check_for_free_space_and_throw_if_it_lacks(m_wallet_file);
m_password = pass;
m_account.generate();
m_account.generate(auditable_wallet);
init_log_prefix();
boost::system::error_code ignored_ec;
THROW_IF_TRUE_WALLET_EX(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, epee::string_encoding::convert_to_ansii(m_wallet_file));
if (m_watch_only)
if (m_watch_only && !auditable_wallet)
{
bool stub;
load_keys2ki(true, stub);
@ -2093,14 +2191,27 @@ void wallet2::generate(const std::wstring& path, const std::string& pass)
store();
}
//----------------------------------------------------------------------------------------------------
void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& restore_key)
void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& seed_phrase_or_awo_blob, bool auditable_watch_only)
{
bool r = false;
clear();
prepare_file_names(path);
m_password = pass;
bool r = m_account.restore_keys_from_braindata(restore_key);
init_log_prefix();
THROW_IF_TRUE_WALLET_EX(!r, error::wallet_wrong_seed_error, epee::string_encoding::convert_to_ansii(m_wallet_file));
if (auditable_watch_only)
{
r = m_account.restore_from_awo_blob(seed_phrase_or_awo_blob);
init_log_prefix();
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(r, "Could not load auditable watch-only wallet from a given blob: invalid awo blob");
m_watch_only = true;
}
else
{
r = m_account.restore_from_braindata(seed_phrase_or_awo_blob);
init_log_prefix();
THROW_IF_FALSE_WALLET_EX(r, error::wallet_wrong_seed_error, epee::string_encoding::convert_to_ansii(m_wallet_file));
}
boost::system::error_code ignored_ec;
THROW_IF_TRUE_WALLET_EX(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, epee::string_encoding::convert_to_ansii(m_wallet_file));
store();
@ -2129,36 +2240,38 @@ void wallet2::load(const std::wstring& wallet_, const std::string& password)
THROW_IF_TRUE_WALLET_EX(e || !exists, error::file_not_found, epee::string_encoding::convert_to_ansii(m_wallet_file));
boost::filesystem::ifstream data_file;
data_file.open(m_wallet_file, std::ios_base::binary | std::ios_base::in);
THROW_IF_TRUE_WALLET_EX(data_file.fail(), error::file_not_found, epee::string_encoding::convert_to_ansii(m_wallet_file));
THROW_IF_TRUE_WALLET_EX(data_file.fail(), error::file_read_error, epee::string_encoding::convert_to_ansii(m_wallet_file));
wallet_file_binary_header wbh = AUTO_VAL_INIT(wbh);
data_file.read((char*)&wbh, sizeof(wbh));
THROW_IF_TRUE_WALLET_EX(data_file.fail(), error::file_not_found, epee::string_encoding::convert_to_ansii(m_wallet_file));
THROW_IF_TRUE_WALLET_EX(data_file.fail(), error::file_read_error, epee::string_encoding::convert_to_ansii(m_wallet_file));
THROW_IF_TRUE_WALLET_EX(wbh.m_signature != WALLET_FILE_SIGNATURE, error::file_not_found, epee::string_encoding::convert_to_ansii(m_wallet_file));
THROW_IF_TRUE_WALLET_EX(wbh.m_signature != WALLET_FILE_SIGNATURE_OLD && wbh.m_signature != WALLET_FILE_SIGNATURE_V2, error::file_read_error, epee::string_encoding::convert_to_ansii(m_wallet_file));
THROW_IF_TRUE_WALLET_EX(wbh.m_cb_body > WALLET_FILE_MAX_BODY_SIZE ||
wbh.m_cb_keys > WALLET_FILE_MAX_KEYS_SIZE, error::file_not_found, epee::string_encoding::convert_to_ansii(m_wallet_file));
wbh.m_cb_keys > WALLET_FILE_MAX_KEYS_SIZE, error::file_read_error, epee::string_encoding::convert_to_ansii(m_wallet_file));
keys_buff.resize(wbh.m_cb_keys);
data_file.read((char*)keys_buff.data(), wbh.m_cb_keys);
load_keys(keys_buff, password);
load_keys(keys_buff, password, wbh.m_signature);
bool need_to_resync = !tools::portable_unserialize_obj_from_stream(*this, data_file);
if (m_watch_only)
if (m_watch_only && !is_auditable())
load_keys2ki(true, need_to_resync);
WLT_LOG_L0("Loaded wallet file" << (m_watch_only ? " (WATCH ONLY) " : " ") << string_encoding::convert_to_ansii(m_wallet_file) << " with public address: " << m_account.get_public_address_str());
WLT_LOG_L0("(after loading: pending_key_images: " << m_pending_key_images.size() << ", pki file elements: " << m_pending_key_images_file_container.size() << ", tx_keys: " << m_tx_keys.size() << ")");
if (need_to_resync)
{
reset_history();
WLT_LOG_L0("Unable to load history data from wallet file, wallet will be resynced!");
}
THROW_IF_TRUE_WALLET_EX(need_to_resync, error::wallet_load_notice_wallet_restored, epee::string_encoding::convert_to_ansii(m_wallet_file));
WLT_LOG_L0("Loaded wallet file" << (m_watch_only ? " (WATCH ONLY) " : " ") << string_encoding::convert_to_ansii(m_wallet_file) << " with public address: " << m_account.get_public_address_str());
WLT_LOG_L0("(after loading: pending_key_images: " << m_pending_key_images.size() << ", pki file elements: " << m_pending_key_images_file_container.size() << ", tx_keys: " << m_tx_keys.size() << ")");
THROW_IF_TRUE_WALLET_EX(need_to_resync, error::wallet_load_notice_wallet_restored, epee::string_encoding::convert_to_ansii(m_wallet_file));
}
//----------------------------------------------------------------------------------------------------
void wallet2::store()
@ -2186,7 +2299,7 @@ void wallet2::store(const std::wstring& path_to_save, const std::string& passwor
//store data
wallet_file_binary_header wbh = AUTO_VAL_INIT(wbh);
wbh.m_signature = WALLET_FILE_SIGNATURE;
wbh.m_signature = WALLET_FILE_SIGNATURE_V2;
wbh.m_cb_keys = keys_buff.size();
//@#@ change it to proper
wbh.m_cb_body = 1000;
@ -2272,8 +2385,11 @@ void wallet2::store_watch_only(const std::wstring& path_to_save, const std::stri
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(!boost::filesystem::exists(wo.m_pending_ki_file), "file " << epee::string_encoding::convert_to_ansii(wo.m_pending_ki_file) << " already exists");
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(wo.m_pending_key_images.empty(), "pending key images is expected to be empty");
bool stub = false;
wo.load_keys2ki(true, stub); // to create outkey2ki file
if (!is_auditable())
{
bool stub = false;
wo.load_keys2ki(true, stub); // to create outkey2ki file
}
// populate pending key images for spent outputs (this will help to resync watch-only wallet)
for (size_t ti = 0; ti < wo.m_transfers.size(); ++ti)
@ -3112,6 +3228,21 @@ void wallet2::update_offer_by_id(const crypto::hash& tx_id, uint64_t of_ind, con
transfer(destinations, 0, 0, od.fee, extra, attachments, detail::ssi_digit, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx);
}
//----------------------------------------------------------------------------------------------------
void wallet2::push_alias_info_to_extra_according_to_hf_status(const currency::extra_alias_entry& ai, std::vector<currency::extra_v>& extra)
{
if (get_top_block_height() > m_core_runtime_config.hard_fork_02_starts_after_height)
{
// after HF2
extra.push_back(ai);
}
else
{
// before HF2
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(!ai.m_address.is_auditable(), "auditable addresses are not supported in aliases prior to HF2");
extra.push_back(ai.to_old());
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::request_alias_registration(const currency::extra_alias_entry& ai, currency::transaction& res_tx, uint64_t fee, uint64_t reward)
{
if (!validate_alias_name(ai.m_alias))
@ -3122,7 +3253,8 @@ void wallet2::request_alias_registration(const currency::extra_alias_entry& ai,
std::vector<currency::tx_destination_entry> destinations;
std::vector<currency::extra_v> extra;
std::vector<currency::attachment_v> attachments;
extra.push_back(ai);
push_alias_info_to_extra_according_to_hf_status(ai, extra);
currency::tx_destination_entry tx_dest_alias_reward;
tx_dest_alias_reward.addr.resize(1);
@ -3152,7 +3284,9 @@ void wallet2::request_alias_update(currency::extra_alias_entry& ai, currency::tr
std::vector<currency::tx_destination_entry> destinations;
std::vector<currency::extra_v> extra;
std::vector<currency::attachment_v> attachments;
extra.push_back(ai);
push_alias_info_to_extra_according_to_hf_status(ai, extra);
transfer(destinations, 0, 0, fee, extra, attachments, detail::ssi_digit, tx_dust_policy(DEFAULT_DUST_THRESHOLD), res_tx, CURRENCY_TO_KEY_OUT_RELAXED, false);
}
//----------------------------------------------------------------------------------------------------
@ -4465,6 +4599,8 @@ void wallet2::transfer(const construct_tx_param& ctp,
bool send_to_network,
std::string* p_signed_tx_blob_str)
{
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(!is_auditable() || !is_watch_only(), "You can't initiate coins transfer using an auditable watch-only wallet."); // btw, watch-only wallets can call transfer() within cold-signing process
check_and_throw_if_self_directed_tx_with_payment_id_requested(ctp);
TIME_MEASURE_START(prepare_transaction_time);
@ -4578,9 +4714,9 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public
set_payment_id_to_tx(ftp.attachments, payment_id);
// put encrypted payer info into the extra
ftp.crypt_address = destination_addr;
currency::tx_payer txp = AUTO_VAL_INIT(txp);
txp.acc_addr = m_account.get_public_address();
ftp.extra.push_back(txp);
currency::create_and_add_tx_payer_to_container_from_address(ftp.extra, m_account.get_public_address(), get_top_block_height(), m_core_runtime_config);
ftp.flags = 0;
// ftp.multisig_id -- not required
// ftp.prepared_destinations -- will be filled by prepare_tx_destinations

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2020 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -24,6 +24,7 @@
#include "currency_core/account_boost_serialization.h"
#include "currency_core/currency_format_utils.h"
#include "common/make_hashable.h"
#include "wallet_public_structs_defs.h"
#include "currency_core/currency_format_utils.h"
#include "common/unordered_containers_boost_serialization.h"
@ -94,6 +95,8 @@ namespace tools
class i_wallet2_callback
{
public:
enum message_severity { ms_red, ms_yellow, ms_normal };
virtual ~i_wallet2_callback() = default;
virtual void on_new_block(uint64_t /*height*/, const currency::block& /*block*/) {}
@ -101,6 +104,7 @@ namespace tools
virtual void on_pos_block_found(const currency::block& /*block*/) {}
virtual void on_sync_progress(const uint64_t& /*percents*/) {}
virtual void on_transfer_canceled(const wallet_public::wallet_transfer_info& wti) {}
virtual void on_message(message_severity /*severity*/, const std::string& /*m*/) {}
};
struct tx_dust_policy
@ -380,6 +384,9 @@ namespace tools
uint32_t m_flags;
uint64_t amount() const { return m_ptx_wallet_info->m_tx.vout[m_internal_output_index].amount; }
const currency::tx_out& output() const { return m_ptx_wallet_info->m_tx.vout[m_internal_output_index]; }
uint8_t mix_attr() const { return output().target.type() == typeid(currency::txout_to_key) ? boost::get<const currency::txout_to_key&>(output().target).mix_attr : UINT8_MAX; }
crypto::hash tx_hash() const { return get_transaction_hash(m_ptx_wallet_info->m_tx); }
bool is_spent() const { return m_flags & WALLET_TRANSFER_DETAIL_FLAG_SPENT; }
bool is_spendable() const { return (m_flags & (WALLET_TRANSFER_DETAIL_FLAG_SPENT | WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION | WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION)) == 0; }
bool is_reserved_for_escrow() const { return ( (m_flags & WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION) != 0 ); }
@ -443,9 +450,10 @@ namespace tools
typedef std::unordered_map<crypto::hash, transfer_details_base> multisig_transfer_container;
typedef std::unordered_map<crypto::hash, tools::wallet_public::escrow_contract_details_basic> escrow_contracts_container;
typedef std::map<uint64_t, std::set<size_t> > free_amounts_cache_type;
typedef std::unordered_map<std::pair<uint64_t, uint64_t>, uint64_t> amount_gindex_to_transfer_id_container; // maps [amount; gindex] -> tid
struct keys_file_data
struct keys_file_data_old
{
crypto::chacha8_iv iv;
std::string account_data;
@ -455,9 +463,32 @@ namespace tools
FIELD(account_data)
END_SERIALIZE()
};
struct keys_file_data
{
uint8_t version;
crypto::chacha8_iv iv;
std::string account_data;
static keys_file_data from_old(const keys_file_data_old& v)
{
keys_file_data result = AUTO_VAL_INIT(result);
result.iv = v.iv;
result.account_data = v.account_data;
return result;
}
DEFINE_SERIALIZATION_VERSION(1)
BEGIN_SERIALIZE_OBJECT()
VERSION_ENTRY(version)
FIELD(iv)
FIELD(account_data)
END_SERIALIZE()
};
void assign_account(const currency::account_base& acc);
void generate(const std::wstring& path, const std::string& password);
void restore(const std::wstring& path, const std::string& pass, const std::string& restore_key);
void generate(const std::wstring& path, const std::string& password, bool auditable_wallet);
void restore(const std::wstring& path, const std::string& pass, const std::string& seed_phrase_or_awo_blob, bool auditable_watch_only);
void load(const std::wstring& path, const std::string& password);
void store();
void store(const std::wstring& path);
@ -480,7 +511,7 @@ namespace tools
//i_wallet2_callback* callback() const { return m_wcallback; }
//void callback(i_wallet2_callback* callback) { m_callback = callback; }
void callback(std::shared_ptr<i_wallet2_callback> callback) { m_wcallback = callback; m_do_rise_transfer = true; }
void callback(std::shared_ptr<i_wallet2_callback> callback) { m_wcallback = callback; m_do_rise_transfer = (callback != nullptr); }
void set_do_rise_transfer(bool do_rise) { m_do_rise_transfer = do_rise; }
bool has_related_alias_entry_unconfirmed(const currency::transaction& tx);
@ -631,6 +662,7 @@ namespace tools
void enumerate_unconfirmed_transfers(callback_t cb) const;
bool is_watch_only() const { return m_watch_only; }
bool is_auditable() const { return m_account.get_public_address().is_auditable(); }
void sign_transfer(const std::string& tx_sources_blob, std::string& signed_tx_blob, currency::transaction& tx);
void sign_transfer_files(const std::string& tx_sources_file, const std::string& signed_tx_file, currency::transaction& tx);
void submit_transfer(const std::string& signed_tx_blob, currency::transaction& tx);
@ -697,6 +729,9 @@ namespace tools
a & m_minimum_height;
}
// v151: m_amount_gindex_to_transfer_id added
if (ver >= 151)
a & m_amount_gindex_to_transfer_id;
a & m_transfers;
a & m_multisig_transfers;
@ -788,7 +823,7 @@ private:
void add_transfers_to_expiration_list(const std::vector<uint64_t>& selected_transfers, uint64_t expiration, uint64_t change_amount, const crypto::hash& related_tx_id);
void remove_transfer_from_expiration_list(uint64_t transfer_index);
void load_keys(const std::string& keys_file_name, const std::string& password);
void load_keys(const std::string& keys_file_name, const std::string& password, uint64_t file_signature);
void process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b);
void detach_blockchain(uint64_t including_height);
bool extract_offers_from_transfer_entry(size_t i, std::unordered_map<crypto::hash, bc_services::offer_details_ex>& offers_local);
@ -910,6 +945,9 @@ private:
uint64_t detach_from_block_ids(uint64_t height);
uint64_t get_wallet_minimum_height();
void push_alias_info_to_extra_according_to_hf_status(const currency::extra_alias_entry& ai, std::vector<currency::extra_v>& extra);
void remove_transfer_from_amount_gindex_map(uint64_t tid);
currency::account_base m_account;
bool m_watch_only;
std::string m_log_prefix; // part of pub address, prefix for logging functions
@ -924,6 +962,7 @@ private:
transfer_container m_transfers;
multisig_transfer_container m_multisig_transfers;
amount_gindex_to_transfer_id_container m_amount_gindex_to_transfer_id;
payment_container m_payments;
std::unordered_map<crypto::key_image, size_t> m_key_images;
std::unordered_map<crypto::public_key, crypto::key_image> m_pending_key_images; // (out_pk -> ki) pairs of change outputs to be added in watch-only wallet without spend sec key

View file

@ -68,6 +68,12 @@ namespace tools
return ss.str();
}
virtual const char* what() const noexcept
{
m_what = to_string();
return m_what.c_str();
}
protected:
wallet_error_base(std::string&& loc, const std::string& message)
: Base(message)
@ -77,6 +83,7 @@ namespace tools
private:
std::string m_loc;
mutable std::string m_what;
};
//----------------------------------------------------------------------------------------------------
const char* const failed_rpc_request_messages[] = {
@ -113,8 +120,9 @@ namespace tools
std::string m_status;
};
//----------------------------------------------------------------------------------------------------
typedef wallet_error_base<std::logic_error> wallet_logic_error;
typedef wallet_error_base<std::runtime_error> wallet_runtime_error;
typedef wallet_error_base<std::runtime_error> wallet_error;
typedef wallet_error wallet_logic_error;
typedef wallet_error wallet_runtime_error;
//----------------------------------------------------------------------------------------------------
struct wallet_internal_error : public wallet_runtime_error
{

View file

@ -15,10 +15,12 @@ namespace tools
{
wi = AUTO_VAL_INIT_T(view::wallet_info);
wi.address = w.get_account().get_public_address_str();
wi.tracking_hey = epee::string_tools::pod_to_hex(w.get_account().get_keys().view_secret_key);
wi.view_sec_key = epee::string_tools::pod_to_hex(w.get_account().get_keys().view_secret_key);
uint64_t fake = 0;
wi.balance = w.balance(wi.unlocked_balance, fake, fake, wi.mined_total);
wi.path = epee::string_encoding::wstring_to_utf8(w.get_wallet_path());
wi.is_auditable = w.is_auditable();
wi.is_watch_only = w.is_watch_only();
return true;
}
}

View file

@ -292,20 +292,15 @@ namespace tools
if (req.push_payer)
{
currency::tx_payer txp = AUTO_VAL_INIT(txp);
txp.acc_addr = m_wallet.get_account().get_keys().account_address;
extra.push_back(txp);
currency::create_and_add_tx_payer_to_container_from_address(extra, m_wallet.get_account().get_keys().account_address, m_wallet.get_top_block_height(), m_wallet.get_core_runtime_config());
}
if (!req.hide_receiver)
{
for (auto& d : dsts)
{
for (auto& a : d.addr)
{
currency::tx_receiver txr = AUTO_VAL_INIT(txr);
txr.acc_addr = a;
extra.push_back(txr);
}
currency::create_and_add_tx_receiver_to_container_from_address(extra, a, m_wallet.get_top_block_height(), m_wallet.get_core_runtime_config());
}
}

View file

@ -805,7 +805,7 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st
try
{
w->load(path, password);
if (w->is_watch_only())
if (w->is_watch_only() && !w->is_auditable())
return API_RETURN_CODE_WALLET_WATCH_ONLY_NOT_SUPPORTED;
w->get_recent_transfers_history(owr.recent_history.history, 0, txs_to_return, owr.recent_history.total_history_items);
//w->get_unconfirmed_transfers(owr.recent_history.unconfirmed);
@ -819,6 +819,10 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st
{
return API_RETURN_CODE_FILE_NOT_FOUND;
}
catch (const tools::error::file_read_error&)
{
return API_RETURN_CODE_INVALID_FILE;
}
catch (const tools::error::wallet_load_notice_wallet_restored& /**/)
{
return_code = API_RETURN_CODE_FILE_RESTORED;
@ -892,7 +896,7 @@ std::string wallets_manager::generate_wallet(const std::wstring& path, const std
try
{
w->generate(path, password);
w->generate(path, password, false);
w->set_minimum_height(m_last_daemon_height);
owr.seed = w->get_account().get_restore_braindata();
}
@ -939,7 +943,7 @@ std::string wallets_manager::is_pos_allowed()
std::string wallets_manager::is_valid_brain_restore_data(const std::string& brain_text)
{
currency::account_base acc;
if (acc.restore_keys_from_braindata(brain_text))
if (acc.restore_from_braindata(brain_text))
return API_RETURN_CODE_TRUE;
else
return API_RETURN_CODE_FALSE;
@ -957,7 +961,7 @@ void wallets_manager::get_gui_options(view::gui_options& opt)
{
opt = m_ui_opt;
}
std::string wallets_manager::restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr)
std::string wallets_manager::restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, bool auditable_watch_only, view::open_wallet_response& owr)
{
std::shared_ptr<tools::wallet2> w(new tools::wallet2());
owr.wallet_id = m_wallet_id_counter++;
@ -979,7 +983,7 @@ std::string wallets_manager::restore_wallet(const std::wstring& path, const std:
currency::account_base acc;
try
{
w->restore(path, password, restore_key);
w->restore(path, password, restore_key, auditable_watch_only);
owr.seed = w->get_account().get_restore_braindata();
}
catch (const tools::error::file_exists&)
@ -1253,20 +1257,14 @@ std::string wallets_manager::transfer(size_t wallet_id, const view::transfer_par
}
if (tp.push_payer)
{
currency::tx_payer txp = AUTO_VAL_INIT(txp);
txp.acc_addr = w->get()->get_account().get_keys().account_address;
extra.push_back(txp);
currency::create_and_add_tx_payer_to_container_from_address(extra, w->get()->get_account().get_keys().account_address, w->get()->get_top_block_height(), w->get()->get_core_runtime_config());
}
if (!tp.hide_receiver)
{
for (auto& d : dsts)
{
for (auto& a : d.addr)
{
currency::tx_receiver txr = AUTO_VAL_INIT(txr);
txr.acc_addr = a;
extra.push_back(txr);
}
currency::create_and_add_tx_receiver_to_container_from_address(extra, a, w->get()->get_top_block_height(), w->get()->get_core_runtime_config());
}
}
w->get()->transfer(dsts, tp.mixin_count, unlock_time ? unlock_time + 1 : 0, fee, extra, attachments, res_tx);

View file

@ -94,7 +94,7 @@ public:
bool send_stop_signal();
std::string open_wallet(const std::wstring& path, const std::string& password, uint64_t txs_to_return, view::open_wallet_response& owr);
std::string generate_wallet(const std::wstring& path, const std::string& password, view::open_wallet_response& owr);
std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr);
std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, bool auditable_watch_only, view::open_wallet_response& owr);
std::string invoke(uint64_t wallet_id, std::string params);
std::string get_wallet_status(uint64_t wallet_id);
std::string run_wallet(uint64_t wallet_id);

View file

@ -18,72 +18,20 @@ using namespace currency;
#define FIFTH_NAME "fifth--01234567890"
#define SIX_NAME "sixsix-double--01234567890"
std::string gen_random_alias(size_t len)
{
const char allowed_chars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
char buffer[2048] = "";
if (len >= sizeof buffer)
return "";
crypto::generate_random_bytes(len, buffer);
buffer[len] = 0;
for(size_t i = 0; i < len; ++i)
buffer[i] = allowed_chars[buffer[i] % (sizeof allowed_chars - 1)];
return buffer;
}
bool put_alias_via_tx_to_list(std::vector<test_event_entry>& events,
std::list<currency::transaction>& tx_set,
const block& head_block,
const account_base& miner_acc,
currency::extra_alias_entry ai2,
test_generator& generator)
{
std::vector<currency::extra_v> ex;
ex.push_back(ai2);
account_base reward_acc;
account_keys& ak = const_cast<account_keys&>(reward_acc.get_keys());
currency::get_aliases_reward_account(ak.account_address, ak.view_secret_key);
uint64_t fee_median = generator.get_last_n_blocks_fee_median(get_block_hash(head_block));
uint64_t reward = currency::get_alias_coast_from_fee(ai2.m_alias, fee_median);
MAKE_TX_MIX_LIST_EXTRA_MIX_ATTR(events,
tx_set,
miner_acc,
reward_acc,
reward,
0,
head_block,
CURRENCY_TO_KEY_OUT_RELAXED,
ex,
std::vector<currency::attachment_v>());
uint64_t found_alias_reward = get_amount_for_zero_pubkeys(tx_set.back());
if (found_alias_reward != reward)
{
LOCAL_ASSERT(false);
CHECK_AND_ASSERT_MES(false, false, "wrong transaction constructed, first input value not match alias amount or account");
return false;
}
return true;
std::list<currency::transaction>& tx_set,
const block& head_block,
const std::string& alias_name,
const account_base& miner_acc,
const account_base& alias_acc,
test_generator& generator)
{
currency::extra_alias_entry ai2 = AUTO_VAL_INIT(ai2);
ai2.m_alias = alias_name;
ai2.m_address = alias_acc.get_keys().account_address;
ai2.m_text_comment = "ssdss";
return put_alias_via_tx_to_list(events, tx_set, head_block, miner_acc, ai2, generator);
}
bool put_alias_via_tx_to_list(std::vector<test_event_entry>& events,
std::list<currency::transaction>& tx_set,
const block& head_block,
const std::string& alias_name,
const account_base& miner_acc,
const account_base& alias_acc,
test_generator& generator)
{
currency::extra_alias_entry ai2 = AUTO_VAL_INIT(ai2);
ai2.m_alias = alias_name;
ai2.m_address = alias_acc.get_keys().account_address;
ai2.m_text_comment = "ssdss";
return put_alias_via_tx_to_list(events, tx_set, head_block, miner_acc, ai2, generator);
}
bool put_next_block_with_alias_in_tx(std::vector<test_event_entry>& events,
block& b,
@ -92,13 +40,13 @@ bool put_next_block_with_alias_in_tx(std::vector<test_event_entry>& events,
const currency::extra_alias_entry& ai,
test_generator& generator)
{
std::list<currency::transaction> txs_0;
if (!put_alias_via_tx_to_list(events, txs_0, head_block, miner_acc, ai, generator))
return false;
std::list<currency::transaction> txs_0;
if (!put_alias_via_tx_to_list(events, txs_0, head_block, miner_acc, ai, generator))
return false;
MAKE_NEXT_BLOCK_TX_LIST(events, blk, head_block, miner_acc, txs_0);
b = blk;
return true;
MAKE_NEXT_BLOCK_TX_LIST(events, blk, head_block, miner_acc, txs_0);
b = blk;
return true;
}

View file

@ -15,7 +15,6 @@
#include "currency_core/currency_core.h"
#include "wallet/wallet2.h"
#include "test_core_time.h"
#include "chaingen_helpers.h"
#define TESTS_DEFAULT_FEE ((uint64_t)TX_DEFAULT_FEE)
#define MK_TEST_COINS(amount) (static_cast<uint64_t>(amount) * TX_DEFAULT_FEE)
@ -1205,3 +1204,4 @@ void append_vector_by_another_vector(U& dst, const V& src)
// --- end of gentime wallet helpers -----------------------------------------------------------------------
#include "chaingen_helpers.h"

View file

@ -228,3 +228,57 @@ inline bool resign_tx(const currency::account_keys& sender_keys, const std::vect
return true;
}
inline std::string gen_random_alias(size_t len)
{
const char allowed_chars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
char buffer[2048] = "";
if (len >= sizeof buffer)
return "";
crypto::generate_random_bytes(len, buffer);
buffer[len] = 0;
for(size_t i = 0; i < len; ++i)
buffer[i] = allowed_chars[buffer[i] % (sizeof allowed_chars - 1)];
return buffer;
}
template<typename alias_entry_t>
inline bool put_alias_via_tx_to_list(std::vector<test_event_entry>& events,
std::list<currency::transaction>& tx_set,
const currency::block& head_block,
const currency::account_base& miner_acc,
const alias_entry_t& ae,
test_generator& generator)
{
std::vector<currency::extra_v> ex;
ex.push_back(ae);
currency::account_base reward_acc;
currency::account_keys& ak = const_cast<currency::account_keys&>(reward_acc.get_keys());
currency::get_aliases_reward_account(ak.account_address, ak.view_secret_key);
uint64_t fee_median = generator.get_last_n_blocks_fee_median(get_block_hash(head_block));
uint64_t reward = currency::get_alias_coast_from_fee(ae.m_alias, fee_median);
MAKE_TX_MIX_LIST_EXTRA_MIX_ATTR(events,
tx_set,
miner_acc,
reward_acc,
reward,
0,
head_block,
CURRENCY_TO_KEY_OUT_RELAXED,
ex,
std::vector<currency::attachment_v>());
uint64_t found_alias_reward = get_amount_for_zero_pubkeys(tx_set.back());
if (found_alias_reward != reward)
{
LOCAL_ASSERT(false);
CHECK_AND_ASSERT_MES(false, false, "wrong transaction constructed, first input value not match alias amount or account");
return false;
}
return true;
}

View file

@ -987,7 +987,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_uint_overflow_2);
// Hardfok1 tests
// Hardfok 1 tests
GENERATE_AND_PLAY(before_hard_fork_1_cumulative_difficulty);
GENERATE_AND_PLAY(inthe_middle_hard_fork_1_cumulative_difficulty);
GENERATE_AND_PLAY(after_hard_fork_1_cumulative_difficulty);
@ -998,10 +998,19 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(hard_fork_1_chain_switch_pow_only);
GENERATE_AND_PLAY(hard_fork_1_checkpoint_basic_test);
GENERATE_AND_PLAY(hard_fork_1_pos_locked_height_vs_time);
//GENERATE_AND_PLAY(gen_block_reward); */
// Hardfork 2 tests
GENERATE_AND_PLAY(hard_fork_2_tx_payer_in_wallet);
GENERATE_AND_PLAY(hard_fork_2_tx_receiver_in_wallet);
GENERATE_AND_PLAY(hard_fork_2_tx_extra_alias_entry_in_wallet);
GENERATE_AND_PLAY(hard_fork_2_auditable_addresses_basics);
GENERATE_AND_PLAY(hard_fork_2_no_new_structures_before_hf);
GENERATE_AND_PLAY(hard_fork_2_awo_wallets_basic_test<true>);
GENERATE_AND_PLAY(hard_fork_2_awo_wallets_basic_test<false>);
// GENERATE_AND_PLAY(gen_block_reward);
// END OF TESTS */
size_t failed_postponed_tests_count = 0;
uint64_t total_time = 0;

View file

@ -37,3 +37,4 @@
#include "hard_fork_1_consensus_test.h"
#include "hard_fork_1_bad_pos_source.h"
#include "hard_fork_1.h"
#include "hard_fork_2.h"

View file

@ -612,7 +612,7 @@ gen_no_attchments_in_coinbase::gen_no_attchments_in_coinbase()
// NOTE: This test is made deterministic to be able to correctly set up checkpoint.
random_state_test_restorer::reset_random(); // random generator's state was previously stored, will be restore on dtor (see also m_random_state_test_restorer)
bool r = m_miner_acc.restore_keys_from_braindata("battle harsh arrow gain best doubt nose raw protect salty apart tell distant final yeah stubborn true stop shoulder breathe throne problem everyone stranger only");
bool r = m_miner_acc.restore_from_braindata("battle harsh arrow gain best doubt nose raw protect salty apart tell distant final yeah stubborn true stop shoulder breathe throne problem everyone stranger only");
CHECK_AND_ASSERT_THROW_MES(r, "gen_no_attchments_in_coinbase: Can't restore account from braindata");
REGISTER_CALLBACK_METHOD(gen_no_attchments_in_coinbase, c1);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
// Copyright (c) 2020 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "chaingen.h"
#include "wallet_tests_basic.h"
struct hard_fork_2_base_test : virtual public test_chain_unit_enchanced
{
hard_fork_2_base_test(size_t hardfork_02_height);
hard_fork_2_base_test(size_t hardfork_01_height, size_t hardfork_02_height);
bool configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
void set_hard_fork_heights_to_generator(test_generator& generator) const;
size_t m_hardfork_01_height;
size_t m_hardfork_02_height;
};
struct hard_fork_2_tx_payer_in_wallet : public wallet_test, public hard_fork_2_base_test
{
hard_fork_2_tx_payer_in_wallet();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct hard_fork_2_tx_receiver_in_wallet : public wallet_test, public hard_fork_2_base_test
{
hard_fork_2_tx_receiver_in_wallet();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
mutable uint64_t m_alice_start_balance;
};
struct hard_fork_2_tx_extra_alias_entry_in_wallet : public wallet_test, public hard_fork_2_base_test
{
hard_fork_2_tx_extra_alias_entry_in_wallet();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct hard_fork_2_auditable_addresses_basics : public wallet_test, public hard_fork_2_base_test
{
hard_fork_2_auditable_addresses_basics();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct hard_fork_2_no_new_structures_before_hf : public wallet_test, public hard_fork_2_base_test
{
using hard_fork_2_base_test::check_block_verification_context; // this is necessary for correct work of do_check_block_verification_context, consider rafactoring
hard_fork_2_no_new_structures_before_hf();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
template<bool before_hf_2>
struct hard_fork_2_awo_wallets_basic_test : public wallet_test, public hard_fork_2_base_test
{
//using hard_fork_2_base_test::check_block_verification_context; // this is necessary for correct work of do_check_block_verification_context, consider rafactoring
hard_fork_2_awo_wallets_basic_test();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};

View file

@ -22,18 +22,6 @@ const std::string g_wallet_password = "dofatibmzibeziyekigo";
const currency::account_base null_account = AUTO_VAL_INIT(null_account);
struct wlt_lambda_on_transfer2_wrapper : public tools::i_wallet2_callback
{
typedef std::function<bool(const tools::wallet_public::wallet_transfer_info&, uint64_t, uint64_t, uint64_t)> Func;
wlt_lambda_on_transfer2_wrapper(Func callback) : m_result(false), m_callback(callback) {}
virtual void on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined) override
{
m_result = m_callback(wti, balance, unlocked_balance, total_mined);
}
bool m_result;
Func m_callback;
};
POD_MAKE_COMPARABLE(currency, tx_out);
// Determines which output is real and actually spent in tx inputs, when there are fake outputs.
@ -1434,7 +1422,7 @@ bool gen_wallet_decrypted_attachments::generate(std::vector<test_event_entry>& e
CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_0r, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// these attachments will be use across all the transactions in this test
// these attachments will be used across all the transactions in this test
currency::tx_payer a_tx_payer = AUTO_VAL_INIT(a_tx_payer);
a_tx_payer.acc_addr = miner_acc.get_keys().account_address;
currency::tx_comment a_tx_comment = AUTO_VAL_INIT(a_tx_comment);

View file

@ -72,6 +72,7 @@ bool wallet_test::check_balance(currency::core& c, size_t ev_index, const std::v
return true;
}
std::shared_ptr<tools::wallet2> wallet_test::init_playtime_test_wallet(const std::vector<test_event_entry>& events, currency::core& c, const account_base& acc) const
{
CHECK_AND_ASSERT_THROW_MES(events.size() > 0 && events[0].type() == typeid(currency::block), "Invalid events queue, can't find genesis block at the beginning");
@ -84,6 +85,7 @@ std::shared_ptr<tools::wallet2> wallet_test::init_playtime_test_wallet(const std
w->set_core_proxy(m_core_proxy);
return w;
}
std::shared_ptr<tools::wallet2> wallet_test::init_playtime_test_wallet(const std::vector<test_event_entry>& events, currency::core& c, size_t account_index) const
{
CHECK_AND_ASSERT_THROW_MES(account_index < m_accounts.size(), "Invalid account index");

View file

@ -6,7 +6,7 @@
#pragma once
#include "chaingen.h"
struct wallet_test : public test_chain_unit_enchanced
struct wallet_test : virtual public test_chain_unit_enchanced
{
enum { MINER_ACC_IDX = 0, ALICE_ACC_IDX = 1, BOB_ACC_IDX = 2, CAROL_ACC_IDX = 3, DAN_ACC_IDX = 4, TOTAL_ACCS_COUNT = 5 }; // to be used as index for m_accounts
@ -87,3 +87,15 @@ struct wallet_callback_balance_checker : public tools::i_wallet2_callback
uint64_t m_unlocked_balance;
uint64_t m_total_mined;
};
struct wlt_lambda_on_transfer2_wrapper : public tools::i_wallet2_callback
{
typedef std::function<bool(const tools::wallet_public::wallet_transfer_info&, uint64_t, uint64_t, uint64_t)> Func;
wlt_lambda_on_transfer2_wrapper(Func callback) : m_result(false), m_callback(callback) {}
virtual void on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined) override
{
m_result = m_callback(wti, balance, unlocked_balance, total_mined);
}
bool m_result;
Func m_callback;
};

View file

@ -439,7 +439,8 @@ namespace
"\xf7\x24\xbc\x5c\x6c\xfb\xb9\xd9\x76\x02\xc3\x00\x42\x3a\x2f\x28"
"\x64\x18\x74\x51\x3a\x03\x57\x78\xa0\xc1\x77\x8d\x83\x32\x01\xe9"
"\x22\x09\x39\x68\x9e\xdf\x1a\xbd\x5b\xc1\xd0\x31\xf7\x3e\xcd\x6c"
"\x99\x3a\xdd\x66\xd6\x80\x88\x70\x45\x6a\xfe\xb8\xe7\xee\xb6\x8d");
"\x99\x3a\xdd\x66\xd6\x80\x88\x70\x45\x6a\xfe\xb8\xe7\xee\xb6\x8d"
"\x00");
std::string test_keys_addr_str = "ZxDqHy6WnyYY5yQcdApjMb8tVPik5BC3LFdaevfbGq7X1KY5vdsWmUi5UQgse2GBZFbMsb47TFqBmPpdFHDDwDxR2ZuZ6zX4W"; // correct str address depends on CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX value
}
@ -499,7 +500,7 @@ TEST(get_account_address_from_str, fails_on_invalid_address_spend_key)
TEST(get_account_address_from_str, fails_on_invalid_address_view_key)
{
std::string serialized_keys_copy = test_serialized_keys;
serialized_keys_copy.back() = '\x01';
serialized_keys_copy[serialized_keys_copy.size() - 2] = '\x01';
std::string addr_str = base58::encode_addr(CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy);
currency::account_public_address addr;
@ -551,3 +552,103 @@ TEST(integ_address, payment_id_sizes)
ASSERT_NE(addr2, addr);
ASSERT_NE(integrated_payment_id, payment_id);
}
struct addr_entry_t
{
std::string address;
std::string view_pub_key;
std::string spend_pub_key;
std::string payment_id_hex;
uint8_t flags;
};
addr_entry_t addr_entries[] =
{
{
// classic normal address
"ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH", // address
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
"", // payment_id_hex
0 // flags
},
{
// classic integrated address
"iZ2Zi6RmTWwcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3iTqEsjvJoco1aLSZXS6T", // address
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
"87440d0b9acc42f1", // payment_id_hex
0 // flags
},
{
// new format normal address with custom flags
"ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3APrDvRoL5C", // address
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
"", // payment_id_hex
0xfe // flags
},
{
// new format integrated address with custom flags
"iZ4mBxubNfqcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3iTrG7nU5rRCWmcozLaMoY95sAbo6", // address
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
"3ba0527bcfb1fa93630d28eed6", // payment_id
0xfe // flags
},
{
// normal auditable address
"aZxb9Et6FhP9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJMEmqQFn", // address
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
"", // payment_id
ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE // flags
},
{
// auditable integrated address
"aiZXDondHWu9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJM9xJH8EbjuRiMJgFmPRATsEV9", // address
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
"3ba0527bcfb1fa93630d28eed6", // payment_id
ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE // flags
}
};
void check_add_entry(const addr_entry_t& ae)
{
std::string payment_id, payment_id_hex;
currency::account_public_address addr = AUTO_VAL_INIT(addr);
ASSERT_TRUE(currency::get_account_address_and_payment_id_from_str(addr, payment_id, ae.address));
payment_id_hex = epee::string_tools::buff_to_hex_nodelimer(payment_id);
ASSERT_EQ(ae.flags, addr.flags);
ASSERT_EQ(ae.payment_id_hex, payment_id_hex);
ASSERT_EQ(ae.view_pub_key, epee::string_tools::pod_to_hex(addr.view_public_key));
ASSERT_EQ(ae.spend_pub_key, epee::string_tools::pod_to_hex(addr.spend_public_key));
}
TEST(auditable_addresses, basic)
{
/*
currency::account_keys keys = AUTO_VAL_INIT(keys);
epee::string_tools::parse_tpod_from_hex_string("248b019d145d485576ecb0367d92b5a12e8aa15084b59ef15014a7a22d1f3b0c", keys.spend_secret_key);
dependent_key(keys.spend_secret_key, keys.view_secret_key);
crypto::secret_key_to_public_key(keys.view_secret_key, keys.account_address.view_public_key);
crypto::secret_key_to_public_key(keys.spend_secret_key, keys.account_address.spend_public_key);
keys.account_address.flags = 0xfe;
std::string payment_id;
epee::string_tools::parse_hexstr_to_binbuff(std::string("3ba0527bcfb1fa93630d28eed6"), payment_id);
std::cout << currency::get_account_address_as_str(keys.account_address) << " " << epee::string_tools::pod_to_hex(keys.account_address.view_public_key) << " " << epee::string_tools::pod_to_hex(keys.account_address.spend_public_key) << ENDL;
std::cout << currency::get_account_address_and_payment_id_as_str(keys.account_address, payment_id) << " " << epee::string_tools::pod_to_hex(keys.account_address.view_public_key) << " " << epee::string_tools::pod_to_hex(keys.account_address.spend_public_key) << ENDL;
*/
for (size_t i = 0; i < sizeof addr_entries / sizeof addr_entries[0]; ++i)
check_add_entry(addr_entries[i]);
}

View file

@ -13,10 +13,10 @@ TEST(brain_wallet, store_restore_test)
{
currency::account_base acc;
acc.generate();
auto restore_data = acc.get_restore_data();
auto seed_phrase = acc.get_restore_braindata();
currency::account_base acc2;
bool r = acc2.restore_keys(restore_data);
bool r = acc2.restore_from_braindata(seed_phrase);
ASSERT_TRUE(r);
if (memcmp(&acc2.get_keys(), &acc.get_keys(), sizeof(currency::account_keys)))
@ -29,10 +29,10 @@ TEST(brain_wallet, store_restore_test)
{
currency::account_base acc;
acc.generate();
auto restore_data = acc.get_restore_braindata();
auto seed_phrase = acc.get_restore_braindata();
currency::account_base acc2;
bool r = acc2.restore_keys_from_braindata(restore_data);
bool r = acc2.restore_from_braindata(seed_phrase);
ASSERT_TRUE(r);
if (memcmp(&acc2.get_keys(), &acc.get_keys(), sizeof(currency::account_keys)))
@ -42,3 +42,136 @@ TEST(brain_wallet, store_restore_test)
}
}
struct wallet_seed_entry
{
std::string seed_phrase;
std::string spend_secret_key;
std::string view_secret_key;
uint64_t timestamp;
bool auditable;
bool valid;
};
wallet_seed_entry wallet_seed_entries[] =
{
{
// legacy 24-word seed phrase -- invalid
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew",
"",
"",
0,
false,
false
},
{
// old-style 25-word seed phrase -- valid
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew",
"5e051454d7226b5734ebd64f754b57db4c655ecda00bd324f1b241d0b6381c0f",
"7dde5590fdf430568c00556ac2accf09da6cde9a29a4bc7d1cb6fd267130f006",
0,
false,
true
},
{
// old-style 25-word seed phrase -- valid
"conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation conversation",
"71162f207499bc16260957c36a6586bb931d54be33ff56b94d565dfedbb3c70e",
"8454372096986c457f4e7dceef2f39b6050c35d87b31d9c9eb8d37bf8f1f430f",
0,
false,
true
},
{
// old-style 25-word seed phrase -- invalid word
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew!",
"",
"",
0,
false,
false
},
{
// old-style 25-word seed phrase -- invalid word
"six six six six six six six six six sex six six six six six six six six six six six six six six six",
"",
"",
0,
false,
false
},
{
// new-style 26-word seed phrase -- invalid word
"six six six six six six six six six six six six six six six six six six six six six six six six six sex",
"",
"",
0,
false,
false
},
{
// new-style 26-word seed phrase -- invalid checksum
"six six six six six six six six six six six six six six six six six six six six six six six six six six",
"",
"",
0,
false,
false
},
{
// new-style 26-word seed phrase - valid
"six six six six six six six six six six six six six six six six six six six six six six six six six frown",
"F54F61E3B974AD86171AE4944205C7BD0395BD7845899CDA8B1FBC5C947BB402",
"A18715058BBD914959C3A735B2022E9AE1D04452BC1FAD9E63C53668B7F57907",
1922832000,
false,
true
},
{
// new-style 26-word seed phrase auditable - valid
"six six six six six six six six six six six six six six six six six six six six six six six six six grace",
"F54F61E3B974AD86171AE4944205C7BD0395BD7845899CDA8B1FBC5C947BB402",
"A18715058BBD914959C3A735B2022E9AE1D04452BC1FAD9E63C53668B7F57907",
1922832000,
true,
true
},
};
TEST(wallet_seed, basic_test)
{
for (size_t i = 0; i < sizeof wallet_seed_entries / sizeof wallet_seed_entries[0]; ++i)
{
const wallet_seed_entry& wse = wallet_seed_entries[i];
currency::account_base acc;
bool r = false;
try
{
r = acc.restore_from_braindata(wse.seed_phrase);
}
catch (...)
{
r = false;
}
ASSERT_EQ(r, wse.valid);
if (r)
{
if (wse.timestamp)
ASSERT_EQ(wse.timestamp, acc.get_createtime());
ASSERT_EQ(wse.auditable, acc.get_public_address().is_auditable());
// check keys
crypto::secret_key v, s;
ASSERT_TRUE(epee::string_tools::parse_tpod_from_hex_string(wse.spend_secret_key, s));
ASSERT_EQ(s, acc.get_keys().spend_secret_key);
ASSERT_TRUE(epee::string_tools::parse_tpod_from_hex_string(wse.view_secret_key, v));
ASSERT_EQ(v, acc.get_keys().view_secret_key);
}
}
}

View file

@ -903,7 +903,7 @@ namespace lmdb_test
static const uint64_t buffer_size = 64 * 1024; // 64 KB
static const uint64_t db_total_size = static_cast<uint64_t>(2.1 * 1024 * 1024 * 1024); // 2.1 GB -- a bit more than 2GB to test 2GB boundary
static const std::string db_file_path = std::string("2gb_") + typeid(db_backend_t).name() + "_test";
static const std::string db_file_path = boost::algorithm::replace_all_copy(boost::algorithm::replace_all_copy(std::string("2gb_") + typeid(db_backend_t).name() + "_test", ":", "_"), " ", "_");
std::shared_ptr<db_backend_t> lmdb_ptr = std::make_shared<db_backend_t>();
db::basic_db_accessor bdba(lmdb_ptr, rw_lock);