mirror of
https://github.com/wownero/wownero-cpp.git
synced 2026-10-17 23:31:32 +02:00
implement parse_tx_set
This commit is contained in:
@@ -317,8 +317,6 @@ void monero_utils::binary_blocks_to_json(const std::string &bin, std::string &js
|
||||
}
|
||||
|
||||
monero_tx_set monero_utils::deserialize_tx_set(const string& tx_set_str) {
|
||||
cout << "monero_utils::deserialize_tx_set(...)" << endl;
|
||||
cout << tx_set_str;
|
||||
|
||||
// deserialize tx set to property
|
||||
std::istringstream iss = tx_set_str.empty() ? std::istringstream() : std::istringstream(tx_set_str);
|
||||
|
||||
@@ -1958,6 +1958,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;
|
||||
@@ -2369,7 +2370,162 @@ namespace monero {
|
||||
}
|
||||
|
||||
monero_tx_set monero_wallet::parse_tx_set(const monero_tx_set& tx_set) {
|
||||
throw runtime_error("Not implemented");
|
||||
|
||||
// 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_mixin = std::numeric_limits<uint32_t>::max() - 1; // smaller ring sizes will overwrite // TODO: remove - 1 when switched to ring size
|
||||
|
||||
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_mixin.get() + 1)
|
||||
tx->m_mixin = ring_size - 1; // TODO: switch to m_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 {
|
||||
|
||||
@@ -119,6 +119,9 @@ namespace monero {
|
||||
monero_tx::copy(static_pointer_cast<monero_tx>(src), static_pointer_cast<monero_tx>(tgt));
|
||||
|
||||
// copy wallet extensions
|
||||
tgt->m_tx_set = src->m_tx_set;
|
||||
tgt->m_is_incoming = src->m_is_incoming;
|
||||
tgt->m_is_outgoing = src->m_is_outgoing;
|
||||
if (!src->m_incoming_transfers.empty()) {
|
||||
tgt->m_incoming_transfers = vector<shared_ptr<monero_incoming_transfer>>();
|
||||
for (const shared_ptr<monero_incoming_transfer>& transfer : src->m_incoming_transfers) {
|
||||
@@ -134,27 +137,33 @@ namespace monero {
|
||||
}
|
||||
tgt->m_note = src->m_note;
|
||||
tgt->m_is_unlocked = src->m_is_unlocked;
|
||||
tgt->m_input_sum = src->m_input_sum;
|
||||
tgt->m_output_sum = src->m_output_sum;
|
||||
tgt->m_change_address = src->m_change_address;
|
||||
tgt->m_change_amount = src->m_change_amount;
|
||||
tgt->m_num_dummy_outputs = src->m_num_dummy_outputs;
|
||||
tgt->m_extra_hex = src->m_extra_hex;
|
||||
|
||||
return tgt;
|
||||
};
|
||||
|
||||
boost::property_tree::ptree monero_tx_wallet::to_property_tree() const {
|
||||
boost::property_tree::ptree node = monero_tx::to_property_tree();
|
||||
if (m_is_incoming != boost::none) node.put("isIncoming", *m_is_incoming);
|
||||
if (m_is_outgoing != boost::none) node.put("isOutgoing", *m_is_outgoing);
|
||||
if (!m_incoming_transfers.empty()) node.add_child("incomingTransfers", monero_utils::to_property_tree(m_incoming_transfers));
|
||||
if (m_outgoing_transfer != boost::none) node.add_child("outgoingTransfer", (*m_outgoing_transfer)->to_property_tree());
|
||||
if (m_note != boost::none) node.put("note", *m_note);
|
||||
if (m_is_unlocked != boost::none) node.put("isUnlocked", *m_is_unlocked);
|
||||
if (m_input_sum != boost::none) node.put("inputSum", *m_input_sum);
|
||||
if (m_output_sum != boost::none) node.put("outputSum", *m_output_sum);
|
||||
if (m_change_address != boost::none) node.put("changeAddress", *m_change_address);
|
||||
if (m_change_amount != boost::none) node.put("changeAmount", *m_change_amount);
|
||||
if (m_num_dummy_outputs != boost::none) node.put("numDummyOutputs", *m_num_dummy_outputs);
|
||||
if (m_extra_hex != boost::none) node.put("extraHex", *m_extra_hex);
|
||||
return node;
|
||||
}
|
||||
|
||||
bool monero_tx_wallet::is_outgoing() const {
|
||||
return m_outgoing_transfer != boost::none;
|
||||
}
|
||||
|
||||
bool monero_tx_wallet::is_incoming() const {
|
||||
return !m_incoming_transfers.empty();
|
||||
}
|
||||
|
||||
void monero_tx_wallet::merge(const shared_ptr<monero_tx>& self, const shared_ptr<monero_tx>& other) {
|
||||
merge(static_pointer_cast<monero_tx_wallet>(self), static_pointer_cast<monero_tx_wallet>(other));
|
||||
}
|
||||
@@ -166,9 +175,6 @@ namespace monero {
|
||||
// merge base classes
|
||||
monero_tx::merge(self, other);
|
||||
|
||||
// merge wallet extensions
|
||||
m_note = gen_utils::reconcile(m_note, other->m_note);
|
||||
|
||||
// merge incoming transfers
|
||||
if (!other->m_incoming_transfers.empty()) {
|
||||
for (const shared_ptr<monero_incoming_transfer>& transfer : other->m_incoming_transfers) { // NOTE: not using reference so shared_ptr is not deleted when tx is dereferenced
|
||||
@@ -183,6 +189,16 @@ namespace monero {
|
||||
if (self->m_outgoing_transfer == boost::none) self->m_outgoing_transfer = other->m_outgoing_transfer;
|
||||
else self->m_outgoing_transfer.get()->merge(self->m_outgoing_transfer.get(), other->m_outgoing_transfer.get());
|
||||
}
|
||||
|
||||
// merge simple extensions
|
||||
m_note = gen_utils::reconcile(m_note, other->m_note);
|
||||
m_is_unlocked = gen_utils::reconcile(m_is_unlocked, other->m_is_unlocked);
|
||||
m_input_sum = gen_utils::reconcile(m_input_sum, other->m_input_sum);
|
||||
m_output_sum = gen_utils::reconcile(m_output_sum, other->m_output_sum);
|
||||
m_change_address = gen_utils::reconcile(m_change_address, other->m_change_address);
|
||||
m_change_amount = gen_utils::reconcile(m_change_amount, other->m_change_amount);
|
||||
m_num_dummy_outputs = gen_utils::reconcile(m_num_dummy_outputs, other->m_num_dummy_outputs);
|
||||
m_extra_hex = gen_utils::reconcile(m_extra_hex, other->m_extra_hex);
|
||||
}
|
||||
|
||||
// -------------------------- MONERO TX REQUEST -----------------------------
|
||||
@@ -281,10 +297,10 @@ namespace monero {
|
||||
}
|
||||
|
||||
// filter on incoming
|
||||
if (m_is_incoming != boost::none && m_is_incoming != tx->is_incoming()) return false;
|
||||
if (m_is_incoming != boost::none && m_is_incoming != tx->m_is_incoming) return false;
|
||||
|
||||
// filter on outgoing
|
||||
if (m_is_outgoing != boost::none && m_is_outgoing != tx->is_outgoing()) return false;
|
||||
if (m_is_outgoing != boost::none && m_is_outgoing != tx->m_is_outgoing) return false;
|
||||
|
||||
// filter on remaining fields
|
||||
boost::optional<uint64_t> txHeight = tx->get_height();
|
||||
|
||||
@@ -258,15 +258,21 @@ 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_unlocked;
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user