Subaddresses

pull/95/head
kenshi84 7 years ago
parent 86e9de588c
commit 53ad5a0f42
No known key found for this signature in database
GPG Key ID: 085D092F1F43D51A

@ -117,9 +117,9 @@ namespace epee
typename stl_container::value_type exchange_val;
typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section);
if(!hval_array) return false;
container.push_back(std::move(exchange_val));
container.insert(container.end(), std::move(exchange_val));
while(stg.get_next_value(hval_array, exchange_val))
container.push_back(std::move(exchange_val));
container.insert(container.end(), std::move(exchange_val));
return true;
}//--------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
@ -152,7 +152,7 @@ namespace epee
"size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type));
size_t count = (loaded_size/sizeof(typename stl_container::value_type));
for(size_t i = 0; i < count; i++)
container.push_back(*(pelem++));
container.insert(container.end(), *(pelem++));
}
return res;
}
@ -186,12 +186,12 @@ namespace epee
typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section);
if(!hsec_array || !hchild_section) return false;
res = val._load(stg, hchild_section);
container.push_back(val);
container.insert(container.end(), val);
while(stg.get_next_section(hsec_array, hchild_section))
{
typename stl_container::value_type val_l = typename stl_container::value_type();
res |= val_l._load(stg, hchild_section);
container.push_back(std::move(val_l));
container.insert(container.end(), std::move(val_l));
}
return res;
}
@ -250,6 +250,18 @@ namespace epee
return unserialize_stl_container_t_val(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return serialize_stl_container_t_val(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return unserialize_stl_container_t_val(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
};
template<>
struct kv_serialization_overloads_impl_is_base_serializable_types<false>
@ -302,6 +314,18 @@ namespace epee
{
return unserialize_stl_container_t_obj(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return serialize_stl_container_t_obj(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
static bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return unserialize_stl_container_t_obj(d, stg, hparent_section, pname);
}
};
template<class t_storage>
struct base_serializable_types: public boost::mpl::vector<uint64_t, uint32_t, uint16_t, uint8_t, int64_t, int32_t, int16_t, int8_t, double, bool, std::string, typename t_storage::meta_entry>::type
@ -399,5 +423,17 @@ namespace epee
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool kv_serialize(const std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
bool kv_unserialize(std::set<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
}
}
}

@ -87,7 +87,7 @@ namespace crypto {
random_scalar_not_thread_safe(res);
}
static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
cn_fast_hash(data, length, reinterpret_cast<hash &>(res));
sc_reduce32(&res);
}
@ -189,6 +189,25 @@ namespace crypto {
sc_add(&derived_key, &base, &scalar);
}
bool crypto_ops::derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &derived_key) {
ec_scalar scalar;
ge_p3 point1;
ge_p3 point2;
ge_cached point3;
ge_p1p1 point4;
ge_p2 point5;
if (ge_frombytes_vartime(&point1, &out_key) != 0) {
return false;
}
derivation_to_scalar(derivation, output_index, scalar);
ge_scalarmult_base(&point2, &scalar);
ge_p3_to_cached(&point3, &point2);
ge_sub(&point4, &point1, &point3);
ge_p1p1_to_p2(&point5, &point4);
ge_tobytes(&derived_key, &point5);
return true;
}
struct s_comm {
hash h;
ec_point key;
@ -246,22 +265,33 @@ namespace crypto {
return sc_isnonzero(&c) == 0;
}
void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const secret_key &r, signature &sig) {
void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
// sanity check
ge_p3 R_p3;
ge_p3 A_p3;
ge_p3 B_p3;
ge_p3 D_p3;
if (ge_frombytes_vartime(&R_p3, &R) != 0) throw std::runtime_error("tx pubkey is invalid");
if (ge_frombytes_vartime(&A_p3, &A) != 0) throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) throw std::runtime_error("recipient spend pubkey is invalid");
if (ge_frombytes_vartime(&D_p3, &D) != 0) throw std::runtime_error("key derivation is invalid");
#if !defined(NDEBUG)
{
assert(sc_check(&r) == 0);
// check R == r*G
ge_p3 dbg_R_p3;
ge_scalarmult_base(&dbg_R_p3, &r);
// check R == r*G or R == r*B
public_key dbg_R;
ge_p3_tobytes(&dbg_R, &dbg_R_p3);
if (B)
{
ge_p2 dbg_R_p2;
ge_scalarmult(&dbg_R_p2, &r, &B_p3);
ge_tobytes(&dbg_R, &dbg_R_p2);
}
else
{
ge_p3 dbg_R_p3;
ge_scalarmult_base(&dbg_R_p3, &r);
ge_p3_tobytes(&dbg_R, &dbg_R_p3);
}
assert(R == dbg_R);
// check D == r*A
ge_p2 dbg_D_p2;
@ -276,43 +306,84 @@ namespace crypto {
ec_scalar k;
random_scalar(k);
// compute X = k*G
ge_p3 X_p3;
ge_scalarmult_base(&X_p3, &k);
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D;
if (B)
{
// compute X = k*B
ge_p2 X_p2;
ge_scalarmult(&X_p2, &k, &B_p3);
ge_tobytes(&buf.X, &X_p2);
}
else
{
// compute X = k*G
ge_p3 X_p3;
ge_scalarmult_base(&X_p3, &k);
ge_p3_tobytes(&buf.X, &X_p3);
}
// compute Y = k*A
ge_p2 Y_p2;
ge_scalarmult(&Y_p2, &k, &A_p3);
ge_tobytes(&buf.Y, &Y_p2);
// sig.c = Hs(Msg || D || X || Y)
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D;
ge_p3_tobytes(&buf.X, &X_p3);
ge_tobytes(&buf.Y, &Y_p2);
hash_to_scalar(&buf, sizeof(s_comm_2), sig.c);
hash_to_scalar(&buf, sizeof(buf), sig.c);
// sig.r = k - sig.c*r
sc_mulsub(&sig.r, &sig.c, &r, &k);
}
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const signature &sig) {
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
// sanity check
ge_p3 R_p3;
ge_p3 A_p3;
ge_p3 B_p3;
ge_p3 D_p3;
if (ge_frombytes_vartime(&R_p3, &R) != 0) return false;
if (ge_frombytes_vartime(&A_p3, &A) != 0) return false;
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) return false;
if (ge_frombytes_vartime(&D_p3, &D) != 0) return false;
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) return false;
// compute sig.c*R
ge_p2 cR_p2;
ge_scalarmult(&cR_p2, &sig.c, &R_p3);
ge_p3 cR_p3;
{
ge_p2 cR_p2;
ge_scalarmult(&cR_p2, &sig.c, &R_p3);
public_key cR;
ge_tobytes(&cR, &cR_p2);
if (ge_frombytes_vartime(&cR_p3, &cR) != 0) return false;
}
// compute sig.r*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_p1p1 X_p1p1;
if (B)
{
// compute X = sig.c*R + sig.r*B
ge_p2 rB_p2;
ge_scalarmult(&rB_p2, &sig.r, &B_p3);
public_key rB;
ge_tobytes(&rB, &rB_p2);
ge_p3 rB_p3;
if (ge_frombytes_vartime(&rB_p3, &rB) != 0) return false;
ge_cached rB_cached;
ge_p3_to_cached(&rB_cached, &rB_p3);
ge_add(&X_p1p1, &cR_p3, &rB_cached);
}
else
{
// compute X = sig.c*R + sig.r*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_add(&X_p1p1, &cR_p3, &rG_cached);
}
ge_p2 X_p2;
ge_p1p1_to_p2(&X_p2, &X_p1p1);
// compute sig.c*D
ge_p2 cD_p2;
@ -322,18 +393,6 @@ namespace crypto {
ge_p2 rA_p2;
ge_scalarmult(&rA_p2, &sig.r, &A_p3);
// compute X = sig.c*R + sig.r*G
public_key cR;
ge_tobytes(&cR, &cR_p2);
ge_p3 cR_p3;
if (ge_frombytes_vartime(&cR_p3, &cR) != 0) return false;
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_p1p1 X_p1p1;
ge_add(&X_p1p1, &cR_p3, &rG_cached);
ge_p2 X_p2;
ge_p1p1_to_p2(&X_p2, &X_p1p1);
// compute Y = sig.c*D + sig.r*A
public_key cD;
public_key rA;

@ -35,6 +35,7 @@
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/optional.hpp>
#include <vector>
#include "common/pod-class.h"
@ -98,6 +99,8 @@ namespace crypto {
};
#pragma pack(pop)
void hash_to_scalar(const void *data, size_t length, ec_scalar &res);
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 32 && sizeof(secret_key) == 32 &&
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 &&
@ -123,14 +126,16 @@ namespace crypto {
friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &);
static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
static bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &);
friend bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &);
static void generate_signature(const hash &, const public_key &, const secret_key &, signature &);
friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &);
static bool check_signature(const hash &, const public_key &, const signature &);
friend bool check_signature(const hash &, const public_key &, const signature &);
static void generate_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const secret_key &, signature &);
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const secret_key &, signature &);
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const signature &);
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const signature &);
static void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
static void generate_key_image(const public_key &, const secret_key &, key_image &);
friend void generate_key_image(const public_key &, const secret_key &, key_image &);
static void generate_ring_signature(const hash &, const key_image &,
@ -198,6 +203,9 @@ namespace crypto {
const secret_key &base, secret_key &derived_key) {
crypto_ops::derive_secret_key(derivation, output_index, base, derived_key);
}
inline bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result) {
return crypto_ops::derive_subaddress_public_key(out_key, derivation, output_index, result);
}
/* Generation and checking of a standard signature.
*/
@ -210,12 +218,13 @@ namespace crypto {
/* Generation and checking of a tx proof; given a tx pubkey R, the recipient's view pubkey A, and the key
* derivation D, the signature proves the knowledge of the tx secret key r such that R=r*G and D=r*A
* When the recipient's address is a subaddress, the tx pubkey R is defined as R=r*B where B is the recipient's spend pubkey
*/
inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const secret_key &r, signature &sig) {
crypto_ops::generate_tx_proof(prefix_hash, R, A, D, r, sig);
inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
crypto_ops::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
}
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const signature &sig) {
return crypto_ops::check_tx_proof(prefix_hash, R, A, D, sig);
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig);
}
/* To send money to a key:
@ -270,8 +279,10 @@ namespace crypto {
}
const static crypto::public_key null_pkey = boost::value_initialized<crypto::public_key>();
const static crypto::secret_key null_skey = boost::value_initialized<crypto::secret_key>();
}
CRYPTO_MAKE_HASHABLE(public_key)
CRYPTO_MAKE_HASHABLE(secret_key)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_COMPARABLE(signature)

@ -131,7 +131,7 @@ DISABLE_VS_WARNINGS(4244 4345)
std::string account_base::get_public_address_str(bool testnet) const
{
//TODO: change this code into base 58
return get_account_address_as_str(testnet, m_keys.m_account_address);
return get_account_address_as_str(testnet, false, m_keys.m_account_address);
}
//-----------------------------------------------------------------
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, bool testnet) const

@ -411,6 +411,17 @@ namespace cryptonote
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_public_key)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_public_key)
END_KV_SERIALIZE_MAP()
bool operator==(const account_public_address& rhs) const
{
return m_spend_public_key == rhs.m_spend_public_key &&
m_view_public_key == rhs.m_view_public_key;
}
bool operator!=(const account_public_address& rhs) const
{
return !(*this == rhs);
}
};
struct keypair
@ -429,6 +440,21 @@ namespace cryptonote
}
namespace std {
template <>
struct hash<cryptonote::account_public_address>
{
std::size_t operator()(const cryptonote::account_public_address& addr) const
{
// https://stackoverflow.com/a/17017281
size_t res = 17;
res = res * 31 + hash<crypto::public_key>()(addr.m_spend_public_key);
res = res * 31 + hash<crypto::public_key>()(addr.m_view_public_key);
return res;
}
};
}
BLOB_SERIALIZER(cryptonote::txout_to_key);
BLOB_SERIALIZER(cryptonote::txout_to_scripthash);

@ -158,11 +158,13 @@ namespace cryptonote {
//-----------------------------------------------------------------------
std::string get_account_address_as_str(
bool testnet
, bool subaddress
, account_public_address const & adr
)
{
uint64_t address_prefix = testnet ?
config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
(subaddress ? config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) :
(subaddress ? config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
return tools::base58::encode_addr(address_prefix, t_serializable_object_to_blob(adr));
}
@ -173,8 +175,7 @@ namespace cryptonote {
, crypto::hash8 const & payment_id
)
{
uint64_t integrated_address_prefix = testnet ?
config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
uint64_t integrated_address_prefix = testnet ? config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
integrated_address iadr = {
adr, payment_id
@ -193,10 +194,8 @@ namespace cryptonote {
return true;
}
//-----------------------------------------------------------------------
bool get_account_integrated_address_from_str(
account_public_address& adr
, bool& has_payment_id
, crypto::hash8& payment_id
bool get_account_address_from_str(
address_parse_info& info
, bool testnet
, std::string const & str
)
@ -205,6 +204,8 @@ namespace cryptonote {
config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
uint64_t integrated_address_prefix = testnet ?
config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
uint64_t subaddress_prefix = testnet ?
config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
if (2 * sizeof(public_address_outer_blob) != str.size())
{
@ -218,18 +219,27 @@ namespace cryptonote {
if (integrated_address_prefix == prefix)
{
has_payment_id = true;
info.is_subaddress = false;
info.has_payment_id = true;
}
else if (address_prefix == prefix)
{
has_payment_id = false;
info.is_subaddress = false;
info.has_payment_id = false;
}
else if (subaddress_prefix == prefix)
{
info.is_subaddress = true;
info.has_payment_id = false;
}
else {
LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix << " or " << integrated_address_prefix);
LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix
<< " or " << integrated_address_prefix
<< " or " << subaddress_prefix);
return false;
}
if (has_payment_id)
if (info.has_payment_id)
{
integrated_address iadr;
if (!::serialization::parse_binary(data, iadr))
@ -237,19 +247,19 @@ namespace cryptonote {
LOG_PRINT_L1("Account public address keys can't be parsed");
return false;
}
adr = iadr.adr;
payment_id = iadr.payment_id;
info.address = iadr.adr;
info.payment_id = iadr.payment_id;
}
else
{
if (!::serialization::parse_binary(data, adr))
if (!::serialization::parse_binary(data, info.address))
{
LOG_PRINT_L1("Account public address keys can't be parsed");
return false;
}
}
if (!crypto::check_key(adr.m_spend_public_key) || !crypto::check_key(adr.m_view_public_key))
if (!crypto::check_key(info.address.m_spend_public_key) || !crypto::check_key(info.address.m_view_public_key))
{
LOG_PRINT_L1("Failed to validate address keys");
return false;
@ -284,51 +294,27 @@ namespace cryptonote {
}
//we success
adr = blob.m_address;
has_payment_id = false;
info.address = blob.m_address;
info.is_subaddress = false;
info.has_payment_id = false;
}
return true;
}
//-----------------------------------------------------------------------
bool get_account_address_from_str(
account_public_address& adr
, bool testnet
, std::string const & str
)
{
bool has_payment_id;
crypto::hash8 payment_id;
return get_account_integrated_address_from_str(adr, has_payment_id, payment_id, testnet, str);
}
//--------------------------------------------------------------------------------
bool get_account_address_from_str_or_url(
cryptonote::account_public_address& address
, bool& has_payment_id
, crypto::hash8& payment_id
address_parse_info& info
, bool testnet
, const std::string& str_or_url
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm
)
{
if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url))
if (get_account_address_from_str(info, testnet, str_or_url))
return true;
bool dnssec_valid;
std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, dns_confirm);
return !address_str.empty() &&
get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str);
}
//--------------------------------------------------------------------------------
bool get_account_address_from_str_or_url(
cryptonote::account_public_address& address
, bool testnet
, const std::string& str_or_url
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm
)
{
bool has_payment_id;
crypto::hash8 payment_id;
return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, dns_confirm);
get_account_address_from_str(info, testnet, address_str);
}
//--------------------------------------------------------------------------------
bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) {

@ -75,6 +75,14 @@ namespace cryptonote {
}
}
struct address_parse_info
{
account_public_address address;
bool is_subaddress;
bool has_payment_id;
crypto::hash8 payment_id;
};
/************************************************************************/
/* Cryptonote helper functions */
/************************************************************************/
@ -87,6 +95,7 @@ namespace cryptonote {
std::string get_account_address_as_str(
bool testnet
, bool subaddress
, const account_public_address& adr
);
@ -96,31 +105,14 @@ namespace cryptonote {
, const crypto::hash8& payment_id
);
bool get_account_integrated_address_from_str(
account_public_address& adr
, bool& has_payment_id
, crypto::hash8& payment_id
, bool testnet
, const std::string& str
);
bool get_account_address_from_str(
account_public_address& adr
address_parse_info& info
, bool testnet
, const std::string& str
);
bool get_account_address_from_str_or_url(
cryptonote::account_public_address& address
, bool& has_payment_id
, crypto::hash8& payment_id
, bool testnet
, const std::string& str_or_url
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address
);
bool get_account_address_from_str_or_url(
cryptonote::account_public_address& address
address_parse_info& info
, bool testnet
, const std::string& str_or_url
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address

@ -83,6 +83,11 @@ namespace boost
{
a & reinterpret_cast<char (&)[sizeof(crypto::hash)]>(x);
}
template <class Archive>
inline void serialize(Archive &a, crypto::hash8 &x, const boost::serialization::version_type ver)
{
a & reinterpret_cast<char (&)[sizeof(crypto::hash8)]>(x);
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::txout_to_script &x, const boost::serialization::version_type ver)

@ -129,16 +129,69 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki)
crypto::secret_key get_subaddress_secret_key(const crypto::secret_key& a, const subaddress_index& index)
{
const char prefix[] = "SubAddr";
char data[sizeof(prefix) + sizeof(crypto::secret_key) + sizeof(subaddress_index)];
memcpy(data, prefix, sizeof(prefix));
memcpy(data + sizeof(prefix), &a, sizeof(crypto::secret_key));
memcpy(data + sizeof(prefix) + sizeof(crypto::secret_key), &index, sizeof(subaddress_index));
crypto::secret_key m;
crypto::hash_to_scalar(data, sizeof(data), m);
return m;
}
//---------------------------------------------------------------
bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki)
{
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")");
std::vector<crypto::key_derivation> additional_recv_derivations;
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
{
crypto::key_derivation additional_recv_derivation = AUTO_VAL_INIT(additional_recv_derivation);
r = crypto::generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
additional_recv_derivations.push_back(additional_recv_derivation);
}
boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index);
CHECK_AND_ASSERT_MES(subaddr_recv_info, false, "key image helper: given output pubkey doesn't seem to belong to this address");
return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki);
}
//---------------------------------------------------------------
bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki)
{
if (ack.m_spend_secret_key == crypto::null_skey)
{
// for watch-only wallet, simply copy the known output pubkey
in_ephemeral.pub = out_key;
in_ephemeral.sec = crypto::null_skey;
}
else
{
// derive secret key with subaddress - step 1: original CN derivation
crypto::secret_key scalar_step1;
crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, scalar_step1); // computes Hs(a*R || idx) + b
// step 2: add Hs(a || index_major || index_minor)
crypto::secret_key scalar_step2;
if (received_index.is_zero())
{
scalar_step2 = scalar_step1; // treat index=(0,0) as a special case representing the main address
}
else
{
crypto::secret_key m = get_subaddress_secret_key(ack.m_view_secret_key, received_index);
sc_add((unsigned char*)&scalar_step2, (unsigned char*)&scalar_step1, (unsigned char*)&m);
}
crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec);
in_ephemeral.sec = scalar_step2;
crypto::secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub);
CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key, false, "key image helper precomp: given output pubkey doesn't match the derived one");
}
crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki);
return true;
@ -277,6 +330,40 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const std::vector<uint8_t>& tx_extra)
{
// parse
std::vector<tx_extra_field> tx_extra_fields;
parse_tx_extra(tx_extra, tx_extra_fields);
// find corresponding field
tx_extra_additional_pub_keys additional_pub_keys;
if(!find_tx_extra_field_by_type(tx_extra_fields, additional_pub_keys))
return {};
return additional_pub_keys.data;
}
//---------------------------------------------------------------
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx)
{
return get_additional_tx_pub_keys_from_extra(tx.extra);
}
//---------------------------------------------------------------
bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys)
{
// convert to variant
tx_extra_field field = tx_extra_additional_pub_keys{ additional_pub_keys };
// serialize
std::ostringstream oss;
binary_archive<true> ar(oss);
bool r = ::do_serialize(ar, field);
CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra additional tx pub keys");
// append
std::string tx_extra_str = oss.str();
size_t pos = tx_extra.size();
tx_extra.resize(tx_extra.size() + tx_extra_str.size());
memcpy(&tx_extra[pos], tx_extra_str.data(), tx_extra_str.size());
return true;
}
//---------------------------------------------------------------
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce)
{
CHECK_AND_ASSERT_MES(extra_nonce.size() <= TX_EXTRA_NONCE_MAX_COUNT, false, "extra nonce could be 255 bytes max");
@ -480,20 +567,43 @@ namespace cryptonote
return res;
}
//---------------------------------------------------------------
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index)
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, size_t output_index)
{
crypto::key_derivation derivation;
generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
crypto::public_key pk;
derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
return pk == out_key.key;
if (pk == out_key.key)
return true;
// try additional tx pubkeys if available
if (!additional_tx_pub_keys.empty())
{
CHECK_AND_ASSERT_MES(output_index < additional_tx_pub_keys.size(), false, "wrong number of additional tx pubkeys");
generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation);
derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
return pk == out_key.key;
}
return false;
}
//---------------------------------------------------------------
bool is_out_to_acc_precomp(const crypto::public_key& spend_public_key, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index)
boost::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index)
{
crypto::public_key pk;
derive_public_key(derivation, output_index, spend_public_key, pk);
return pk == out_key.key;
// try the shared tx pubkey
crypto::public_key subaddress_spendkey;
derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey);
auto found = subaddresses.find(subaddress_spendkey);
if (found != subaddresses.end())
return subaddress_receive_info{ found->second, derivation };
// try additional tx pubkeys if available
if (!additional_derivations.empty())
{
CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations");
derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey);
found = subaddresses.find(subaddress_spendkey);
if (found != subaddresses.end())
return subaddress_receive_info{ found->second, additional_derivations[output_index] };
}
return boost::none;
}
//---------------------------------------------------------------
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered)
@ -501,17 +611,19 @@ namespace cryptonote
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
if(null_pkey == tx_pub_key)
return false;
return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered);
std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
return lookup_acc_outs(acc, tx, tx_pub_key, additional_tx_pub_keys, outs, money_transfered);
}
//---------------------------------------------------------------
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered)
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, std::vector<size_t>& outs, uint64_t& money_transfered)
{
CHECK_AND_ASSERT_MES(additional_tx_pub_keys.empty() || additional_tx_pub_keys.size() == tx.vout.size(), false, "wrong number of additional pubkeys" );
money_transfered = 0;
size_t i = 0;
for(const tx_out& o: tx.vout)
{
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" );
if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i))
if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, additional_tx_pub_keys, i))
{
outs.push_back(i);
money_transfered += o.amount;

@ -32,9 +32,11 @@
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "cryptonote_basic_impl.h"
#include "account.h"
#include "subaddress_index.h"
#include "include_base_utils.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include <unordered_map>
namespace cryptonote
{
@ -63,19 +65,29 @@ namespace cryptonote
crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index = 0);
bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key);
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const std::vector<uint8_t>& tx_extra);
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx);
bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys);
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce);
bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type);
void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id);
void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id);
bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id);
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash8& payment_id);
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index);
bool is_out_to_acc_precomp(const crypto::public_key& spend_public_key, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered);
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t output_index);
struct subaddress_receive_info
{
subaddress_index index;
crypto::key_derivation derivation;
};
boost::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_public_keys, std::vector<size_t>& outs, uint64_t& money_transfered);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered);
bool get_tx_fee(const transaction& tx, uint64_t & fee);
uint64_t get_tx_fee(const transaction& tx);
bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki);
crypto::secret_key get_subaddress_secret_key(const crypto::secret_key& a, const subaddress_index& index);
bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki);
bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki);
void get_blob_hash(const blobdata& blob, crypto::hash& res);
crypto::hash get_blob_hash(const blobdata& blob);
std::string short_hash_str(const crypto::hash& h);

@ -228,11 +228,13 @@ namespace cryptonote
if(command_line::has_arg(vm, arg_start_mining))
{
if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining)))
address_parse_info info;
if(!cryptonote::get_account_address_from_str(info, testnet, command_line::get_arg(vm, arg_start_mining)) || info.is_subaddress)
{
LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled");
return false;
}
m_mine_address = info.address;
m_threads_total = 1;
m_do_mining = true;
if(command_line::has_arg(vm, arg_mining_threads))

@ -0,0 +1,102 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "serialization/keyvalue_serialization.h"
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/version.hpp>
#include <ostream>
namespace cryptonote
{
struct subaddress_index
{
uint32_t major;
uint32_t minor;
bool operator==(const subaddress_index& rhs) const { return !memcmp(this, &rhs, sizeof(subaddress_index)); }
bool operator!=(const subaddress_index& rhs) const { return !(*this == rhs); }
bool is_zero() const { return major == 0 && minor == 0; }
BEGIN_SERIALIZE_OBJECT()
FIELD(major)
FIELD(minor)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(major)
KV_SERIALIZE(minor)
END_KV_SERIALIZE_MAP()
};
}
namespace cryptonote {
inline std::ostream& operator<<(std::ostream& out, const cryptonote::subaddress_index& subaddr_index)
{
return out << subaddr_index.major << '/' << subaddr_index.minor;
}
}
namespace std
{
template <>
struct hash<cryptonote::subaddress_index>
{
size_t operator()(const cryptonote::subaddress_index& index ) const
{
size_t res;
if (sizeof(size_t) == 8)
{
res = ((uint64_t)index.major << 32) | index.minor;
}
else
{
// https://stackoverflow.com/a/17017281
res = 17;
res = res * 31 + hash<uint32_t>()(index.major);
res = res * 31 + hash<uint32_t>()(index.minor);
}
return res;
}
};
}
BOOST_CLASS_VERSION(cryptonote::subaddress_index, 0)
namespace boost
{
namespace serialization
{
template <class Archive>
inline void serialize(Archive &a, cryptonote::subaddress_index &x, const boost::serialization::version_type ver)
{
a & x.major;
a & x.minor;
}
}
}

@ -38,6 +38,7 @@
#define TX_EXTRA_TAG_PUBKEY 0x01
#define TX_EXTRA_NONCE 0x02
#define TX_EXTRA_MERGE_MINING_TAG 0x03
#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04
#define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
@ -159,6 +160,16 @@ namespace cryptonote
}
};
// per-output additional tx pubkey for multi-destination transfers involving at least one subaddress
struct tx_extra_additional_pub_keys
{
std::vector<crypto::public_key> data;
BEGIN_SERIALIZE()
FIELD(data)
END_SERIALIZE()
};
struct tx_extra_mysterious_minergate
{
std::string data;
@ -172,11 +183,12 @@ namespace cryptonote
// varint tag;
// varint size;
// varint data[];
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_mysterious_minergate> tx_extra_field;
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate> tx_extra_field;
}
VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_pub_key, TX_EXTRA_TAG_PUBKEY);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG);

@ -147,6 +147,7 @@ namespace config
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18;
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19;
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 26;
uint16_t const P2P_DEFAULT_PORT = 18080;
uint16_t const RPC_DEFAULT_PORT = 18081;
uint16_t const ZMQ_RPC_DEFAULT_PORT = 18082;
@ -160,6 +161,7 @@ namespace config
{
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 53;
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 54;
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 63;
uint16_t const P2P_DEFAULT_PORT = 28080;
uint16_t const RPC_DEFAULT_PORT = 28081;
uint16_t const ZMQ_RPC_DEFAULT_PORT = 28082;

@ -28,6 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <unordered_set>
#include "include_base_utils.h"
using namespace epee;
@ -157,8 +158,14 @@ namespace cryptonote
return destinations[0].addr.m_view_public_key;
}
//---------------------------------------------------------------
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const cryptonote::account_public_address& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct)
{
if (destinations.empty())
{
LOG_ERROR("The destinations must be non-empty");
return false;
}
std::vector<rct::key> amount_keys;
tx.set_null();
amount_keys.clear();
@ -237,8 +244,12 @@ namespace cryptonote
in_contexts.push_back(input_generation_context_data());
keypair& in_ephemeral = in_contexts.back().in_ephemeral;
crypto::key_image img;
if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img))
const auto& out_key = reinterpret_cast<const crypto::public_key&>(src_entr.outputs[src_entr.real_output].second.dest);
if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral, img))
{
LOG_ERROR("Key image generation failed!");
return false;
}
//check that derivated key is equal with real output key
if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
@ -283,6 +294,47 @@ namespace cryptonote
std::swap(sources[i0], sources[i1]);
});
// figure out if we need to make additional tx pubkeys
size_t num_stdaddresses = 0;
size_t num_subaddresses = 0;
std::unordered_set<cryptonote::account_public_address> unique_dst_addresses;
account_public_address single_dest_subaddress;
for(const tx_destination_entry& dst_entr: destinations)
{
if (dst_entr.addr == change_addr)
continue;
if (unique_dst_addresses.count(dst_entr.addr) == 0)
{
unique_dst_addresses.insert(dst_entr.addr);
if (dst_entr.is_subaddress)
{
++num_subaddresses;
single_dest_subaddress = dst_entr.addr;
}
else
{
++num_stdaddresses;
}
}
}
LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << "subaddresses");
// if this is a single-destination transfer to a subaddress, we set the tx pubkey to R=s*D
if (num_stdaddresses == 0 && num_subaddresses == 1)
{
txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(txkey.sec)));
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key));
add_tx_pub_key_to_extra(tx, txkey.pub);
}
std::vector<crypto::public_key> additional_tx_public_keys;
additional_tx_keys.clear();
// we don't need to include additional tx keys if:
// - all the destinations are standard addresses
// - there's only one destination which is a subaddress
bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
uint64_t summary_outs_money = 0;
//fill outputs
size_t output_index = 0;
@ -291,8 +343,35 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
crypto::key_derivation derivation;
crypto::public_key out_eph_public_key;
bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation);
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")");
// make additional tx pubkey if necessary
keypair additional_txkey;
if (need_additional_txkeys)
{
additional_txkey = keypair::generate();
if (dst_entr.is_subaddress)
additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec)));
}
bool r;
if (dst_entr.addr == change_addr)
{
// sending change to yourself; derivation = a*R
r = crypto::generate_key_derivation(txkey.pub, sender_account_keys.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey.pub << ", " << sender_account_keys.m_view_secret_key << ")");
}
else
{
// sending to the recipient; derivation = r*A (or s*C in the subaddress scheme)
r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : txkey.sec, derivation);
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : txkey.sec) << ")");
}
if (need_additional_txkeys)
{
additional_tx_public_keys.push_back(additional_txkey.pub);
additional_tx_keys.push_back(additional_txkey.sec);
}
if (tx.version > 1)
{
@ -313,6 +392,17 @@ namespace cryptonote
summary_outs_money += dst_entr.amount;
}
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys));
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
LOG_PRINT_L2("tx pubkey: " << txkey.pub);
if (need_additional_txkeys)
{
LOG_PRINT_L2("additional tx pubkeys: ");
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
LOG_PRINT_L2(additional_tx_public_keys[i]);
}
//check money
if(summary_outs_money > summary_inputs_money )
{
@ -477,8 +567,11 @@ namespace cryptonote
//---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time)
{
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
crypto::secret_key tx_key;
return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key);
std::vector<crypto::secret_key> additional_tx_keys;
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, destinations.back().addr, extra, tx, unlock_time, tx_key, additional_tx_keys);
}
//---------------------------------------------------------------
bool generate_genesis_block(

@ -46,6 +46,7 @@ namespace cryptonote
std::vector<output_entry> outputs; //index + key + optional ringct commitment
size_t real_output; //index in outputs vector of real output_entry
crypto::public_key real_out_tx_key; //incoming real tx public key
std::vector<crypto::public_key> real_out_additional_tx_keys; //incoming real tx additional public keys
size_t real_output_in_tx_index; //index in transaction outputs vector
uint64_t amount; //money
bool rct; //true if the output is rct
@ -58,20 +59,22 @@ namespace cryptonote
{
uint64_t amount; //money
account_public_address addr; //destination address
bool is_subaddress;
tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { }
tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { }
tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)), is_subaddress(false) { }
tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress) { }
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(addr)
FIELD(is_subaddress)
END_SERIALIZE()
};
//---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false);
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const cryptonote::account_public_address& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false);
bool generate_genesis_block(
block& bl
@ -82,6 +85,7 @@ namespace cryptonote
}
BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0)
BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 1)
namespace boost
{
@ -98,5 +102,15 @@ namespace boost
a & x.rct;
a & x.mask;
}
template <class Archive>
inline void serialize(Archive& a, cryptonote::tx_destination_entry& x, const boost::serialization::version_type ver)
{
a & x.amount;
a & x.addr;
if (ver < 1)
return;
a & x.is_subaddress;
}
}
}

@ -252,20 +252,18 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
return true;
}
cryptonote::account_public_address adr;
bool has_payment_id;
crypto::hash8 payment_id;
cryptonote::address_parse_info info;
bool testnet = false;
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, false, args.front()))
if(!cryptonote::get_account_address_from_str(info, false, args.front()))
{
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, args.front()))
if(!cryptonote::get_account_address_from_str(info, true, args.front()))
{
bool dnssec_valid;
std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(args.front(), dnssec_valid,
[](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid){return addresses[0];});
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, false, address_str))
if(!cryptonote::get_account_address_from_str(info, false, address_str))
{
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, address_str))
if(!cryptonote::get_account_address_from_str(info, true, address_str))
{
std::cout << "target account address has wrong format" << std::endl;
return true;
@ -281,6 +279,11 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
testnet = true;
}
}
if (info.is_subaddress)
{
tools::fail_msg_writer() << "subaddress for mining reward is not yet supported!" << std::endl;
return true;
}
if(testnet)
std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl;
uint64_t threads_count = 1;
@ -307,7 +310,7 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
threads_count = (ok && 0 < threads_count) ? threads_count : 1;
}
m_executor.start_mining(adr, threads_count, testnet, do_background_mining, ignore_battery);
m_executor.start_mining(info.address, threads_count, testnet, do_background_mining, ignore_battery);
return true;
}

@ -1014,7 +1014,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining, bool ignore_battery) {
cryptonote::COMMAND_RPC_START_MINING::request req;
cryptonote::COMMAND_RPC_START_MINING::response res;
req.miner_address = cryptonote::get_account_address_as_str(testnet, address);
req.miner_address = cryptonote::get_account_address_as_str(testnet, false, address);
req.threads_count = num_threads;
req.do_background_mining = do_background_mining;
req.ignore_battery = ignore_battery;

@ -445,9 +445,11 @@ namespace cryptonote {
static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); }
}
namespace rct {
inline std::ostream &operator <<(std::ostream &o, const rct::key &v) {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
}
BLOB_SERIALIZER(rct::key);

@ -693,13 +693,19 @@ namespace cryptonote
bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res)
{
CHECK_CORE_READY();
account_public_address adr;
if(!get_account_address_from_str(adr, m_testnet, req.miner_address))
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, m_testnet, req.miner_address))
{
res.status = "Failed, wrong address";
LOG_PRINT_L0(res.status);
return true;
}
if (info.is_subaddress)
{
res.status = "Mining to subaddress isn't supported yet";
LOG_PRINT_L0(res.status);
return true;
}
unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
@ -721,7 +727,7 @@ namespace cryptonote
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
{
res.status = "Failed, mining not started";
LOG_PRINT_L0(res.status);
@ -755,7 +761,7 @@ namespace cryptonote
res.speed = lMiner.get_speed();
res.threads_count = lMiner.get_threads_count();
const account_public_address& lMiningAdr = lMiner.get_mining_address();
res.address = get_account_address_as_str(m_testnet, lMiningAdr);
res.address = get_account_address_as_str(m_testnet, false, lMiningAdr);
}
res.status = CORE_RPC_STATUS_OK;
@ -935,19 +941,25 @@ namespace cryptonote
return false;
}
cryptonote::account_public_address acc = AUTO_VAL_INIT(acc);
cryptonote::address_parse_info info;
if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(acc, m_testnet, req.wallet_address))
if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, m_testnet, req.wallet_address))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS;
error_resp.message = "Failed to parse wallet address";
return false;
}
if (info.is_subaddress)
{
error_resp.code = CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS;
error_resp.message = "Mining to subaddress is not supported yet";
return false;
}
block b = AUTO_VAL_INIT(b);
cryptonote::blobdata blob_reserve;
blob_reserve.resize(req.reserve_size, 0);
if(!m_core.get_block_template(b, acc, res.difficulty, res.height, res.expected_reward, blob_reserve))
if(!m_core.get_block_template(b, info.address, res.difficulty, res.height, res.expected_reward, blob_reserve))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";

@ -41,5 +41,6 @@
#define CORE_RPC_ERROR_CODE_CORE_BUSY -9
#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10
#define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11
#define CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS -12

@ -412,14 +412,21 @@ namespace rpc
void DaemonHandler::handle(const StartMining::Request& req, StartMining::Response& res)
{
account_public_address adr;
if(!get_account_address_from_str(adr, m_core.get_testnet(), req.miner_address))
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, m_core.get_testnet(), req.miner_address))
{
res.error_details = "Failed, wrong address";
LOG_PRINT_L0(res.error_details);
res.status = Message::STATUS_FAILED;
return;
}
if (info.is_subaddress)
{
res.error_details = "Failed, mining to subaddress isn't supported yet";
LOG_PRINT_L0(res.error_details);
res.status = Message::STATUS_FAILED;
return;
}
unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
@ -442,7 +449,7 @@ namespace rpc
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
{
res.error_details = "Failed, mining not started";
LOG_PRINT_L0(res.error_details);
@ -518,7 +525,7 @@ namespace rpc
res.speed = lMiner.get_speed();
res.threads_count = lMiner.get_threads_count();
const account_public_address& lMiningAdr = lMiner.get_mining_address();
res.address = get_account_address_as_str(m_core.get_testnet(), lMiningAdr);
res.address = get_account_address_as_str(m_core.get_testnet(), false, lMiningAdr);
}
res.status = Message::STATUS_OK;

File diff suppressed because it is too large Load Diff

@ -128,7 +128,7 @@ namespace cryptonote
bool stop_mining(const std::vector<std::string> &args);
bool save_bc(const std::vector<std::string>& args);
bool refresh(const std::vector<std::string> &args);
bool show_balance_unlocked();
bool show_balance_unlocked(bool detailed = false);
bool show_balance(const std::vector<std::string> &args = std::vector<std::string>());
bool show_incoming_transfers(const std::vector<std::string> &args);
bool show_payments(const std::vector<std::string> &args);
@ -147,6 +147,8 @@ namespace cryptonote
std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits
);
bool account(const std::vector<std::string> &args = std::vector<std::string>());
void print_accounts();
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
@ -157,13 +159,13 @@ namespace cryptonote
bool set_log(const std::vector<std::string> &args);
bool get_tx_key(const std::vector<std::string> &args);
bool check_tx_key(const std::vector<std::string> &args);
bool check_tx_key_helper(const crypto::hash &txid, const cryptonote::account_public_address &address, const crypto::key_derivation &derivation);
bool check_tx_key_helper(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const crypto::key_derivation &derivation);
bool get_tx_proof(const std::vector<std::string> &args);
bool check_tx_proof(const std::vector<std::string> &args);
bool show_transfers(const std::vector<std::string> &args);
bool unspent_outputs(const std::vector<std::string> &args);
bool rescan_blockchain(const std::vector<std::string> &args);
bool refresh_main(uint64_t start_height, bool reset = false);
bool refresh_main(uint64_t start_height, bool reset = false, bool is_init = false);
bool set_tx_note(const std::vector<std::string> &args);
bool get_tx_note(const std::vector<std::string> &args);
bool status(const std::vector<std::string> &args);
@ -207,9 +209,9 @@ namespace cryptonote
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block);
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount);
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount);
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx);
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index);
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index);
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
//----------------------------------------------------------
@ -300,5 +302,6 @@ namespace cryptonote
std::atomic<bool> m_auto_refresh_enabled;
bool m_auto_refresh_refreshing;
std::atomic<bool> m_in_manual_refresh;
uint32_t m_current_subaddress_account;
};
}

@ -41,6 +41,8 @@ set(wallet_sources
api/pending_transaction.cpp
api/utils.cpp
api/address_book.cpp
api/subaddress.cpp
api/subaddress_account.cpp
api/unsigned_transaction.cpp)
set(wallet_api_headers
@ -62,6 +64,8 @@ set(wallet_private_headers
api/pending_transaction.h
api/common_defines.h
api/address_book.h
api/subaddress.h
api/subaddress_account.h
api/unsigned_transaction.h)
monero_private_headers(wallet

@ -48,10 +48,8 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa
{
clearStatus();
cryptonote::account_public_address addr;
bool has_short_pid;
crypto::hash8 payment_id_short;
if(!cryptonote::get_account_integrated_address_from_str(addr, has_short_pid, payment_id_short, m_wallet->m_wallet->testnet(), dst_addr)) {
cryptonote::address_parse_info info;
if(!cryptonote::get_account_address_from_str(info, m_wallet->m_wallet->testnet(), dst_addr)) {
m_errorString = tr("Invalid destination address");
m_errorCode = Invalid_Address;
return false;
@ -75,19 +73,19 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa
}
// integrated + long payment id provided
if(has_long_pid && has_short_pid) {
if(has_long_pid && info.has_payment_id) {
m_errorString = tr("Integrated address and long payment id can't be used at the same time");
m_errorCode = Invalid_Payment_Id;
return false;
}
// Pad short pid with zeros
if (has_short_pid)
if (info.has_payment_id)
{
memcpy(payment_id.data, payment_id_short.data, 8);
memcpy(payment_id.data, info.payment_id.data, 8);
}
bool r = m_wallet->m_wallet->add_address_book_row(addr,payment_id,description);
bool r = m_wallet->m_wallet->add_address_book_row(info.address,payment_id,description,info.is_subaddress);
if (r)
refresh();
else
@ -107,9 +105,9 @@ void AddressBookImpl::refresh()
tools::wallet2::address_book_row * row = &rows.at(i);
std::string payment_id = (row->m_payment_id == crypto::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id);
std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->testnet(),row->m_address);
std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->testnet(), row->m_is_subaddress, row->m_address);
// convert the zero padded short payment id to integrated address
if (payment_id.length() > 16 && payment_id.substr(16).find_first_not_of('0') == std::string::npos) {
if (!row->m_is_subaddress && payment_id.length() > 16 && payment_id.substr(16).find_first_not_of('0') == std::string::npos) {
payment_id = payment_id.substr(0,16);
crypto::hash8 payment_id_short;
if(tools::wallet2::parse_short_payment_id(payment_id, payment_id_short)) {

@ -172,6 +172,22 @@ uint64_t PendingTransactionImpl::txCount() const
return m_pending_tx.size();
}
std::vector<uint32_t> PendingTransactionImpl::subaddrAccount() const
{
std::vector<uint32_t> result;
for (const auto& ptx : m_pending_tx)
result.push_back(ptx.construction_data.subaddr_account);
return result;
}
std::vector<std::set<uint32_t>> PendingTransactionImpl::subaddrIndices() const
{
std::vector<std::set<uint32_t>> result;
for (const auto& ptx : m_pending_tx)
result.push_back(ptx.construction_data.subaddr_indices);
return result;
}
}
namespace Bitmonero = Monero;

@ -51,6 +51,8 @@ public:
uint64_t fee() const;
std::vector<std::string> txid() const;
uint64_t txCount() const;
std::vector<uint32_t> subaddrAccount() const;
std::vector<std::set<uint32_t>> subaddrIndices() const;
// TODO: continue with interface;
private:

@ -0,0 +1,91 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "subaddress.h"
#include "wallet.h"
#include "crypto/hash.h"
#include "wallet/wallet2.h"
#include "common_defines.h"
#include <vector>
namespace Monero {
Subaddress::~Subaddress() {}
SubaddressImpl::SubaddressImpl(WalletImpl *wallet)
: m_wallet(wallet) {}
void SubaddressImpl::addRow(uint32_t accountIndex, const std::string &label)
{
m_wallet->m_wallet->add_subaddress(accountIndex, label);
refresh(accountIndex);
}
void SubaddressImpl::setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label)
{
try
{
m_wallet->m_wallet->set_subaddress_label({accountIndex, addressIndex}, label);
refresh(accountIndex);
}
catch (const std::exception& e)
{
LOG_ERROR("setLabel: " << e.what());
}
}
void SubaddressImpl::refresh(uint32_t accountIndex)
{
LOG_PRINT_L2("Refreshing subaddress");
clearRows();
for (size_t i = 0; i < m_wallet->m_wallet->get_num_subaddresses(accountIndex); ++i)
{
m_rows.push_back(new SubaddressRow(i, m_wallet->m_wallet->get_subaddress_as_str({accountIndex, (uint32_t)i}), m_wallet->m_wallet->get_subaddress_label({accountIndex, (uint32_t)i})));
}
}
void SubaddressImpl::clearRows() {
for (auto r : m_rows) {
delete r;
}
m_rows.clear();
}
std::vector<SubaddressRow*> SubaddressImpl::getAll() const
{
return m_rows;
}
SubaddressImpl::~SubaddressImpl()
{
clearRows();
}
} // namespace

@ -0,0 +1,56 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "wallet/wallet2_api.h"
#include "wallet/wallet2.h"
namespace Monero {
class WalletImpl;
class SubaddressImpl : public Subaddress
{
public:
SubaddressImpl(WalletImpl * wallet);
~SubaddressImpl();
// Fetches addresses from Wallet2
void refresh(uint32_t accountIndex);
std::vector<SubaddressRow*> getAll() const;
void addRow(uint32_t accountIndex, const std::string &label);
void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label);
private:
void clearRows();
private:
WalletImpl *m_wallet;
std::vector<SubaddressRow*> m_rows;
};
}

@ -0,0 +1,90 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "subaddress_account.h"
#include "wallet.h"
#include "crypto/hash.h"
#include "wallet/wallet2.h"
#include "common_defines.h"
#include <vector>
namespace Monero {
SubaddressAccount::~SubaddressAccount() {}
SubaddressAccountImpl::SubaddressAccountImpl(WalletImpl *wallet)
: m_wallet(wallet) {}
void SubaddressAccountImpl::addRow(const std::string &label)
{
m_wallet->m_wallet->add_subaddress_account(label);
refresh();
}
void SubaddressAccountImpl::setLabel(uint32_t accountIndex, const std::string &label)
{
m_wallet->m_wallet->set_subaddress_label({accountIndex, 0}, label);
refresh();
}
void SubaddressAccountImpl::refresh()
{
LOG_PRINT_L2("Refreshing subaddress account");
clearRows();
for (uint32_t i = 0; i < m_wallet->m_wallet->get_num_subaddress_accounts(); ++i)
{
m_rows.push_back(new SubaddressAccountRow(
i,
m_wallet->m_wallet->get_subaddress_as_str({i,0}).substr(0,6),
m_wallet->m_wallet->get_subaddress_label({i,0}),
cryptonote::print_money(m_wallet->m_wallet->balance(i)),
cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i))
));
}
}
void SubaddressAccountImpl::clearRows() {
for (auto r : m_rows) {
delete r;
}
m_rows.clear();
}
std::vector<SubaddressAccountRow*> SubaddressAccountImpl::getAll() const
{
return m_rows;
}
SubaddressAccountImpl::~SubaddressAccountImpl()
{
clearRows();
}
} // namespace

@ -0,0 +1,56 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "wallet/wallet2_api.h"
#include "wallet/wallet2.h"
namespace Monero {
class WalletImpl;
class SubaddressAccountImpl : public SubaddressAccount
{
public:
SubaddressAccountImpl(WalletImpl * wallet);
~SubaddressAccountImpl();
// Fetches addresses from Wallet2
void refresh();
std::vector<SubaddressAccountRow*> getAll() const;
void addRow(const std::string &label);
void setLabel(uint32_t accountIndex, const std::string &label);
private:
void clearRows();
private:
WalletImpl *m_wallet;
std::vector<SubaddressAccountRow*> m_rows;
};
}

@ -130,6 +130,9 @@ void TransactionHistoryImpl::refresh()
ti->m_direction = TransactionInfo::Direction_In;
ti->m_hash = string_tools::pod_to_hex(pd.m_tx_hash);
ti->m_blockheight = pd.m_block_height;
ti->m_subaddrIndex = { pd.m_subaddr_index.minor };
ti->m_subaddrAccount = pd.m_subaddr_index.major;
ti->m_label = m_wallet->m_wallet->get_subaddress_label(pd.m_subaddr_index);
ti->m_timestamp = pd.m_timestamp;
ti->m_confirmations = wallet_height - pd.m_block_height;
ti->m_unlock_time = pd.m_unlock_time;
@ -174,12 +177,15 @@ void TransactionHistoryImpl::refresh()
ti->m_direction = TransactionInfo::Direction_Out;
ti->m_hash = string_tools::pod_to_hex(hash);
ti->m_blockheight = pd.m_block_height;
ti->m_subaddrIndex = pd.m_subaddr_indices;
ti->m_subaddrAccount = pd.m_subaddr_account;
ti->m_label = pd.m_subaddr_indices.size() == 1 ? m_wallet->m_wallet->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : "";
ti->m_timestamp = pd.m_timestamp;
ti->m_confirmations = wallet_height - pd.m_block_height;
// single output transaction might contain multiple transfers
for (const auto &d: pd.m_dests) {
ti->m_transfers.push_back({d.amount, get_account_address_as_str(m_wallet->m_wallet->testnet(), d.addr)});
ti->m_transfers.push_back({d.amount, get_account_address_as_str(m_wallet->m_wallet->testnet(), d.is_subaddress, d.addr)});
}
m_history.push_back(ti);
}
@ -199,12 +205,15 @@ void TransactionHistoryImpl::refresh()
TransactionInfoImpl * ti = new TransactionInfoImpl();
ti->m_paymentid = payment_id;
ti->m_amount = amount - pd.m_change;
ti->m_amount = amount - pd.m_change - fee;
ti->m_fee = fee;
ti->m_direction = TransactionInfo::Direction_Out;
ti->m_failed = is_failed;
ti->m_pending = true;
ti->m_hash = string_tools::pod_to_hex(hash);
ti->m_subaddrIndex = pd.m_subaddr_indices;
ti->m_subaddrAccount = pd.m_subaddr_account;
ti->m_label = pd.m_subaddr_indices.size() == 1 ? m_wallet->m_wallet->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : "";
ti->m_timestamp = pd.m_timestamp;
ti->m_confirmations = 0;
m_history.push_back(ti);
@ -226,6 +235,9 @@ void TransactionHistoryImpl::refresh()
ti->m_hash = string_tools::pod_to_hex(pd.m_tx_hash);
ti->m_blockheight = pd.m_block_height;
ti->m_pending = true;
ti->m_subaddrIndex = { pd.m_subaddr_index.minor };
ti->m_subaddrAccount = pd.m_subaddr_index.major;
ti->m_label = m_wallet->m_wallet->get_subaddress_label(pd.m_subaddr_index);
ti->m_timestamp = pd.m_timestamp;
ti->m_confirmations = 0;
m_history.push_back(ti);

@ -48,6 +48,7 @@ TransactionInfoImpl::TransactionInfoImpl()
, m_amount(0)
, m_fee(0)
, m_blockheight(0)
, m_subaddrAccount(0)
, m_timestamp(0)
, m_confirmations(0)
, m_unlock_time(0)
@ -91,6 +92,22 @@ uint64_t TransactionInfoImpl::blockHeight() const
return m_blockheight;
}
std::set<uint32_t> TransactionInfoImpl::subaddrIndex() const
{
return m_subaddrIndex;
}
uint32_t TransactionInfoImpl::subaddrAccount() const
{
return m_subaddrAccount;
}
string TransactionInfoImpl::label() const
{
return m_label;
}
string TransactionInfoImpl::hash() const
{
return m_hash;

@ -50,6 +50,9 @@ public:
//! always 0 for incoming txes
virtual uint64_t fee() const;
virtual uint64_t blockHeight() const;
virtual std::set<uint32_t> subaddrIndex() const;
virtual uint32_t subaddrAccount() const;
virtual std::string label() const;
virtual std::string hash() const;
virtual std::time_t timestamp() const;
@ -65,6 +68,9 @@ private:
uint64_t m_amount;
uint64_t m_fee;
uint64_t m_blockheight;
std::set<uint32_t> m_subaddrIndex; // always unique index for incoming transfers; can be multiple indices for outgoing transfers
uint32_t m_subaddrAccount;
std::string m_label;
std::string m_hash;
std::time_t m_timestamp;
std::string m_paymentid;

@ -102,12 +102,38 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
// gather info to ask the user
uint64_t amount = 0, amount_to_dests = 0, change = 0;
size_t min_ring_size = ~0;
std::unordered_map<std::string, uint64_t> dests;
const std::string wallet_address = m_wallet.m_wallet->get_account().get_public_address_str(m_wallet.m_wallet->testnet());
std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
int first_known_non_zero_change_index = -1;
std::string payment_id_string = "";
for (size_t n = 0; n < get_num_txes(); ++n)
{
const tools::wallet2::tx_construction_data &cd = get_tx(n);
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
bool has_encrypted_payment_id = false;
crypto::hash8 payment_id8 = crypto::null_hash8;
if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
{
cryptonote::tx_extra_nonce extra_nonce;
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
crypto::hash payment_id;
if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
if (!payment_id_string.empty())
payment_id_string += ", ";
payment_id_string = std::string("encrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id8);
has_encrypted_payment_id = true;
}
else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
if (!payment_id_string.empty())
payment_id_string += ", ";
payment_id_string = std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id);
}
}
}
for (size_t s = 0; s < cd.sources.size(); ++s)
{
amount += cd.sources[s].amount;
@ -118,24 +144,31 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
{
const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
std::string address = get_account_address_as_str(m_wallet.m_wallet->testnet(), entry.addr);
std::unordered_map<std::string,uint64_t>::iterator i = dests.find(address);
std::string address, standard_address = get_account_address_as_str(m_wallet.testnet(), entry.is_subaddress, entry.addr);
if (has_encrypted_payment_id && !entry.is_subaddress)
{
address = get_account_integrated_address_as_str(m_wallet.testnet(), entry.addr, payment_id8);
address += std::string(" (" + standard_address + " with encrypted payment id " + epee::string_tools::pod_to_hex(payment_id8) + ")");
}
else
address = standard_address;
auto i = dests.find(entry.addr);
if (i == dests.end())
dests.insert(std::make_pair(address, entry.amount));
dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
else
i->second += entry.amount;
i->second.second += entry.amount;
amount_to_dests += entry.amount;
}
if (cd.change_dts.amount > 0)
{
std::unordered_map<std::string, uint64_t>::iterator it = dests.find(get_account_address_as_str(m_wallet.m_wallet->testnet(), cd.change_dts.addr));
auto it = dests.find(cd.change_dts.addr);
if (it == dests.end())
{
m_status = Status_Error;
m_errorString = tr("Claimed change does not go to a paid address");
return false;
}
if (it->second < cd.change_dts.amount)
if (it->second.second < cd.change_dts.amount)
{
m_status = Status_Error;
m_errorString = tr("Claimed change is larger than payment to the change address");
@ -153,15 +186,15 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
}
}
change += cd.change_dts.amount;
it->second -= cd.change_dts.amount;
if (it->second == 0)
dests.erase(get_account_address_as_str(m_wallet.m_wallet->testnet(), cd.change_dts.addr));
it->second.second -= cd.change_dts.amount;
if (it->second.second == 0)
dests.erase(cd.change_dts.addr);
}
}
std::string dest_string;
for (std::unordered_map<std::string, uint64_t>::const_iterator i = dests.begin(); i != dests.end(); )
for (auto i = dests.begin(); i != dests.end(); )
{
dest_string += (boost::format(tr("sending %s to %s")) % cryptonote::print_money(i->second) % i->first).str();
dest_string += (boost::format(tr("sending %s to %s")) % cryptonote::print_money(i->second.second) % i->second.first).str();
++i;
if (i != dests.end())
dest_string += ", ";
@ -172,7 +205,7 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
std::string change_string;
if (change > 0)
{
std::string address = get_account_address_as_str(m_wallet.m_wallet->testnet(), get_tx(0).change_dts.addr);
std::string address = get_account_address_as_str(m_wallet.m_wallet->testnet(), get_tx(0).subaddr_account > 0, get_tx(0).change_dts.addr);
change_string += (boost::format(tr("%s change to %s")) % cryptonote::print_money(change) % address).str();
}
else
@ -260,7 +293,7 @@ std::vector<std::string> UnsignedTransactionImpl::recipientAddress() const
// TODO: return integrated address if short payment ID exists
std::vector<string> result;
for (const auto &utx: m_unsigned_tx_set.txes) {
result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->testnet(), utx.dests[0].addr));
result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->testnet(), utx.dests[0].is_subaddress, utx.dests[0].addr));
}
return result;
}

@ -34,6 +34,8 @@
#include "unsigned_transaction.h"
#include "transaction_history.h"
#include "address_book.h"
#include "subaddress.h"
#include "subaddress_account.h"
#include "common_defines.h"
#include "common/util.h"
@ -100,14 +102,15 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount)
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index)
{
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
LOG_PRINT_L3(__FUNCTION__ << ": money received. height: " << height
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount));
<< ", amount: " << print_money(amount)
<< ", idx: " << subaddr_index);
// do not signal on received tx if wallet is not syncronized completely
if (m_listener && m_wallet->synchronized()) {
m_listener->moneyReceived(tx_hash, amount);
@ -115,14 +118,15 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount)
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index)
{
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
LOG_PRINT_L3(__FUNCTION__ << ": unconfirmed money received. height: " << height
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount));
<< ", amount: " << print_money(amount)
<< ", idx: " << subaddr_index);
// do not signal on received tx if wallet is not syncronized completely
if (m_listener && m_wallet->synchronized()) {
m_listener->unconfirmedMoneyReceived(tx_hash, amount);
@ -131,13 +135,14 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx,
uint64_t amount, const cryptonote::transaction& spend_tx)
uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index)
{
// TODO;
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
LOG_PRINT_L3(__FUNCTION__ << ": money spent. height: " << height
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount));
<< ", amount: " << print_money(amount)
<< ", idx: " << subaddr_index);
// do not signal on sent tx if wallet is not syncronized completely
if (m_listener && m_wallet->synchronized()) {
m_listener->moneySpent(tx_hash, amount);
@ -198,18 +203,14 @@ bool Wallet::paymentIdValid(const string &paiment_id)
bool Wallet::addressValid(const std::string &str, bool testnet)
{
bool has_payment_id;
cryptonote::account_public_address address;
crypto::hash8 pid;
return get_account_integrated_address_from_str(address, has_payment_id, pid, testnet, str);
cryptonote::address_parse_info info;
return get_account_address_from_str(info, testnet, str);
}
bool Wallet::keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error)
{
bool has_payment_id;
cryptonote::account_public_address address;
crypto::hash8 pid;
if(!get_account_integrated_address_from_str(address, has_payment_id, pid, testnet, address_string)) {
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, testnet, address_string)) {
error = tr("Failed to parse address");
return false;
}
@ -230,9 +231,9 @@ bool Wallet::keyValid(const std::string &secret_key_string, const std::string &a
}
bool matchAddress = false;
if(isViewKey)
matchAddress = address.m_view_public_key == pkey;
matchAddress = info.address.m_view_public_key == pkey;
else
matchAddress = address.m_spend_public_key == pkey;
matchAddress = info.address.m_spend_public_key == pkey;
if(!matchAddress) {
error = tr("key does not match address");
@ -244,14 +245,12 @@ bool Wallet::keyValid(const std::string &secret_key_string, const std::string &a
std::string Wallet::paymentIdFromAddress(const std::string &str, bool testnet)
{
bool has_payment_id;
cryptonote::account_public_address address;
crypto::hash8 pid;
if (!get_account_integrated_address_from_str(address, has_payment_id, pid, testnet, str))
cryptonote::address_parse_info info;
if (!get_account_address_from_str(info, testnet, str))
return "";
if (!has_payment_id)
if (!info.has_payment_id)
return "";
return epee::string_tools::pod_to_hex(pid);
return epee::string_tools::pod_to_hex(info.payment_id);
}
uint64_t Wallet::maximumAllowedAmount()
@ -286,6 +285,8 @@ WalletImpl::WalletImpl(bool testnet)
m_refreshThreadDone = false;
m_refreshEnabled = false;
m_addressBook = new AddressBookImpl(this);
m_subaddress = new SubaddressImpl(this);
m_subaddressAccount = new SubaddressAccountImpl(this);
m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS;
@ -309,6 +310,8 @@ WalletImpl::~WalletImpl()
delete m_wallet2Callback;
delete m_history;
delete m_addressBook;
delete m_subaddress;
delete m_subaddressAccount;
delete m_wallet;
LOG_PRINT_L1(__FUNCTION__ << " finished");
}
@ -423,10 +426,8 @@ bool WalletImpl::recoverFromKeys(const std::string &path,
const std::string &viewkey_string,
const std::string &spendkey_string)
{
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 new_payment_id;
if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, m_wallet->testnet(), address_string))
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, m_wallet->testnet(), address_string))
{
m_errorString = tr("failed to parse address");
m_status = Status_Error;
@ -471,7 +472,7 @@ bool WalletImpl::recoverFromKeys(const std::string &path,
m_status = Status_Error;
return false;
}
if (address.m_spend_public_key != pkey) {
if (info.address.m_spend_public_key != pkey) {
m_errorString = tr("spend key does not match address");
m_status = Status_Error;
return false;
@ -482,7 +483,7 @@ bool WalletImpl::recoverFromKeys(const std::string &path,
m_status = Status_Error;
return false;
}
if (address.m_view_public_key != pkey) {
if (info.address.m_view_public_key != pkey) {
m_errorString = tr("view key does not match address");
m_status = Status_Error;
return false;
@ -491,12 +492,12 @@ bool WalletImpl::recoverFromKeys(const std::string &path,
try
{
if (has_spendkey) {
m_wallet->generate(path, "", address, spendkey, viewkey);
m_wallet->generate(path, "", info.address, spendkey, viewkey);
setSeedLanguage(language);
LOG_PRINT_L1("Generated new wallet from keys with seed language: " + language);
}
else {
m_wallet->generate(path, "", address, viewkey);
m_wallet->generate(path, "", info.address, viewkey);
LOG_PRINT_L1("Generated new view only wallet from keys");
}
@ -635,9 +636,9 @@ bool WalletImpl::setPassword(const std::string &password)
return m_status == Status_Ok;
}
std::string WalletImpl::address() const
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const
{
return m_wallet->get_account().get_public_address_str(m_wallet->testnet());
return m_wallet->get_subaddress_as_str({accountIndex, addressIndex});
}
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
@ -646,7 +647,7 @@ std::string WalletImpl::integratedAddress(const std::string &payment_id) const
if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) {
return "";
}
return m_wallet->get_account().get_public_integrated_address_str(pid, m_wallet->testnet());
return m_wallet->get_integrated_address_as_str(pid);
}
std::string WalletImpl::secretViewKey() const
@ -720,14 +721,14 @@ void WalletImpl::setRecoveringFromSeed(bool recoveringFromSeed)
m_recoveringFromSeed = recoveringFromSeed;
}
uint64_t WalletImpl::balance() const
uint64_t WalletImpl::balance(uint32_t accountIndex) const
{
return m_wallet->balance();
return m_wallet->balance(accountIndex);
}
uint64_t WalletImpl::unlockedBalance() const
uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const
{
return m_wallet->unlocked_balance();
return m_wallet->unlocked_balance(accountIndex);
}
uint64_t WalletImpl::blockChainHeight() const
@ -914,6 +915,50 @@ bool WalletImpl::importKeyImages(const string &filename)
return true;
}
void WalletImpl::addSubaddressAccount(const std::string& label)
{
m_wallet->add_subaddress_account(label);
}
size_t WalletImpl::numSubaddressAccounts() const
{
return m_wallet->get_num_subaddress_accounts();
}
size_t WalletImpl::numSubaddresses(uint32_t accountIndex) const
{
return m_wallet->get_num_subaddresses(accountIndex);
}
void WalletImpl::addSubaddress(uint32_t accountIndex, const std::string& label)
{
m_wallet->add_subaddress(accountIndex, label);
}
std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const
{
try
{
return m_wallet->get_subaddress_label({accountIndex, addressIndex});
}
catch (const std::exception &e)
{
LOG_ERROR("Error getting subaddress label: ") << e.what();
m_errorString = string(tr("Failed to get subaddress label: ")) + e.what();
m_status = Status_Error;
return "";
}
}
void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label)
{
try
{
return m_wallet->set_subaddress_label({accountIndex, addressIndex}, label);
}
catch (const std::exception &e)
{
LOG_ERROR("Error setting subaddress label: ") << e.what();
m_errorString = string(tr("Failed to set subaddress label: ")) + e.what();
m_status = Status_Error;
}
}
// TODO:
// 1 - properly handle payment id (add another menthod with explicit 'payment_id' param)
// 2 - check / design how "Transaction" can be single interface
@ -925,6 +970,7 @@ bool WalletImpl::importKeyImages(const string &filename)
// - confirmed_transfer_details)
PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count,
uint32_t subaddr_account, std::set<uint32_t> subaddr_indices,
PendingTransaction::Priority priority)
{
@ -932,11 +978,9 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
// Pause refresh thread while creating transaction
pauseRefresh();
cryptonote::account_public_address addr;
cryptonote::address_parse_info info;
// indicates if dst_addr is integrated address (address + payment_id)
bool has_payment_id;
crypto::hash8 payment_id_short;
// TODO: (https://bitcointalk.org/index.php?topic=753252.msg9985441#msg9985441)
size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin();
if (fake_outs_count == 0)
@ -945,7 +989,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
do {
if(!cryptonote::get_account_integrated_address_from_str(addr, has_payment_id, payment_id_short, m_wallet->testnet(), dst_addr)) {
if(!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), dst_addr)) {
// TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
m_status = Status_Error;
m_errorString = "Invalid destination address";
@ -955,7 +999,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
std::vector<uint8_t> extra;
// if dst_addr is not an integrated address, parse payment_id
if (!has_payment_id && !payment_id.empty()) {
if (!info.has_payment_id && !payment_id.empty()) {
// copy-pasted from simplewallet.cpp:2212
crypto::hash payment_id_long;
bool r = tools::wallet2::parse_long_payment_id(payment_id, payment_id_long);
@ -964,10 +1008,10 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id_long);
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
} else {
r = tools::wallet2::parse_short_payment_id(payment_id, payment_id_short);
r = tools::wallet2::parse_short_payment_id(payment_id, info.payment_id);
if (r) {
std::string extra_nonce;
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id_short);
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
}
}
@ -978,13 +1022,13 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
break;
}
}
else if (has_payment_id) {
else if (info.has_payment_id) {
std::string extra_nonce;
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id_short);
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
if (!r) {
m_status = Status_Error;
m_errorString = tr("Failed to add short payment id: ") + epee::string_tools::pod_to_hex(payment_id_short);
m_errorString = tr("Failed to add short payment id: ") + epee::string_tools::pod_to_hex(info.payment_id);
break;
}
}
@ -996,16 +1040,23 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
if (amount) {
vector<cryptonote::tx_destination_entry> dsts;
cryptonote::tx_destination_entry de;
de.addr = addr;
de.addr = info.address;
de.amount = *amount;
de.is_subaddress = info.is_subaddress;
dsts.push_back(de);
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */,
static_cast<uint32_t>(priority),
extra, m_trustedDaemon);
extra, subaddr_account, subaddr_indices, m_trustedDaemon);
} else {
transaction->m_pending_tx = m_wallet->create_transactions_all(0, addr, fake_outs_count, 0 /* unlock_time */,
// for the GUI, sweep_all (i.e. amount set as "(all)") will always sweep all the funds in all the addresses
if (subaddr_indices.empty())
{
for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(subaddr_account); ++index)
subaddr_indices.insert(index);
}
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */,
static_cast<uint32_t>(priority),
extra, m_trustedDaemon);
extra, subaddr_account, subaddr_indices, m_trustedDaemon);
}
} catch (const tools::error::daemon_busy&) {
@ -1186,16 +1237,26 @@ void WalletImpl::disposeTransaction(PendingTransaction *t)
delete t;
}
TransactionHistory *WalletImpl::history() const
TransactionHistory *WalletImpl::history()
{
return m_history;
}
AddressBook *WalletImpl::addressBook() const
AddressBook *WalletImpl::addressBook()
{
return m_addressBook;
}
Subaddress *WalletImpl::subaddress()
{
return m_subaddress;
}
SubaddressAccount *WalletImpl::subaddressAccount()
{
return m_subaddressAccount;
}
void WalletImpl::setListener(WalletListener *l)
{
// TODO thread synchronization;
@ -1243,7 +1304,8 @@ std::string WalletImpl::getTxKey(const std::string &txid) const
const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
crypto::secret_key tx_key;
if (m_wallet->get_tx_key(htxid, tx_key))
std::vector<crypto::secret_key> additional_tx_keys;
if (m_wallet->get_tx_key(htxid, tx_key, additional_tx_keys))
{
return epee::string_tools::pod_to_hex(tx_key);
}
@ -1260,14 +1322,12 @@ std::string WalletImpl::signMessage(const std::string &message)
bool WalletImpl::verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const
{
cryptonote::account_public_address addr;
bool has_payment_id;
crypto::hash8 payment_id;
cryptonote::address_parse_info info;
if (!cryptonote::get_account_integrated_address_from_str(addr, has_payment_id, payment_id, m_wallet->testnet(), address))
if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address))
return false;
return m_wallet->verify(message, addr, signature);
return m_wallet->verify(message, info.address, signature);
}
bool WalletImpl::connectToDaemon()

@ -45,6 +45,8 @@ class TransactionHistoryImpl;
class PendingTransactionImpl;
class UnsignedTransactionImpl;
class AddressBookImpl;
class SubaddressImpl;
class SubaddressAccountImpl;
struct Wallet2CallbackImpl;
class WalletImpl : public Wallet
@ -71,7 +73,7 @@ public:
int status() const;
std::string errorString() const;
bool setPassword(const std::string &password);
std::string address() const;
std::string address(uint32_t accountIndex, uint32_t addressIndex) const;
std::string integratedAddress(const std::string &payment_id) const;
std::string secretViewKey() const;
std::string publicViewKey() const;
@ -86,8 +88,8 @@ public:
ConnectionStatus connected() const;
void setTrustedDaemon(bool arg);
bool trustedDaemon() const;
uint64_t balance() const;
uint64_t unlockedBalance() const;
uint64_t balance(uint32_t accountIndex) const;
uint64_t unlockedBalance(uint32_t accountIndex) const;
uint64_t blockChainHeight() const;
uint64_t approximateBlockChainHeight() const;
uint64_t daemonBlockChainHeight() const;
@ -106,8 +108,17 @@ public:
void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const;
bool useForkRules(uint8_t version, int64_t early_blocks) const;
void addSubaddressAccount(const std::string& label);
size_t numSubaddressAccounts() const;
size_t numSubaddresses(uint32_t accountIndex) const;
void addSubaddress(uint32_t accountIndex, const std::string& label);
std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const;
void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label);
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count,
uint32_t subaddr_account,
std::set<uint32_t> subaddr_indices,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low);
virtual PendingTransaction * createSweepUnmixableTransaction();
bool submitTransaction(const std::string &fileName);
@ -116,8 +127,10 @@ public:
bool importKeyImages(const std::string &filename);
virtual void disposeTransaction(PendingTransaction * t);
virtual TransactionHistory * history() const;
virtual AddressBook * addressBook() const;
virtual TransactionHistory * history();
virtual AddressBook * addressBook();
virtual Subaddress * subaddress();
virtual SubaddressAccount * subaddressAccount();
virtual void setListener(WalletListener * l);
virtual uint32_t defaultMixin() const;
virtual void setDefaultMixin(uint32_t arg);
@ -146,6 +159,8 @@ private:
friend class TransactionHistoryImpl;
friend struct Wallet2CallbackImpl;
friend class AddressBookImpl;
friend class SubaddressImpl;
friend class SubaddressAccountImpl;
tools::wallet2 * m_wallet;
mutable std::atomic<int> m_status;
@ -155,6 +170,8 @@ private:
bool m_trustedDaemon;
Wallet2CallbackImpl * m_wallet2Callback;
AddressBookImpl * m_addressBook;
SubaddressImpl * m_subaddress;
SubaddressAccountImpl * m_subaddressAccount;
// multi-threaded refresh stuff
std::atomic<bool> m_refreshEnabled;

@ -215,10 +215,8 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std:
tx_key = *reinterpret_cast<const crypto::secret_key*>(tx_key_data.data());
bool testnet = address_text[0] != '4';
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id;
if(!cryptonote::get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_text))
cryptonote::address_parse_info info;
if(!cryptonote::get_account_address_from_str(info, testnet, address_text))
{
error = tr("failed to parse address");
return false;
@ -258,7 +256,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std:
}
crypto::key_derivation derivation;
if (!crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation))
if (!crypto::generate_key_derivation(info.address.m_view_public_key, tx_key, derivation))
{
error = tr("failed to generate key derivation from supplied parameters");
return false;
@ -272,7 +270,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std:
continue;
const cryptonote::txout_to_key tx_out_to_key = boost::get<cryptonote::txout_to_key>(tx.vout[n].target);
crypto::public_key pubkey;
derive_public_key(derivation, n, address.m_spend_public_key, pubkey);
derive_public_key(derivation, n, info.address.m_spend_public_key, pubkey);
if (pubkey == tx_out_to_key.key)
{
uint64_t amount;
@ -287,7 +285,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std:
rct::key Ctmp;
//rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
crypto::key_derivation derivation;
bool r = crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation);
bool r = crypto::generate_key_derivation(info.address.m_view_public_key, tx_key, derivation);
if (!r)
{
LOG_ERROR("Failed to generate key derivation to decode rct output " << n);
@ -322,11 +320,11 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std:
if (received > 0)
{
LOG_PRINT_L1(get_account_address_as_str(testnet, address) << " " << tr("received") << " " << cryptonote::print_money(received) << " " << tr("in txid") << " " << txid);
LOG_PRINT_L1(get_account_address_as_str(testnet, info.is_subaddress, info.address) << " " << tr("received") << " " << cryptonote::print_money(received) << " " << tr("in txid") << " " << txid);
}
else
{
LOG_PRINT_L1(get_account_address_as_str(testnet, address) << " " << tr("received nothing in txid") << " " << txid);
LOG_PRINT_L1(get_account_address_as_str(testnet, info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid);
}
if (res.txs.front().in_pool)
{

File diff suppressed because it is too large Load Diff

@ -72,9 +72,9 @@ namespace tools
{
public:
virtual void on_new_block(uint64_t height, const cryptonote::block& block) {}
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) {}
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) {}
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {}
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
virtual ~i_wallet2_callback() {}
};
@ -175,9 +175,9 @@ namespace tools
uint64_t amount;
uint64_t money_transfered;
bool error;
bool received;
boost::optional<cryptonote::subaddress_receive_info> received;
tx_scan_info_t(): money_transfered(0), error(true), received(false) {}
tx_scan_info_t(): money_transfered(0), error(true) {}
};
struct transfer_details
@ -195,6 +195,7 @@ namespace tools
bool m_rct;
bool m_key_image_known;
size_t m_pk_index;
cryptonote::subaddress_index m_subaddr_index;
bool is_rct() const { return m_rct; }
uint64_t amount() const { return m_amount; }
@ -214,6 +215,7 @@ namespace tools
FIELD(m_rct)
FIELD(m_key_image_known)
FIELD(m_pk_index)
FIELD(m_subaddr_index)
END_SERIALIZE()
};
@ -224,6 +226,7 @@ namespace tools
uint64_t m_block_height;
uint64_t m_unlock_time;
uint64_t m_timestamp;
cryptonote::subaddress_index m_subaddr_index;
};
struct unconfirmed_transfer_details
@ -237,6 +240,8 @@ namespace tools
crypto::hash m_payment_id;
enum { pending, pending_not_in_pool, failed } m_state;
uint64_t m_timestamp;
uint32_t m_subaddr_account; // subaddress account of your wallet to be used in this transfer
std::set<uint32_t> m_subaddr_indices; // set of address indices used as inputs in this transfer
};
struct confirmed_transfer_details
@ -249,10 +254,12 @@ namespace tools
crypto::hash m_payment_id;
uint64_t m_timestamp;
uint64_t m_unlock_time;
uint32_t m_subaddr_account; // subaddress account of your wallet to be used in this transfer
std::set<uint32_t> m_subaddr_indices; // set of address indices used as inputs in this transfer
confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(crypto::null_hash), m_timestamp(0), m_unlock_time(0) {}
confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(crypto::null_hash), m_timestamp(0), m_unlock_time(0), m_subaddr_account((uint32_t)-1) {}
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height):
m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time) {}
m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time), m_subaddr_account(utd.m_subaddr_account), m_subaddr_indices(utd.m_subaddr_indices) {}
};
struct tx_construction_data
@ -265,6 +272,8 @@ namespace tools
uint64_t unlock_time;
bool use_rct;
std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change
uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer
std::set<uint32_t> subaddr_indices; // set of address indices used as inputs in this transfer
};
typedef std::vector<transfer_details> transfer_container;
@ -282,6 +291,7 @@ namespace tools
std::list<size_t> selected_transfers;
std::string key_images;
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
std::vector<cryptonote::tx_destination_entry> dests;
tx_construction_data construction_data;
@ -329,6 +339,7 @@ namespace tools
cryptonote::account_public_address m_address;
crypto::hash m_payment_id;
std::string m_description;
bool m_is_subaddress;
};
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
@ -419,6 +430,21 @@ namespace tools
* \brief Sets the seed language
*/
void set_seed_language(const std::string &language);
// Subaddress scheme
cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index& index) const;
cryptonote::account_public_address get_address() const { return get_subaddress({0,0}); }
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const;
std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); }
std::string get_integrated_address_as_str(const crypto::hash8& payment_id) const;
void add_subaddress_account(const std::string& label);
size_t get_num_subaddress_accounts() const { return m_subaddress_labels.size(); }
size_t get_num_subaddresses(uint32_t index_major) const { return index_major < m_subaddress_labels.size() ? m_subaddress_labels[index_major].size() : 0; }
void add_subaddress(uint32_t index_major, const std::string& label); // throws when index is out of bound
void expand_subaddresses(const cryptonote::subaddress_index& index);
std::string get_subaddress_label(const cryptonote::subaddress_index& index) const; // throws when index is out of bound
void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label); // throws when index is out of bound
/*!
* \brief Tells if the wallet file is deprecated.
*/
@ -435,9 +461,15 @@ namespace tools
bool restricted() const { return m_restricted; }
bool watch_only() const { return m_watch_only; }
uint64_t balance() const;
uint64_t unlocked_balance() const;
uint64_t unlocked_dust_balance(const tx_dust_policy &dust_policy) const;
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
uint64_t unlocked_balance(uint32_t subaddr_index_major) const;
// locked & unlocked balance per subaddress of given or current subaddress account
std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major) const;
std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress(uint32_t subaddr_index_major) const;
// all locked & unlocked balances of all subaddress accounts
uint64_t balance_all() const;
uint64_t unlocked_balance_all() const;
template<typename T>
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, bool trusted_daemon);
template<typename T>
@ -445,10 +477,10 @@ namespace tools
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, bool trusted_daemon);
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon);
template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count,
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t> selected_transfers, size_t fake_outputs_count,
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
@ -462,19 +494,19 @@ namespace tools
// load unsigned_tx_set from file.
bool load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs);
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1) const;
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
uint64_t min_height, uint64_t max_height = (uint64_t)-1) const;
void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments) const;
void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments) const;
uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
void rescan_spent();
@ -555,6 +587,12 @@ namespace tools
return;
a & m_scanned_pool_txs[0];
a & m_scanned_pool_txs[1];
if (ver < 20)
return;
a & m_subaddresses;
a & m_subaddresses_inv;
a & m_subaddress_labels;
a & m_additional_tx_keys;
}
/*!
@ -603,13 +641,13 @@ namespace tools
void set_confirm_backlog_threshold(uint32_t threshold) { m_confirm_backlog_threshold = threshold; };
uint32_t get_confirm_backlog_threshold() const { return m_confirm_backlog_threshold; };
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const;
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
/*!
* \brief GUI Address book get/store
*/
std::vector<address_book_row> get_address_book() const { return m_address_book; }
bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description);
bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress);
bool delete_address_book_row(std::size_t row_id);
uint64_t get_num_rct_outputs();
@ -651,7 +689,7 @@ namespace tools
void import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments);
std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const;
void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc);
bool export_key_images(const std::string filename);
bool export_key_images(const std::string &filename);
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const;
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
@ -704,27 +742,27 @@ namespace tools
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<size_t>& selected_transfers, bool trusted_daemon);
bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received);
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount);
void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
void generate_genesis(cryptonote::block& b);
void check_genesis(const crypto::hash& genesis_hash) const; //throws
bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
crypto::hash8 get_short_payment_id(const pending_tx &ptx) const;
void check_acc_out_precomp(const crypto::public_key &spend_public_key, const cryptonote::tx_out &o, const crypto::key_derivation &derivation, size_t i, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_transaction_size_limit();
std::vector<uint64_t> get_unspent_amounts_vector();
uint64_t get_dynamic_per_kb_fee_estimate();
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
void set_spent(size_t idx, uint64_t height);
void set_unspent(size_t idx);
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
void scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, uint64_t &tx_money_got_in_outs, std::vector<size_t> &outs);
void scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs);
void trim_hashchain();
cryptonote::account_base m_account;
@ -740,12 +778,16 @@ namespace tools
std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
cryptonote::checkpoints m_checkpoints;
std::unordered_map<crypto::hash, std::vector<crypto::secret_key>> m_additional_tx_keys;
transfer_container m_transfers;
payment_container m_payments;
std::unordered_map<crypto::key_image, size_t> m_key_images;
std::unordered_map<crypto::public_key, size_t> m_pub_keys;
cryptonote::account_public_address m_account_public_address;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> m_subaddresses;
std::unordered_map<cryptonote::subaddress_index, crypto::public_key> m_subaddresses_inv;
std::vector<std::vector<std::string>> m_subaddress_labels;
std::unordered_map<crypto::hash, std::string> m_tx_notes;
std::vector<tools::wallet2::address_book_row> m_address_book;
uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
@ -780,16 +822,16 @@ namespace tools
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
};
}
BOOST_CLASS_VERSION(tools::wallet2, 19)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 7)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 4)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 16)
BOOST_CLASS_VERSION(tools::wallet2, 20)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 8)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 7)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 5)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 0)
BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 0)
BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 1)
BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 1)
namespace boost
{
@ -823,6 +865,10 @@ namespace boost
{
x.m_pk_index = 0;
}
if (ver < 8)
{
x.m_subaddr_index = {};
}
}
template <class Archive>
@ -890,6 +936,12 @@ namespace boost
return;
}
a & x.m_pk_index;
if (ver < 8)
{
initialize_transfer_details(a, x, ver);
return;
}
a & x.m_subaddr_index;
}
template <class Archive>
@ -929,6 +981,10 @@ namespace boost
if (!typename Archive::is_saving() && x.m_change != (uint64_t)-1)
x.m_amount_out += x.m_change;
}
if (ver < 7)
return;
a & x.m_subaddr_account;
a & x.m_subaddr_indices;
}
template <class Archive>
@ -966,6 +1022,10 @@ namespace boost
return;
}
a & x.m_unlock_time;
if (ver < 5)
return;
a & x.m_subaddr_account;
a & x.m_subaddr_indices;
}
template <class Archive>
@ -978,13 +1038,9 @@ namespace boost
if (ver < 1)
return;
a & x.m_timestamp;
}
template <class Archive>
inline void serialize(Archive& a, cryptonote::tx_destination_entry& x, const boost::serialization::version_type ver)
{
a & x.amount;
a & x.addr;
if (ver < 2)
return;
a & x.m_subaddr_index;
}
template <class Archive>
@ -993,6 +1049,9 @@ namespace boost
a & x.m_address;
a & x.m_payment_id;
a & x.m_description;
if (ver < 17)
return;
a & x.m_is_subaddress;
}
template <class Archive>
@ -1020,6 +1079,10 @@ namespace boost
a & x.unlock_time;
a & x.use_rct;
a & x.dests;
if (ver < 1)
return;
a & x.subaddr_account;
a & x.subaddr_indices;
}
template <class Archive>
@ -1035,6 +1098,9 @@ namespace boost
a & x.tx_key;
a & x.dests;
a & x.construction_data;
if (ver < 1)
return;
a & x.additional_tx_keys;
}
}
}
@ -1055,18 +1121,18 @@ namespace tools
for(auto& de: dsts)
{
cryptonote::decompose_amount_into_digits(de.amount, 0,
[&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, de.addr)); },
[&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, de.addr)); } );
[&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, de.addr, de.is_subaddress)); },
[&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, de.addr, de.is_subaddress)); } );
}
cryptonote::decompose_amount_into_digits(change_dst.amount, 0,
[&](uint64_t chunk) {
if (chunk <= dust_threshold)
dust_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr));
dust_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr, false));
else
splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr));
splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr, false));
},
[&](uint64_t a_dust) { dust_dsts.push_back(cryptonote::tx_destination_entry(a_dust, change_dst.addr)); } );
[&](uint64_t a_dust) { dust_dsts.push_back(cryptonote::tx_destination_entry(a_dust, change_dst.addr, false)); } );
}
//----------------------------------------------------------------------------------------------------
inline void null_split_strategy(const std::vector<cryptonote::tx_destination_entry>& dsts,
@ -1080,7 +1146,7 @@ namespace tools
if (0 != change)
{
splitted_dsts.push_back(cryptonote::tx_destination_entry(change, change_dst.addr));
splitted_dsts.push_back(cryptonote::tx_destination_entry(change, change_dst.addr, false));
}
}
//----------------------------------------------------------------------------------------------------
@ -1103,7 +1169,7 @@ namespace tools
}
template<typename T>
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices,
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, bool trusted_daemon)
{
using namespace cryptonote;
@ -1128,6 +1194,10 @@ namespace tools
uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers, trusted_daemon);
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee);
uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
THROW_WALLET_EXCEPTION_IF(subaddr_account != *i, error::wallet_internal_error, "the tx uses funds from multiple accounts");
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
@ -1215,7 +1285,7 @@ namespace tools
cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
if (needed_money < found_money)
{
change_dts.addr = m_account.get_keys().m_account_address;
change_dts.addr = get_subaddress({subaddr_account, 0});
change_dts.amount = found_money - needed_money;
}
@ -1228,12 +1298,13 @@ namespace tools
}
for(auto& d: dust_dsts) {
if (!dust_policy.add_to_fee)
splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust));
splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust, d.is_subaddress));
dust += d.amount;
}
crypto::secret_key tx_key;
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time, tx_key);
std::vector<crypto::secret_key> additional_tx_keys;
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
@ -1259,6 +1330,7 @@ namespace tools
ptx.change_dts = change_dts;
ptx.selected_transfers = selected_transfers;
ptx.tx_key = tx_key;
ptx.additional_tx_keys = additional_tx_keys;
ptx.dests = dsts;
ptx.construction_data.sources = sources;
ptx.construction_data.change_dts = change_dts;
@ -1268,6 +1340,11 @@ namespace tools
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
ptx.construction_data.subaddr_indices.clear();
for (size_t idx: selected_transfers)
ptx.construction_data.subaddr_indices.insert(m_transfers[idx].m_subaddr_index.minor);
}

@ -33,6 +33,7 @@
#include <string>
#include <vector>
#include <set>
#include <ctime>
#include <iostream>
@ -88,6 +89,8 @@ struct PendingTransaction
* \return
*/
virtual uint64_t txCount() const = 0;
virtual std::vector<uint32_t> subaddrAccount() const = 0;
virtual std::vector<std::set<uint32_t>> subaddrIndices() const = 0;
};
/**
@ -155,6 +158,9 @@ struct TransactionInfo
virtual uint64_t amount() const = 0;
virtual uint64_t fee() const = 0;
virtual uint64_t blockHeight() const = 0;
virtual std::set<uint32_t> subaddrIndex() const = 0;
virtual uint32_t subaddrAccount() const = 0;
virtual std::string label() const = 0;
virtual uint64_t confirmations() const = 0;
virtual uint64_t unlockTime() const = 0;
//! transaction_id
@ -223,6 +229,66 @@ struct AddressBook
virtual int lookupPaymentID(const std::string &payment_id) const = 0;
};
struct SubaddressRow {
public:
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label):
m_rowId(_rowId),
m_address(_address),
m_label(_label) {}
private:
std::size_t m_rowId;
std::string m_address;
std::string m_label;
public:
std::string extra;
std::string getAddress() const {return m_address;}
std::string getLabel() const {return m_label;}
std::size_t getRowId() const {return m_rowId;}
};
struct Subaddress
{
virtual ~Subaddress() = 0;
virtual std::vector<SubaddressRow*> getAll() const = 0;
virtual void addRow(uint32_t accountIndex, const std::string &label) = 0;
virtual void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0;
virtual void refresh(uint32_t accountIndex) = 0;
};
struct SubaddressAccountRow {
public:
SubaddressAccountRow(std::size_t _rowId, const std::string &_address, const std::string &_label, const std::string &_balance, const std::string &_unlockedBalance):
m_rowId(_rowId),
m_address(_address),
m_label(_label),
m_balance(_balance),
m_unlockedBalance(_unlockedBalance) {}
private:
std::size_t m_rowId;
std::string m_address;
std::string m_label;
std::string m_balance;
std::string m_unlockedBalance;
public:
std::string extra;
std::string getAddress() const {return m_address;}
std::string getLabel() const {return m_label;}
std::string getBalance() const {return m_balance;}
std::string getUnlockedBalance() const {return m_unlockedBalance;}
std::size_t getRowId() const {return m_rowId;}
};
struct SubaddressAccount
{
virtual ~SubaddressAccount() = 0;
virtual std::vector<SubaddressAccountRow*> getAll() const = 0;
virtual void addRow(const std::string &label) = 0;
virtual void setLabel(uint32_t accountIndex, const std::string &label) = 0;
virtual void refresh() = 0;
};
struct WalletListener
{
virtual ~WalletListener() = 0;
@ -294,7 +360,8 @@ struct Wallet
//! in case error status, returns error string
virtual std::string errorString() const = 0;
virtual bool setPassword(const std::string &password) = 0;
virtual std::string address() const = 0;
virtual std::string address(uint32_t accountIndex, uint32_t addressIndex) const = 0;
std::string mainAddress() const { return address(0, 0); }
virtual std::string path() const = 0;
virtual bool testnet() const = 0;
//! returns current hard fork info
@ -406,8 +473,20 @@ struct Wallet
virtual ConnectionStatus connected() const = 0;
virtual void setTrustedDaemon(bool arg) = 0;
virtual bool trustedDaemon() const = 0;
virtual uint64_t balance() const = 0;
virtual uint64_t unlockedBalance() const = 0;
virtual uint64_t balance(uint32_t accountIndex) const = 0;
uint64_t balanceAll() const {
uint64_t result = 0;
for (uint32_t i = 0; i < numSubaddressAccounts(); ++i)
result += balance(i);
return result;
}
virtual uint64_t unlockedBalance(uint32_t accountIndex) const = 0;
uint64_t unlockedBalanceAll() const {
uint64_t result = 0;
for (uint32_t i = 0; i < numSubaddressAccounts(); ++i)
result += unlockedBalance(i);
return result;
}
/**
* @brief watchOnly - checks if wallet is watch only
@ -492,6 +571,39 @@ struct Wallet
*/
virtual int autoRefreshInterval() const = 0;
/**
* @brief addSubaddressAccount - appends a new subaddress account at the end of the last major index of existing subaddress accounts
* @param label - the label for the new account (which is the as the label of the primary address (accountIndex,0))
*/
virtual void addSubaddressAccount(const std::string& label) = 0;
/**
* @brief numSubaddressAccounts - returns the number of existing subaddress accounts
*/
virtual size_t numSubaddressAccounts() const = 0;
/**
* @brief numSubaddresses - returns the number of existing subaddresses associated with the specified subaddress account
* @param accountIndex - the major index specifying the subaddress account
*/
virtual size_t numSubaddresses(uint32_t accountIndex) const = 0;
/**
* @brief addSubaddress - appends a new subaddress at the end of the last minor index of the specified subaddress account
* @param accountIndex - the major index specifying the subaddress account
* @param label - the label for the new subaddress
*/
virtual void addSubaddress(uint32_t accountIndex, const std::string& label) = 0;
/**
* @brief getSubaddressLabel - gets the label of the specified subaddress
* @param accountIndex - the major index specifying the subaddress account
* @param addressIndex - the minor index specifying the subaddress
*/
virtual std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const = 0;
/**
* @brief setSubaddressLabel - sets the label of the specified subaddress
* @param accountIndex - the major index specifying the subaddress account
* @param addressIndex - the minor index specifying the subaddress
* @param label - the new label for the specified subaddress
*/
virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0;
/*!
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
@ -499,6 +611,8 @@ struct Wallet
* \param payment_id optional payment_id, can be empty string
* \param amount amount
* \param mixin_count mixin count. if 0 passed, wallet will use default value
* \param subaddr_account subaddress account from which the input funds are taken
* \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices
* \param priority
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
* after object returned
@ -506,6 +620,8 @@ struct Wallet
virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count,
uint32_t subaddr_account,
std::set<uint32_t> subaddr_indices,
PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0;
/*!
@ -551,8 +667,10 @@ struct Wallet
virtual bool importKeyImages(const std::string &filename) = 0;
virtual TransactionHistory * history() const = 0;
virtual AddressBook * addressBook() const = 0;
virtual TransactionHistory * history() = 0;
virtual AddressBook * addressBook() = 0;
virtual Subaddress * subaddress() = 0;
virtual SubaddressAccount * subaddressAccount() = 0;
virtual void setListener(WalletListener *) = 0;
/*!
* \brief defaultMixin - returns number of mixins used in transactions

@ -492,7 +492,7 @@ namespace tools
for (size_t i = 0; i < m_destinations.size(); ++i)
{
const cryptonote::tx_destination_entry& dst = m_destinations[i];
ss << "\n " << i << ": " << cryptonote::get_account_address_as_str(m_testnet, dst.addr) << " " <<
ss << "\n " << i << ": " << cryptonote::get_account_address_as_str(m_testnet, dst.is_subaddress, dst.addr) << " " <<
cryptonote::print_money(dst.amount);
}
@ -567,7 +567,7 @@ namespace tools
", destinations:";
for (const auto& dst : m_destinations)
{
ss << '\n' << cryptonote::print_money(dst.amount) << " -> " << cryptonote::get_account_address_as_str(m_testnet, dst.addr);
ss << '\n' << cryptonote::print_money(dst.amount) << " -> " << cryptonote::get_account_address_as_str(m_testnet, dst.is_subaddress, dst.addr);
}
return ss.str();
}

@ -253,6 +253,7 @@ namespace tools
entry.fee = 0; // TODO
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "in";
entry.subaddr_index = pd.m_subaddr_index;
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd)
@ -273,10 +274,11 @@ namespace tools
entry.destinations.push_back(wallet_rpc::transfer_destination());
wallet_rpc::transfer_destination &td = entry.destinations.back();
td.amount = d.amount;
td.address = get_account_address_as_str(m_wallet->testnet(), d.addr);
td.address = get_account_address_as_str(m_wallet->testnet(), d.is_subaddress, d.addr);
}
entry.type = "out";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd)
@ -294,6 +296,7 @@ namespace tools
entry.unlock_time = pd.m_tx.unlock_time;
entry.note = m_wallet->get_tx_note(txid);
entry.type = is_failed ? "failed" : "pending";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
@ -309,6 +312,7 @@ namespace tools
entry.fee = 0; // TODO
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "pool";
entry.subaddr_index = pd.m_subaddr_index;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er)
@ -316,8 +320,24 @@ namespace tools
if (!m_wallet) return not_open(er);
try
{
res.balance = m_wallet->balance();
res.unlocked_balance = m_wallet->unlocked_balance();
res.balance = m_wallet->balance(req.account_index);
res.unlocked_balance = m_wallet->unlocked_balance(req.account_index);
std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(req.account_index);
std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(req.account_index);
std::vector<tools::wallet2::transfer_details> transfers;
m_wallet->get_transfers(transfers);
for (const auto& i : balance_per_subaddress)
{
wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info;
info.address_index = i.first;
cryptonote::subaddress_index index = {req.account_index, info.address_index};
info.address = m_wallet->get_subaddress_as_str(index);
info.balance = i.second;
info.unlocked_balance = unlocked_balance_per_subaddress[i.first];
info.label = m_wallet->get_subaddress_label(index);
info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; });
res.per_subaddress.push_back(info);
}
}
catch (const std::exception& e)
{
@ -333,7 +353,126 @@ namespace tools
if (!m_wallet) return not_open(er);
try
{
res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
res.addresses.resize(m_wallet->get_num_subaddresses(req.account_index));
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
cryptonote::subaddress_index index = {req.account_index, 0};
for (; index.minor < m_wallet->get_num_subaddresses(req.account_index); ++index.minor)
{
auto& info = res.addresses[index.minor];
info.address = m_wallet->get_subaddress_as_str(index);
info.label = m_wallet->get_subaddress_label(index);
info.address_index = index.minor;
info.used = std::find_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return td.m_subaddr_index == index; }) != transfers.end();
}
res.address = m_wallet->get_subaddress_as_str({req.account_index, 0});
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
m_wallet->add_subaddress(req.account_index, req.label);
res.address_index = m_wallet->get_num_subaddresses(req.account_index) - 1;
res.address = m_wallet->get_subaddress_as_str({req.account_index, res.address_index});
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (req.index.major >= m_wallet->get_num_subaddress_accounts())
{
er.code = WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND;
er.message = "Account index is out of bound";
return false;
}
if (req.index.minor >= m_wallet->get_num_subaddresses(req.index.major))
{
er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUTOFBOUND;
er.message = "Address index is out of bound";
return false;
}
try
{
m_wallet->set_subaddress_label(req.index, req.label);
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
try
{
res.total_balance = 0;
res.total_unlocked_balance = 0;
cryptonote::subaddress_index subaddr_index = {0,0};
for (; subaddr_index.major < m_wallet->get_num_subaddress_accounts(); ++subaddr_index.major)
{
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
info.account_index = subaddr_index.major;
info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
info.balance = m_wallet->balance(subaddr_index.major);
info.unlocked_balance = m_wallet->unlocked_balance(subaddr_index.major);
info.label = m_wallet->get_subaddress_label(subaddr_index);
res.subaddress_accounts.push_back(info);
res.total_balance += info.balance;
res.total_unlocked_balance += info.unlocked_balance;
}
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
try
{
m_wallet->add_subaddress_account(req.label);
res.account_index = m_wallet->get_num_subaddress_accounts() - 1;
res.address = m_wallet->get_subaddress_as_str({res.account_index, 0});
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (req.account_index >= m_wallet->get_num_subaddress_accounts())
{
er.code = WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND;
er.message = "Account index is out of bound";
return false;
}
try
{
m_wallet->set_subaddress_label({req.account_index, 0}, req.label);
}
catch (const std::exception& e)
{
@ -360,17 +499,16 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er)
bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er)
{
crypto::hash8 integrated_payment_id = crypto::null_hash8;
std::string extra_nonce;
for (auto it = destinations.begin(); it != destinations.end(); it++)
{
cryptonote::address_parse_info info;
cryptonote::tx_destination_entry de;
bool has_payment_id;
crypto::hash8 new_payment_id;
er.message = "";
if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address,
if(!get_account_address_from_str_or_url(info, m_wallet->testnet(), it->address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid)
{
@ -390,10 +528,13 @@ namespace tools
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
return false;
}
de.addr = info.address;
de.is_subaddress = info.is_subaddress;
de.amount = it->amount;
dsts.push_back(de);
if (has_payment_id)
if (info.has_payment_id)
{
if (!payment_id.empty() || integrated_payment_id != crypto::null_hash8)
{
@ -401,7 +542,7 @@ namespace tools
er.message = "A single payment id is allowed per transaction";
return false;
}
integrated_payment_id = new_payment_id;
integrated_payment_id = info.payment_id;
cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, integrated_payment_id);
/* Append Payment ID data into extra */
@ -472,7 +613,7 @@ namespace tools
try
{
uint64_t mixin = adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
// reject proposed transactions if there are more than one. see on_transfer_split below.
if (ptx_vector.size() != 1)
@ -548,7 +689,7 @@ namespace tools
uint64_t ptx_amount;
std::vector<wallet2::pending_tx> ptx_vector;
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
if (!req.do_not_relay)
@ -688,7 +829,7 @@ namespace tools
try
{
uint64_t mixin = adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
if (!req.do_not_relay)
m_wallet->commit_tx(ptx_vector);
@ -752,7 +893,7 @@ namespace tools
}
}
res.integrated_address = m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet());
res.integrated_address = m_wallet->get_integrated_address_as_str(payment_id);
res.payment_id = epee::string_tools::pod_to_hex(payment_id);
return true;
}
@ -770,24 +911,22 @@ namespace tools
if (!m_wallet) return not_open(er);
try
{
cryptonote::account_public_address address;
crypto::hash8 payment_id;
bool has_payment_id;
cryptonote::address_parse_info info;
if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), req.integrated_address))
if(!get_account_address_from_str(info, m_wallet->testnet(), req.integrated_address))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Invalid address";
return false;
}
if(!has_payment_id)
if(!info.has_payment_id)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Address is not an integrated address";
return false;
}
res.standard_address = get_account_address_as_str(m_wallet->testnet(),address);
res.payment_id = epee::string_tools::pod_to_hex(payment_id);
res.standard_address = get_account_address_as_str(m_wallet->testnet(), info.is_subaddress, info.address);
res.payment_id = epee::string_tools::pod_to_hex(info.payment_id);
return true;
}
catch (const std::exception &e)
@ -863,6 +1002,7 @@ namespace tools
rpc_payment.amount = payment.m_amount;
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
res.payments.push_back(rpc_payment);
}
@ -888,6 +1028,7 @@ namespace tools
rpc_payment.amount = payment.second.m_amount;
rpc_payment.block_height = payment.second.m_block_height;
rpc_payment.unlock_time = payment.second.m_unlock_time;
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
res.payments.push_back(std::move(rpc_payment));
}
@ -937,6 +1078,7 @@ namespace tools
rpc_payment.amount = payment.m_amount;
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
res.payments.push_back(std::move(rpc_payment));
}
}
@ -975,6 +1117,8 @@ namespace tools
{
if (!filter || available != td.m_spent)
{
if (req.account_index != td.m_subaddr_index.major || (!req.subaddr_indices.empty() && req.subaddr_indices.count(td.m_subaddr_index.minor) == 0))
continue;
if (!transfers_found)
{
transfers_found = true;
@ -986,6 +1130,7 @@ namespace tools
rpc_transfers.global_index = td.m_global_output_index;
rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.tx_size = txBlob.size();
rpc_transfers.subaddr_index = td.m_subaddr_index.minor;
res.transfers.push_back(rpc_transfers);
}
}
@ -1071,11 +1216,9 @@ namespace tools
return false;
}
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id;
cryptonote::address_parse_info info;
er.message = "";
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address,
if(!get_account_address_from_str_or_url(info, m_wallet->testnet(), req.address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid)
{
@ -1094,7 +1237,7 @@ namespace tools
return false;
}
res.good = m_wallet->verify(req.data, address, req.signature);
res.good = m_wallet->verify(req.data, info.address, req.signature);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@ -1214,7 +1357,7 @@ namespace tools
if (req.in)
{
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet->get_payments(payments, min_height, max_height);
m_wallet->get_payments(payments, min_height, max_height, req.account_index, req.subaddr_indices);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.in.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second);
@ -1224,7 +1367,7 @@ namespace tools
if (req.out)
{
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
m_wallet->get_payments_out(payments, min_height, max_height);
m_wallet->get_payments_out(payments, min_height, max_height, req.account_index, req.subaddr_indices);
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.out.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.out.back(), i->first, i->second);
@ -1233,7 +1376,7 @@ namespace tools
if (req.pending || req.failed) {
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
m_wallet->get_unconfirmed_payments_out(upayments);
m_wallet->get_unconfirmed_payments_out(upayments, req.account_index, req.subaddr_indices);
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
@ -1250,7 +1393,7 @@ namespace tools
m_wallet->update_pool_state();
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments);
m_wallet->get_unconfirmed_payments(payments, req.account_index, req.subaddr_indices);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.pool.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.pool.back(), i->first, i->second);
@ -1453,7 +1596,7 @@ namespace tools
{
uint64_t idx = 0;
for (const auto &entry: ab)
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->testnet(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
}
else
{
@ -1466,7 +1609,7 @@ namespace tools
return false;
}
const auto &entry = ab[idx];
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->testnet(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
}
}
return true;
@ -1482,12 +1625,10 @@ namespace tools
return false;
}
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id8;
cryptonote::address_parse_info info;
crypto::hash payment_id = crypto::null_hash;
er.message = "";
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address,
if(!get_account_address_from_str_or_url(info, m_wallet->testnet(), req.address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid)
{
@ -1507,14 +1648,14 @@ namespace tools
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
return false;
}
if (has_payment_id)
if (info.has_payment_id)
{
memcpy(payment_id.data, payment_id8.data, 8);
memcpy(payment_id.data, info.payment_id.data, 8);
memset(payment_id.data + 8, 0, 24);
}
if (!req.payment_id.empty())
{
if (has_payment_id)
if (info.has_payment_id)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Separate payment ID given with integrated address";
@ -1526,7 +1667,7 @@ namespace tools
if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
{
if (!wallet2::parse_short_payment_id(req.payment_id, payment_id8))
if (!wallet2::parse_short_payment_id(req.payment_id, info.payment_id))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 16 or 64 character string";
@ -1534,12 +1675,12 @@ namespace tools
}
else
{
memcpy(payment_id.data, payment_id8.data, 8);
memcpy(payment_id.data, info.payment_id.data, 8);
memset(payment_id.data + 8, 0, 24);
}
}
}
if (!m_wallet->add_address_book_row(address, payment_id, req.description))
if (!m_wallet->add_address_book_row(info.address, payment_id, req.description, info.is_subaddress))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to add address book entry";

@ -69,6 +69,11 @@ namespace tools
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("create_address", on_create_address, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS)
MAP_JON_RPC_WE("label_address", on_label_address, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS)
MAP_JON_RPC_WE("get_accounts", on_get_accounts, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS)
MAP_JON_RPC_WE("create_account", on_create_account, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT)
MAP_JON_RPC_WE("label_account", on_label_account, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT)
MAP_JON_RPC_WE("getheight", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
@ -108,8 +113,13 @@ namespace tools
//json_rpc
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er);
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er);
bool on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er);
bool on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er);
bool on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er);
bool validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, const std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er);
bool validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er);
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);

@ -31,6 +31,7 @@
#pragma once
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/subaddress_index.h"
#include "crypto/hash.h"
#include "wallet_rpc_server_error_codes.h"
@ -48,7 +49,28 @@ namespace wallet_rpc
{
struct request
{
uint32_t account_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
END_KV_SERIALIZE_MAP()
};
struct per_subaddress_info
{
uint32_t address_index;
std::string address;
uint64_t balance;
uint64_t unlocked_balance;
std::string label;
uint64_t num_unspent_outputs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address_index)
KV_SERIALIZE(address)
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(label)
KV_SERIALIZE(num_unspent_outputs)
END_KV_SERIALIZE_MAP()
};
@ -56,10 +78,12 @@ namespace wallet_rpc
{
uint64_t balance;
uint64_t unlocked_balance;
std::vector<per_subaddress_info> per_subaddress;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(per_subaddress)
END_KV_SERIALIZE_MAP()
};
};
@ -68,20 +92,164 @@ namespace wallet_rpc
{
struct request
{
uint32_t account_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
END_KV_SERIALIZE_MAP()
};
struct address_info
{
std::string address;
std::string label;
uint32_t address_index;
bool used;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(label)
KV_SERIALIZE(address_index)
KV_SERIALIZE(used)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string address; // to remain compatible with older RPC format
std::vector<address_info> addresses;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(addresses)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_CREATE_ADDRESS
{
struct request
{
uint32_t account_index;
std::string label;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
KV_SERIALIZE(label)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string address;
uint32_t address_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(address_index)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_LABEL_ADDRESS
{
struct request
{
cryptonote::subaddress_index index;
std::string label;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(index)
KV_SERIALIZE(label)
END_KV_SERIALIZE_MAP()
};
struct response
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_GET_ACCOUNTS
{
struct request
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
struct subaddress_account_info
{
uint32_t account_index;
std::string base_address;
uint64_t balance;
uint64_t unlocked_balance;
std::string label;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
KV_SERIALIZE(base_address)
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(label)
END_KV_SERIALIZE_MAP()
};
struct response
{
uint64_t total_balance;
uint64_t total_unlocked_balance;
std::vector<subaddress_account_info> subaddress_accounts;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(total_balance)
KV_SERIALIZE(total_unlocked_balance)
KV_SERIALIZE(subaddress_accounts)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_CREATE_ACCOUNT
{
struct request
{
std::string label;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(label)
END_KV_SERIALIZE_MAP()
};
struct response
{
uint32_t account_index;
std::string address; // the 0-th address for convenience
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_LABEL_ACCOUNT
{
struct request
{
uint32_t account_index;
std::string label;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
KV_SERIALIZE(label)
END_KV_SERIALIZE_MAP()
};
struct response
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_GET_HEIGHT
{
struct request
@ -114,6 +282,8 @@ namespace wallet_rpc
struct request
{
std::list<transfer_destination> destinations;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
uint32_t priority;
uint64_t mixin;
uint64_t unlock_time;
@ -124,6 +294,8 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
KV_SERIALIZE(priority)
KV_SERIALIZE(mixin)
KV_SERIALIZE(unlock_time)
@ -157,6 +329,8 @@ namespace wallet_rpc
struct request
{
std::list<transfer_destination> destinations;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
uint32_t priority;
uint64_t mixin;
uint64_t unlock_time;
@ -167,6 +341,8 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
KV_SERIALIZE(priority)
KV_SERIALIZE(mixin)
KV_SERIALIZE(unlock_time)
@ -249,6 +425,8 @@ namespace wallet_rpc
struct request
{
std::string address;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
uint32_t priority;
uint64_t mixin;
uint64_t unlock_time;
@ -260,6 +438,8 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
KV_SERIALIZE(priority)
KV_SERIALIZE(mixin)
KV_SERIALIZE(unlock_time)
@ -318,6 +498,7 @@ namespace wallet_rpc
uint64_t amount;
uint64_t block_height;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(payment_id)
@ -325,6 +506,7 @@ namespace wallet_rpc
KV_SERIALIZE(amount)
KV_SERIALIZE(block_height)
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index)
END_KV_SERIALIZE_MAP()
};
@ -379,6 +561,7 @@ namespace wallet_rpc
uint64_t global_index;
std::string tx_hash;
uint64_t tx_size;
uint32_t subaddr_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
@ -386,6 +569,7 @@ namespace wallet_rpc
KV_SERIALIZE(global_index)
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_size)
KV_SERIALIZE(subaddr_index)
END_KV_SERIALIZE_MAP()
};
@ -394,9 +578,13 @@ namespace wallet_rpc
struct request
{
std::string transfer_type;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(transfer_type)
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
END_KV_SERIALIZE_MAP()
};
@ -470,10 +658,12 @@ namespace wallet_rpc
{
std::string standard_address;
std::string payment_id;
bool is_subaddress;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(standard_address)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(is_subaddress)
END_KV_SERIALIZE_MAP()
};
};
@ -561,6 +751,7 @@ namespace wallet_rpc
std::list<transfer_destination> destinations;
std::string type;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txid);
@ -573,6 +764,7 @@ namespace wallet_rpc
KV_SERIALIZE(destinations);
KV_SERIALIZE(type);
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index);
END_KV_SERIALIZE_MAP()
};
@ -589,6 +781,8 @@ namespace wallet_rpc
bool filter_by_height;
uint64_t min_height;
uint64_t max_height;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(in);
@ -599,6 +793,8 @@ namespace wallet_rpc
KV_SERIALIZE(filter_by_height);
KV_SERIALIZE(min_height);
KV_SERIALIZE(max_height);
KV_SERIALIZE(account_index);
KV_SERIALIZE(subaddr_indices);
END_KV_SERIALIZE_MAP()
};

@ -44,3 +44,5 @@
#define WALLET_RPC_ERROR_CODE_WRONG_URI -11
#define WALLET_RPC_ERROR_CODE_WRONG_INDEX -12
#define WALLET_RPC_ERROR_CODE_NOT_OPEN -13
#define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND -14
#define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUTOFBOUND -15

@ -152,7 +152,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev
std::vector<size_t> tx_outs;
uint64_t transfered;
lookup_acc_outs(m_recipient_account_4.get_keys(), tx_pool.front(), get_tx_pub_key_from_extra(tx_pool.front()), tx_outs, transfered);
lookup_acc_outs(m_recipient_account_4.get_keys(), tx_pool.front(), get_tx_pub_key_from_extra(tx_pool.front()), get_additional_tx_pub_keys_from_extra(tx_pool.front()), tx_outs, transfered);
CHECK_EQ(MK_COINS(13), transfered);
m_chain_1.swap(blocks);

@ -344,7 +344,7 @@ bool init_output_indices(map_output_idx_t& outs, std::map<uint64_t, std::vector<
size_t tx_global_idx = outs[out.amount].size() - 1;
outs[out.amount][tx_global_idx].idx = tx_global_idx;
// Is out to me?
if (is_out_to_acc(from.get_keys(), boost::get<txout_to_key>(out.target), get_tx_pub_key_from_extra(tx), j)) {
if (is_out_to_acc(from.get_keys(), boost::get<txout_to_key>(out.target), get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), j)) {
outs_mine[out.amount].push_back(tx_global_idx);
}
}
@ -364,7 +364,10 @@ bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine,
// construct key image for this output
crypto::key_image img;
keypair in_ephemeral;
generate_key_image_helper(from.get_keys(), get_tx_pub_key_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img);
crypto::public_key out_key = boost::get<txout_to_key>(oi.out).key;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[from.get_keys().m_account_address.m_spend_public_key] = {0,0};
generate_key_image_helper(from.get_keys(), subaddresses, out_key, get_tx_pub_key_from_extra(*oi.p_tx), get_additional_tx_pub_keys_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img);
// lookup for this key image in the events vector
BOOST_FOREACH(auto& tx_pair, mtx) {

@ -169,10 +169,10 @@ bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const
std::vector<cryptonote::tx_destination_entry> destinations;
const account_public_address& bob_addr = bob_account.get_keys().m_account_address;
destinations.push_back(tx_destination_entry(MONEY_SUPPLY, bob_addr));
destinations.push_back(tx_destination_entry(MONEY_SUPPLY - 1, bob_addr));
destinations.push_back(tx_destination_entry(MONEY_SUPPLY, bob_addr, false));
destinations.push_back(tx_destination_entry(MONEY_SUPPLY - 1, bob_addr, false));
// sources.front().amount = destinations[0].amount + destinations[2].amount + destinations[3].amount + TESTS_DEFAULT_FEE
destinations.push_back(tx_destination_entry(sources.front().amount - MONEY_SUPPLY - MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr));
destinations.push_back(tx_destination_entry(sources.front().amount - MONEY_SUPPLY - MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr, false));
cryptonote::transaction tx_1;
if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_1, 0))

@ -117,7 +117,10 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
destinations.push_back(td); // 30 -> 7.39 * 4
crypto::secret_key tx_key;
bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), sources, destinations, std::vector<uint8_t>(), rct_txes[n], 0, tx_key, true);
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes[n], 0, tx_key, additional_tx_keys, true);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
events.push_back(rct_txes[n]);
starting_rct_tx_hashes.push_back(get_transaction_hash(rct_txes[n]));
@ -215,7 +218,10 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
transaction tx;
crypto::secret_key tx_key;
bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), sources, destinations, std::vector<uint8_t>(), tx, 0, tx_key, true);
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[0].get_keys().m_account_address.m_spend_public_key] = {0,0};
bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_keys, true);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx)

@ -337,7 +337,8 @@ bool gen_ring_signature_big::check_balances_2(cryptonote::core& c, size_t ev_ind
std::vector<size_t> tx_outs;
uint64_t transfered;
lookup_acc_outs(m_alice_account.get_keys(), boost::get<transaction>(events[events.size() - 3]), get_tx_pub_key_from_extra(boost::get<transaction>(events[events.size() - 3])), tx_outs, transfered);
const transaction& tx = boost::get<transaction>(events[events.size() - 3]);
lookup_acc_outs(m_alice_account.get_keys(), boost::get<transaction>(events[events.size() - 3]), get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), tx_outs, transfered);
CHECK_EQ(m_tx_amount, transfered);
return true;

@ -125,11 +125,11 @@ bool test_transaction_generation_and_ring_signature()
std::vector<size_t> outs;
uint64_t money = 0;
r = lookup_acc_outs(rv_acc.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), outs, money);
r = lookup_acc_outs(rv_acc.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), get_additional_tx_pub_keys_from_extra(tx_rc1), outs, money);
CHECK_AND_ASSERT_MES(r, false, "failed to lookup_acc_outs");
CHECK_AND_ASSERT_MES(td.amount == money, false, "wrong money amount in new transaction");
money = 0;
r = lookup_acc_outs(rv_acc2.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), outs, money);
r = lookup_acc_outs(rv_acc2.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), get_additional_tx_pub_keys_from_extra(tx_rc1), outs, money);
CHECK_AND_ASSERT_MES(r, false, "failed to lookup_acc_outs");
CHECK_AND_ASSERT_MES(0 == money, false, "wrong money amount in new transaction");
return true;
@ -139,11 +139,11 @@ bool test_block_creation()
{
uint64_t vszs[] = {80,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,9391,476,476,475,475,474,475,8819,8301,475,472,4302,5316,14347,16620,19583,19403,19728,19442,19852,19015,19000,19016,19795,19749,18087,19787,19704,19750,19267,19006,19050,19445,19407,19522,19546,19788,19369,19486,19329,19370,18853,19600,19110,19320,19746,19474,19474,19743,19494,19755,19715,19769,19620,19368,19839,19532,23424,28287,30707};
std::vector<uint64_t> szs(&vszs[0], &vszs[90]);
account_public_address adr;
bool r = get_account_address_from_str(adr, false, "0099be99c70ef10fd534c43c88e9d13d1c8853213df7e362afbec0e4ee6fec4948d0c190b58f4b356cd7feaf8d9d0a76e7c7e5a9a0a497a6b1faf7a765882dd08ac2");
address_parse_info info;
bool r = get_account_address_from_str(info, false, "0099be99c70ef10fd534c43c88e9d13d1c8853213df7e362afbec0e4ee6fec4948d0c190b58f4b356cd7feaf8d9d0a76e7c7e5a9a0a497a6b1faf7a765882dd08ac2");
CHECK_AND_ASSERT_MES(r, false, "failed to import");
block b;
r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, b.miner_tx, blobdata(), 11);
r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, info.address, b.miner_tx, blobdata(), 11);
return r;
}

@ -59,7 +59,10 @@ namespace
m_in_contexts.push_back(keypair());
keypair& in_ephemeral = m_in_contexts.back();
crypto::key_image img;
generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img);
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
auto& out_key = reinterpret_cast<const crypto::public_key&>(src_entr.outputs[src_entr.real_output].second.dest);
generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral, img);
// put key image into tx input
txin_to_key input_to_key;

@ -52,10 +52,6 @@ bool operator !=(const ec_point &a, const ec_point &b) {
return 0 != memcmp(&a, &b, sizeof(ec_point));
}
bool operator !=(const secret_key &a, const secret_key &b) {
return 0 != memcmp(&a, &b, sizeof(secret_key));
}
bool operator !=(const key_derivation &a, const key_derivation &b) {
return 0 != memcmp(&a, &b, sizeof(key_derivation));
}
@ -99,7 +95,7 @@ int main(int argc, char *argv[]) {
vector<char> data;
ec_scalar expected, actual;
get(input, data, expected);
hash_to_scalar(data.data(), data.size(), actual);
crypto::hash_to_scalar(data.data(), data.size(), actual);
if (expected != actual) {
goto error;
}

@ -172,7 +172,7 @@ bool transactions_flow_test(std::string& working_folder,
//wait for money, until balance will have enough money
w1.refresh(blocks_fetched, received_money, ok);
while(w1.unlocked_balance() < amount_to_transfer)
while(w1.unlocked_balance(0) < amount_to_transfer)
{
misc_utils::sleep_no_w(1000);
w1.refresh(blocks_fetched, received_money, ok);
@ -185,7 +185,7 @@ bool transactions_flow_test(std::string& working_folder,
{
tools::wallet2::transfer_container incoming_transfers;
w1.get_transfers(incoming_transfers);
if(incoming_transfers.size() > FIRST_N_TRANSFERS && get_money_in_first_transfers(incoming_transfers, FIRST_N_TRANSFERS) < w1.unlocked_balance() )
if(incoming_transfers.size() > FIRST_N_TRANSFERS && get_money_in_first_transfers(incoming_transfers, FIRST_N_TRANSFERS) < w1.unlocked_balance(0) )
{
//lets go!
size_t count = 0;
@ -220,7 +220,7 @@ bool transactions_flow_test(std::string& working_folder,
for(i = 0; i != transactions_count; i++)
{
uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size: (amount_to_transfer - transfered_money);
while(w1.unlocked_balance() < amount_to_tx + TEST_FEE)
while(w1.unlocked_balance(0) < amount_to_tx + TEST_FEE)
{
misc_utils::sleep_no_w(1000);
LOG_PRINT_L0("not enough money, waiting for cashback or mining");
@ -269,7 +269,7 @@ bool transactions_flow_test(std::string& working_folder,
misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*1000);//wait two blocks before sync on another wallet on another daemon
}
uint64_t money_2 = w2.balance();
uint64_t money_2 = w2.balance(0);
if(money_2 == transfered_money)
{
MGINFO_GREEN("-----------------------FINISHING TRANSACTIONS FLOW TEST OK-----------------------");

@ -66,9 +66,8 @@ int SignatureFuzzer::init()
boost::filesystem::remove("/tmp/signature-test.address.txt");
boost::filesystem::remove("/tmp/signature-test");
bool has_payment_id;
crypto::hash8 new_payment_id;
if (!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, true, "9uVsvEryzpN8WH2t1WWhFFCG5tS8cBNdmJYNRuckLENFimfauV5pZKeS1P2CbxGkSDTUPHXWwiYE5ZGSXDAGbaZgDxobqDN"))
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str_or_url(info, true, "9uVsvEryzpN8WH2t1WWhFFCG5tS8cBNdmJYNRuckLENFimfauV5pZKeS1P2CbxGkSDTUPHXWwiYE5ZGSXDAGbaZgDxobqDN"))
{
std::cerr << "failed to parse address" << std::endl;
return 1;

@ -130,7 +130,7 @@ struct Utils
{
Monero::WalletManager *wmgr = Monero::WalletManagerFactory::getWalletManager();
Monero::Wallet * w = wmgr->openWallet(filename, password, true);
std::string result = w->address();
std::string result = w->mainAddress();
wmgr->closeWallet(w);
return result;
}
@ -215,8 +215,8 @@ TEST_F(WalletManagerTest, WalletManagerCreatesWallet)
boost::split(words, seed, boost::is_any_of(" "), boost::token_compress_on);
ASSERT_TRUE(words.size() == 25);
std::cout << "** seed: " << wallet->seed() << std::endl;
ASSERT_FALSE(wallet->address().empty());
std::cout << "** address: " << wallet->address() << std::endl;
ASSERT_FALSE(wallet->mainAddress().empty());
std::cout << "** address: " << wallet->mainAddress() << std::endl;
ASSERT_TRUE(wmgr->closeWallet(wallet));
}
@ -261,7 +261,7 @@ void open_wallet_helper(Monero::WalletManager *wmgr, Monero::Wallet **wallet, co
mutex->lock();
LOG_PRINT_L3("opening wallet in thread: " << boost::this_thread::get_id());
*wallet = wmgr->openWallet(WALLET_NAME, pass, true);
LOG_PRINT_L3("wallet address: " << (*wallet)->address());
LOG_PRINT_L3("wallet address: " << (*wallet)->mainAddress());
LOG_PRINT_L3("wallet status: " << (*wallet)->status());
LOG_PRINT_L3("closing wallet in thread: " << boost::this_thread::get_id());
if (mutex)
@ -371,14 +371,14 @@ TEST_F(WalletManagerTest, WalletManagerRecoversWallet)
{
Monero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
std::string seed1 = wallet1->seed();
std::string address1 = wallet1->address();
std::string address1 = wallet1->mainAddress();
ASSERT_FALSE(address1.empty());
ASSERT_TRUE(wmgr->closeWallet(wallet1));
Utils::deleteWallet(WALLET_NAME);
Monero::Wallet * wallet2 = wmgr->recoveryWallet(WALLET_NAME, seed1);
ASSERT_TRUE(wallet2->status() == Monero::Wallet::Status_Ok);
ASSERT_TRUE(wallet2->seed() == seed1);
ASSERT_TRUE(wallet2->address() == address1);
ASSERT_TRUE(wallet2->mainAddress() == address1);
ASSERT_TRUE(wmgr->closeWallet(wallet2));
}
@ -387,7 +387,7 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet1)
{
Monero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
std::string seed1 = wallet1->seed();
std::string address1 = wallet1->address();
std::string address1 = wallet1->mainAddress();
ASSERT_TRUE(wallet1->store(""));
ASSERT_TRUE(wallet1->store(WALLET_NAME_COPY));
@ -395,7 +395,7 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet1)
Monero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME_COPY, WALLET_PASS);
ASSERT_TRUE(wallet2->status() == Monero::Wallet::Status_Ok);
ASSERT_TRUE(wallet2->seed() == seed1);
ASSERT_TRUE(wallet2->address() == address1);
ASSERT_TRUE(wallet2->mainAddress() == address1);
ASSERT_TRUE(wmgr->closeWallet(wallet2));
}
@ -404,7 +404,7 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet2)
{
Monero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
std::string seed1 = wallet1->seed();
std::string address1 = wallet1->address();
std::string address1 = wallet1->mainAddress();
ASSERT_TRUE(wallet1->store(WALLET_NAME_WITH_DIR));
ASSERT_TRUE(wmgr->closeWallet(wallet1));
@ -412,7 +412,7 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet2)
wallet1 = wmgr->openWallet(WALLET_NAME_WITH_DIR, WALLET_PASS);
ASSERT_TRUE(wallet1->status() == Monero::Wallet::Status_Ok);
ASSERT_TRUE(wallet1->seed() == seed1);
ASSERT_TRUE(wallet1->address() == address1);
ASSERT_TRUE(wallet1->mainAddress() == address1);
ASSERT_TRUE(wmgr->closeWallet(wallet1));
}
@ -421,7 +421,7 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet3)
{
Monero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
std::string seed1 = wallet1->seed();
std::string address1 = wallet1->address();
std::string address1 = wallet1->mainAddress();
ASSERT_FALSE(wallet1->store(WALLET_NAME_WITH_DIR_NON_WRITABLE));
ASSERT_TRUE(wmgr->closeWallet(wallet1));
@ -435,7 +435,7 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet3)
wallet1 = wmgr->openWallet(WALLET_NAME, WALLET_PASS);
ASSERT_TRUE(wallet1->status() == Monero::Wallet::Status_Ok);
ASSERT_TRUE(wallet1->seed() == seed1);
ASSERT_TRUE(wallet1->address() == address1);
ASSERT_TRUE(wallet1->mainAddress() == address1);
ASSERT_TRUE(wmgr->closeWallet(wallet1));
}
@ -445,7 +445,7 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet4)
{
Monero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
std::string seed1 = wallet1->seed();
std::string address1 = wallet1->address();
std::string address1 = wallet1->mainAddress();
ASSERT_TRUE(wallet1->store(""));
ASSERT_TRUE(wallet1->status() == Monero::Wallet::Status_Ok);
@ -458,7 +458,7 @@ TEST_F(WalletManagerTest, WalletManagerStoresWallet4)
wallet1 = wmgr->openWallet(WALLET_NAME, WALLET_PASS);
ASSERT_TRUE(wallet1->status() == Monero::Wallet::Status_Ok);
ASSERT_TRUE(wallet1->seed() == seed1);
ASSERT_TRUE(wallet1->address() == address1);
ASSERT_TRUE(wallet1->mainAddress() == address1);
ASSERT_TRUE(wmgr->closeWallet(wallet1));
}
@ -496,18 +496,18 @@ TEST_F(WalletTest1, WalletGeneratesIntegratedAddress)
TEST_F(WalletTest1, WalletShowsBalance)
{
Monero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
ASSERT_TRUE(wallet1->balance() > 0);
ASSERT_TRUE(wallet1->unlockedBalance() > 0);
ASSERT_TRUE(wallet1->balance(0) > 0);
ASSERT_TRUE(wallet1->unlockedBalance(0) > 0);
uint64_t balance1 = wallet1->balance();
uint64_t unlockedBalance1 = wallet1->unlockedBalance();
uint64_t balance1 = wallet1->balance(0);
uint64_t unlockedBalance1 = wallet1->unlockedBalance(0);
ASSERT_TRUE(wmgr->closeWallet(wallet1));
Monero::Wallet * wallet2 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
ASSERT_TRUE(balance1 == wallet2->balance());
std::cout << "wallet balance: " << wallet2->balance() << std::endl;
ASSERT_TRUE(unlockedBalance1 == wallet2->unlockedBalance());
std::cout << "wallet unlocked balance: " << wallet2->unlockedBalance() << std::endl;
ASSERT_TRUE(balance1 == wallet2->balance(0));
std::cout << "wallet balance: " << wallet2->balance(0) << std::endl;
ASSERT_TRUE(unlockedBalance1 == wallet2->unlockedBalance(0));
std::cout << "wallet unlocked balance: " << wallet2->unlockedBalance(0) << std::endl;
ASSERT_TRUE(wmgr->closeWallet(wallet2));
}
@ -569,7 +569,7 @@ TEST_F(WalletTest1, WalletTransaction)
// make sure testnet daemon is running
ASSERT_TRUE(wallet1->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet1->refresh());
uint64_t balance = wallet1->balance();
uint64_t balance = wallet1->balance(0);
ASSERT_TRUE(wallet1->status() == Monero::PendingTransaction::Status_Ok);
std::string recepient_address = Utils::get_wallet_address(CURRENT_DST_WALLET, TESTNET_WALLET_PASS);
@ -580,14 +580,16 @@ TEST_F(WalletTest1, WalletTransaction)
PAYMENT_ID_EMPTY,
AMOUNT_10XMR,
MIXIN_COUNT,
0,
std::set<uint32_t>{},
Monero::PendingTransaction::Priority_Medium);
ASSERT_TRUE(transaction->status() == Monero::PendingTransaction::Status_Ok);
wallet1->refresh();
ASSERT_TRUE(wallet1->balance() == balance);
ASSERT_TRUE(wallet1->balance(0) == balance);
ASSERT_TRUE(transaction->amount() == AMOUNT_10XMR);
ASSERT_TRUE(transaction->commit());
ASSERT_FALSE(wallet1->balance() == balance);
ASSERT_FALSE(wallet1->balance(0) == balance);
ASSERT_TRUE(wmgr->closeWallet(wallet1));
}
@ -611,14 +613,15 @@ TEST_F(WalletTest1, WalletTransactionWithMixin)
// make sure testnet daemon is running
ASSERT_TRUE(wallet1->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet1->refresh());
uint64_t balance = wallet1->balance();
uint64_t balance = wallet1->balance(0);
ASSERT_TRUE(wallet1->status() == Monero::PendingTransaction::Status_Ok);
std::string recepient_address = Utils::get_wallet_address(CURRENT_DST_WALLET, TESTNET_WALLET_PASS);
for (auto mixin : mixins) {
std::cerr << "Transaction mixin count: " << mixin << std::endl;
Monero::PendingTransaction * transaction = wallet1->createTransaction(
recepient_address, payment_id, AMOUNT_5XMR, mixin);
recepient_address, payment_id, AMOUNT_5XMR, mixin, 0, std::set<uint32_t>{});
std::cerr << "Transaction status: " << transaction->status() << std::endl;
std::cerr << "Transaction fee: " << Monero::Wallet::displayAmount(transaction->fee()) << std::endl;
@ -629,7 +632,7 @@ TEST_F(WalletTest1, WalletTransactionWithMixin)
wallet1->refresh();
ASSERT_TRUE(wallet1->balance() == balance);
ASSERT_TRUE(wallet1->balance(0) == balance);
ASSERT_TRUE(wmgr->closeWallet(wallet1));
}
@ -643,7 +646,7 @@ TEST_F(WalletTest1, WalletTransactionWithPriority)
// make sure testnet daemon is running
ASSERT_TRUE(wallet1->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet1->refresh());
uint64_t balance = wallet1->balance();
uint64_t balance = wallet1->balance(0);
ASSERT_TRUE(wallet1->status() == Monero::PendingTransaction::Status_Ok);
std::string recepient_address = Utils::get_wallet_address(CURRENT_DST_WALLET, TESTNET_WALLET_PASS);
@ -658,8 +661,9 @@ TEST_F(WalletTest1, WalletTransactionWithPriority)
for (auto it = priorities.begin(); it != priorities.end(); ++it) {
std::cerr << "Transaction priority: " << *it << std::endl;
Monero::PendingTransaction * transaction = wallet1->createTransaction(
recepient_address, payment_id, AMOUNT_5XMR, mixin, *it);
recepient_address, payment_id, AMOUNT_5XMR, mixin, 0, std::set<uint32_t>{}, *it);
std::cerr << "Transaction status: " << transaction->status() << std::endl;
std::cerr << "Transaction fee: " << Monero::Wallet::displayAmount(transaction->fee()) << std::endl;
std::cerr << "Transaction error: " << transaction->errorString() << std::endl;
@ -669,7 +673,7 @@ TEST_F(WalletTest1, WalletTransactionWithPriority)
wallet1->disposeTransaction(transaction);
}
wallet1->refresh();
ASSERT_TRUE(wallet1->balance() == balance);
ASSERT_TRUE(wallet1->balance(0) == balance);
ASSERT_TRUE(wmgr->closeWallet(wallet1));
}
@ -715,7 +719,7 @@ TEST_F(WalletTest1, WalletTransactionAndHistory)
Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr,
PAYMENT_ID_EMPTY,
AMOUNT_10XMR * 5, 1);
AMOUNT_10XMR * 5, 1, 0, std::set<uint32_t>{});
ASSERT_TRUE(tx->status() == Monero::PendingTransaction::Status_Ok);
ASSERT_TRUE(tx->commit());
@ -757,7 +761,7 @@ TEST_F(WalletTest1, WalletTransactionWithPaymentId)
Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr,
payment_id,
AMOUNT_1XMR, 1);
AMOUNT_1XMR, 1, 0, std::set<uint32_t>{});
ASSERT_TRUE(tx->status() == Monero::PendingTransaction::Status_Ok);
ASSERT_TRUE(tx->commit());
@ -816,7 +820,7 @@ struct MyWalletListener : public Monero::WalletListener
virtual void moneySpent(const string &txId, uint64_t amount)
{
std::cerr << "wallet: " << wallet->address() << "**** just spent money ("
std::cerr << "wallet: " << wallet->mainAddress() << "**** just spent money ("
<< txId << ", " << wallet->displayAmount(amount) << ")" << std::endl;
total_tx += amount;
send_triggered = true;
@ -825,7 +829,7 @@ struct MyWalletListener : public Monero::WalletListener
virtual void moneyReceived(const string &txId, uint64_t amount)
{
std::cout << "wallet: " << wallet->address() << "**** just received money ("
std::cout << "wallet: " << wallet->mainAddress() << "**** just received money ("
<< txId << ", " << wallet->displayAmount(amount) << ")" << std::endl;
total_rx += amount;
receive_triggered = true;
@ -834,7 +838,7 @@ struct MyWalletListener : public Monero::WalletListener
virtual void unconfirmedMoneyReceived(const string &txId, uint64_t amount)
{
std::cout << "wallet: " << wallet->address() << "**** just received unconfirmed money ("
std::cout << "wallet: " << wallet->mainAddress() << "**** just received unconfirmed money ("
<< txId << ", " << wallet->displayAmount(amount) << ")" << std::endl;
// Don't trigger recieve until tx is mined
// total_rx += amount;
@ -844,7 +848,7 @@ struct MyWalletListener : public Monero::WalletListener
virtual void newBlock(uint64_t height)
{
// std::cout << "wallet: " << wallet->address()
// std::cout << "wallet: " << wallet->mainAddress()
// <<", new block received, blockHeight: " << height << std::endl;
static int bc_height = wallet->daemonBlockChainHeight();
std::cout << height
@ -920,17 +924,17 @@ TEST_F(WalletTest2, WalletCallbackSent)
ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet_src->refresh());
MyWalletListener * wallet_src_listener = new MyWalletListener(wallet_src);
uint64_t balance = wallet_src->balance();
std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance()) << std::endl;
uint64_t balance = wallet_src->balance(0);
std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance(0)) << std::endl;
Monero::Wallet * wallet_dst = wmgr->openWallet(CURRENT_DST_WALLET, TESTNET_WALLET_PASS, true);
uint64_t amount = AMOUNT_1XMR * 5;
std::cout << "** Sending " << Monero::Wallet::displayAmount(amount) << " to " << wallet_dst->address();
std::cout << "** Sending " << Monero::Wallet::displayAmount(amount) << " to " << wallet_dst->mainAddress();
Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet_dst->address(),
Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet_dst->mainAddress(),
PAYMENT_ID_EMPTY,
amount, 1);
amount, 1, 0, std::set<uint32_t>{});
std::cout << "** Committing transaction: " << Monero::Wallet::displayAmount(tx->amount())
<< " with fee: " << Monero::Wallet::displayAmount(tx->fee());
@ -944,8 +948,8 @@ TEST_F(WalletTest2, WalletCallbackSent)
std::cerr << "TEST: send lock acquired...\n";
ASSERT_TRUE(wallet_src_listener->send_triggered);
ASSERT_TRUE(wallet_src_listener->update_triggered);
std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance()) << std::endl;
ASSERT_TRUE(wallet_src->balance() < balance);
std::cout << "** Balance: " << wallet_src->displayAmount(wallet_src->balance(0)) << std::endl;
ASSERT_TRUE(wallet_src->balance(0) < balance);
wmgr->closeWallet(wallet_src);
wmgr->closeWallet(wallet_dst);
}
@ -958,20 +962,20 @@ TEST_F(WalletTest2, WalletCallbackReceived)
// make sure testnet daemon is running
ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet_src->refresh());
std::cout << "** Balance src1: " << wallet_src->displayAmount(wallet_src->balance()) << std::endl;
std::cout << "** Balance src1: " << wallet_src->displayAmount(wallet_src->balance(0)) << std::endl;
Monero::Wallet * wallet_dst = wmgr->openWallet(CURRENT_DST_WALLET, TESTNET_WALLET_PASS, true);
ASSERT_TRUE(wallet_dst->init(TESTNET_DAEMON_ADDRESS, 0));
ASSERT_TRUE(wallet_dst->refresh());
uint64_t balance = wallet_dst->balance();
std::cout << "** Balance dst1: " << wallet_dst->displayAmount(wallet_dst->balance()) << std::endl;
uint64_t balance = wallet_dst->balance(0);
std::cout << "** Balance dst1: " << wallet_dst->displayAmount(wallet_dst->balance(0)) << std::endl;
std::unique_ptr<MyWalletListener> wallet_dst_listener (new MyWalletListener(wallet_dst));
uint64_t amount = AMOUNT_1XMR * 5;
std::cout << "** Sending " << Monero::Wallet::displayAmount(amount) << " to " << wallet_dst->address();
Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet_dst->address(),
std::cout << "** Sending " << Monero::Wallet::displayAmount(amount) << " to " << wallet_dst->mainAddress();
Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet_dst->mainAddress(),
PAYMENT_ID_EMPTY,
amount, 1);
amount, 1, 0, std::set<uint32_t>{});
std::cout << "** Committing transaction: " << Monero::Wallet::displayAmount(tx->amount())
<< " with fee: " << Monero::Wallet::displayAmount(tx->fee());
@ -987,10 +991,10 @@ TEST_F(WalletTest2, WalletCallbackReceived)
ASSERT_TRUE(wallet_dst_listener->receive_triggered);
ASSERT_TRUE(wallet_dst_listener->update_triggered);
std::cout << "** Balance src2: " << wallet_dst->displayAmount(wallet_src->balance()) << std::endl;
std::cout << "** Balance dst2: " << wallet_dst->displayAmount(wallet_dst->balance()) << std::endl;
std::cout << "** Balance src2: " << wallet_dst->displayAmount(wallet_src->balance(0)) << std::endl;
std::cout << "** Balance dst2: " << wallet_dst->displayAmount(wallet_dst->balance(0)) << std::endl;
ASSERT_TRUE(wallet_dst->balance() > balance);
ASSERT_TRUE(wallet_dst->balance(0) > balance);
wmgr->closeWallet(wallet_src);
wmgr->closeWallet(wallet_dst);
@ -1099,7 +1103,7 @@ TEST_F(WalletManagerMainnetTest, RecoverAndRefreshWalletMainNetAsync)
int SECONDS_TO_REFRESH = 120;
Monero::Wallet * wallet = wmgr->createWallet(WALLET_NAME_MAINNET, "", WALLET_LANG);
std::string seed = wallet->seed();
std::string address = wallet->address();
std::string address = wallet->mainAddress();
wmgr->closeWallet(wallet);
// deleting wallet files
@ -1108,7 +1112,7 @@ TEST_F(WalletManagerMainnetTest, RecoverAndRefreshWalletMainNetAsync)
wallet = wmgr->recoveryWallet(WALLET_NAME_MAINNET, seed);
ASSERT_TRUE(wallet->status() == Monero::Wallet::Status_Ok);
ASSERT_TRUE(wallet->address() == address);
ASSERT_TRUE(wallet->mainAddress() == address);
std::unique_ptr<MyWalletListener> wallet_listener (new MyWalletListener(wallet));
boost::chrono::seconds wait_for = boost::chrono::seconds(SECONDS_TO_REFRESH);
boost::unique_lock<boost::mutex> lock (wallet_listener->mutex);

@ -62,10 +62,13 @@ public:
m_alice.generate();
std::vector<tx_destination_entry> destinations;
destinations.push_back(tx_destination_entry(this->m_source_amount, m_alice.get_keys().m_account_address));
destinations.push_back(tx_destination_entry(this->m_source_amount, m_alice.get_keys().m_account_address, false));
crypto::secret_key tx_key;
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector<uint8_t>(), m_tx, 0, tx_key, rct))
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct))
return false;
get_transaction_prefix_hash(m_tx, m_tx_prefix_hash);

@ -61,7 +61,7 @@ public:
for (size_t i = 0; i < out_count; ++i)
{
m_destinations.push_back(tx_destination_entry(this->m_source_amount / out_count, m_alice.get_keys().m_account_address));
m_destinations.push_back(tx_destination_entry(this->m_source_amount / out_count, m_alice.get_keys().m_account_address, false));
}
return true;
@ -70,7 +70,10 @@ public:
bool test()
{
crypto::secret_key tx_key;
return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, m_destinations, std::vector<uint8_t>(), m_tx, 0, tx_key, rct);
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct);
}
private:

@ -52,7 +52,7 @@ public:
m_alice.generate();
std::vector<tx_destination_entry> destinations;
destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address));
destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false));
return construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector<uint8_t>(), m_tx, 0);
}

@ -45,6 +45,9 @@ public:
{
cryptonote::keypair in_ephemeral;
crypto::key_image ki;
return cryptonote::generate_key_image_helper(m_bob.get_keys(), m_tx_pub_key, 0, in_ephemeral, ki);
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[m_bob.get_keys().m_account_address.m_spend_public_key] = {0,0};
crypto::public_key out_key = boost::get<cryptonote::txout_to_key>(m_tx.vout[0].target).key;
return cryptonote::generate_key_image_helper(m_bob.get_keys(), subaddresses, out_key, m_tx_pub_key, m_additional_tx_pub_keys, 0, in_ephemeral, ki);
}
};

@ -44,7 +44,7 @@ public:
bool test()
{
const cryptonote::txout_to_key& tx_out = boost::get<cryptonote::txout_to_key>(m_tx.vout[0].target);
return cryptonote::is_out_to_acc(m_bob.get_keys(), tx_out, m_tx_pub_key, 0);
return cryptonote::is_out_to_acc(m_bob.get_keys(), tx_out, m_tx_pub_key, m_additional_tx_pub_keys, 0);
}
};

@ -47,6 +47,7 @@ public:
return false;
m_tx_pub_key = get_tx_pub_key_from_extra(m_tx);
m_additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(m_tx);
return true;
}
@ -54,4 +55,5 @@ protected:
cryptonote::account_base m_bob;
cryptonote::transaction m_tx;
crypto::public_key m_tx_pub_key;
std::vector<crypto::public_key> m_additional_tx_pub_keys;
};

@ -474,43 +474,43 @@ TEST(get_account_address_as_str, works_correctly)
{
cryptonote::account_public_address addr;
ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr));
std::string addr_str = cryptonote::get_account_address_as_str(false, addr);
std::string addr_str = cryptonote::get_account_address_as_str(false, false, addr);
ASSERT_EQ(addr_str, test_keys_addr_str);
}
TEST(get_account_address_from_str, handles_valid_address)
{
cryptonote::account_public_address addr;
ASSERT_TRUE(cryptonote::get_account_address_from_str(addr, false, test_keys_addr_str));
cryptonote::address_parse_info info;
ASSERT_TRUE(cryptonote::get_account_address_from_str(info, false, test_keys_addr_str));
std::string blob;
ASSERT_TRUE(serialization::dump_binary(addr, blob));
ASSERT_TRUE(serialization::dump_binary(info.address, blob));
ASSERT_EQ(blob, test_serialized_keys);
}
TEST(get_account_address_from_str, fails_on_invalid_address_format)
{
cryptonote::account_public_address addr;
cryptonote::address_parse_info info;
std::string addr_str = test_keys_addr_str;
addr_str[0] = '0';
ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, false, addr_str));
ASSERT_FALSE(cryptonote::get_account_address_from_str(info, false, addr_str));
}
TEST(get_account_address_from_str, fails_on_invalid_address_prefix)
{
std::string addr_str = base58::encode_addr(0, test_serialized_keys);
cryptonote::account_public_address addr;
ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, false, addr_str));
cryptonote::address_parse_info info;
ASSERT_FALSE(cryptonote::get_account_address_from_str(info, false, addr_str));
}
TEST(get_account_address_from_str, fails_on_invalid_address_content)
{
std::string addr_str = base58::encode_addr(config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, test_serialized_keys.substr(1));
cryptonote::account_public_address addr;
ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, false, addr_str));
cryptonote::address_parse_info info;
ASSERT_FALSE(cryptonote::get_account_address_from_str(info, false, addr_str));
}
TEST(get_account_address_from_str, fails_on_invalid_address_spend_key)
@ -519,8 +519,8 @@ TEST(get_account_address_from_str, fails_on_invalid_address_spend_key)
serialized_keys_copy[0] = '\0';
std::string addr_str = base58::encode_addr(config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy);
cryptonote::account_public_address addr;
ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, false, addr_str));
cryptonote::address_parse_info info;
ASSERT_FALSE(cryptonote::get_account_address_from_str(info, false, addr_str));
}
TEST(get_account_address_from_str, fails_on_invalid_address_view_key)
@ -529,12 +529,12 @@ TEST(get_account_address_from_str, fails_on_invalid_address_view_key)
serialized_keys_copy.back() = '\x01';
std::string addr_str = base58::encode_addr(config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy);
cryptonote::account_public_address addr;
ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, false, addr_str));
cryptonote::address_parse_info info;
ASSERT_FALSE(cryptonote::get_account_address_from_str(info, false, addr_str));
}
TEST(get_account_address_from_str, parses_old_address_format)
{
cryptonote::account_public_address addr;
ASSERT_TRUE(cryptonote::get_account_address_from_str(addr, false, "002391bbbb24dea6fd95232e97594a27769d0153d053d2102b789c498f57a2b00b69cd6f2f5c529c1660f2f4a2b50178d6640c20ce71fe26373041af97c5b10236fc"));
cryptonote::address_parse_info info;
ASSERT_TRUE(cryptonote::get_account_address_from_str(info, false, "002391bbbb24dea6fd95232e97594a27769d0153d053d2102b789c498f57a2b00b69cd6f2f5c529c1660f2f4a2b50178d6640c20ce71fe26373041af97c5b10236fc"));
}

@ -987,15 +987,15 @@ TEST(Serialization, portability_unsigned_tx)
ASSERT_TRUE(epee::string_tools::pod_to_hex(tse.mask) == "789bafff169ef206aa21219342c69ca52ce1d78d776c10b21d14bdd960fc7703");
// tcd.change_dts
ASSERT_TRUE(tcd.change_dts.amount == 9631208773403);
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, tcd.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, tcd.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
// tcd.splitted_dsts
ASSERT_TRUE(tcd.splitted_dsts.size() == 2);
auto& splitted_dst0 = tcd.splitted_dsts[0];
auto& splitted_dst1 = tcd.splitted_dsts[1];
ASSERT_TRUE(splitted_dst0.amount == 1400000000000);
ASSERT_TRUE(splitted_dst1.amount == 9631208773403);
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, splitted_dst0.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, splitted_dst1.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst0.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst1.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
// tcd.selected_transfers
ASSERT_TRUE(tcd.selected_transfers.size() == 1);
ASSERT_TRUE(tcd.selected_transfers.front() == 2);
@ -1008,7 +1008,7 @@ TEST(Serialization, portability_unsigned_tx)
ASSERT_TRUE(tcd.dests.size() == 1);
auto& dest = tcd.dests[0];
ASSERT_TRUE(dest.amount == 1400000000000);
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, dest.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, dest.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
// transfers
ASSERT_TRUE(exported_txs.transfers.size() == 3);
auto& td0 = exported_txs.transfers[0];
@ -1101,7 +1101,7 @@ TEST(Serialization, portability_signed_tx)
ASSERT_FALSE(ptx.dust_added_to_fee);
// ptx.change.{amount, addr}
ASSERT_TRUE(ptx.change_dts.amount == 9631208773403);
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, ptx.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, ptx.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
// ptx.selected_transfers
ASSERT_TRUE(ptx.selected_transfers.size() == 1);
ASSERT_TRUE(ptx.selected_transfers.front() == 2);
@ -1111,7 +1111,7 @@ TEST(Serialization, portability_signed_tx)
// ptx.dests
ASSERT_TRUE(ptx.dests.size() == 1);
ASSERT_TRUE(ptx.dests[0].amount == 1400000000000);
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, ptx.dests[0].addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, ptx.dests[0].addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
// ptx.construction_data
auto& tcd = ptx.construction_data;
ASSERT_TRUE(tcd.sources.size() == 1);
@ -1142,15 +1142,15 @@ TEST(Serialization, portability_signed_tx)
ASSERT_TRUE(epee::string_tools::pod_to_hex(tse.mask) == "789bafff169ef206aa21219342c69ca52ce1d78d776c10b21d14bdd960fc7703");
// ptx.construction_data.change_dts
ASSERT_TRUE(tcd.change_dts.amount == 9631208773403);
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, tcd.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, tcd.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
// ptx.construction_data.splitted_dsts
ASSERT_TRUE(tcd.splitted_dsts.size() == 2);
auto& splitted_dst0 = tcd.splitted_dsts[0];
auto& splitted_dst1 = tcd.splitted_dsts[1];
ASSERT_TRUE(splitted_dst0.amount == 1400000000000);
ASSERT_TRUE(splitted_dst1.amount == 9631208773403);
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, splitted_dst0.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, splitted_dst1.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst0.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst1.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk");
// ptx.construction_data.selected_transfers
ASSERT_TRUE(tcd.selected_transfers.size() == 1);
ASSERT_TRUE(tcd.selected_transfers.front() == 2);
@ -1163,7 +1163,7 @@ TEST(Serialization, portability_signed_tx)
ASSERT_TRUE(tcd.dests.size() == 1);
auto& dest = tcd.dests[0];
ASSERT_TRUE(dest.amount == 1400000000000);
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, dest.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, dest.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA");
// key_images
ASSERT_TRUE(exported_txs.key_images.size() == 3);
auto& ki0 = exported_txs.key_images[0];

Loading…
Cancel
Save