aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt2
-rw-r--r--src/wallet/api/wallet.cpp13
-rw-r--r--src/wallet/node_rpc_proxy.cpp66
-rw-r--r--src/wallet/node_rpc_proxy.h14
-rw-r--r--src/wallet/ringdb.cpp117
-rw-r--r--src/wallet/ringdb.h9
-rw-r--r--src/wallet/wallet2.cpp909
-rw-r--r--src/wallet/wallet2.h270
-rw-r--r--src/wallet/wallet_errors.h18
-rw-r--r--src/wallet/wallet_rpc_server.cpp373
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h23
12 files changed, 881 insertions, 935 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index a16f4fe19..be10b9f62 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -90,6 +90,8 @@ target_link_libraries(wallet_rpc_server
cncrypto
common
version
+ daemonizer
+ ${EPEE_READLINE}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 43a023d3c..7b04a7165 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -376,7 +376,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
, m_rebuildWalletCache(false)
, m_is_connected(false)
{
- m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds);
+ m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true);
m_history = new TransactionHistoryImpl(this);
m_wallet2Callback = new Wallet2CallbackImpl(this);
m_wallet->callback(m_wallet2Callback);
@@ -1311,6 +1311,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin();
if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIXIN;
+ fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast<uint32_t>(priority));
@@ -1394,8 +1395,8 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
setStatusError(tr("RPC error: ") + e.to_string());
- } catch (const tools::error::get_random_outs_error &e) {
- setStatusError((boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str());
+ } catch (const tools::error::get_outs_error &e) {
+ setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str());
} catch (const tools::error::not_enough_unlocked_money& e) {
std::ostringstream writer;
@@ -1476,8 +1477,8 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
setStatusError(tr("RPC error: ") + e.to_string());
- } catch (const tools::error::get_random_outs_error&) {
- setStatusError(tr("failed to get random outputs to mix"));
+ } catch (const tools::error::get_outs_error&) {
+ setStatusError(tr("failed to get outputs to mix"));
} catch (const tools::error::not_enough_unlocked_money& e) {
setStatusError("");
std::ostringstream writer;
@@ -2046,7 +2047,7 @@ bool WalletImpl::isNewWallet() const
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
{
// claim RPC so there's no in-memory encryption for now
- if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
+ if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
return false;
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 2072495cd..21f75371b 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -50,12 +50,13 @@ void NodeRPCProxy::invalidate()
m_height = 0;
for (size_t n = 0; n < 256; ++n)
m_earliest_height[n] = 0;
- m_dynamic_per_kb_fee_estimate = 0;
- m_dynamic_per_kb_fee_estimate_cached_height = 0;
- m_dynamic_per_kb_fee_estimate_grace_blocks = 0;
+ m_dynamic_base_fee_estimate = 0;
+ m_dynamic_base_fee_estimate_cached_height = 0;
+ m_dynamic_base_fee_estimate_grace_blocks = 0;
+ m_fee_quantization_mask = 1;
m_rpc_version = 0;
m_target_height = 0;
- m_block_size_limit = 0;
+ m_block_weight_limit = 0;
m_get_info_time = 0;
}
@@ -99,7 +100,7 @@ boost::optional<std::string> NodeRPCProxy::get_info() const
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height");
m_height = resp_t.height;
m_target_height = resp_t.target_height;
- m_block_size_limit = resp_t.block_size_limit;
+ m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit;
m_get_info_time = now;
}
return boost::optional<std::string>();
@@ -123,12 +124,12 @@ boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) c
return boost::optional<std::string>();
}
-boost::optional<std::string> NodeRPCProxy::get_block_size_limit(uint64_t &block_size_limit) const
+boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &block_weight_limit) const
{
auto res = get_info();
if (res)
return res;
- block_size_limit = m_block_size_limit;
+ block_weight_limit = m_block_weight_limit;
return boost::optional<std::string>();
}
@@ -153,7 +154,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
return boost::optional<std::string>();
}
-boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const
+boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const
{
uint64_t height;
@@ -161,10 +162,10 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6
if (result)
return result;
- if (m_dynamic_per_kb_fee_estimate_cached_height != height || m_dynamic_per_kb_fee_estimate_grace_blocks != grace_blocks)
+ if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks)
{
- cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
- cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
+ cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
+ cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock();
req_t.grace_blocks = grace_blocks;
@@ -173,12 +174,47 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
- m_dynamic_per_kb_fee_estimate = resp_t.fee;
- m_dynamic_per_kb_fee_estimate_cached_height = height;
- m_dynamic_per_kb_fee_estimate_grace_blocks = grace_blocks;
+ m_dynamic_base_fee_estimate = resp_t.fee;
+ m_dynamic_base_fee_estimate_cached_height = height;
+ m_dynamic_base_fee_estimate_grace_blocks = grace_blocks;
+ m_fee_quantization_mask = resp_t.quantization_mask;
}
- fee = m_dynamic_per_kb_fee_estimate;
+ fee = m_dynamic_base_fee_estimate;
+ return boost::optional<std::string>();
+}
+
+boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask) const
+{
+ uint64_t height;
+
+ boost::optional<std::string> result = get_height(height);
+ if (result)
+ return result;
+
+ if (m_dynamic_base_fee_estimate_cached_height != height)
+ {
+ cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
+ cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
+
+ m_daemon_rpc_mutex.lock();
+ req_t.grace_blocks = m_dynamic_base_fee_estimate_grace_blocks;
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
+ CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
+ CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
+ m_dynamic_base_fee_estimate = resp_t.fee;
+ m_dynamic_base_fee_estimate_cached_height = height;
+ m_fee_quantization_mask = resp_t.quantization_mask;
+ }
+
+ fee_quantization_mask = m_fee_quantization_mask;
+ if (fee_quantization_mask == 0)
+ {
+ MERROR("Fee quantization mask is 0, forcing to 1");
+ fee_quantization_mask = 1;
+ }
return boost::optional<std::string>();
}
diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h
index 8a65884f7..65f13eaaa 100644
--- a/src/wallet/node_rpc_proxy.h
+++ b/src/wallet/node_rpc_proxy.h
@@ -47,9 +47,10 @@ public:
boost::optional<std::string> get_height(uint64_t &height) const;
void set_height(uint64_t h);
boost::optional<std::string> get_target_height(uint64_t &height) const;
- boost::optional<std::string> get_block_size_limit(uint64_t &block_size_limit) const;
+ boost::optional<std::string> get_block_weight_limit(uint64_t &block_weight_limit) const;
boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height) const;
- boost::optional<std::string> get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const;
+ boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const;
+ boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask) const;
private:
boost::optional<std::string> get_info() const;
@@ -59,12 +60,13 @@ private:
mutable uint64_t m_height;
mutable uint64_t m_earliest_height[256];
- mutable uint64_t m_dynamic_per_kb_fee_estimate;
- mutable uint64_t m_dynamic_per_kb_fee_estimate_cached_height;
- mutable uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks;
+ mutable uint64_t m_dynamic_base_fee_estimate;
+ mutable uint64_t m_dynamic_base_fee_estimate_cached_height;
+ mutable uint64_t m_dynamic_base_fee_estimate_grace_blocks;
+ mutable uint64_t m_fee_quantization_mask;
mutable uint32_t m_rpc_version;
mutable uint64_t m_target_height;
- mutable uint64_t m_block_size_limit;
+ mutable uint64_t m_block_weight_limit;
mutable time_t m_get_info_time;
};
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index 3f2634c8b..e9fc6866d 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -55,6 +55,13 @@ static int compare_hash32(const MDB_val *a, const MDB_val *b)
return 0;
}
+static int compare_uint64(const MDB_val *a, const MDB_val *b)
+{
+ const uint64_t va = *(const uint64_t*) a->mv_data;
+ const uint64_t vb = *(const uint64_t*) b->mv_data;
+ return va < vb ? -1 : va > vb;
+}
+
static std::string compress_ring(const std::vector<uint64_t> &ring)
{
std::string s;
@@ -146,7 +153,7 @@ static int resize_env(MDB_env *env, const char *db_path, size_t needed)
MDB_stat mst;
int ret;
- needed = std::max(needed, (size_t)(2ul * 1024 * 1024)); // at least 2 MB
+ needed = std::max(needed, (size_t)(100ul * 1024 * 1024)); // at least 100 MB
ret = mdb_env_info(env, &mei);
if (ret)
@@ -217,9 +224,9 @@ ringdb::ringdb(std::string filename, const std::string &genesis):
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
mdb_set_compare(txn, dbi_rings, compare_hash32);
- dbr = mdb_dbi_open(txn, ("blackballs-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs);
+ dbr = mdb_dbi_open(txn, ("blackballs2-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
- mdb_set_dupsort(txn, dbi_blackballs, compare_hash32);
+ mdb_set_dupsort(txn, dbi_blackballs, compare_uint64);
dbr = mdb_txn_commit(txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr)));
@@ -374,7 +381,7 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return true;
}
-bool ringdb::blackball_worker(const crypto::public_key &output, int op)
+bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op)
{
MDB_txn *txn;
MDB_cursor *cursor;
@@ -382,49 +389,61 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op)
bool tx_active = false;
bool ret = true;
- dbr = resize_env(env, filename.c_str(), 32 * 2); // a pubkey, and some slack
+ THROW_WALLET_EXCEPTION_IF(outputs.size() > 1 && op == BLACKBALL_QUERY, tools::error::wallet_internal_error, "Blackball query only makes sense for a single output");
+
+ dbr = resize_env(env, filename.c_str(), 32 * 2 * outputs.size()); // a pubkey, and some slack
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
tx_active = true;
- MDB_val key = zerokeyval;
- MDB_val data;
- data.mv_data = (void*)&output;
- data.mv_size = sizeof(output);
- switch (op)
+ MDB_val key, data;
+ for (const std::pair<uint64_t, uint64_t> &output: outputs)
+ {
+ key.mv_data = (void*)&output.first;
+ key.mv_size = sizeof(output.first);
+ data.mv_data = (void*)&output.second;
+ data.mv_size = sizeof(output.second);
+
+ switch (op)
+ {
+ case BLACKBALL_BLACKBALL:
+ MDEBUG("Blackballing output " << output.first << "/" << output.second);
+ dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_APPENDDUP);
+ if (dbr == MDB_KEYEXIST)
+ dbr = 0;
+ break;
+ case BLACKBALL_UNBLACKBALL:
+ MDEBUG("Unblackballing output " << output.first << "/" << output.second);
+ dbr = mdb_del(txn, dbi_blackballs, &key, &data);
+ if (dbr == MDB_NOTFOUND)
+ dbr = 0;
+ break;
+ case BLACKBALL_QUERY:
+ dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor);
+ THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr)));
+ dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
+ THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr)));
+ ret = dbr != MDB_NOTFOUND;
+ if (dbr == MDB_NOTFOUND)
+ dbr = 0;
+ mdb_cursor_close(cursor);
+ break;
+ case BLACKBALL_CLEAR:
+ break;
+ default:
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Invalid blackball op");
+ }
+ THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr)));
+ }
+
+ if (op == BLACKBALL_CLEAR)
{
- case BLACKBALL_BLACKBALL:
- MDEBUG("Blackballing output " << output);
- dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_NODUPDATA);
- if (dbr == MDB_KEYEXIST)
- dbr = 0;
- break;
- case BLACKBALL_UNBLACKBALL:
- MDEBUG("Unblackballing output " << output);
- dbr = mdb_del(txn, dbi_blackballs, &key, &data);
- if (dbr == MDB_NOTFOUND)
- dbr = 0;
- break;
- case BLACKBALL_QUERY:
- dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor);
- THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr)));
- dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
- THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr)));
- ret = dbr != MDB_NOTFOUND;
- if (dbr == MDB_NOTFOUND)
- dbr = 0;
- mdb_cursor_close(cursor);
- break;
- case BLACKBALL_CLEAR:
- dbr = mdb_drop(txn, dbi_blackballs, 0);
- break;
- default:
- THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Invalid blackball op");
+ dbr = mdb_drop(txn, dbi_blackballs, 0);
+ THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to clear blackballs table: " + std::string(mdb_strerror(dbr)));
}
- THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_commit(txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn blackballing output to database: " + std::string(mdb_strerror(dbr)));
@@ -432,24 +451,32 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op)
return ret;
}
-bool ringdb::blackball(const crypto::public_key &output)
+bool ringdb::blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs)
+{
+ return blackball_worker(outputs, BLACKBALL_BLACKBALL);
+}
+
+bool ringdb::blackball(const std::pair<uint64_t, uint64_t> &output)
{
- return blackball_worker(output, BLACKBALL_BLACKBALL);
+ std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
+ return blackball_worker(outputs, BLACKBALL_BLACKBALL);
}
-bool ringdb::unblackball(const crypto::public_key &output)
+bool ringdb::unblackball(const std::pair<uint64_t, uint64_t> &output)
{
- return blackball_worker(output, BLACKBALL_UNBLACKBALL);
+ std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
+ return blackball_worker(outputs, BLACKBALL_UNBLACKBALL);
}
-bool ringdb::blackballed(const crypto::public_key &output)
+bool ringdb::blackballed(const std::pair<uint64_t, uint64_t> &output)
{
- return blackball_worker(output, BLACKBALL_QUERY);
+ std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
+ return blackball_worker(outputs, BLACKBALL_QUERY);
}
bool ringdb::clear_blackballs()
{
- return blackball_worker(crypto::public_key(), BLACKBALL_CLEAR);
+ return blackball_worker(std::vector<std::pair<uint64_t, uint64_t>>(), BLACKBALL_CLEAR);
}
}
diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h
index 6b4bce124..7b448b0d7 100644
--- a/src/wallet/ringdb.h
+++ b/src/wallet/ringdb.h
@@ -49,13 +49,14 @@ namespace tools
bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
- bool blackball(const crypto::public_key &output);
- bool unblackball(const crypto::public_key &output);
- bool blackballed(const crypto::public_key &output);
+ bool blackball(const std::pair<uint64_t, uint64_t> &output);
+ bool blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs);
+ bool unblackball(const std::pair<uint64_t, uint64_t> &output);
+ bool blackballed(const std::pair<uint64_t, uint64_t> &output);
bool clear_blackballs();
private:
- bool blackball_worker(const crypto::public_key &output, int op);
+ bool blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op);
private:
std::string filename;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index a2e36706e..6502463e2 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -84,8 +84,8 @@ using namespace cryptonote;
// used to choose when to stop adding outputs to a tx
#define APPROXIMATE_INPUT_BYTES 80
-// used to target a given block size (additional outputs may be added on top to build fee)
-#define TX_SIZE_TARGET(bytes) (bytes*2/3)
+// used to target a given block weight (additional outputs may be added on top to build fee)
+#define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
// arbitrary, used to generate different hashes from the same input
#define CHACHA8_KEY_TAIL 0x8c
@@ -161,6 +161,7 @@ struct options {
}
};
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
+ const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
@@ -183,22 +184,24 @@ uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplie
return kB * fee_per_kb * fee_multiplier;
}
-uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, uint64_t fee_multiplier)
+uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{
- return calculate_fee(fee_per_kb, blob.size(), fee_multiplier);
+ uint64_t fee = weight * base_fee * fee_multiplier;
+ fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
+ return fee;
}
-std::string get_size_string(size_t sz)
+std::string get_weight_string(size_t weight)
{
- return std::to_string(sz) + " bytes (" + std::to_string((sz + 1023) / 1024) + " kB)";
+ return std::to_string(weight) + " weight";
}
-std::string get_size_string(const cryptonote::blobdata &tx)
+std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_size)
{
- return get_size_string(tx.size());
+ return get_weight_string(get_transaction_weight(tx, blob_size));
}
-std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
@@ -209,6 +212,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
+ auto device_name = command_line::get_arg(vm, opts.hw_device);
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
@@ -259,10 +263,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
catch (const std::exception &e) { }
}
- std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds));
- wallet->init(rpc, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
+ std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
+ wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
+ wallet->device_name(device_name);
return wallet;
}
@@ -295,7 +300,7 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
}
-std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
@@ -433,7 +438,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error,
tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
- wallet.reset(make_basic(vm, rpc, opts, password_prompter).release());
+ wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
wallet->set_refresh_from_block_height(field_scan_from_height);
wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
@@ -576,7 +581,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// rangeSigs
if (bulletproof)
- size += ((2*6 + 4 + 5)*32 + 3) * n_outputs;
+ {
+ size_t log_padded_outputs = 0;
+ while ((1<<log_padded_outputs) < n_outputs)
+ ++log_padded_outputs;
+ size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
+ }
else
size += (2*64*32+32+64*32) * n_outputs;
@@ -595,23 +605,63 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// txnFee
size += 4;
- LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " with ring size " << (mixin+1) << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
+ LOG_PRINT_L2("estimated " << (bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
return size;
}
size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
{
if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size, bulletproof);
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
else
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
}
+uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
+{
+ size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ if (use_rct && bulletproof && n_outputs > 2)
+ {
+ const uint64_t bp_base = 368;
+ size_t log_padded_outputs = 2;
+ while ((1<<log_padded_outputs) < n_outputs)
+ ++log_padded_outputs;
+ uint64_t nlr = 2 * (6 + log_padded_outputs);
+ const uint64_t bp_size = 32 * (9 + nlr);
+ const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
+ MDEBUG("clawback on size " << size << ": " << bp_clawback);
+ size += bp_clawback;
+ }
+ return size;
+}
+
uint8_t get_bulletproof_fork()
{
return 8;
}
+uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
+{
+ if (use_per_byte_fee)
+ {
+ const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
+ }
+ else
+ {
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
+ }
+}
+
+uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
+{
+ if (use_per_byte_fee)
+ return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
+ else
+ return calculate_fee(base_fee, blob_size, fee_multiplier);
+}
+
crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
{
crypto::hash8 payment_id8 = null_hash8;
@@ -674,8 +724,11 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<too
w(w),
locked(password != boost::none)
{
- if (!locked || w.is_rpc())
+ if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt)
+ {
+ locked = false;
return;
+ }
const epee::wipeable_string pass = password->password();
w.generate_chacha_key_from_password(pass, key);
w.decrypt_keys(key);
@@ -698,7 +751,7 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
w.encrypt_keys(key);
}
-wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
+wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
m_run(true),
@@ -717,7 +770,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
m_explicit_refresh_from_block_height(true),
m_confirm_missing_payment_id(true),
m_confirm_non_default_ring_size(true),
- m_ask_password(true),
+ m_ask_password(AskPasswordToDecrypt),
m_min_output_count(0),
m_min_output_value(0),
m_merge_destinations(false),
@@ -746,7 +799,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
m_ringdb(),
m_last_block_reward(0),
m_encrypt_keys_after_refresh(boost::none),
- m_rpc(false)
+ m_unattended(unattended)
{
}
@@ -764,6 +817,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v
return command_line::get_arg(vm, options().stagenet);
}
+std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
+{
+ return command_line::get_arg(vm, options().hw_device);
+}
+
void wallet2::init_options(boost::program_options::options_description& desc_params)
{
const options opts{};
@@ -779,16 +837,17 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.stagenet);
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
command_line::add_arg(desc_params, opts.kdf_rounds);
+ command_line::add_arg(desc_params, opts.hw_device);
}
-std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- return generate_from_json(json_file, vm, rpc, opts, password_prompter);
+ return generate_from_json(json_file, vm, unattended, opts, password_prompter);
}
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
- const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+ const boost::program_options::variables_map& vm, bool unattended, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
auto pwd = get_password(vm, opts, password_prompter, false);
@@ -796,7 +855,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
{
return {nullptr, password_container{}};
}
- auto wallet = make_basic(vm, rpc, opts, password_prompter);
+ auto wallet = make_basic(vm, unattended, opts, password_prompter);
if (wallet)
{
wallet->load(wallet_file, pwd->password());
@@ -804,7 +863,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
return {std::move(wallet), std::move(*pwd)};
}
-std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
+std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
auto pwd = get_password(vm, opts, password_prompter, true);
@@ -812,24 +871,23 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const
{
return {nullptr, password_container{}};
}
- return {make_basic(vm, rpc, opts, password_prompter), std::move(*pwd)};
+ return {make_basic(vm, unattended, opts, password_prompter), std::move(*pwd)};
}
-std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
+std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- return make_basic(vm, rpc, opts, password_prompter);
+ return make_basic(vm, unattended, opts, password_prompter);
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl, bool trusted_daemon)
+bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon)
{
- m_rpc = rpc;
m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected())
m_http_client.disconnect();
m_is_initialized = true;
- m_upper_transaction_size_limit = upper_transaction_size_limit;
+ m_upper_transaction_weight_limit = upper_transaction_weight_limit;
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
m_trusted_daemon = trusted_daemon;
@@ -934,6 +992,27 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
return true;
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::reconnect_device()
+{
+ bool r = true;
+ hw::device &hwdev = hw::get_device(m_device_name);
+ hwdev.set_name(m_device_name);
+ r = hwdev.init();
+ if (!r){
+ LOG_PRINT_L2("Could not init device");
+ return false;
+ }
+
+ r = hwdev.connect();
+ if (!r){
+ LOG_PRINT_L2("Could not connect to the device");
+ return false;
+ }
+
+ m_account.set_device(hwdev);
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
/*!
* \brief Gets the seed language
*/
@@ -1142,10 +1221,9 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
switch (rv.type)
{
case rct::RCTTypeSimple:
- case rct::RCTTypeSimpleBulletproof:
+ case rct::RCTTypeBulletproof:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull:
- case rct::RCTTypeFullBulletproof:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
default:
LOG_ERROR("Unsupported rct type: " << rv.type);
@@ -1164,7 +1242,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi
THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
// if keys are encrypted, ask for password
- if (m_ask_password && !m_rpc && !m_watch_only && !m_multisig_rescan_k)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k)
{
static critical_section password_lock;
CRITICAL_REGION_LOCAL(password_lock);
@@ -2021,9 +2099,11 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
{
drop_from_short_history(short_chain_history, 3);
+ THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
+
// prepend the last 3 blocks, should be enough to guard against a block or two's reorg
std::vector<parsed_block>::const_reverse_iterator i = prev_parsed_blocks.rbegin();
- for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n)
+ for (size_t n = 0; n < std::min((size_t)3, prev_parsed_blocks.size()); ++n)
{
short_chain_history.push_front(i->hash);
++i;
@@ -2343,6 +2423,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
while (missing_blocks-- > 0)
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
+ m_blockchain.trim(checkpoint_height);
short_chain_history.clear();
get_short_chain_history(short_chain_history);
}
@@ -2554,10 +2635,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
break;
}
- // switch to the new blocks from the daemon
- blocks_start_height = next_blocks_start_height;
- blocks = std::move(next_blocks);
- parsed_blocks = std::move(next_parsed_blocks);
first = false;
// handle error from async fetching thread
@@ -2565,6 +2642,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
{
throw std::runtime_error("proxy exception in refresh thread");
}
+
+ // switch to the new blocks from the daemon
+ blocks_start_height = next_blocks_start_height;
+ blocks = std::move(next_blocks);
+ parsed_blocks = std::move(next_parsed_blocks);
}
catch (const tools::error::password_needed&)
{
@@ -2797,7 +2879,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
crypto::chacha_key key;
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
account.encrypt_viewkey(key);
account.decrypt_keys(key);
@@ -2876,7 +2958,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
- value2.SetInt(m_ask_password ? 1 :0);
+ value2.SetInt(m_ask_password);
json.AddMember("ask_password", value2, json.GetAllocator());
value2.SetUint(m_min_output_count);
@@ -2927,6 +3009,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(1);
json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
+ value.SetString(m_device_name.c_str(), m_device_name.size());
+ json.AddMember("device_name", value, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -2957,7 +3042,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
// re-encrypt, but keep viewkey unencrypted
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
m_account.encrypt_keys(key);
m_account.decrypt_viewkey(key);
@@ -2973,7 +3058,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
//----------------------------------------------------------------------------------------------------
void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
{
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
decrypt_keys(original_password);
setup_keys(new_password);
rewrite(filename, new_password);
@@ -3021,7 +3106,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_refresh_type = RefreshType::RefreshDefault;
m_confirm_missing_payment_id = true;
m_confirm_non_default_ring_size = true;
- m_ask_password = true;
+ m_ask_password = AskPasswordToDecrypt;
m_min_output_count = 0;
m_min_output_value = 0;
m_merge_destinations = false;
@@ -3035,6 +3120,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_ignore_fractional_outputs = true;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
+ m_device_name = "";
m_key_on_device = false;
encrypted_secret_keys = false;
}
@@ -3130,7 +3216,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_confirm_missing_payment_id = field_confirm_missing_payment_id;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, AskPasswordType, Int, false, AskPasswordToDecrypt);
m_ask_password = field_ask_password;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
cryptonote::set_default_decimal_point(field_default_decimal_point);
@@ -3166,8 +3252,15 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
+
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
encrypted_secret_keys = field_encrypted_secret_keys;
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
+ if (m_device_name.empty() && field_device_name_found)
+ {
+ m_device_name = field_device_name;
+ }
}
else
{
@@ -3178,7 +3271,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
r = epee::serialization::load_t_from_binary(m_account, account_data);
if (r && m_key_on_device) {
LOG_PRINT_L0("Account on device. Initing device...");
- hw::device &hwdev = hw::get_device("Ledger");
+ hw::device &hwdev = hw::get_device(m_device_name);
+ hwdev.set_name(m_device_name);
hwdev.init();
hwdev.connect();
m_account.set_device(hwdev);
@@ -3194,7 +3288,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
else
{
// rewrite with encrypted keys, ignore errors
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
encrypt_keys(key);
bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
if (!saved_ret)
@@ -3202,7 +3296,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
// just moan a bit, but not fatal
MERROR("Error saving keys file with encrypted keys, not fatal");
}
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
decrypt_keys(key);
m_keys_file_locker.reset();
}
@@ -3325,6 +3419,31 @@ void wallet2::decrypt_keys(const epee::wipeable_string &password)
decrypt_keys(key);
}
+void wallet2::setup_new_blockchain()
+{
+ cryptonote::block b;
+ generate_genesis(b);
+ m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
+ add_subaddress_account(tr("Primary account"));
+}
+
+void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file)
+{
+ if (!wallet_.empty())
+ {
+ bool r = store_keys(m_keys_file, password, watch_only);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+
+ if (create_address_file)
+ {
+ r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
+ if(!r) MERROR("String with address text not saved");
+ }
+ }
+}
+
+
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -3402,23 +3521,8 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_key_on_device = false;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3462,23 +3566,9 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_refresh_from_block_height = estimate_blockchain_height();
}
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3559,23 +3649,9 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_key_on_device = false;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, true);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file);
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3613,23 +3689,9 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_key_on_device = false;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+ create_keys_file(wallet_, false, password, create_address_file);
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3641,7 +3703,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
* \param password Password of wallet file
* \param device_name device string address
*/
-void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name)
+void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file)
{
clear();
prepare_file_names(wallet_);
@@ -3652,32 +3714,27 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
m_key_on_device = true;
- m_account.create_from_device(device_name);
+
+ auto &hwdev = hw::get_device(device_name);
+ hwdev.set_name(device_name);
+
+ m_account.create_from_device(hwdev);
m_account_public_address = m_account.get_keys().m_account_address;
m_watch_only = false;
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
setup_keys(password);
+ m_device_name = device_name;
- if (!wallet_.empty()) {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
{
// the default lookahead setting (50:200) is clearly too much for hardware wallet
m_subaddress_lookahead_major = 5;
m_subaddress_lookahead_minor = 20;
}
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty()) {
store();
}
@@ -3700,7 +3757,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
// decrypt keys
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
crypto::chacha_key chacha_key;
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
@@ -3773,23 +3830,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
// re-encrypt keys
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
- if (!m_wallet_file.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!m_wallet_file.empty())
store();
@@ -3857,7 +3900,7 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
// keys are decrypted
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
- if (m_ask_password && !m_rpc && !m_watch_only)
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
crypto::chacha_key chacha_key;
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
@@ -3891,17 +3934,7 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
// keys are encrypted again
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
- if (!m_wallet_file.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
m_subaddresses.clear();
m_subaddress_labels.clear();
@@ -4227,7 +4260,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
lock_keys_file();
- wallet_keys_unlocker unlocker(*this, m_ask_password && !m_rpc && !m_watch_only, password);
+ wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
//keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem
@@ -4712,12 +4745,7 @@ void wallet2::rescan_blockchain(bool refresh)
{
clear();
- cryptonote::block genesis;
- generate_genesis(genesis);
- crypto::hash genesis_hash = get_block_hash(genesis);
- m_blockchain.push_back(genesis_hash);
- m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (refresh)
this->refresh(false);
@@ -4754,7 +4782,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig
uint64_t current_time = static_cast<uint64_t>(time(NULL));
// XXX: this needs to be fast, so we'd need to get the starting heights
// from the daemon to be correct once voting kicks in
- uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827;
+ uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827;
uint64_t leeway = block_height < v2height ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2;
if(current_time + leeway >= unlock_time)
return true;
@@ -4947,69 +4975,6 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo
}
//----------------------------------------------------------------------------------------------------
-void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_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)
-{
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx);
-}
-//----------------------------------------------------------------------------------------------------
-void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_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;
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx);
-}
-
-namespace {
-// split_amounts(vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
-//
-// split amount for each dst in dsts into num_splits parts
-// and make num_splits new vector<crypt...> instances to hold these new amounts
-std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
- std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
-{
- std::vector<std::vector<cryptonote::tx_destination_entry>> retVal;
-
- if (num_splits <= 1)
- {
- retVal.push_back(dsts);
- return retVal;
- }
-
- // for each split required
- for (size_t i=0; i < num_splits; i++)
- {
- std::vector<cryptonote::tx_destination_entry> new_dsts;
-
- // for each destination
- for (size_t j=0; j < dsts.size(); j++)
- {
- cryptonote::tx_destination_entry de;
- uint64_t amount;
-
- amount = dsts[j].amount;
- amount = amount / num_splits;
-
- // if last split, add remainder
- if (i + 1 == num_splits)
- {
- amount += dsts[j].amount % num_splits;
- }
-
- de.addr = dsts[j].addr;
- de.amount = amount;
-
- new_dsts.push_back(de);
- }
-
- retVal.push_back(new_dsts);
- }
-
- return retVal;
-}
-} // anonymous namespace
-//----------------------------------------------------------------------------------------------------
crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
{
std::vector<tx_extra_field> tx_extra_fields;
@@ -5268,10 +5233,18 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
+ rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
+ if (sd.use_bulletproofs)
+ {
+ range_proof_type = rct::RangeProofBulletproof;
+ for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
+ if (proof.V.size() > 1)
+ range_proof_type = rct::RangeProofPaddedBulletproof;
+ }
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, sd.use_bulletproofs, m_multisig ? &msout : NULL);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, range_proof_type, m_multisig ? &msout : NULL);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
@@ -5684,7 +5657,15 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
cryptonote::transaction tx;
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
auto sources = sd.sources;
- bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, sd.use_bulletproofs, &msout, false);
+ rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
+ if (sd.use_bulletproofs)
+ {
+ range_proof_type = rct::RangeProofBulletproof;
+ for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
+ if (proof.V.size() > 1)
+ range_proof_type = rct::RangeProofPaddedBulletproof;
+ }
+ bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, range_proof_type, &msout, false);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx),
@@ -5784,9 +5765,18 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
{
- static const uint64_t old_multipliers[3] = {1, 2, 3};
- static const uint64_t new_multipliers[3] = {1, 20, 166};
- static const uint64_t newer_multipliers[4] = {1, 4, 20, 166};
+ static const struct
+ {
+ size_t count;
+ uint64_t multipliers[4];
+ }
+ multipliers[] =
+ {
+ { 3, {1, 2, 3} },
+ { 3, {1, 20, 166} },
+ { 4, {1, 4, 20, 166} },
+ { 4, {1, 5, 25, 1000} },
+ };
if (fee_algorithm == -1)
fee_algorithm = get_fee_algorithm();
@@ -5802,47 +5792,68 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
priority = 1;
}
+ THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 3, error::invalid_priority);
+
// 1 to 3/4 are allowed as priorities
- uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3;
+ const uint32_t max_priority = multipliers[fee_algorithm].count;
if (priority >= 1 && priority <= max_priority)
{
- switch (fee_algorithm)
- {
- case 0: return old_multipliers[priority-1];
- case 1: return new_multipliers[priority-1];
- case 2: return newer_multipliers[priority-1];
- default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority);
- }
+ return multipliers[fee_algorithm].multipliers[priority-1];
}
THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority);
return 1;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_dynamic_per_kb_fee_estimate() const
+uint64_t wallet2::get_dynamic_base_fee_estimate() const
{
uint64_t fee;
- boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_per_kb_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
+ boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
if (!result)
return fee;
- LOG_PRINT_L1("Failed to query per kB fee, using " << print_money(FEE_PER_KB));
- return FEE_PER_KB;
+ const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : FEE_PER_KB;
+ LOG_PRINT_L1("Failed to query base fee, using " << print_money(base_fee));
+ return base_fee;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_per_kb_fee() const
+uint64_t wallet2::get_base_fee() const
{
if(m_light_wallet)
- return m_light_wallet_per_kb_fee;
+ {
+ if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
+ return m_light_wallet_per_kb_fee / 1024;
+ else
+ return m_light_wallet_per_kb_fee;
+ }
bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
if (!use_dyn_fee)
return FEE_PER_KB;
- return get_dynamic_per_kb_fee_estimate();
+ return get_dynamic_base_fee_estimate();
+}
+//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_fee_quantization_mask() const
+{
+ if(m_light_wallet)
+ {
+ return 1; // TODO
+ }
+ bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
+ if (!use_per_byte_fee)
+ return 1;
+
+ uint64_t fee_quantization_mask;
+ boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
+ if (result)
+ return 1;
+ return fee_quantization_mask;
}
//----------------------------------------------------------------------------------------------------
int wallet2::get_fee_algorithm() const
{
- // changes at v3 and v5
+ // changes at v3, v5, v8
+ if (use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0))
+ return 3;
if (use_fork_rules(5, 0))
return 2;
if (use_fork_rules(3, -720 * 14))
@@ -5850,19 +5861,39 @@ int wallet2::get_fee_algorithm() const
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_min_ring_size() const
+{
+ if (use_fork_rules(8, 10))
+ return 11;
+ if (use_fork_rules(7, 10))
+ return 7;
+ if (use_fork_rules(6, 10))
+ return 5;
+ if (use_fork_rules(2, 10))
+ return 3;
+ return 0;
+}
+//------------------------------------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_max_ring_size() const
+{
+ if (use_fork_rules(8, 10))
+ return 11;
+ return 0;
+}
+//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::adjust_mixin(uint64_t mixin) const
{
- if (mixin < 6 && use_fork_rules(7, 10)) {
- MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 7, using 7");
- mixin = 6;
- }
- else if (mixin < 4 && use_fork_rules(6, 10)) {
- MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5");
- mixin = 4;
+ const uint64_t min_ring_size = get_min_ring_size();
+ if (mixin + 1 < min_ring_size)
+ {
+ MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size);
+ mixin = min_ring_size-1;
}
- else if (mixin < 2 && use_fork_rules(2, 10)) {
- MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3");
- mixin = 2;
+ const uint64_t max_ring_size = get_max_ring_size();
+ if (max_ring_size && mixin + 1 > max_ring_size)
+ {
+ MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size);
+ mixin = max_ring_size-1;
}
return mixin;
}
@@ -5874,7 +5905,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
try
{
// check if there's a backlog in the tx pool
- const double fee_level = get_fee_multiplier(1) * get_per_kb_fee() * (12/(double)13) / (double)1024;
+ const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
+ const uint64_t base_fee = get_base_fee();
+ const uint64_t fee_multiplier = get_fee_multiplier(1);
+ const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
if (blocks.size() != 1)
{
@@ -5888,10 +5922,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
}
// get the current full reward zone
- uint64_t block_size_limit = 0;
- const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
+ uint64_t block_weight_limit = 0;
+ const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info");
- const uint64_t full_reward_zone = block_size_limit / 2;
+ const uint64_t full_reward_zone = block_weight_limit / 2;
// get the last N block headers and sum the block sizes
const size_t N = 10;
@@ -5915,14 +5949,14 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
MERROR("Bad blockheaders size");
return priority;
}
- size_t block_size_sum = 0;
+ size_t block_weight_sum = 0;
for (const cryptonote::block_header_response &i : getbh_res.headers)
{
- block_size_sum += i.block_size;
+ block_weight_sum += i.block_weight;
}
// estimate how 'full' the last N blocks are
- const size_t P = 100 * block_size_sum / (N * full_reward_zone);
+ const size_t P = 100 * block_weight_sum / (N * full_reward_zone);
MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
if (P > 80)
{
@@ -5940,115 +5974,6 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
return priority;
}
//----------------------------------------------------------------------------------------------------
-// separated the call(s) to wallet2::transfer into their own function
-//
-// this function will make multiple calls to wallet2::transfer if multiple
-// transactions will be required
-std::vector<wallet2::pending_tx> wallet2::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)
-{
- const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true);
-
- const uint64_t fee_per_kb = get_per_kb_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
-
- // failsafe split attempt counter
- size_t attempt_count = 0;
-
- for(attempt_count = 1; ;attempt_count++)
- {
- size_t num_tx = 0.5 + pow(1.7,attempt_count-1);
-
- auto split_values = split_amounts(dsts, num_tx);
-
- // Throw if split_amounts comes back with a vector of size different than it should
- if (split_values.size() != num_tx)
- {
- throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested");
- }
-
- std::vector<pending_tx> ptx_vector;
- try
- {
- // for each new destination vector (i.e. for each new tx)
- for (auto & dst_vector : split_values)
- {
- cryptonote::transaction tx;
- pending_tx ptx;
-
- // loop until fee is met without increasing tx size to next KB boundary.
- const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size(), false);
- uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
- do
- {
- transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx);
- auto txBlob = t_serializable_object_to_blob(ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- } while (ptx.fee < needed_fee);
-
- ptx_vector.push_back(ptx);
-
- // mark transfers to be used as "spent"
- for(size_t idx: ptx.selected_transfers)
- {
- set_spent(idx, 0);
- }
- }
-
- // if we made it this far, we've selected our transactions. committing them will mark them spent,
- // so this is a failsafe in case they don't go through
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
-
- }
-
- // if we made it this far, we're OK to actually send the transactions
- return ptx_vector;
-
- }
- // only catch this here, other exceptions need to pass through to the calling function
- catch (const tools::error::tx_too_big& e)
- {
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- if (attempt_count >= MAX_SPLIT_ATTEMPTS)
- {
- throw;
- }
- }
- catch (...)
- {
- // in case of some other exception, make sure any tx in queue are marked unspent again
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- throw;
- }
- }
-}
-
bool wallet2::set_ring_database(const std::string &filename)
{
m_ring_database = filename;
@@ -6221,7 +6146,7 @@ bool wallet2::find_and_save_rings(bool force)
return true;
}
-bool wallet2::blackball_output(const crypto::public_key &output)
+bool wallet2::blackball_output(const std::pair<uint64_t, uint64_t> &output)
{
if (!m_ringdb)
return false;
@@ -6229,7 +6154,7 @@ bool wallet2::blackball_output(const crypto::public_key &output)
catch (const std::exception &e) { return false; }
}
-bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add)
+bool wallet2::set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add)
{
if (!m_ringdb)
return false;
@@ -6238,14 +6163,13 @@ bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &out
bool ret = true;
if (!add)
ret &= m_ringdb->clear_blackballs();
- for (const auto &output: outputs)
- ret &= m_ringdb->blackball(output);
+ ret &= m_ringdb->blackball(outputs);
return ret;
}
catch (const std::exception &e) { return false; }
}
-bool wallet2::unblackball_output(const crypto::public_key &output)
+bool wallet2::unblackball_output(const std::pair<uint64_t, uint64_t> &output)
{
if (!m_ringdb)
return false;
@@ -6253,7 +6177,7 @@ bool wallet2::unblackball_output(const crypto::public_key &output)
catch (const std::exception &e) { return false; }
}
-bool wallet2::is_output_blackballed(const crypto::public_key &output) const
+bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const
{
if (!m_ringdb)
return false;
@@ -6298,8 +6222,8 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
return false;
- if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
- return false;
+// if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
+// return false;
outs.back().push_back(item);
return true;
}
@@ -6796,6 +6720,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (seen_indices.count(i))
continue;
+ if (is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs
+ continue;
seen_indices.emplace(i);
LOG_PRINT_L2("picking " << i << " as " << type);
@@ -6818,7 +6744,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, daemon_resp.status);
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " +
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
@@ -6952,7 +6878,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
+ uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee;
LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money));
@@ -7054,10 +6980,10 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout;
LOG_PRINT_L2("constructing tx");
- 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, false, false, m_multisig ? &msout : NULL);
+ 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, false, rct::RangeProofBulletproof, m_multisig ? &msout : NULL);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
- THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
+ THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
std::string key_images;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
@@ -7103,13 +7029,13 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<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, bool bulletproof)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
+ uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee;
LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money));
LOG_PRINT_L2("selected transfers: " << strjoin(selected_transfers, " "));
@@ -7259,10 +7185,10 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
rct::multisig_out msout;
LOG_PRINT_L2("constructing tx");
auto sources_copy = sources;
- 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, true, bulletproof, m_multisig ? &msout : NULL);
+ 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, true, range_proof_type, m_multisig ? &msout : NULL);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
- THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
+ THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
// work out the permutation done on sources
std::vector<size_t> ins_order;
@@ -7296,7 +7222,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
THROW_WALLET_EXCEPTION_IF(selected_transfers.size() != sources.size(), error::wallet_internal_error, "mismatched selected_transfers and sources sixes");
for(size_t idx: selected_transfers)
{
- cryptonote::tx_source_entry& src = sources[src_idx];
+ cryptonote::tx_source_entry& src = sources_copy[src_idx];
src.multisig_kLRki = get_multisig_composite_kLRki(idx, multisig_signers[signer_index], used_L, new_used_L);
++src_idx;
}
@@ -7304,10 +7230,10 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("Creating supplementary multisig transaction");
cryptonote::transaction ms_tx;
auto sources_copy_copy = sources_copy;
- bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, bulletproof, &msout, false);
+ bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, range_proof_type, &msout, false);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
- THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
+ THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix");
multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout});
@@ -7975,11 +7901,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx;
pending_tx ptx;
- size_t bytes;
+ size_t weight;
uint64_t needed_fee;
std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
- TX() : bytes(0), needed_fee(0) {}
+ TX() : weight(0), needed_fee(0) {}
void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
if (merge_destinations)
@@ -8007,12 +7933,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<TX> txes;
bool adding_fee; // true if new outputs go towards fee, rather than destinations
uint64_t needed_fee, available_for_fee = 0;
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
+ uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
+ const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const bool use_rct = use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
- const uint64_t fee_per_kb = get_per_kb_fee();
+ const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
+ const uint64_t fee_quantization_mask = get_fee_quantization_mask();
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
@@ -8043,7 +7972,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// early out if we know we can't make it anyway
// we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through
- const uint64_t min_fee = (fee_multiplier * fee_per_kb * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024;
+ const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024;
uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
@@ -8061,11 +7990,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
// determine threshold for fractional amount
- const size_t tx_size_one_ring = estimate_tx_size(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
- const size_t tx_size_two_rings = estimate_tx_size(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
- THROW_WALLET_EXCEPTION_IF(tx_size_one_ring > tx_size_two_rings, error::wallet_internal_error, "Estimated tx size with 1 input is larger than with 2 inputs!");
- const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring;
- const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024;
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
+ THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
+ const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
+ const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
// gather all dust and non-dust outputs belonging to specified subaddresses
size_t num_nondust_outputs = 0;
@@ -8158,7 +8087,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
- uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size(), bulletproof), fee_multiplier);
+ uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty())
{
@@ -8200,7 +8129,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
TX &tx = txes.back();
- LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size());
+ LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size() << ", tx.dsts.size() " << tx.dsts.size());
LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount)));
@@ -8263,7 +8192,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit))
+ while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
{
// we can fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
@@ -8275,7 +8204,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) {
+ if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
// we can partially fill that destination
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@@ -8287,7 +8216,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
- << upper_transaction_size_limit);
+ << upper_transaction_weight_limit);
bool try_tx = false;
// if we have preferred picks, but haven't yet used all of them, continue
if (preferred_inputs.empty())
@@ -8299,8 +8228,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
- try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
+ const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
+ try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
}
}
@@ -8308,8 +8237,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction test_tx;
pending_tx test_ptx;
- const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
- needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_multiplier);
uint64_t inputs = 0, outputs = needed_fee;
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
@@ -8326,14 +8254,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
tx.selected_transfers.size() << " inputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx, bulletproof);
+ test_tx, test_ptx, range_proof_type);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
- LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
@@ -8369,22 +8297,22 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while (needed_fee > test_ptx.fee) {
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx, bulletproof);
+ test_tx, test_ptx, range_proof_type);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
+ LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
}
- LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
tx.ptx = test_ptx;
- tx.bytes = txBlob.size();
+ tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs;
tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
@@ -8442,7 +8370,7 @@ skip_tx:
extra, /* const std::vector<uint8_t>& extra, */
test_tx, /* OUT cryptonote::transaction& tx, */
test_ptx, /* OUT cryptonote::transaction& tx, */
- bulletproof);
+ range_proof_type);
} else {
transfer_selected(tx.dsts,
tx.selected_transfers,
@@ -8459,7 +8387,7 @@ skip_tx:
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx;
tx.ptx = test_ptx;
- tx.bytes = txBlob.size();
+ tx.weight = get_transaction_weight(test_tx, txBlob.size());
}
std::vector<wallet2::pending_tx> ptx_vector;
@@ -8470,7 +8398,7 @@ skip_tx:
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -8568,21 +8496,24 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx;
pending_tx ptx;
- size_t bytes;
+ size_t weight;
uint64_t needed_fee;
std::vector<std::vector<get_outs_entry>> outs;
- TX() : bytes(0), needed_fee(0) {}
+ TX() : weight(0), needed_fee(0) {}
};
std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0;
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
+ uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
std::vector<std::vector<get_outs_entry>> outs;
+ const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
- const uint64_t fee_per_kb = get_per_kb_fee();
+ const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
+ const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
+ const uint64_t fee_quantization_mask = get_fee_quantization_mask();
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
@@ -8604,7 +8535,25 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// get a random unspent output and use it to pay next chunk. We try to alternate
// dust and non dust to ensure we never get with only dust, from which we might
// get a tx that can't pay for itself
- size_t idx = unused_transfers_indices.empty() ? pop_best_value(unused_dust_indices, tx.selected_transfers) : unused_dust_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_per_kb * fee_multiplier * (upper_transaction_size_limit + 1023) / 1024) ? pop_best_value(unused_dust_indices, tx.selected_transfers) : pop_best_value(unused_transfers_indices, tx.selected_transfers);
+ uint64_t fee_dust_threshold;
+ if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
+ {
+ const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
+ fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
+ }
+ else
+ {
+ fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
+ }
+
+ size_t idx =
+ unused_transfers_indices.empty()
+ ? pop_best_value(unused_dust_indices, tx.selected_transfers)
+ : unused_dust_indices.empty()
+ ? pop_best_value(unused_transfers_indices, tx.selected_transfers)
+ : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold)
+ ? pop_best_value(unused_dust_indices, tx.selected_transfers)
+ : pop_best_value(unused_transfers_indices, tx.selected_transfers);
const transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
@@ -8619,16 +8568,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
- << upper_transaction_size_limit);
- const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size(), bulletproof);
- bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
+ << upper_transaction_weight_limit);
+ const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof);
+ bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
if (try_tx) {
cryptonote::transaction test_tx;
pending_tx test_ptx;
- const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
- needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
@@ -8636,14 +8584,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.selected_transfers.size() << " outputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx, bulletproof);
+ test_tx, test_ptx, range_proof_type);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
- LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
@@ -8653,22 +8601,22 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.dsts[0].amount = available_for_fee - needed_fee;
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx, bulletproof);
+ test_tx, test_ptx, range_proof_type);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
+ LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
} while (needed_fee > test_ptx.fee);
- LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
tx.ptx = test_ptx;
- tx.bytes = txBlob.size();
+ tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs;
tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
@@ -8692,7 +8640,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
pending_tx test_ptx;
if (use_rct) {
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
- test_tx, test_ptx, bulletproof);
+ test_tx, test_ptx, range_proof_type);
} else {
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
@@ -8700,7 +8648,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx;
tx.ptx = test_ptx;
- tx.bytes = txBlob.size();
+ tx.weight = get_transaction_weight(test_tx, txBlob.size());
}
std::vector<wallet2::pending_tx> ptx_vector;
@@ -8711,7 +8659,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -8746,12 +8694,15 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
return close_enough;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_upper_transaction_size_limit() const
+uint64_t wallet2::get_upper_transaction_weight_limit() const
{
- if (m_upper_transaction_size_limit > 0)
- return m_upper_transaction_size_limit;
+ if (m_upper_transaction_weight_limit > 0)
+ return m_upper_transaction_weight_limit;
uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
- return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ if (use_fork_rules(8, 10))
+ return full_reward_zone / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ else
+ return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const
@@ -8857,16 +8808,14 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_unmixable_outputs()
{
- // request all outputs with less than 3 instances
- const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6
- return select_available_outputs_from_histogram(min_mixin + 1, false, true, false);
+ // request all outputs with less instances than the min ring size
+ return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false);
}
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_mixable_outputs()
{
- // request all outputs with at least 3 instances, so we can use mixin 2 with
- const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6
- return select_available_outputs_from_histogram(min_mixin + 1, true, true, true);
+ // request all outputs with at least as many instances as the min ring size
+ return select_available_outputs_from_histogram(get_min_ring_size(), true, true, true);
}
//----------------------------------------------------------------------------------------------------
std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
@@ -8875,7 +8824,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
- const uint64_t fee_per_kb = get_per_kb_fee();
+ const uint64_t base_fee = get_base_fee();
// may throw
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
@@ -8890,7 +8839,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
for (auto n: unmixable_outputs)
{
- if (m_transfers[n].amount() < fee_per_kb)
+ if (m_transfers[n].amount() < base_fee)
unmixable_dust_outputs.push_back(n);
else
unmixable_transfer_outputs.push_back(n);
@@ -9283,6 +9232,8 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
const rct::key C = tx.rct_signatures.outPk[n].mask;
rct::key Ctmp;
+ THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
+ THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount");
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
if (rct::equalKeys(C, Ctmp))
amount = rct::h2d(ecdh_info.amount);
@@ -9570,6 +9521,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
if (account_minreserve)
{
+ THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
// minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
{ return m_transfers[a].amount() > m_transfers[b].amount(); });
@@ -9853,7 +9805,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const
// Calculated blockchain height
uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
// testnet got some huge rollbacks, so the estimation is way off
- static const uint64_t approximate_testnet_rolled_back_blocks = 148540;
+ static const uint64_t approximate_testnet_rolled_back_blocks = 303967;
if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
@@ -10878,7 +10830,12 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
refresh(false);
}
- catch (...) {}
+ catch (...)
+ {
+ m_multisig_rescan_info = NULL;
+ m_multisig_rescan_k = NULL;
+ throw;
+ }
m_multisig_rescan_info = NULL;
m_multisig_rescan_k = NULL;
@@ -11211,46 +11168,46 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
- uint64_t block_size_limit = 0;
- const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
+ uint64_t block_weight_limit = 0;
+ const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info");
- uint64_t full_reward_zone = block_size_limit / 2;
- THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block size limit from daemon");
+ uint64_t full_reward_zone = block_weight_limit / 2;
+ THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon");
std::vector<std::pair<uint64_t, uint64_t>> blocks;
for (const auto &fee_level: fee_levels)
{
const double our_fee_byte_min = fee_level.first;
const double our_fee_byte_max = fee_level.second;
- uint64_t priority_size_min = 0, priority_size_max = 0;
+ uint64_t priority_weight_min = 0, priority_weight_max = 0;
for (const auto &i: res.backlog)
{
- if (i.blob_size == 0)
+ if (i.weight == 0)
{
- MWARNING("Got 0 sized blob from txpool, ignored");
+ MWARNING("Got 0 weight tx from txpool, ignored");
continue;
}
- double this_fee_byte = i.fee / (double)i.blob_size;
+ double this_fee_byte = i.fee / (double)i.weight;
if (this_fee_byte >= our_fee_byte_min)
- priority_size_min += i.blob_size;
+ priority_weight_min += i.weight;
if (this_fee_byte >= our_fee_byte_max)
- priority_size_max += i.blob_size;
+ priority_weight_max += i.weight;
}
- uint64_t nblocks_min = priority_size_min / full_reward_zone;
- uint64_t nblocks_max = priority_size_max / full_reward_zone;
- MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for "
+ uint64_t nblocks_min = priority_weight_min / full_reward_zone;
+ uint64_t nblocks_max = priority_weight_max / full_reward_zone;
+ MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for "
<< our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, "
- << nblocks_min << " - " << nblocks_max << " blocks at block size " << full_reward_zone);
+ << nblocks_min << " - " << nblocks_max << " blocks at block weight " << full_reward_zone);
blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
}
return blocks;
}
//----------------------------------------------------------------------------------------------------
-std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees)
+std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees)
{
- THROW_WALLET_EXCEPTION_IF(min_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
- THROW_WALLET_EXCEPTION_IF(max_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(min_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(max_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
for (uint64_t fee: fees)
{
THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
@@ -11258,7 +11215,7 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t mi
std::vector<std::pair<double, double>> fee_levels;
for (uint64_t fee: fees)
{
- double our_fee_byte_min = fee / (double)min_blob_size, our_fee_byte_max = fee / (double)max_blob_size;
+ double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (double)max_tx_weight;
fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
}
return estimate_backlog(fee_levels);
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 556679f51..c30ca1d85 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -162,28 +162,35 @@ namespace tools
RefreshDefault = RefreshOptimizeCoinbase,
};
+ enum AskPasswordType {
+ AskPasswordNever = 0,
+ AskPasswordOnAction = 1,
+ AskPasswordToDecrypt = 2,
+ };
+
static const char* tr(const char* str);
static bool has_testnet_option(const boost::program_options::variables_map& vm);
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
+ static std::string device_name_option(const boost::program_options::variables_map& vm);
static void init_options(boost::program_options::options_description& desc_params);
//! Uses stdin and stdout. Returns a wallet2 if no errors.
- static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
static std::pair<std::unique_ptr<wallet2>, password_container>
- make_from_file(const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ make_from_file(const boost::program_options::variables_map& vm, bool unattended, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
- static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Just parses variables.
- static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
+ static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
- wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1);
+ wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false);
~wallet2();
struct multisig_info
@@ -544,8 +551,9 @@ namespace tools
* \param wallet_ Name of wallet file
* \param password Password of wallet file
* \param device_name name of HW to use
+ * \param create_address_file Whether to create an address file
*/
- void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name);
+ void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file);
/*!
* \brief Creates a multisig wallet
@@ -630,14 +638,9 @@ namespace tools
void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;}
bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;}
- // upper_transaction_size_limit as defined below is set to
- // approximately 125% of the fixed minimum allowable penalty
- // free block size. TODO: fix this so that it actually takes
- // into account the current median block size rather than
- // the minimum block size.
bool deinit();
- bool init(bool rpc, std::string daemon_address = "http://localhost:8080",
- boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false, bool trusted_daemon = false);
+ bool init(std::string daemon_address = "http://localhost:8080",
+ boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false);
void stop() { m_run.store(false, std::memory_order_relaxed); }
@@ -707,6 +710,7 @@ namespace tools
bool has_unknown_key_images() const;
bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
bool key_on_device() const { return m_key_on_device; }
+ bool reconnect_device();
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
@@ -718,18 +722,12 @@ namespace tools
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);
- 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, cryptonote::transaction& tx, pending_tx& ptx);
- 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);
- 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);
- template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<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::vector<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, bool bulletproof);
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type);
void commit_tx(pending_tx& ptx_vector);
void commit_tx(std::vector<pending_tx>& ptx_vector);
@@ -751,7 +749,6 @@ namespace tools
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
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);
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
- 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);
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); // 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);
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, 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);
@@ -917,8 +914,8 @@ namespace tools
void auto_refresh(bool r) { m_auto_refresh = r; }
bool confirm_missing_payment_id() const { return m_confirm_missing_payment_id; }
void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; }
- bool ask_password() const { return m_ask_password; }
- void ask_password(bool always) { m_ask_password = always; }
+ AskPasswordType ask_password() const { return m_ask_password; }
+ void ask_password(AskPasswordType ask) { m_ask_password = ask; }
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
uint32_t get_min_output_count() const { return m_min_output_count; }
void set_min_output_value(uint64_t value) { m_min_output_value = value; }
@@ -943,6 +940,8 @@ namespace tools
void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; }
bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
+ const std::string & device_name() const { return m_device_name; }
+ void device_name(const std::string & device_name) { m_device_name = device_name; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
@@ -1084,14 +1083,17 @@ namespace tools
bool is_synced() const;
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
- std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees);
+ std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const;
- uint64_t get_per_kb_fee() const;
+ uint64_t get_base_fee() const;
+ uint64_t get_fee_quantization_mask() const;
+ uint64_t get_min_ring_size() const;
+ uint64_t get_max_ring_size() const;
uint64_t adjust_mixin(uint64_t mixin) const;
uint32_t adjust_priority(uint32_t priority);
- bool is_rpc() const { return m_rpc; }
+ bool is_unattended() const { return m_unattended; }
// Light wallet specific functions
// fetch unspent outs from lw node and store in m_transfers
@@ -1161,10 +1163,10 @@ namespace tools
bool set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
bool find_and_save_rings(bool force = true);
- bool blackball_output(const crypto::public_key &output);
- bool set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add = false);
- bool unblackball_output(const crypto::public_key &output);
- bool is_output_blackballed(const crypto::public_key &output) const;
+ bool blackball_output(const std::pair<uint64_t, uint64_t> &output);
+ bool set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add = false);
+ bool unblackball_output(const std::pair<uint64_t, uint64_t> &output);
+ bool is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const;
bool lock_keys_file();
bool unlock_keys_file();
@@ -1212,9 +1214,9 @@ namespace tools
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, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) 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() const;
+ uint64_t get_upper_transaction_weight_limit() const;
std::vector<uint64_t> get_unspent_amounts_vector() const;
- uint64_t get_dynamic_per_kb_fee_estimate() const;
+ uint64_t get_dynamic_base_fee_estimate() const;
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, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
void set_spent(size_t idx, uint64_t height);
@@ -1244,6 +1246,9 @@ namespace tools
void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
+ void setup_new_blockchain();
+ void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
+
cryptonote::account_base m_account;
boost::optional<epee::net_utils::http::login> m_daemon_login;
std::string m_daemon_address;
@@ -1269,7 +1274,7 @@ namespace tools
std::unordered_map<std::string, std::string> m_attributes;
std::vector<tools::wallet2::address_book_row> m_address_book;
std::pair<std::map<std::string, std::string>, std::vector<std::string>> m_account_tags;
- uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
+ uint64_t m_upper_transaction_weight_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info;
const std::vector<std::vector<rct::key>> *m_multisig_rescan_k;
@@ -1302,7 +1307,7 @@ namespace tools
bool m_explicit_refresh_from_block_height;
bool m_confirm_missing_payment_id;
bool m_confirm_non_default_ring_size;
- bool m_ask_password;
+ AskPasswordType m_ask_password;
uint32_t m_min_output_count;
uint64_t m_min_output_value;
bool m_merge_destinations;
@@ -1318,6 +1323,7 @@ namespace tools
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
+ std::string m_device_name;
// Light wallet
bool m_light_wallet; /* sends view key to daemon for scanning */
@@ -1344,7 +1350,7 @@ namespace tools
crypto::chacha_key m_cache_key;
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
- bool m_rpc;
+ bool m_unattended;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 25)
@@ -1819,198 +1825,4 @@ namespace tools
//----------------------------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------------------------
- template<typename T>
- void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_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)
- {
- pending_tx ptx;
- cryptonote::transaction tx;
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx);
- }
-
- template<typename T>
- 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)
- {
- using namespace cryptonote;
- // throw if attempting a transaction with no destinations
- THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
-
- THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
-
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
- uint64_t needed_money = fee;
-
- // calculate total amount being sent to all destinations
- // throw if total amount overflows uint64_t
- for(auto& dt: dsts)
- {
- THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
- needed_money += dt.amount;
- THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype);
- }
-
- // randomly select inputs for transaction
- // throw if requested send amount is greater than (unlocked) amount available to send
- std::vector<size_t> selected_transfers;
- uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers);
- THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_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;
-
- COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
- if(fake_outputs_count)
- {
- COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
- req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key
- for(size_t idx: selected_transfers)
- {
- const transfer_container::const_iterator it = m_transfers.begin() + idx;
- THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
- "m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
- " is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size()));
- req.amounts.push_back(it->amount());
- }
-
- m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_bin("/getrandom_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
- THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error,
- "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " +
- std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size()));
-
- std::unordered_map<uint64_t, uint64_t> scanty_outs;
- for(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs: daemon_resp.outs)
- {
- if (amount_outs.outs.size() < fake_outputs_count)
- {
- scanty_outs[amount_outs.amount] = amount_outs.outs.size();
- }
- }
- THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
- }
-
- //prepare inputs
- size_t i = 0;
- std::vector<cryptonote::tx_source_entry> sources;
- for(size_t idx: selected_transfers)
- {
- sources.resize(sources.size()+1);
- cryptonote::tx_source_entry& src = sources.back();
- const transfer_details& td = m_transfers[idx];
- src.amount = td.amount();
- src.rct = false;
- //paste mixin transaction
- if(daemon_resp.outs.size())
- {
- daemon_resp.outs[i].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;});
- for(out_entry& daemon_oe: daemon_resp.outs[i].outs)
- {
- if(td.m_global_output_index == daemon_oe.global_amount_index)
- continue;
- tx_output_entry oe;
- oe.first = daemon_oe.global_amount_index;
- oe.second.dest = rct::pk2rct(daemon_oe.out_key);
- oe.second.mask = rct::identity();
- src.outputs.push_back(oe);
- if(src.outputs.size() >= fake_outputs_count)
- break;
- }
- }
-
- //paste real transaction to the random index
- auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
- {
- return a.first >= td.m_global_output_index;
- });
- //size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
- tx_output_entry real_oe;
- real_oe.first = td.m_global_output_index;
- real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
- real_oe.second.mask = rct::identity();
- auto interted_it = src.outputs.insert(it_to_insert, real_oe);
- src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
- src.real_output = interted_it - src.outputs.begin();
- src.real_output_in_tx_index = td.m_internal_output_index;
- src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()});
- detail::print_source_entry(src);
- ++i;
- }
-
- cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
- if (needed_money < found_money)
- {
- change_dts.addr = get_subaddress({subaddr_account, 0});
- change_dts.amount = found_money - needed_money;
- }
-
- std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
- uint64_t dust = 0;
- destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
- for(auto& d: dust_dsts) {
- THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
- std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
- }
- 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, d.is_subaddress));
- dust += d.amount;
- }
-
- crypto::secret_key tx_key;
- std::vector<crypto::secret_key> additional_tx_keys;
- rct::multisig_out msout;
- 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, false, false, m_multisig ? &msout : NULL);
- THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
- THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
-
- std::string key_images;
- bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
- {
- CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
- key_images += boost::to_string(in.k_image) + " ";
- return true;
- });
- THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx);
-
- bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key
- || dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key);
-
- if (dust_policy.add_to_fee || dust_sent_elsewhere) change_dts.amount -= dust;
-
- ptx.key_images = key_images;
- ptx.fee = (dust_policy.add_to_fee ? fee+dust : fee);
- ptx.dust = ((dust_policy.add_to_fee || dust_sent_elsewhere) ? dust : 0);
- ptx.dust_added_to_fee = dust_policy.add_to_fee;
- ptx.tx = tx;
- 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;
- ptx.construction_data.splitted_dsts = splitted_dsts;
- ptx.construction_data.selected_transfers = selected_transfers;
- ptx.construction_data.extra = tx.extra;
- ptx.construction_data.unlock_time = unlock_time;
- ptx.construction_data.use_rct = false;
- ptx.construction_data.use_bulletproofs = 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);
- }
-
-
}
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 243953280..bc518d04a 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -73,7 +73,7 @@ namespace tools
// get_tx_pool_error
// out_of_hashchain_bounds_error
// transfer_error *
- // get_random_outs_general_error
+ // get_outs_general_error
// not_enough_unlocked_money
// not_enough_money
// tx_not_possible
@@ -128,7 +128,7 @@ namespace tools
get_blocks_error_message_index,
get_hashes_error_message_index,
get_out_indices_error_message_index,
- get_random_outs_error_message_index
+ get_outs_error_message_index
};
template<typename Base, int msg_index>
@@ -427,7 +427,7 @@ namespace tools
}
};
//----------------------------------------------------------------------------------------------------
- typedef failed_rpc_request<transfer_error, get_random_outs_error_message_index> get_random_outs_error;
+ typedef failed_rpc_request<transfer_error, get_outs_error_message_index> get_outs_error;
//----------------------------------------------------------------------------------------------------
struct not_enough_unlocked_money : public transfer_error
{
@@ -679,30 +679,30 @@ namespace tools
//----------------------------------------------------------------------------------------------------
struct tx_too_big : public transfer_error
{
- explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_size_limit)
+ explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_weight_limit)
: transfer_error(std::move(loc), "transaction is too big")
, m_tx(tx)
- , m_tx_size_limit(tx_size_limit)
+ , m_tx_weight_limit(tx_weight_limit)
{
}
const cryptonote::transaction& tx() const { return m_tx; }
- uint64_t tx_size_limit() const { return m_tx_size_limit; }
+ uint64_t tx_weight_limit() const { return m_tx_weight_limit; }
std::string to_string() const
{
std::ostringstream ss;
cryptonote::transaction tx = m_tx;
ss << transfer_error::to_string() <<
- ", tx_size_limit = " << m_tx_size_limit <<
- ", tx size = " << get_object_blobsize(m_tx) <<
+ ", tx_weight_limit = " << m_tx_weight_limit <<
+ ", tx weight = " << get_transaction_weight(m_tx) <<
", tx:\n" << cryptonote::obj_to_json_str(tx);
return ss.str();
}
private:
cryptonote::transaction m_tx;
- uint64_t m_tx_size_limit;
+ uint64_t m_tx_weight_limit;
};
//----------------------------------------------------------------------------------------------------
struct zero_destination : public transfer_error
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 67f26c7a7..86b46b173 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -51,6 +51,7 @@ using namespace epee;
#include "mnemonics/electrum-words.h"
#include "rpc/rpc_args.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "daemonizer/daemonizer.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
@@ -155,22 +156,18 @@ namespace tools
return false;
m_vm = vm;
- tools::wallet2 *walvars;
- std::unique_ptr<tools::wallet2> tmpwal;
- if (m_wallet)
- walvars = m_wallet;
- else
- {
- tmpwal = tools::wallet2::make_dummy(*m_vm, true, password_prompter);
- walvars = tmpwal.get();
- }
boost::optional<epee::net_utils::http::login> http_login{};
std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port);
const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login);
m_restricted = command_line::get_arg(*m_vm, arg_restricted);
- if (command_line::has_arg(*m_vm, arg_wallet_dir))
+ if (!command_line::is_arg_defaulted(*m_vm, arg_wallet_dir))
{
+ if (!command_line::is_arg_defaulted(*m_vm, wallet_args::arg_wallet_file()))
+ {
+ MERROR(arg_wallet_dir.name << " and " << wallet_args::arg_wallet_file().name << " are incompatible, use only one of them");
+ return false;
+ }
m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir);
#ifdef _WIN32
#define MKDIR(path, mode) mkdir(path)
@@ -763,10 +760,10 @@ namespace tools
{
if (get_tx_key)
{
- std::string s = epee::string_tools::pod_to_hex(ptx.tx_key);
+ epee::wipeable_string s = epee::to_hex::wipeable_string(ptx.tx_key);
for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
- s += epee::string_tools::pod_to_hex(additional_tx_key);
- fill(tx_key, s);
+ s += epee::to_hex::wipeable_string(additional_tx_key);
+ fill(tx_key, std::string(s.data(), s.size()));
}
// Compute amount leaving wallet in tx. By convention dests does not include change outputs
fill(amount, total_amount(ptx));
@@ -1542,7 +1539,6 @@ namespace tools
rpc_transfers.spent = td.m_spent;
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;
rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
res.transfers.push_back(rpc_transfers);
@@ -1574,11 +1570,13 @@ namespace tools
}
else if(req.key_type.compare("view_key") == 0)
{
- res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key);
+ epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_view_secret_key);
+ res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("spend_key") == 0)
{
- res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key);
+ epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
+ res.key = std::string(key.data(), key.size());
}
else
{
@@ -1804,11 +1802,11 @@ namespace tools
return false;
}
- std::ostringstream oss;
- oss << epee::string_tools::pod_to_hex(tx_key);
+ epee::wipeable_string s;
+ s += epee::to_hex::wipeable_string(tx_key);
for (size_t i = 0; i < additional_tx_keys.size(); ++i)
- oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
- res.tx_key = oss.str();
+ s += epee::to_hex::wipeable_string(additional_tx_keys[i]);
+ res.tx_key = std::string(s.data(), s.size());
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1824,26 +1822,33 @@ namespace tools
return false;
}
- std::string tx_key_str = req.tx_key;
+ epee::wipeable_string tx_key_str = req.tx_key;
+ if (tx_key_str.size() < 64 || tx_key_str.size() % 64)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
+ er.message = "Tx key has invalid format";
+ return false;
+ }
+ const char *data = tx_key_str.data();
crypto::secret_key tx_key;
- if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key))
+ if (!epee::wipeable_string(data, 64).hex_to_pod(unwrap(unwrap(tx_key))))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
er.message = "Tx key has invalid format";
return false;
}
- tx_key_str = tx_key_str.substr(64);
+ size_t offset = 64;
std::vector<crypto::secret_key> additional_tx_keys;
- while (!tx_key_str.empty())
+ while (offset < tx_key_str.size())
{
additional_tx_keys.resize(additional_tx_keys.size() + 1);
- if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back()))
+ if (!epee::wipeable_string(data + offset, 64).hex_to_pod(unwrap(unwrap(additional_tx_keys.back()))))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
er.message = "Tx key has invalid format";
return false;
}
- tx_key_str = tx_key_str.substr(64);
+ offset += 64;
}
cryptonote::address_parse_info info;
@@ -2678,8 +2683,20 @@ namespace tools
er.message = "Failed to generate wallet";
return false;
}
+
if (m_wallet)
+ {
+ try
+ {
+ m_wallet->store();
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
delete m_wallet;
+ }
m_wallet = wal.release();
return true;
}
@@ -2736,12 +2753,42 @@ namespace tools
er.message = "Failed to open wallet";
return false;
}
+
if (m_wallet)
+ {
+ try
+ {
+ m_wallet->store();
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
delete m_wallet;
+ }
m_wallet = wal.release();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ try
+ {
+ m_wallet->store();
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ delete m_wallet;
+ m_wallet = NULL;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -3228,12 +3275,172 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
}
+class t_daemon
+{
+private:
+ const boost::program_options::variables_map& vm;
+
+public:
+ t_daemon(boost::program_options::variables_map const & _vm)
+ : vm(_vm)
+ {
+ }
+
+ bool run()
+ {
+ std::unique_ptr<tools::wallet2> wal;
+ try
+ {
+ const bool testnet = tools::wallet2::has_testnet_option(vm);
+ const bool stagenet = tools::wallet2::has_stagenet_option(vm);
+ if (testnet && stagenet)
+ {
+ MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet"));
+ return false;
+ }
+
+ const auto arg_wallet_file = wallet_args::arg_wallet_file();
+ const auto arg_from_json = wallet_args::arg_generate_from_json();
+
+ const auto wallet_file = command_line::get_arg(vm, arg_wallet_file);
+ const auto from_json = command_line::get_arg(vm, arg_from_json);
+ const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir);
+ const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password);
+ const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
+
+ if(!wallet_file.empty() && !from_json.empty())
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json"));
+ return false;
+ }
+
+ if (!wallet_dir.empty())
+ {
+ wal = NULL;
+ goto just_dir;
+ }
+
+ if (wallet_file.empty() && from_json.empty())
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
+ return false;
+ }
+
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
+ if(!wallet_file.empty())
+ {
+ wal = tools::wallet2::make_from_file(vm, true, wallet_file, password_prompt).first;
+ }
+ else
+ {
+ try
+ {
+ wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Error creating wallet: " << e.what());
+ return false;
+ }
+ }
+ if (!wal)
+ {
+ return false;
+ }
+
+ bool quit = false;
+ tools::signal_handler::install([&wal, &quit](int) {
+ assert(wal);
+ quit = true;
+ wal->stop();
+ });
+
+ wal->refresh(wal->is_trusted_daemon());
+ // if we ^C during potentially length load/refresh, there's no server loop yet
+ if (quit)
+ {
+ MINFO(tools::wallet_rpc_server::tr("Saving wallet..."));
+ wal->store();
+ MINFO(tools::wallet_rpc_server::tr("Successfully saved"));
+ return false;
+ }
+ MINFO(tools::wallet_rpc_server::tr("Successfully loaded"));
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
+ return false;
+ }
+ just_dir:
+ tools::wallet_rpc_server wrpc;
+ if (wal) wrpc.set_wallet(wal.release());
+ bool r = wrpc.init(&vm);
+ CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server"));
+ tools::signal_handler::install([&wrpc](int) {
+ wrpc.send_stop_signal();
+ });
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
+ try
+ {
+ wrpc.run();
+ }
+ catch (const std::exception &e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
+ return false;
+ }
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server"));
+ try
+ {
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet..."));
+ wrpc.stop();
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved"));
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what());
+ return false;
+ }
+ return true;
+ }
+};
+
+class t_executor final
+{
+public:
+ static std::string const NAME;
+
+ std::string const & name()
+ {
+ return NAME;
+ }
+
+ t_daemon create_daemon(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm);
+ }
+
+ bool run_non_interactive(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm).run();
+ }
+
+ bool run_interactive(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm).run();
+ }
+};
+
+std::string const t_executor::NAME = "Wallet RPC Daemon";
+
int main(int argc, char** argv) {
namespace po = boost::program_options;
const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_from_json = wallet_args::arg_generate_from_json();
+ po::options_description hidden_options("Hidden");
+
po::options_description desc_params(wallet_args::tr("Wallet options"));
tools::wallet2::init_options(desc_params);
command_line::add_arg(desc_params, arg_rpc_bind_port);
@@ -3245,6 +3452,8 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_wallet_dir);
command_line::add_arg(desc_params, arg_prompt_for_password);
+ daemonizer::init_options(hidden_options, desc_params);
+
boost::optional<po::variables_map> vm;
bool should_terminate = false;
std::tie(vm, should_terminate) = wallet_args::main(
@@ -3266,115 +3475,5 @@ int main(int argc, char** argv) {
return 0;
}
- std::unique_ptr<tools::wallet2> wal;
- try
- {
- const bool testnet = tools::wallet2::has_testnet_option(*vm);
- const bool stagenet = tools::wallet2::has_stagenet_option(*vm);
- if (testnet && stagenet)
- {
- MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet"));
- return 1;
- }
-
- const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file);
- const auto from_json = command_line::get_arg(*vm, arg_from_json);
- const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir);
- const auto prompt_for_password = command_line::get_arg(*vm, arg_prompt_for_password);
- const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
-
- if(!wallet_file.empty() && !from_json.empty())
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json"));
- return 1;
- }
-
- if (!wallet_dir.empty())
- {
- wal = NULL;
- goto just_dir;
- }
-
- if (wallet_file.empty() && from_json.empty())
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
- return 1;
- }
-
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
- if(!wallet_file.empty())
- {
- wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first;
- }
- else
- {
- try
- {
- wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt);
- }
- catch (const std::exception &e)
- {
- MERROR("Error creating wallet: " << e.what());
- return 1;
- }
- }
- if (!wal)
- {
- return 1;
- }
-
- bool quit = false;
- tools::signal_handler::install([&wal, &quit](int) {
- assert(wal);
- quit = true;
- wal->stop();
- });
-
- wal->refresh(wal->is_trusted_daemon());
- // if we ^C during potentially length load/refresh, there's no server loop yet
- if (quit)
- {
- MINFO(tools::wallet_rpc_server::tr("Saving wallet..."));
- wal->store();
- MINFO(tools::wallet_rpc_server::tr("Successfully saved"));
- return 1;
- }
- MINFO(tools::wallet_rpc_server::tr("Successfully loaded"));
- }
- catch (const std::exception& e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
- return 1;
- }
-just_dir:
- tools::wallet_rpc_server wrpc;
- if (wal) wrpc.set_wallet(wal.release());
- bool r = wrpc.init(&(vm.get()));
- CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server"));
- tools::signal_handler::install([&wrpc](int) {
- wrpc.send_stop_signal();
- });
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
- try
- {
- wrpc.run();
- }
- catch (const std::exception &e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
- return 1;
- }
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server"));
- try
- {
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet..."));
- wrpc.stop();
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved"));
- }
- catch (const std::exception& e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what());
- return 1;
- }
- return 0;
+ return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1;
}
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 7525b9dd0..ab7917a78 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -133,6 +133,7 @@ namespace tools
MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES)
MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET)
MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET)
+ MAP_JON_RPC_WE("close_wallet", on_close_wallet, wallet_rpc::COMMAND_RPC_CLOSE_WALLET)
MAP_JON_RPC_WE("change_wallet_password", on_change_wallet_password, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD)
MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG)
MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG)
@@ -209,6 +210,7 @@ namespace tools
bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er);
bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er);
bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er);
+ bool on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er);
bool on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er);
bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 62fb98ebb..4501cf575 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -766,15 +766,9 @@ namespace wallet_rpc
struct response
{
std::string tx_hash;
- std::string tx_key;
- uint64_t fee;
- std::string tx_blob;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
- KV_SERIALIZE(tx_key)
- KV_SERIALIZE(fee)
- KV_SERIALIZE(tx_blob)
END_KV_SERIALIZE_MAP()
};
};
@@ -865,7 +859,6 @@ namespace wallet_rpc
bool spent;
uint64_t global_index;
std::string tx_hash;
- uint64_t tx_size;
uint32_t subaddr_index;
std::string key_image;
@@ -874,7 +867,6 @@ namespace wallet_rpc
KV_SERIALIZE(spent)
KV_SERIALIZE(global_index)
KV_SERIALIZE(tx_hash)
- KV_SERIALIZE(tx_size)
KV_SERIALIZE(subaddr_index)
KV_SERIALIZE(key_image)
END_KV_SERIALIZE_MAP()
@@ -1829,6 +1821,21 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_CLOSE_WALLET
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_CHANGE_WALLET_PASSWORD
{
struct request