Electroneum
Loading...
Searching...
No Matches
transactions_flow_test.cpp
Go to the documentation of this file.
1// Copyright (c) 2014-2019, The Monero Project
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification, are
6// permitted provided that the following conditions are met:
7//
8// 1. Redistributions of source code must retain the above copyright notice, this list of
9// conditions and the following disclaimer.
10//
11// 2. Redistributions in binary form must reproduce the above copyright notice, this list
12// of conditions and the following disclaimer in the documentation and/or other
13// materials provided with the distribution.
14//
15// 3. Neither the name of the copyright holder nor the names of its contributors may be
16// used to endorse or promote products derived from this software without specific
17// prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28//
29// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
30
31#include <boost/uuid/uuid.hpp>
32#include <boost/uuid/uuid_io.hpp>
33#include <boost/uuid/random_generator.hpp>
34#include <unordered_map>
35
36#include "include_base_utils.h"
37using namespace epee;
38#include "wallet/wallet2.h"
39using namespace cryptonote;
40
41namespace
42{
43 uint64_t const TEST_FEE = 5000000000; // 5 * 10^9
44 uint64_t const TEST_DUST_THRESHOLD = 5000000000; // 5 * 10^9
45}
46
48{
49 std::stringstream ss;
50 ss << boost::uuids::random_generator()();
51 return ss.str();
52}
53
54inline uint64_t random(const uint64_t max_value) {
55 return (uint64_t(rand()) ^
56 (uint64_t(rand())<<16) ^
57 (uint64_t(rand())<<32) ^
58 (uint64_t(rand())<<48)) % max_value;
59}
60
61bool do_send_etn(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, uint64_t amount_to_transfer, transaction& tx, size_t parts=1)
62{
63 CHECK_AND_ASSERT_MES(parts > 0, false, "parts must be > 0");
64
65 std::vector<cryptonote::tx_destination_entry> dsts;
66 dsts.reserve(parts);
67 uint64_t amount_used = 0;
68 uint64_t max_part = amount_to_transfer / parts;
69
70 for (size_t i = 0; i < parts; ++i)
71 {
74
75 if (i < parts - 1)
76 de.amount = random(max_part);
77 else
78 de.amount = amount_to_transfer - amount_used;
79 amount_used += de.amount;
80
81 //std::cout << "PARTS (" << amount_to_transfer << ") " << amount_used << " " << de.amount << std::endl;
82
83 dsts.push_back(de);
84 }
85
86 try
87 {
88 std::vector<tools::wallet2::pending_tx> ptx;
89 ptx = w1.create_transactions_2(dsts, mix_in_factor, 0, 0, std::vector<uint8_t>(), 0, {});
90 for (auto &p: ptx)
91 w1.commit_tx(p);
92 return true;
93 }
94 catch (const std::exception&)
95 {
96 return false;
97 }
98}
99
100uint64_t get_etn_in_first_transfers(const tools::wallet2::transfer_container& incoming_transfers, size_t n_transfers)
101{
102 uint64_t summ = 0;
103 size_t count = 0;
104 BOOST_FOREACH(const tools::wallet2::transfer_details& td, incoming_transfers)
105 {
106 summ += td.m_tx.vout[td.m_internal_output_index].amount;
107 if(++count >= n_transfers)
108 return summ;
109 }
110 return summ;
111}
112
113#define FIRST_N_TRANSFERS 10*10
114
115bool transactions_flow_test(std::string& working_folder,
116 std::string path_source_wallet,
117 std::string path_target_wallet,
118 std::string& daemon_addr_a,
119 std::string& daemon_addr_b,
120 uint64_t amount_to_transfer, size_t mix_in_factor, size_t transactions_count, size_t transactions_per_second)
121{
122 LOG_PRINT_L0("-----------------------STARTING TRANSACTIONS FLOW TEST-----------------------");
123 tools::wallet2 w1, w2;
124 if(path_source_wallet.empty())
125 path_source_wallet = generate_random_wallet_name();
126
127 if(path_target_wallet.empty())
128 path_target_wallet = generate_random_wallet_name();
129
130
131 try
132 {
133 w1.generate(working_folder + "/" + path_source_wallet, "");
134 w2.generate(working_folder + "/" + path_target_wallet, "");
135 }
136 catch (const std::exception& e)
137 {
138 LOG_ERROR("failed to generate wallet: " << e.what());
139 return false;
140 }
141
142 w1.init(daemon_addr_a);
143
144 uint64_t blocks_fetched = 0;
145 bool received_etn;
146 bool ok;
147 if(!w1.refresh(true, blocks_fetched, received_etn, ok))
148 {
149 LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
150 return false;
151 }
152
153 w2.init(daemon_addr_b);
154
155 MGINFO_GREEN("Using wallets: " << ENDL
156 << "Source: " << w1.get_account().get_public_address_str(MAINNET) << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL
157 << "Target: " << w2.get_account().get_public_address_str(MAINNET) << ENDL << "Path: " << working_folder + "/" + path_target_wallet);
158
159 //lets do some etn
161 COMMAND_RPC_STOP_MINING::request daemon1_req = AUTO_VAL_INIT(daemon1_req);
162 COMMAND_RPC_STOP_MINING::response daemon1_rsp = AUTO_VAL_INIT(daemon1_rsp);
163 bool r = http_client.set_server(daemon_addr_a, boost::none) && net_utils::invoke_http_json("/stop_mine", daemon1_req, daemon1_rsp, http_client, std::chrono::seconds(10));
164 CHECK_AND_ASSERT_MES(r, false, "failed to stop mining");
165
166 COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req);
167 COMMAND_RPC_START_MINING::response daemon_rsp = AUTO_VAL_INIT(daemon_rsp);
168 daemon_req.miner_address = w1.get_account().get_public_address_str(MAINNET);
169 daemon_req.threads_count = 9;
170 r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_rsp, http_client, std::chrono::seconds(10));
171 CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
172 CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
173
174 //wait for etn, until balance will have enough etn
175 w1.refresh(true, blocks_fetched, received_etn, ok);
176 while(w1.unlocked_balance(0, false) < amount_to_transfer)
177 {
179 w1.refresh(true, blocks_fetched, received_etn, ok);
180 }
181
182 //lets make a lot of small outs to ourselves
183 //since it is not possible to start from transaction that bigger than 20Kb, we gonna make transactions
184 //with 500 outs (about 18kb), and we have to wait appropriate count blocks, mined for test wallet
185 while(true)
186 {
187 tools::wallet2::transfer_container incoming_transfers;
188 w1.get_transfers(incoming_transfers);
189 if(incoming_transfers.size() > FIRST_N_TRANSFERS && get_etn_in_first_transfers(incoming_transfers, FIRST_N_TRANSFERS) < w1.unlocked_balance(0, false) )
190 {
191 //lets go!
192 size_t count = 0;
193 BOOST_FOREACH(tools::wallet2::transfer_details& td, incoming_transfers)
194 {
196 bool r = do_send_etn(w1, w1, 0, td.m_tx.vout[td.m_internal_output_index].amount - TEST_FEE, tx_s, 50);
197 CHECK_AND_ASSERT_MES(r, false, "Failed to send starter tx " << get_transaction_hash(tx_s));
198 MGINFO_GREEN("Starter transaction sent " << get_transaction_hash(tx_s));
199 if(++count >= FIRST_N_TRANSFERS)
200 break;
201 }
202 break;
203 }else
204 {
206 w1.refresh(true, blocks_fetched, received_etn, ok);
207 }
208 }
209 //do actual transfer
210 uint64_t transfered_etn = 0;
211 uint64_t transfer_size = amount_to_transfer/transactions_count;
212 size_t i = 0;
213 struct tx_test_entry
214 {
215 transaction tx;
216 size_t m_received_count;
217 uint64_t amount_transfered;
218 };
219 crypto::key_image lst_sent_ki = AUTO_VAL_INIT(lst_sent_ki);
220 std::unordered_map<crypto::hash, tx_test_entry> txs;
221 for(i = 0; i != transactions_count; i++)
222 {
223 uint64_t amount_to_tx = (amount_to_transfer - transfered_etn) > transfer_size ? transfer_size: (amount_to_transfer - transfered_etn);
224 while(w1.unlocked_balance(0, false) < amount_to_tx + TEST_FEE)
225 {
227 LOG_PRINT_L0("not enough ETN, waiting for cashback or mining");
228 w1.refresh(true, blocks_fetched, received_etn, ok);
229 }
230
231 transaction tx;
232 /*size_t n_attempts = 0;
233 while (!do_send_etn(w1, w2, mix_in_factor, amount_to_tx, tx)) {
234 n_attempts++;
235 std::cout << "failed to transfer ETN, refresh and try again (attempts=" << n_attempts << ")" << std::endl;
236 w1.refresh();
237 }*/
238
239
240 if(!do_send_etn(w1, w2, mix_in_factor, amount_to_tx, tx))
241 {
242 LOG_PRINT_L0("failed to transfer ETN, tx: " << get_transaction_hash(tx) << ", refresh and try again" );
243 w1.refresh(true, blocks_fetched, received_etn, ok);
244 if(!do_send_etn(w1, w2, mix_in_factor, amount_to_tx, tx))
245 {
246 LOG_PRINT_L0( "failed to transfer ETN, second chance. tx: " << get_transaction_hash(tx) << ", exit" );
247 LOCAL_ASSERT(false);
248 return false;
249 }
250 }
251 lst_sent_ki = boost::get<txin_to_key>(tx.vin[0]).k_image;
252
253 transfered_etn += amount_to_tx;
254
255 LOG_PRINT_L0("transferred " << amount_to_tx << ", i=" << i );
256 tx_test_entry& ent = txs[get_transaction_hash(tx)] = boost::value_initialized<tx_test_entry>();
257 ent.amount_transfered = amount_to_tx;
258 ent.tx = tx;
259 //if(i % transactions_per_second)
260 // misc_utils::sleep_no_w(1000);
261 }
262
263
264 LOG_PRINT_L0( "waiting some new blocks...");
265 misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*20*1000);//wait two blocks before sync on another wallet on another daemon
266 LOG_PRINT_L0( "refreshing...");
267 bool recvd_etn = false;
268 while(w2.refresh(true, blocks_fetched, recvd_etn, ok) && ( (blocks_fetched && recvd_etn) || !blocks_fetched ) )
269 {
270 misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*1000);//wait two blocks before sync on another wallet on another daemon
271 }
272
273 uint64_t etn_2 = w2.balance(0, false);
274 if(etn_2 == transfered_etn)
275 {
276 MGINFO_GREEN("-----------------------FINISHING TRANSACTIONS FLOW TEST OK-----------------------");
277 MGINFO_GREEN("transferred " << print_etn(transfered_etn) << " via " << i << " transactions" );
278 return true;
279 }else
280 {
282 w2.get_transfers(tc);
283 BOOST_FOREACH(tools::wallet2::transfer_details& td, tc)
284 {
285 auto it = txs.find(td.m_txid);
286 CHECK_AND_ASSERT_MES(it != txs.end(), false, "transaction not found in local cache");
287 it->second.m_received_count += 1;
288 }
289
290 BOOST_FOREACH(auto& tx_pair, txs)
291 {
292 if(tx_pair.second.m_received_count != 1)
293 {
294 MERROR("Transaction lost: " << get_transaction_hash(tx_pair.second.tx));
295 }
296
297 }
298
299 MERROR("-----------------------FINISHING TRANSACTIONS FLOW TEST FAILED-----------------------" );
300 MERROR("income " << print_etn(etn_2) << " via " << i << " transactions, expected ETN = " << print_etn(transfered_etn) );
301 LOCAL_ASSERT(false);
302 return false;
303 }
304
305 return true;
306}
307
std::string get_public_address_str(network_type nettype) const
Definition account.cpp:269
const account_keys & get_keys() const
Definition account.cpp:264
bool set_server(const std::string &address, boost::optional< login > user, ssl_options_t ssl_options=ssl_support_t::e_ssl_support_autodetect)
std::vector< wallet2::pending_tx > create_transactions_2(std::vector< cryptonote::tx_destination_entry > dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector< uint8_t > &extra, uint32_t subaddr_account, std::set< uint32_t > subaddr_indices)
Definition wallet2.cpp:9725
void refresh(bool trusted_daemon)
Definition wallet2.cpp:3060
cryptonote::account_base & get_account()
Definition wallet2.h:734
bool init(std::string daemon_address="http://localhost:8080", boost::optional< epee::net_utils::http::login > daemon_login=boost::none, boost::asio::ip::tcp::endpoint proxy={}, uint64_t upper_transaction_weight_limit=0, bool trusted_daemon=true, epee::net_utils::ssl_options_t ssl_options=epee::net_utils::ssl_support_t::e_ssl_support_autodetect, std::string blockchain_db_path="")
Definition wallet2.cpp:1282
void get_transfers(wallet2::transfer_container &incoming_transfers) const
Definition wallet2.cpp:6315
std::vector< transfer_details > transfer_container
Definition wallet2.h:449
uint64_t unlocked_balance(uint32_t subaddr_index_major, bool public_blockchain, uint64_t *blocks_to_unlock=NULL) const
Definition wallet2.cpp:6172
void generate(const std::string &wallet_, const epee::wipeable_string &password, const epee::wipeable_string &multisig_data, bool create_address_file=false)
Generates a wallet or restores one.
Definition wallet2.cpp:4869
uint64_t balance(uint32_t subaddr_index_major, bool public_blockchain) const
Definition wallet2.cpp:6162
void commit_tx(pending_tx &ptx_vector)
Definition wallet2.cpp:6887
#define CORE_RPC_STATUS_OK
#define DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN
#define AUTO_VAL_INIT(v)
#define MERROR(x)
Definition misc_log_ex.h:73
#define MGINFO_GREEN(x)
Definition misc_log_ex.h:82
#define LOCAL_ASSERT(expr)
#define ENDL
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define LOG_ERROR(x)
Definition misc_log_ex.h:98
#define LOG_PRINT_L0(x)
Definition misc_log_ex.h:99
POD_CLASS key_image
Definition crypto.h:105
Holds cryptonote related classes and helpers.
Definition ban.cpp:40
crypto::hash get_transaction_hash(const transaction &t)
std::string print_etn(uint64_t amount, unsigned int decimal_point)
bool sleep_no_w(long ms)
http_simple_client_template< blocked_mode_client > http_simple_client
bool invoke_http_json(const boost::string_ref uri, const t_request &out_struct, t_response &result_struct, t_transport &transport, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref method="GET")
unsigned __int64 uint64_t
Definition stdint.h:136
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
account_public_address m_account_address
Definition account.h:43
uint64_t amount
account_public_address addr
cryptonote::transaction_prefix m_tx
Definition wallet2.h:304
#define FIRST_N_TRANSFERS
std::string generate_random_wallet_name()
bool do_send_etn(tools::wallet2 &w1, tools::wallet2 &w2, size_t mix_in_factor, uint64_t amount_to_transfer, transaction &tx, size_t parts=1)
uint64_t get_etn_in_first_transfers(const tools::wallet2::transfer_container &incoming_transfers, size_t n_transfers)
bool transactions_flow_test(std::string &working_folder, std::string path_source_wallet, std::string path_target_wallet, std::string &daemon_addr_a, std::string &daemon_addr_b, uint64_t amount_to_transfer, size_t mix_in_factor, size_t transactions_count, size_t transactions_per_second)
uint64_t random(const uint64_t max_value)