From 6d4932a084c174457a0877b2f5fe45271f7ebd0f Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Thu, 31 Jan 2019 18:49:53 +0300 Subject: [PATCH 1/9] RTA transaction --- src/cryptonote_basic/cryptonote_basic.h | 33 ++++++++++- .../cryptonote_format_utils.cpp | 23 ++++++++ .../cryptonote_format_utils.h | 17 ++++++ src/cryptonote_basic/tx_extra.h | 21 ++++++- src/cryptonote_core/tx_pool.cpp | 12 ++-- src/wallet/api/pending_transaction.cpp | 10 ++-- src/wallet/api/pending_transaction.h | 1 + src/wallet/wallet2_api.h | 2 +- .../graft_splitted_tx_test.cpp | 10 +--- tests/unit_tests/serialization.cpp | 42 +++++++++++++ tests/unit_tests/test_tx_utils.cpp | 59 +++++++++++++++++++ 11 files changed, 205 insertions(+), 25 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 41cb575b2..b7b33ff69 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -203,14 +203,39 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; + // container for RTA identities (public keys) + // stores RTA payment ID, PoS public one-time identification key (used to identify PoS in the network and protect data for it), + // auth sample supernode public identification keys (graftnode will need it to validate auth sample signatures), + // PoS and Wallet Proxy Supernode identification keys to transaction_header.extra. + // TODO: better name? + struct rta_header + { + std::string payment_id; + std::vector keys; + BEGIN_SERIALIZE_OBJECT() + FIELD(payment_id) + FIELD(keys) + END_SERIALIZE() + bool operator== (const rta_header &other) const + { + return this->payment_id == other.payment_id + && this->keys == other.keys; + } + }; + struct rta_signature { - account_public_address address; + size_t key_index; // reference to the corresponding pubkey. alternatively we can just iterate by matching signatures and keys crypto::signature signature; BEGIN_SERIALIZE_OBJECT() - FIELD(address) + FIELD(key_index) FIELD(signature) END_SERIALIZE() + bool operator== (const rta_signature &other) const + { + return this->key_index == other.key_index + && this->signature == other.signature; + } }; class transaction: public transaction_prefix @@ -238,8 +263,10 @@ namespace cryptonote tx_type_invalid = 255 }; // graft: tx type field + // TODO: consider to removed 'type' field. we can check if transaction is rta either by + // 1. checking if 'tx_extra_graft_rta_header' is present in tx_extra + // 2. simply checking tx version, so 'type' only needed for 'alpha' compatibilty. size_t type = tx_type_generic; - std::vector rta_signatures; transaction(); diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index d7c214bd8..cf0271724 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -934,6 +934,29 @@ namespace cryptonote return true; } + bool add_graft_rta_header_to_extra(std::vector &extra, const rta_header &rta_header) + { + std::string blob; + ::serialization::dump_binary(const_cast(rta_header), blob); + tx_extra_graft_rta_header container; + container.data = blob; + blob.clear(); + ::serialization::dump_binary(container, blob); + extra.push_back(TX_EXTRA_GRAFT_RTA_HEADER_TAG); + std::copy(blob.begin(), blob.end(), std::back_inserter(extra)); + return true; + } + + bool get_graft_rta_header_from_extra(const transaction &tx, rta_header &rta_header) + { + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + tx_extra_graft_rta_header rta_header_data; + if(!find_tx_extra_field_by_type(tx_extra_fields, rta_header_data)) + return false; + return ::serialization::parse_binary(rta_header_data.data, rta_header); + } + bool add_graft_tx_secret_key_to_extra(std::vector &extra, const crypto::secret_key& secret_key) { tx_extra_graft_tx_secret_key container; diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index f378d5152..7f1b336ae 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -72,6 +72,7 @@ namespace cryptonote * \param graft_extra - graft fields to add * \return - true on success */ + // TODO: probably we will remove "..graft_tx_extra.." methods bool add_graft_tx_extra_to_extra(transaction &tx, const supernode::GraftTxExtra &graft_extra); /*! @@ -123,6 +124,22 @@ namespace cryptonote crypto::signature &supernode_signature, crypto::secret_key &tx_secret_key); + /*! + * \brief add_graft_rta_header_to_extra - add rta_header to the extra + * \param extra - extra to add fields to + * \param rta_header - source rta_header + * \return - true on success + */ + bool add_graft_rta_header_to_extra(std::vector &extra, const rta_header &rta_header); + + /*! + * \brief get_graft_rta_header_from_extra - read rta_header from tx extra + * \param tx - input transaction + * \param rta_header - output rta_header + * \return - true on success + */ + bool get_graft_rta_header_from_extra(const transaction& tx, rta_header &rta_header); + bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); bool remove_field_from_tx_extra(std::vector& tx_extra, const std::type_info &type); void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 6d9e76aaf..7bc18992c 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -30,6 +30,8 @@ #pragma once +#include "cryptonote_basic.h" // only need rta_header so it probably make sense to move it to separate file + #define TX_EXTRA_PADDING_MAX_COUNT 255 #define TX_EXTRA_NONCE_MAX_COUNT 255 @@ -38,9 +40,12 @@ #define TX_EXTRA_TAG_PUBKEY 0x01 #define TX_EXTRA_NONCE 0x02 #define TX_EXTRA_MERGE_MINING_TAG 0x03 +// TODO: suggested to remove #define TX_EXTRA_GRAFT_EXTRA_TAG 0x04 -#define TX_EXTRA_GRAFT_STAKE_TX_TAG 0x10 -#define TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG 0x11 +#define TX_EXTRA_GRAFT_STAKE_TX_TAG 0x80 +#define TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG 0x82 +#define TX_EXTRA_GRAFT_RTA_HEADER_TAG 0x83 + #define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 @@ -197,12 +202,21 @@ namespace cryptonote END_SERIALIZE() }; + struct tx_extra_graft_rta_header + { + std::string data; + BEGIN_SERIALIZE() + FIELD(data) + END_SERIALIZE() + }; + + // tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: // varint tag; // varint size; // varint data[]; typedef boost::variant tx_extra_field; + tx_extra_mysterious_minergate, tx_extra_graft_extra, tx_extra_graft_stake_tx, tx_extra_graft_tx_secret_key, tx_extra_graft_rta_header> tx_extra_field; } VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); @@ -213,3 +227,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_ VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_extra, TX_EXTRA_GRAFT_EXTRA_TAG); VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_stake_tx, TX_EXTRA_GRAFT_STAKE_TX_TAG); VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_tx_secret_key, TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG); +VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_rta_header, TX_EXTRA_GRAFT_RTA_HEADER_TAG); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 25efc2bc4..109231d47 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -179,13 +179,15 @@ namespace cryptonote auto rta_validator = [&](const std::vector &rta_signs) -> bool { bool result = true; for (const auto &rta_sign : rta_signs) { - result &= crypto::check_signature(id, rta_sign.address.m_spend_public_key, rta_sign.signature); + // TODO: implement RTA validation here + // result &= crypto::check_signature(id, rta_sign.address.m_spend_public_key, rta_sign.signature); + result = true; if (!result) { - LOG_ERROR("Failed to validate rta tx " - << epee::string_tools::pod_to_hex(id) - << " for address: " - << cryptonote::get_account_address_as_str(m_blockchain.testnet(), rta_sign.address)); + LOG_ERROR("Failed to validate rta tx "); +// << epee::string_tools::pod_to_hex(id) +// << " for address: " +// << cryptonote::get_account_address_as_str(m_blockchain.testnet(), rta_sign.address)); break; } diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 0cefd0243..fdc4e35a3 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -203,11 +203,11 @@ void PendingTransactionImpl::putRtaSignatures(const std::vector & std::vector bin_signs; for (const auto &sign : signs) { cryptonote::rta_signature bin_sign; - - if (!cryptonote::get_account_address_from_str(bin_sign.address, m_wallet.testnet(), sign.address)) { - LOG_ERROR("error parsing address from string: " << sign.address); - continue; - } +// TODO: update for new rta transaction +// if (!cryptonote::get_account_address_from_str(bin_sign.address, m_wallet.testnet(), sign.address)) { +// LOG_ERROR("error parsing address from string: " << sign.address); +// continue; +// } epee::string_tools::hex_to_pod(sign.signature, bin_sign.signature); bin_signs.push_back(bin_sign); diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index 0c19b68ed..0c3dbd539 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -54,6 +54,7 @@ class PendingTransactionImpl : public PendingTransaction bool save(std::ostream &stream); std::vector getRawTransaction() const override; void updateTransactionCache() override; + // TODO: update for new rta tx structure void putRtaSignatures(const std::vector &) override; private: friend class WalletImpl; diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index c52dabb5e..c4c9839c2 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -59,7 +59,7 @@ namespace Monero { struct RtaSignature { - std::string address; + size_t key_index; std::string signature; }; diff --git a/tests/supernode_tests/graft_splitted_tx_test.cpp b/tests/supernode_tests/graft_splitted_tx_test.cpp index 3cb36b2b7..8878f4454 100644 --- a/tests/supernode_tests/graft_splitted_tx_test.cpp +++ b/tests/supernode_tests/graft_splitted_tx_test.cpp @@ -236,7 +236,6 @@ TEST_F(GraftSplittedFeeTest, RtaSignatures) cryptonote::rta_signature sign; - sign.address = wallet->get_account().get_keys().m_account_address; const cryptonote::account_keys &keys = wallet->get_account().get_keys(); crypto::hash tx_hash = cryptonote::get_transaction_hash(ptx.tx); crypto::generate_signature(tx_hash, keys.m_account_address.m_spend_public_key, keys.m_spend_secret_key, sign.signature); @@ -258,9 +257,6 @@ TEST_F(GraftSplittedFeeTest, RtaSignatures) ASSERT_TRUE(tx_test.rta_signatures.size() == 8); ASSERT_TRUE(ptx.tx.rta_signatures.size() == tx_test.rta_signatures.size()); for (size_t i = 0; i < ptx.tx.rta_signatures.size(); ++i) { - std::string address1 = cryptonote::get_account_address_as_str(true, ptx.tx.rta_signatures.at(i).address); - std::string address2 = cryptonote::get_account_address_as_str(true, tx_test.rta_signatures.at(i).address); - ASSERT_TRUE(address1 == address2); crypto::signature sign1 = ptx.tx.rta_signatures.at(i).signature; crypto::signature sign2 = tx_test.rta_signatures.at(i).signature; ASSERT_TRUE(sign1 == sign2); @@ -308,7 +304,6 @@ TEST_F(GraftSplittedFeeTest, RtaTransaction) tools::wallet2 *wallet2 = new tools::wallet2(true, false); ASSERT_NO_THROW(wallet2->load(wallet_path1, "")); cryptonote::rta_signature sign; - sign.address = wallet2->get_account().get_keys().m_account_address; const cryptonote::account_keys &keys = wallet2->get_account().get_keys(); crypto::hash tx_hash; epee::string_tools::hex_to_pod(ptx->txid()[0], tx_hash); @@ -317,8 +312,7 @@ TEST_F(GraftSplittedFeeTest, RtaTransaction) for (int i = 0; i < 8; ++i) { hex_signs.push_back({ - epee::string_tools::pod_to_hex(sign.address), - epee::string_tools::pod_to_hex(sign.signature) + size_t(i), epee::string_tools::pod_to_hex(sign.signature) }); } @@ -337,7 +331,7 @@ TEST_F(GraftSplittedFeeTest, RtaTransaction) ASSERT_TRUE(tx.version == 3); ASSERT_TRUE(epee::string_tools::pod_to_hex(tx_hash2) == ptx->txid()[0]); ASSERT_TRUE(tx.rta_signatures.size() == 8); - ASSERT_TRUE(crypto::check_signature(tx_hash2, tx.rta_signatures[0].address.m_spend_public_key, tx.rta_signatures[0].signature)); + // ASSERT_TRUE(crypto::check_signature(tx_hash2, tx.rta_signatures[0].address.m_spend_public_key, tx.rta_signatures[0].signature)); ASSERT_TRUE(tx.type == cryptonote::transaction::tx_type_rta); ASSERT_TRUE(ptx->commit()); wallet->store(""); diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 56f46f692..ae20ac24d 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -672,6 +672,48 @@ TEST(Serialization, serializes_ringct_types) } +TEST(Serialization, serializes_rta_transaction_correctly) +{ + string blob; + + // Empty tx + cryptonote::transaction tx; + cryptonote::transaction tx1; + tx.version = 3; + tx.type = cryptonote::transaction::tx_type_rta; + cryptonote::rta_header rta_hdr_in, rta_hdr_out; + std::vector accounts; + + for (size_t i = 0; i < 10; ++i) { + cryptonote::account_base acc; + acc.generate(); + rta_hdr_in.keys.push_back(acc.get_keys().m_account_address.m_view_public_key); + accounts.push_back(acc); + } + + cryptonote::add_graft_rta_header_to_extra(tx.extra, rta_hdr_in); + + crypto::hash tx_hash; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); + + for (size_t i = 0; i < 10; ++i) { + crypto::signature sign; + crypto::generate_signature(tx_hash, accounts[i].get_keys().m_account_address.m_view_public_key, accounts[i].get_keys().m_view_secret_key, sign); + tx.rta_signatures.push_back({i, sign}); + } + + ASSERT_TRUE(serialization::dump_binary(tx, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, tx1)); + ASSERT_EQ(tx, tx1); + ASSERT_EQ(tx.rta_signatures, tx1.rta_signatures); + crypto::hash tx_hash1; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx1, tx_hash1)); + ASSERT_EQ(tx_hash, tx_hash1); + ASSERT_TRUE(cryptonote::get_graft_rta_header_from_extra(tx1, rta_hdr_out)); + ASSERT_EQ(rta_hdr_in, rta_hdr_out); + +} + TEST(Serialization, portability_wallet) { const bool testnet = true; diff --git a/tests/unit_tests/test_tx_utils.cpp b/tests/unit_tests/test_tx_utils.cpp index 3c6f23977..95dc0cf09 100644 --- a/tests/unit_tests/test_tx_utils.cpp +++ b/tests/unit_tests/test_tx_utils.cpp @@ -256,3 +256,62 @@ TEST(parse_tx_extra, handles_graft_tx_extra_and_pubkey) ASSERT_TRUE(cryptonote::get_graft_tx_extra_from_extra(tx, graft_tx_extra2)); ASSERT_EQ(graft_tx_extra1, graft_tx_extra2); } + +TEST(parse_tx_extra, handles_rta_header) +{ + cryptonote::transaction tx = AUTO_VAL_INIT(tx); + cryptonote::account_base acc; + acc.generate(); + cryptonote::blobdata b = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + ASSERT_TRUE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, TEST_FEE, acc.get_keys().m_account_address, tx, b, 1)); + cryptonote::rta_header rta_hdr_in; + rta_hdr_in.payment_id = "01234567890"; + + for (int i = 0; i < 10; ++i) { + cryptonote::account_base acc; + acc.generate(); + rta_hdr_in.keys.push_back(acc.get_keys().m_account_address.m_view_public_key); + } + + ASSERT_TRUE(cryptonote::add_graft_rta_header_to_extra(tx.extra, rta_hdr_in)); + cryptonote::rta_header rta_hdr_out; + + ASSERT_TRUE(cryptonote::get_graft_rta_header_from_extra(tx, rta_hdr_out)); + ASSERT_EQ(rta_hdr_in, rta_hdr_out); +} + + +TEST(parse_tx_extra, handles_rta_signatures) +{ + cryptonote::transaction tx = AUTO_VAL_INIT(tx); + cryptonote::account_base acc; + acc.generate(); + + cryptonote::rta_header rta_hdr_in, rta_hdr_out; + rta_hdr_in.payment_id = "01234567890"; + + std::vector accounts; + for (int i = 0; i < 10; ++i) { + cryptonote::account_base acc; + acc.generate(); + rta_hdr_in.keys.push_back(acc.get_keys().m_account_address.m_view_public_key); + accounts.push_back(acc); + } + + ASSERT_TRUE(cryptonote::add_graft_rta_header_to_extra(tx.extra, rta_hdr_in)); + crypto::hash tx_hash; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); + + for (size_t i = 0; i < 10; ++i) { + crypto::signature sign; + crypto::generate_signature(tx_hash, accounts[i].get_keys().m_account_address.m_view_public_key, accounts[i].get_keys().m_view_secret_key, sign); + tx.rta_signatures.push_back({i, sign}); + } + + ASSERT_TRUE(cryptonote::get_graft_rta_header_from_extra(tx, rta_hdr_out)); + for (size_t i = 0; i < 10; ++i) { + const crypto::signature & sign = tx.rta_signatures[i].signature; + ASSERT_TRUE(crypto::check_signature(tx_hash, rta_hdr_out.keys[i], sign)); + } +} + From 44ebcda3a77366bb8e9b2374861e9b32e609d593 Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Wed, 20 Feb 2019 16:33:41 +0300 Subject: [PATCH 2/9] Revert "[WIP] fixing unit tests for added field transaction_prefix.type" This reverts commit 71d7058d28adf5802ac272581670357e0e91d085. --- tests/unit_tests/blockchain_db.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index 617287b5d..5592106cb 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -51,8 +51,8 @@ namespace { // anonymous namespace const std::vector t_blocks = { - "0606ccbba9d2057a4aca581b75d483b69a1f65f1e35446c348109bbacf886b48f7c4f30006bcc12775b6f1023d0001ff010180c0f19eded9cb99730293b634fa62c5cc21d3475b10086988584de7d1f73918b64a9708c9b08b7e510221016c5db4a5e2246f9eb6243bf31367b401cd32450d80b631b789ea2ca71804acde0000" - , "0606ccbba9d20573a01dfa6363dd8f324f3cb2d6fc34811c8f0c6f44f0b3fb80da765dcd697a1aac20b3db023e0001ff0201848799c199b3040248ef97c4f5c182f05e327136641d1bd74e98d3c3290b32d323051de4e66945fa210101c056e48b759a02eab4214c2884619ecb06168fdb383a64f076735d37cdabfd0000" + "0100d5adc49a053b8818b2b6023cd2d532c6774e164a8fcacd603651cb3ea0cb7f9340b28ec016b4bc4ca301aa0101ff6e08acbb2702eab03067870349139bee7eab2ca2e030a6bb73d4f68ab6a3b6ca937214054cdac0843d028bbe23b57ea9bae53f12da93bb57bf8a2e40598d9fccd10c2921576e987d93cd80b4891302468738e391f07c4f2b356f7957160968e0bfef6e907c3cee2d8c23cbf04b089680c6868f01025a0f41f063e195a966051e3a29e17130a9ce97d48f55285b9bb04bdd55a09ae78088aca3cf0202d0f26169290450fe17e08974789c3458910b4db18361cdc564f8f2d0bdd2cf568090cad2c60e02d6f3483ec45505cc3be841046c7a12bf953ac973939bc7b727e54258e1881d4d80e08d84ddcb0102dae6dfb16d3e28aaaf43e00170b90606b36f35f38f8a3dceb5ee18199dd8f17c80c0caf384a30202385d7e57a4daba4cdd9e550a92dcc188838386e7581f13f09de796cbed4716a42101c052492a077abf41996b50c1b2e67fd7288bcd8c55cdc657b4e22d0804371f6901beb76a82ea17400cd6d7f595f70e1667d2018ed8f5a78d1ce07484222618c3cd" + , "0100f9adc49a057d3113f562eac36f14afa08c22ae20bbbf8cffa31a4466d24850732cb96f80e9762365ee01ab0101ff6f08cc953502be76deb845c431f2ed9a4862457654b914003693b8cd672abc935f0d97b16380c08db7010291819f2873e3efbae65ecd5a736f5e8a26318b591c21e39a03fb536520ac63ba80dac40902439a10fde02e39e48e0b31e57cc084a07eedbefb8cbea0143aedd0442b189caa80c6868f010227b84449de4cd7a48cbdce8974baf0b6646e03384e32055e705c243a86bef8a58088aca3cf0202fa7bd15e4e7e884307ab130bb9d50e33c5fcea6546042a26f948efd5952459ee8090cad2c60e028695583dbb8f8faab87e3ef3f88fa827db097bbf51761d91924f5c5b74c6631780e08d84ddcb010279d2f247b54690e3b491e488acff16014a825fd740c23988a25df7c4670c1f2580c0caf384a302022599dfa3f8788b66295051d85937816e1c320cdb347a0fba5219e3fe60c83b2421010576509c5672025d28fd5d3f38efce24e1f9aaf65dd3056b2504e6e2b7f19f7800" }; const std::vector t_sizes = From 7dbac4d1a31b6dccfda5dc4904b4127778d9316a Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Sat, 9 Mar 2019 17:36:09 +0300 Subject: [PATCH 3/9] predefined key indexes fro pos, pos-proxy, wallet-proxy --- src/cryptonote_basic/cryptonote_basic.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index b7b33ff69..2362621de 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -211,6 +211,11 @@ namespace cryptonote struct rta_header { std::string payment_id; + // pre-defined key indexes for POS, POS Proxy and Wallet Proxy + static constexpr size_t POS_KEY_INDEX = 0; + static constexpr size_t POS_PROXY_KEY_INDEX = 1; + static constexpr size_t WALLET_PROXY_KEY_INDEX = 2; + std::vector keys; BEGIN_SERIALIZE_OBJECT() FIELD(payment_id) From d074ec6267538d7131df48f72a22d7b68410d983 Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Sat, 9 Mar 2019 21:48:34 +0300 Subject: [PATCH 4/9] tx_extra contant - temporary revert --- src/cryptonote_basic/tx_extra.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 7bc18992c..50d8416ac 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -42,8 +42,9 @@ #define TX_EXTRA_MERGE_MINING_TAG 0x03 // TODO: suggested to remove #define TX_EXTRA_GRAFT_EXTRA_TAG 0x04 -#define TX_EXTRA_GRAFT_STAKE_TX_TAG 0x80 -#define TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG 0x82 + +#define TX_EXTRA_GRAFT_STAKE_TX_TAG 0x10 // TODO: change this to 0x80 before public testnet release +#define TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG 0x11 // TODO: change this to 0x81 before public testnet release #define TX_EXTRA_GRAFT_RTA_HEADER_TAG 0x83 #define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE From 6d8ffba49bfe36dd4046945c83ab2103db9326c2 Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Sat, 9 Mar 2019 21:48:55 +0300 Subject: [PATCH 5/9] rta tx validation --- src/cryptonote_core/tx_pool.cpp | 53 ++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 109231d47..b5f0b12a4 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -174,34 +174,62 @@ namespace cryptonote // 1. if tx.type == tx_type_rta and tx.rta_signatures.size() > 0 // 2. if tx.version >= 3 and tx.rta_signatures.size() > 0 - bool is_rta_tx = tx.type == transaction::tx_type_rta && tx.rta_signatures.size() > 0; + bool is_rta_tx = tx.type == transaction::tx_type_rta && tx.rta_signatures.size() > 0; + + auto supernode_validator = [&] (const crypto::public_key &id_key, uint64_t height) -> bool { + // TODO: implement me: + // check if key belongs to a valid supernode + return true; + }; + // validator for rta_transaction: - auto rta_validator = [&](const std::vector &rta_signs) -> bool { + auto rta_validator = [&](const std::vector &rta_signs, const cryptonote::rta_header &rta_hdr) -> bool { bool result = true; + + if (rta_hdr.keys.size() != rta_signs.size()) { + MERROR("Failed to validate rta tx: " << id << ", keys.size() != signatures.size()"); + return false; + } for (const auto &rta_sign : rta_signs) { - // TODO: implement RTA validation here - // result &= crypto::check_signature(id, rta_sign.address.m_spend_public_key, rta_sign.signature); - result = true; - if (!result) { + // check if key index is in range + if (rta_sign.key_index >= rta_hdr.keys.size()) { + MERROR("signature: " << rta_sign.signature << " has wrong key index: " << rta_sign.key_index); + result = false; + break; + } - LOG_ERROR("Failed to validate rta tx "); -// << epee::string_tools::pod_to_hex(id) -// << " for address: " -// << cryptonote::get_account_address_as_str(m_blockchain.testnet(), rta_sign.address)); + result &= supernode_validator(rta_hdr.keys[rta_sign.key_index], m_blockchain.get_current_blockchain_height()); + if (!result) { + MERROR("Failed to validate rta tx: " << epee::string_tools::pod_to_hex(id) << ", key: " << rta_hdr.keys[rta_sign.key_index] << " doesn't belong to a valid supernode"); + break; + } + result &= crypto::check_signature(id, rta_hdr.keys[rta_sign.key_index], rta_sign.signature); + if (!result) { + MERROR("Failed to validate rta tx signature: " << epee::string_tools::pod_to_hex(id) << " for key: " << rta_hdr.keys[rta_sign.key_index]); break; } } return result; }; + if (is_rta_tx) { - bool is_rta_tx_valid = rta_validator(tx.rta_signatures); + cryptonote::rta_header rta_hdr; + if (!cryptonote::get_graft_rta_header_from_extra(tx, rta_hdr)) { + MERROR("Failed to parse rta-header from tx extra: " << id); + tvc.m_rta_signature_failed = true; + tvc.m_verifivation_failed = true; + return false; + } + + bool is_rta_tx_valid = rta_validator(tx.rta_signatures, rta_hdr); if (!kept_by_block && !is_rta_tx_valid) { LOG_ERROR("failed to validate rta tx, tx contains " << tx.rta_signatures.size() << " signatures"); tvc.m_rta_signature_failed = true; tvc.m_verifivation_failed = true; return false; } + } else { if (!kept_by_block && !m_blockchain.check_fee(blob_size, fee)) { @@ -211,9 +239,6 @@ namespace cryptonote } } - - - size_t tx_size_limit = get_transaction_size_limit(version); if (!kept_by_block && blob_size >= tx_size_limit) { From 898d36adf56c7448fbad9eccdc949c0b46284571 Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Sun, 10 Mar 2019 22:44:02 +0300 Subject: [PATCH 6/9] add: "create_rta_tx" wallet rpc --- src/wallet/wallet_rpc_server.cpp | 104 +++++++++++++++++++ src/wallet/wallet_rpc_server.h | 3 + src/wallet/wallet_rpc_server_commands_defs.h | 35 +++++++ src/wallet/wallet_rpc_server_error_codes.h | 1 + 4 files changed, 143 insertions(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 44f5a5af2..a6cd6baba 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -48,6 +48,7 @@ using namespace epee; #include "mnemonics/electrum-words.h" #include "rpc/rpc_args.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "utils/cryptmsg.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" @@ -521,6 +522,109 @@ namespace tools } return true; } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_create_rta_tx(const wallet_rpc::COMMAND_RPC_TRANSFER_RTA::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_RTA::response& res, epee::json_rpc::error& er) + { + std::vector dsts; + std::vector extra; + + LOG_PRINT_L3("on_transfer starts"); + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + // validate the transfer requested and populate dsts & extra + if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er)) + { + return false; + } + + // fill rta_header + if (req.supernode_keys.empty()) { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = "Missing supernode keys"; + return false; + } + + if (req.graft_payment_id.empty()) { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = "Missing RTA payment id"; + return false; + } + + cryptonote::rta_header rta_header; + rta_header.payment_id = req.graft_payment_id; + for (const std::string &key_str : req.supernode_keys) { + crypto::public_key key; + if (!epee::string_tools::hex_to_pod(key_str, key)) { + er.code = WALLET_RPC_ERROR_CODE_WRONG_SUPERNODE_KEY; + er.message = "Failed to parse key"; + return false; + } + rta_header.keys.push_back(key); + } + + cryptonote::add_graft_rta_header_to_extra(extra, rta_header); + + try + { + uint64_t mixin = adjust_mixin(req.mixin); + std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon, true); + + // reject proposed transactions if there are more than one. see on_transfer_split below. + if (ptx_vector.size() != 1) + { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = "Transaction would be too large. try /transfer_split."; + return false; + } + + // populate response with tx hash + res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx)); + if (req.get_tx_key) + { + res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key); + } + res.fee = ptx_vector.back().fee; + + cryptonote::blobdata blob; + tx_to_blob(ptx_vector.back().tx, blob); + res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob); + + // return encrypted tx key + std::string encrypted_key_blob; + graft::crypto_tools::encryptMessage(std::string(reinterpret_cast(&ptx_vector.back().tx_key), + sizeof(crypto::secret_key)), + rta_header.keys, encrypted_key_blob); + res.encrypted_tx_key = epee::string_tools::buff_to_hex_nodelimer(encrypted_key_blob); + return true; + } + catch (const tools::error::daemon_busy& e) + { + er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; + er.message = e.what(); + return false; + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = e.what(); + return false; + } + catch (...) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + return false; + } + return true; + + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index dd54222b0..25c40d6c9 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -84,6 +84,7 @@ namespace tools MAP_JON_RPC_WE("rescan_blockchain", on_rescan_blockchain, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN) MAP_JON_RPC_WE("set_tx_notes", on_set_tx_notes, wallet_rpc::COMMAND_RPC_SET_TX_NOTES) MAP_JON_RPC_WE("get_tx_notes", on_get_tx_notes, wallet_rpc::COMMAND_RPC_GET_TX_NOTES) + MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS) MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID) MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN) @@ -101,6 +102,7 @@ namespace tools MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES) MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET) MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET) + MAP_JON_RPC_WE("create_rta_tx", on_create_rta_tx, wallet_rpc::COMMAND_RPC_TRANSFER_RTA) END_JSON_RPC_MAP() END_URI_MAP2() @@ -110,6 +112,7 @@ namespace tools bool on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er); bool validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er); bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er); + bool on_create_rta_tx(const wallet_rpc::COMMAND_RPC_TRANSFER_RTA::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_RTA::response& res, epee::json_rpc::error& er); bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er); bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er); bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index fa5c154de..b19fc07fe 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -204,6 +204,41 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_TRANSFER_RTA + { + struct request : public COMMAND_RPC_TRANSFER::request + { + std::list supernode_keys; + std::string graft_payment_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(destinations) + KV_SERIALIZE(priority) + KV_SERIALIZE(mixin) + KV_SERIALIZE(unlock_time) + KV_SERIALIZE(payment_id) + KV_SERIALIZE(get_tx_key) + KV_SERIALIZE(supernode_keys) + KV_SERIALIZE(graft_payment_id) + KV_SERIALIZE_OPT(do_not_relay, false) + KV_SERIALIZE_OPT(get_tx_hex, false) + END_KV_SERIALIZE_MAP() + }; + + struct response : public COMMAND_RPC_TRANSFER::response + { + std::string encrypted_tx_key; // encrypted tx key using multiple key encryption + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash) + KV_SERIALIZE(tx_key) + KV_SERIALIZE(amount_keys) + KV_SERIALIZE(fee) + KV_SERIALIZE(tx_blob) + KV_SERIALIZE(encrypted_tx_key) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_SWEEP_DUST { struct request diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 3c79c0ac3..15cb5c641 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -44,3 +44,4 @@ #define WALLET_RPC_ERROR_CODE_WRONG_URI -11 #define WALLET_RPC_ERROR_CODE_WRONG_INDEX -12 #define WALLET_RPC_ERROR_CODE_NOT_OPEN -13 +#define WALLET_RPC_ERROR_CODE_WRONG_SUPERNODE_KEY -50 From 3dc55ad8408f15fd39969ae98b3f8c858af846f2 Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Mon, 11 Mar 2019 13:08:49 +0300 Subject: [PATCH 7/9] RTA tx validation + tests --- src/cryptonote_core/tx_pool.cpp | 82 ++++---- src/cryptonote_core/tx_pool.h | 9 +- tests/supernode_tests/graft_wallet_tests.cpp | 194 +++++++++++++++++++ 3 files changed, 244 insertions(+), 41 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index b5f0b12a4..26ee1c164 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -45,6 +45,7 @@ #include "warnings.h" #include "common/perf_timer.h" #include "crypto/hash.h" +#include "stake_transaction_processor.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "txpool" @@ -174,45 +175,7 @@ namespace cryptonote // 1. if tx.type == tx_type_rta and tx.rta_signatures.size() > 0 // 2. if tx.version >= 3 and tx.rta_signatures.size() > 0 - bool is_rta_tx = tx.type == transaction::tx_type_rta && tx.rta_signatures.size() > 0; - - auto supernode_validator = [&] (const crypto::public_key &id_key, uint64_t height) -> bool { - // TODO: implement me: - // check if key belongs to a valid supernode - return true; - }; - - // validator for rta_transaction: - auto rta_validator = [&](const std::vector &rta_signs, const cryptonote::rta_header &rta_hdr) -> bool { - bool result = true; - - if (rta_hdr.keys.size() != rta_signs.size()) { - MERROR("Failed to validate rta tx: " << id << ", keys.size() != signatures.size()"); - return false; - } - for (const auto &rta_sign : rta_signs) { - // check if key index is in range - if (rta_sign.key_index >= rta_hdr.keys.size()) { - MERROR("signature: " << rta_sign.signature << " has wrong key index: " << rta_sign.key_index); - result = false; - break; - } - - result &= supernode_validator(rta_hdr.keys[rta_sign.key_index], m_blockchain.get_current_blockchain_height()); - if (!result) { - MERROR("Failed to validate rta tx: " << epee::string_tools::pod_to_hex(id) << ", key: " << rta_hdr.keys[rta_sign.key_index] << " doesn't belong to a valid supernode"); - break; - } - - result &= crypto::check_signature(id, rta_hdr.keys[rta_sign.key_index], rta_sign.signature); - if (!result) { - MERROR("Failed to validate rta tx signature: " << epee::string_tools::pod_to_hex(id) << " for key: " << rta_hdr.keys[rta_sign.key_index]); - break; - } - } - return result; - }; - + bool is_rta_tx = tx.type == transaction::tx_type_rta; if (is_rta_tx) { cryptonote::rta_header rta_hdr; if (!cryptonote::get_graft_rta_header_from_extra(tx, rta_hdr)) { @@ -222,7 +185,7 @@ namespace cryptonote return false; } - bool is_rta_tx_valid = rta_validator(tx.rta_signatures, rta_hdr); + bool is_rta_tx_valid = validate_rta_tx(id, tx.rta_signatures, rta_hdr); if (!kept_by_block && !is_rta_tx_valid) { LOG_ERROR("failed to validate rta tx, tx contains " << tx.rta_signatures.size() << " signatures"); tvc.m_rta_signature_failed = true; @@ -1104,4 +1067,43 @@ namespace cryptonote { return true; } + + //--------------------------------------------------------------------------------- + bool tx_memory_pool::validate_rta_tx(const crypto::hash &txid, const std::vector &rta_signs, const rta_header &rta_hdr) const + { + bool result = true; + + if (rta_hdr.keys.size() != rta_signs.size()) { + MERROR("Failed to validate rta tx: " << txid << ", keys.size() != signatures.size()"); + return false; + } + for (const auto &rta_sign : rta_signs) { + // check if key index is in range + if (rta_sign.key_index >= rta_hdr.keys.size()) { + MERROR("signature: " << rta_sign.signature << " has wrong key index: " << rta_sign.key_index); + result = false; + break; + } + + result &= validate_supernode(m_blockchain.get_current_blockchain_height(), rta_hdr.keys[rta_sign.key_index]); + if (!result) { + MERROR("Failed to validate rta tx: " << epee::string_tools::pod_to_hex(txid) << ", key: " << rta_hdr.keys[rta_sign.key_index] << " doesn't belong to a valid supernode"); + break; + } + + result &= crypto::check_signature(txid, rta_hdr.keys[rta_sign.key_index], rta_sign.signature); + if (!result) { + MERROR("Failed to validate rta tx signature: " << epee::string_tools::pod_to_hex(txid) << " for key: " << rta_hdr.keys[rta_sign.key_index]); + break; + } + } + return result; + } + + bool tx_memory_pool::validate_supernode(uint64_t height, const public_key &id) const + { + // TODO: add pointer/reference to the StakeTransactionProcessor to this class and + // check if key belongs to a valid supernode + return true; + }; } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 6414620c9..47f54277c 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -50,6 +50,7 @@ namespace cryptonote { class Blockchain; + class StakeTransactionProcessor; /************************************************************************/ /* */ /************************************************************************/ @@ -106,7 +107,8 @@ namespace cryptonote * @param id the transaction's hash * @param blob_size the transaction's size */ - bool add_tx(transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); + bool add_tx(transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, + uint8_t version); /** * @brief add a transaction to the transaction pool @@ -455,6 +457,11 @@ namespace cryptonote */ bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const; + + bool validate_rta_tx(const crypto::hash &txid, const std::vector &rta_signs, const cryptonote::rta_header &rta_hdr) const; + + bool validate_supernode(uint64_t heigh, const crypto::public_key &id) const; + //TODO: confirm the below comments and investigate whether or not this // is the desired behavior //! map key images to transactions which spent them diff --git a/tests/supernode_tests/graft_wallet_tests.cpp b/tests/supernode_tests/graft_wallet_tests.cpp index fee932035..56128839d 100644 --- a/tests/supernode_tests/graft_wallet_tests.cpp +++ b/tests/supernode_tests/graft_wallet_tests.cpp @@ -37,6 +37,9 @@ #include "utils/utils.h" #include "net/http_client.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "wallet/wallet_rpc_server_commands_defs.h" +#include "utils/cryptmsg.h" + #include #include @@ -294,3 +297,194 @@ TEST_F(UtilsTest, DecodeAmountWithTxKeyFail) MDEBUG("amount: " << amount); ASSERT_FALSE(amount > 0); } + + +// TODO: wrong place for this tests, but just for 'quick-n-dirty' purpose +struct RtaRpcTest : public testing::Test +{ + +}; + +TEST_F(RtaRpcTest, ReturnsRtaTransaction) +{ + epee::net_utils::http::http_simple_client http_client_wallet; + using namespace cryptonote; + using namespace epee; + http_client_wallet.set_server("localhost:28682", boost::optional()); + epee::json_rpc::request req = AUTO_VAL_INIT(req); + epee::json_rpc::response res = AUTO_VAL_INIT(res); + + req.method = "create_rta_tx"; + req.jsonrpc = "2.0"; + req.id = epee::serialization::storage_entry(0); + + req.params.graft_payment_id = "test-test-test"; + req.params.priority = 0; + req.params.mixin = 7; + req.params.get_tx_key = 1; + std::vector pub_keys; + std::vector pvt_keys; + + for (int i = 0; i < 8; ++i) { + crypto::public_key pub_key; + crypto::secret_key pvt_key; + generate_keys(pub_key, pvt_key); + pub_keys.push_back(pub_key); + pvt_keys.push_back(pvt_key); + } + + req.params.destinations.push_back({200000000000, "F8sHVwypnjw4fVdMvf3iZ5bisEUJ9DcQoZDBuC7aMRMFPCTHrGtPy7CBe5r68qHdjVKgdggg5NGpAD3r6JoQQiMBAoSEo7x"}); + req.params.destinations.push_back({200000000000, "F3udPm1csSbfMeFAm3DY6xFDyNjGJY4oiPMgrYdxvLC8X4Hom2yNa4oWV3nUcPKzU5R898Ds3xCVv5choXmA5zyiDtiiHkd"}); + req.params.destinations.push_back({200000000000, "FAu8g9xL8fRhDK6rQFhdKhWjZiRunC8Nj891VY99r8C5ipcdbAMmniHPfxB7TAEAQx9kSwJ6FugJTMnkPFtaVKobJ8RUP2Y"}); + req.params.destinations.push_back({200000000000, "F7gHVKGJUmH1Bzxq2e379KF5s8BUaSAhrZuQyTMuVKkKWVK9yHBsc47f6Dc5yFoVu12fPp64ooZdf9UDpa5gmp5j2ArzktC"}); + + req.params.destinations.push_back({200000000000, "F6sFs6eJQmMeLz5G5EjDNgj2n6dZaPUuqeU4ZYyrdHVJaAVU8BXLcrRDkQswqX1Twp47DH6EBEMp2heUQ5yoi8TC6fnouxP"}); + req.params.destinations.push_back({200000000000, "FC9AP2aforbFauUn1jxvmbVBawVpq3BdPVVsxE2sDu9YNHttprbnKpiMghnbYf2mLMV3vePEV23WvCEi6TCBP6JK7ttytY3"}); + req.params.destinations.push_back({200000000000, "F5T3RSYy34iLrXHHaST9aRVC1nnSrivyA8ockQmTYLDLcybcswwGp8BMNYKmP8k2dqNM3GRQi1BEEfqr5abEJZtH3tuShKL"}); + + req.params.destinations.push_back({500000000000, "FBHdqDnz8YNU3TscB1X7eH25XLKaHmba9Vws2xwDtLKJWS7vZrii1vHVKAMGgRVz6WVcd2jN7qC1YEB4ZALUag2d9h1nnEu"}); + + for (const crypto::public_key &pkey : pub_keys) { + req.params.supernode_keys.push_back(epee::string_tools::pod_to_hex(pkey)); + } + + bool r = net_utils::invoke_http_json("/json_rpc", req, res, http_client_wallet); + ASSERT_TRUE(r); + + cryptonote::transaction tx; + std::string tx_blob; + ASSERT_TRUE(string_tools::parse_hexstr_to_binbuff(res.result.tx_blob, tx_blob)); + ASSERT_TRUE(cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx)); + + cryptonote::rta_header rta_hdr_in; + rta_hdr_in.payment_id = req.params.graft_payment_id; + rta_hdr_in.keys = pub_keys; + + cryptonote::rta_header rta_hdr_out; + ASSERT_TRUE(cryptonote::get_graft_rta_header_from_extra(tx, rta_hdr_out)); + ASSERT_EQ(rta_hdr_in, rta_hdr_out); + + std::string tx_key_encrypted; + ASSERT_TRUE(epee::string_tools::parse_hexstr_to_binbuff(res.result.encrypted_tx_key, tx_key_encrypted)); + std::string tx_key_plain; + + for (int i = 0; i < 8; ++i) { + ASSERT_TRUE(graft::crypto_tools::decryptMessage(tx_key_encrypted, pvt_keys[i], tx_key_plain)); + crypto::secret_key tx_key_decrypted = *(reinterpret_cast(tx_key_plain.c_str())); + ASSERT_EQ(epee::string_tools::pod_to_hex(tx_key_decrypted), res.result.tx_key); + } +} +// TODO: move this to appropriate place, probably even better to move to supernode (graft_server) project +TEST_F(RtaRpcTest, TransfersRtaTransaction) +{ + using namespace cryptonote; + using namespace epee; + epee::net_utils::http::http_simple_client http_client_wallet, http_client_daemon; + + http_client_wallet.set_server("localhost:28682", boost::optional()); + http_client_daemon.set_server("localhost:28681", boost::optional()); + epee::json_rpc::request req = AUTO_VAL_INIT(req); + epee::json_rpc::response res = AUTO_VAL_INIT(res); + + req.method = "create_rta_tx"; + req.jsonrpc = "2.0"; + req.id = epee::serialization::storage_entry(0); + + req.params.graft_payment_id = "test-test-test"; + req.params.priority = 0; + req.params.mixin = 7; + req.params.get_tx_key = 1; + + + // auth sample + req.params.destinations.push_back({2000000000, "F8sHVwypnjw4fVdMvf3iZ5bisEUJ9DcQoZDBuC7aMRMFPCTHrGtPy7CBe5r68qHdjVKgdggg5NGpAD3r6JoQQiMBAoSEo7x"}); + req.params.destinations.push_back({2000000000, "F3udPm1csSbfMeFAm3DY6xFDyNjGJY4oiPMgrYdxvLC8X4Hom2yNa4oWV3nUcPKzU5R898Ds3xCVv5choXmA5zyiDtiiHkd"}); + req.params.destinations.push_back({2000000000, "FAu8g9xL8fRhDK6rQFhdKhWjZiRunC8Nj891VY99r8C5ipcdbAMmniHPfxB7TAEAQx9kSwJ6FugJTMnkPFtaVKobJ8RUP2Y"}); + req.params.destinations.push_back({2000000000, "F7gHVKGJUmH1Bzxq2e379KF5s8BUaSAhrZuQyTMuVKkKWVK9yHBsc47f6Dc5yFoVu12fPp64ooZdf9UDpa5gmp5j2ArzktC"}); + // destination wallet + req.params.destinations.push_back({5000000000, "FBHdqDnz8YNU3TscB1X7eH25XLKaHmba9Vws2xwDtLKJWS7vZrii1vHVKAMGgRVz6WVcd2jN7qC1YEB4ZALUag2d9h1nnEu"}); + + std::vector pub_keys; + { + crypto::public_key pubkey; + epee::string_tools::hex_to_pod("cdba49cbdece633266681b3c6f80f1085e7b3d3e0cca395d3986d10ab3ea0d6a", pubkey); + pub_keys.push_back(pubkey); + epee::string_tools::hex_to_pod("ce7cf758df6f2eb7f74d28730078be733cb953bda37a5f6e54ab09140f40e712", pubkey); + pub_keys.push_back(pubkey); + epee::string_tools::hex_to_pod("25b316d25e6c2dd8dd60fd983de9fbd5a9bb1fcf96d65bbb1c295708bafa00cb", pubkey); + pub_keys.push_back(pubkey); + epee::string_tools::hex_to_pod("914c13339fdfacdbbebe4c223d1900415432aab24f1f995823286104c7ac9eaa", pubkey); + pub_keys.push_back(pubkey); + } + + std::vector pvt_keys; + { + crypto::secret_key pvtkey; + epee::string_tools::hex_to_pod("55260a5bf280cc91f9c36105b1dff9fef1559003f144d2fe577de8ba113ffc0b", pvtkey); + pvt_keys.push_back(pvtkey); + epee::string_tools::hex_to_pod("574fbb96e8af38f372e95608cab335f1f7d1895735004d1161c51ddba4988f09", pvtkey); + pvt_keys.push_back(pvtkey); + epee::string_tools::hex_to_pod("86cb6f1d884b2280c9ec703946b6476888d9aba78ca5e5c6367378c9ca347300", pvtkey); + pvt_keys.push_back(pvtkey); + epee::string_tools::hex_to_pod("546164cec18a87a729e83ff7683722ed184d91434638f8a04bf037b13aeb900f", pvtkey); + pvt_keys.push_back(pvtkey); + } + + for (const crypto::public_key &pkey : pub_keys) { + req.params.supernode_keys.push_back(epee::string_tools::pod_to_hex(pkey)); + } + + bool r = net_utils::invoke_http_json("/json_rpc", req, res, http_client_wallet); + ASSERT_TRUE(r); + + + cryptonote::transaction tx; + std::string tx_blob; + ASSERT_TRUE(string_tools::parse_hexstr_to_binbuff(res.result.tx_blob, tx_blob)); + ASSERT_TRUE(cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx)); + COMMAND_RPC_SEND_RAW_TX::request tx_req; + COMMAND_RPC_SEND_RAW_TX::response tx_resp; + // Try to submit tx without rta signatures + tx_req.tx_as_hex = res.result.tx_blob; + tx_req.do_not_relay = false; + r = net_utils::invoke_http_json("/sendrawtransaction", tx_req, tx_resp, http_client_daemon); + ASSERT_TRUE(r); + ASSERT_TRUE(tx_resp.rta_validation_failed); + + // try to submit tx with incorrect signatures + std::string wrong_tx_id_str = res.result.tx_hash; + for (int i = 0; i < 5; ++i) { + wrong_tx_id_str[i] = '1'; + } + crypto::hash wrong_tx_id; + epee::string_tools::hex_to_pod(wrong_tx_id_str, wrong_tx_id); + + for (size_t i = 0; i < pub_keys.size(); ++i) { + crypto::signature sign; + crypto::generate_signature(wrong_tx_id, pub_keys[i], pvt_keys[i], sign); + tx.rta_signatures.push_back({i, sign}); + } + + cryptonote::blobdata blob; + tx_to_blob(tx, blob); + tx_req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(blob); + r = net_utils::invoke_http_json("/sendrawtransaction", tx_req, tx_resp, http_client_daemon); + ASSERT_TRUE(r); + ASSERT_TRUE(tx_resp.rta_validation_failed); + + // try to submit tx with correct signatures + crypto::hash tx_id; + epee::string_tools::hex_to_pod(res.result.tx_hash, tx_id); + tx.rta_signatures.clear(); + for (size_t i = 0; i < pub_keys.size(); ++i) { + crypto::signature sign; + crypto::generate_signature(tx_id, pub_keys[i], pvt_keys[i], sign); + tx.rta_signatures.push_back({i, sign}); + } + blob.clear(); + tx_to_blob(tx, blob); + tx_req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(blob); + r = net_utils::invoke_http_json("/sendrawtransaction", tx_req, tx_resp, http_client_daemon); + ASSERT_TRUE(r); + ASSERT_FALSE(tx_resp.rta_validation_failed); +} From 99417101d618cfaa0adc43f6f119c05a24c01e4a Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Mon, 11 Mar 2019 23:03:49 +0300 Subject: [PATCH 8/9] rta-tx: check if supernode is valid --- src/cryptonote_basic/cryptonote_basic.h | 5 +++- src/cryptonote_core/cryptonote_core.cpp | 2 +- src/cryptonote_core/tx_pool.cpp | 26 ++++++++++++++------ src/cryptonote_core/tx_pool.h | 8 +++++- src/wallet/wallet_rpc_server.cpp | 14 ++++++++--- src/wallet/wallet_rpc_server_commands_defs.h | 3 ++- tests/supernode_tests/graft_wallet_tests.cpp | 6 ++++- 7 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 2362621de..8ef31df81 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -215,16 +215,19 @@ namespace cryptonote static constexpr size_t POS_KEY_INDEX = 0; static constexpr size_t POS_PROXY_KEY_INDEX = 1; static constexpr size_t WALLET_PROXY_KEY_INDEX = 2; + uint64_t auth_sample_height = 0; // block height for auth sample generation std::vector keys; BEGIN_SERIALIZE_OBJECT() FIELD(payment_id) + FIELD(auth_sample_height) FIELD(keys) END_SERIALIZE() bool operator== (const rta_header &other) const { return this->payment_id == other.payment_id - && this->keys == other.keys; + && this->keys == other.keys + && this->auth_sample_height == other.auth_sample_height; } }; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 2c6d01985..a5b228178 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -407,7 +407,7 @@ namespace cryptonote blocks_per_sync, sync_mode, fast_sync); r = m_blockchain_storage.init(db, m_testnet, test_options); - + m_mempool.set_stake_transaction_processor(&m_graft_stake_transaction_processor); r = m_mempool.init(); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 26ee1c164..75dc66a0c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -46,6 +46,7 @@ #include "common/perf_timer.h" #include "crypto/hash.h" #include "stake_transaction_processor.h" +#include "graft_rta_config.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "txpool" @@ -1073,10 +1074,16 @@ namespace cryptonote { bool result = true; + if (rta_hdr.keys.size() == 0) { + MERROR("Failed to validate rta tx, missing auth sample keys for tx: " << txid ); + return false; + } +#if 0 // don't validate signatures for rta mining if (rta_hdr.keys.size() != rta_signs.size()) { MERROR("Failed to validate rta tx: " << txid << ", keys.size() != signatures.size()"); return false; } + for (const auto &rta_sign : rta_signs) { // check if key index is in range if (rta_sign.key_index >= rta_hdr.keys.size()) { @@ -1085,11 +1092,6 @@ namespace cryptonote break; } - result &= validate_supernode(m_blockchain.get_current_blockchain_height(), rta_hdr.keys[rta_sign.key_index]); - if (!result) { - MERROR("Failed to validate rta tx: " << epee::string_tools::pod_to_hex(txid) << ", key: " << rta_hdr.keys[rta_sign.key_index] << " doesn't belong to a valid supernode"); - break; - } result &= crypto::check_signature(txid, rta_hdr.keys[rta_sign.key_index], rta_sign.signature); if (!result) { @@ -1097,13 +1099,21 @@ namespace cryptonote break; } } +#endif + for (const crypto::public_key &key : rta_hdr.keys) { + result &= validate_supernode(rta_hdr.auth_sample_height, key); + if (!result) { + MERROR("Failed to validate rta tx: " << epee::string_tools::pod_to_hex(txid) << ", key: " << key << " doesn't belong to a valid supernode"); + break; + } + } + return result; } bool tx_memory_pool::validate_supernode(uint64_t height, const public_key &id) const { - // TODO: add pointer/reference to the StakeTransactionProcessor to this class and - // check if key belongs to a valid supernode - return true; + supernode_stake * stake = const_cast(m_stp->find_supernode_stake(height, epee::string_tools::pod_to_hex(id))); + return stake ? stake->amount >= config::graft::TIER1_STAKE_AMOUNT : false; }; } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 47f54277c..7b06e2bc5 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -331,6 +331,11 @@ namespace cryptonote size_t validate(uint8_t version); + void set_stake_transaction_processor(StakeTransactionProcessor * arg) + { + m_stp = arg; + } + #define CURRENT_MEMPOOL_ARCHIVE_VER 11 #define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 12 @@ -460,7 +465,7 @@ namespace cryptonote bool validate_rta_tx(const crypto::hash &txid, const std::vector &rta_signs, const cryptonote::rta_header &rta_hdr) const; - bool validate_supernode(uint64_t heigh, const crypto::public_key &id) const; + bool validate_supernode(uint64_t height, const crypto::public_key &id) const; //TODO: confirm the below comments and investigate whether or not this // is the desired behavior @@ -508,6 +513,7 @@ namespace cryptonote std::unordered_set m_timed_out_transactions; Blockchain& m_blockchain; //!< reference to the Blockchain object + StakeTransactionProcessor * m_stp = nullptr; }; } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a6cd6baba..88f438c8f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -558,6 +558,8 @@ namespace tools cryptonote::rta_header rta_header; rta_header.payment_id = req.graft_payment_id; + rta_header.auth_sample_height = req.auth_sample_height; + for (const std::string &key_str : req.supernode_keys) { crypto::public_key key; if (!epee::string_tools::hex_to_pod(key_str, key)) { @@ -583,6 +585,9 @@ namespace tools return false; } + if (!req.do_not_relay) + m_wallet->commit_tx(ptx_vector); + // populate response with tx hash res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx)); if (req.get_tx_key) @@ -591,9 +596,12 @@ namespace tools } res.fee = ptx_vector.back().fee; - cryptonote::blobdata blob; - tx_to_blob(ptx_vector.back().tx, blob); - res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob); + if (req.get_tx_hex) + { + cryptonote::blobdata blob; + tx_to_blob(ptx_vector.back().tx, blob); + res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob); + } // return encrypted tx key std::string encrypted_key_blob; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index b19fc07fe..c0bd73b32 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -210,7 +210,7 @@ namespace wallet_rpc { std::list supernode_keys; std::string graft_payment_id; - + uint64_t auth_sample_height; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) KV_SERIALIZE(priority) @@ -220,6 +220,7 @@ namespace wallet_rpc KV_SERIALIZE(get_tx_key) KV_SERIALIZE(supernode_keys) KV_SERIALIZE(graft_payment_id) + KV_SERIALIZE(auth_sample_height) KV_SERIALIZE_OPT(do_not_relay, false) KV_SERIALIZE_OPT(get_tx_hex, false) END_KV_SERIALIZE_MAP() diff --git a/tests/supernode_tests/graft_wallet_tests.cpp b/tests/supernode_tests/graft_wallet_tests.cpp index 56128839d..20fc7c9d2 100644 --- a/tests/supernode_tests/graft_wallet_tests.cpp +++ b/tests/supernode_tests/graft_wallet_tests.cpp @@ -391,6 +391,7 @@ TEST_F(RtaRpcTest, TransfersRtaTransaction) req.id = epee::serialization::storage_entry(0); req.params.graft_payment_id = "test-test-test"; + req.params.auth_sample_height = 243285; req.params.priority = 0; req.params.mixin = 7; req.params.get_tx_key = 1; @@ -436,8 +437,9 @@ TEST_F(RtaRpcTest, TransfersRtaTransaction) bool r = net_utils::invoke_http_json("/json_rpc", req, res, http_client_wallet); ASSERT_TRUE(r); + ASSERT_FALSE(res.result.tx_hash.empty()); - +#if 0 cryptonote::transaction tx; std::string tx_blob; ASSERT_TRUE(string_tools::parse_hexstr_to_binbuff(res.result.tx_blob, tx_blob)); @@ -487,4 +489,6 @@ TEST_F(RtaRpcTest, TransfersRtaTransaction) r = net_utils::invoke_http_json("/sendrawtransaction", tx_req, tx_resp, http_client_daemon); ASSERT_TRUE(r); ASSERT_FALSE(tx_resp.rta_validation_failed); +#endif + } From 3640677343c75cf7eddf991f1c96ced0a7ac32d1 Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Mon, 11 Mar 2019 23:06:07 +0300 Subject: [PATCH 9/9] rename: create_rta_tx->transfer_rta --- src/wallet/wallet_rpc_server.cpp | 4 ++-- src/wallet/wallet_rpc_server.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 88f438c8f..cb773e9a9 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -523,12 +523,12 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_create_rta_tx(const wallet_rpc::COMMAND_RPC_TRANSFER_RTA::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_RTA::response& res, epee::json_rpc::error& er) + bool wallet_rpc_server::on_transfer_rta(const wallet_rpc::COMMAND_RPC_TRANSFER_RTA::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_RTA::response& res, epee::json_rpc::error& er) { std::vector dsts; std::vector extra; - LOG_PRINT_L3("on_transfer starts"); + LOG_PRINT_L3("on_transfer_rta starts"); if (!m_wallet) return not_open(er); if (m_wallet->restricted()) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 25c40d6c9..b876c52b4 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -102,7 +102,7 @@ namespace tools MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES) MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET) MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET) - MAP_JON_RPC_WE("create_rta_tx", on_create_rta_tx, wallet_rpc::COMMAND_RPC_TRANSFER_RTA) + MAP_JON_RPC_WE("transfer_rta", on_transfer_rta, wallet_rpc::COMMAND_RPC_TRANSFER_RTA) END_JSON_RPC_MAP() END_URI_MAP2() @@ -112,7 +112,7 @@ namespace tools bool on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er); bool validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er); bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er); - bool on_create_rta_tx(const wallet_rpc::COMMAND_RPC_TRANSFER_RTA::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_RTA::response& res, epee::json_rpc::error& er); + bool on_transfer_rta(const wallet_rpc::COMMAND_RPC_TRANSFER_RTA::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_RTA::response& res, epee::json_rpc::error& er); bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er); bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er); bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er);