Electroneum
unsigned_transaction.cpp
Go to the documentation of this file.
1 // Copyrights(c) 2017-2021, The Electroneum Project
2 // Copyrights(c) 2014-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
31 
32 #include "unsigned_transaction.h"
33 #include "wallet.h"
34 #include "common_defines.h"
35 
38 
39 #include <memory>
40 #include <vector>
41 #include <sstream>
42 #include <boost/format.hpp>
43 
44 using namespace std;
45 
46 namespace Electroneum {
47 
48 UnsignedTransaction::~UnsignedTransaction() {}
49 
50 
51 UnsignedTransactionImpl::UnsignedTransactionImpl(WalletImpl &wallet)
52  : m_wallet(wallet)
53 {
54  m_status = Status_Ok;
55 }
56 
58 {
59  LOG_PRINT_L3("Unsigned tx deleted");
60 }
61 
63 {
64  return m_status;
65 }
66 
68 {
69  return m_errorString;
70 }
71 
72 bool UnsignedTransactionImpl::sign(const std::string &signedFileName)
73 {
74  if(m_wallet.watchOnly())
75  {
76  m_errorString = tr("This is a watch only wallet");
77  m_status = Status_Error;
78  return false;
79  }
80  std::vector<tools::wallet2::pending_tx> ptx;
81  try
82  {
83  bool r = m_wallet.m_wallet->sign_tx(m_unsigned_tx_set, signedFileName, ptx);
84  if (!r)
85  {
86  m_errorString = tr("Failed to sign transaction");
87  m_status = Status_Error;
88  return false;
89  }
90  }
91  catch (const std::exception &e)
92  {
93  m_errorString = string(tr("Failed to sign transaction")) + e.what();
94  m_status = Status_Error;
95  return false;
96  }
97  return true;
98 }
99 
100 //----------------------------------------------------------------------------------------------------
101 bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message)
102 {
103  // gather info to ask the user
104  uint64_t amount = 0, amount_to_dests = 0, change = 0;
105  size_t min_ring_size = ~0;
106  std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
107  int first_known_non_zero_change_index = -1;
108  std::string payment_id_string = "";
109  for (size_t n = 0; n < get_num_txes(); ++n)
110  {
111  const tools::wallet2::tx_construction_data &cd = get_tx(n);
112 
113  std::vector<cryptonote::tx_extra_field> tx_extra_fields;
114  bool has_encrypted_payment_id = false;
115  crypto::hash8 payment_id8 = crypto::null_hash8;
116  if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
117  {
118  cryptonote::tx_extra_nonce extra_nonce;
119  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
120  {
121  crypto::hash payment_id;
123  {
124  if (!payment_id_string.empty())
125  payment_id_string += ", ";
126 
127  crypto::hash payment_id = crypto::null_hash;
128  memcpy(payment_id.data, payment_id8.data, 8); // convert short pid to regular
129  memset(payment_id.data + 8, 0, 24); // merely a sanity check
130  payment_id_string = std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id);
131 
132  }
133  else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
134  {
135  if (!payment_id_string.empty())
136  payment_id_string += ", ";
137  payment_id_string = std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id);
138  }
139  }
140  }
141 
142  for (size_t s = 0; s < cd.sources.size(); ++s)
143  {
144  amount += cd.sources[s].amount;
145  size_t ring_size = cd.sources[s].outputs.size();
146  if (ring_size < min_ring_size)
147  min_ring_size = ring_size;
148  }
149  for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
150  {
151  const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
152  std::string address, standard_address = get_account_address_as_str(m_wallet.m_wallet->nettype(), entry.is_subaddress, entry.addr);
153  if (has_encrypted_payment_id && !entry.is_subaddress)
154  {
155  address = get_account_integrated_address_as_str(m_wallet.m_wallet->nettype(), entry.addr, payment_id8);
156  address += std::string(" (" + standard_address + " with encrypted payment id " + epee::string_tools::pod_to_hex(payment_id8) + ")");
157  }
158  else
159  address = standard_address;
160  auto i = dests.find(entry.addr);
161  if (i == dests.end())
162  dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
163  else
164  i->second.second += entry.amount;
165  amount_to_dests += entry.amount;
166  }
167  if (cd.change_dts.amount > 0)
168  {
169  auto it = dests.find(cd.change_dts.addr);
170  if (it == dests.end())
171  {
172  m_status = Status_Error;
173  m_errorString = tr("Claimed change does not go to a paid address");
174  return false;
175  }
176  if (it->second.second < cd.change_dts.amount)
177  {
178  m_status = Status_Error;
179  m_errorString = tr("Claimed change is larger than payment to the change address");
180  return false;
181  }
182  if (cd.change_dts.amount > 0)
183  {
184  if (first_known_non_zero_change_index == -1)
185  first_known_non_zero_change_index = n;
186  if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr)))
187  {
188  m_status = Status_Error;
189  m_errorString = tr("Change goes to more than one address");
190  return false;
191  }
192  }
193  change += cd.change_dts.amount;
194  it->second.second -= cd.change_dts.amount;
195  if (it->second.second == 0)
196  dests.erase(cd.change_dts.addr);
197  }
198  }
199  std::string dest_string;
200  for (auto i = dests.begin(); i != dests.end(); )
201  {
202  dest_string += (boost::format(tr("sending %s to %s")) % cryptonote::print_etn(i->second.second) % i->second.first).str();
203  ++i;
204  if (i != dests.end())
205  dest_string += ", ";
206  }
207  if (dest_string.empty())
208  dest_string = tr("with no destinations");
209 
210  std::string change_string;
211  if (change > 0)
212  {
213  std::string address = get_account_address_as_str(m_wallet.m_wallet->nettype(), get_tx(0).subaddr_account > 0, get_tx(0).change_dts.addr);
214  change_string += (boost::format(tr("%s change to %s")) % cryptonote::print_etn(change) % address).str();
215  }
216  else
217  change_string += tr("no change");
218  uint64_t fee = amount - amount_to_dests;
219  m_confirmationMessage = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu. %s")) % (unsigned long)get_num_txes() % cryptonote::print_etn(amount) % cryptonote::print_etn(fee) % dest_string % change_string % (unsigned long)min_ring_size % extra_message).str();
220  return true;
221 }
222 
223 std::vector<uint64_t> UnsignedTransactionImpl::amount() const
224 {
225  std::vector<uint64_t> result;
226  for (const auto &utx : m_unsigned_tx_set.txes) {
227  for (const auto &unsigned_dest : utx.dests) {
228  result.push_back(unsigned_dest.amount);
229  }
230  }
231  return result;
232 }
233 
234 std::vector<uint64_t> UnsignedTransactionImpl::fee() const
235 {
236  std::vector<uint64_t> result;
237  for (const auto &utx : m_unsigned_tx_set.txes) {
238  uint64_t fee = 0;
239  for (const auto &i: utx.sources) fee += i.amount;
240  for (const auto &i: utx.splitted_dsts) fee -= i.amount;
241  result.push_back(fee);
242  }
243  return result;
244 }
245 
246 std::vector<uint64_t> UnsignedTransactionImpl::mixin() const
247 {
248  std::vector<uint64_t> result;
249  for (const auto &utx: m_unsigned_tx_set.txes) {
250  size_t min_mixin = ~0;
251  // TODO: Is this loop needed or is sources[0] ?
252  for (size_t s = 0; s < utx.sources.size(); ++s) {
253  size_t mixin = utx.sources[s].outputs.size() - 1;
254  if (mixin < min_mixin)
255  min_mixin = mixin;
256  }
257  result.push_back(min_mixin);
258  }
259  return result;
260 }
261 
263 {
264  return m_unsigned_tx_set.txes.size();
265 }
266 
267 std::vector<std::string> UnsignedTransactionImpl::paymentId() const
268 {
269  std::vector<string> result;
270  for (const auto &utx: m_unsigned_tx_set.txes) {
271  crypto::hash payment_id = crypto::null_hash;
272  cryptonote::tx_extra_nonce extra_nonce;
273  std::vector<cryptonote::tx_extra_field> tx_extra_fields;
274  cryptonote::parse_tx_extra(utx.extra, tx_extra_fields);
275  if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
276  {
277  crypto::hash8 payment_id8 = crypto::null_hash8;
279  {
280  // We can't decrypt short pid without recipient key.
281  memcpy(payment_id.data, payment_id8.data, 8);
282  }
283  else if (!cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
284  {
285  payment_id = crypto::null_hash;
286  }
287  }
288  if(payment_id != crypto::null_hash)
289  result.push_back(epee::string_tools::pod_to_hex(payment_id));
290  else
291  result.push_back("");
292  }
293  return result;
294 }
295 
296 std::vector<std::string> UnsignedTransactionImpl::recipientAddress() const
297 {
298  // TODO: return integrated address if short payment ID exists
299  std::vector<string> result;
300  for (const auto &utx: m_unsigned_tx_set.txes) {
301  if (utx.dests.empty()) {
302  MERROR("empty destinations, skipped");
303  continue;
304  }
305  result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->nettype(), utx.dests[0].is_subaddress, utx.dests[0].addr));
306  }
307  return result;
308 }
309 
311 {
312  uint64_t min_mixin = ~0;
313  for (const auto &utx: m_unsigned_tx_set.txes) {
314  for (size_t s = 0; s < utx.sources.size(); ++s) {
315  size_t mixin = utx.sources[s].outputs.size() - 1;
316  if (mixin < min_mixin)
317  min_mixin = mixin;
318  }
319  }
320  return min_mixin;
321 }
322 
323 } // namespace
324 
325 namespace Bitelectroneum = Electroneum;
326 
#define tr(x)
Definition: common_defines.h:4
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
#define MERROR(x)
Definition: misc_log_ex.h:73
std::vector< uint64_t > fee() const override
std::vector< std::string > recipientAddress() const override
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
bool sign(const std::string &signedFileName) override
sign - Sign txs and saves to file
::std::string string
Definition: gtest-port.h:1097
std::string print_etn(uint64_t amount, unsigned int decimal_point)
std::vector< uint64_t > mixin() const override
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:423
STL namespace.
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:422
std::vector< tx_construction_data > txes
Definition: wallet2.h:499
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
unsigned __int64 uint64_t
Definition: stdint.h:136
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition: wallet2.h:424
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
uint64_t amount
bool is_subaddress
std::vector< uint8_t > extra
Definition: wallet2.h:426
#define LOG_PRINT_L3(x)
Definition: misc_log_ex.h:102
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
account_public_address addr
POD_CLASS hash8
Definition: hash.h:53
std::string errorString() const override
bool watchOnly() const override
watchOnly - checks if wallet is watch only
Definition: wallet.cpp:2043
void * memcpy(void *a, const void *b, size_t c)
std::vector< uint64_t > amount() const override
POD_CLASS hash
Definition: hash.h:50
const char * address
Definition: multisig.cpp:37
std::vector< std::string > paymentId() const override
uint64_t txCount() const override
txCount - number of transactions current transaction will be splitted to