· 7 years ago · Apr 23, 2018, 09:50 PM
1//
2// Created by marcin on 5/11/15.
3//
4
5#include "tools.h"
6#include <codecvt>
7
8
9namespace xmreg
10{
11
12/**
13 * Parse key string, e.g., a viewkey in a string
14 * into crypto::secret_key or crypto::public_key
15 * depending on the template argument.
16 */
17template <typename T>
18bool
19parse_str_secret_key(const string& key_str, T& secret_key)
20{
21
22 // hash and keys have same structure, so to parse string of
23 // a key, e.g., a view key, we can first parse it into the hash
24 // object using parse_hash256 function, and then copy the reslting
25 // hash data into secret key.
26 crypto::hash hash_;
27
28 if(!parse_hash256(key_str, hash_))
29 {
30 cerr << "Cant parse a key (e.g. viewkey): " << key_str << endl;
31 return false;
32 }
33
34 // crypto::hash and crypto::secret_key have basicly same
35 // structure. They both keep they key/hash as c-style char array
36 // of fixed size. Thus we can just copy data from hash
37 // to key
38 copy(begin(hash_.data), end(hash_.data), secret_key.data);
39
40 return true;
41}
42
43// explicit instantiations of get template function
44template bool parse_str_secret_key<crypto::secret_key>(const string& key_str, crypto::secret_key& secret_key);
45template bool parse_str_secret_key<crypto::public_key>(const string& key_str, crypto::public_key& secret_key);
46template bool parse_str_secret_key<crypto::hash>(const string& key_str, crypto::hash& secret_key);
47
48/**
49 * Get transaction tx using given tx hash. Hash is represent as string here,
50 * so before we can tap into the blockchain, we need to pare it into
51 * crypto::hash object.
52 */
53bool
54get_tx_pub_key_from_str_hash(Blockchain& core_storage, const string& hash_str, transaction& tx)
55{
56 crypto::hash tx_hash;
57 parse_hash256(hash_str, tx_hash);
58
59 try
60 {
61 // get transaction with given hash
62 tx = core_storage.get_db().get_tx(tx_hash);
63 }
64 catch (const TX_DNE& e)
65 {
66 cerr << e.what() << endl;
67 return false;
68 }
69
70 return true;
71}
72
73/**
74 * Parse monero address in a string form into
75 * cryptonote::account_public_address object
76 */
77bool
78parse_str_address(const string& address_str,
79 account_public_address& address,
80 bool testnet)
81{
82
83 if (!get_account_address_from_str(address, testnet, address_str))
84 {
85 cerr << "Error getting address: " << address_str << endl;
86 return false;
87 }
88
89 return true;
90}
91
92
93/**
94 * Return string representation of monero address
95 */
96string
97print_address(const account_public_address& address, bool testnet)
98{
99 return "<" + get_account_address_as_str(testnet, address) + ">";
100}
101
102string
103print_sig (const signature& sig)
104{
105 stringstream ss;
106
107 ss << "c: <" << epee::string_tools::pod_to_hex(sig.c) << "> "
108 << "r: <" << epee::string_tools::pod_to_hex(sig.r) << ">";
109
110 return ss.str();
111}
112
113/**
114 * Check if a character is a path seprator
115 */
116inline bool
117is_separator(char c)
118{
119 // default linux path separator
120 const char separator = PATH_SEPARARTOR;
121
122 return c == separator;
123}
124
125
126
127/**
128 * Remove trailinig path separator.
129 */
130string
131remove_trailing_path_separator(const string& in_path)
132{
133 string new_string = in_path;
134 if (!new_string.empty() && is_separator(new_string[new_string.size() - 1]))
135 new_string.erase(new_string.size() - 1);
136 return new_string;
137}
138
139bf::path
140remove_trailing_path_separator(const bf::path& in_path)
141{
142 string path_str = in_path.native();
143 return bf::path(remove_trailing_path_separator(path_str));
144}
145
146
147string
148timestamp_to_str_gm(time_t timestamp, const char* format)
149{
150 const time_t* t = ×tamp;
151
152 const int TIME_LENGTH = 60;
153
154 char str_buff[TIME_LENGTH];
155
156 std::tm tmp;
157 gmtime_r(t, &tmp);
158
159 size_t len;
160
161 len = std::strftime(str_buff, TIME_LENGTH, format, &tmp);
162
163 return string(str_buff, len);
164}
165
166string
167timestamp_to_str_local(time_t timestamp, const char* format)
168{
169
170 const int TIME_LENGTH = 60;
171
172 char str_buff[TIME_LENGTH];
173
174 tm *tm_ptr;
175 tm_ptr = localtime(×tamp);
176
177 size_t len;
178
179 len = std::strftime(str_buff, TIME_LENGTH, format, tm_ptr);
180
181 return string(str_buff, len);
182}
183
184
185ostream&
186operator<< (ostream& os, const account_public_address& addr)
187{
188 os << get_account_address_as_str(false, addr);
189 return os;
190}
191
192
193/*
194 * Generate key_image of foran ith output
195 */
196bool
197generate_key_image(const crypto::key_derivation& derivation,
198 const std::size_t i,
199 const crypto::secret_key& sec_key,
200 const crypto::public_key& pub_key,
201 crypto::key_image& key_img)
202{
203
204 cryptonote::keypair in_ephemeral;
205
206 if (!crypto::derive_public_key(derivation, i,
207 pub_key,
208 in_ephemeral.pub))
209 {
210 cerr << "Error generating publick key " << pub_key << endl;
211 return false;
212 }
213
214 try
215 {
216
217 crypto::derive_secret_key(derivation, i,
218 sec_key,
219 in_ephemeral.sec);
220 }
221 catch(const std::exception& e)
222 {
223 cerr << "Error generate secret image: " << e.what() << endl;
224 return false;
225 }
226
227
228 try
229 {
230 crypto::generate_key_image(in_ephemeral.pub,
231 in_ephemeral.sec,
232 key_img);
233 }
234 catch(const std::exception& e)
235 {
236 cerr << "Error generate key image: " << e.what() << endl;
237 return false;
238 }
239
240 return true;
241}
242
243
244string
245get_default_lmdb_folder(bool testnet)
246{
247 // default path to monero folder
248 // on linux this is /home/<username>/.bitmonero
249 string default_monero_dir = tools::get_default_data_dir();
250
251 if (testnet)
252 default_monero_dir += "/testnet";
253
254
255 // the default folder of the lmdb blockchain database
256 // is therefore as follows
257 return default_monero_dir + string("/lmdb");
258}
259
260
261/*
262 * Ge blockchain exception from command line option
263 *
264 * If not given, provide default path
265 */
266bool
267get_blockchain_path(bf::path& blockchain_path,
268 bool testnet)
269{
270 // the default folder of the lmdb blockchain database
271 string default_lmdb_dir = xmreg::get_default_lmdb_folder(testnet);
272
273
274 blockchain_path = !blockchain_path.empty()
275 ? blockchain_path
276 : bf::path(default_lmdb_dir);
277
278 if (!bf::is_directory(blockchain_path))
279 {
280 cerr << "Given path \"" << blockchain_path << "\" "
281 << "is not a folder or does not exist" << " "
282 << endl;
283
284 return false;
285 }
286
287 blockchain_path = xmreg::remove_trailing_path_separator(blockchain_path);
288
289 return true;
290}
291
292
293
294
295array<uint64_t, 4>
296summary_of_in_out_rct(
297 const transaction& tx,
298 vector<pair<txout_to_key, uint64_t>>& output_pub_keys,
299 vector<txin_to_key>& input_key_imgs)
300{
301
302 uint64_t xmr_outputs {0};
303 uint64_t xmr_inputs {0};
304 uint64_t mixin_no {0};
305 uint64_t num_nonrct_inputs {0};
306
307
308 for (const tx_out& txout: tx.vout)
309 {
310 if (txout.target.type() != typeid(txout_to_key))
311 {
312 // push empty pair.
313 output_pub_keys.push_back(pair<txout_to_key, uint64_t>{});
314 continue;
315 }
316
317 // get tx input key
318 const txout_to_key& txout_key
319 = boost::get<cryptonote::txout_to_key>(txout.target);
320
321 output_pub_keys.push_back(make_pair(txout_key, txout.amount));
322
323 xmr_outputs += txout.amount;
324 }
325
326 size_t input_no = tx.vin.size();
327
328 for (size_t i = 0; i < input_no; ++i)
329 {
330
331 if(tx.vin[i].type() != typeid(cryptonote::txin_to_key))
332 {
333 continue;
334 }
335
336 // get tx input key
337 const cryptonote::txin_to_key& tx_in_to_key
338 = boost::get<cryptonote::txin_to_key>(tx.vin[i]);
339
340 xmr_inputs += tx_in_to_key.amount;
341
342 if (tx_in_to_key.amount != 0)
343 {
344 ++num_nonrct_inputs;
345 }
346
347 if (mixin_no == 0)
348 {
349 mixin_no = tx_in_to_key.key_offsets.size();
350 }
351
352 input_key_imgs.push_back(tx_in_to_key);
353
354 } // for (size_t i = 0; i < input_no; ++i)
355
356
357 return {xmr_outputs, xmr_inputs, mixin_no, num_nonrct_inputs};
358};
359
360
361// this version for mempool txs from json
362array<uint64_t, 6>
363summary_of_in_out_rct(const json& _json)
364{
365 uint64_t xmr_outputs {0};
366 uint64_t xmr_inputs {0};
367 uint64_t no_outputs {0};
368 uint64_t no_inputs {0};
369 uint64_t mixin_no {0};
370 uint64_t num_nonrct_inputs {0};
371
372 for (const json& vout: _json["vout"])
373 {
374 xmr_outputs += vout["amount"].get<uint64_t>();
375 }
376
377 no_outputs = _json["vout"].size();
378
379 for (const json& vin: _json["vin"])
380 {
381 uint64_t amount = vin["key"]["amount"].get<uint64_t>();
382
383 xmr_inputs += amount;
384
385 if (amount != 0)
386 ++num_nonrct_inputs;
387 }
388
389 no_inputs = _json["vin"].size();
390
391 mixin_no = _json["vin"].at(0)["key"]["key_offsets"].size() - 1;
392
393 return {xmr_outputs, xmr_inputs, no_outputs, no_inputs, mixin_no, num_nonrct_inputs};
394};
395
396
397
398 uint64_t
399sum_money_in_outputs(const transaction& tx)
400{
401 uint64_t sum_xmr {0};
402
403 for (const tx_out& txout: tx.vout)
404 {
405 sum_xmr += txout.amount;
406 }
407
408 return sum_xmr;
409}
410
411pair<uint64_t, uint64_t>
412sum_money_in_outputs(const string& json_str)
413{
414 pair<uint64_t, uint64_t> sum_xmr {0, 0};
415
416 json j;
417
418 try
419 {
420 j = json::parse( json_str);
421 }
422 catch (std::invalid_argument& e)
423 {
424 cerr << "sum_money_in_outputs: " << e.what() << endl;
425 return sum_xmr;
426 }
427
428 for (json& vout: j["vout"])
429 {
430 sum_xmr.first += vout["amount"].get<uint64_t>();
431 ++sum_xmr.second;
432 }
433
434
435 return sum_xmr;
436};
437
438
439
440uint64_t
441sum_money_in_inputs(const transaction& tx)
442{
443 uint64_t sum_xmr {0};
444
445 size_t input_no = tx.vin.size();
446
447 for (size_t i = 0; i < input_no; ++i)
448 {
449
450 if(tx.vin[i].type() != typeid(cryptonote::txin_to_key))
451 {
452 continue;
453 }
454
455 // get tx input key
456 const cryptonote::txin_to_key& tx_in_to_key
457 = boost::get<cryptonote::txin_to_key>(tx.vin[i]);
458
459 sum_xmr += tx_in_to_key.amount;
460 }
461
462 return sum_xmr;
463}
464
465pair<uint64_t, uint64_t>
466sum_money_in_inputs(const string& json_str)
467{
468 pair<uint64_t, uint64_t> sum_xmr {0, 0};
469
470 json j;
471 try
472 {
473 j = json::parse( json_str);
474 }
475 catch (std::invalid_argument& e)
476 {
477 cerr << "sum_money_in_outputs: " << e.what() << endl;
478 return sum_xmr;
479 }
480
481 for (json& vin: j["vin"])
482 {
483 sum_xmr.first += vin["key"]["amount"].get<uint64_t>();
484 ++sum_xmr.second;
485 }
486
487 return sum_xmr;
488};
489
490array<uint64_t, 2>
491sum_money_in_tx(const transaction& tx)
492{
493 array<uint64_t, 2> sum_xmr;
494
495 sum_xmr[0] = sum_money_in_inputs(tx);
496 sum_xmr[1] = sum_money_in_outputs(tx);
497
498 return sum_xmr;
499};
500
501
502array<uint64_t, 2>
503sum_money_in_txs(const vector<transaction>& txs)
504{
505 array<uint64_t, 2> sum_xmr {0,0};
506
507 for (const transaction& tx: txs)
508 {
509 sum_xmr[0] += sum_money_in_inputs(tx);
510 sum_xmr[1] += sum_money_in_outputs(tx);
511 }
512
513 return sum_xmr;
514};
515
516
517uint64_t
518sum_fees_in_txs(const vector<transaction>& txs)
519{
520 uint64_t fees_sum {0};
521
522 for (const transaction& tx: txs)
523 {
524 fees_sum += get_tx_fee(tx);
525 }
526
527 return fees_sum;
528}
529
530
531
532vector<pair<txout_to_key, uint64_t>>
533get_ouputs(const transaction& tx)
534{
535 vector<pair<txout_to_key, uint64_t>> outputs;
536
537 for (const tx_out& txout: tx.vout)
538 {
539 if (txout.target.type() != typeid(txout_to_key))
540 {
541 continue;
542 }
543
544 // get tx input key
545 const txout_to_key& txout_key
546 = boost::get<cryptonote::txout_to_key>(txout.target);
547
548 outputs.push_back(make_pair(txout_key, txout.amount));
549 }
550
551 return outputs;
552
553};
554
555vector<tuple<txout_to_key, uint64_t, uint64_t>>
556get_ouputs_tuple(const transaction& tx)
557{
558 vector<tuple<txout_to_key, uint64_t, uint64_t>> outputs;
559
560 for (uint64_t n = 0; n < tx.vout.size(); ++n)
561 {
562
563 if (tx.vout[n].target.type() != typeid(txout_to_key))
564 {
565 continue;
566 }
567
568 // get tx input key
569 const txout_to_key& txout_key
570 = boost::get<cryptonote::txout_to_key>(tx.vout[n].target);
571
572 outputs.push_back(make_tuple(txout_key, tx.vout[n].amount, n));
573 }
574
575 return outputs;
576};
577
578uint64_t
579get_mixin_no(const transaction& tx)
580{
581 uint64_t mixin_no {0};
582
583 size_t input_no = tx.vin.size();
584
585 for (size_t i = 0; i < input_no; ++i)
586 {
587
588 if(tx.vin[i].type() != typeid(cryptonote::txin_to_key))
589 {
590 continue;
591 }
592
593 // get tx input key
594 const txin_to_key& tx_in_to_key
595 = boost::get<cryptonote::txin_to_key>(tx.vin[i]);
596
597 mixin_no = tx_in_to_key.key_offsets.size();
598
599 // look for first mixin number.
600 // all inputs in a single transaction have same number
601 if (mixin_no > 0)
602 {
603 break;
604 }
605 }
606
607 return mixin_no;
608}
609vector<uint64_t>
610get_mixin_no(const string& json_str)
611{
612 vector<uint64_t> mixin_no;
613
614 json j;
615
616 try
617 {
618 j = json::parse(json_str);
619
620 mixin_no.push_back(j["vin"].at(0)["key"]["key_offsets"].size());
621 }
622 catch (std::invalid_argument& e)
623 {
624 cerr << "get_mixin_no: " << e.what() << endl;
625 return mixin_no;
626 }
627
628 return mixin_no;
629}
630
631
632
633vector<uint64_t>
634get_mixin_no_in_txs(const vector<transaction>& txs)
635{
636 vector<uint64_t> mixin_no;
637
638 for (const transaction& tx: txs)
639 {
640 mixin_no.push_back(get_mixin_no(tx));
641 }
642
643 return mixin_no;
644}
645
646
647vector<txin_to_key>
648get_key_images(const transaction& tx)
649{
650 vector<txin_to_key> key_images;
651
652 size_t input_no = tx.vin.size();
653
654 for (size_t i = 0; i < input_no; ++i)
655 {
656
657 if(tx.vin[i].type() != typeid(txin_to_key))
658 {
659 continue;
660 }
661
662 // get tx input key
663 const txin_to_key& tx_in_to_key
664 = boost::get<cryptonote::txin_to_key>(tx.vin[i]);
665
666 key_images.push_back(tx_in_to_key);
667 }
668
669 return key_images;
670}
671
672bool
673get_payment_id(const vector<uint8_t>& extra,
674 crypto::hash& payment_id,
675 crypto::hash8& payment_id8)
676{
677 payment_id = null_hash;
678 payment_id8 = null_hash8;
679
680 std::vector<tx_extra_field> tx_extra_fields;
681
682 if(!parse_tx_extra(extra, tx_extra_fields))
683 {
684 return false;
685 }
686
687 tx_extra_nonce extra_nonce;
688
689 if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
690 {
691 // first check for encrypted id and then for normal one
692 if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
693 {
694 return true;
695 }
696 else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
697 {
698 return true;
699 }
700 }
701
702 return false;
703}
704
705
706bool
707get_payment_id(const transaction& tx,
708 crypto::hash& payment_id,
709 crypto::hash8& payment_id8)
710{
711 return get_payment_id(tx.extra, payment_id, payment_id8);
712}
713
714
715array<size_t, 5>
716timestamp_difference(uint64_t t1, uint64_t t2)
717{
718
719 uint64_t timestamp_diff = t1 - t2;
720
721 // calculate difference of timestamps from current block to the mixin one
722 if (t2 > t1)
723 {
724 timestamp_diff = t2 - t1;
725 }
726
727 uint64_t time_diff_years = timestamp_diff / 31536000;
728
729 timestamp_diff -= time_diff_years * 31536000;
730
731 uint64_t time_diff_days = timestamp_diff / 86400;
732
733 timestamp_diff -= time_diff_days * 86400;
734
735 uint64_t time_diff_hours = timestamp_diff / 3600;
736
737 timestamp_diff -= time_diff_hours * 3600;
738
739 uint64_t time_diff_minutes = timestamp_diff / 60;
740
741 timestamp_diff -= time_diff_minutes * 60;
742
743 uint64_t time_diff_seconds = timestamp_diff ;
744
745 return array<size_t, 5> {time_diff_years, time_diff_days,
746 time_diff_hours, time_diff_minutes,
747 time_diff_seconds};
748
749};
750
751
752string
753read(string filename)
754{
755 if (!bf::exists(bf::path(filename)))
756 {
757 cerr << "File does not exist: " << filename << endl;
758 return string();
759 }
760
761 std::ifstream t(filename);
762 return string(std::istreambuf_iterator<char>(t),
763 std::istreambuf_iterator<char>());
764}
765
766pair<string, double>
767timestamps_time_scale(const vector<uint64_t>& timestamps,
768 uint64_t timeN,
769 uint64_t resolution,
770 uint64_t time0)
771{
772 string empty_time = string(resolution, '_');
773
774 size_t time_axis_length = empty_time.size();
775
776 uint64_t interval_length = timeN-time0;
777
778 double scale = double(interval_length) / double(time_axis_length);
779
780 for (const auto& timestamp: timestamps)
781 {
782
783 if (timestamp < time0 || timestamp > timeN)
784 {
785 cout << "Out of range" << endl;
786 continue;
787 }
788
789 uint64_t timestamp_place = double(timestamp-time0)
790 / double(interval_length)*(time_axis_length - 1);
791
792 empty_time[timestamp_place + 1] = '*';
793 }
794
795 return make_pair(empty_time, scale);
796}
797
798// useful reference to get epoch time in correct timezon
799// http://www.boost.org/doc/libs/1_41_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch
800time_t
801ptime_to_time_t(const pt::ptime& in_ptime)
802{
803 static pt::ptime epoch(gt::date(1970, 1, 1));
804 pt::time_duration::sec_type no_seconds = (in_ptime - epoch).total_seconds();
805 return time_t(no_seconds);
806}
807
808bool
809decode_ringct(const rct::rctSig& rv,
810 const crypto::public_key &pub,
811 const crypto::secret_key &sec,
812 unsigned int i,
813 rct::key & mask,
814 uint64_t & amount)
815{
816 crypto::key_derivation derivation;
817
818 bool r = crypto::generate_key_derivation(pub, sec, derivation);
819
820 if (!r)
821 {
822 cerr <<"Failed to generate key derivation to decode rct output " << i << endl;
823 return false;
824 }
825
826 crypto::secret_key scalar1;
827
828 crypto::derivation_to_scalar(derivation, i, scalar1);
829
830 try
831 {
832 switch (rv.type)
833 {
834 case rct::RCTTypeSimple:
835 amount = rct::decodeRctSimple(rv,
836 rct::sk2rct(scalar1),
837 i,
838 mask);
839 break;
840 case rct::RCTTypeFull:
841 amount = rct::decodeRct(rv,
842 rct::sk2rct(scalar1),
843 i,
844 mask);
845 break;
846 default:
847 cerr << "Unsupported rct type: " << rv.type << endl;
848 return false;
849 }
850 }
851 catch (const std::exception &e)
852 {
853 cerr << "Failed to decode input " << i << endl;
854 return false;
855 }
856
857 return true;
858}
859
860bool
861url_decode(const std::string& in, std::string& out)
862{
863 out.clear();
864 out.reserve(in.size());
865 for (std::size_t i = 0; i < in.size(); ++i)
866 {
867 if (in[i] == '%')
868 {
869 if (i + 3 <= in.size())
870 {
871 int value = 0;
872 std::istringstream is(in.substr(i + 1, 2));
873 if (is >> std::hex >> value)
874 {
875 out += static_cast<char>(value);
876 i += 2;
877 }
878 else
879 {
880 return false;
881 }
882 }
883 else
884 {
885 return false;
886 }
887 }
888 else if (in[i] == '+')
889 {
890 out += ' ';
891 }
892 else
893 {
894 out += in[i];
895 }
896 }
897 return true;
898}
899
900map<std::string, std::string>
901parse_crow_post_data(const string& req_body)
902{
903 map<std::string, std::string> body;
904
905 vector<string> vec;
906 string tmp;
907 bool result = url_decode(req_body, tmp);
908 if (result)
909 {
910 boost::algorithm::split(vec, tmp, [](char x) {return x == '&'; });
911 for(auto &it : vec)
912 {
913 auto pos = it.find("=");
914 if (pos != string::npos)
915 {
916 body[it.substr(0, pos)] = it.substr(pos + 1);
917 }
918 else
919 {
920 break;
921 }
922 }
923 }
924 return body;
925}
926
927
928// from wallet2::decrypt
929string
930decrypt(const std::string &ciphertext,
931 const crypto::secret_key &skey,
932 bool authenticated)
933{
934
935 const size_t prefix_size = sizeof(chacha8_iv)
936 + (authenticated ? sizeof(crypto::signature) : 0);
937 if (ciphertext.size() < prefix_size)
938 {
939 cerr << "Unexpected ciphertext size" << endl;
940 return {};
941 }
942
943 crypto::chacha8_key key;
944 crypto::generate_chacha8_key(&skey, sizeof(skey), key);
945
946 const crypto::chacha8_iv &iv = *(const crypto::chacha8_iv*)&ciphertext[0];
947
948 std::string plaintext;
949
950 plaintext.resize(ciphertext.size() - prefix_size);
951
952 if (authenticated)
953 {
954 crypto::hash hash;
955 crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash);
956 crypto::public_key pkey;
957 crypto::secret_key_to_public_key(skey, pkey);
958
959 const crypto::signature &signature =
960 *(const crypto::signature*)&ciphertext[ciphertext.size()
961 - sizeof(crypto::signature)];
962
963 if (!crypto::check_signature(hash, pkey, signature))
964 {
965 cerr << "Failed to authenticate criphertext" << endl;
966 return {};
967 }
968
969 }
970
971 crypto::chacha8(ciphertext.data() + sizeof(iv),
972 ciphertext.size() - prefix_size,
973 key, iv, &plaintext[0]);
974
975 return plaintext;
976}
977
978// based on
979// crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
980public_key
981get_tx_pub_key_from_received_outs(const transaction &tx)
982{
983 std::vector<tx_extra_field> tx_extra_fields;
984
985 if(!parse_tx_extra(tx.extra, tx_extra_fields))
986 {
987 // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
988 }
989
990 // Due to a previous bug, there might be more than one tx pubkey in extra, one being
991 // the result of a previously discarded signature.
992 // For speed, since scanning for outputs is a slow process, we check whether extra
993 // contains more than one pubkey. If not, the first one is returned. If yes, they're
994 // checked for whether they yield at least one output
995 tx_extra_pub_key pub_key_field;
996
997 if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0))
998 {
999 return null_pkey;
1000 }
1001
1002 public_key tx_pub_key = pub_key_field.pub_key;
1003
1004 bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1);
1005
1006 if (!two_found)
1007 {
1008 // easy case, just one found
1009 return tx_pub_key;
1010 }
1011 else
1012 {
1013 // just return second one if there are two.
1014 // this does not require private view key, as
1015 // its not needed for my use case.
1016 return pub_key_field.pub_key;
1017 }
1018
1019 return null_pkey;
1020}
1021
1022
1023
1024string
1025xmr_amount_to_str(const uint64_t& xmr_amount, string format)
1026{
1027 return fmt::format("{:0.12f}", XMR_AMOUNT(xmr_amount));
1028}
1029
1030
1031
1032/**
1033* Check if given output (specified by output_index)
1034* belongs is ours based
1035* on our private view key and public spend key
1036*/
1037bool
1038is_output_ours(const size_t& output_index,
1039 const transaction& tx,
1040 const public_key& pub_tx_key,
1041 const secret_key& private_view_key,
1042 const public_key& public_spend_key)
1043{
1044 // public transaction key is combined with our viewkey
1045 // to create, so called, derived key.
1046 key_derivation derivation;
1047
1048 if (!generate_key_derivation(pub_tx_key, private_view_key, derivation))
1049 {
1050 cerr << "Cant get dervied key for: " << "\n"
1051 << "pub_tx_key: " << pub_tx_key << " and "
1052 << "prv_view_key" << private_view_key << endl;
1053
1054 return false;
1055 }
1056
1057
1058 // get the tx output public key
1059 // that normally would be generated for us,
1060 // if someone had sent us some xmr.
1061 public_key pubkey;
1062
1063 derive_public_key(derivation,
1064 output_index,
1065 public_spend_key,
1066 pubkey);
1067
1068 //cout << "\n" << tx.vout.size() << " " << output_index << endl;
1069
1070 // get tx output public key
1071 const txout_to_key tx_out_to_key
1072 = boost::get<txout_to_key>(tx.vout[output_index].target);
1073
1074
1075 if (tx_out_to_key.key == pubkey)
1076 {
1077 return true;
1078 }
1079
1080 return false;
1081}
1082
1083
1084bool
1085get_real_output_for_key_image(const key_image& ki,
1086 const transaction& tx,
1087 const secret_key& private_view_key,
1088 const public_key& public_spend_key,
1089 uint64_t output_idx,
1090 public_key output_pub_key)
1091{
1092
1093
1094
1095 return false;
1096}
1097
1098
1099bool
1100make_tx_from_json(const string& json_str, transaction& tx)
1101{
1102 json j;
1103
1104 try
1105 {
1106 j = json::parse(json_str);
1107 }
1108 catch (std::invalid_argument& e)
1109 {
1110 cerr << "make_tx_from_json: cant parse json string: " << e.what() << endl;
1111 return false;
1112 }
1113
1114 // get version and unlock time from json
1115 tx.version = j["version"].get<size_t>();
1116 tx.unlock_time = j["unlock_time"].get<uint64_t>();
1117
1118 // next get extra data
1119 for (json& extra_val: j["extra"])
1120 tx.extra.push_back(extra_val.get<uint8_t>());
1121
1122
1123 // now populate output data from json
1124 vector<tx_out>& tx_outputs = tx.vout;
1125
1126 for (json& vo: j["vout"])
1127 {
1128 uint64_t amount = vo["amount"].get<uint64_t>();
1129
1130 public_key out_pub_key;
1131
1132 if (!epee::string_tools::hex_to_pod(vo["target"]["key"], out_pub_key))
1133 {
1134 cerr << "Faild to parse public_key of an output from json" << endl;
1135 return false;
1136 }
1137
1138 txout_target_v target {txout_to_key {out_pub_key}};
1139
1140 tx_outputs.push_back(tx_out {amount, target});
1141 }
1142
1143 // now populate input data from json
1144 vector<txin_v>& tx_inputs = tx.vin;
1145
1146 for (json& vi: j["vin"])
1147 {
1148 uint64_t amount = vi["key"]["amount"].get<uint64_t>();
1149
1150 key_image in_key_image;
1151
1152 if (!epee::string_tools::hex_to_pod(vi["key"]["k_image"], in_key_image))
1153 {
1154 cerr << "Faild to parse key_image of an input from json" << endl;
1155 return false;
1156 }
1157
1158 vector<uint64_t> key_offsets;
1159
1160 for (json& ko: vi["key"]["key_offsets"])
1161 {
1162 key_offsets.push_back(ko.get<uint64_t>());
1163 }
1164
1165 txin_v _txin_v {txin_to_key {amount, key_offsets, in_key_image}};
1166
1167 tx_inputs.push_back(_txin_v);
1168 }
1169
1170 // add ring signatures field
1171
1172 if (j.find("signatures") != j.end())
1173 {
1174 vector<vector<signature>>& signatures = tx.signatures;
1175
1176 for (json& sigs: j["signatures"])
1177 {
1178 string concatanted_sig = sigs;
1179
1180 vector<signature> sig_split;
1181
1182 auto split_sig = [&](string::iterator &b, string::iterator &e)
1183 {
1184 signature a_sig;
1185
1186 if (!epee::string_tools::hex_to_pod(string(b, e), a_sig))
1187 {
1188 cerr << "Faild to parse signature from json" << endl;
1189 return false;
1190 }
1191
1192 sig_split.push_back(a_sig);
1193
1194 return true;
1195 };
1196
1197 chunks(concatanted_sig.begin(), concatanted_sig.end(), 128, split_sig);
1198
1199 signatures.push_back(sig_split);
1200 }
1201
1202 }
1203
1204 // now add rct_signatures from json to tx if exist
1205
1206 if (j.find("rct_signatures") != j.end())
1207 {
1208 rct::rctSig& rct_signatures = tx.rct_signatures;
1209
1210 if (j["rct_signatures"].find("pseudoOuts") != j["rct_signatures"].end())
1211 {
1212 rct::keyV& pseudoOuts = rct_signatures.pseudoOuts;
1213
1214 for (json& pOut: j["rct_signatures"]["pseudoOuts"])
1215 {
1216 rct::key pOut_key;
1217
1218 if (!epee::string_tools::hex_to_pod(pOut, pOut_key))
1219 {
1220 cerr << "Faild to parse pOut_key of pseudoOuts from json" << endl;
1221 return false;
1222 }
1223
1224 pseudoOuts.push_back(pOut_key);
1225 }
1226 }
1227
1228 vector<rct::ecdhTuple>& ecdhInfo = rct_signatures.ecdhInfo;
1229
1230 for (json& ecdhI: j["rct_signatures"]["ecdhInfo"])
1231 {
1232 rct::ecdhTuple a_tuple;
1233
1234 //cout << "ecdhI[\"amount\"]: " << ecdhI["amount"] << endl;
1235
1236 if (!epee::string_tools::hex_to_pod(ecdhI["amount"], a_tuple.amount))
1237 {
1238 cerr << "Faild to parse ecdhInfo of an amount from json" << endl;
1239 return false;
1240 }
1241
1242 //cout << epee::string_tools::pod_to_hex(a_tuple.amount) << endl;
1243
1244 if (!epee::string_tools::hex_to_pod(ecdhI["mask"], a_tuple.mask))
1245 {
1246 cerr << "Faild to parse ecdhInfo of an mask from json" << endl;
1247 return false;
1248 }
1249
1250 ecdhInfo.push_back(a_tuple);
1251 }
1252
1253 vector<rct::ctkey>& outPk = rct_signatures.outPk;
1254
1255 for (json& pk: j["rct_signatures"]["outPk"])
1256 {
1257 outPk.push_back(rct::ctkey {rct::zero(), rct::zero()});
1258
1259 rct::key& mask = outPk.back().mask;
1260
1261 if (!epee::string_tools::hex_to_pod(pk, mask))
1262 {
1263 cerr << "Faild to parse rct::key of an outPk from json" << endl;
1264 return false;
1265 }
1266
1267 // cout << "dest: " << epee::string_tools::pod_to_hex(outPk.back().mask) << endl;
1268 }
1269
1270 rct_signatures.txnFee = j["rct_signatures"]["txnFee"].get<uint64_t>();
1271 rct_signatures.type = j["rct_signatures"]["type"].get<uint8_t>();
1272
1273 } // if (j.find("rct_signatures") != j.end())
1274
1275
1276 if (j.find("rctsig_prunable") != j.end())
1277 {
1278 rct::rctSigPrunable& rctsig_prunable = tx.rct_signatures.p;
1279
1280 vector<rct::rangeSig>& range_sigs = rctsig_prunable.rangeSigs;
1281
1282 for (json& range_s: j["rctsig_prunable"]["rangeSigs"])
1283 {
1284 rct::boroSig asig;
1285
1286 if (!epee::string_tools::hex_to_pod(range_s["asig"], asig))
1287 {
1288 cerr << "Faild to parse asig of an asnlSig from json" << endl;
1289 return false;
1290 }
1291
1292
1293 struct {
1294 rct::key64 Ci;
1295 } key64_contained;
1296
1297 if (!epee::string_tools::hex_to_pod(range_s["Ci"], key64_contained))
1298 {
1299 cerr << "Faild to parse Ci of an asnlSig from json" << endl;
1300 return false;
1301 }
1302
1303 range_sigs.push_back(rct::rangeSig {});
1304
1305 rct::rangeSig& last_range_sig = range_sigs.back();
1306
1307 last_range_sig.asig = asig;
1308
1309 memcpy(&(last_range_sig.Ci), &(key64_contained.Ci), sizeof(rct::key64));
1310 }
1311
1312 vector<rct::mgSig>& mg_sigs = rctsig_prunable.MGs;
1313
1314 for (json& a_mgs: j["rctsig_prunable"]["MGs"])
1315 {
1316 rct::mgSig new_mg_sig;
1317
1318 vector<rct::keyV>& ss = new_mg_sig.ss;
1319
1320 for (json& ss_j: a_mgs["ss"])
1321 {
1322 rct::key a_key1;
1323
1324 if (!epee::string_tools::hex_to_pod(ss_j[0], a_key1))
1325 {
1326 cerr << "Faild to parse ss a_key1 of an MGs from json" << endl;
1327 return false;
1328 }
1329
1330 rct::key a_key2;
1331
1332 if (!epee::string_tools::hex_to_pod(ss_j[1], a_key2))
1333 {
1334 cerr << "Faild to parse ss a_key2 of an MGs from json" << endl;
1335 return false;
1336 }
1337
1338 ss.push_back(vector<rct::key>{a_key1, a_key2});
1339 }
1340
1341 json& cc_j = a_mgs["cc"];
1342
1343 if (!epee::string_tools::hex_to_pod(cc_j, new_mg_sig.cc))
1344 {
1345 cerr << "Faild to parse cc an MGs from json" << endl;
1346 return false;
1347 }
1348
1349 mg_sigs.push_back(new_mg_sig);
1350 }
1351
1352 } // j.find("rctsig_prunable") != j.end()
1353
1354
1355 //cout << j.dump(4) << endl;
1356
1357 //cout << "From reconstructed tx: " << obj_to_json_str(tx) << endl;
1358
1359 return true;
1360}
1361
1362string
1363get_human_readable_timestamp(uint64_t ts)
1364{
1365 char buffer[64];
1366 if (ts < 1234567890)
1367 return "<unknown>";
1368
1369 time_t tt = ts;
1370
1371 struct tm tm;
1372
1373 gmtime_r(&tt, &tm);
1374
1375 strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M:%S ", &tm);
1376
1377 return std::string(buffer);
1378}
1379
1380string
1381make_hash(const string& in_str)
1382{
1383 crypto::hash vk_hash;
1384 crypto::cn_fast_hash(in_str.c_str(), in_str.length(), vk_hash);
1385 return pod_to_hex(vk_hash);
1386}
1387
1388
1389}