diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 43315d1119..6b3979cf42 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -250,6 +250,7 @@ namespace cryptonote { else if (swap_address_prefix == prefix || integrated_swap_address_prefix == prefix) { // Identify addr as swap addr adr.is_swap_addr = true; + has_payment_id = false; } else { diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 9ff9b30457..40a634dd86 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -114,14 +114,14 @@ static const struct { } testnet_hard_forks[] = { // version 1 from the start of the blockchain { 1, 1, 0, config::testnet::GENESIS_TIMESTAMP }, - { 2, 101, 0, 1518115575 }, - { 3, 201, 0, 1518117468 }, - { 4, 301, 0, 1518118888 }, - { 5, 401, 0, 1539941268 }, - { 6, 501, 0, 1551264860 }, - { 7, 901, 0, 1551264860 + 1000 } // Give it some time offset + { 2, 11, 0, 1518115575 }, + { 3, 21, 0, 1518117468 }, + { 4, 31, 0, 1518118888 }, + { 5, 41, 0, 1539941268 }, + { 6, 51, 0, 1551264860 }, + { 7, 71, 0, 1551264860 + 1000 } // Give it some time offset }; -static const uint64_t testnet_hard_fork_version_1_till = 100; +static const uint64_t testnet_hard_fork_version_1_till = 10; //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 30f46e4f17..6ba5788a24 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -37,6 +37,7 @@ using namespace epee; #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" +#include "cryptonote_core/swap_address.h" namespace cryptonote { @@ -279,6 +280,7 @@ namespace cryptonote for(const tx_destination_entry& dst_entr: shuffled_dsts) { CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount); + crypto::key_derivation derivation; crypto::public_key out_eph_public_key; bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation); @@ -298,13 +300,7 @@ namespace cryptonote txout_to_key tk = AUTO_VAL_INIT(tk); - if (dst_entr.addr.is_swap_addr) { - // Zero key for swap txs - // TODO Should probably do all address math only when neccessary - } else { - tk.key = out_eph_public_key; - } - + tk.key = out_eph_public_key; out.target = tk; tx.vout.push_back(out); @@ -514,7 +510,7 @@ namespace cryptonote //--------------------------------------------------------------- bool is_swap_tx(const transaction& tx, const std::vector& destinations) { - // a tx is a swap tx if it has at least one swap destination address (null_pkey output) AND swap info in extra.userdata + // a tx is a swap tx if it has at least one swap destination address (null_pkey output)(not true anymore) AND swap info in extra.userdata bool has_swap_destinations = false; if (!destinations.empty()) @@ -540,9 +536,9 @@ namespace cryptonote } } } - - if (!has_swap_destinations) - return false; // No swap destinations + // Ignore, we are using regular swap wallet + //if (!has_swap_destinations) + // return false; // No swap destinations std::vector extra_fields; diff --git a/src/cryptonote_core/swap_address.h b/src/cryptonote_core/swap_address.h index d55d46c6bc..ef1396277a 100644 --- a/src/cryptonote_core/swap_address.h +++ b/src/cryptonote_core/swap_address.h @@ -6,10 +6,26 @@ #define SWAP_PUBLIC_ADDRESS_BASE58_PREFIX 0x73f7 // 'iT' #define SWAP_PUBLIC_INTEG_ADDRESS_BASE58_PREFIX 0x6af7 // 'iTH' +#define SWAP_ENABLED 0 + +#if SWAP_ENABLED + +#define SWAP_ADDRESS_ENCRYPTION_PUB_KEY "" +#define SWAP_ADDRESS_ENCRYPTION_SEC_KEY "" + +#define SWAP_WALLET "" + +#else + // Just for testing #define SWAP_ADDRESS_ENCRYPTION_PUB_KEY "f2de2998375bd562ca98a2f9b576fa0f659651fc15b557c4d411e0004a47df24" #define SWAP_ADDRESS_ENCRYPTION_SEC_KEY "72ae3e7de47bbb5af78ed6608a1eabe77a2429c385d28e708c01afaa82737900" +#define SWAP_WALLET "TixxsGTkkhyKydNiptoWW8fNkoHiwfvQDPbqPxWwt6VVKJnu59mmSAEGh2ezTLfXZhVAfrJwV7AT3YGXtzTf7H8r9p32qm7UZP" + +#endif + + namespace cryptonote { #pragma pack(push, 1) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b02fcaab96..a5a4fe3930 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -43,6 +43,8 @@ using namespace epee; #include "crypto/hash.h" #include "rpc/rpc_args.h" #include "core_rpc_server_error_codes.h" +#include "cryptonote_core/swap_address.h" +#include "ringct/rctSigs.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc" @@ -1765,7 +1767,154 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + uint64_t decodeRctHelper(const rct::rctSig & rv, const crypto::public_key &pub, const crypto::secret_key &sec, unsigned int i, rct::key & mask) + { + crypto::key_derivation derivation; + bool r = crypto::generate_key_derivation(pub, sec, derivation); + if (!r) + { + LOG_ERROR("Failed to generate key derivation to decode rct output " << i); + return 0; + } + crypto::secret_key scalar1; + crypto::derivation_to_scalar(derivation, i, scalar1); + try + { + switch (rv.type) + { + case rct::RCTTypeSimple: + return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask); + case rct::RCTTypeFull: + return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask); + default: + LOG_ERROR("Unsupported rct type: " << rv.type); + return 0; + } + } + catch (const std::exception &e) + { + LOG_ERROR("Failed to decode input " << i); + return 0; + } + } + + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_block_swap_txs_by_height(const COMMAND_RPC_GET_BLOCK_SWAP_TXS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_SWAP_TXS_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp) + { + if(!check_core_busy()) + { + error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; + error_resp.message = "Core is busy."; + return false; + } + + Blockchain& bchain = m_core.get_blockchain_storage(); + + if (bchain.get_current_blockchain_height() < req.height) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Invalid height param"; + return false; + } + + block target_block = AUTO_VAL_INIT(target_block); + if(!bchain.get_block_by_hash(bchain.get_block_id_by_height(req.height), target_block)) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Couldn't get block data"; + return false; + } + + crypto::secret_key priv_view; + if (!epee::string_tools::hex_to_pod(req.priv_view, priv_view)) { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Couldn't decode view key"; + return false; + } + + crypto::secret_key swap_dev_key; + if (!epee::string_tools::hex_to_pod(req.swap_dev_key, swap_dev_key)) { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Couldn't decode swap dev key"; + return false; + } + + account_public_address swap_wallet_addr; + crypto::hash8 payment_id; + bool has_payment_id; + if (!get_account_integrated_address_from_str(swap_wallet_addr, has_payment_id, payment_id, !SWAP_ENABLED, SWAP_WALLET)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Couldn't decode swap wallet address"; + return false; + } + + for(const auto& blk_tx_hash : target_block.tx_hashes) + { + try { + transaction blk_tx = bchain.get_db().get_tx(blk_tx_hash); + if (is_swap_tx(blk_tx)) + { + crypto::secret_key swap_encrypt_sec_key = AUTO_VAL_INIT(swap_encrypt_sec_key); + epee::string_tools::hex_to_pod(SWAP_ADDRESS_ENCRYPTION_SEC_KEY, swap_encrypt_sec_key); + account_public_address swap_addr = AUTO_VAL_INIT(swap_addr); + cryptonote::get_swap_data_from_tx(blk_tx, swap_encrypt_sec_key, swap_addr); + + // Check for tx public key + crypto::public_key tx_pub = get_tx_pub_key_from_extra(blk_tx); + + if(null_pkey == tx_pub) { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Couldn't get tx pub key"; + return false; + } + + crypto::key_derivation derivation; + generate_key_derivation(tx_pub, priv_view, derivation); + + uint64_t total_amount = 0; + for (int i = 0; i < blk_tx.vout.size(); i++) + { + bool received = false; + + if (blk_tx.vout[i].target.type() != typeid(txout_to_key)) + { + LOG_ERROR("wrong type id in transaction out"); + continue; + } + + // See if we own the out + received = is_out_to_acc_precomp(swap_wallet_addr.m_spend_public_key, boost::get(blk_tx.vout[i].target), derivation, i); + if (received) + { + cryptonote::keypair in_ephemeral; + crypto::key_image key_image; + rct::key mask; + // If we do, decode real amount + uint64_t money_transfered = decodeRctHelper(blk_tx.rct_signatures, tx_pub, priv_view, i, mask); + total_amount += money_transfered; + } else { + continue; + } + } + + swap_tx_info tx_data = { + .tx_hash = epee::string_tools::pod_to_hex(blk_tx_hash), + .amount = total_amount, + .rcv_address = get_account_address_as_str(true, swap_addr) + }; + res.txs.push_back(tx_data); + } + } catch (...) { + continue; + } + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ const command_line::arg_descriptor core_rpc_server::arg_rpc_bind_port = { "rpc-bind-port" , "Port for RPC server" diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index dbbe07972c..4e3bb0c828 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -126,6 +126,7 @@ namespace cryptonote MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted) MAP_JON_RPC_WE("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG) + MAP_JON_RPC_WE("get_block_swap_txs", on_get_block_swap_txs_by_height, COMMAND_RPC_GET_BLOCK_SWAP_TXS_BY_HEIGHT) END_JSON_RPC_MAP() END_URI_MAP2() @@ -184,6 +185,7 @@ namespace cryptonote bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp); bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp); bool on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp); + bool on_get_block_swap_txs_by_height(const COMMAND_RPC_GET_BLOCK_SWAP_TXS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_SWAP_TXS_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp); //----------------------- private: diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 57662924d5..29b4f2d644 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 14 +#define CORE_RPC_VERSION_MINOR 15 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -1691,4 +1691,39 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct swap_tx_info + { + std::string tx_hash; + uint64_t amount; + std::string rcv_address; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash) + KV_SERIALIZE(amount) + KV_SERIALIZE(rcv_address) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_GET_BLOCK_SWAP_TXS_BY_HEIGHT + { + struct request { + uint64_t height; + std::string priv_view; // Swap wallet priv view key + std::string swap_dev_key; // Swap priv key + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(height) + KV_SERIALIZE(priv_view) + KV_SERIALIZE(swap_dev_key) + END_KV_SERIALIZE_MAP() + }; + + struct response { + vector txs; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 6586734066..9cbe7cc25e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -61,6 +61,7 @@ #include "common/json_util.h" #include "ringct/rctSigs.h" #include "wallet/wallet_args.h" +#include "cryptonote_core/swap_address.h" #include #ifdef HAVE_READLINE @@ -2439,12 +2440,19 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector dsts; for (size_t i = 0; i < local_args.size(); i += 2) { cryptonote::tx_destination_entry de; bool has_payment_id; crypto::hash8 new_payment_id; + + // If destination is on new blockchain + // Swap addr flag should be set in following line if (!cryptonote::get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i], oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); @@ -2478,21 +2486,55 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectortestnet(), SWAP_WALLET)) + { + // Change target addr to swap_wallet + de.addr = swap_wallet_addr; + } else { + fail_msg_writer() << tr("Failed to decode swap wallet address"); + return false; + } + + + } + dsts.push_back(de); } - // prompt is there is no payment id and confirmation is required - if (!payment_id_seen && m_wallet->confirm_missing_payment_id()) - { - std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); - if (std::cin.eof()) - return true; - if (!command_line::is_yes(accepted)) - { - fail_msg_writer() << tr("transaction cancelled."); + if (is_swap_transfer) { + // For now use random payment ids for swap transfers + crypto::hash payment_id; + generate_random_bytes_not_thread_safe(sizeof(crypto::hash), &payment_id); + set_swap_tx_extra(extra, payment_id, swap_addr); + } else { + // prompt is there is no payment id and confirmation is required + if (!payment_id_seen && m_wallet->confirm_missing_payment_id()) + { + std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); + if (std::cin.eof()) + return true; + if (!command_line::is_yes(accepted)) + { + fail_msg_writer() << tr("transaction cancelled."); - return true; - } + return true; + } + } } try @@ -2601,7 +2643,16 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector 1) { prompt << boost::format(tr("Your transaction needs to be split into %llu transactions. " @@ -2642,6 +2693,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorwatch_only()) { + if (is_swap_transfer) + { + fail_msg_writer() << tr("Swap txs not allowed on watch only wallets"); + return true; + } + bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); if (!r) {