Merge branch 'v15_7_js'

Conflicts:
	src/utils/monero_utils.cpp
This commit is contained in:
woodser
2019-12-01 08:51:32 -05:00
11 changed files with 1591 additions and 843 deletions
+14 -6
View File
@@ -8,18 +8,19 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++11 -F/Library/Frameworks -
project(MoneroCppLibrary)
#############
# System
#############
###################
# monero-project
###################
set(MONERO_CORE "external/monero-core")
set(MONERO_CORE_SRC "${MONERO_CORE}/src")
# header includes
include_directories(src/)
include_directories(${MONERO_CORE}/contrib/epee/include)
include_directories(${MONERO_CORE}/external/)
include_directories(${MONERO_CORE}/external/easylogging++)
include_directories("${MONERO_CORE}/contrib/epee/include")
include_directories("${MONERO_CORE}/external/")
include_directories("${MONERO_CORE}/external/easylogging++")
include_directories("${MONERO_CORE}/external/rapidjson/include")
include_directories("${MONERO_CORE_SRC}/")
include_directories("${MONERO_CORE_SRC}/wallet")
include_directories("${MONERO_CORE_SRC}/wallet/api")
@@ -221,6 +222,10 @@ set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero-core/libversion.a)
add_library(randomx STATIC IMPORTED)
set_target_properties(randomx PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero-core/librandomx.a)
########################
# Build c++ library
@@ -278,6 +283,7 @@ target_link_libraries(monero-cpp
device_trezor
multisig
version
randomx
${EXTRA_LIBRARIES}
)
@@ -335,6 +341,7 @@ target_link_libraries(sample_code
device_trezor
multisig
version
randomx
${EXTRA_LIBRARIES}
)
@@ -394,6 +401,7 @@ target_link_libraries(scratchpad
device_trezor
multisig
version
randomx
${EXTRA_LIBRARIES}
)
+2
View File
@@ -6,6 +6,8 @@ In addition, this project conforms to an [API specification](http://moneroecosys
This library may be used to build Monero-related applications, such as GUIs, libraries in other languages (e.g. [monero-java](https://github.com/monero-ecosystem/monero-java)), or a compliant REST/JSON-RPC API.
This library is compatible with [Monero Core v0.15.0.1 Carbon Chamaeleon](https://github.com/monero-project/monero/tree/v0.15.0.1).
## Main Features
- Manage Monero wallets which connect to a daemon
+262 -90
View File
@@ -81,32 +81,66 @@ namespace monero {
// ------------------------- SERIALIZABLE STRUCT ----------------------------
string serializable_struct::serialize() const {
return monero_utils::serialize(to_property_tree());
rapidjson::Document doc;
doc.SetObject();
rapidjson::Value val = to_rapidjson_val(doc.GetAllocator());
val.Swap(doc);
return monero_utils::serialize(doc);
}
// ----------------------------- MONERO VERSION -----------------------------
rapidjson::Value monero_version::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
// create root
rapidjson::Value root(rapidjson::kObjectType);
// set num values
rapidjson::Value value_num(rapidjson::kNumberType);
if (m_version_number != boost::none) monero_utils::addJsonMember("versionNumber", m_version_number.get(), allocator, root, value_num);
// set bool values
if (m_is_release != boost::none) monero_utils::addJsonMember("isRelease", m_is_release.get(), allocator, root);
// return root
return root;
}
// ------------------------- MONERO BLOCK HEADER ----------------------------
boost::property_tree::ptree monero_block_header::to_property_tree() const {
boost::property_tree::ptree node;
if (m_id != boost::none) node.put("id", *m_id);
if (m_height != boost::none) node.put("height", *m_height);
if (m_timestamp != boost::none) node.put("timestamp", *m_timestamp);
if (m_size != boost::none) node.put("size", *m_size);
if (m_weight != boost::none) node.put("weight", *m_weight);
if (m_long_term_weight != boost::none) node.put("longTermWeight", *m_long_term_weight);
if (m_depth != boost::none) node.put("depth", *m_depth);
if (m_difficulty != boost::none) node.put("difficulty", *m_difficulty);
if (m_cumulative_difficulty != boost::none) node.put("cumulativeDifficulty", *m_cumulative_difficulty);
if (m_major_version != boost::none) node.put("majorVersion", *m_major_version);
if (m_minor_version != boost::none) node.put("minorVersion", *m_minor_version);
if (m_nonce != boost::none) node.put("m_nonce", *m_nonce);
if (m_miner_tx_id != boost::none) node.put("minerTxId", *m_miner_tx_id);
if (m_num_txs != boost::none) node.put("numTxs", *m_num_txs);
if (m_orphan_status != boost::none) node.put("orphanStatus", *m_orphan_status);
if (m_prev_id != boost::none) node.put("prevId", *m_prev_id);
if (m_reward != boost::none) node.put("m_reward", *m_reward);
if (m_pow_hash != boost::none) node.put("powHash", *m_pow_hash);
return node;
rapidjson::Value monero_block_header::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
// create root
rapidjson::Value root(rapidjson::kObjectType);
// set num values
rapidjson::Value value_num(rapidjson::kNumberType);
if (m_height != boost::none) monero_utils::addJsonMember("height", m_height.get(), allocator, root, value_num);
if (m_timestamp != boost::none) monero_utils::addJsonMember("timestamp", m_timestamp.get(), allocator, root, value_num);
if (m_size != boost::none) monero_utils::addJsonMember("size", m_size.get(), allocator, root, value_num);
if (m_weight != boost::none) monero_utils::addJsonMember("weight", m_weight.get(), allocator, root, value_num);
if (m_long_term_weight != boost::none) monero_utils::addJsonMember("longTermWeight", m_long_term_weight.get(), allocator, root, value_num);
if (m_depth != boost::none) monero_utils::addJsonMember("depth", m_depth.get(), allocator, root, value_num);
if (m_difficulty != boost::none) monero_utils::addJsonMember("difficulty", m_difficulty.get(), allocator, root, value_num);
if (m_cumulative_difficulty != boost::none) monero_utils::addJsonMember("cumulativeDifficulty", m_cumulative_difficulty.get(), allocator, root, value_num);
if (m_major_version != boost::none) monero_utils::addJsonMember("majorVersion", m_major_version.get(), allocator, root, value_num);
if (m_minor_version != boost::none) monero_utils::addJsonMember("minorVersion", m_minor_version.get(), allocator, root, value_num);
if (m_nonce != boost::none) monero_utils::addJsonMember("nonce", m_nonce.get(), allocator, root, value_num);
if (m_miner_tx_id != boost::none) monero_utils::addJsonMember("minerTxId", m_miner_tx_id.get(), allocator, root, value_num);
if (m_num_txs != boost::none) monero_utils::addJsonMember("numTxs", m_num_txs.get(), allocator, root, value_num);
if (m_reward != boost::none) monero_utils::addJsonMember("reward", m_reward.get(), allocator, root, value_num);
// set string values
rapidjson::Value value_str(rapidjson::kStringType);
if (m_id != boost::none) monero_utils::addJsonMember("id", m_id.get(), allocator, root, value_str);
if (m_prev_id != boost::none) monero_utils::addJsonMember("prevId", m_prev_id.get(), allocator, root, value_str);
if (m_pow_hash != boost::none) monero_utils::addJsonMember("powHash", m_pow_hash.get(), allocator, root, value_str);
// set bool values
if (m_orphan_status != boost::none) monero_utils::addJsonMember("orphanStatus", m_orphan_status.get(), allocator, root);
// return root
return root;
}
void monero_block_header::merge(const shared_ptr<monero_block_header>& self, const shared_ptr<monero_block_header>& other) {
@@ -134,13 +168,24 @@ namespace monero {
// ----------------------------- MONERO BLOCK -------------------------------
boost::property_tree::ptree monero_block::to_property_tree() const {
boost::property_tree::ptree node = monero_block_header::to_property_tree();
if (m_hex != boost::none) node.put("hex", *m_hex);
if (m_miner_tx != boost::none) node.add_child("minerTx", (*m_miner_tx)->to_property_tree());
if (!m_txs.empty()) node.add_child("txs", monero_utils::to_property_tree(m_txs));
if (!m_tx_ids.empty()) node.add_child("txIds", monero_utils::to_property_tree(m_tx_ids));
return node;
rapidjson::Value monero_block::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
// serialize root from superclass
rapidjson::Value root = monero_block_header::to_rapidjson_val(allocator);
// set string values
rapidjson::Value value_str(rapidjson::kStringType);
if (m_hex != boost::none) monero_utils::addJsonMember("hex", m_hex.get(), allocator, root, value_str);
// set sub-arrays
if (!m_txs.empty()) root.AddMember("txs", monero_utils::to_rapidjson_val(allocator, m_txs), allocator);
if (!m_tx_ids.empty()) root.AddMember("txIds", monero_utils::to_rapidjson_val(allocator, m_tx_ids), allocator);
// set sub-objects
if (m_miner_tx != boost::none) root.AddMember("minerTx", m_miner_tx.get()->to_rapidjson_val(allocator), allocator);
// return root
return root;
}
void monero_block::merge(const shared_ptr<monero_block_header>& self, const shared_ptr<monero_block_header>& other) {
@@ -176,6 +221,107 @@ namespace monero {
// ------------------------------- MONERO TX --------------------------------
rapidjson::Value monero_tx::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
// create root
rapidjson::Value root(rapidjson::kObjectType);
// set num values
rapidjson::Value value_num(rapidjson::kNumberType);
if (m_version != boost::none) monero_utils::addJsonMember("version", m_version.get(), allocator, root, value_num);
if (m_fee != boost::none) monero_utils::addJsonMember("fee", m_fee.get(), allocator, root, value_num);
if (m_ring_size != boost::none) monero_utils::addJsonMember("ringSize", m_ring_size.get(), allocator, root, value_num);
if (m_num_confirmations != boost::none) monero_utils::addJsonMember("numConfirmations", m_num_confirmations.get(), allocator, root, value_num);
if (m_unlock_time != boost::none) monero_utils::addJsonMember("unlockTime", m_unlock_time.get(), allocator, root, value_num);
if (m_last_relayed_timestamp != boost::none) monero_utils::addJsonMember("lastRelayedTimestamp", m_last_relayed_timestamp.get(), allocator, root, value_num);
if (m_received_timestamp != boost::none) monero_utils::addJsonMember("receivedTimestamp", m_received_timestamp.get(), allocator, root, value_num);
if (m_size != boost::none) monero_utils::addJsonMember("size", m_size.get(), allocator, root, value_num);
if (m_weight != boost::none) monero_utils::addJsonMember("weight", m_weight.get(), allocator, root, value_num);
if (m_last_failed_height != boost::none) monero_utils::addJsonMember("lastFailedHeight", m_last_failed_height.get(), allocator, root, value_num);
if (m_max_used_block_height != boost::none) monero_utils::addJsonMember("maxUsedBlockHeight", m_max_used_block_height.get(), allocator, root, value_num);
// set string values
rapidjson::Value value_str(rapidjson::kStringType);
if (m_id != boost::none) monero_utils::addJsonMember("id", m_id.get(), allocator, root, value_str);
if (m_payment_id != boost::none) monero_utils::addJsonMember("paymentId", m_payment_id.get(), allocator, root, value_str);
if (m_key != boost::none) monero_utils::addJsonMember("key", m_key.get(), allocator, root, value_str);
if (m_full_hex != boost::none) monero_utils::addJsonMember("fullHex", m_full_hex.get(), allocator, root, value_str);
if (m_pruned_hex != boost::none) monero_utils::addJsonMember("prunedHex", m_pruned_hex.get(), allocator, root, value_str);
if (m_prunable_hex != boost::none) monero_utils::addJsonMember("prunableHex", m_prunable_hex.get(), allocator, root, value_str);
if (m_prunable_hash != boost::none) monero_utils::addJsonMember("prunableHash", m_prunable_hash.get(), allocator, root, value_str);
if (m_metadata != boost::none) monero_utils::addJsonMember("metadata", m_metadata.get(), allocator, root, value_str);
if (m_common_tx_sets != boost::none) monero_utils::addJsonMember("commonTxSets", m_common_tx_sets.get(), allocator, root, value_str);
if (m_rct_signatures != boost::none) monero_utils::addJsonMember("rctSignatures", m_rct_signatures.get(), allocator, root, value_str);
if (m_rct_sig_prunable != boost::none) monero_utils::addJsonMember("rctSigPrunable", m_rct_sig_prunable.get(), allocator, root, value_str);
if (m_last_failed_id != boost::none) monero_utils::addJsonMember("lastFailedId", m_last_failed_id.get(), allocator, root, value_str);
if (m_max_used_block_id != boost::none) monero_utils::addJsonMember("maxUsedBlockId", m_max_used_block_id.get(), allocator, root, value_str);
// set bool values
if (m_is_miner_tx != boost::none) monero_utils::addJsonMember("isMinerTx", m_is_miner_tx.get(), allocator, root);
if (m_do_not_relay != boost::none) monero_utils::addJsonMember("doNotRelay", m_do_not_relay.get(), allocator, root);
if (m_is_relayed != boost::none) monero_utils::addJsonMember("isRelayed", m_is_relayed.get(), allocator, root);
if (m_is_confirmed != boost::none) monero_utils::addJsonMember("isConfirmed", m_is_confirmed.get(), allocator, root);
if (m_in_tx_pool != boost::none) monero_utils::addJsonMember("inTxPool", m_in_tx_pool.get(), allocator, root);
if (m_is_double_spend_seen != boost::none) monero_utils::addJsonMember("isDoubleSpendSeen", m_is_double_spend_seen.get(), allocator, root);
if (m_is_kept_by_block != boost::none) monero_utils::addJsonMember("isKeptByBlock", m_is_kept_by_block.get(), allocator, root);
if (m_is_failed != boost::none) monero_utils::addJsonMember("isFailed", m_is_failed.get(), allocator, root);
// set sub-arrays
if (!m_vins.empty()) root.AddMember("vins", monero_utils::to_rapidjson_val(allocator, m_vins), allocator);
if (!m_vouts.empty()) root.AddMember("vouts", monero_utils::to_rapidjson_val(allocator, m_vouts), allocator);
if (!m_output_indices.empty()) root.AddMember("outputIndices", monero_utils::to_rapidjson_val(allocator, m_output_indices), allocator);
if (!m_extra.empty()) root.AddMember("extra", monero_utils::to_rapidjson_val(allocator, m_extra), allocator);
if (!m_signatures.empty()) root.AddMember("signatures", monero_utils::to_rapidjson_val(allocator, m_signatures), allocator);
// return root
return root;
}
void monero_tx::from_property_tree(const boost::property_tree::ptree& node, shared_ptr<monero_tx> tx) {
// initialize tx from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("id")) tx->m_id = it->second.data();
else if (key == string("version")) throw runtime_error("version deserializationn not implemented");
else if (key == string("isMinerTx")) tx->m_is_miner_tx = it->second.get_value<bool>();
else if (key == string("paymentId")) tx->m_payment_id = it->second.data(); // TODO: this threw runtime_error, how is "Can get transactions by payment ids" test passing in Java?
else if (key == string("fee")) tx->m_fee = it->second.get_value<uint64_t>();
else if (key == string("mixin")) throw runtime_error("mixin deserialization not implemented");
else if (key == string("doNotRelay")) tx->m_do_not_relay = it->second.get_value<bool>();
else if (key == string("isRelayed")) tx->m_is_relayed = it->second.get_value<bool>();
else if (key == string("isConfirmed")) tx->m_is_confirmed = it->second.get_value<bool>();
else if (key == string("inTxPool")) tx->m_in_tx_pool = it->second.get_value<bool>();
else if (key == string("numConfirmations")) tx->m_num_confirmations = it->second.get_value<uint64_t>();
else if (key == string("unlockTime")) tx->m_unlock_time = it->second.get_value<uint64_t>();
else if (key == string("lastRelayedTimestamp")) tx->m_last_relayed_timestamp = it->second.get_value<uint64_t>();
else if (key == string("receivedTimestamp")) tx->m_received_timestamp = it->second.get_value<uint64_t>();
else if (key == string("isDoubleSpendSeen")) tx->m_is_double_spend_seen = it->second.get_value<bool>();
else if (key == string("key")) tx->m_key = it->second.data();
else if (key == string("fullHex")) tx->m_full_hex = it->second.data();
else if (key == string("prunedHex")) tx->m_pruned_hex = it->second.data();
else if (key == string("prunableHex")) tx->m_prunable_hex = it->second.data();
else if (key == string("prunableHash")) tx->m_prunable_hash = it->second.data();
else if (key == string("size")) throw runtime_error("size deserialization not implemented");
else if (key == string("weight")) throw runtime_error("weight deserialization not implemented");
else if (key == string("vins")) throw runtime_error("vins deserializationn not implemented");
else if (key == string("vouts")) throw runtime_error("vouts deserializationn not implemented");
else if (key == string("outputIndices")) throw runtime_error("m_output_indices deserialization not implemented");
else if (key == string("metadata")) tx->m_metadata = it->second.data();
else if (key == string("commonTxSets")) throw runtime_error("commonTxSets deserialization not implemented");
else if (key == string("extra")) throw runtime_error("extra deserialization not implemented");
else if (key == string("rctSignatures")) throw runtime_error("rctSignatures deserialization not implemented");
else if (key == string("rctSigPrunable")) throw runtime_error("rctSigPrunable deserialization not implemented");
else if (key == string("isKeptByBlock")) tx->m_is_kept_by_block = it->second.get_value<bool>();
else if (key == string("isFailed")) tx->m_is_failed = it->second.get_value<bool>();
else if (key == string("lastFailedHeight")) throw runtime_error("lastFailedHeight deserialization not implemented");
else if (key == string("lastFailedId")) tx->m_last_failed_id = it->second.data();
else if (key == string("maxUsedBlockHeight")) throw runtime_error("maxUsedBlockHeight deserialization not implemented");
else if (key == string("maxUsedBlockId")) tx->m_max_used_block_id = it->second.data();
else if (key == string("signatures")) throw runtime_error("signatures deserialization not implemented");
}
}
shared_ptr<monero_tx> monero_tx::copy(const shared_ptr<monero_tx>& src, const shared_ptr<monero_tx>& tgt) const {
MTRACE("monero_tx::copy(const shared_ptr<monero_tx>& src, const shared_ptr<monero_tx>& tgt)");
tgt->m_id = src->m_id;
@@ -183,7 +329,7 @@ namespace monero {
tgt->m_is_miner_tx = src->m_is_miner_tx;
tgt->m_payment_id = src->m_payment_id;
tgt->m_fee = src->m_fee;
tgt->m_mixin = src->m_mixin;
tgt->m_ring_size = src->m_ring_size;
tgt->m_do_not_relay = src->m_do_not_relay;
tgt->m_is_relayed = src->m_is_relayed;
tgt->m_is_confirmed = src->m_is_confirmed;
@@ -232,48 +378,6 @@ namespace monero {
return tgt;
}
boost::property_tree::ptree monero_tx::to_property_tree() const {
boost::property_tree::ptree node;
if (m_id != boost::none) node.put("id", *m_id);
if (m_version != boost::none) node.put("version", *m_version);
if (m_is_miner_tx != boost::none) node.put("isMinerTx", *m_is_miner_tx);
if (m_payment_id != boost::none) node.put("paymentId", *m_payment_id);
if (m_fee != boost::none) node.put("fee", *m_fee);
if (m_mixin != boost::none) node.put("mixin", *m_mixin);
if (m_do_not_relay != boost::none) node.put("doNotRelay", *m_do_not_relay);
if (m_is_relayed != boost::none) node.put("isRelayed", *m_is_relayed);
if (m_is_confirmed != boost::none) node.put("isConfirmed", *m_is_confirmed);
if (m_in_tx_pool != boost::none) node.put("inTxPool", *m_in_tx_pool);
if (m_num_confirmations != boost::none) node.put("numConfirmations", *m_num_confirmations);
if (m_unlock_time != boost::none) node.put("unlockTime", *m_unlock_time);
if (m_last_relayed_timestamp != boost::none) node.put("lastRelayedTimestamp", *m_last_relayed_timestamp);
if (m_received_timestamp != boost::none) node.put("receivedTimestamp", *m_received_timestamp);
if (m_is_double_spend_seen != boost::none) node.put("isDoubleSpendSeen", *m_is_double_spend_seen);
if (m_key != boost::none) node.put("key", *m_key);
if (m_full_hex != boost::none) node.put("fullHex", *m_full_hex);
if (m_pruned_hex != boost::none) node.put("prunedHex", *m_pruned_hex);
if (m_prunable_hex != boost::none) node.put("prunableHex", *m_prunable_hex);
if (m_prunable_hash != boost::none) node.put("prunableHash", *m_prunable_hash);
if (m_size != boost::none) node.put("size", *m_size);
if (m_weight != boost::none) node.put("weight", *m_weight);
if (!m_vins.empty()) node.add_child("vins", monero_utils::to_property_tree(m_vins));
if (!m_vouts.empty()) node.add_child("vouts", monero_utils::to_property_tree(m_vouts));
if (!m_output_indices.empty()) throw runtime_error("m_output_indices not implemented");
if (m_metadata != boost::none) node.put("metadata", *m_metadata);
if (m_common_tx_sets != boost::none) throw runtime_error("m_common_tx_sets not implemented");
if (!m_extra.empty()) node.add_child("extra", monero_utils::to_property_tree(m_extra));
if (m_rct_signatures != boost::none) throw runtime_error("m_rct_signatures not implemented");
if (m_rct_sig_prunable != boost::none) throw runtime_error("m_rct_sig_prunable not implemented");
if (m_is_kept_by_block != boost::none) node.put("isKeptByBlock", *m_is_kept_by_block);
if (m_is_failed != boost::none) node.put("isFailed", *m_is_failed);
if (m_last_failed_height != boost::none) node.put("lastFailedHeight", *m_last_failed_height);
if (m_last_failed_id != boost::none) node.put("lastFailedId", *m_last_failed_id);
if (m_max_used_block_height != boost::none) node.put("maxUsedBlockHeight", *m_max_used_block_height);
if (m_max_used_block_id != boost::none) node.put("maxUsedBlockId", *m_max_used_block_id);
if (!m_signatures.empty()) throw runtime_error("m_signatures not implemented");
return node;
}
boost::optional<uint64_t> monero_tx::get_height() const {
if (m_block == boost::none) return boost::none;
return *((*m_block)->m_height);
@@ -304,7 +408,7 @@ namespace monero {
m_version = gen_utils::reconcile(m_version, other->m_version);
m_payment_id = gen_utils::reconcile(m_payment_id, other->m_payment_id);
m_fee = gen_utils::reconcile(m_fee, other->m_fee, "tx fee");
m_mixin = gen_utils::reconcile(m_mixin, other->m_mixin, "tx m_mixin");
m_ring_size = gen_utils::reconcile(m_ring_size, other->m_ring_size, "tx m_ring_size");
m_is_confirmed = gen_utils::reconcile(m_is_confirmed, other->m_is_confirmed);
m_do_not_relay = gen_utils::reconcile(m_do_not_relay, other->m_do_not_relay);
m_is_relayed = gen_utils::reconcile(m_is_relayed, other->m_is_relayed);
@@ -435,6 +539,53 @@ namespace monero {
// --------------------------- MONERO KEY IMAGE -----------------------------
rapidjson::Value monero_key_image::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
// create root
rapidjson::Value root(rapidjson::kObjectType);
// set string values
rapidjson::Value value_str(rapidjson::kStringType);
if (m_hex != boost::none) monero_utils::addJsonMember("hex", m_hex.get(), allocator, root, value_str);
if (m_signature != boost::none) monero_utils::addJsonMember("signature", m_signature.get(), allocator, root, value_str);
// return root
return root;
}
void monero_key_image::from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_key_image>& key_image) {
// initialize key image from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("hex")) key_image->m_hex = it->second.data();
else if (key == string("signature")) key_image->m_signature = it->second.data();
}
}
vector<shared_ptr<monero_key_image>> monero_key_image::deserialize_key_images(const string& key_images_json) {
// deserialize json to property node
std::istringstream iss = key_images_json.empty() ? std::istringstream() : std::istringstream(key_images_json);
boost::property_tree::ptree node;
boost::property_tree::read_json(iss, node);
// convert property tree to key images
vector<shared_ptr<monero_key_image>> key_images;
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("keyImages")) {
for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) {
shared_ptr<monero_key_image> key_image = make_shared<monero_key_image>();
monero_key_image::from_property_tree(it2->second, key_image);
key_images.push_back(key_image);
}
}
else MWARNING("WARNING MoneroWalletJni::deserialize_key_images() unrecognized key: " << key);
}
return key_images;
}
shared_ptr<monero_key_image> monero_key_image::copy(const shared_ptr<monero_key_image>& src, const shared_ptr<monero_key_image>& tgt) const {
if (this != src.get()) throw runtime_error("this != src");
tgt->m_hex = src->m_hex;
@@ -442,19 +593,50 @@ namespace monero {
return tgt;
}
boost::property_tree::ptree monero_key_image::to_property_tree() const {
boost::property_tree::ptree node;
if (m_hex != boost::none) node.put("hex", *m_hex);
if (m_signature != boost::none) node.put("signature", *m_signature);
return node;
}
void monero_key_image::merge(const shared_ptr<monero_key_image>& self, const shared_ptr<monero_key_image>& other) {
throw runtime_error("Not implemented");
}
// ------------------------------ MONERO OUTPUT -----------------------------
rapidjson::Value monero_output::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
// create root
rapidjson::Value root(rapidjson::kObjectType);
// set num values
rapidjson::Value value_num(rapidjson::kNumberType);
if (m_amount != boost::none) monero_utils::addJsonMember("amount", m_amount.get(), allocator, root, value_num);
if (m_index != boost::none) monero_utils::addJsonMember("index", m_index.get(), allocator, root, value_num);
if (m_stealth_public_key != boost::none) monero_utils::addJsonMember("stealthPublicKey", m_stealth_public_key.get(), allocator, root, value_num);
// set sub-arrays
if (!m_ring_output_indices.empty()) root.AddMember("ringOutputIndices", monero_utils::to_rapidjson_val(allocator, m_ring_output_indices), allocator);
// set sub-objects
if (m_key_image != boost::none) root.AddMember("keyImage", m_key_image.get()->to_rapidjson_val(allocator), allocator);
// return root
return root;
}
void monero_output::from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_output>& output) {
// initialize output from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("keyImage")) {
output->m_key_image = make_shared<monero_key_image>();
monero_key_image::from_property_tree(it->second, output->m_key_image.get());
}
else if (key == string("amount")) output->m_amount = it->second.get_value<uint64_t>();
else if (key == string("index")) output->m_index = it->second.get_value<uint32_t>();
else if (key == string("ringOutputIndices")) throw runtime_error("node_to_tx() deserialize ringOutputIndices not implemented");
else if (key == string("stealthPublicKey")) throw runtime_error("node_to_tx() deserialize stealthPublicKey not implemented");
else cout << "WARNING: unrecognized field deserializing node to output: " << key << endl;
}
}
shared_ptr<monero_output> monero_output::copy(const shared_ptr<monero_output>& src, const shared_ptr<monero_output>& tgt) const {
if (this != src.get()) throw runtime_error("this != src");
tgt->m_tx = src->m_tx; // reference same parent tx by default
@@ -466,16 +648,6 @@ namespace monero {
return tgt;
}
boost::property_tree::ptree monero_output::to_property_tree() const {
boost::property_tree::ptree node;
if (m_key_image != boost::none) node.add_child("keyImage", (*m_key_image)->to_property_tree());
if (m_amount != boost::none) node.put("amount", *m_amount);
if (m_index != boost::none) node.put("index", *m_index);
if (!m_ring_output_indices.empty()) node.add_child("ringOutputIndices", monero_utils::to_property_tree(m_ring_output_indices));
if (m_stealth_public_key != boost::none) node.put("stealthPublicKey", *m_stealth_public_key);
return node;
}
void monero_output::merge(const shared_ptr<monero_output>& self, const shared_ptr<monero_output>& other) {
if (this != self.get()) throw runtime_error("this != self");
if (self == other) return;
+27 -9
View File
@@ -54,6 +54,9 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
using namespace std;
@@ -75,11 +78,12 @@ namespace monero {
string serialize() const;
/**
* Convert the struct to a property tree.
* Converts the struct to a rapidjson Value.
*
* @return the converted property tree
* @param allocator is the rapidjson document allocator
* @return the struct as a rapidjson Value
*/
virtual boost::property_tree::ptree to_property_tree() const = 0;
virtual rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const = 0;
};
/**
@@ -91,6 +95,16 @@ namespace monero {
STAGENET
};
/**
* Models a Monero version.
*/
struct monero_version : public serializable_struct {
boost::optional<uint32_t> m_version_number;
boost::optional<bool> m_is_release;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
* Models a connection to a daemon.
*
@@ -132,7 +146,7 @@ namespace monero {
boost::optional<uint64_t> m_reward;
boost::optional<string> m_pow_hash;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
virtual void merge(const shared_ptr<monero_block_header>& self, const shared_ptr<monero_block_header>& other);
};
@@ -145,7 +159,7 @@ namespace monero {
vector<shared_ptr<monero_tx>> m_txs;
vector<string> m_tx_ids;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
void merge(const shared_ptr<monero_block_header>& self, const shared_ptr<monero_block_header>& other);
void merge(const shared_ptr<monero_block>& self, const shared_ptr<monero_block>& other);
};
@@ -161,7 +175,7 @@ namespace monero {
boost::optional<bool> m_is_miner_tx;
boost::optional<string> m_payment_id;
boost::optional<uint64_t> m_fee;
boost::optional<uint32_t> m_mixin;
boost::optional<uint32_t> m_ring_size;
boost::optional<bool> m_do_not_relay;
boost::optional<bool> m_is_relayed;
boost::optional<bool> m_is_confirmed;
@@ -194,8 +208,9 @@ namespace monero {
boost::optional<string> m_max_used_block_id;
vector<string> m_signatures;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, shared_ptr<monero_tx> tx);
shared_ptr<monero_tx> copy(const shared_ptr<monero_tx>& src, const shared_ptr<monero_tx>& tgt) const;
boost::property_tree::ptree to_property_tree() const;
virtual void merge(const shared_ptr<monero_tx>& self, const shared_ptr<monero_tx>& other);
boost::optional<uint64_t> get_height() const;
};
@@ -207,8 +222,10 @@ namespace monero {
boost::optional<string> m_hex;
boost::optional<string> m_signature;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_key_image>& key_image);
static vector<shared_ptr<monero_key_image>> deserialize_key_images(const string& key_images_json); // TODO: remove this specialty util used once
shared_ptr<monero_key_image> copy(const shared_ptr<monero_key_image>& src, const shared_ptr<monero_key_image>& tgt) const;
boost::property_tree::ptree to_property_tree() const;
void merge(const shared_ptr<monero_key_image>& self, const shared_ptr<monero_key_image>& other);
};
@@ -223,8 +240,9 @@ namespace monero {
vector<uint64_t> m_ring_output_indices;
boost::optional<string> m_stealth_public_key;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_output>& output);
shared_ptr<monero_output> copy(const shared_ptr<monero_output>& src, const shared_ptr<monero_output>& tgt) const;
boost::property_tree::ptree to_property_tree() const;
virtual void merge(const shared_ptr<monero_output>& self, const shared_ptr<monero_output>& other);
};
}
+79 -348
View File
@@ -60,196 +60,7 @@ using namespace std;
using namespace cryptonote;
using namespace monero_utils;
// ------------------------------- INNER HELPERS ------------------------------
void node_to_transfer(const boost::property_tree::ptree& node, shared_ptr<monero_transfer> transfer) {
// initialize transfer from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("accountIndex")) transfer->m_account_index = it->second.get_value<uint32_t>();
}
}
shared_ptr<monero_transfer_query> node_to_transfer_query(const boost::property_tree::ptree& node) {
shared_ptr<monero_transfer_query> m_transfer_query = make_shared<monero_transfer_query>();
node_to_transfer(node, m_transfer_query);
// initialize query from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("isIncoming")) m_transfer_query->m_is_incoming = it->second.get_value<bool>();
else if (key == string("address")) m_transfer_query->m_address = it->second.data();
else if (key == string("addresses")) throw runtime_error("addresses not implemented");
else if (key == string("subaddressIndex")) m_transfer_query->m_subaddress_index = it->second.get_value<uint32_t>();
else if (key == string("subaddressIndices")) {
vector<uint32_t> m_subaddress_indices;
for (const auto& child : it->second) m_subaddress_indices.push_back(child.second.get_value<uint32_t>());
m_transfer_query->m_subaddress_indices = m_subaddress_indices;
}
else if (key == string("destinations")) throw runtime_error("destinations not implemented");
else if (key == string("hasDestinations")) m_transfer_query->m_has_destinations = it->second.get_value<bool>();
else if (key == string("txQuery")) throw runtime_error("txQuery not implemented");
}
return m_transfer_query;
}
shared_ptr<monero_key_image> node_to_key_image(const boost::property_tree::ptree& node) {
// initialize key image from node
shared_ptr<monero_key_image> m_key_image = make_shared<monero_key_image>();
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("hex")) m_key_image->m_hex = it->second.data();
else if (key == string("signature")) m_key_image->m_signature = it->second.data();
}
return m_key_image;
}
void node_to_output(const boost::property_tree::ptree& node, shared_ptr<monero_output> output) {
// initialize output from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("keyImage")) output->m_key_image = node_to_key_image(it->second);
else if (key == string("amount")) output->m_amount = it->second.get_value<uint64_t>();
else if (key == string("index")) output->m_index = it->second.get_value<uint32_t>();
else if (key == string("ringOutputIndices")) throw runtime_error("node_to_tx() deserialize ringOutputIndices not implemented");
else if (key == string("stealthPublicKey")) throw runtime_error("node_to_tx() deserialize stealthPublicKey not implemented");
}
}
void node_to_output_wallet(const boost::property_tree::ptree& node, shared_ptr<monero_output_wallet> output_wallet) {
node_to_output(node, output_wallet);
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("accountIndex")) output_wallet->m_account_index = it->second.get_value<uint32_t>();
else if (key == string("subaddressIndex")) output_wallet->m_subaddress_index = it->second.get_value<uint32_t>();
else if (key == string("isSpent")) output_wallet->m_is_spent = it->second.get_value<bool>();
else if (key == string("isUnlocked")) output_wallet->m_is_unlocked = it->second.get_value<bool>();
else if (key == string("isFrozen")) output_wallet->m_is_frozen = it->second.get_value<bool>();
}
}
shared_ptr<monero_output_query> node_to_output_query(const boost::property_tree::ptree& node) {
shared_ptr<monero_output_query> m_output_query = make_shared<monero_output_query>();
node_to_output_wallet(node, m_output_query);
// initialize query from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("subaddressIndices")) for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) m_output_query->m_subaddress_indices.push_back(it2->second.get_value<uint32_t>());
else if (key == string("txQuery")) {} // ignored
}
return m_output_query;
}
void node_to_tx(const boost::property_tree::ptree& node, shared_ptr<monero_tx> tx) {
// initialize tx from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("id")) tx->m_id = it->second.data();
else if (key == string("version")) throw runtime_error("version deserializationn not implemented");
else if (key == string("isMinerTx")) tx->m_is_miner_tx = it->second.get_value<bool>();
else if (key == string("paymentId")) throw runtime_error("paymentId deserializationn not implemented");
else if (key == string("fee")) throw runtime_error("fee deserialization not implemented");
else if (key == string("mixin")) throw runtime_error("mixin deserialization not implemented");
else if (key == string("doNotRelay")) tx->m_do_not_relay = it->second.get_value<bool>();
else if (key == string("isRelayed")) tx->m_is_relayed = it->second.get_value<bool>();
else if (key == string("isConfirmed")) tx->m_is_confirmed = it->second.get_value<bool>();
else if (key == string("inTxPool")) tx->m_in_tx_pool = it->second.get_value<bool>();
else if (key == string("numConfirmations")) throw runtime_error("numConfirmations deserialization not implemented");
else if (key == string("unlockTime")) throw runtime_error("unlockTime deserialization not implemented");
else if (key == string("lastRelayedTimestamp")) throw runtime_error("lastRelayedTimestamp deserialization not implemented");
else if (key == string("receivedTimestamp")) throw runtime_error("receivedTimestamp deserializationn not implemented");
else if (key == string("isDoubleSpendSeen")) tx->m_is_double_spend_seen = it->second.get_value<bool>();
else if (key == string("key")) tx->m_key = it->second.data();
else if (key == string("fullHex")) tx->m_full_hex = it->second.data();
else if (key == string("prunedHex")) tx->m_pruned_hex = it->second.data();
else if (key == string("prunableHex")) tx->m_prunable_hex = it->second.data();
else if (key == string("prunableHash")) tx->m_prunable_hash = it->second.data();
else if (key == string("size")) throw runtime_error("size deserialization not implemented");
else if (key == string("weight")) throw runtime_error("weight deserialization not implemented");
else if (key == string("vins")) throw runtime_error("vins deserializationn not implemented");
else if (key == string("vouts")) throw runtime_error("vouts deserializationn not implemented");
else if (key == string("outputIndices")) throw runtime_error("m_output_indices deserialization not implemented");
else if (key == string("metadata")) throw runtime_error("metadata deserialization not implemented");
else if (key == string("commonTxSets")) throw runtime_error("commonTxSets deserialization not implemented");
else if (key == string("extra")) throw runtime_error("extra deserialization not implemented");
else if (key == string("rctSignatures")) throw runtime_error("rctSignatures deserialization not implemented");
else if (key == string("rctSigPrunable")) throw runtime_error("rctSigPrunable deserialization not implemented");
else if (key == string("isKeptByBlock")) tx->m_is_kept_by_block = it->second.get_value<bool>();
else if (key == string("isFailed")) tx->m_is_failed = it->second.get_value<bool>();
else if (key == string("lastFailedHeight")) throw runtime_error("lastFailedHeight deserialization not implemented");
else if (key == string("lastFailedId")) tx->m_last_failed_id = it->second.data();
else if (key == string("maxUsedBlockHeight")) throw runtime_error("maxUsedBlockHeight deserialization not implemented");
else if (key == string("maxUsedBlockId")) tx->m_max_used_block_id = it->second.data();
else if (key == string("signatures")) throw runtime_error("signatures deserialization not implemented");
}
}
// TODO: fill this out
void node_to_tx_wallet(const boost::property_tree::ptree& node, shared_ptr<monero_tx_wallet> tx_wallet) {
node_to_tx(node, tx_wallet);
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
//if (key == string("id")) tx->m_id = it->second.data();
}
}
shared_ptr<monero_tx_query> node_to_tx_query(const boost::property_tree::ptree& node) {
shared_ptr<monero_tx_query> m_tx_query = make_shared<monero_tx_query>();
node_to_tx_wallet(node, m_tx_query);
// initialize query from node
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("isOutgoing")) m_tx_query->m_is_outgoing = it->second.get_value<bool>();
else if (key == string("isIncoming")) m_tx_query->m_is_incoming = it->second.get_value<bool>();
else if (key == string("txIds")) for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) m_tx_query->m_tx_ids.push_back(it2->second.data());
else if (key == string("hasPaymentId")) m_tx_query->m_has_payment_id = it->second.get_value<bool>();
else if (key == string("paymentIds")) for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) m_tx_query->m_payment_ids.push_back(it2->second.data());
else if (key == string("height")) m_tx_query->m_height = it->second.get_value<uint64_t>();
else if (key == string("minHeight")) m_tx_query->m_min_height = it->second.get_value<uint64_t>();
else if (key == string("maxHeight")) m_tx_query->m_max_height = it->second.get_value<uint64_t>();
else if (key == string("includeOutputs")) m_tx_query->m_include_outputs = it->second.get_value<bool>();
else if (key == string("transferQuery")) m_tx_query->m_transfer_query = node_to_transfer_query(it->second);
else if (key == string("outputQuery")) m_tx_query->m_output_query = node_to_output_query(it->second);
}
return m_tx_query;
}
shared_ptr<monero_block> node_to_blockquery(const boost::property_tree::ptree& node) {
shared_ptr<monero_block> block = make_shared<monero_block>();
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("txs")) {
boost::property_tree::ptree txsNode = it->second;
for (boost::property_tree::ptree::const_iterator it2 = txsNode.begin(); it2 != txsNode.end(); ++it2) {
block->m_txs.push_back(node_to_tx_query(it2->second));
}
}
}
return block;
}
shared_ptr<monero_destination> node_to_destination(const boost::property_tree::ptree& node) {
shared_ptr<monero_destination> destination = make_shared<monero_destination>();
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("address")) destination->m_address = it->second.data();
else if (key == string("amount")) destination->m_amount = it->second.get_value<uint64_t>();
}
return destination;
}
// ------------------------- PUBLIC STATIC UTILITIES --------------------------
// -------------------------- BINARY SERIALIZATION ----------------------------
void monero_utils::json_to_binary(const std::string &json, std::string &bin) {
epee::serialization::portable_storage ps;
@@ -288,21 +99,21 @@ void monero_utils::binary_blocks_to_json(const std::string &bin, std::string &js
}
// parse and validate txs
boost::property_tree::ptree txsNode;
boost::property_tree::ptree txs_node;
for (int txIdx = 0; txIdx < resp_struct.blocks[blockIdx].txs.size(); txIdx++) {
cryptonote::transaction tx;
if (cryptonote::parse_and_validate_tx_from_blob(resp_struct.blocks[blockIdx].txs[txIdx], tx)) {
if (cryptonote::parse_and_validate_tx_from_blob(resp_struct.blocks[blockIdx].txs[txIdx].blob, tx)) {
// add tx node to txs node
boost::property_tree::ptree txNode;
MTRACE("PRUNED:\n" << monero_utils::get_pruned_tx_json(tx));
txNode.put("", monero_utils::get_pruned_tx_json(tx)); // TODO: no pretty print
txsNode.push_back(std::make_pair("", txNode));
txs_node.push_back(std::make_pair("", txNode));
} else {
throw std::runtime_error("failed to parse tx blob at index " + std::to_string(txIdx));
}
}
txsNodes.push_back(std::make_pair("", txsNode)); // array of array of transactions, one array per block
txsNodes.push_back(std::make_pair("", txs_node)); // array of array of transactions, one array per block
}
root.add_child("blocks", blocksNode);
root.add_child("txs", txsNodes);
@@ -315,135 +126,93 @@ void monero_utils::binary_blocks_to_json(const std::string &bin, std::string &js
json = ss.str();
}
shared_ptr<monero_tx_query> monero_utils::deserialize_tx_query(const string& tx_query_str) {
// ------------------------------- RAPIDJSON ----------------------------------
// deserialize tx query string to property rooted at block
std::istringstream iss = tx_query_str.empty() ? std::istringstream() : std::istringstream(tx_query_str);
boost::property_tree::ptree block_node;
boost::property_tree::read_json(iss, block_node);
// convert query property tree to block
shared_ptr<monero_block> block = node_to_blockquery(block_node);
// get tx query
shared_ptr<monero_tx_query> tx_query = static_pointer_cast<monero_tx_query>(block->m_txs[0]);
// return deserialized query
return tx_query;
string monero_utils::serialize(const rapidjson::Document& doc) {
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
doc.Accept(writer);
return buffer.GetString();
}
shared_ptr<monero_transfer_query> monero_utils::deserialize_transfer_query(const string& transfer_query_str) {
// deserialize transfer query string to property rooted at block
std::istringstream iss = transfer_query_str.empty() ? std::istringstream() : std::istringstream(transfer_query_str);
boost::property_tree::ptree blockNode;
boost::property_tree::read_json(iss, blockNode);
// convert query property tree to block
shared_ptr<monero_block> block = node_to_blockquery(blockNode);
// return mpty query if no txs
if (block->m_txs.empty()) return make_shared<monero_transfer_query>();
// get tx query
shared_ptr<monero_tx_query> tx_query = static_pointer_cast<monero_tx_query>(block->m_txs[0]);
// get / create transfer query
shared_ptr<monero_transfer_query> transfer_query = tx_query->m_transfer_query == boost::none ? make_shared<monero_transfer_query>() : *tx_query->m_transfer_query;
// transfer query references tx query but not the other way around to avoid circular loop // TODO: could add check within meetsCriterias()
transfer_query->m_tx_query = tx_query;
tx_query->m_transfer_query = boost::none;
// return deserialized query
return transfer_query;
void monero_utils::addJsonMember(string key, uint8_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field) {
rapidjson::Value field_key(key.c_str(), key.size(), allocator);
field.SetInt(val);
root.AddMember(field_key, field, allocator);
}
shared_ptr<monero_output_query> monero_utils::deserialize_output_query(const string& output_query_str) {
// deserialize output query string to property rooted at block
std::istringstream iss = output_query_str.empty() ? std::istringstream() : std::istringstream(output_query_str);
boost::property_tree::ptree blockNode;
boost::property_tree::read_json(iss, blockNode);
// convert query property tree to block
shared_ptr<monero_block> block = node_to_blockquery(blockNode);
// empty query if no txs
if (block->m_txs.empty()) return make_shared<monero_output_query>();
// get tx query
shared_ptr<monero_tx_query> tx_query = static_pointer_cast<monero_tx_query>(block->m_txs[0]);
// get / create output query
shared_ptr<monero_output_query> output_query = tx_query->m_output_query == boost::none ? make_shared<monero_output_query>() : *tx_query->m_output_query;
// output query references tx query but not the other way around to avoid circular loop // TODO: could add check within meetsCriterias(), TODO: move this temp hack to the caller
output_query->m_tx_query = tx_query;
tx_query->m_output_query = boost::none;
// return deserialized query
return output_query;
void monero_utils::addJsonMember(string key, uint32_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field) {
rapidjson::Value field_key(key.c_str(), key.size(), allocator);
field.SetUint64(val);
root.AddMember(field_key, field, allocator);
}
shared_ptr<monero_send_request> monero_utils::deserialize_send_request(const string& send_request_str) {
void monero_utils::addJsonMember(string key, uint64_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field) {
rapidjson::Value field_key(key.c_str(), key.size(), allocator);
field.SetUint64(val);
root.AddMember(field_key, field, allocator);
}
// deserialize send request json to property node
std::istringstream iss = send_request_str.empty() ? std::istringstream() : std::istringstream(send_request_str);
boost::property_tree::ptree node;
boost::property_tree::read_json(iss, node);
void monero_utils::addJsonMember(string key, string val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field) {
rapidjson::Value field_key(key.c_str(), key.size(), allocator);
field.SetString(val.c_str(), val.size(), allocator);
root.AddMember(field_key, field, allocator);
}
// convert request property tree to monero_send_request
shared_ptr<monero_send_request> send_request = make_shared<monero_send_request>();
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("destinations")) {
boost::property_tree::ptree destinationsNode = it->second;
for (boost::property_tree::ptree::const_iterator it2 = destinationsNode.begin(); it2 != destinationsNode.end(); ++it2) {
send_request->m_destinations.push_back(node_to_destination(it2->second));
}
}
else if (key == string("paymentId")) send_request->m_payment_id = it->second.data();
else if (key == string("priority")) throw runtime_error("deserialize_send_request() priority not implemented");
else if (key == string("mixin")) send_request->m_mixin = it->second.get_value<uint32_t>();
else if (key == string("ringSize")) send_request->m_ring_size = it->second.get_value<uint32_t>();
else if (key == string("fee")) send_request->m_fee = it->second.get_value<uint64_t>();
else if (key == string("accountIndex")) send_request->m_account_index = it->second.get_value<uint32_t>();
else if (key == string("subaddressIndices")) for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) send_request->m_subaddress_indices.push_back(it2->second.get_value<uint32_t>());
else if (key == string("unlockTime")) send_request->m_unlock_time = it->second.get_value<uint64_t>();
else if (key == string("canSplit")) send_request->m_can_split = it->second.get_value<bool>();
else if (key == string("doNotRelay")) send_request->m_do_not_relay = it->second.get_value<bool>();
else if (key == string("note")) send_request->m_note = it->second.data();
else if (key == string("recipientName")) send_request->m_recipient_name = it->second.data();
else if (key == string("belowAmount")) send_request->m_below_amount = it->second.get_value<uint64_t>();
else if (key == string("sweepEachSubaddress")) send_request->m_sweep_each_subaddress = it->second.get_value<bool>();
else if (key == string("keyImage")) send_request->m_key_image = it->second.data();
void monero_utils::addJsonMember(string key, bool val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root) {
rapidjson::Value field_key(key.c_str(), key.size(), allocator);
if (val) {
rapidjson::Value field_val(rapidjson::kTrueType);
root.AddMember(field_key, field_val, allocator);
} else {
rapidjson::Value field_val(rapidjson::kFalseType);
root.AddMember(field_key, field_val, allocator);
}
return send_request;
}
vector<shared_ptr<monero_key_image>> monero_utils::deserialize_key_images(const string& key_images_json) {
// deserialize json to property node
std::istringstream iss = key_images_json.empty() ? std::istringstream() : std::istringstream(key_images_json);
boost::property_tree::ptree node;
boost::property_tree::read_json(iss, node);
// convert property tree to key images
vector<shared_ptr<monero_key_image>> key_images;
for (boost::property_tree::ptree::const_iterator it = node.begin(); it != node.end(); ++it) {
string key = it->first;
if (key == string("keyImages")) {
for (boost::property_tree::ptree::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) {
key_images.push_back(node_to_key_image(it2->second));
}
}
else MWARNING("WARNING MoneroWalletJni::deserialize_key_images() unrecognized key: " << key);
rapidjson::Value monero_utils::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<string>& strs) {
rapidjson::Value value_arr(rapidjson::kArrayType);
rapidjson::Value value_str(rapidjson::kStringType);
for (const string& str : strs) {
value_str.SetString(str.c_str(), str.size(), allocator);
value_arr.PushBack(value_str, allocator);
}
return key_images;
return value_arr;
}
rapidjson::Value monero_utils::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<uint8_t>& nums) {
rapidjson::Value value_arr(rapidjson::kArrayType);
rapidjson::Value value_num(rapidjson::kNumberType);
for (const auto& num : nums) {
value_num.SetInt(num);
value_arr.PushBack(value_num, allocator);
}
return value_arr;
}
// TODO: remove these redundant implementations for different sizes?
rapidjson::Value monero_utils::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<uint32_t>& nums) {
rapidjson::Value value_arr(rapidjson::kArrayType);
rapidjson::Value value_num(rapidjson::kNumberType);
for (const auto& num : nums) {
value_num.SetUint64(num);
value_arr.PushBack(value_num, allocator);
}
return value_arr;
}
rapidjson::Value monero_utils::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<uint64_t>& nums) {
rapidjson::Value value_arr(rapidjson::kArrayType);
rapidjson::Value value_num(rapidjson::kNumberType);
for (const auto& num : nums) {
value_num.SetUint64(num);
value_arr.PushBack(value_num, allocator);
}
return value_arr;
}
// ------------------------ PROPERTY TREES ---------------------------
string monero_utils::serialize(const boost::property_tree::ptree& node) {
std::stringstream ss;
boost::property_tree::write_json(ss, node, false);
@@ -451,7 +220,6 @@ string monero_utils::serialize(const boost::property_tree::ptree& node) {
return str.substr(0, str.size() - 1); // strip newline
}
// TODO: switch deserializes in this utils class to use this deserialize util
void monero_utils::deserialize(const string& json, boost::property_tree::ptree& root) {
std::istringstream iss = json.empty() ? std::istringstream() : std::istringstream(json);
try {
@@ -461,46 +229,9 @@ void monero_utils::deserialize(const string& json, boost::property_tree::ptree&
}
}
boost::property_tree::ptree monero_utils::to_property_tree(const vector<string>& strs) {
boost::property_tree::ptree strsNode;
for (const auto& str : strs) {
boost::property_tree::ptree strNode;
strNode.put("", str);
strsNode.push_back(std::make_pair("", strNode));
}
return strsNode;
}
// ----------------------------------------------------------------------------
boost::property_tree::ptree monero_utils::to_property_tree(const vector<uint8_t>& nums) {
boost::property_tree::ptree numsNode;
for (const auto& num : nums) {
boost::property_tree::ptree numNode;
numNode.put("", num);
numsNode.push_back(std::make_pair("", numNode));
}
return numsNode;
}
// TODO: remove these redundant implementations for different sizes?
boost::property_tree::ptree monero_utils::to_property_tree(const vector<uint32_t>& nums) {
boost::property_tree::ptree numsNode;
for (const auto& num : nums) {
boost::property_tree::ptree numNode;
numNode.put("", num);
numsNode.push_back(std::make_pair("", numNode));
}
return numsNode;
}
boost::property_tree::ptree monero_utils::to_property_tree(const vector<uint64_t>& nums) {
boost::property_tree::ptree numsNode;
for (const auto& num : nums) {
boost::property_tree::ptree numNode;
numNode.put("", num);
numsNode.push_back(std::make_pair("", numNode));
}
return numsNode;
}
// TODO: this is unused
shared_ptr<monero_block> monero_utils::cn_block_to_block(const cryptonote::block& cn_block) {
cryptonote::block temp = cn_block;
+38 -33
View File
@@ -59,30 +59,60 @@
#include "storages/portable_storage.h"
/**
* Collection of utilities for working with Monero's binary portable storage format.
* Collection of utilities for the Monero library.
*/
namespace monero_utils
{
using namespace std;
using namespace cryptonote;
// ------------------------ BINARY SERIALIZATION ----------------------------
void json_to_binary(const std::string &json, std::string &bin);
void binary_to_json(const std::string &bin, std::string &json);
void binary_blocks_to_json(const std::string &bin, std::string &json);
shared_ptr<monero_tx_query> deserialize_tx_query(const string& txQueryStr);
// ------------------------------ RAPIDJSON ---------------------------------
shared_ptr<monero_transfer_query> deserialize_transfer_query(const string& transfer_query_str);
string serialize(const rapidjson::Document& doc);
shared_ptr<monero_output_query> deserialize_output_query(const string& output_query_str);
void addJsonMember(string key, uint8_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field);
void addJsonMember(string key, uint32_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field);
void addJsonMember(string key, uint64_t val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field);
void addJsonMember(string key, string val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root, rapidjson::Value& field);
void addJsonMember(string key, bool val, rapidjson::Document::AllocatorType& allocator, rapidjson::Value& root);
shared_ptr<monero_send_request> deserialize_send_request(const string& send_request_str);
// TODO: template implementation here, could move to monero_utils.hpp per https://stackoverflow.com/questions/3040480/c-template-function-compiles-in-header-but-not-implementation
template <class T> rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<shared_ptr<T>>& vals) {
rapidjson::Value value_arr(rapidjson::kArrayType);
for (const auto& val : vals) {
value_arr.PushBack(val->to_rapidjson_val(allocator), allocator);
}
return value_arr;
}
vector<shared_ptr<monero_key_image>> deserialize_key_images(const string& key_images_json);
// TODO: template implementation here, could move to monero_utils.hpp per https://stackoverflow.com/questions/3040480/c-template-function-compiles-in-header-but-not-implementation
template <class T> rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<T>& vals) {
rapidjson::Value value_arr(rapidjson::kArrayType);
for (const auto& val : vals) {
value_arr.PushBack(val.to_rapidjson_val(allocator), allocator);
}
return value_arr;
}
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<string>& strs);
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<uint8_t>& nums);
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<uint32_t>& nums);
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator, const vector<uint64_t>& nums);
// ------------------------ PROPERTY TREES ---------------------------
// TODO: fully switch from property trees to rapidjson
string serialize(const boost::property_tree::ptree& node);
void deserialize(const string& json, boost::property_tree::ptree& root);
// --------------------------------------------------------------------------
void deserialize(const string& json, boost::property_tree::ptree& root);
@@ -116,30 +146,5 @@ namespace monero_utils
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to serialize rct signatures base");
return ss.str();
}
// ----------------------------- PROPERTY TREE ------------------------------
// TODO: template implementation here, could move to monero_utils.hpp per https://stackoverflow.com/questions/3040480/c-template-function-compiles-in-header-but-not-implementation
template <class T> boost::property_tree::ptree to_property_tree(const vector<shared_ptr<T>>& types) {
boost::property_tree::ptree typeNodes;
for (const auto& type : types) {
typeNodes.push_back(std::make_pair("", type->to_property_tree()));
}
return typeNodes;
}
// TODO: template implementation here, could move to monero_utils.hpp per https://stackoverflow.com/questions/3040480/c-template-function-compiles-in-header-but-not-implementation
template <class T> boost::property_tree::ptree to_property_tree(const vector<T>& types) {
boost::property_tree::ptree typeNodes;
for (const auto& type : types) {
typeNodes.push_back(std::make_pair("", type.to_property_tree()));
}
return typeNodes;
}
boost::property_tree::ptree to_property_tree(const vector<string>& strs);
boost::property_tree::ptree to_property_tree(const vector<uint8_t>& nums);
boost::property_tree::ptree to_property_tree(const vector<uint32_t>& nums);
boost::property_tree::ptree to_property_tree(const vector<uint64_t>& nums);
}
#endif /* monero_utils_h */
+337 -54
View File
@@ -78,6 +78,7 @@ namespace monero {
static const int DEFAULT_SYNC_INTERVAL_MILLIS = 1000 * 10; // default refresh interval 10 sec
static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 30; // default connection timeout 30 sec
static const bool STRICT = false; // relies exclusively on blockchain data if true, includes local wallet data if false TODO: good use case to expose externally?
// ----------------------- INTERNAL PRIVATE HELPERS -----------------------
@@ -97,10 +98,12 @@ namespace monero {
tx->m_block = block;
block->m_txs.push_back(tx);
tx->m_id = string_tools::pod_to_hex(pd.m_tx_hash);
tx->m_is_incoming = true;
tx->m_payment_id = string_tools::pod_to_hex(payment_id);
if (tx->m_payment_id->substr(16).find_first_not_of('0') == std::string::npos) tx->m_payment_id = tx->m_payment_id->substr(0, 16); // TODO monero core: this should be part of core wallet
if (tx->m_payment_id == monero_tx::DEFAULT_PAYMENT_ID) tx->m_payment_id = boost::none; // clear default payment id
tx->m_unlock_time = pd.m_unlock_time;
tx->m_is_locked = !m_w2.is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
tx->m_fee = pd.m_fee;
tx->m_note = m_w2.get_tx_note(pd.m_tx_hash);
if (tx->m_note->empty()) tx->m_note = boost::none; // clear empty note
@@ -148,10 +151,12 @@ namespace monero {
tx->m_block = block;
block->m_txs.push_back(tx);
tx->m_id = string_tools::pod_to_hex(txid);
tx->m_is_outgoing = true;
tx->m_payment_id = string_tools::pod_to_hex(pd.m_payment_id);
if (tx->m_payment_id->substr(16).find_first_not_of('0') == std::string::npos) tx->m_payment_id = tx->m_payment_id->substr(0, 16); // TODO monero core: this should be part of core wallet
if (tx->m_payment_id == monero_tx::DEFAULT_PAYMENT_ID) tx->m_payment_id = boost::none; // clear default payment id
tx->m_unlock_time = pd.m_unlock_time;
tx->m_is_locked = !m_w2.is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
tx->m_fee = pd.m_amount_in - pd.m_amount_out;
tx->m_note = m_w2.get_tx_note(txid);
if (tx->m_note->empty()) tx->m_note = boost::none; // clear empty note
@@ -187,7 +192,7 @@ namespace monero {
for (const auto &d: pd.m_dests) {
shared_ptr<monero_destination> destination = make_shared<monero_destination>();
destination->m_amount = d.amount;
destination->m_address = d.original.empty() ? cryptonote::get_account_address_as_str(m_w2.nettype(), d.is_subaddress, d.addr) : d.original;
destination->m_address = d.address(m_w2.nettype(), pd.m_payment_id);
outgoing_transfer->m_destinations.push_back(destination);
}
@@ -214,10 +219,12 @@ namespace monero {
const tools::wallet2::payment_details &pd = ppd.m_pd;
shared_ptr<monero_tx_wallet> tx = make_shared<monero_tx_wallet>();
tx->m_id = string_tools::pod_to_hex(pd.m_tx_hash);
tx->m_is_incoming = true;
tx->m_payment_id = string_tools::pod_to_hex(payment_id);
if (tx->m_payment_id->substr(16).find_first_not_of('0') == std::string::npos) tx->m_payment_id = tx->m_payment_id->substr(0, 16); // TODO monero core: this should be part of core wallet
if (tx->m_payment_id == monero_tx::DEFAULT_PAYMENT_ID) tx->m_payment_id = boost::none; // clear default payment id
tx->m_unlock_time = pd.m_unlock_time;
tx->m_is_locked = true;
tx->m_fee = pd.m_fee;
tx->m_note = m_w2.get_tx_note(pd.m_tx_hash);
if (tx->m_note->empty()) tx->m_note = boost::none; // clear empty note
@@ -254,10 +261,12 @@ namespace monero {
shared_ptr<monero_tx_wallet> tx = make_shared<monero_tx_wallet>();
tx->m_is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
tx->m_id = string_tools::pod_to_hex(txid);
tx->m_is_outgoing = true;
tx->m_payment_id = string_tools::pod_to_hex(pd.m_payment_id);
if (tx->m_payment_id->substr(16).find_first_not_of('0') == std::string::npos) tx->m_payment_id = tx->m_payment_id->substr(0, 16); // TODO monero core: this should be part of core wallet
if (tx->m_payment_id == monero_tx::DEFAULT_PAYMENT_ID) tx->m_payment_id = boost::none; // clear default payment id
tx->m_unlock_time = pd.m_tx.unlock_time;
tx->m_is_locked = true;
tx->m_fee = pd.m_amount_in - pd.m_amount_out;
tx->m_note = m_w2.get_tx_note(txid);
if (tx->m_note->empty()) tx->m_note = boost::none; // clear empty note
@@ -288,7 +297,7 @@ namespace monero {
for (const auto &d: pd.m_dests) {
shared_ptr<monero_destination> destination = make_shared<monero_destination>();
destination->m_amount = d.amount;
destination->m_address = d.original.empty() ? cryptonote::get_account_address_as_str(m_w2.nettype(), d.is_subaddress, d.addr) : d.original;
destination->m_address = d.address(m_w2.nettype(), pd.m_payment_id);
outgoing_transfer->m_destinations.push_back(destination);
}
@@ -326,6 +335,7 @@ namespace monero {
tx->m_in_tx_pool = false;
tx->m_do_not_relay = false;
tx->m_is_double_spend_seen = false;
tx->m_is_locked = !m_w2.is_transfer_unlocked(td);
// construct vout
shared_ptr<monero_output_wallet> vout = make_shared<monero_output_wallet>();
@@ -336,7 +346,6 @@ namespace monero {
vout->m_account_index = td.m_subaddr_index.major;
vout->m_subaddress_index = td.m_subaddr_index.minor;
vout->m_is_spent = td.m_spent;
vout->m_is_unlocked = m_w2.is_transfer_unlocked(td);
vout->m_is_frozen = td.m_frozen;
if (td.m_key_image_known) {
vout->m_key_image = make_shared<monero_key_image>();
@@ -533,30 +542,9 @@ namespace monero {
if (!payment_id.empty())
{
/* Just to clarify */
const std::string& payment_id_str = payment_id;
crypto::hash long_payment_id;
crypto::hash8 short_payment_id;
/* Parse payment ID */
if (wallet2::parse_long_payment_id(payment_id_str, long_payment_id)) {
cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, long_payment_id);
}
else {
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64 character string";
return false;
}
/* Append Payment ID data into extra */
if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string";
return false;
}
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Standalone payment IDs are obsolete. Use subaddresses or integrated addresses instead";
return false;
}
return true;
}
@@ -898,7 +886,7 @@ namespace monero {
// validate and parse private view key
bool has_view_key = true;
crypto::secret_key view_key_sk;
if (!view_key.empty()) {
if (view_key.empty()) {
if (has_spend_key) has_view_key = false;
else throw runtime_error("Neither view key nor spend key supplied, cancelled");
}
@@ -997,6 +985,13 @@ namespace monero {
return m_is_synced;
}
monero_version monero_wallet::get_version() const {
monero_version version;
version.m_version_number = 65552; // same as monero-wallet-rpc v0.15.0.1 release
version.m_is_release = false; // TODO: could pull from MONERO_VERSION_IS_RELEASE in version.cpp
return version;
}
string monero_wallet::get_path() const {
return m_w2->path();
}
@@ -1249,29 +1244,29 @@ namespace monero {
// isMultisigImportNeeded
uint64_t monero_wallet::get_balance() const {
return m_w2->balance_all();
return m_w2->balance_all(STRICT);
}
uint64_t monero_wallet::get_balance(uint32_t account_idx) const {
return m_w2->balance(account_idx);
return m_w2->balance(account_idx, STRICT);
}
uint64_t monero_wallet::get_balance(uint32_t account_idx, uint32_t subaddress_idx) const {
map<uint32_t, uint64_t> balance_per_subaddress = m_w2->balance_per_subaddress(account_idx);
map<uint32_t, uint64_t> balance_per_subaddress = m_w2->balance_per_subaddress(account_idx, STRICT);
auto iter = balance_per_subaddress.find(subaddress_idx);
return iter == balance_per_subaddress.end() ? 0 : iter->second;
}
uint64_t monero_wallet::get_unlocked_balance() const {
return m_w2->unlocked_balance_all();
return m_w2->unlocked_balance_all(STRICT);
}
uint64_t monero_wallet::get_unlocked_balance(uint32_t account_idx) const {
return m_w2->unlocked_balance(account_idx);
return m_w2->unlocked_balance(account_idx, STRICT);
}
uint64_t monero_wallet::get_unlocked_balance(uint32_t account_idx, uint32_t subaddress_idx) const {
map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_w2->unlocked_balance_per_subaddress(account_idx);
map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_w2->unlocked_balance_per_subaddress(account_idx, STRICT);
auto iter = unlocked_balance_per_subaddress.find(subaddress_idx);
return iter == unlocked_balance_per_subaddress.end() ? 0 : iter->second.first;
}
@@ -1304,8 +1299,8 @@ namespace monero {
monero_account account;
account.m_index = account_idx;
account.m_primary_address = get_address(account_idx, 0);
account.m_balance = m_w2->balance(account_idx);
account.m_unlocked_balance = m_w2->unlocked_balance(account_idx);
account.m_balance = m_w2->balance(account_idx, STRICT);
account.m_unlocked_balance = m_w2->unlocked_balance(account_idx, STRICT);
if (include_subaddresses) account.m_subaddresses = get_subaddresses_aux(account_idx, vector<uint32_t>(), transfers);
accounts.push_back(account);
}
@@ -1328,8 +1323,8 @@ namespace monero {
monero_account account;
account.m_index = account_idx;
account.m_primary_address = get_address(account_idx, 0);
account.m_balance = m_w2->balance(account_idx);
account.m_unlocked_balance = m_w2->unlocked_balance(account_idx);
account.m_balance = m_w2->balance(account_idx, STRICT);
account.m_unlocked_balance = m_w2->unlocked_balance(account_idx, STRICT);
if (include_subaddresses) account.m_subaddresses = get_subaddresses_aux(account_idx, vector<uint32_t>(), transfers);
return account;
}
@@ -1522,7 +1517,7 @@ namespace monero {
if (query.m_tx_query.get()->m_transfer_query != boost::none && query.m_tx_query.get()->m_transfer_query.get().get() == &query) {
_query = tx_query->m_transfer_query.get();
} else {
if (query.m_tx_query.get()->m_transfer_query != boost::none) throw new runtime_error("Transfer query's tx query must be a circular reference or null");
if (query.m_tx_query.get()->m_transfer_query != boost::none) throw runtime_error("Transfer query's tx query must be a circular reference or null");
shared_ptr<monero_transfer_query> query_ptr = make_shared<monero_transfer_query>(query); // convert query to shared pointer for copy
_query = query_ptr->copy(query_ptr, make_shared<monero_transfer_query>());
_query->m_tx_query = tx_query;
@@ -1592,7 +1587,12 @@ namespace monero {
// get unconfirmed incoming transfers
if (is_pool) {
m_w2->update_pool_state(); // TODO monero-core: this should be encapsulated in wallet when unconfirmed transfers queried
// update pool state TODO monero-core: this should be encapsulated in wallet when unconfirmed transfers queried
std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
m_w2->update_pool_state(process_txs);
if (!process_txs.empty()) m_w2->process_pool_state(process_txs);
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
m_w2->get_unconfirmed_payments(payments, account_index, subaddress_indices);
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
@@ -1612,6 +1612,10 @@ namespace monero {
vector<shared_ptr<monero_transfer>> transfers;
for (const shared_ptr<monero_tx_wallet>& tx : txs) {
// tx is not incoming/outgoing unless already set
if (tx->m_is_incoming == boost::none) tx->m_is_incoming = false;
if (tx->m_is_outgoing == boost::none) tx->m_is_outgoing = false;
// sort transfers
sort(tx->m_incoming_transfers.begin(), tx->m_incoming_transfers.end(), incoming_transfer_before);
@@ -1660,7 +1664,7 @@ namespace monero {
if (query.m_tx_query.get()->m_output_query != boost::none && query.m_tx_query.get()->m_output_query.get().get() == &query) {
_query = tx_query->m_output_query.get();
} else {
if (query.m_tx_query.get()->m_output_query != boost::none) throw new runtime_error("Output query's tx query must be a circular reference or null");
if (query.m_tx_query.get()->m_output_query != boost::none) throw runtime_error("Output query's tx query must be a circular reference or null");
shared_ptr<monero_output_query> query_ptr = make_shared<monero_output_query>(query); // convert query to shared pointer for copy
_query = query_ptr->copy(query_ptr, make_shared<monero_output_query>());
_query->m_tx_query = tx_query;
@@ -1903,7 +1907,7 @@ namespace monero {
std::vector<uint8_t> extra;
epee::json_rpc::error err;
if (!validate_transfer(m_w2.get(), tr_destinations, payment_id, dsts, extra, true, err)) {
throw runtime_error("Need to handle send_split() validate_transfer error"); // TODO
throw runtime_error(err.message);
}
// prepare parameters for wallet2's create_transactions_2()
@@ -1965,6 +1969,7 @@ namespace monero {
out_transfer->m_amount = *tx_amounts_iter;
// init other known fields
tx->m_is_outgoing = true;
tx->m_payment_id = request.m_payment_id;
tx->m_is_confirmed = false;
tx->m_is_miner_tx = false;
@@ -1974,8 +1979,9 @@ namespace monero {
tx->m_in_tx_pool = !tx->m_do_not_relay.get();
if (!tx->m_is_failed.get() && tx->m_is_relayed.get()) tx->m_is_double_spend_seen = false; // TODO: test and handle if true
tx->m_num_confirmations = 0;
tx->m_mixin = request.m_mixin;
tx->m_ring_size = request.m_ring_size;
tx->m_unlock_time = request.m_unlock_time == boost::none ? 0 : request.m_unlock_time.get();
tx->m_is_locked = true;
if (tx->m_is_relayed.get()) tx->m_last_relayed_timestamp = static_cast<uint64_t>(time(NULL)); // set last relayed timestamp to current time iff relayed // TODO monero core: this should be encapsulated in wallet2
out_transfer->m_account_index = request.m_account_index;
if (request.m_subaddress_indices.size() == 1) out_transfer->m_subaddress_indices.push_back(request.m_subaddress_indices[0]); // subaddress index is known iff 1 requested // TODO: get all known subaddress indices here
@@ -2130,6 +2136,8 @@ namespace monero {
shared_ptr<monero_tx_wallet> tx = make_shared<monero_tx_wallet>();
txs.push_back(tx);
tx->m_id = *tx_ids_iter;
tx->m_is_locked = true;
tx->m_is_outgoing = true;
tx->m_key = *tx_keys_iter;
tx->m_fee = *tx_fees_iter;
tx->m_full_hex = *tx_blobs_iter;
@@ -2150,7 +2158,7 @@ namespace monero {
tx->m_in_tx_pool = !do_not_relay;
if (!tx->m_is_failed.get() && tx->m_is_relayed.get()) tx->m_is_double_spend_seen = false; // TODO: test and handle if true
tx->m_num_confirmations = 0;
tx->m_mixin = request.m_mixin;
tx->m_ring_size = request.m_ring_size;
tx->m_unlock_time = request.m_unlock_time == boost::none ? 0 : request.m_unlock_time.get();
if (tx->m_is_relayed.get()) tx->m_last_relayed_timestamp = static_cast<uint64_t>(time(NULL)); // set last relayed timestamp to current time iff relayed // TODO monero core: this should be encapsulated in wallet2
out_transfer->m_account_index = request.m_account_index;
@@ -2202,10 +2210,10 @@ namespace monero {
}
// create transaction
uint64_t m_mixin = m_w2->adjust_mixin(request.m_ring_size == boost::none ? 0 : request.m_ring_size.get() - 1);
uint64_t mixin = m_w2->adjust_mixin(request.m_ring_size == boost::none ? 0 : request.m_ring_size.get() - 1);
uint32_t priority = m_w2->adjust_priority(request.m_priority == boost::none ? 0 : request.m_priority.get());
uint64_t m_unlock_time = request.m_unlock_time == boost::none ? 0 : request.m_unlock_time.get();
std::vector<wallet2::pending_tx> ptx_vector = m_w2->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, 1, m_mixin, m_unlock_time, priority, extra);
uint64_t unlock_time = request.m_unlock_time == boost::none ? 0 : request.m_unlock_time.get();
std::vector<wallet2::pending_tx> ptx_vector = m_w2->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, 1, mixin, unlock_time, priority, extra);
// validate created transaction
if (ptx_vector.empty()) throw runtime_error("No outputs found");
@@ -2246,6 +2254,7 @@ namespace monero {
shared_ptr<monero_tx_wallet> tx = make_shared<monero_tx_wallet>();
txs.push_back(tx);
tx->m_id = *tx_ids_iter;
tx->m_is_outgoing = true;
tx->m_key = *tx_keys_iter;
tx->m_fee = *tx_fees_iter;
tx->m_full_hex = *tx_blobs_iter;
@@ -2264,8 +2273,9 @@ namespace monero {
tx->m_in_tx_pool = !tx->m_do_not_relay.get();
if (!tx->m_is_failed.get() && tx->m_is_relayed.get()) tx->m_is_double_spend_seen = false; // TODO: test and handle if true
tx->m_num_confirmations = 0;
tx->m_mixin = request.m_mixin;
tx->m_ring_size = request.m_ring_size;
tx->m_unlock_time = request.m_unlock_time == boost::none ? 0 : request.m_unlock_time.get();
tx->m_is_locked = true;
if (tx->m_is_relayed.get()) tx->m_last_relayed_timestamp = static_cast<uint64_t>(time(NULL)); // set last relayed timestamp to current time iff relayed // TODO monero core: this should be encapsulated in wallet2
out_transfer->m_account_index = request.m_account_index;
if (request.m_subaddress_indices.size() == 1) out_transfer->m_subaddress_indices.push_back(request.m_subaddress_indices[0]); // subaddress index is known iff 1 requested // TODO: get all known subaddress indices here
@@ -2331,6 +2341,7 @@ namespace monero {
shared_ptr<monero_tx_wallet> tx = make_shared<monero_tx_wallet>();
txs.push_back(tx);
tx->m_id = *tx_ids_iter;
tx->m_is_outgoing = true;
tx->m_key = *tx_keys_iter;
tx->m_fee = *tx_fees_iter;
tx->m_full_hex = *tx_blobs_iter;
@@ -2348,7 +2359,7 @@ namespace monero {
tx->m_in_tx_pool = !tx->m_do_not_relay.get();
if (!tx->m_is_failed.get() && tx->m_is_relayed.get()) tx->m_is_double_spend_seen = false; // TODO: test and handle if true
tx->m_num_confirmations = 0;
//tx->m_mixin = request.m_mixin; // TODO: how to get?
//tx->m_ring_size = request.m_ring_size; // TODO: how to get?
tx->m_unlock_time = 0;
if (tx->m_is_relayed.get()) tx->m_last_relayed_timestamp = static_cast<uint64_t>(time(NULL)); // set last relayed timestamp to current time iff relayed // TODO monero core: this should be encapsulated in wallet2
out_transfer->m_destinations[0]->m_amount = *tx_amounts_iter;
@@ -2373,6 +2384,165 @@ namespace monero {
return tx_set;
}
monero_tx_set monero_wallet::parse_tx_set(const monero_tx_set& tx_set) {
// get unsigned and multisig tx sets
string unsigned_tx_hex = tx_set.m_unsigned_tx_hex == boost::none ? "" : tx_set.m_unsigned_tx_hex.get();
string multisig_tx_hex = tx_set.m_multisig_tx_hex == boost::none ? "" : tx_set.m_multisig_tx_hex.get();
// validate request
if (m_w2->key_on_device()) throw runtime_error("command not supported by HW wallet");
if (m_w2->watch_only()) throw runtime_error("command not supported by watch-only wallet");
if (unsigned_tx_hex.empty() && multisig_tx_hex.empty()) throw runtime_error("no txset provided");
std::vector <wallet2::tx_construction_data> tx_constructions;
if (!unsigned_tx_hex.empty()) {
try {
tools::wallet2::unsigned_tx_set exported_txs;
cryptonote::blobdata blob;
if (!epee::string_tools::parse_hexstr_to_binbuff(unsigned_tx_hex, blob)) throw runtime_error("Failed to parse hex.");
if (!m_w2->parse_unsigned_tx_from_str(blob, exported_txs)) throw runtime_error("cannot load unsigned_txset");
tx_constructions = exported_txs.txes;
}
catch (const std::exception &e) {
throw runtime_error("failed to parse unsigned transfers: " + std::string(e.what()));
}
} else if (!multisig_tx_hex.empty()) {
try {
tools::wallet2::multisig_tx_set exported_txs;
cryptonote::blobdata blob;
if (!epee::string_tools::parse_hexstr_to_binbuff(multisig_tx_hex, blob)) throw runtime_error("Failed to parse hex.");
if (!m_w2->parse_multisig_tx_from_str(blob, exported_txs)) throw runtime_error("cannot load multisig_txset");
for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n) {
tx_constructions.push_back(exported_txs.m_ptx[n].construction_data);
}
}
catch (const std::exception &e) {
throw runtime_error("failed to parse multisig transfers: " + std::string(e.what()));
}
}
std::vector<tools::wallet2::pending_tx> ptx; // TODO wallet_rpc_server: unused variable
try
{
// gather info for each tx
vector<shared_ptr<monero_tx_wallet>> txs;
std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
int first_known_non_zero_change_index = -1;
for (size_t n = 0; n < tx_constructions.size(); ++n)
{
// pre-initialize tx
shared_ptr<monero_tx_wallet> tx = make_shared<monero_tx_wallet>();
tx->m_is_outgoing = true;
tx->m_input_sum = 0;
tx->m_output_sum = 0;
tx->m_change_amount = 0;
tx->m_num_dummy_outputs = 0;
tx->m_ring_size = std::numeric_limits<uint32_t>::max(); // smaller ring sizes will overwrite
const tools::wallet2::tx_construction_data &cd = tx_constructions[n];
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
bool has_encrypted_payment_id = false;
crypto::hash8 payment_id8 = crypto::null_hash8;
if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
{
cryptonote::tx_extra_nonce extra_nonce;
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
crypto::hash payment_id;
if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
if (payment_id8 != crypto::null_hash8)
{
tx->m_payment_id = epee::string_tools::pod_to_hex(payment_id8);
has_encrypted_payment_id = true;
}
}
else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
tx->m_payment_id = epee::string_tools::pod_to_hex(payment_id);
}
}
}
for (size_t s = 0; s < cd.sources.size(); ++s)
{
tx->m_input_sum = tx->m_input_sum.get() + cd.sources[s].amount;
size_t ring_size = cd.sources[s].outputs.size();
if (ring_size < tx->m_ring_size.get())
tx->m_ring_size = ring_size;
}
for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
{
const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
std::string address = cryptonote::get_account_address_as_str(m_w2->nettype(), entry.is_subaddress, entry.addr);
if (has_encrypted_payment_id && !entry.is_subaddress && address != entry.original)
address = cryptonote::get_account_integrated_address_as_str(m_w2->nettype(), entry.addr, payment_id8);
auto i = dests.find(entry.addr);
if (i == dests.end())
dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
else
i->second.second += entry.amount;
tx->m_output_sum = tx->m_output_sum.get() + entry.amount;
}
if (cd.change_dts.amount > 0)
{
auto it = dests.find(cd.change_dts.addr);
if (it == dests.end()) throw runtime_error("Claimed change does not go to a paid address");
if (it->second.second < cd.change_dts.amount) throw runtime_error("Claimed change is larger than payment to the change address");
if (cd.change_dts.amount > 0)
{
if (first_known_non_zero_change_index == -1)
first_known_non_zero_change_index = n;
const tools::wallet2::tx_construction_data &cdn = tx_constructions[first_known_non_zero_change_index];
if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr))) throw runtime_error("Change goes to more than one address");
}
tx->m_change_amount = tx->m_change_amount.get() + cd.change_dts.amount;
it->second.second -= cd.change_dts.amount;
if (it->second.second == 0)
dests.erase(cd.change_dts.addr);
}
tx->m_outgoing_transfer = make_shared<monero_outgoing_transfer>();
size_t n_dummy_outputs = 0;
for (auto i = dests.begin(); i != dests.end(); )
{
if (i->second.second > 0)
{
shared_ptr<monero_destination> destination = make_shared<monero_destination>();
destination->m_address = i->second.first;
destination->m_amount = i->second.second;
tx->m_outgoing_transfer.get()->m_destinations.push_back(destination);
}
else
tx->m_num_dummy_outputs = tx->m_num_dummy_outputs.get() + 1;
++i;
}
if (tx->m_change_amount.get() > 0)
{
const tools::wallet2::tx_construction_data &cd0 = tx_constructions[0];
tx->m_change_address = get_account_address_as_str(m_w2->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
}
tx->m_fee = tx->m_input_sum.get() - tx->m_output_sum.get();
tx->m_unlock_time = cd.unlock_time;
tx->m_extra_hex = epee::to_hex::string({cd.extra.data(), cd.extra.size()});
txs.push_back(tx);
}
// build and return tx set
monero_tx_set tx_set;
tx_set.m_txs = txs;
return tx_set;
}
catch (const std::exception &e)
{
throw runtime_error("failed to parse unsigned transfers");
}
}
string monero_wallet::sign(const string& msg) const {
return m_w2->sign(msg);
}
@@ -2619,6 +2789,119 @@ namespace monero {
}
}
vector<monero_address_book_entry> monero_wallet::get_address_book_entries(const vector<uint64_t>& indices) const {
MTRACE("monero_wallet::get_address_book_entries()");
// get wallet2 address book entries
const auto w2_entries = m_w2->get_address_book();
// build entries from wallet2 types
vector<monero_address_book_entry> entries;
if (indices.empty()) {
uint64_t idx = 0;
for (const auto &w2_entry: w2_entries) {
monero_address_book_entry entry(idx++, cryptonote::get_account_address_as_str(m_w2->nettype(), w2_entry.m_is_subaddress, w2_entry.m_address), w2_entry.m_description, epee::string_tools::pod_to_hex(w2_entry.m_payment_id));
entries.push_back(entry);
}
} else {
for (uint64_t idx : indices) {
if (idx >= w2_entries.size()) throw runtime_error("Index out of range: " + std::to_string(idx));
const auto &w2_entry = w2_entries[idx];
monero_address_book_entry entry(idx++, cryptonote::get_account_address_as_str(m_w2->nettype(), w2_entry.m_is_subaddress, w2_entry.m_address), w2_entry.m_description, epee::string_tools::pod_to_hex(w2_entry.m_payment_id));
entries.push_back(entry);
}
}
return entries;
}
uint64_t monero_wallet::add_address_book_entry(const string& address, const string& description, const string& payment_id) {
MTRACE("add_address_book_entry()");
cryptonote::address_parse_info info;
crypto::hash payment_id_hash = crypto::null_hash;
epee::json_rpc::error er;
if(!get_account_address_from_str_or_url(info, m_w2->nettype(), address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid) throw runtime_error(std::string("Invalid DNSSEC for ") + url);
if (addresses.empty()) throw runtime_error(std::string("No Monero address found at ") + url);
return addresses[0];
}))
{
throw runtime_error(std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + address);
}
if (info.has_payment_id) {
memcpy(payment_id_hash.data, info.payment_id.data, 8);
memset(payment_id_hash.data + 8, 0, 24);
}
if (!payment_id.empty()) {
if (info.has_payment_id) throw runtime_error("Separate payment ID given with integrated address");
crypto::hash long_payment_id;
if (!wallet2::parse_long_payment_id(payment_id, payment_id_hash)) {
if (!wallet2::parse_short_payment_id(payment_id, info.payment_id)) throw runtime_error("Payment id has invalid format: \"" + payment_id + "\", expected 64 character string");
else throw runtime_error("Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address");
}
}
if (!m_w2->add_address_book_row(info.address, payment_id_hash, description, info.is_subaddress)) throw runtime_error("Failed to add address book entry");
return m_w2->get_address_book().size() - 1;
}
void monero_wallet::edit_address_book_entry(uint64_t index, bool set_address, const string& address, bool set_description, const string& description, bool set_payment_id, const string& payment_id) {
MTRACE("edit_address_book_entry()");
const auto ab = m_w2->get_address_book();
if (index >= ab.size()) throw runtime_error("Index out of range: " + std::to_string(index));
tools::wallet2::address_book_row entry = ab[index];
cryptonote::address_parse_info info;
crypto::hash payment_id_hash = crypto::null_hash;
epee::json_rpc::error er;
if (set_address) {
er.message = "";
if(!get_account_address_from_str_or_url(info, m_w2->nettype(), address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid) throw runtime_error(std::string("Invalid DNSSEC for ") + url);
if (addresses.empty()) throw runtime_error(std::string("No Monero address found at ") + url);
return addresses[0];
}))
{
throw runtime_error("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: " + address);
}
entry.m_address = info.address;
entry.m_is_subaddress = info.is_subaddress;
if (info.has_payment_id) {
memcpy(entry.m_payment_id.data, info.payment_id.data, 8);
memset(entry.m_payment_id.data + 8, 0, 24);
}
}
if (set_payment_id) {
if (payment_id.empty()) {
payment_id_hash = crypto::null_hash;
} else {
if (set_address && info.has_payment_id) throw runtime_error("Separate payment ID given with integrated address");
if (!wallet2::parse_long_payment_id(payment_id, payment_id_hash)) {
crypto::hash8 spid;
if (!wallet2::parse_short_payment_id(payment_id, spid)) throw runtime_error("Payment id has invalid format: \"" + payment_id + "\", expected 64 character string");
else throw runtime_error("Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address");
}
}
entry.m_payment_id = payment_id_hash;
}
if (set_description) entry.m_description = description;
if (!m_w2->set_address_book_row(index, entry.m_address, entry.m_payment_id, entry.m_description, entry.m_is_subaddress)) {
throw runtime_error("Failed to edit address book entry");
}
}
void monero_wallet::delete_address_book_entry(uint64_t index) {
const auto w2_entries = m_w2->get_address_book();
if (index >= w2_entries.size()) throw runtime_error("Index out of range: " + std::to_string(index));
if (!m_w2->delete_address_book_row(index)) throw runtime_error("Failed to delete address book entry");
}
string monero_wallet::create_payment_uri(const monero_send_request& request) const {
MTRACE("create_payment_uri()");
@@ -2669,8 +2952,8 @@ namespace monero {
return send_request;
}
string monero_wallet::get_attribute(const string& key) const {
return m_w2->get_attribute(key);
bool monero_wallet::get_attribute(const string& key, string& value) const {
return m_w2->get_attribute(key, value);
}
void monero_wallet::set_attribute(const string& key, const string& val) {
@@ -2780,7 +3063,7 @@ namespace monero {
// validate state and args
bool ready;
uint32_t threshold, total;
if (!m_w2->multisig(&ready, &threshold, &total)) throw new runtime_error("This wallet is not multisig");
if (!m_w2->multisig(&ready, &threshold, &total)) throw runtime_error("This wallet is not multisig");
if (ready) throw runtime_error("This wallet is multisig, and already finalized");
if (multisig_hexes.size() < 1 || multisig_hexes.size() > total) throw runtime_error("Needs multisig info from more participants");
@@ -2964,8 +3247,8 @@ namespace monero {
vector<monero_subaddress> subaddresses;
// get balances per subaddress as maps
map<uint32_t, uint64_t> balance_per_subaddress = m_w2->balance_per_subaddress(account_idx);
map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_w2->unlocked_balance_per_subaddress(account_idx);
map<uint32_t, uint64_t> balance_per_subaddress = m_w2->balance_per_subaddress(account_idx, STRICT);
map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_w2->unlocked_balance_per_subaddress(account_idx, STRICT);
// get all indices if no indices given
vector<uint32_t> subaddress_indices_req;
+68 -56
View File
@@ -245,6 +245,13 @@ namespace monero {
*/
bool is_synced() const;
/**
* Get the wallet's version.
*
* @return the wallet's version
*/
monero_version get_version() const;
/**
* Get the path of this wallet's file on disk.
*
@@ -648,15 +655,15 @@ namespace monero {
* Get wallet transactions. Wallet transactions contain one or more
* transfers that are either incoming or outgoing to the wallet.
*
* Query results can be filtered by passing in a transaction request.
* Transactions must meet every criteria defined in the request in order to
* Query results can be filtered by passing in a transaction query.
* Transactions must meet every criteria defined in the query in order to
* be returned. All filtering is optional and no filtering is applied when
* not defined.
*
* @param request filters query results (optional)
* @return wallet transactions per the request
* @param query filters query results (optional)
* @return wallet transactions per the query
*/
vector<shared_ptr<monero_tx_wallet>> get_txs(const monero_tx_query& request) const;
vector<shared_ptr<monero_tx_wallet>> get_txs(const monero_tx_query& query) const;
// /**
// * Get all incoming and outgoing transfers to and from this wallet. An
@@ -706,14 +713,14 @@ namespace monero {
* are stored on the blockchain.
*
* Query results can be filtered by passing in a monero_transfer_query.
* Transfers must meet every criteria defined in the request in order to be
* Transfers must meet every criteria defined in the query in order to be
* returned. All filtering is optional and no filtering is applied when not
* defined.
*
* @param request filters query results (optional)
* @return wallet transfers per the request
* @param query filters query results (optional)
* @return wallet transfers per the query
*/
vector<shared_ptr<monero_transfer>> get_transfers(const monero_transfer_query& request) const;
vector<shared_ptr<monero_transfer>> get_transfers(const monero_transfer_query& query) const;
// /**
// * Get outputs created from previous transactions that belong to the wallet
@@ -730,13 +737,13 @@ namespace monero {
* transactions which are stored in blocks on the blockchain.
*
* Results can be configured by passing a monero_output_query. Outputs must
* meet every criteria defined in the request in order to be returned. All
* meet every criteria defined in the query in order to be returned. All
* filtering is optional and no filtering is applied when not defined.
*
* @param request specifies request options (optional)
* @return List<monero_output_wallet> are wallet outputs per the request
* @param query specifies query options (optional)
* @return wallet outputs per the query
*/
vector<shared_ptr<monero_output_wallet>> get_outputs(const monero_output_query& request) const;
vector<shared_ptr<monero_output_wallet>> get_outputs(const monero_output_query& query) const;
/**
* Export all outputs in hex format.
@@ -999,6 +1006,14 @@ namespace monero {
*/
monero_tx_set sweep_dust(bool do_not_relay = false);
/**
* Parses a tx set containing unsigned or multisig tx hex to a new tx set containing structured transactions.
*
* @param tx_set is a tx set containing unsigned or multisig tx hex
* @return the parsed tx set containing structured transactions
*/
monero_tx_set parse_tx_set(const monero_tx_set& tx_set);
/**
* Sign a message.
*
@@ -1152,47 +1167,43 @@ namespace monero {
*/
void set_tx_notes(const vector<string>& tx_ids, const vector<string>& notes);
// /**
// * Get all address book entries.
// *
// * @return the address book entries
// */
// public List<MoneroAddressBookEntry> getAddressBookEntries();
//
// /**
// * Get address book entries.
// *
// * @param entryIndices are indices of the entries to get
// * @return the address book entries
// */
// public List<MoneroAddressBookEntry> getAddressBookEntries(Collection<Integer> entryIndices);
//
// /**
// * Add an address book entry.
// *
// * @param address is the entry address
// * @param description is the entry description (optional)
// * @return the index of the added entry
// */
// public int addAddressBookEntry(string address, string description);
//
// /**
// * Add an address book entry.
// *
// * @param address is the entry address
// * @param description is the entry description (optional)
// * @param payment_id is the entry paymet id (optional)
// * @return the index of the added entry
// */
// public int addAddressBookEntry(string address, string description, string payment_id);
//
// /**
// * Delete an address book entry.
// *
// * @param entryIdx is the index of the entry to delete
// */
// public void deleteAddressBookEntry(int entryIdx);
//
/**
* Get all address book entries.
*
* @param indices are indices of the entries to get
* @return the address book entries
*/
vector<monero_address_book_entry> get_address_book_entries(const vector<uint64_t>& indices) const;
/**
* Add an address book entry.
*
* @param address is the entry address
* @param description is the entry description (optional)
* @return the index of the added entry
*/
uint64_t add_address_book_entry(const string& address, const string& description, const string& payment_id = "");
/**
* Edit an address book entry.
*
* @param index is the index of the address book entry to edit
* @param set_address specifies if the address should be updated
* @param address is the updated address
* @param set_description specifies if the description should be updated
* @param description is the updated description
* @param set_payment_id specifies if the payment id should be updated
* @param payment_id is the updated payment id
*/
void edit_address_book_entry(uint64_t index, bool set_address, const string& address, bool set_description, const string& description, bool set_payment_id = false, const string& payment_id = "");
/**
* Delete an address book entry.
*
* @param index is the index of the entry to delete
*/
void delete_address_book_entry(uint64_t index);
// /**
// * Tag accounts.
// *
@@ -1243,9 +1254,10 @@ namespace monero {
* Get an attribute.
*
* @param key is the attribute to get the value of
* @return the attribute's value
* @param value is set to the key's value if set
* @return true if the key's value has been set, false otherwise
*/
string get_attribute(const string& key) const;
bool get_attribute(const string& key, string& value) const;
/**
* Set an arbitrary attribute.
File diff suppressed because it is too large Load Diff
+64 -52
View File
@@ -65,11 +65,13 @@ namespace monero {
/**
* Models a result of syncing a wallet.
*/
struct monero_sync_result {
struct monero_sync_result : public serializable_struct {
uint64_t m_num_blocks_fetched;
bool m_received_money;
monero_sync_result() {}
monero_sync_result(const uint64_t num_blocks_fetched, const bool received_money) : m_num_blocks_fetched(num_blocks_fetched), m_received_money(received_money) {}
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -85,20 +87,8 @@ namespace monero {
boost::optional<uint64_t> m_num_unspent_outputs;
boost::optional<bool> m_is_used;
boost::optional<uint64_t> m_num_blocks_to_unlock;
// BEGIN_KV_SERIALIZE_MAP()
// KV_SERIALIZE(m_account_index)
// KV_SERIALIZE(index)
// KV_SERIALIZE(address)
// KV_SERIALIZE(label)
// KV_SERIALIZE(balance)
// KV_SERIALIZE(m_unlocked_balance)
// KV_SERIALIZE(m_num_unspent_outputs)
// KV_SERIALIZE(m_is_used)
// KV_SERIALIZE(m_num_blocks_to_unlock)
// END_KV_SERIALIZE_MAP()
boost::property_tree::ptree to_property_tree() const;
//void merge(const shared_ptr<monero_subaddress>& self, const shared_ptr<monero_subaddress>& other);
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -111,17 +101,8 @@ namespace monero {
boost::optional<uint64_t> m_unlocked_balance;
boost::optional<string> m_tag;
vector<monero_subaddress> m_subaddresses;
// BEGIN_KV_SERIALIZE_MAP()
// KV_SERIALIZE(index)
// KV_SERIALIZE(m_primary_address)
// KV_SERIALIZE(label)
// KV_SERIALIZE(balance)
// KV_SERIALIZE(m_unlocked_balance)
// KV_SERIALIZE(tag)
// KV_SERIALIZE(subaddresses)
// END_KV_SERIALIZE_MAP()
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -133,8 +114,9 @@ namespace monero {
monero_destination() {}
monero_destination(const string& address, uint64_t amount) : m_address(address), m_amount(amount) {}
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_destination>& destination);
shared_ptr<monero_destination> copy(const shared_ptr<monero_destination>& src, const shared_ptr<monero_destination>& tgt) const;
boost::property_tree::ptree to_property_tree() const;
};
// forward declarations
@@ -151,13 +133,14 @@ namespace monero {
boost::optional<uint32_t> m_account_index;
boost::optional<uint64_t> m_num_suggested_confirmations;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_transfer>& transfer);
virtual boost::optional<bool> is_incoming() const = 0; // derived class must implement
shared_ptr<monero_transfer> copy(const shared_ptr<monero_transfer>& src, const shared_ptr<monero_transfer>& tgt) const;
boost::optional<bool> is_outgoing() const {
if (is_incoming() == boost::none) return boost::none;
return !(*is_incoming());
}
boost::property_tree::ptree to_property_tree() const;
void merge(const shared_ptr<monero_transfer>& self, const shared_ptr<monero_transfer>& other);
};
@@ -168,10 +151,10 @@ namespace monero {
boost::optional<uint32_t> m_subaddress_index;
boost::optional<string> m_address;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
shared_ptr<monero_incoming_transfer> copy(const shared_ptr<monero_transfer>& src, const shared_ptr<monero_transfer>& tgt) const;
shared_ptr<monero_incoming_transfer> copy(const shared_ptr<monero_incoming_transfer>& src, const shared_ptr<monero_incoming_transfer>& tgt) const;
boost::optional<bool> is_incoming() const;
boost::property_tree::ptree to_property_tree() const;
void merge(const shared_ptr<monero_transfer>& self, const shared_ptr<monero_transfer>& other);
void merge(const shared_ptr<monero_incoming_transfer>& self, const shared_ptr<monero_incoming_transfer>& other);
};
@@ -184,10 +167,10 @@ namespace monero {
vector<string> m_addresses;
vector<shared_ptr<monero_destination>> m_destinations;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
shared_ptr<monero_outgoing_transfer> copy(const shared_ptr<monero_transfer>& src, const shared_ptr<monero_transfer>& tgt) const;
shared_ptr<monero_outgoing_transfer> copy(const shared_ptr<monero_outgoing_transfer>& src, const shared_ptr<monero_outgoing_transfer>& tgt) const;
boost::optional<bool> is_incoming() const;
boost::property_tree::ptree to_property_tree() const;
void merge(const shared_ptr<monero_transfer>& self, const shared_ptr<monero_transfer>& other);
void merge(const shared_ptr<monero_outgoing_transfer>& self, const shared_ptr<monero_outgoing_transfer>& other);
};
@@ -207,10 +190,12 @@ namespace monero {
boost::optional<bool> m_has_destinations;
boost::optional<shared_ptr<monero_tx_query>> m_tx_query;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_transfer_query>& transfer_query);
static shared_ptr<monero_transfer_query> deserialize_from_block(const string& transfer_query_json);
shared_ptr<monero_transfer_query> copy(const shared_ptr<monero_transfer>& src, const shared_ptr<monero_transfer>& tgt) const;
shared_ptr<monero_transfer_query> copy(const shared_ptr<monero_transfer_query>& src, const shared_ptr<monero_transfer_query>& tgt) const;
boost::optional<bool> is_incoming() const;
boost::property_tree::ptree to_property_tree() const;
bool meets_criteria(monero_transfer* transfer) const;
};
@@ -221,12 +206,12 @@ namespace monero {
boost::optional<uint32_t> m_account_index;
boost::optional<uint32_t> m_subaddress_index;
boost::optional<bool> m_is_spent;
boost::optional<bool> m_is_unlocked;
boost::optional<bool> m_is_frozen;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_output_wallet>& output_wallet);
shared_ptr<monero_output_wallet> copy(const shared_ptr<monero_output>& src, const shared_ptr<monero_output>& tgt) const;
shared_ptr<monero_output_wallet> copy(const shared_ptr<monero_output_wallet>& src, const shared_ptr<monero_output_wallet>& tgt) const;
boost::property_tree::ptree to_property_tree() const;
void merge(const shared_ptr<monero_output>& self, const shared_ptr<monero_output>& other);
void merge(const shared_ptr<monero_output_wallet>& self, const shared_ptr<monero_output_wallet>& other);
};
@@ -241,12 +226,14 @@ namespace monero {
vector<uint32_t> m_subaddress_indices;
boost::optional<shared_ptr<monero_tx_query>> m_tx_query;
// TODO: necessary to override all super classes?
//boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_output_query>& output_query);
static shared_ptr<monero_output_query> deserialize_from_block(const string& output_query_json);
bool is_default() const;
shared_ptr<monero_output_query> copy(const shared_ptr<monero_output>& src, const shared_ptr<monero_output>& tgt) const;
shared_ptr<monero_output_query> copy(const shared_ptr<monero_output_wallet>& src, const shared_ptr<monero_output_wallet>& tgt) const;
shared_ptr<monero_output_query> copy(const shared_ptr<monero_output_wallet>& src, const shared_ptr<monero_output_wallet>& tgt) const; // TODO: necessary to override all super classes?
shared_ptr<monero_output_query> copy(const shared_ptr<monero_output_query>& src, const shared_ptr<monero_output_query>& tgt) const;
boost::property_tree::ptree to_property_tree() const;
bool meets_criteria(monero_output_wallet* output) const;
private:
@@ -258,15 +245,23 @@ namespace monero {
*/
struct monero_tx_wallet : public monero_tx {
boost::optional<shared_ptr<monero_tx_set>> m_tx_set;
boost::optional<bool> m_is_incoming;
boost::optional<bool> m_is_outgoing;
vector<shared_ptr<monero_incoming_transfer>> m_incoming_transfers;
boost::optional<shared_ptr<monero_outgoing_transfer>> m_outgoing_transfer;
boost::optional<string> m_note;
boost::optional<bool> m_is_locked;
boost::optional<uint64_t> m_input_sum;
boost::optional<uint64_t> m_output_sum;
boost::optional<string> m_change_address;
boost::optional<uint64_t> m_change_amount;
boost::optional<uint32_t> m_num_dummy_outputs;
boost::optional<string> m_extra_hex;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_tx_wallet>& tx_wallet);
shared_ptr<monero_tx_wallet> copy(const shared_ptr<monero_tx>& src, const shared_ptr<monero_tx>& tgt) const;
shared_ptr<monero_tx_wallet> copy(const shared_ptr<monero_tx_wallet>& src, const shared_ptr<monero_tx_wallet>& tgt) const;
bool is_incoming() const;
bool is_outgoing() const;
boost::property_tree::ptree to_property_tree() const;
void merge(const shared_ptr<monero_tx>& self, const shared_ptr<monero_tx>& other);
void merge(const shared_ptr<monero_tx_wallet>& self, const shared_ptr<monero_tx_wallet>& other);
};
@@ -289,11 +284,12 @@ namespace monero {
boost::optional<shared_ptr<monero_transfer_query>> m_transfer_query;
boost::optional<shared_ptr<monero_output_query>> m_output_query;
// TODO: necessary to override all super classes?
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_tx_query>& tx_query);
static shared_ptr<monero_tx_query> deserialize_from_block(const string& tx_query_json);
shared_ptr<monero_tx_query> copy(const shared_ptr<monero_tx>& src, const shared_ptr<monero_tx>& tgt) const;
shared_ptr<monero_tx_query> copy(const shared_ptr<monero_tx_wallet>& src, const shared_ptr<monero_tx_wallet>& tgt) const;
shared_ptr<monero_tx_query> copy(const shared_ptr<monero_tx_wallet>& src, const shared_ptr<monero_tx_wallet>& tgt) const; // TODO: necessary to override all super classes?
shared_ptr<monero_tx_query> copy(const shared_ptr<monero_tx_query>& src, const shared_ptr<monero_tx_query>& tgt) const;
boost::property_tree::ptree to_property_tree() const;
bool meets_criteria(monero_tx_wallet* tx) const;
};
@@ -305,13 +301,15 @@ namespace monero {
* hex string which is needed in order to sign and submit the multisig
* transactions.
*/
struct monero_tx_set : serializable_struct {
struct monero_tx_set : public serializable_struct {
vector<shared_ptr<monero_tx_wallet>> m_txs;
boost::optional<string> m_signed_tx_hex;
boost::optional<string> m_unsigned_tx_hex;
boost::optional<string> m_multisig_tx_hex;
boost::property_tree::ptree to_property_tree() const;
//boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static monero_tx_set deserialize(const string& tx_set_json);
};
/**
@@ -322,7 +320,7 @@ namespace monero {
string m_payment_id;
string m_integrated_address;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -342,7 +340,6 @@ namespace monero {
vector<shared_ptr<monero_destination>> m_destinations;
boost::optional<string> m_payment_id;
boost::optional<monero_send_priority> m_priority;
boost::optional<uint32_t> m_mixin;
boost::optional<uint32_t> m_ring_size;
boost::optional<uint64_t> m_fee;
boost::optional<uint32_t> m_account_index;
@@ -358,8 +355,9 @@ namespace monero {
monero_send_request() {}
monero_send_request(const monero_send_request& request);
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
static shared_ptr<monero_send_request> deserialize(const string& send_request_json);
monero_send_request copy() const;
boost::property_tree::ptree to_property_tree() const;
};
/**
@@ -370,7 +368,7 @@ namespace monero {
boost::optional<uint64_t> m_spent_amount;
boost::optional<uint64_t> m_unspent_amount;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -379,7 +377,7 @@ namespace monero {
struct monero_check : public serializable_struct {
bool m_is_good;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -390,7 +388,7 @@ namespace monero {
boost::optional<uint64_t> m_num_confirmations;
boost::optional<uint64_t> m_received_amount;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -400,7 +398,7 @@ namespace monero {
boost::optional<uint64_t> m_total_amount;
boost::optional<uint64_t> m_unconfirmed_spent_amount;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -412,7 +410,7 @@ namespace monero {
uint32_t m_threshold;
uint32_t m_num_participants;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -424,7 +422,7 @@ namespace monero {
boost::optional<string> m_address;
boost::optional<string> m_multisig_hex;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
@@ -434,6 +432,20 @@ namespace monero {
boost::optional<string> m_signed_multisig_tx_hex;
vector<string> m_tx_ids;
boost::property_tree::ptree to_property_tree() const;
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
/**
* Monero address book entry model.
*/
struct monero_address_book_entry : serializable_struct {
boost::optional<uint64_t> m_index; // TODO: not boost::optional
boost::optional<string> m_address;
boost::optional<string> m_description;
boost::optional<string> m_payment_id;
monero_address_book_entry() {}
monero_address_book_entry(uint64_t index, const string& address, const string& description, const string& payment_id) : m_index(index), m_address(address), m_description(description), m_payment_id(payment_id) {}
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
};
}
+2 -3
View File
@@ -1,8 +1,7 @@
TODO
gen_utils.h and monero_utils.h should not depend on monero_wallet.h
add to ecosystem?
preserve rpc error codes, just throwing runtime_error now
monero_wallet.cpp move utilities in monero_namespace to be private
polish model: no boost::optional<shared_ptr>? no boost::optional<string>? etc
polish model: remove all boost::optional from model to be consistent with core?
ensure model toPropertyTrees(), etc are not unecessarily copying return objects
monero_wallet_listener should be a class?
way to use copy constructors instead of query copy(sp, sp) methods?