From 2763db6ca1d70db833664ba7d91155696ed0d6f7 Mon Sep 17 00:00:00 2001 From: moneroexamples Date: Wed, 4 Jul 2018 08:54:37 +0800 Subject: [PATCH] paymate table refactored and some tests added --- config/config.json | 2 +- sql/openmonero.sql | 1 + sql/openmonero_test.sql | 21 +++- src/MySqlAccounts.cpp | 130 ++++++++------------ src/MySqlAccounts.h | 22 +--- src/YourMoneroRequests.cpp | 46 ++++++-- src/ssqlses.cpp | 4 +- src/ssqlses.h | 33 ++++-- tests/mysql_tests.cpp | 235 +++++++++++++++++++++++++++++++++---- 9 files changed, 347 insertions(+), 147 deletions(-) diff --git a/config/config.json b/config/config.json index b996fbb..ad636a4 100755 --- a/config/config.json +++ b/config/config.json @@ -34,7 +34,7 @@ "wallet_import" : { "_comment": "if fee is 0, then importing is free. fee is in base 1e12, e.g., 0.1 xmr is 0.1 x 1e12 = 100000000000", - "fee" : 0, + "fee" : 100000000000, "testnet" : { "address" : "9tzmPMTViHYM3z6NAgQni1Qm1Emzxy5hQFibPgWD3LVTAz91yok5Eni1pH6zKhBHzpTU15GZooPHSGHXFvFuXEdmEG2sWAZ", diff --git a/sql/openmonero.sql b/sql/openmonero.sql index 65f8b3d..0f9590c 100755 --- a/sql/openmonero.sql +++ b/sql/openmonero.sql @@ -59,6 +59,7 @@ CREATE TABLE IF NOT EXISTS `Inputs` ( `amount` bigint(20) UNSIGNED ZEROFILL NOT NULL DEFAULT '00000000000000000000', `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), + UNIQUE KEY `output_id` (`output_id`,`key_image`), KEY `account_id2` (`account_id`), KEY `tx_id2` (`tx_id`), KEY `output_id2` (`output_id`) diff --git a/sql/openmonero_test.sql b/sql/openmonero_test.sql index 789645d..dc3afd0 100644 --- a/sql/openmonero_test.sql +++ b/sql/openmonero_test.sql @@ -76,6 +76,7 @@ CREATE TABLE IF NOT EXISTS `Inputs` ( `amount` bigint(20) UNSIGNED ZEROFILL NOT NULL DEFAULT '00000000000000000000', `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), + UNIQUE KEY `output_id` (`output_id`,`key_image`), KEY `account_id2` (`account_id`), KEY `tx_id2` (`tx_id`), KEY `output_id2` (`output_id`) @@ -265,17 +266,18 @@ INSERT INTO `Outputs` (`id`, `account_id`, `tx_id`, `out_pub_key`, `rct_outpk`, DROP TABLE IF EXISTS `Payments`; CREATE TABLE IF NOT EXISTS `Payments` ( `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `address` varchar(95) NOT NULL, - `payment_id` varchar(64) NOT NULL, + `account_id` bigint(20) UNSIGNED NOT NULL, + `payment_id` varchar(16) NOT NULL, `tx_hash` varchar(64) NOT NULL DEFAULT '', `request_fulfilled` tinyint(1) NOT NULL DEFAULT '0', - `payment_address` varchar(95) NOT NULL, `import_fee` bigint(20) NOT NULL, + `payment_address` varchar(95) NOT NULL, `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), + UNIQUE KEY `account_id` (`account_id`) USING BTREE, UNIQUE KEY `payment_id` (`payment_id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- -- Truncate table before insert `Payments` @@ -286,8 +288,9 @@ TRUNCATE TABLE `Payments`; -- Dumping data for table `Payments` -- -INSERT INTO `Payments` (`id`, `address`, `payment_id`, `tx_hash`, `request_fulfilled`, `payment_address`, `import_fee`, `created`, `modified`) VALUES -(1, '57Hx8QpLUSMjhgoCNkvJ2Ch91mVyxcffESCprnRPrtbphMCv8iGUEfCUJxrpUWUeWrS9vPWnFrnMmTwnFpSKJrSKNuaXc5q', 'c3a7db070babeb64', 'be2ee71be2adf0da1b966c7a3c0573e2f340fc61374ca3c7e87e82cb0118b752', 1, '5DUWE29P72Eb8inMa41HuNJG4tj9CcaNKGr6EVSbvhWGJdpDQCiNNYBUNF1oDb8BczU5aD68d3HNKXaEsPq8cvbQLN4Ptby', 100000000000, '2018-06-14 05:20:15', '2018-06-14 05:20:15'); +INSERT INTO `Payments` (`id`, `account_id`, `payment_id`, `tx_hash`, `request_fulfilled`, `import_fee`, `payment_address`, `created`, `modified`) VALUES +(1, 129, 'e410eb43e14a28fb', '', 0, 100000000000, '5DUWE29P72Eb8inMa41HuNJG4tj9CcaNKGr6EVSbvhWGJdpDQCiNNYBUNF1oDb8BczU5aD68d3HNKXaEsPq8cvbQLPHdavp', '2018-07-03 23:54:55', '2018-07-03 23:54:55'), +(2, 134, '74854c1cd490e148', '', 0, 100000000000, '5DUWE29P72Eb8inMa41HuNJG4tj9CcaNKGr6EVSbvhWGJdpDQCiNNYBUNF1oDb8BczU5aD68d3HNKXaEsPq8cvbQLK4Tiiy', '2018-07-03 23:55:57', '2018-07-03 23:55:57'); -- -------------------------------------------------------- @@ -438,6 +441,12 @@ ALTER TABLE `Outputs` ADD CONSTRAINT `account_id3_FK` FOREIGN KEY (`account_id`) REFERENCES `Accounts` (`id`) ON DELETE CASCADE, ADD CONSTRAINT `transaction_id_FK` FOREIGN KEY (`tx_id`) REFERENCES `Transactions` (`id`) ON DELETE CASCADE; +-- +-- Constraints for table `Payments` +-- +ALTER TABLE `Payments` + ADD CONSTRAINT `account_id` FOREIGN KEY (`account_id`) REFERENCES `Accounts` (`id`) ON DELETE CASCADE; + -- -- Constraints for table `Transactions` -- diff --git a/src/MySqlAccounts.cpp b/src/MySqlAccounts.cpp index 2bd3369..7412d90 100755 --- a/src/MySqlAccounts.cpp +++ b/src/MySqlAccounts.cpp @@ -22,7 +22,7 @@ bool MysqlInputs::select_for_out(const uint64_t& output_id, vector& ins) { - Query query = conn->query(XmrInput::SELECT_STMT3); + Query query = conn->query(XmrInput::SELECT_STMT4); query.parse(); try @@ -44,33 +44,6 @@ MysqlInputs::select_for_out(const uint64_t& output_id, vector& ins) MysqlOutpus::MysqlOutpus(shared_ptr _conn): conn {_conn} {} -bool -MysqlOutpus::select(uint64_t out_id, XmrOutput& out) -{ - Query query = conn->query(XmrOutput::SELECT_STMT3); - query.parse(); - - try - { - vector outs; - - query.storein(outs, out_id); - - if (!outs.empty()) - { - out = std::move(outs.at(0)); - return true; - } - } - catch (std::exception& e) - { - MYSQL_EXCEPTION_MSG(e); - //throw e; - } - - return false; -} - bool @@ -235,28 +208,6 @@ MysqlTransactions::get_total_recieved(const uint64_t& account_id) MysqlPayments::MysqlPayments(shared_ptr _conn): conn {_conn} {} -bool -MysqlPayments::select(const string& address, vector& payments) -{ - - Query query = conn->query(XmrPayment::SELECT_STMT); - query.parse(); - - try - { - query.storein(payments, address); - - return !payments.empty(); - } - catch (std::exception& e) - { - MYSQL_EXCEPTION_MSG(e); - //throw e; - } - - return false; -} - bool MysqlPayments::select_by_payment_id(const string& payment_id, vector& payments) { @@ -422,6 +373,8 @@ MySqlAccounts::select(uint64_t account_id, vector& selected_data) try { + selected_data.clear(); + query.storein(selected_data, account_id); return !selected_data.empty(); @@ -450,6 +403,9 @@ bool MySqlAccounts::select(uint64_t tx_id, vector& sele template bool MySqlAccounts::select(uint64_t account_id, vector& selected_data); +template +bool MySqlAccounts::select(uint64_t account_id, vector& selected_data); + template // this will use SELECT_STMT2 which selectes based on transaction id, not account_id, bool MySqlAccounts::select(uint64_t tx_id, vector& selected_data); @@ -469,6 +425,48 @@ template // this will use SELECT_STMT2 which selectes based on transaction id, n bool MySqlAccounts::select_for_tx(uint64_t tx_id, vector& selected_data); + +template +bool +MySqlAccounts::select_by_primary_id(uint64_t id, T& selected_data) +{ + Query query = conn->query(T::SELECT_STMT3); + query.parse(); + + try + { + vector outs; + + query.storein(outs, id); + + if (!outs.empty()) + { + selected_data = std::move(outs.at(0)); + return true; + } + } + catch (std::exception& e) + { + MYSQL_EXCEPTION_MSG(e); + //throw e; + } + + return false; +} + + +template +bool MySqlAccounts::select_by_primary_id(uint64_t id, XmrTransaction& selected_data); + +template +bool MySqlAccounts::select_by_primary_id(uint64_t id, XmrInput& selected_data); + +template +bool MySqlAccounts::select_by_primary_id(uint64_t id, XmrOutput& selected_data); + +template +bool MySqlAccounts::select_by_primary_id(uint64_t id, XmrPayment& selected_data); + bool MySqlAccounts::select_txs_for_account_spendability_check( const uint64_t& account_id, @@ -568,11 +566,6 @@ MySqlAccounts::select_txs_for_account_spendability_check( return true; } -bool -MySqlAccounts::select_output_with_id(const uint64_t& out_id, XmrOutput& out) -{ - return mysql_out->select(out_id, out); -} bool @@ -617,33 +610,6 @@ MySqlAccounts::select_payment_by_id(const string& payment_id, vector return mysql_payment->select_by_payment_id(payment_id, payments); } -bool -MySqlAccounts::select_payment_by_address(const string& address, vector& payments) -{ - return mysql_payment->select(address, payments); -} - -bool -MySqlAccounts::select_payment_by_address(const string& address, XmrPayment& payment) -{ - - vector payments; - - bool r = mysql_payment->select(address, payments); - - if (!r) - return false; - - if (payments.empty()) - return false; - - // always get last payment details. - payment = payments.back(); - - return r; -} - - bool MySqlAccounts::update_payment(XmrPayment& payment_orginal, XmrPayment& payment_new) { diff --git a/src/MySqlAccounts.h b/src/MySqlAccounts.h index b8c7e15..6758f75 100755 --- a/src/MySqlAccounts.h +++ b/src/MySqlAccounts.h @@ -42,9 +42,6 @@ public: MysqlInputs(shared_ptr _conn); - bool - select(const uint64_t& address_id, vector& ins); - bool select_for_out(const uint64_t& output_id, vector& ins); }; @@ -60,9 +57,6 @@ public: MysqlOutpus(shared_ptr _conn); - bool - select(uint64_t out_id, XmrOutput& outs); - bool exist(const string& output_public_key_str, XmrOutput& out); }; @@ -103,9 +97,6 @@ public: MysqlPayments(shared_ptr _conn); - bool - select(const string& address, vector& payments); - bool select_by_payment_id(const string& payment_id, vector& payments); @@ -162,12 +153,13 @@ public: bool select_for_tx(uint64_t tx_id, vector& selected_data); + template bool - select_txs_for_account_spendability_check(const uint64_t& account_id, - vector& txs); + select_by_primary_id(uint64_t id, T& selected_data); bool - select_output_with_id(const uint64_t& out_id, XmrOutput& out); + select_txs_for_account_spendability_check(const uint64_t& account_id, + vector& txs); bool select_inputs_for_out(const uint64_t& output_id, vector& ins); @@ -190,12 +182,6 @@ public: bool select_payment_by_id(const string& payment_id, vector& payments); - bool - select_payment_by_address(const string& address, vector& payments); - - bool - select_payment_by_address(const string& address, XmrPayment& payment); - bool update_payment(XmrPayment& payment_orginal, XmrPayment& payment_new); diff --git a/src/YourMoneroRequests.cpp b/src/YourMoneroRequests.cpp index 9151e30..67039c2 100755 --- a/src/YourMoneroRequests.cpp +++ b/src/YourMoneroRequests.cpp @@ -258,7 +258,7 @@ YourMoneroRequests::get_address_txs(const shared_ptr< Session > session, const B { XmrOutput out; - if (xmr_accounts->select_output_with_id(input.output_id, out)) + if (xmr_accounts->select_by_primary_id(input.output_id, out)) { total_spent += input.amount; @@ -887,9 +887,6 @@ YourMoneroRequests::import_wallet_request(const shared_ptr< Session > session, c string xmr_address = j_request["address"]; - // a placeholder for existing or new payment data - xmreg::XmrPayment xmr_payment; - json j_response; j_response["request_fulfilled"] = false; @@ -923,13 +920,46 @@ YourMoneroRequests::import_wallet_request(const shared_ptr< Session > session, c return; } + XmrAccount acc; + + if (!xmr_accounts->select(xmr_address, acc)) + { + cerr << "xmr_address does not exists! " << endl; + j_response["error"] = "The account does not exists!"; + + string response_body = j_response.dump(); + auto response_headers = make_headers({{ "Content-Length", to_string(response_body.size())}}); + + session->close( OK, response_body, response_headers); + return; + } + + + // a placeholder for existing or new payment data + vector xmr_payments; + + // select this payment if its existing one - if (xmr_accounts->select_payment_by_address(xmr_address, xmr_payment)) + if (xmr_accounts->select(acc.id.data, xmr_payments)) { // payment record exists, so now we need to check if // actually payment has been done, and updated // mysql record accordingly. + if (xmr_payments.size() > 1) + { + cerr << "More than one payment record found! " << endl; + j_response["error"] = "TMore than one payment record found!"; + + string response_body = j_response.dump(); + auto response_headers = make_headers({{ "Content-Length", to_string(response_body.size())}}); + + session->close( OK, response_body, response_headers); + return; + } + + XmrPayment& xmr_payment = xmr_payments[0]; + bool request_fulfilled = bool {xmr_payment.request_fulfilled}; string integrated_address = @@ -1035,8 +1065,10 @@ YourMoneroRequests::import_wallet_request(const shared_ptr< Session > session, c CurrentBlockchainStatus::get_account_integrated_address_as_str( random_payment_id8); + XmrPayment xmr_payment; + xmr_payment.id = mysqlpp::null; - xmr_payment.address = xmr_address; + xmr_payment.account_id = acc.id.data; xmr_payment.payment_id = pod_to_hex(random_payment_id8); xmr_payment.import_fee = CurrentBlockchainStatus::import_fee; // xmr xmr_payment.request_fulfilled = false; @@ -1420,7 +1452,7 @@ YourMoneroRequests::get_tx(const shared_ptr< Session > session, const Bytes & bo { XmrOutput out; - if (xmr_accounts->select_output_with_id(input.output_id, out)) + if (xmr_accounts->select_by_primary_id(input.output_id, out)) { total_spent += input.amount; diff --git a/src/ssqlses.cpp b/src/ssqlses.cpp index b0b4e7b..884a810 100755 --- a/src/ssqlses.cpp +++ b/src/ssqlses.cpp @@ -118,12 +118,12 @@ json XmrPayment::to_json() const { json j {{"id" , id.data}, - {"address" , address}, + {"account_id" , account_id}, {"payment_id" , payment_id}, {"tx_hash" , tx_hash}, {"request_fulfilled", bool {request_fulfilled}}, + {"import_fee" , import_fee}, {"payment_address" , payment_address}, - {"import_fee" , import_fee} }; return j; diff --git a/src/ssqlses.h b/src/ssqlses.h index ee14b1a..43f0517 100755 --- a/src/ssqlses.h +++ b/src/ssqlses.h @@ -52,6 +52,11 @@ struct XmrAccount : public Accounts, Table SELECT * FROM `Accounts` WHERE `address` = (%0q) )"; + // SELECT_STMT3 same as SELECT_STMT which is fine + // easier to work with templates later + static constexpr const char* SELECT_STMT3 = R"( + SELECT * FROM `Accounts` WHERE `id` = (%0q) + )"; static constexpr const char* INSERT_STMT = R"( INSERT INTO `Accounts` (`address`, `viewkey_hash`, @@ -104,6 +109,11 @@ struct XmrTransaction : public Transactions, Table SELECT * FROM `Transactions` WHERE `id` = (%0q) )"; + // same as SELECT_STMT2 for similicity later on + static constexpr const char* SELECT_STMT3 = R"( + SELECT * FROM `Transactions` WHERE `id` = (%0q) + )"; + static constexpr const char* EXIST_STMT = R"( SELECT * FROM `Transactions` WHERE `account_id` = (%0q) AND `hash` = (%1q) )"; @@ -182,7 +192,7 @@ struct XmrOutput : public Outputs, Table )"; static constexpr const char* SELECT_STMT2 = R"( - SELECT * FROM `Outputs` WHERE `tx_id` = (%0q) ORDER BY amount + SELECT * FROM `Outputs` WHERE `tx_id` = (%0q) )"; static constexpr const char* SELECT_STMT3 = R"( @@ -246,6 +256,10 @@ struct XmrInput : public Inputs, Table )"; static constexpr const char* SELECT_STMT3 = R"( + SELECT * FROM `Inputs` WHERE `id` = (%0q) + )"; + + static constexpr const char* SELECT_STMT4 = R"( SELECT * FROM `Inputs` WHERE `output_id` = (%0q) )"; @@ -266,12 +280,12 @@ struct XmrInput : public Inputs, Table sql_create_9(Payments, 1, 7, sql_bigint_unsigned_null, id, - sql_varchar , address, + sql_bigint_unsigned , account_id, sql_varchar , payment_id, sql_varchar , tx_hash, sql_boolean , request_fulfilled, - sql_varchar , payment_address, sql_bigint_unsigned , import_fee, + sql_varchar , payment_address, sql_timestamp , created, sql_timestamp , modified); @@ -280,19 +294,22 @@ struct XmrPayment : public Payments, Table { static constexpr const char* SELECT_STMT = R"( - SELECT * FROM `Payments` WHERE `address` = (%0q) + SELECT * FROM `Payments` WHERE `account_id` = (%0q) )"; static constexpr const char* SELECT_STMT2 = R"( SELECT * FROM `Payments` WHERE `payment_id` = (%0q) )"; + static constexpr const char* SELECT_STMT3 = R"( + SELECT * FROM `Payments` WHERE `id` = (%0q) + )"; static constexpr const char* INSERT_STMT = R"( - INSERT IGNORE INTO `Payments` (`address`, `payment_id`, `tx_hash`, - `request_fulfilled`, `payment_address`, `import_fee`) - VALUES (%0q, %1q, %2q, - %3q, %4q, %5q); + INSERT IGNORE INTO `Payments` (`account_id`, `payment_id`, `tx_hash`, + `request_fulfilled`, `import_fee`, + `payment_address`) + VALUES (%0q, %1q, %2q, %3q, %4q, %5q); )"; diff --git a/tests/mysql_tests.cpp b/tests/mysql_tests.cpp index a828a53..a48d4f6 100644 --- a/tests/mysql_tests.cpp +++ b/tests/mysql_tests.cpp @@ -46,6 +46,27 @@ class MYSQL_TEST : public ::testing::Test { public: + static void initDatabase() + { + mysqlpp::Query query = xmr_accounts + ->get_connection()->get_connection().query(db_data); + + query.parse(); + + try + { + ASSERT_TRUE(query.exec()); + + while(query.more_results()) + query.store_next(); + } + catch (std::exception &e) + { + std::cerr << e.what() << '\n'; + throw e; + } + } + static void SetUpTestCase() { // read in confing json file and get test db info @@ -102,29 +123,16 @@ public: db_data = xmreg::read("../sql/openmonero_test.sql"); } + static void TearDownTestCase() + { + initDatabase(); + } + protected: virtual void SetUp() { - - mysqlpp::Query query = xmr_accounts - ->get_connection()->get_connection().query(db_data); - - query.parse(); - - try - { - bool is_success = query.exec(); - - while(query.more_results()) - query.store_next(); - } - catch (std::exception &e) - { - std::cerr << e.what() << '\n'; - throw e; - } - + initDatabase(); } static std::string db_data; @@ -477,7 +485,7 @@ INSTANTIATE_TEST_CASE_P( )); -xmreg::XmrOutput +auto make_mock_output_data(string last_char_pub_key = "4") { xmreg::XmrOutput mock_output_data ; @@ -511,6 +519,8 @@ TEST_F(MYSQL_TEST, SelectOutputsForAccount) ASSERT_TRUE(xmr_accounts->select(acc.id.data, outputs)); + // can use this command to get list of outputs + // ./xmr2csv --stagenet -m -a 5AjfkEY7RFgNGDYvoRQkncfwHXT6Fh7oJBisqFUX5u96i3ZepxDPocQK29tmAwBDuvKRpskZnfA6N8Ra58qFzA4bSA3QZFp -v 3ee772709dcf834a881c432fc15625d84585e4462d7905c42460dd478a315008 -t 90000 -g 101610 EXPECT_EQ(outputs.size(), 9); } @@ -552,7 +562,7 @@ TEST_F(MYSQL_TEST, InsertOneOutput) xmreg::XmrOutput out_data2; - EXPECT_TRUE(xmr_accounts->select_output_with_id(inserted_output_id, out_data2)); + EXPECT_TRUE(xmr_accounts->select_by_primary_id(inserted_output_id, out_data2)); EXPECT_EQ(out_data2.tx_id, mock_output_data.tx_id); EXPECT_EQ(out_data2.out_pub_key, mock_output_data.out_pub_key); @@ -640,7 +650,7 @@ TEST_F(MYSQL_TEST, InsertSeverlOutputsAtOnce) uint64_t output_id_to_get = expected_primary_id + i; xmreg::XmrOutput out_data; - xmr_accounts->select_output_with_id(output_id_to_get, out_data); + xmr_accounts->select_by_primary_id(output_id_to_get, out_data); EXPECT_EQ(mock_outputs_data[i].out_pub_key, out_data.out_pub_key); } @@ -658,6 +668,8 @@ TEST_F(MYSQL_TEST, SelectInputsForAccount) ASSERT_TRUE(xmr_accounts->select(acc.id.data, inputs)); + // can use this command to get the list of ring members + // ./xmr2csv --stagenet -m -a 5AjfkEY7RFgNGDYvoRQkncfwHXT6Fh7oJBisqFUX5u96i3ZepxDPocQK29tmAwBDuvKRpskZnfA6N8Ra58qFzA4bSA3QZFp -v 3ee772709dcf834a881c432fc15625d84585e4462d7905c42460dd478a315008 -t 90000 -g 101610 EXPECT_EQ(inputs.size(), 12); } @@ -707,7 +719,7 @@ TEST_F(MYSQL_TEST, SelectInputsForOutput) EXPECT_EQ(inputs.size(), 3); - EXPECT_EQ(inputs.front().key_image, "7d8e2a1f1755ef12ce28185821e6b19cb6023d535411bbb7abdced139c923bad"); + EXPECT_EQ(inputs.front().key_image, "00dd88b3a16b3616d342faec2bc47b24add433407ef79b9a00b55b75d96239a4"); EXPECT_EQ(inputs.back().key_image, "abc529357f90641d501d5108f822617049c19461569eafa45cb5400ee45bef33"); inputs.clear(); @@ -718,22 +730,199 @@ TEST_F(MYSQL_TEST, SelectInputsForOutput) +auto +make_mock_input_data(string last_char_pub_key = "0") +{ + xmreg::XmrInput mock_data; + + mock_data.id = mysqlpp::null; + // mock_output_data.account_id = acc.id; need to be set when used + mock_data.tx_id = 106086; // some tx id for this output + mock_data.output_id = 428900; // some output id + mock_data.key_image = "18c6a80311d6f455ac1e5abdce7e86828d1ecf911f78da12a56ce8fdd5c716f" + + last_char_pub_key; // out_pub_key is unique field, so to make + // few public keys, we just change its last char. + mock_data.amount = 999916984840000ull; + mock_data.timestamp = mysqlpp::DateTime(static_cast(44434554));; + + return mock_data; +} + + +TEST_F(MYSQL_TEST, InsertOneInput) +{ + ACC_FROM_HEX(owner_addr_5Ajfk); + + xmreg::XmrInput mock_input_data = make_mock_input_data(); + + mock_input_data.account_id = acc.id.data; + + uint64_t expected_primary_id = xmr_accounts->get_next_primary_id(mock_input_data); + uint64_t inserted_id = xmr_accounts->insert(mock_input_data); + + EXPECT_EQ(expected_primary_id, inserted_id); + + // now we fetch the inserted input and compare its values + + xmreg::XmrInput in_data2; + + EXPECT_TRUE(xmr_accounts->select_by_primary_id(inserted_id, in_data2)); + + EXPECT_EQ(in_data2.account_id, mock_input_data.account_id); + EXPECT_EQ(in_data2.tx_id, mock_input_data.tx_id); + EXPECT_EQ(in_data2.amount, mock_input_data.amount); + EXPECT_EQ(in_data2.key_image, mock_input_data.key_image); + EXPECT_EQ(in_data2.output_id, mock_input_data.output_id); + EXPECT_EQ(in_data2.timestamp, mock_input_data.timestamp); +} + + + +TEST_F(MYSQL_TEST, InsertSeverlInputsAtOnce) +{ + ACC_FROM_HEX(owner_addr_5Ajfk); + + // create vector of several inputs to be written into database + vector mock_data; + + for (size_t i = 0; i < 10; ++i) + { + mock_data.push_back(make_mock_input_data(std::to_string(i))); + mock_data.back().account_id = acc.id.data; + } + + uint64_t expected_primary_id = xmr_accounts->get_next_primary_id(xmreg::XmrInput()); + + // first time insert should be fine + uint64_t no_inserted_rows = xmr_accounts->insert(mock_data); + + EXPECT_EQ(no_inserted_rows, mock_data.size()); + + // after inserting 10 rows, the expected ID should be before + 11 + uint64_t expected_primary_id2 = xmr_accounts->get_next_primary_id(xmreg::XmrInput()); + + EXPECT_EQ(expected_primary_id2, expected_primary_id + mock_data.size()); + + for (size_t i = 0; i < 10; ++i) + { + uint64_t id_to_get = expected_primary_id + i; + + xmreg::XmrInput out_data; + xmr_accounts->select_by_primary_id(id_to_get, out_data); + + EXPECT_EQ(mock_data[i].key_image, out_data.key_image); + } + +} + + +TEST_F(MYSQL_TEST, TryToInsertSameInputTwice) +{ + // the input table requires a row to be unique for pair + // of key_image + output_id + + ACC_FROM_HEX(owner_addr_5Ajfk); + + xmreg::XmrInput mock_data = make_mock_input_data(); + + mock_data.account_id = acc.id.data; + + // first time insert should be fine + uint64_t inserted_id = xmr_accounts->insert(mock_data); + EXPECT_GT(inserted_id, 0); + // second insert should fail and result in 0 + inserted_id = xmr_accounts->insert(mock_data); + + EXPECT_EQ(inserted_id, 0); +} + + +TEST_F(MYSQL_TEST, SelectPaymentForAccount) +{ + ACC_FROM_HEX(owner_addr_5Ajfk); + + vector payments; + ASSERT_TRUE(xmr_accounts->select(acc.id.data, payments)); + EXPECT_EQ(payments.size(), 1); + EXPECT_EQ(payments[0].payment_id, "74854c1cd490e148"); + EXPECT_EQ(payments[0].payment_address, "5DUWE29P72Eb8inMa41HuNJG4tj9CcaNKGr6EVSbvhWGJdpDQCiNNYBUNF1oDb8BczU5aD68d3HNKXaEsPq8cvbQLK4Tiiy"); + EXPECT_FALSE(static_cast(payments[0].request_fulfilled)); + //check if there is no payment for the given account id + EXPECT_FALSE(xmr_accounts->select(5555, payments)); +} + + + +auto +make_mock_payment_data(string last_char_pub_key = "0") +{ + xmreg::XmrPayment mock_data; + mock_data.id = mysqlpp::null; + // mock_output_data.account_id = acc.id; need to be set when used + mock_data.payment_id = pod_to_hex(crypto::rand()); + mock_data.import_fee = 10000000010ull; // xmr + mock_data.request_fulfilled = false; + mock_data.tx_hash = ""; // no tx_hash yet with the payment + mock_data.payment_address = "5DUWE29P72Eb8inMa41HuNJG4tj9CcaNKGr6EVSbvhWGJdpDQCiNNYBUNF1oDb8BczU5aD68d3HNKXaEsPq8cvbQLK4Tiiy"; + return mock_data; +} +TEST_F(MYSQL_TEST, InsertOnePayment) +{ + ACC_FROM_HEX(addr_55Zb); + xmreg::XmrPayment mock_data = make_mock_payment_data(); + mock_data.account_id = acc.id.data; + uint64_t expected_primary_id = xmr_accounts->get_next_primary_id(mock_data); + uint64_t inserted_id = xmr_accounts->insert(mock_data); + EXPECT_EQ(expected_primary_id, inserted_id); + // now we fetch the inserted input and compare its values + xmreg::XmrPayment in_data2; + + EXPECT_TRUE(xmr_accounts->select_by_primary_id(inserted_id, in_data2)); + + EXPECT_EQ(in_data2.account_id, mock_data.account_id); + EXPECT_EQ(in_data2.payment_id, mock_data.payment_id); + EXPECT_EQ(in_data2.import_fee, mock_data.import_fee); + EXPECT_EQ(in_data2.payment_address, mock_data.payment_address); + EXPECT_EQ(in_data2.request_fulfilled, mock_data.request_fulfilled); +} + + +TEST_F(MYSQL_TEST, TryToInsertSamePaymentTwice) +{ + // the input table requires a row to be unique for pair + // of key_image + output_id + + ACC_FROM_HEX(addr_55Zb); + + xmreg::XmrPayment mock_data = make_mock_payment_data(); + + mock_data.account_id = acc.id.data; + + uint64_t inserted_id = xmr_accounts->insert(mock_data); + + EXPECT_GT(inserted_id, 0); + + // second insert should fail and result in 0 + inserted_id = xmr_accounts->insert(mock_data); + + EXPECT_EQ(inserted_id, 0); +}