diff --git a/contrib/tor-connect b/contrib/tor-connect index 486a46e9..b589edb1 160000 --- a/contrib/tor-connect +++ b/contrib/tor-connect @@ -1 +1 @@ -Subproject commit 486a46e927449a5827cf3cb69731bc0e3811b46a +Subproject commit b589edb1906dccb387cfeded6ed12286c5f0405f diff --git a/src/common/error_codes.h b/src/common/error_codes.h index 7b77fec3..313a9aad 100644 --- a/src/common/error_codes.h +++ b/src/common/error_codes.h @@ -40,4 +40,5 @@ #define API_RETURN_CODE_TX_IS_TOO_BIG "TX_IS_TOO_BIG" #define API_RETURN_CODE_TX_REJECTED "TX_REJECTED" #define API_RETURN_CODE_HTLC_ORIGIN_HASH_MISSMATCHED "HTLC_ORIGIN_HASH_MISSMATCHED" -#define API_RETURN_CODE_WRAP "WRAP" \ No newline at end of file +#define API_RETURN_CODE_WRAP "WRAP" +#define API_RETURN_CODE_MISSING_ZC_INPUTS "MISSING_ZC_INPUTS" \ No newline at end of file diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index be5a1417..e601b888 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -5150,6 +5150,88 @@ std::shared_ptr blockchain_storage::find_key_imag return std::shared_ptr(); } +//--------------------------------------------------------------- +bool blockchain_storage::fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short) const +{ + //tei.blob = tx_ptr->tx + tei.id = epee::string_tools::pod_to_hex(h); + if (!tei.blob_size) + tei.blob_size = get_object_blobsize(tx); + + tei.fee = get_tx_fee(tx); + tei.pub_key = epee::string_tools::pod_to_hex(get_tx_pub_key_from_extra(tx)); + tei.timestamp = timestamp; + tei.amount = get_outs_money_amount(tx); + + if (is_short) + return true; + + fill_tx_rpc_inputs(tei, tx); + fill_tx_rpc_outputs(tei, tx, ptce); + fill_tx_rpc_payload_items(tei.extra, tx.extra); + fill_tx_rpc_payload_items(tei.attachments, tx.attachment); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::fill_tx_rpc_inputs(tx_rpc_extended_info& tei, const transaction& tx) const +{ + + //handle inputs + for (auto in : tx.vin) + { + tei.ins.push_back(tx_in_rpc_entry()); + tx_in_rpc_entry& entry_to_fill = tei.ins.back(); + if (in.type() == typeid(txin_gen)) + { + entry_to_fill.amount = 0; + } + else if (in.type() == typeid(txin_to_key) || in.type() == typeid(txin_htlc) || in.type() == typeid(txin_zc_input)) + { + //TODO: add htlc info + entry_to_fill.amount = get_amount_from_variant(in); + entry_to_fill.kimage_or_ms_id = epee::string_tools::pod_to_hex(get_key_image_from_txin_v(in)); + const std::vector& key_offsets = get_key_offsets_from_txin_v(in); + std::vector absolute_offsets = relative_output_offsets_to_absolute(key_offsets); + for (auto& ao : absolute_offsets) + { + entry_to_fill.global_indexes.push_back(0); + if (ao.type() == typeid(uint64_t)) + { + entry_to_fill.global_indexes.back() = boost::get(ao); + } + else if (ao.type() == typeid(ref_by_id)) + { + //disable for the reset at the moment + auto tx_ptr = get_tx_chain_entry(boost::get(ao).tx_id); + if (!tx_ptr || tx_ptr->m_global_output_indexes.size() <= boost::get(ao).n) + { + tei.ins.back().global_indexes.back() = std::numeric_limits::max(); + return false; + } + tei.ins.back().global_indexes.back() = tx_ptr->m_global_output_indexes[boost::get(ao).n]; + } + } + if (in.type() == typeid(txin_htlc)) + { + entry_to_fill.htlc_origin = epee::string_tools::buff_to_hex_nodelimer(boost::get(in).hltc_origin); + } + //tk.etc_details -> visualize it may be later + } + else if (in.type() == typeid(txin_multisig)) + { + txin_multisig& tms = boost::get(in); + entry_to_fill.amount = tms.amount; + entry_to_fill.kimage_or_ms_id = epee::string_tools::pod_to_hex(tms.multisig_out_id); + if (tx.signatures.size() >= tei.ins.size() && + tx.signatures[tei.ins.size() - 1].type() == typeid(NLSAG_sig)) + { + entry_to_fill.multisig_count = boost::get(tx.signatures[tei.ins.size() - 1]).s.size(); + } + + } + } + return true; +} //------------------------------------------------------------------ bool blockchain_storage::prune_aged_alt_blocks() { @@ -6327,15 +6409,7 @@ bool blockchain_storage::prevalidate_block(const block& bl) return true; } - // HF3 - if ( m_core_runtime_config.is_hardfork_active_for_height(3, block_height) && - !m_core_runtime_config.is_hardfork_active_for_height(4, block_height)) - { - CHECK_AND_ASSERT_MES(bl.major_version == HF3_BLOCK_MAJOR_VERSION, false, "HF3, incorrect block major version: " << (int)bl.major_version); - } - - - //after hard_fork3 + // >= HF3 if (bl.major_version > CURRENT_BLOCK_MAJOR_VERSION) { LOG_ERROR("prevalidation failed for block " << get_block_hash(bl) << ": major block version " << static_cast(bl.major_version) << " is incorrect, " << CURRENT_BLOCK_MAJOR_VERSION << " is expected" << ENDL diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index e5791569..f9dfa437 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -334,6 +334,8 @@ namespace currency // returns true as soon as the hardfork is active for the NEXT upcoming block (not for the top block in the blockchain storage) bool is_hardfork_active(size_t hardfork_id) const; + bool fill_tx_rpc_inputs(tx_rpc_extended_info& tei, const transaction& tx) const; + bool fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short = false) const; wide_difficulty_type block_difficulty(size_t i)const; bool forecast_difficulty(std::vector> &out_height_2_diff_vector, bool pos) const; diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index e64155c0..b86d5e70 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -56,8 +56,8 @@ namespace currency const static crypto::key_derivation null_derivation = AUTO_VAL_INIT(null_derivation); const static crypto::hash gdefault_genesis = epee::string_tools::hex_to_pod("CC608F59F8080E2FBFE3C8C80EB6E6A953D47CF2D6AEBD345BADA3A1CAB99852"); - const static crypto::hash ffff_hash = epee::string_tools::hex_to_pod("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - const static crypto::public_key ffff_pkey = epee::string_tools::hex_to_pod("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // TODO @#@# consider getting rid of this + //const static crypto::hash ffff_hash = epee::string_tools::hex_to_pod("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + //const static crypto::public_key ffff_pkey = epee::string_tools::hex_to_pod("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // TODO @#@# consider getting rid of this // TODO: rewtire the following to a normal aggregate initialization once we move to C++17 -- sowle const static crypto::public_key native_coin_asset_id = reinterpret_cast(static_cast(crypto::ec_scalar{'\xd6', '\x32', '\x9b', '\x5b', '\x1f', '\x7c', '\x08', '\x05', '\xb5', '\xc3', '\x45', '\xf4', '\x95', '\x75', '\x54', '\x00', '\x2a', '\x2f', '\x55', '\x78', '\x45', '\xf6', '\x4d', '\x76', '\x45', '\xda', '\xe0', '\xe0', '\x51', '\xa6', '\x49', '\x8a'})); // == crypto::c_point_H, checked in crypto_constants diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index b36eaeaa..c5a82881 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -406,7 +406,7 @@ namespace currency tx_destination_entry de = AUTO_VAL_INIT(de); de.addr.push_back(miner_address); de.amount = a; - de.explicit_native_asset_id = true; // don't use asset id blinding as it's obvious which asset it is + de.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; // don't use asset id blinding as it's obvious which asset it is if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW) { //this means that block is creating after hardfork_1 and unlock_time is needed to set for every destination separately @@ -421,7 +421,7 @@ namespace currency if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW) stake_lock_time = pe.stake_unlock_time; destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address, stake_lock_time)); - destinations.back().explicit_native_asset_id = true; // don't use asset id blinding as it's obvious which asset it is + destinations.back().flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; // don't use asset id blinding as it's obvious which asset it is } CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS || height == 0, false, "Too many outs (" << destinations.size() << ")! Miner tx can't be constructed."); @@ -1143,8 +1143,8 @@ namespace currency blinded_asset_id = crypto::point_t(de.asset_id) + asset_blinding_mask * crypto::c_point_X; out.blinded_asset_id = (crypto::c_scalar_1div8 * blinded_asset_id).to_public_key(); // T = 1/8 * (H_asset + s * X) - CHECK_AND_ASSERT_MES(!de.explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id"); - asset_blinding_mask = de.explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + CHECK_AND_ASSERT_MES(!de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id"); + asset_blinding_mask = de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) amount_commitment = de.amount * blinded_asset_id + amount_blinding_mask * crypto::c_point_G; out.amount_commitment = (crypto::c_scalar_1div8 * amount_commitment).to_public_key(); // E = 1/8 * e * T + 1/8 * y * G @@ -1163,8 +1163,8 @@ namespace currency crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h); out.encrypted_amount = de.amount ^ amount_mask.m_u64[0]; - CHECK_AND_ASSERT_MES(!de.explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id"); - asset_blinding_mask = de.explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + CHECK_AND_ASSERT_MES(!de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id || de.asset_id == currency::native_coin_asset_id, false, "explicit_native_asset_id may be used only with native asset id"); + asset_blinding_mask = de.flags & tx_destination_entry_flags::tdef_explicit_native_asset_id ? 0 : crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_ASSET_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) blinded_asset_id = crypto::point_t(de.asset_id) + asset_blinding_mask * crypto::c_point_X; out.blinded_asset_id = (crypto::c_scalar_1div8 * blinded_asset_id).to_public_key(); // T = 1/8 * (H_asset + s * X) @@ -1549,13 +1549,6 @@ namespace currency crypto::key_derivation get_encryption_key_derivation(bool is_income, const transaction& tx, const account_keys& acc_keys) { crypto::key_derivation derivation = null_derivation; - tx_crypto_checksum crypto_info = AUTO_VAL_INIT(crypto_info); - if (!get_type_in_variant_container(tx.extra, crypto_info) && !get_type_in_variant_container(tx.attachment, crypto_info)) - { - //no crypt info in tx - return null_derivation; - } - if (is_income) { crypto::public_key tx_pub_key = currency::get_tx_pub_key_from_extra(tx); @@ -1563,18 +1556,25 @@ namespace currency bool r = crypto::generate_key_derivation(tx_pub_key, acc_keys.view_secret_key, derivation); CHECK_AND_ASSERT_MES(r, null_derivation, "failed to generate_key_derivation"); LOG_PRINT_GREEN("DECRYPTING ON KEY: " << epee::string_tools::pod_to_hex(derivation) << ", key derived from destination addr: " << currency::get_account_address_as_str(acc_keys.account_address), LOG_LEVEL_0); + return derivation; } else { + tx_crypto_checksum crypto_info = AUTO_VAL_INIT(crypto_info); + if (!get_type_in_variant_container(tx.extra, crypto_info) && !get_type_in_variant_container(tx.attachment, crypto_info)) + { + //no crypt info in tx + return null_derivation; + } + derivation = crypto_info.encrypted_key_derivation; crypto::chacha_crypt(derivation, acc_keys.spend_secret_key); LOG_PRINT_GREEN("DECRYPTING ON KEY: " << epee::string_tools::pod_to_hex(derivation) << ", key decrypted from sender address: " << currency::get_account_address_as_str(acc_keys.account_address), LOG_LEVEL_0); + //validate derivation we here. Yoda style + crypto::hash hash_for_check_sum = crypto::cn_fast_hash(&derivation, sizeof(derivation)); + CHECK_AND_ASSERT_MES(*(uint32_t*)&hash_for_check_sum == crypto_info.derivation_hash, null_derivation, "Derivation hash missmatched in tx id " << currency::get_transaction_hash(tx)); + return derivation; } - - //validate derivation we here. Yoda style - crypto::hash hash_for_check_sum = crypto::cn_fast_hash(&derivation, sizeof(derivation)); - CHECK_AND_ASSERT_MES(*(uint32_t*)&hash_for_check_sum == crypto_info.derivation_hash, null_derivation, "Derivation hash missmatched in tx id " << currency::get_transaction_hash(tx)); - return derivation; } //--------------------------------------------------------------- template @@ -1612,9 +1612,8 @@ namespace currency } //--------------------------------------------------------------- - void encrypt_attachments(transaction& tx, const account_keys& sender_keys, const account_public_address& destination_addr, const keypair& tx_random_key) + void encrypt_attachments(transaction& tx, const account_keys& sender_keys, const account_public_address& destination_addr, const keypair& tx_random_key, crypto::key_derivation& derivation) { - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); bool r = crypto::generate_key_derivation(destination_addr.view_public_key, tx_random_key.sec, derivation); CHECK_AND_ASSERT_MES(r, void(), "failed to generate_key_derivation"); bool was_attachment_crypted_entries = false; @@ -1645,6 +1644,12 @@ namespace currency } } //--------------------------------------------------------------- + void encrypt_attachments(transaction& tx, const account_keys& sender_keys, const account_public_address& destination_addr, const keypair& tx_random_key) + { + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); + return encrypt_attachments(tx, sender_keys, destination_addr, tx_random_key, derivation); + } + //--------------------------------------------------------------- void load_wallet_transfer_info_flags(tools::wallet_public::wallet_transfer_info& x) { x.is_service = currency::is_service_tx(x.tx); @@ -2022,7 +2027,8 @@ namespace currency append_mode = true; keypair txkey = AUTO_VAL_INIT(txkey); - + size_t zc_inputs_count = 0; + bool has_non_zc_inputs = false; if (!append_mode) { @@ -2052,7 +2058,7 @@ namespace currency //include offers if need tx.attachment = attachments; - encrypt_attachments(tx, sender_account_keys, crypt_destination_addr, txkey); + encrypt_attachments(tx, sender_account_keys, crypt_destination_addr, txkey, result.derivation); } else { @@ -2061,7 +2067,8 @@ namespace currency CHECK_AND_ASSERT_MES(txkey.pub != null_pkey && txkey.sec != null_skey, false, "In append mode both public and secret keys must be provided"); //separately encrypt attachments without putting extra - crypto::key_derivation derivation = get_encryption_key_derivation(true, tx, sender_account_keys); + result.derivation = get_encryption_key_derivation(true, tx, sender_account_keys); + crypto::key_derivation& derivation = result.derivation; CHECK_AND_ASSERT_MES(derivation != null_derivation, false, "failed to generate_key_derivation"); bool was_attachment_crypted_entries = false; std::vector extra_local = extra; @@ -2076,9 +2083,20 @@ namespace currency tx.attachment.insert(tx.attachment.end(), attachments_local.begin(), attachments_local.end()); tx.extra.insert(tx.extra.end(), extra_local.begin(), extra_local.end()); + + for (const auto in : tx.vin) + { + if (in.type() == typeid(txin_zc_input)) + { + zc_inputs_count++; + } + else { + has_non_zc_inputs = true; + } + } } - + size_t thirdparty_zc_inputs_count = zc_inputs_count; // // INs // @@ -2088,7 +2106,6 @@ namespace currency size_t current_index = 0; inputs_mapping.resize(sources.size()); size_t input_starter_index = tx.vin.size(); - size_t zc_inputs_count = 0; bool all_inputs_are_obviously_native_coins = true; for (const tx_source_entry& src_entr : sources) { @@ -2107,6 +2124,7 @@ namespace currency input_multisig.multisig_out_id = src_entr.multisig_id; input_multisig.sigs_count = src_entr.ms_sigs_count; tx.vin.push_back(input_multisig); + has_non_zc_inputs = true; } else if (src_entr.htlc_origin.size()) { @@ -2141,6 +2159,7 @@ namespace currency input_to_key.key_offsets.push_back(src_entr.outputs.front().out_reference); tx.vin.push_back(input_to_key); + has_non_zc_inputs = true; } else { @@ -2183,6 +2202,7 @@ namespace currency input_to_key.k_image = img; input_to_key.key_offsets = std::move(key_offsets); tx.vin.push_back(input_to_key); + has_non_zc_inputs = true; } } @@ -2203,15 +2223,15 @@ namespace currency } } } - bool has_non_zc_inputs = zc_inputs_count != sources.size(); // TODO @#@# reconsider this for consilidated txs + // // OUTs // std::vector shuffled_dsts(destinations); - size_t outputs_to_be_constructed = shuffled_dsts.size(); + //size_t outputs_to_be_constructed = shuffled_dsts.size(); tx_generation_context& gen_context = result.ftp.gen_context; - gen_context.resize(zc_inputs_count, outputs_to_be_constructed); + gen_context.resize(zc_inputs_count, tx.vout.size() + shuffled_dsts.size()); // ASSET oprations handling if (tx.version > TRANSACTION_VERSION_PRE_HF4) @@ -2235,7 +2255,7 @@ namespace currency uint64_t amount_of_emitted_asset = 0; for (auto& item : shuffled_dsts) { - if (item.asset_id == currency::ffff_pkey) + if (item.asset_id == currency::null_pkey) { item.asset_id = gen_context.ao_asset_id; // set calculated asset_id to the asset's outputs, if this asset is being emitted within this tx amount_of_emitted_asset += item.amount; @@ -2259,24 +2279,24 @@ namespace currency // construct outputs uint64_t native_coins_output_sum = 0; size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1 - uint64_t range_proof_start_index = output_index; + uint64_t range_proof_start_index = 0; std::set deriv_cache; - for(size_t j = 0; j < outputs_to_be_constructed; ++j, ++output_index) + for(size_t destination_index = 0; destination_index < shuffled_dsts.size(); ++destination_index, ++output_index) { - tx_destination_entry& dst_entr = shuffled_dsts[j]; - if (all_inputs_are_obviously_native_coins && gen_context.ao_asset_id == currency::null_pkey) - dst_entr.explicit_native_asset_id = true; // all inputs are obviously native coins -- all outputs must have explicit asset ids (unless there's an asset emission) + tx_destination_entry& dst_entr = shuffled_dsts[destination_index]; + if (!append_mode && all_inputs_are_obviously_native_coins && gen_context.ao_asset_id == currency::null_pkey) + dst_entr.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; // all inputs are obviously native coins -- all outputs must have explicit asset ids (unless there's an asset emission) CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); // <<-- TODO @#@# consider removing this check r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, - gen_context.asset_id_blinding_masks[j], gen_context.amount_blinding_masks[j], - gen_context.blinded_asset_ids[j], gen_context.amount_commitments[j], result, tx_outs_attr); + gen_context.asset_id_blinding_masks[output_index], gen_context.amount_blinding_masks[output_index], + gen_context.blinded_asset_ids[output_index], gen_context.amount_commitments[output_index], result, tx_outs_attr); CHECK_AND_ASSERT_MES(r, false, "Failed to construct tx out"); - gen_context.amounts[j] = dst_entr.amount; - gen_context.asset_ids[j] = crypto::point_t(dst_entr.asset_id); - gen_context.asset_id_blinding_mask_x_amount_sum += gen_context.asset_id_blinding_masks[j] * dst_entr.amount; - gen_context.amount_blinding_masks_sum += gen_context.amount_blinding_masks[j]; - gen_context.amount_commitments_sum += gen_context.amount_commitments[j]; + gen_context.amounts[output_index] = dst_entr.amount; + gen_context.asset_ids[output_index] = crypto::point_t(dst_entr.asset_id); + gen_context.asset_id_blinding_mask_x_amount_sum += gen_context.asset_id_blinding_masks[output_index] * dst_entr.amount; + gen_context.amount_blinding_masks_sum += gen_context.amount_blinding_masks[output_index]; + gen_context.amount_commitments_sum += gen_context.amount_commitments[output_index]; if (dst_entr.is_native_coin()) native_coins_output_sum += dst_entr.amount; } @@ -2311,8 +2331,16 @@ namespace currency std::sort(tx.vin.begin() + input_starter_index, tx.vin.end(), less_txin_v); // add explicit fee info - r = add_tx_fee_amount_to_extra(tx, native_coins_input_sum - native_coins_output_sum); - CHECK_AND_ASSERT_MES(r, false, "add_tx_fee_amount_to_extra failed"); + uint64_t fee_to_declare = native_coins_input_sum - native_coins_output_sum; + if (flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) + { + fee_to_declare = ftp.mode_separate_fee; + } + if (fee_to_declare) + { + r = add_tx_fee_amount_to_extra(tx, fee_to_declare); + CHECK_AND_ASSERT_MES(r, false, "add_tx_fee_amount_to_extra failed"); + } } // attachments container should be sealed by now @@ -2348,13 +2376,13 @@ namespace currency // ring signatures (per-input proofs) r = false; + size_t curren_zc_index = thirdparty_zc_inputs_count; for (size_t i_ = 0; i_ != sources.size(); i_++) { size_t i_mapped = inputs_mapping[i_]; - const tx_source_entry& source_entry = sources[i_mapped]; crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, i_ + input_starter_index, tx_prefix_hash); - CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "prepare_prefix_hash_for_sign failed"); + CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "prepare_prefix_hash_for_sign failed"); std::stringstream ss_ring_s; if (source_entry.is_zc()) @@ -2363,6 +2391,8 @@ namespace currency // blinding_masks_sum is supposed to be sum(mask of all tx output) - sum(masks of all pseudo out commitments) r = generate_ZC_sig(tx_hash_for_signature, i_ + input_starter_index, source_entry, in_contexts[i_mapped], sender_account_keys, flags, gen_context, tx, i_ + 1 == sources.size()); CHECK_AND_ASSERT_MES(r, false, "generate_ZC_sigs failed"); + gen_context.zc_input_amounts[curren_zc_index] = source_entry.amount; + curren_zc_index++; } else { @@ -2377,7 +2407,8 @@ namespace currency // // proofs (transaction-wise, not pre-input) // - if (tx.version > TRANSACTION_VERSION_PRE_HF4) + if (tx.version > TRANSACTION_VERSION_PRE_HF4 && + (append_mode || (flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) == 0)) { // asset surjection proof currency::zc_asset_surjection_proof asp{}; @@ -3502,7 +3533,7 @@ namespace currency } if (p_amount_burnt) *p_amount_burnt = sum_of_bare_outs_burnt; - return sum_of_bare_outs_burnt == amount; + return sum_of_bare_outs_burnt >= amount; } // post HF-4 txs @@ -3564,235 +3595,7 @@ namespace currency return cnt; } - struct rpc_tx_payload_handler : public boost::static_visitor - { - tx_extra_rpc_entry& tv; - rpc_tx_payload_handler(tx_extra_rpc_entry& t) : tv(t) - {} - bool operator()(const tx_service_attachment& ee) - { - tv.type = "service"; - tv.short_view = ee.service_id + ":" + ee.instruction; - if (ee.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY) - tv.short_view += "(encrypted)"; - else - { - std::string deflated_buff; - const std::string* pfinalbuff = &ee.body; - if (ee.flags&TX_SERVICE_ATTACHMENT_DEFLATE_BODY) - { - bool r = epee::zlib_helper::unpack(ee.body, deflated_buff); - CHECK_AND_ASSERT_MES(r, false, "Failed to unpack"); - pfinalbuff = &deflated_buff; - } - if (ee.service_id == BC_PAYMENT_ID_SERVICE_ID || ee.service_id == BC_OFFERS_SERVICE_ID) - tv.details_view = *pfinalbuff; - else - tv.details_view = "BINARY DATA"; - } - return true; - } - bool operator()(const tx_crypto_checksum& ee) - { - tv.type = "crypto_checksum"; - tv.short_view = std::string("derivation_hash: ") + epee::string_tools::pod_to_hex(ee.derivation_hash); - tv.details_view = std::string("derivation_hash: ") + epee::string_tools::pod_to_hex(ee.derivation_hash) + "\n" - + "encrypted_key_derivation: " + epee::string_tools::pod_to_hex(ee.encrypted_key_derivation); - - return true; - } - bool operator()(const etc_tx_time& ee) - { - tv.type = "pos_time"; - tv.short_view = std::string("timestamp: ") + std::to_string(ee.v) + " " + epee::misc_utils::get_internet_time_str(ee.v); - return true; - } - bool operator()(const etc_tx_details_unlock_time& ee) - { - tv.type = "unlock_time"; - if (ee.v < CURRENCY_MAX_BLOCK_NUMBER) - tv.short_view = std::string("height: ") + std::to_string(ee.v); - else - tv.short_view = std::string("timestamp: ") + std::to_string(ee.v) + " " + epee::misc_utils::get_internet_time_str(ee.v); - - return true; - } - bool operator()(const etc_tx_details_unlock_time2& ee) - { - tv.type = "unlock_time"; - std::stringstream ss; - ss << "["; - for (auto v : ee.unlock_time_array) - { - ss << " " << v; - } - ss << "]"; - tv.short_view = ss.str(); - - return true; - } - bool operator()(const etc_tx_details_expiration_time& ee) - { - tv.type = "expiration_time"; - if (ee.v < CURRENCY_MAX_BLOCK_NUMBER) - tv.short_view = std::string("height: ") + std::to_string(ee.v); - else - tv.short_view = std::string("timestamp: ") + std::to_string(ee.v) + " " + epee::misc_utils::get_internet_time_str(ee.v); - - return true; - } - bool operator()(const etc_tx_details_flags& ee) - { - tv.type = "details_flags"; - tv.short_view = epee::string_tools::pod_to_hex(ee.v); - return true; - } - bool operator()(const crypto::public_key& ee) - { - tv.type = "pub_key"; - tv.short_view = epee::string_tools::pod_to_hex(ee); - return true; - } - bool operator()(const extra_attachment_info& ee) - { - tv.type = "attachment_info"; - tv.short_view = std::to_string(ee.sz) + " bytes"; - tv.details_view = currency::obj_to_json_str(ee); - return true; - } - bool operator()(const extra_alias_entry& ee) - { - tv.type = "alias_info"; - tv.short_view = ee.m_alias + "-->" + get_account_address_as_str(ee.m_address); - tv.details_view = currency::obj_to_json_str(ee); - - return true; - } - bool operator()(const extra_alias_entry_old& ee) - { - return operator()(static_cast(ee)); - } - bool operator()(const extra_user_data& ee) - { - tv.type = "user_data"; - tv.short_view = std::to_string(ee.buff.size()) + " bytes"; - tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee.buff); - - return true; - } - bool operator()(const extra_padding& ee) - { - tv.type = "extra_padding"; - tv.short_view = std::to_string(ee.buff.size()) + " bytes"; - if (!ee.buff.empty()) - tv.details_view = epee::string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(&ee.buff[0]), ee.buff.size())); - - return true; - } - bool operator()(const tx_comment& ee) - { - tv.type = "comment"; - tv.short_view = std::to_string(ee.comment.size()) + " bytes(encrypted)"; - tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee.comment); - - return true; - } - bool operator()(const tx_payer& ee) - { - //const tx_payer& ee = boost::get(extra); - tv.type = "payer"; - tv.short_view = "(encrypted)"; - - return true; - } - bool operator()(const tx_payer_old&) - { - tv.type = "payer_old"; - tv.short_view = "(encrypted)"; - - return true; - } - bool operator()(const tx_receiver& ee) - { - //const tx_payer& ee = boost::get(extra); - tv.type = "receiver"; - tv.short_view = "(encrypted)"; - - return true; - } - bool operator()(const tx_receiver_old& ee) - { - tv.type = "receiver_old"; - tv.short_view = "(encrypted)"; - - return true; - } - bool operator()(const tx_derivation_hint& ee) - { - tv.type = "derivation_hint"; - tv.short_view = std::to_string(ee.msg.size()) + " bytes"; - tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee.msg); - - return true; - } - bool operator()(const std::string& ee) - { - tv.type = "string"; - tv.short_view = std::to_string(ee.size()) + " bytes"; - tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee); - - return true; - } - bool operator()(const etc_tx_flags16_t& dh) - { - tv.type = "FLAGS16"; - tv.short_view = epee::string_tools::pod_to_hex(dh); - tv.details_view = epee::string_tools::pod_to_hex(dh); - - return true; - } - bool operator()(const zarcanum_tx_data_v1& ztxd) - { - tv.type = "zarcanum_tx_data_v1"; - tv.short_view = "fee = " + print_money_brief(ztxd.fee); - tv.details_view = tv.short_view; - return true; - } - bool operator()(const zc_outs_range_proof& rp) - { - tv.type = "zc_outs_range_proof"; - // TODO @#@# - //tv.short_view = "outputs_count = " + std::to_string(rp.outputs_count); - return true; - } - bool operator()(const zc_balance_proof& bp) - { - tv.type = "zc_balance_proof"; - return true; - } - template - bool operator()(const t_type& t_t) - { - tv.type = typeid(t_t).name(); - return true; - } - }; - //------------------------------------------------------------------ - template - bool fill_tx_rpc_payload_items(std::vector& target_vector, const t_container& tc) - { - //handle extra - for (auto& extra : tc) - { - target_vector.push_back(tx_extra_rpc_entry()); - tx_extra_rpc_entry& tv = target_vector.back(); - - rpc_tx_payload_handler vstr(tv); - boost::apply_visitor(vstr, extra); - } - return true; - } //------------------------------------------------------------------ bool fill_tx_rpc_outputs(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce) { @@ -3913,28 +3716,7 @@ namespace currency pei_rpc.penalty = (pei_rpc.base_reward + pei_rpc.total_fee) - pei_rpc.summary_reward; return true; } - //--------------------------------------------------------------- - bool fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short) - { - //tei.blob = tx_ptr->tx - tei.id = epee::string_tools::pod_to_hex(h); - if(!tei.blob_size) - tei.blob_size = get_object_blobsize(tx); - tei.fee = get_tx_fee(tx); - tei.pub_key = epee::string_tools::pod_to_hex(get_tx_pub_key_from_extra(tx)); - tei.timestamp = timestamp; - tei.amount = get_outs_money_amount(tx); - - if (is_short) - return true; - - fill_tx_rpc_inputs(tei, tx); - fill_tx_rpc_outputs(tei, tx, ptce); - fill_tx_rpc_payload_items(tei.extra, tx.extra); - fill_tx_rpc_payload_items(tei.attachments, tx.attachment); - return true; - } //------------------------------------------------------------------ void append_per_block_increments_for_tx(const transaction& tx, std::unordered_map& gindices) { diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index a025c43b..195e1cb3 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -29,6 +29,9 @@ #include "currency_format_utils_transactions.h" #include "core_runtime_config.h" #include "wallet/wallet_public_structs_defs.h" +#include "bc_attachments_helpers.h" +#include "bc_payments_id_service.h" +#include "bc_offers_service_basic.h" // ------ get_tx_type_definition ------------- @@ -139,6 +142,7 @@ namespace currency struct finalize_tx_param { + uint64_t unlock_time; std::vector extra; std::vector attachments; @@ -153,6 +157,7 @@ namespace currency uint64_t expiration_time; crypto::public_key spend_pub_key; // only for validations uint64_t tx_version; + uint64_t mode_separate_fee = 0; tx_generation_context gen_context{}; // solely for consolidated txs @@ -171,6 +176,7 @@ namespace currency FIELD(expiration_time) FIELD(spend_pub_key) FIELD(tx_version) + FIELD(mode_separate_fee) if (flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) FIELD(gen_context); END_SERIALIZE() @@ -183,6 +189,7 @@ namespace currency finalize_tx_param ftp; std::string htlc_origin; std::vector> outs_key_images; // pairs (out_index, key_image) for each change output + crypto::key_derivation derivation; BEGIN_SERIALIZE_OBJECT() FIELD(tx) @@ -190,6 +197,7 @@ namespace currency FIELD(ftp) FIELD(htlc_origin) FIELD(outs_key_images) + FIELD(derivation) END_SERIALIZE() }; @@ -339,6 +347,7 @@ namespace currency bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t current_blockchain_size, uint64_t current_time); crypto::key_derivation get_encryption_key_derivation(bool is_income, const transaction& tx, const account_keys& acc_keys); bool decrypt_payload_items(bool is_income, const transaction& tx, const account_keys& acc_keys, std::vector& decrypted_items); + void encrypt_attachments(transaction& tx, const account_keys& sender_keys, const account_public_address& destination_addr, const keypair& tx_random_key, crypto::key_derivation& derivation); void encrypt_attachments(transaction& tx, const account_keys& sender_keys, const account_public_address& destination_addr, const keypair& tx_random_key); bool is_derivation_used_to_encrypt(const transaction& tx, const crypto::key_derivation& derivation); bool is_address_like_wrapped(const std::string& addr); @@ -414,8 +423,7 @@ namespace currency update_alias_rpc_details alias_info_to_rpc_update_alias_info(const currency::extra_alias_entry& ai, const std::string& old_address); size_t get_service_attachments_count_in_tx(const transaction& tx); bool fill_tx_rpc_outputs(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce); - bool fill_tx_rpc_inputs(tx_rpc_extended_info& tei, const transaction& tx); - bool fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short = false); + bool fill_block_rpc_details(block_rpc_extended_info& pei_rpc, const block_extended_info& bei_chain, const crypto::hash& h); void append_per_block_increments_for_tx(const transaction& tx, std::unordered_map& gindices); std::string get_word_from_timstamp(uint64_t timestamp, bool use_password); @@ -891,5 +899,236 @@ namespace currency const difficulties& b_diff ); + struct rpc_tx_payload_handler : public boost::static_visitor + { + tx_extra_rpc_entry& tv; + rpc_tx_payload_handler(tx_extra_rpc_entry& t) : tv(t) + {} + + bool operator()(const tx_service_attachment& ee) + { + tv.type = "service"; + tv.short_view = ee.service_id + ":" + ee.instruction; + if (ee.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY) + tv.short_view += "(encrypted)"; + else + { + std::string deflated_buff; + const std::string* pfinalbuff = &ee.body; + if (ee.flags&TX_SERVICE_ATTACHMENT_DEFLATE_BODY) + { + bool r = epee::zlib_helper::unpack(ee.body, deflated_buff); + CHECK_AND_ASSERT_MES(r, false, "Failed to unpack"); + pfinalbuff = &deflated_buff; + } + if (ee.service_id == BC_PAYMENT_ID_SERVICE_ID || ee.service_id == BC_OFFERS_SERVICE_ID) + tv.details_view = *pfinalbuff; + else + tv.details_view = "BINARY DATA"; + } + return true; + } + bool operator()(const tx_crypto_checksum& ee) + { + tv.type = "crypto_checksum"; + tv.short_view = std::string("derivation_hash: ") + epee::string_tools::pod_to_hex(ee.derivation_hash); + tv.details_view = std::string("derivation_hash: ") + epee::string_tools::pod_to_hex(ee.derivation_hash) + "\n" + + "encrypted_key_derivation: " + epee::string_tools::pod_to_hex(ee.encrypted_key_derivation); + + return true; + } + bool operator()(const etc_tx_time& ee) + { + tv.type = "pos_time"; + tv.short_view = std::string("timestamp: ") + std::to_string(ee.v) + " " + epee::misc_utils::get_internet_time_str(ee.v); + return true; + } + bool operator()(const etc_tx_details_unlock_time& ee) + { + tv.type = "unlock_time"; + if (ee.v < CURRENCY_MAX_BLOCK_NUMBER) + tv.short_view = std::string("height: ") + std::to_string(ee.v); + else + tv.short_view = std::string("timestamp: ") + std::to_string(ee.v) + " " + epee::misc_utils::get_internet_time_str(ee.v); + + return true; + } + bool operator()(const etc_tx_details_unlock_time2& ee) + { + tv.type = "unlock_time"; + std::stringstream ss; + ss << "["; + for (auto v : ee.unlock_time_array) + { + ss << " " << v; + } + ss << "]"; + tv.short_view = ss.str(); + + return true; + } + bool operator()(const etc_tx_details_expiration_time& ee) + { + tv.type = "expiration_time"; + if (ee.v < CURRENCY_MAX_BLOCK_NUMBER) + tv.short_view = std::string("height: ") + std::to_string(ee.v); + else + tv.short_view = std::string("timestamp: ") + std::to_string(ee.v) + " " + epee::misc_utils::get_internet_time_str(ee.v); + + return true; + } + bool operator()(const etc_tx_details_flags& ee) + { + tv.type = "details_flags"; + tv.short_view = epee::string_tools::pod_to_hex(ee.v); + return true; + } + bool operator()(const crypto::public_key& ee) + { + tv.type = "pub_key"; + tv.short_view = epee::string_tools::pod_to_hex(ee); + return true; + } + bool operator()(const extra_attachment_info& ee) + { + tv.type = "attachment_info"; + tv.short_view = std::to_string(ee.sz) + " bytes"; + tv.details_view = currency::obj_to_json_str(ee); + return true; + } + bool operator()(const extra_alias_entry& ee) + { + tv.type = "alias_info"; + tv.short_view = ee.m_alias + "-->" + get_account_address_as_str(ee.m_address); + tv.details_view = currency::obj_to_json_str(ee); + + return true; + } + bool operator()(const extra_alias_entry_old& ee) + { + return operator()(static_cast(ee)); + } + bool operator()(const extra_user_data& ee) + { + tv.type = "user_data"; + tv.short_view = std::to_string(ee.buff.size()) + " bytes"; + tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee.buff); + + return true; + } + bool operator()(const extra_padding& ee) + { + tv.type = "extra_padding"; + tv.short_view = std::to_string(ee.buff.size()) + " bytes"; + if (!ee.buff.empty()) + tv.details_view = epee::string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(&ee.buff[0]), ee.buff.size())); + + return true; + } + bool operator()(const tx_comment& ee) + { + tv.type = "comment"; + tv.short_view = std::to_string(ee.comment.size()) + " bytes(encrypted)"; + tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee.comment); + + return true; + } + bool operator()(const tx_payer& ee) + { + //const tx_payer& ee = boost::get(extra); + tv.type = "payer"; + tv.short_view = "(encrypted)"; + + return true; + } + bool operator()(const tx_payer_old&) + { + tv.type = "payer_old"; + tv.short_view = "(encrypted)"; + + return true; + } + bool operator()(const tx_receiver& ee) + { + //const tx_payer& ee = boost::get(extra); + tv.type = "receiver"; + tv.short_view = "(encrypted)"; + + return true; + } + bool operator()(const tx_receiver_old& ee) + { + tv.type = "receiver_old"; + tv.short_view = "(encrypted)"; + + return true; + } + bool operator()(const tx_derivation_hint& ee) + { + tv.type = "derivation_hint"; + tv.short_view = std::to_string(ee.msg.size()) + " bytes"; + tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee.msg); + + return true; + } + bool operator()(const std::string& ee) + { + tv.type = "string"; + tv.short_view = std::to_string(ee.size()) + " bytes"; + tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee); + + return true; + } + bool operator()(const etc_tx_flags16_t& dh) + { + tv.type = "FLAGS16"; + tv.short_view = epee::string_tools::pod_to_hex(dh); + tv.details_view = epee::string_tools::pod_to_hex(dh); + + return true; + } + bool operator()(const zarcanum_tx_data_v1& ztxd) + { + tv.type = "zarcanum_tx_data_v1"; + tv.short_view = "fee = " + print_money_brief(ztxd.fee); + tv.details_view = tv.short_view; + return true; + } + bool operator()(const zc_outs_range_proof& rp) + { + tv.type = "zc_outs_range_proof"; + // TODO @#@# + //tv.short_view = "outputs_count = " + std::to_string(rp.outputs_count); + return true; + } + bool operator()(const zc_balance_proof& bp) + { + tv.type = "zc_balance_proof"; + return true; + } + template + bool operator()(const t_type& t_t) + { + tv.type = typeid(t_t).name(); + return true; + } + }; + //------------------------------------------------------------------ + + + template + bool fill_tx_rpc_payload_items(std::vector& target_vector, const t_container& tc) + { + //handle extra + for (auto& extra : tc) + { + target_vector.push_back(tx_extra_rpc_entry()); + tx_extra_rpc_entry& tv = target_vector.back(); + + rpc_tx_payload_handler vstr(tv); + boost::apply_visitor(vstr, extra); + } + return true; + } } // namespace currency diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index e7071e0c..95bb4cdd 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -397,5 +397,14 @@ namespace currency CATCH_ENTRY2(std::vector{}); } //--------------------------------------------------------------- + bool validate_tx_output_details_againt_tx_generation_context(const transaction& tx, const tx_generation_context& gen_context, const crypto::secret_key& onet_time_key) + { + //TODO: Implement this function before mainnet +#ifdef TESTNET + return true; +#else + return false; +#endif + } } \ No newline at end of file diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index 4b7aeeb7..e4f16d1f 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -94,6 +94,13 @@ namespace currency }; + enum tx_destination_entry_flags + { + tdef_none = 0, + tdef_explicit_native_asset_id = 0x0001, + tdef_explicit_amount_to_provide = 0x0002 + }; + struct tx_destination_entry { uint64_t amount = 0; // money @@ -103,7 +110,7 @@ namespace currency uint64_t unlock_time = 0; destination_option_htlc_out htlc_options; // htlc options crypto::public_key asset_id = currency::native_coin_asset_id; // not blinded, not premultiplied - bool explicit_native_asset_id = false; + uint64_t flags = 0; // set of flags (see tx_destination_entry_flags) tx_destination_entry() = default; tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad) {} @@ -123,7 +130,7 @@ namespace currency FIELD(unlock_time) FIELD(htlc_options) FIELD(asset_id) - FIELD(explicit_native_asset_id) + FIELD(flags) END_SERIALIZE() }; //--------------------------------------------------------------- @@ -201,7 +208,7 @@ namespace currency bool tx_to_blob(const transaction& b, blobdata& b_blob); bool read_keyimages_from_tx(const transaction& tx, std::list& kil); bool validate_inputs_sorting(const transaction& tx); - bool is_asset_emitting_transaction(const transaction& tx, asset_descriptor_operation* p_ado = nullptr); + bool is_asset_emitting_transaction(const transaction& tx, asset_descriptor_operation* p_ado = nullptr); std::vector prepare_outputs_entries_for_key_offsets(const std::vector& outputs, size_t old_real_index, size_t& new_real_index) noexcept; @@ -218,6 +225,7 @@ namespace currency asset_id_blinding_masks.resize(outs_count); amounts.resize(outs_count); amount_blinding_masks.resize(outs_count); + zc_input_amounts.resize(zc_ins_count); } // TODO @#@# reconsider this check -- sowle @@ -245,6 +253,7 @@ namespace currency std::vector pseudo_outs_blinded_asset_ids; // generate_asset_surjection_proof crypto::scalar_vec_t pseudo_outs_plus_real_out_blinding_masks; // r_pi + r'_j // generate_asset_surjection_proof std::vector real_zc_ins_asset_ids; // H_i // generate_asset_surjection_proof + std::vector zc_input_amounts; // ZC only input amounts // common data: inputs crypto::point_t pseudo_out_amount_commitments_sum = crypto::c_point_0; // generate_tx_balance_proof generate_ZC_sig @@ -264,44 +273,48 @@ namespace currency // consider redesign, some data may possibly be excluded from kv serialization -- sowle BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(asset_ids); - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blinded_asset_ids); - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(amount_commitments); - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(asset_id_blinding_masks); - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(amounts); - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(amount_blinding_masks); - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(pseudo_outs_blinded_asset_ids); - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(pseudo_outs_plus_real_out_blinding_masks); - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(real_zc_ins_asset_ids); - KV_SERIALIZE_POD_AS_HEX_STRING(pseudo_out_amount_commitments_sum); - KV_SERIALIZE_POD_AS_HEX_STRING(pseudo_out_amount_blinding_masks_sum); - KV_SERIALIZE_POD_AS_HEX_STRING(real_in_asset_id_blinding_mask_x_amount_sum); - KV_SERIALIZE_POD_AS_HEX_STRING(amount_commitments_sum); - KV_SERIALIZE_POD_AS_HEX_STRING(amount_blinding_masks_sum); - KV_SERIALIZE_POD_AS_HEX_STRING(asset_id_blinding_mask_x_amount_sum); - KV_SERIALIZE_POD_AS_HEX_STRING(ao_asset_id); - KV_SERIALIZE_POD_AS_HEX_STRING(ao_asset_id_pt); - KV_SERIALIZE_POD_AS_HEX_STRING(ao_amount_commitment); - KV_SERIALIZE_POD_AS_HEX_STRING(ao_amount_blinding_mask); + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(asset_ids) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blinded_asset_ids) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(amount_commitments) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(asset_id_blinding_masks) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(amounts) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(amount_blinding_masks) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(pseudo_outs_blinded_asset_ids) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(pseudo_outs_plus_real_out_blinding_masks) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(real_zc_ins_asset_ids) + KV_SERIALIZE(zc_input_amounts) + KV_SERIALIZE_POD_AS_HEX_STRING(pseudo_out_amount_commitments_sum) + KV_SERIALIZE_POD_AS_HEX_STRING(pseudo_out_amount_blinding_masks_sum) + KV_SERIALIZE_POD_AS_HEX_STRING(real_in_asset_id_blinding_mask_x_amount_sum) + KV_SERIALIZE_POD_AS_HEX_STRING(amount_commitments_sum) + KV_SERIALIZE_POD_AS_HEX_STRING(amount_blinding_masks_sum) + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id_blinding_mask_x_amount_sum) + KV_SERIALIZE_POD_AS_HEX_STRING(ao_asset_id) + KV_SERIALIZE_POD_AS_HEX_STRING(ao_asset_id_pt) + KV_SERIALIZE_POD_AS_HEX_STRING(ao_amount_commitment) + KV_SERIALIZE_POD_AS_HEX_STRING(ao_amount_blinding_mask) END_KV_SERIALIZE_MAP() // solely for consolidated txs, asset opration fields are not serialized BEGIN_SERIALIZE_OBJECT() - FIELD(asset_ids); - FIELD(blinded_asset_ids); - FIELD(amount_commitments); - FIELD((std::vector&)(asset_id_blinding_masks)); - FIELD((std::vector&)(amounts)); - FIELD((std::vector&)(amount_blinding_masks)); - FIELD(pseudo_outs_blinded_asset_ids); - FIELD((std::vector&)(pseudo_outs_plus_real_out_blinding_masks)); - FIELD(real_zc_ins_asset_ids); - FIELD(pseudo_out_amount_commitments_sum); - FIELD(pseudo_out_amount_blinding_masks_sum); - FIELD(real_in_asset_id_blinding_mask_x_amount_sum); - FIELD(amount_commitments_sum); - FIELD(amount_blinding_masks_sum); - FIELD(asset_id_blinding_mask_x_amount_sum); + VERSION() + CURRENT_VERSION(0) + FIELD(asset_ids) + FIELD(blinded_asset_ids) + FIELD(amount_commitments) + FIELD((std::vector&)(asset_id_blinding_masks)) + FIELD((std::vector&)(amounts)) + FIELD((std::vector&)(amount_blinding_masks)) + FIELD(pseudo_outs_blinded_asset_ids) + FIELD((std::vector&)(pseudo_outs_plus_real_out_blinding_masks)) + FIELD(real_zc_ins_asset_ids) + FIELD(zc_input_amounts) + FIELD(pseudo_out_amount_commitments_sum) + FIELD(pseudo_out_amount_blinding_masks_sum) + FIELD(real_in_asset_id_blinding_mask_x_amount_sum) + FIELD(amount_commitments_sum) + FIELD(amount_blinding_masks_sum) + FIELD(asset_id_blinding_mask_x_amount_sum) // no asset operation fields here //ao_asset_id @@ -311,4 +324,6 @@ namespace currency END_SERIALIZE() }; // struct tx_generation_context + bool validate_tx_output_details_againt_tx_generation_context(const transaction& tx, const tx_generation_context& gen_context, const crypto::secret_key& onet_time_key); + } // namespace currency diff --git a/src/currency_core/tx_pool.cpp b/src/currency_core/tx_pool.cpp index 61e5a449..8fcca00f 100644 --- a/src/currency_core/tx_pool.cpp +++ b/src/currency_core/tx_pool.cpp @@ -516,7 +516,7 @@ namespace currency txs.push_back(tx_rpc_extended_info()); tx_rpc_extended_info& trei = txs.back(); trei.blob_size = tx_entry.blob_size; - fill_tx_rpc_details(trei, tx_entry.tx, nullptr, h, tx_entry.receive_time, true); + m_blockchain.fill_tx_rpc_details(trei, tx_entry.tx, nullptr, h, tx_entry.receive_time, true); return true; }); @@ -568,7 +568,7 @@ namespace currency txs.push_back(tx_rpc_extended_info()); tx_rpc_extended_info& trei = txs.back(); trei.blob_size = ptei->blob_size; - fill_tx_rpc_details(trei, ptei->tx, nullptr, id, ptei->receive_time, false); + m_blockchain.fill_tx_rpc_details(trei, ptei->tx, nullptr, id, ptei->receive_time, false); } return true; } @@ -605,7 +605,7 @@ namespace currency if (!ptei) return false; - fill_tx_rpc_details(trei, ptei->tx, nullptr, id, ptei->receive_time, false); + m_blockchain.fill_tx_rpc_details(trei, ptei->tx, nullptr, id, ptei->receive_time, false); return true; } //--------------------------------------------------------------------------------- diff --git a/src/gui/qt-daemon/application/mainwindow.cpp b/src/gui/qt-daemon/application/mainwindow.cpp index c4f44b7a..b61b4cce 100644 --- a/src/gui/qt-daemon/application/mainwindow.cpp +++ b/src/gui/qt-daemon/application/mainwindow.cpp @@ -18,6 +18,7 @@ #define PREPARE_ARG_FROM_JSON(arg_type, var_name) \ arg_type var_name = AUTO_VAL_INIT(var_name); \ view::api_response default_ar = AUTO_VAL_INIT(default_ar); \ + LOG_PRINT_BLUE("[REQUEST]: " << param.toStdString(), LOG_LEVEL_3); \ if (!epee::serialization::load_t_from_json(var_name, param.toStdString())) \ { \ default_ar.error_code = API_RETURN_CODE_BAD_ARG; \ @@ -28,6 +29,7 @@ template QString make_response(const T& r) { std::string str = epee::serialization::store_t_to_json(r); + LOG_PRINT_BLUE("[RESPONSE]: " << str, LOG_LEVEL_3); return str.c_str(); } @@ -549,6 +551,14 @@ void MainWindow::store_pos(bool consider_showed) void MainWindow::restore_pos(bool consider_showed) { TRY_ENTRY(); + if (consider_showed) + { + if (m_config.is_showed) + this->showNormal(); + else + this->showMinimized(); + } + if (m_config.is_maximazed) { this->setWindowState(windowState() | Qt::WindowMaximized); @@ -580,14 +590,6 @@ void MainWindow::restore_pos(bool consider_showed) } } - if (consider_showed) - { - if (m_config.is_showed) - this->showNormal(); - else - this->showMinimized(); - } - CATCH_ENTRY2(void()); } void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) @@ -2160,7 +2162,39 @@ QString MainWindow::get_wallet_info(const QString& param) return MAKE_RESPONSE(ar); CATCH_ENTRY_FAIL_API_RESPONCE(); } +QString MainWindow::create_ionic_swap_proposal(const QString& param) +{ + TRY_ENTRY(); + LOG_API_TIMING(); + PREPARE_ARG_FROM_JSON(view::create_ionic_swap_proposal_request, cispr); + PREPARE_RESPONSE(std::string, ar); + ar.error_code = m_backend.create_ionic_swap_proposal(cispr.wallet_id, cispr, ar.response_data); + return MAKE_RESPONSE(ar); + CATCH_ENTRY_FAIL_API_RESPONCE(); +} +QString MainWindow::get_ionic_swap_proposal_info(const QString& param) +{ + TRY_ENTRY(); + LOG_API_TIMING(); + PREPARE_ARG_FROM_JSON(view::api_request_t, tx_raw_hex); + PREPARE_RESPONSE(tools::wallet_public::ionic_swap_proposal_info, ar); + ar.error_code = m_backend.get_ionic_swap_proposal_info(tx_raw_hex.wallet_id, tx_raw_hex.req_data, ar.response_data); + return MAKE_RESPONSE(ar); + CATCH_ENTRY_FAIL_API_RESPONCE(); + +} + +QString MainWindow::accept_ionic_swap_proposal(const QString& param) +{ + TRY_ENTRY(); + LOG_API_TIMING(); + PREPARE_ARG_FROM_JSON(view::api_request_t, tx_raw_hex); + PREPARE_RESPONSE(std::string, ar); + ar.error_code = m_backend.accept_ionic_swap_proposal(tx_raw_hex.wallet_id, tx_raw_hex.req_data, ar.response_data); + return MAKE_RESPONSE(ar); + CATCH_ENTRY_FAIL_API_RESPONCE(); +} QString MainWindow::backup_wallet_keys(const QString& param) { TRY_ENTRY(); diff --git a/src/gui/qt-daemon/application/mainwindow.h b/src/gui/qt-daemon/application/mainwindow.h index 37f1dc92..45306094 100644 --- a/src/gui/qt-daemon/application/mainwindow.h +++ b/src/gui/qt-daemon/application/mainwindow.h @@ -172,6 +172,9 @@ public: QString remove_custom_asset_id(const QString& param); QString get_wallet_info(const QString& param); + QString create_ionic_swap_proposal(const QString& param); + QString get_ionic_swap_proposal_info(const QString& param); + QString accept_ionic_swap_proposal(const QString& param); bool get_is_disabled_notifications(); bool set_is_disabled_notifications(const bool& param); diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 5b5383b1..d2e8d029 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -63,8 +63,8 @@ inline bool do_serialize(Archive &ar, T &v) #define BEGIN_SERIALIZE() \ template class Archive> bool do_serialize(Archive &_ser_ar) {uint8_t s_current_version ATTRIBUTE_UNUSED = 0; uint8_t s_version ATTRIBUTE_UNUSED = 0; #define BEGIN_SERIALIZE_OBJECT() \ - template class Archive> bool do_serialize(Archive &_ser_ar) { _ser_ar.begin_object(); bool _ser_res = do_serialize_object(_ser_ar); _ser_ar.end_object(); return _ser_res; } \ - template class Archive> bool do_serialize_object(Archive &_ser_ar){ + template class Archive> bool do_serialize(Archive &_ser_ar) {_ser_ar.begin_object(); bool _ser_res = do_serialize_object(_ser_ar); _ser_ar.end_object(); return _ser_res; } \ + template class Archive> bool do_serialize_object(Archive &_ser_ar){ uint8_t s_current_version ATTRIBUTE_UNUSED = 0; uint8_t s_version ATTRIBUTE_UNUSED = 0; #define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) ::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive::is_saving()) #define END_SERIALIZE() return true;} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index eea0bad4..d23a3ea1 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4,6 +4,10 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#if defined(WIN32) + #include +#endif #include #include #include @@ -22,11 +26,10 @@ #include "string_coding.h" #include "wallet/wrap_service.h" #include "common/general_purpose_commands_defs.h" -#include -#if defined(WIN32) -#include -#endif +#include "wallet/wallet_helpers.h" + + using namespace std; using namespace epee; @@ -229,6 +232,10 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("add_custom_asset_id", boost::bind(&simple_wallet::add_custom_asset_id, this, ph::_1), "Approve asset id to be recognized in the wallet and returned in balances"); m_cmd_binder.set_handler("remove_custom_asset_id", boost::bind(&simple_wallet::remove_custom_asset_id, this, ph::_1), "Cancel previously made approval for asset id"); + m_cmd_binder.set_handler("generate_ionic_swap_proposal", boost::bind(&simple_wallet::generate_ionic_swap_proposal, this, _1), "generate_ionic_swap_proposal - Generates ionic_swap proposal with given conditions"); + m_cmd_binder.set_handler("get_ionic_swap_proposal_info", boost::bind(&simple_wallet::get_ionic_swap_proposal_info, this, _1), "get_ionic_swap_proposal_info - Extracts and display information from ionic_swap proposal raw data"); + m_cmd_binder.set_handler("accept_ionic_swap_proposal", boost::bind(&simple_wallet::accept_ionic_swap_proposal, this, _1), "accept_ionic_swap_proposal - Accept ionic_swap proposal and generates exchange transaction"); + } //---------------------------------------------------------------------------------------------------- simple_wallet::~simple_wallet() @@ -691,6 +698,17 @@ void simple_wallet::on_tor_status_change(const std::string& state) message_writer(epee::log_space::console_color_yellow, true, std::string("[TOR]: ")) << human_message; } //---------------------------------------------------------------------------------------------------- +void simple_wallet::on_mw_get_wallets(std::vector& wallets) +{ + wallets.resize(1); + tools::get_wallet_info(*m_wallet, wallets[0].wi); +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::on_mw_select_wallet(uint64_t wallet_id) +{ + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::refresh(const std::vector& args) { if (m_offline_mode) @@ -1801,7 +1819,7 @@ bool simple_wallet::deploy_new_asset(const std::vector &args) tx_destination_entry td = AUTO_VAL_INIT(td); td.addr.push_back(m_wallet->get_account().get_public_address()); td.amount = adb.current_supply; - td.asset_id = currency::ffff_pkey; + td.asset_id = currency::null_pkey; std::vector destinations; destinations.push_back(td); currency::transaction result_tx = AUTO_VAL_INIT(result_tx); @@ -1849,6 +1867,104 @@ bool simple_wallet::add_custom_asset_id(const std::vector &args) } return true; } +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::generate_ionic_swap_proposal(const std::vector &args) +{ + + if (args.size() != 2) + { + fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1"; + } + + view::ionic_swap_proposal_info proposal = AUTO_VAL_INIT(proposal); + bool r = epee::serialization::load_t_from_json_file(proposal, args[0]); + if (!r) + { + fail_msg_writer() << "Failed to load json file with asset specification: " << args[0]; + } + currency::account_public_address destination_addr = AUTO_VAL_INIT(destination_addr); + currency::payment_id_t integrated_payment_id; + if (!m_wallet->get_transfer_address(args[1], destination_addr, integrated_payment_id)) + { + fail_msg_writer() << "wrong address: " << args[1]; + return true; + } + if (integrated_payment_id.size()) + { + fail_msg_writer() << "Integrated addresses not supported yet"; + return true; + } + + transaction tx_template = AUTO_VAL_INIT(tx_template); + r = m_wallet->create_ionic_swap_proposal(proposal, destination_addr, tx_template); + if (!r) + { + fail_msg_writer() << "Failed to create ionic_swap proposal"; + return true; + } + else + { + success_msg_writer() << "Generated proposal: " << ENDL << epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(tx_template)); + } + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::get_ionic_swap_proposal_info(const std::vector &args) +{ + if (args.size() != 1) + { + fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1"; + } + + std::string raw_tx_template; + bool r = epee::string_tools::parse_hexstr_to_binbuff(args[0], raw_tx_template); + if (!r) + { + fail_msg_writer() << "Failed to parse proposal hex to raw data"; + return true; + } + + view::ionic_swap_proposal_info proposal = AUTO_VAL_INIT(proposal); + if (!m_wallet->get_ionic_swap_proposal_info(raw_tx_template, proposal)) + { + fail_msg_writer() << "Failed to decode proposal info"; + return true; + } + std::string json_proposal = epee::serialization::store_t_to_json(proposal); + + + success_msg_writer() << "Proposal details: " << ENDL << json_proposal; + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::accept_ionic_swap_proposal(const std::vector &args) +{ + if (args.size() != 1) + { + fail_msg_writer() << "invalid arguments count: " << args.size() << ", expected 1"; + } + + std::string raw_tx_template; + bool r = epee::string_tools::parse_hexstr_to_binbuff(args[0], raw_tx_template); + if (!r) + { + fail_msg_writer() << "Failed to parse proposal hex to raw data"; + return true; + } + + currency::transaction result_tx = AUTO_VAL_INIT(result_tx); + if (!m_wallet->accept_ionic_swap_proposal(raw_tx_template, result_tx)) + { + fail_msg_writer() << "Failed accept ionic_swap proposal"; + return true; + } + + success_msg_writer() << "Proposal accepted and executed, tx_id : " << currency::get_transaction_hash(result_tx); + + return true; +} + //---------------------------------------------------------------------------------------------------- bool simple_wallet::remove_custom_asset_id(const std::vector &args) { @@ -2239,7 +2355,8 @@ int main(int argc, char* argv[]) if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - tools::wallet2 wal; + std::shared_ptr wallet_ptr(new tools::wallet2()); + tools::wallet2& wal = *wallet_ptr; //try to open it while (true) { @@ -2295,7 +2412,7 @@ int main(int argc, char* argv[]) } } - tools::wallet_rpc_server wrpc(wal); + tools::wallet_rpc_server wrpc(wallet_ptr); bool r = wrpc.init(vm); CHECK_AND_ASSERT_MES(r, EXIT_FAILURE, "Failed to initialize wallet rpc server"); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 823fee05..01d637e4 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -92,6 +92,11 @@ namespace currency bool add_custom_asset_id(const std::vector &args); bool remove_custom_asset_id(const std::vector &args); + //---------------------------------------------------------------------------------------------------- + bool generate_ionic_swap_proposal(const std::vector &args); + bool get_ionic_swap_proposal_info(const std::vector &args); + bool accept_ionic_swap_proposal(const std::vector &args); + bool validate_wrap_status(uint64_t amount); bool get_alias_from_daemon(const std::string& alias_name, currency::extra_alias_entry_base& ai); @@ -106,6 +111,8 @@ namespace currency virtual void on_message(i_wallet2_callback::message_severity severity, const std::string& m) override; virtual void on_tor_status_change(const std::string& state) override; + virtual void on_mw_get_wallets(std::vector& wallets) override; + virtual bool on_mw_select_wallet(uint64_t wallet_id) override; //---------------------------------------------------------- friend class refresh_progress_reporter_t; @@ -178,7 +185,7 @@ namespace currency epee::console_handlers_binder m_cmd_binder; - std::unique_ptr m_wallet; + std::shared_ptr m_wallet; net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; }; diff --git a/src/wallet/view_iface.h b/src/wallet/view_iface.h index 9c4a1ef9..ebce8c5e 100644 --- a/src/wallet/view_iface.h +++ b/src/wallet/view_iface.h @@ -209,40 +209,9 @@ public: END_KV_SERIALIZE_MAP() }; - struct wallet_info - { - std::list balances; - uint64_t mined_total; - std::string address; - std::string view_sec_key; - std::string path; - bool is_auditable; - bool is_watch_only; + typedef tools::wallet_public::wallet_info wallet_info; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(balances) - KV_SERIALIZE(mined_total) - KV_SERIALIZE(address) - KV_SERIALIZE(view_sec_key) - KV_SERIALIZE(path) - KV_SERIALIZE(is_auditable); - KV_SERIALIZE(is_watch_only); - END_KV_SERIALIZE_MAP() - }; - - - - struct wallet_entry_info - { - wallet_info wi; - uint64_t wallet_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(wi) - KV_SERIALIZE(wallet_id) - END_KV_SERIALIZE_MAP() - - }; + typedef tools::wallet_public::wallet_entry_info wallet_entry_info; @@ -440,7 +409,7 @@ public: struct wallet_and_asset_id { uint64_t wallet_id = 0; - crypto::hash asset_id = currency::null_hash; + crypto::public_key asset_id = currency::null_pkey; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(wallet_id) @@ -686,14 +655,11 @@ public: }; - - struct wallet_and_contract_id_param { uint64_t wallet_id; crypto::hash contract_id; - BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(wallet_id) KV_SERIALIZE_POD_AS_HEX_STRING(contract_id) @@ -742,6 +708,11 @@ public: END_KV_SERIALIZE_MAP() }; + + typedef tools::wallet_public::asset_funds asset_funds; + typedef tools::wallet_public::ionic_swap_proposal_info ionic_swap_proposal_info; + typedef tools::wallet_public::create_ionic_swap_proposal_request create_ionic_swap_proposal_request; + struct address_validation_response { std::string error_code; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c5f18243..5da66730 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -42,6 +42,8 @@ using namespace epee; using namespace currency; +#define SET_CONTEXT_OBJ_FOR_SCOPE(name, obj) m_current_context.name = &obj; \ + auto COMBINE(auto_scope_var_, __LINE__) = epee::misc_utils::create_scope_leave_handler([&]() { m_current_context.name = nullptr; }); #define MINIMUM_REQUIRED_WALLET_FREE_SPACE_BYTES (100*1024*1024) // 100 MB @@ -934,7 +936,9 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept construct_tx_param construct_param = AUTO_VAL_INIT(construct_param); construct_param.fee = b_acceptance_fee; - currency::transaction tx = contr_it->second.proposal.tx_template; + mode_separate_context msc = AUTO_VAL_INIT(msc); + msc.tx_for_mode_separate = contr_it->second.proposal.tx_template; + currency::transaction& tx = msc.tx_for_mode_separate; crypto::secret_key one_time_key = contr_it->second.proposal.tx_onetime_secret_key; construct_param.crypt_address = m_account.get_public_address(); construct_param.flags = TX_FLAG_SIGNATURE_MODE_SEPARATE; @@ -992,7 +996,7 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept //build transaction currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); ftp.tx_version = this->get_current_tx_version(); - prepare_transaction(construct_param, ftp, tx); + prepare_transaction(construct_param, ftp, msc); mark_transfers_as_spent(ftp.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been accepted with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(tx)) + ">"); try @@ -4739,7 +4743,7 @@ void wallet2::build_escrow_template(const bc_services::contract_private_details& currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); ftp.tx_version = this->get_current_tx_version(); - prepare_transaction(ctp, ftp, tx); + prepare_transaction(ctp, ftp); selected_transfers = ftp.selected_transfers; @@ -4991,6 +4995,337 @@ bool wallet2::check_htlc_redeemed(const crypto::hash& htlc_tx_id, std::string& o return false; } //---------------------------------------------------------------------------------------------------- +bool wallet2::create_ionic_swap_proposal(const wallet_public::ionic_swap_proposal_info& proposal_details, const currency::account_public_address& destination_addr, wallet_public::ionic_swap_proposal& proposal) +{ + std::vector selected_transfers_for_template; + + build_ionic_swap_template(proposal_details, destination_addr, proposal, selected_transfers_for_template); + + const uint32_t mask_to_mark_escrow_template_locked_transfers = WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION; + mark_transfers_with_flag(selected_transfers_for_template, mask_to_mark_escrow_template_locked_transfers, "preparing ionic_swap"); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::build_ionic_swap_template(const wallet_public::ionic_swap_proposal_info& proposal_detais, const currency::account_public_address& destination_addr, + wallet_public::ionic_swap_proposal& proposal, + std::vector& selected_transfers) +{ + construct_tx_param ctp = get_default_construct_tx_param(); + + ctp.fake_outputs_count = proposal_detais.mixins; + ctp.fee = proposal_detais.fee_paid_by_a; + ctp.flags = TX_FLAG_SIGNATURE_MODE_SEPARATE; + ctp.mark_tx_as_complete = false; + ctp.crypt_address = destination_addr; + + etc_tx_details_expiration_time t = AUTO_VAL_INIT(t); + t.v = proposal_detais.expiration_time; + ctp.extra.push_back(t); + + ctp.dsts.resize(proposal_detais.to_bob.size() + proposal_detais.to_alice.size()); + size_t i = 0; + // Here is an proposed for exchange funds + for (; i != proposal_detais.to_bob.size(); i++) + { + ctp.dsts[i].amount = proposal_detais.to_bob[i].amount; + ctp.dsts[i].amount_to_provide = proposal_detais.to_bob[i].amount; + ctp.dsts[i].flags |= tx_destination_entry_flags::tdef_explicit_amount_to_provide; + ctp.dsts[i].addr.push_back(destination_addr); + ctp.dsts[i].asset_id = proposal_detais.to_bob[i].asset_id; + } + // Here is an expected in return funds + for (size_t j = 0; j != proposal_detais.to_alice.size(); j++, i++) + { + ctp.dsts[i].amount = proposal_detais.to_alice[j].amount; + ctp.dsts[i].amount_to_provide = 0; + ctp.dsts[i].flags |= tx_destination_entry_flags::tdef_explicit_amount_to_provide; + ctp.dsts[i].addr.push_back(m_account.get_public_address()); + ctp.dsts[i].asset_id = proposal_detais.to_alice[j].asset_id; + } + + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + ftp.mode_separate_fee = ctp.fee; + ftp.tx_version = this->get_current_tx_version(); + prepare_transaction(ctp, ftp); + + selected_transfers = ftp.selected_transfers; + currency::finalized_tx finalize_result = AUTO_VAL_INIT(finalize_result); + finalize_transaction(ftp, finalize_result, false); + add_transfers_to_expiration_list(selected_transfers, proposal_detais.expiration_time, 0, currency::null_hash); + + //wrap it all + proposal.tx_template = finalize_result.tx; + wallet_public::ionic_swap_proposal_context ispc = AUTO_VAL_INIT(ispc); + ispc.gen_context = finalize_result.ftp.gen_context; + ispc.one_time_skey = finalize_result.one_time_key; + std::string proposal_context_blob = t_serializable_object_to_blob(ispc); + proposal.encrypted_context = crypto::chacha_crypt(static_cast(proposal_context_blob), finalize_result.derivation); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::get_ionic_swap_proposal_info(const std::string&raw_proposal, wallet_public::ionic_swap_proposal_info& proposal_info) +{ + wallet_public::ionic_swap_proposal proposal = AUTO_VAL_INIT(proposal); + bool r = t_unserializable_object_from_blob(proposal, raw_proposal); + THROW_IF_TRUE_WALLET_EX(!r, error::wallet_internal_error, "Failed to parse proposal"); + return get_ionic_swap_proposal_info(proposal, proposal_info); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::get_ionic_swap_proposal_info(const wallet_public::ionic_swap_proposal& proposal, wallet_public::ionic_swap_proposal_info& proposal_info) +{ + wallet_public::ionic_swap_proposal_context ionic_context = AUTO_VAL_INIT(ionic_context); + return get_ionic_swap_proposal_info(proposal, proposal_info, ionic_context); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::get_ionic_swap_proposal_info(const wallet_public::ionic_swap_proposal& proposal, wallet_public::ionic_swap_proposal_info& proposal_info, wallet_public::ionic_swap_proposal_context& ionic_context) +{ + const transaction& tx = proposal.tx_template; + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); + std::vector outs; + uint64_t tx_money_got_in_outs = 0; + bool r = lookup_acc_outs(m_account.get_keys(), tx, outs, tx_money_got_in_outs, derivation); + THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "Failed to lookup_acc_outs for tx: " << get_transaction_hash(tx)); + + if (!outs.size()) + { + return false; + } + + //decrypt context + std::string decrypted_raw_context = crypto::chacha_crypt(proposal.encrypted_context, derivation); + r = t_unserializable_object_from_blob(ionic_context, decrypted_raw_context); + THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "Failed to unserialize decrypted ionic_context"); + + r = validate_tx_output_details_againt_tx_generation_context(tx, ionic_context.gen_context, ionic_context.one_time_skey); + THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "Failed to validate decrypted ionic_context"); + + std::unordered_map amounts_provided_by_a; + + std::unordered_map ammounts_to_a; //amounts to Alice (the one who created proposal), should be NOT funded + std::unordered_map ammounts_to_b; //amounts to Bob (the one who received proposal), should BE funded + std::vector bob_outs; + bob_outs.resize(proposal.tx_template.vout.size()); + + for (const auto& o : outs) + { + THROW_IF_FALSE_WALLET_INT_ERR_EX(ionic_context.gen_context.asset_ids.size() > o.index, "Tx gen context has mismatch with tx(asset_ids) "); + THROW_IF_FALSE_WALLET_INT_ERR_EX(ionic_context.gen_context.asset_ids[o.index].to_public_key() == o.asset_id, "Tx gen context has mismatch with tx(asset_id != asset_id) "); + THROW_IF_FALSE_WALLET_INT_ERR_EX(ionic_context.gen_context.amounts[o.index].m_u64[0] == o.amount, "Tx gen context has mismatch with tx(amount != amount)"); + + ammounts_to_b[o.asset_id] += o.amount; + bob_outs[o.index] = true; + } + size_t i = 0; + //validate outputs against decrypted tx generation context + for (i = 0; i != tx.vout.size(); i++) + { + + if (bob_outs[i]) + { + continue; + } + + crypto::public_key asset_id = ionic_context.gen_context.asset_ids[i].to_public_key(); + uint64_t amount = ionic_context.gen_context.amounts[i].m_u64[0]; + ammounts_to_a[asset_id] += amount; + } + + //read amounts already provided by third party + size_t zc_current_index = 0; //some inputs might be old ones, so it's asset id assumed as native and there is no entry for it in real_zc_ins_asset_ids + //THROW_IF_FALSE_WALLET_INT_ERR_EX(ionic_context.gen_context.input_amounts.size() == tx.vin.size(), "Tx gen context has mismatch with tx(amount != amount)"); + for (i = 0; i != tx.vin.size(); i++) + { + size_t mx = 0; + uint64_t amount = 0; + crypto::public_key in_asset_id = currency::native_coin_asset_id; + if (tx.vin[i].type() == typeid(txin_zc_input)) + { + in_asset_id = ionic_context.gen_context.real_zc_ins_asset_ids[zc_current_index].to_public_key(); + amount = ionic_context.gen_context.zc_input_amounts[zc_current_index]; + zc_current_index++; + mx = boost::get(tx.vin[i]).key_offsets.size() - 1; + } + else if (tx.vin[i].type() == typeid(txin_to_key)) + { + amount = boost::get(tx.vin[i]).amount; + mx = boost::get(tx.vin[i]).key_offsets.size() - 1; + } + else + { + WLT_LOG_RED("Unexpected type of input in ionic_swap tx: " << tx.vin[i].type().name(), LOG_LEVEL_0); + return false; + } + amounts_provided_by_a[in_asset_id] += amount; + + if (proposal_info.mixins == 0 || proposal_info.mixins > mx) + { + proposal_info.mixins = mx; + } + + } + + //this might be 0, if Alice don't want to pay fee herself + proposal_info.fee_paid_by_a = currency::get_tx_fee(tx); + if (proposal_info.fee_paid_by_a) + { + THROW_IF_FALSE_WALLET_INT_ERR_EX(amounts_provided_by_a[currency::native_coin_asset_id] >= proposal_info.fee_paid_by_a, "Fee mentioned as specified but not provided by A"); + amounts_provided_by_a[currency::native_coin_asset_id] -= proposal_info.fee_paid_by_a; + } + + //proposal_info.fee = currency::get_tx_fee(tx); + //need to make sure that funds for Bob properly funded + for (const auto& a : ammounts_to_b) + { + uint64_t amount_sent_back_to_alice = ammounts_to_a[a.first]; + + if (amounts_provided_by_a[a.first] < (a.second + amount_sent_back_to_alice) ) + { + WLT_LOG_RED("Amount[" << a.first << "] provided by Alice(" << amounts_provided_by_a[a.first] << ") is less then transfered to Bob(" << a.second <<")", LOG_LEVEL_0); + return false; + } + amounts_provided_by_a[a.first] -= (amount_sent_back_to_alice + a.second); + proposal_info.to_bob.push_back(view::asset_funds{ a.first, a.second }); + //clean accounted assets + ammounts_to_a.erase(ammounts_to_a.find(a.first)); + if (amounts_provided_by_a[a.first] > 0) + { + WLT_LOG_RED("Amount[" << a.first << "] provided by Alice has unused leftovers: " << amounts_provided_by_a[a.first], LOG_LEVEL_0); + return false; + } + } + + //need to see what Alice actually expect in return + for (const auto& a : ammounts_to_a) + { + //now amount provided by A should be less or equal to what we have in a.second + if (amounts_provided_by_a[a.first] > a.second) + { + //could be fee + WLT_LOG_RED("Amount[" << a.first << "] provided by Alice has unused leftovers: " << amounts_provided_by_a[a.first], LOG_LEVEL_0); + return false; + } + + proposal_info.to_alice.push_back(view::asset_funds{ a.first, a.second - amounts_provided_by_a[a.first] }); + } + + etc_tx_details_expiration_time t = AUTO_VAL_INIT(t); + if (!get_type_in_variant_container(tx.extra, t)) + { + return false; + } + + proposal_info.expiration_time = t.v; + return true; +} + +//---------------------------------------------------------------------------------------------------- +bool wallet2::accept_ionic_swap_proposal(const std::string& raw_proposal, currency::transaction& result_tx) +{ + wallet_public::ionic_swap_proposal proposal = AUTO_VAL_INIT(proposal); + bool r = t_unserializable_object_from_blob(proposal, raw_proposal); + THROW_IF_TRUE_WALLET_EX(!r, error::wallet_internal_error, "Failed to parse proposal info"); + + return accept_ionic_swap_proposal(proposal, result_tx); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::accept_ionic_swap_proposal(const wallet_public::ionic_swap_proposal& proposal, currency::transaction& result_tx) +{ + mode_separate_context msc = AUTO_VAL_INIT(msc); + msc.tx_for_mode_separate = proposal.tx_template; + result_tx = msc.tx_for_mode_separate; + + wallet_public::ionic_swap_proposal_context ionic_context = AUTO_VAL_INIT(ionic_context); + bool r = get_ionic_swap_proposal_info(proposal, msc.proposal_info, ionic_context); + THROW_IF_TRUE_WALLET_EX(!r, error::wallet_internal_error, "Failed to get info from proposal"); + + std::unordered_map balances; + uint64_t mined = 0; + this->balance(balances, mined); + //validate balances needed + uint64_t native_amount_required = 0; + for (const auto& item : msc.proposal_info.to_alice) + { + if (balances[item.asset_id].unlocked < item.amount) + { + return false; + } + if (item.asset_id == currency::native_coin_asset_id) + { + native_amount_required = item.amount; + } + } + + // balances is ok, check if fee is added to tx + uint64_t additional_fee = 0; + if (msc.proposal_info.fee_paid_by_a < m_core_runtime_config.tx_default_fee) + { + additional_fee = m_core_runtime_config.tx_default_fee - msc.proposal_info.fee_paid_by_a; + if (balances[currency::native_coin_asset_id].unlocked < additional_fee + native_amount_required) + { + return false; + } + } + + //everything is seemed to be ok + + construct_tx_param construct_param = get_default_construct_tx_param(); + construct_param.fee = additional_fee; + + crypto::secret_key one_time_key = ionic_context.one_time_skey; + construct_param.crypt_address = m_account.get_public_address(); + construct_param.flags = TX_FLAG_SIGNATURE_MODE_SEPARATE; + construct_param.mark_tx_as_complete = true; + + //build transaction + currency::finalize_tx_param ftp = AUTO_VAL_INIT(ftp); + ftp.tx_version = this->get_current_tx_version(); + ftp.gen_context = ionic_context.gen_context; + prepare_transaction(construct_param, ftp, msc); + + + + try + { + finalize_transaction(ftp, result_tx, one_time_key, true); + } + catch (...) + { + clear_transfers_from_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_SPENT, std::string("exception in finalize_transaction, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(result_tx))); + throw; + } + mark_transfers_as_spent(ftp.selected_transfers, std::string("Proposal has been accepted with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(result_tx))) + ">"); + return true; +} +//---------------------------------------------------------------------------------------------------- + +// Signing and auth +bool wallet2::sign_buffer(const std::string& buff, crypto::signature& sig) +{ + crypto::hash h = crypto::cn_fast_hash(buff.data(), buff.size()); + crypto::generate_signature(h, m_account.get_public_address().spend_public_key, m_account.get_keys().spend_secret_key, sig); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::validate_sign(const std::string& buff, const crypto::signature& sig, const crypto::public_key& pkey) +{ + crypto::hash h = crypto::cn_fast_hash(buff.data(), buff.size()); + return crypto::check_signature(h, pkey, sig); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::encrypt_buffer(const std::string& buff, std::string& res_buff) +{ + res_buff = buff; + crypto::chacha_crypt(res_buff, m_account.get_keys().view_secret_key); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::decrypt_buffer(const std::string& buff, std::string& res_buff) +{ + res_buff = buff; + crypto::chacha_crypt(res_buff, m_account.get_keys().view_secret_key); + return true; +} +//---------------------------------------------------------------------------------------------------- bool wallet2::prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector& sources, std::vector& selected_indicies, uint64_t& found_money) { prepare_free_transfers_cache(fake_outputs_count); @@ -5271,16 +5606,21 @@ assets_selection_context wallet2::get_needed_money(uint64_t fee, const std::vect amounts_map[currency::native_coin_asset_id].needed_amount = fee; for(auto& dt : dsts) { - if(dt.asset_id == currency::ffff_pkey) + if(dt.asset_id == currency::null_pkey) continue; //this destination for emmition only THROW_IF_TRUE_WALLET_EX(0 == dt.amount, error::zero_destination); uint64_t money_to_add = dt.amount; - if (dt.amount_to_provide) + if (dt.amount_to_provide || dt.flags & tx_destination_entry_flags::tdef_explicit_amount_to_provide) money_to_add = dt.amount_to_provide; amounts_map[dt.asset_id].needed_amount += money_to_add; THROW_IF_TRUE_WALLET_EX(amounts_map[dt.asset_id].needed_amount < money_to_add, error::tx_sum_overflow, dsts, fee); + //clean up empty entries + if (amounts_map[dt.asset_id].needed_amount == 0) + { + amounts_map.erase(amounts_map.find(dt.asset_id)); + } } return std::move(amounts_map); } @@ -5559,6 +5899,38 @@ bool wallet2::get_actual_offers(std::list& offers return true; } //---------------------------------------------------------------------------------------------------- +bool wallet2::expand_selection_with_zc_input(assets_selection_context& needed_money_map, uint64_t fake_outputs_count, std::vector& selected_indexes) +{ + + free_amounts_cache_type& found_free_amounts = m_found_free_amounts[currency::native_coin_asset_id]; + auto& asset_needed_money_item = needed_money_map[currency::native_coin_asset_id]; + //need to add ZC input + for (auto it = found_free_amounts.begin(); it != found_free_amounts.end(); it++) + { + for (auto it_in_amount = it->second.begin(); it_in_amount != it->second.end(); it_in_amount++) + { + if (!m_transfers[*it_in_amount].is_zc()) + { + continue; + } + + if (is_transfer_ready_to_go(m_transfers[*it->second.begin()], fake_outputs_count)) + { + asset_needed_money_item.found_amount += it->first; + selected_indexes.push_back(*it_in_amount); + it->second.erase(it_in_amount); + if (!it->second.size()) + { + found_free_amounts.erase(it); + } + return true; + } + } + } + WLT_THROW_IF_FALSE_WALLET_EX_MES(false , error::no_zc_inputs, "Missing ZC inputs for TX_FLAG_SIGNATURE_MODE_SEPARATE operation"); + return true; +} +//---------------------------------------------------------------------------------------------------- bool wallet2::select_indices_for_transfer(assets_selection_context& needed_money_map, uint64_t fake_outputs_count, std::vector& selected_indexes) { bool res = true; @@ -5570,6 +5942,24 @@ bool wallet2::select_indices_for_transfer(assets_selection_context& needed_money item.second.found_amount = select_indices_for_transfer(selected_indexes, asset_cashe_it->second, item.second.needed_amount, fake_outputs_count); WLT_THROW_IF_FALSE_WALLET_EX_MES(item.second.found_amount >= item.second.needed_amount, error::not_enough_money, "", item.second.found_amount, item.second.needed_amount, 0, item.first); } + if (m_current_context.pconstruct_tx_param && m_current_context.pconstruct_tx_param->need_at_least_1_zc) + { + bool found_zc_input = false; + for (auto i : selected_indexes) + { + if (m_transfers[i].is_zc()) + { + found_zc_input = true; + break; + } + } + if (!found_zc_input) + { + expand_selection_with_zc_input(needed_money_map, fake_outputs_count, selected_indexes); + } + } + + return res; } //---------------------------------------------------------------------------------------------------- @@ -5577,6 +5967,7 @@ uint64_t wallet2::select_indices_for_transfer(std::vector& selected_in { WLT_LOG_GREEN("Selecting indices for transfer of " << print_money_brief(needed_money) << " with " << fake_outputs_count << " fake outs, found_free_amounts.size()=" << found_free_amounts.size() << "...", LOG_LEVEL_0); uint64_t found_money = 0; + //uint64_t found_zc_input = false; std::string selected_amounts_str; while (found_money < needed_money && found_free_amounts.size()) { @@ -5598,6 +5989,7 @@ uint64_t wallet2::select_indices_for_transfer(std::vector& selected_in found_free_amounts.erase(it); } + WLT_LOG_GREEN("Found " << print_money_brief(found_money) << " as " << selected_indexes.size() << " out(s): " << selected_amounts_str << ", found_free_amounts.size()=" << found_free_amounts.size(), LOG_LEVEL_0); return found_money; } @@ -5721,11 +6113,16 @@ bool wallet2::read_money_transfer2_details_from_tx(const transaction& tx, const else if (i.type() == typeid(currency::txin_zc_input)) { const currency::txin_zc_input& in_zc = boost::get(i); - auto it = m_key_images.find(in_zc.k_image); //should we panic if image not found? - WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_key_images.end(), "[read_money_transfer2_details_from_tx]Unknown key image in tx: " << get_transaction_hash(tx)); - WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->second < m_transfers.size(), "[read_money_transfer2_details_from_tx]Index out of range for key image in tx: " << get_transaction_hash(tx)); - wtd.spn.push_back(m_transfers[it->second].amount()); + //@zoidberg: nope! + if (m_key_images.count(in_zc.k_image)) + { + auto it = m_key_images.find(in_zc.k_image); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_key_images.end(), "[read_money_transfer2_details_from_tx]Unknown key image in tx: " << get_transaction_hash(tx)); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->second < m_transfers.size(), "[read_money_transfer2_details_from_tx]Index out of range for key image in tx: " << get_transaction_hash(tx)); + wtd.spn.push_back(m_transfers[it->second].amount()); + } + } } return true; @@ -5917,7 +6314,7 @@ void wallet2::prepare_tx_destinations(const assets_selection_context& needed_mon { // special case for asset minting destinations for (auto& dst : dsts) - if (dst.asset_id == currency::ffff_pkey) + if (dst.asset_id == currency::null_pkey) final_destinations.emplace_back(dst.amount, dst.addr, dst.asset_id); // if there's not ehough destinations items (i.e. outputs), split the last one @@ -5976,20 +6373,29 @@ void wallet2::prepare_tx_destinations(uint64_t needed_money, } } //---------------------------------------------------------------------------------------------------- -void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate /* = currency::transaction() */) +void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const mode_separate_context& mode_separatemode_separate) { + + SET_CONTEXT_OBJ_FOR_SCOPE(pconstruct_tx_param, ctp); + SET_CONTEXT_OBJ_FOR_SCOPE(pfinalize_tx_param, ftp); + SET_CONTEXT_OBJ_FOR_SCOPE(pmode_separate_context, mode_separatemode_separate); + TIME_MEASURE_START_MS(get_needed_money_time); + const currency::transaction& tx_for_mode_separate = mode_separatemode_separate.tx_for_mode_separate; assets_selection_context needed_money_map = get_needed_money(ctp.fee, ctp.dsts); // // TODO @#@# need to do refactoring over this part to support hidden amounts and asset_id // - if (ctp.flags & TX_FLAG_SIGNATURE_MODE_SEPARATE && tx_for_mode_separate.vout.size() ) { WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(get_tx_flags(tx_for_mode_separate) & TX_FLAG_SIGNATURE_MODE_SEPARATE, "tx_param.flags differs from tx.flags"); - needed_money_map[currency::null_pkey].needed_amount += (currency::get_outs_money_amount(tx_for_mode_separate) - get_inputs_money_amount(tx_for_mode_separate)); + for (const auto& el : mode_separatemode_separate.proposal_info.to_alice) + { + needed_money_map[el.asset_id].needed_amount += el.amount; + } + ctp.need_at_least_1_zc = true; } TIME_MEASURE_FINISH_MS(get_needed_money_time); @@ -6004,15 +6410,15 @@ void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx { //htlc //@#@ need to do refactoring over this part to support hidden amounts and asset_id - prepare_tx_sources_htlc(ctp.htlc_tx_id, ctp.htlc_origin, ftp.sources, needed_money_map[currency::null_pkey].found_amount); + prepare_tx_sources_htlc(ctp.htlc_tx_id, ctp.htlc_origin, ftp.sources, needed_money_map[currency::native_coin_asset_id].found_amount); WLT_THROW_IF_FALSE_WITH_CODE(ctp.dsts.size() == 1, "htlc: unexpected ctp.dsts.size() =" << ctp.dsts.size(), API_RETURN_CODE_INTERNAL_ERROR); - WLT_THROW_IF_FALSE_WITH_CODE(needed_money_map[currency::null_pkey].found_amount > ctp.fee, + WLT_THROW_IF_FALSE_WITH_CODE(needed_money_map[currency::native_coin_asset_id].found_amount > ctp.fee, "htlc: found money less then fee", API_RETURN_CODE_INTERNAL_ERROR); //fill amount - ctp.dsts.begin()->amount = needed_money_map[currency::null_pkey].found_amount - ctp.fee; + ctp.dsts.begin()->amount = needed_money_map[currency::native_coin_asset_id].found_amount - ctp.fee; } else if (ctp.multisig_id != currency::null_hash) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index cf1de3c1..b09fd5e8 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -44,6 +44,7 @@ #include "wallet_chain_shortener.h" #include "tor-connect/torlib/tor_lib_iface.h" #include "currency_core/pos_mining.h" +#include "view_iface.h" #define WALLET_DEFAULT_TX_SPENDABLE_AGE 10 @@ -126,6 +127,10 @@ namespace tools virtual void on_transfer_canceled(const wallet_public::wallet_transfer_info& wti) {} virtual void on_message(message_severity /*severity*/, const std::string& /*m*/) {} virtual void on_tor_status_change(const std::string& state) {} + + //mw api + virtual void on_mw_get_wallets(std::vector& wallets) {} + virtual bool on_mw_select_wallet(uint64_t wallet_id) { return true; } }; struct tx_dust_policy @@ -295,8 +300,15 @@ namespace tools uint8_t tx_outs_attr = 0; bool shuffle = false; bool perform_packing = false; + bool need_at_least_1_zc = false; }; + struct mode_separate_context + { + currency::transaction tx_for_mode_separate; + view::ionic_swap_proposal_info proposal_info; + }; + struct selection_for_amount { @@ -458,6 +470,21 @@ namespace tools crypto::hash related_tx_id = currency::null_hash; // tx id which caused money lock, if any (ex: escrow proposal transport tx) }; + /* + This might be not the best solution so far, but after discussion with @sowle we came up to conclusion + that passing great variety of arguments through the long call stack of different member functions of the wallet will + only complicate codebase and make it harder to understand. + current_operation_context will keep pointers to some useful data, and every function that use it, should + make sure(!!!) that pointer got nulled before pointed object got destroyed, likely by using SET_CONTEXT_OBJ_FOR_SCOPE macro + + */ + struct current_operation_context + { + construct_tx_param* pconstruct_tx_param = nullptr; + currency::finalize_tx_param* pfinalize_tx_param = nullptr; + const mode_separate_context* pmode_separate_context = nullptr; + }; + typedef std::unordered_multimap payment_container; @@ -559,6 +586,7 @@ namespace tools //i_wallet2_callback* callback() const { return m_wcallback; } //void callback(i_wallet2_callback* callback) { m_callback = callback; } void callback(std::shared_ptr callback) { m_wcallback = callback; m_do_rise_transfer = (callback != nullptr); } + i_wallet2_callback* get_callback() { return m_wcallback.get(); } void set_do_rise_transfer(bool do_rise) { m_do_rise_transfer = do_rise; } bool has_related_alias_entry_unconfirmed(const currency::transaction& tx); @@ -903,7 +931,7 @@ namespace tools const std::list& get_expiration_entries() const { return m_money_expirations; }; bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const; - void prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate = currency::transaction()); + void prepare_transaction(construct_tx_param& ctp, currency::finalize_tx_param& ftp, const mode_separate_context& emode_separate = mode_separate_context()); void finalize_transaction(const currency::finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key = true); void finalize_transaction(const currency::finalize_tx_param& ftp, currency::finalized_tx& result, bool broadcast_tx, bool store_tx_secret_key = true ); @@ -934,13 +962,29 @@ namespace tools void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin, currency::transaction& result_tx); void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin); bool check_htlc_redeemed(const crypto::hash& htlc_tx_id, std::string& origin, crypto::hash& redeem_tx_id); + + // ionic swaps: + bool create_ionic_swap_proposal(const wallet_public::ionic_swap_proposal_info& proposal_details, const currency::account_public_address& destination_addr, wallet_public::ionic_swap_proposal& proposal); + bool build_ionic_swap_template(const wallet_public::ionic_swap_proposal_info& proposal_detais, const currency::account_public_address& destination_addr, + wallet_public::ionic_swap_proposal& proposal, + std::vector& selected_transfers_for_template); + bool get_ionic_swap_proposal_info(const std::string&raw_proposal, wallet_public::ionic_swap_proposal_info& proposal_info); + bool get_ionic_swap_proposal_info(const wallet_public::ionic_swap_proposal& proposal, wallet_public::ionic_swap_proposal_info& proposal_info); + bool get_ionic_swap_proposal_info(const wallet_public::ionic_swap_proposal& proposal, wallet_public::ionic_swap_proposal_info& proposal_info, wallet_public::ionic_swap_proposal_context& ionic_context); + bool accept_ionic_swap_proposal(const std::string& raw_proposal, currency::transaction& result_tx); + bool accept_ionic_swap_proposal(const wallet_public::ionic_swap_proposal& proposal, currency::transaction& result_tx); + + // Signing and auth + bool sign_buffer(const std::string& buff, crypto::signature& sig); + bool validate_sign(const std::string& buff, const crypto::signature& sig, const crypto::public_key& pkey); + bool encrypt_buffer(const std::string& buff, std::string& res_buff); + bool decrypt_buffer(const std::string& buff, std::string& res_buff); + private: // -------- t_transport_state_notifier ------------------------------------------------ virtual void notify_state_change(const std::string& state_code, const std::string& details = std::string()); // ------------------------------------------------------------------------------------ - - void add_transfers_to_expiration_list(const std::vector& selected_transfers, uint64_t expiration, uint64_t change_amount, const crypto::hash& related_tx_id); void remove_transfer_from_expiration_list(uint64_t transfer_index); void load_keys(const std::string& keys_file_name, const std::string& password, uint64_t file_signature, keys_file_data& kf_data); @@ -1081,7 +1125,7 @@ private: bool is_in_hardfork_zone(uint64_t hardfork_index) const; uint8_t out_get_mixin_attr(const currency::tx_out_v& out_t); const crypto::public_key& out_get_pub_key(const currency::tx_out_v& out_t, std::list& htlc_info_list); - + bool expand_selection_with_zc_input(assets_selection_context& needed_money_map, uint64_t fake_outputs_count, std::vector& selected_indexes); void push_alias_info_to_extra_according_to_hf_status(const currency::extra_alias_entry& ai, std::vector& extra); void remove_transfer_from_amount_gindex_map(uint64_t tid); @@ -1146,6 +1190,8 @@ private: mutable uint64_t m_current_wallet_file_size; bool m_use_deffered_global_outputs; bool m_disable_tor_relay; + + mutable current_operation_context m_current_context; //this needed to access wallets state in coretests, for creating abnormal blocks and tranmsactions friend class test_generator; diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 3d118c92..9e8426c0 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -380,6 +380,17 @@ namespace tools uint64_t m_fee; crypto::public_key m_asset_id; }; + + struct no_zc_inputs : public transfer_error + { + no_zc_inputs(const std::string& /*v*/): transfer_error(std::string(""), API_RETURN_CODE_MISSING_ZC_INPUTS) + {} + + virtual const char* what() const noexcept + { + return API_RETURN_CODE_MISSING_ZC_INPUTS; + } + }; //---------------------------------------------------------------------------------------------------- struct not_enough_outs_to_mix : public transfer_error { diff --git a/src/wallet/wallet_id_adapter.h b/src/wallet/wallet_id_adapter.h index 4a4b0a8c..e31e2fab 100644 --- a/src/wallet/wallet_id_adapter.h +++ b/src/wallet/wallet_id_adapter.h @@ -17,6 +17,10 @@ public: virtual void on_sync_progress(size_t wallet_id, const uint64_t& /*percents*/) {} virtual void on_transfer_canceled(size_t wallet_id, const tools::wallet_public::wallet_transfer_info& wti) {} virtual void on_tor_status_change(size_t wallet_id, const std::string& state) {} + + virtual void on_mw_get_wallets(std::vector& wallets) {} + virtual bool on_mw_select_wallet(uint64_t wallet_id) { return true; } + }; struct i_wallet_to_i_backend_adapter: public tools::i_wallet2_callback @@ -45,6 +49,16 @@ struct i_wallet_to_i_backend_adapter: public tools::i_wallet2_callback m_pbackend->on_tor_status_change(m_wallet_id, state); } + virtual void on_mw_get_wallets(std::vector& wallets) + { + m_pbackend->on_mw_get_wallets(wallets); + } + virtual bool on_mw_select_wallet(uint64_t wallet_id) + { + return m_pbackend->on_mw_select_wallet(wallet_id); + } + + private: i_backend_wallet_callback* m_pbackend; size_t m_wallet_id; diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 05a31f9c..75539c96 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -1179,6 +1179,310 @@ namespace wallet_public }; }; + + + struct asset_funds + { + crypto::public_key asset_id; + uint64_t amount; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) + KV_SERIALIZE(amount) + END_KV_SERIALIZE_MAP() + }; + + + struct ionic_swap_proposal_info + { + std::vector to_bob; //assets that funded by side that making proposal(Alice) and addressed to receiver of proposal (Bob) + std::vector to_alice; //assets expected to be funded by the side that receiving proposal (Bob) and addressed to Alice + uint64_t mixins; + uint64_t fee_paid_by_a; + uint64_t expiration_time; + + BEGIN_KV_SERIALIZE_MAP() + + KV_SERIALIZE(to_bob) + KV_SERIALIZE(to_alice) + KV_SERIALIZE(mixins) + KV_SERIALIZE(fee_paid_by_a) + KV_SERIALIZE(expiration_time) + END_KV_SERIALIZE_MAP() + + }; + + + struct ionic_swap_proposal_context + { + currency::tx_generation_context gen_context; + crypto::secret_key one_time_skey; + + BEGIN_SERIALIZE_OBJECT() + VERSION() + CURRENT_VERSION(0) + FIELD(gen_context) + FIELD(one_time_skey) + END_SERIALIZE() + }; + + struct ionic_swap_proposal + { + currency::transaction tx_template; + std::string encrypted_context; //ionic_swap_proposal_context encrypted with derivation + + + BEGIN_SERIALIZE_OBJECT() + VERSION() + CURRENT_VERSION(0) + FIELD(tx_template) + FIELD(encrypted_context) + END_SERIALIZE() + }; + + struct create_ionic_swap_proposal_request + { + uint64_t wallet_id; + ionic_swap_proposal_info proposal_info; + std::string destination_add; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(wallet_id) + KV_SERIALIZE(proposal_info) + KV_SERIALIZE(destination_add) + END_KV_SERIALIZE_MAP() + }; + + + struct COMMAND_IONIC_SWAP_GENERATE_PROPOSAL + { + struct request + { + ionic_swap_proposal_info proposal; + std::string destination_address; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(proposal) + KV_SERIALIZE(destination_address) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string hex_raw_proposal; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(hex_raw_proposal) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO + { + struct request + { + std::string hex_raw_proposal; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(hex_raw_proposal) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + ionic_swap_proposal_info proposal; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(proposal) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL + { + struct request + { + std::string hex_raw_proposal; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(hex_raw_proposal) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + crypto::hash result_tx_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(result_tx_id) + END_KV_SERIALIZE_MAP() + }; + }; + + + struct wallet_info + { + std::list balances; + uint64_t mined_total; + std::string address; + std::string view_sec_key; + std::string path; + bool is_auditable; + bool is_watch_only; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(balances) + KV_SERIALIZE(mined_total) + KV_SERIALIZE(address) + KV_SERIALIZE(view_sec_key) + KV_SERIALIZE(path) + KV_SERIALIZE(is_auditable); + KV_SERIALIZE(is_watch_only); + END_KV_SERIALIZE_MAP() + }; + + + + struct wallet_entry_info + { + wallet_info wi; + uint64_t wallet_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(wi) + KV_SERIALIZE(wallet_id) + END_KV_SERIALIZE_MAP() + + }; + + struct COMMAND_MW_GET_WALLETS + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::vector wallets; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(wallets) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_MW_SELECT_WALLET + { + struct request + { + uint64_t wallet_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(wallet_id) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_SIGN_MESSAGE + { + struct request + { + std::string buff; //base64 encoded data + + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(buff) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + crypto::signature sig; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(sig) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_VALIDATE_SIGNATURE + { + struct request + { + std::string buff; //base64 encoded data + crypto::signature sig; + crypto::public_key pkey; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(buff) + KV_SERIALIZE_POD_AS_HEX_STRING(sig) + KV_SERIALIZE_POD_AS_HEX_STRING(pkey) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_ENCRYPT_DATA + { + struct request + { + std::string buff; //base64 encoded data + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(buff) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string res_buff; //base64 encoded encrypted data + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(res_buff) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_DECRYPT_DATA + { + struct request + { + std::string buff; //base64 encoded encrypted data + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(buff) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string res_buff; //base64 encoded data + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(res_buff) + END_KV_SERIALIZE_MAP() + }; + }; + + struct assets_whitelist { std::vector assets; @@ -1212,6 +1516,10 @@ namespace wallet_public } } + inline bool operator==(const asset_funds& lhs, const asset_funds& rhs) + { + return lhs.amount == rhs.amount && lhs.asset_id == rhs.asset_id; + } } // namespace wallet_rpc } // namespace tools diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a685c3d8..e9b156cd 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -56,13 +56,18 @@ namespace tools command_line::add_arg(desc, arg_deaf_mode); } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server(wallet2& w) - : m_wallet(w) + wallet_rpc_server::wallet_rpc_server(std::shared_ptr wptr) + : m_pwallet(wptr) , m_do_mint(false) , m_deaf(false) , m_last_wallet_store_height(0) {} //------------------------------------------------------------------------------------------------------------------------------ + std::shared_ptr wallet_rpc_server::get_wallet() + { + return std::shared_ptr(m_pwallet); + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run(bool do_mint, bool offline_mode, const currency::account_public_address& miner_address) { static const uint64_t wallet_rpc_idle_work_period_ms = 2000; @@ -79,7 +84,7 @@ namespace tools bool received_money = false, ok = false; std::atomic stop(false); LOG_PRINT_L2("wallet RPC idle: refreshing..."); - m_wallet.refresh(blocks_fetched, received_money, ok, stop); + get_wallet()->refresh(blocks_fetched, received_money, ok, stop); if (stop) { LOG_PRINT_L1("wallet RPC idle: refresh failed"); @@ -88,24 +93,24 @@ namespace tools bool has_related_alias_in_unconfirmed = false; LOG_PRINT_L2("wallet RPC idle: scanning tx pool..."); - m_wallet.scan_tx_pool(has_related_alias_in_unconfirmed); + get_wallet()->scan_tx_pool(has_related_alias_in_unconfirmed); if (m_do_mint) { LOG_PRINT_L2("wallet RPC idle: trying to do PoS iteration..."); - m_wallet.try_mint_pos(miner_address); + get_wallet()->try_mint_pos(miner_address); } //auto-store wallet in server mode, let's do it every 24-hour - if (m_wallet.get_top_block_height() < m_last_wallet_store_height) + if (get_wallet()->get_top_block_height() < m_last_wallet_store_height) { - LOG_ERROR("Unexpected m_last_wallet_store_height = " << m_last_wallet_store_height << " or " << m_wallet.get_top_block_height()); + LOG_ERROR("Unexpected m_last_wallet_store_height = " << m_last_wallet_store_height << " or " << get_wallet()->get_top_block_height()); } - else if (m_wallet.get_top_block_height() - m_last_wallet_store_height > CURRENCY_BLOCKS_PER_DAY) + else if (get_wallet()->get_top_block_height() - m_last_wallet_store_height > CURRENCY_BLOCKS_PER_DAY) { //store wallet - m_wallet.store(); - m_last_wallet_store_height = m_wallet.get_top_block_height(); + get_wallet()->store(); + m_last_wallet_store_height = get_wallet()->get_top_block_height(); } } catch (error::no_connection_to_daemon&) @@ -141,14 +146,14 @@ namespace tools if (command_line::has_arg(vm, arg_miner_text_info)) { - m_wallet.set_miner_text_info(command_line::get_arg(vm, arg_miner_text_info)); + get_wallet()->set_miner_text_info(command_line::get_arg(vm, arg_miner_text_info)); } return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) { - m_last_wallet_store_height = m_wallet.get_top_block_height(); + m_last_wallet_store_height = get_wallet()->get_top_block_height(); m_net_server.set_threads_prefix("RPC"); bool r = handle_command_line(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); @@ -187,7 +192,7 @@ namespace tools try { uint64_t stub_mined = 0; // unused - bool r = m_wallet.balance(res.balances, stub_mined); + bool r = get_wallet()->balance(res.balances, stub_mined); CHECK_AND_ASSERT_THROW_MES(r, "m_wallet.balance failed"); for (auto it = res.balances.begin(); it != res.balances.end(); ++it) { @@ -212,7 +217,7 @@ namespace tools { try { - res.address = m_wallet.get_account().get_public_address_str(); + res.address = get_wallet()->get_account().get_public_address_str(); } catch (std::exception& e) { @@ -226,17 +231,17 @@ namespace tools { try { - res.address = m_wallet.get_account().get_public_address_str(); - res.is_whatch_only = m_wallet.is_watch_only(); - res.path = epee::string_encoding::convert_to_ansii(m_wallet.get_wallet_path()); - res.transfers_count = m_wallet.get_recent_transfers_total_count(); - res.transfer_entries_count = m_wallet.get_transfer_entries_count(); + res.address = get_wallet()->get_account().get_public_address_str(); + res.is_whatch_only = get_wallet()->is_watch_only(); + res.path = epee::string_encoding::convert_to_ansii(get_wallet()->get_wallet_path()); + res.transfers_count = get_wallet()->get_recent_transfers_total_count(); + res.transfer_entries_count = get_wallet()->get_transfer_entries_count(); std::map distribution; - m_wallet.get_utxo_distribution(distribution); + get_wallet()->get_utxo_distribution(distribution); for (const auto& ent : distribution) res.utxo_distribution.push_back(currency::print_money_brief(ent.first) + ":" + std::to_string(ent.second)); - res.current_height = m_wallet.get_top_block_height(); + res.current_height = get_wallet()->get_top_block_height(); return true; } catch (std::exception& e) @@ -250,7 +255,7 @@ namespace tools { try { - res.seed_phrase = m_wallet.get_account().get_seed_phrase(req.seed_password); + res.seed_phrase = get_wallet()->get_account().get_seed_phrase(req.seed_password); return true; } catch (std::exception& e) @@ -271,21 +276,21 @@ namespace tools { if (req.update_provision_info) { - res.pi.balance = m_wallet.balance(res.pi.unlocked_balance); - res.pi.transfer_entries_count = m_wallet.get_transfer_entries_count(); - res.pi.transfers_count = m_wallet.get_recent_transfers_total_count(); - res.pi.curent_height = m_wallet.get_top_block_height(); + res.pi.balance = get_wallet()->balance(res.pi.unlocked_balance); + res.pi.transfer_entries_count = get_wallet()->get_transfer_entries_count(); + res.pi.transfers_count = get_wallet()->get_recent_transfers_total_count(); + res.pi.curent_height = get_wallet()->get_top_block_height(); } if (req.offset == 0 && !req.exclude_unconfirmed) - m_wallet.get_unconfirmed_transfers(res.transfers, req.exclude_mining_txs); + get_wallet()->get_unconfirmed_transfers(res.transfers, req.exclude_mining_txs); bool start_from_end = true; if (req.order == ORDER_FROM_BEGIN_TO_END) { start_from_end = false; } - m_wallet.get_recent_transfers_history(res.transfers, req.offset, req.count, res.total_transfers, res.last_item_index, req.exclude_mining_txs, start_from_end); + get_wallet()->get_recent_transfers_history(res.transfers, req.offset, req.count, res.total_transfers, res.last_item_index, req.exclude_mining_txs, start_from_end); return true; } @@ -299,10 +304,10 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_transfer(const wallet_public::COMMAND_RPC_TRANSFER::request& req, wallet_public::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) { - if (req.fee < m_wallet.get_core_runtime_config().tx_pool_min_fee) + if (req.fee < get_wallet()->get_core_runtime_config().tx_pool_min_fee) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; - er.message = std::string("Given fee is too low: ") + epee::string_tools::num_to_string_fast(req.fee) + ", minimum is: " + epee::string_tools::num_to_string_fast(m_wallet.get_core_runtime_config().tx_pool_min_fee); + er.message = std::string("Given fee is too low: ") + epee::string_tools::num_to_string_fast(req.fee) + ", minimum is: " + epee::string_tools::num_to_string_fast(get_wallet()->get_core_runtime_config().tx_pool_min_fee); return false; } @@ -314,7 +319,7 @@ namespace tools return false; } - construct_tx_param ctp = m_wallet.get_default_construct_tx_param_inital(); + construct_tx_param ctp = get_wallet()->get_default_construct_tx_param_inital(); if (req.service_entries_permanent) { //put it to extra @@ -357,7 +362,7 @@ namespace tools wrap = true; //encrypt body with a special way } - else if(!m_wallet.get_transfer_address(it->address, de.addr.back(), embedded_payment_id)) + else if(!get_wallet()->get_transfer_address(it->address, de.addr.back(), embedded_payment_id)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; @@ -396,7 +401,7 @@ namespace tools if (req.push_payer && !wrap) { - currency::create_and_add_tx_payer_to_container_from_address(extra, m_wallet.get_account().get_keys().account_address, m_wallet.get_top_block_height(), m_wallet.get_core_runtime_config()); + currency::create_and_add_tx_payer_to_container_from_address(extra, get_wallet()->get_account().get_keys().account_address, get_wallet()->get_top_block_height(), get_wallet()->get_core_runtime_config()); } if (!req.hide_receiver) @@ -404,7 +409,7 @@ namespace tools for (auto& d : dsts) { for (auto& a : d.addr) - currency::create_and_add_tx_receiver_to_container_from_address(extra, a, m_wallet.get_top_block_height(), m_wallet.get_core_runtime_config()); + currency::create_and_add_tx_receiver_to_container_from_address(extra, a, get_wallet()->get_top_block_height(), get_wallet()->get_core_runtime_config()); } } @@ -412,8 +417,8 @@ namespace tools std::string unsigned_tx_blob_str; ctp.fee = req.fee; ctp.fake_outputs_count = req.mixin; - m_wallet.transfer(ctp, result, true, &unsigned_tx_blob_str); - if (m_wallet.is_watch_only()) + get_wallet()->transfer(ctp, result, true, &unsigned_tx_blob_str); + if (get_wallet()->is_watch_only()) { res.tx_unsigned_hex = epee::string_tools::buff_to_hex_nodelimer(unsigned_tx_blob_str); // watch-only wallets could not sign and relay transactions // leave res.tx_hash empty, because tx hash will change after signing @@ -449,9 +454,9 @@ namespace tools bool wallet_rpc_server::on_store(const wallet_public::COMMAND_RPC_STORE::request& req, wallet_public::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); - m_wallet.store(); + get_wallet()->store(); boost::system::error_code ec = AUTO_VAL_INIT(ec); - res.wallet_file_size = m_wallet.get_wallet_file_size(); + res.wallet_file_size = get_wallet()->get_wallet_file_size(); WALLET_RPC_CATCH_TRY_ENTRY(); return true; } @@ -468,7 +473,7 @@ namespace tools res.payments.clear(); std::list payment_list; - m_wallet.get_payments(payment_id, payment_list); + get_wallet()->get_payments(payment_id, payment_list); for (auto payment : payment_list) { if (payment.m_unlock_time && !req.allow_locked_transactions) @@ -505,7 +510,7 @@ namespace tools } std::list payment_list; - m_wallet.get_payments(payment_id, payment_list, req.min_block_height); + get_wallet()->get_payments(payment_id, payment_list, req.min_block_height); for (auto & payment : payment_list) { @@ -553,7 +558,7 @@ namespace tools crypto::generate_random_bytes(payment_id.size(), &payment_id.front()); } - res.integrated_address = currency::get_account_address_and_payment_id_as_str(m_wallet.get_account().get_public_address(), payment_id); + res.integrated_address = currency::get_account_address_and_payment_id_as_str(get_wallet()->get_account().get_public_address(), payment_id); res.payment_id = epee::string_tools::buff_to_hex_nodelimer(payment_id); return !res.integrated_address.empty(); @@ -587,7 +592,7 @@ namespace tools currency::account_public_address addr; currency::payment_id_t integrated_payment_id; - if (!m_wallet.get_transfer_address(req.address, addr, integrated_payment_id)) + if (!get_wallet()->get_transfer_address(req.address, addr, integrated_payment_id)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("Invalid address: ") + req.address; @@ -606,10 +611,10 @@ namespace tools payment_id = integrated_payment_id; } - if (req.fee < m_wallet.get_core_runtime_config().tx_pool_min_fee) + if (req.fee < get_wallet()->get_core_runtime_config().tx_pool_min_fee) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; - er.message = std::string("Given fee is too low: ") + epee::string_tools::num_to_string_fast(req.fee) + ", minimum is: " + epee::string_tools::num_to_string_fast(m_wallet.get_core_runtime_config().tx_pool_min_fee); + er.message = std::string("Given fee is too low: ") + epee::string_tools::num_to_string_fast(req.fee) + ", minimum is: " + epee::string_tools::num_to_string_fast(get_wallet()->get_core_runtime_config().tx_pool_min_fee); return false; } @@ -620,14 +625,14 @@ namespace tools uint64_t amount_total = 0, amount_swept = 0; std::string unsigned_tx_blob_str; - m_wallet.sweep_below(req.mixin, addr, req.amount, payment_id, req.fee, outs_total, amount_total, outs_swept, amount_swept, &tx, &unsigned_tx_blob_str); + get_wallet()->sweep_below(req.mixin, addr, req.amount, payment_id, req.fee, outs_total, amount_total, outs_swept, amount_swept, &tx, &unsigned_tx_blob_str); res.amount_swept = amount_swept; res.amount_total = amount_total; res.outs_swept = outs_swept; res.outs_total = outs_total; - if (m_wallet.is_watch_only()) + if (get_wallet()->is_watch_only()) { res.tx_unsigned_hex = epee::string_tools::buff_to_hex_nodelimer(unsigned_tx_blob_str); // watch-only wallets can't sign and relay transactions // leave res.tx_hash empty, because tx has will change after signing @@ -673,7 +678,7 @@ namespace tools return false; } std::string tx_signed_blob; - m_wallet.sign_transfer(tx_unsigned_blob, tx_signed_blob, tx); + get_wallet()->sign_transfer(tx_unsigned_blob, tx_signed_blob, tx); res.tx_signed_hex = epee::string_tools::buff_to_hex_nodelimer(tx_signed_blob); res.tx_hash = epee::string_tools::pod_to_hex(currency::get_transaction_hash(tx)); @@ -693,7 +698,7 @@ namespace tools WALLET_RPC_BEGIN_TRY_ENTRY(); currency::transaction tx = AUTO_VAL_INIT(tx); - m_wallet.submit_transfer(tx_signed_blob, tx); + get_wallet()->submit_transfer(tx_signed_blob, tx); res.tx_hash = epee::string_tools::pod_to_hex(currency::get_transaction_hash(tx)); WALLET_RPC_CATCH_TRY_ENTRY(); @@ -705,7 +710,7 @@ namespace tools bool tx_id_specified = req.tx_id != currency::null_hash; // process confirmed txs - m_wallet.enumerate_transfers_history([&](const wallet_public::wallet_transfer_info& wti) -> bool { + get_wallet()->enumerate_transfers_history([&](const wallet_public::wallet_transfer_info& wti) -> bool { if (tx_id_specified) { @@ -741,7 +746,7 @@ namespace tools // process unconfirmed txs if (req.pool) { - m_wallet.enumerate_unconfirmed_transfers([&](const wallet_public::wallet_transfer_info& wti) -> bool { + get_wallet()->enumerate_unconfirmed_transfers([&](const wallet_public::wallet_transfer_info& wti) -> bool { if ((wti.is_income && req.in) || (!wti.is_income && req.out)) res.pool.push_back(wti); return true; // continue @@ -752,7 +757,7 @@ namespace tools } bool wallet_rpc_server::on_get_mining_history(const wallet_public::COMMAND_RPC_GET_MINING_HISTORY::request& req, wallet_public::COMMAND_RPC_GET_MINING_HISTORY::response& res, epee::json_rpc::error& er, connection_context& cntx) { - m_wallet.get_mining_history(res, req.v); + get_wallet()->get_mining_history(res, req.v); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -775,7 +780,7 @@ namespace tools } currency::transaction tx = AUTO_VAL_INIT(tx); - m_wallet.request_alias_registration(ai, tx, m_wallet.get_default_fee(), 0, req.authority_key); + get_wallet()->request_alias_registration(ai, tx, get_wallet()->get_default_fee(), 0, req.authority_key); res.tx_id = get_transaction_hash(tx); return true; WALLET_RPC_CATCH_TRY_ENTRY(); @@ -786,7 +791,7 @@ namespace tools WALLET_RPC_BEGIN_TRY_ENTRY(); currency::transaction tx = AUTO_VAL_INIT(tx); currency::transaction template_tx = AUTO_VAL_INIT(template_tx); - m_wallet.send_escrow_proposal(req, tx, template_tx); + get_wallet()->send_escrow_proposal(req, tx, template_tx); return true; WALLET_RPC_CATCH_TRY_ENTRY(); } @@ -794,7 +799,7 @@ namespace tools bool wallet_rpc_server::on_contracts_accept_proposal(const wallet_public::COMMAND_CONTRACTS_ACCEPT_PROPOSAL::request& req, wallet_public::COMMAND_CONTRACTS_ACCEPT_PROPOSAL::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); - m_wallet.accept_proposal(req.contract_id, req.acceptance_fee); + get_wallet()->accept_proposal(req.contract_id, req.acceptance_fee); return true; WALLET_RPC_CATCH_TRY_ENTRY(); } @@ -803,7 +808,7 @@ namespace tools { WALLET_RPC_BEGIN_TRY_ENTRY(); tools::wallet2::escrow_contracts_container ecc; - m_wallet.get_contracts(ecc); + get_wallet()->get_contracts(ecc); res.contracts.resize(ecc.size()); size_t i = 0; for (auto& c : ecc) @@ -819,7 +824,7 @@ namespace tools bool wallet_rpc_server::on_contracts_release(const wallet_public::COMMAND_CONTRACTS_RELEASE::request& req, wallet_public::COMMAND_CONTRACTS_RELEASE::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); - m_wallet.finish_contract(req.contract_id, req.release_type); + get_wallet()->finish_contract(req.contract_id, req.release_type); return true; WALLET_RPC_CATCH_TRY_ENTRY(); } @@ -827,7 +832,7 @@ namespace tools bool wallet_rpc_server::on_contracts_request_cancel(const wallet_public::COMMAND_CONTRACTS_REQUEST_CANCEL::request& req, wallet_public::COMMAND_CONTRACTS_REQUEST_CANCEL::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); - m_wallet.request_cancel_contract(req.contract_id, req.fee, req.expiration_period); + get_wallet()->request_cancel_contract(req.contract_id, req.fee, req.expiration_period); return true; WALLET_RPC_CATCH_TRY_ENTRY(); } @@ -835,7 +840,7 @@ namespace tools bool wallet_rpc_server::on_contracts_accept_cancel(const wallet_public::COMMAND_CONTRACTS_ACCEPT_CANCEL::request& req, wallet_public::COMMAND_CONTRACTS_ACCEPT_CANCEL::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); - m_wallet.accept_cancel_contract(req.contract_id); + get_wallet()->accept_cancel_contract(req.contract_id); return true; WALLET_RPC_CATCH_TRY_ENTRY(); } @@ -843,9 +848,9 @@ namespace tools bool wallet_rpc_server::on_marketplace_get_my_offers(const wallet_public::COMMAND_MARKETPLACE_GET_MY_OFFERS::request& req, wallet_public::COMMAND_MARKETPLACE_GET_MY_OFFERS::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); - m_wallet.get_actual_offers(res.offers); + get_wallet()->get_actual_offers(res.offers); size_t offers_count_before_filtering = res.offers.size(); - bc_services::filter_offers_list(res.offers, req.filter, m_wallet.get_core_runtime_config().get_core_time()); + bc_services::filter_offers_list(res.offers, req.filter, get_wallet()->get_core_runtime_config().get_core_time()); LOG_PRINT("get_my_offers(): " << res.offers.size() << " offers returned (" << offers_count_before_filtering << " was before filter)", LOG_LEVEL_1); return true; WALLET_RPC_CATCH_TRY_ENTRY(); @@ -855,7 +860,7 @@ namespace tools { WALLET_RPC_BEGIN_TRY_ENTRY(); currency::transaction res_tx = AUTO_VAL_INIT(res_tx); - m_wallet.push_offer(req.od, res_tx); + get_wallet()->push_offer(req.od, res_tx); res.tx_hash = string_tools::pod_to_hex(currency::get_transaction_hash(res_tx)); res.tx_blob_size = currency::get_object_blobsize(res_tx); @@ -868,7 +873,7 @@ namespace tools WALLET_RPC_BEGIN_TRY_ENTRY(); currency::transaction res_tx = AUTO_VAL_INIT(res_tx); - m_wallet.update_offer_by_id(req.tx_id, req.no, req.od, res_tx); + get_wallet()->update_offer_by_id(req.tx_id, req.no, req.od, res_tx); res.tx_hash = string_tools::pod_to_hex(currency::get_transaction_hash(res_tx)); res.tx_blob_size = currency::get_object_blobsize(res_tx); @@ -880,7 +885,7 @@ namespace tools { WALLET_RPC_BEGIN_TRY_ENTRY(); currency::transaction res_tx = AUTO_VAL_INIT(res_tx); - m_wallet.cancel_offer_by_id(req.tx_id, req.no, req.fee, res_tx); + get_wallet()->cancel_offer_by_id(req.tx_id, req.no, req.fee, res_tx); res.tx_hash = string_tools::pod_to_hex(currency::get_transaction_hash(res_tx)); res.tx_blob_size = currency::get_object_blobsize(res_tx); @@ -892,7 +897,7 @@ namespace tools { WALLET_RPC_BEGIN_TRY_ENTRY(); currency::transaction tx = AUTO_VAL_INIT(tx); - m_wallet.create_htlc_proposal(req.amount, req.counterparty_address, req.lock_blocks_count, tx, req.htlc_hash, res.derived_origin_secret); + get_wallet()->create_htlc_proposal(req.amount, req.counterparty_address, req.lock_blocks_count, tx, req.htlc_hash, res.derived_origin_secret); res.result_tx_blob = currency::tx_to_blob(tx); res.result_tx_id = get_transaction_hash(tx); WALLET_RPC_CATCH_TRY_ENTRY(); @@ -902,7 +907,7 @@ namespace tools bool wallet_rpc_server::on_get_list_of_active_htlc(const wallet_public::COMMAND_GET_LIST_OF_ACTIVE_HTLC::request& req, wallet_public::COMMAND_GET_LIST_OF_ACTIVE_HTLC::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); - m_wallet.get_list_of_active_htlc(res.htlcs, req.income_redeem_only); + get_wallet()->get_list_of_active_htlc(res.htlcs, req.income_redeem_only); WALLET_RPC_CATCH_TRY_ENTRY(); return true; } @@ -911,7 +916,7 @@ namespace tools { WALLET_RPC_BEGIN_TRY_ENTRY(); currency::transaction tx = AUTO_VAL_INIT(tx); - m_wallet.redeem_htlc(req.tx_id, req.origin_secret, tx); + get_wallet()->redeem_htlc(req.tx_id, req.origin_secret, tx); res.result_tx_blob = currency::tx_to_blob(tx); res.result_tx_id = get_transaction_hash(tx); WALLET_RPC_CATCH_TRY_ENTRY(); @@ -921,11 +926,150 @@ namespace tools bool wallet_rpc_server::on_check_htlc_redeemed(const wallet_public::COMMAND_CHECK_HTLC_REDEEMED::request& req, wallet_public::COMMAND_CHECK_HTLC_REDEEMED::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); - m_wallet.check_htlc_redeemed(req.htlc_tx_id, res.origin_secrete, res.redeem_tx_id); + get_wallet()->check_htlc_redeemed(req.htlc_tx_id, res.origin_secrete, res.redeem_tx_id); WALLET_RPC_CATCH_TRY_ENTRY(); return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_ionic_swap_generate_proposal(const wallet_public::COMMAND_IONIC_SWAP_GENERATE_PROPOSAL::request& req, wallet_public::COMMAND_IONIC_SWAP_GENERATE_PROPOSAL::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + currency::account_public_address destination_addr = AUTO_VAL_INIT(destination_addr); + currency::payment_id_t integrated_payment_id; + if (!get_wallet()->get_transfer_address(req.destination_address, destination_addr, integrated_payment_id)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; + er.message = "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS"; + return false; + } + if (integrated_payment_id.size()) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; + er.message = "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS - integrated address is noit supported yet"; + return false; + } + wallet_public::ionic_swap_proposal proposal = AUTO_VAL_INIT(proposal); + bool r = get_wallet()->create_ionic_swap_proposal(req.proposal, destination_addr, proposal); + if (!r) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; + er.message = "WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT - Error creating proposal"; + return false; + } + res.hex_raw_proposal = epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(proposal)); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_ionic_swap_get_proposal_info(const wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO::request& req, wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + std::string raw_tx_template; + bool r = epee::string_tools::parse_hexstr_to_binbuff(req.hex_raw_proposal, raw_tx_template); + if (!r) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; + er.message = "WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT - failed to parse template from hex"; + return false; + } + if (!get_wallet()->get_ionic_swap_proposal_info(raw_tx_template, res.proposal)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; + er.message = "WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT - get_ionic_swap_proposal_info"; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_ionic_swap_accept_proposal(const wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL::request& req, wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + + std::string raw_tx_template; + bool r = epee::string_tools::parse_hexstr_to_binbuff(req.hex_raw_proposal, raw_tx_template); + if (!r) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; + er.message = "WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT - failed to parse template from hex"; + return false; + } + + currency::transaction result_tx = AUTO_VAL_INIT(result_tx); + if (!get_wallet()->accept_ionic_swap_proposal(raw_tx_template, result_tx)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; + er.message = "WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT - failed to accept_ionic_swap_proposal()"; + return false; + } + + res.result_tx_id = currency::get_transaction_hash(result_tx); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + i_wallet2_callback* pcallback = get_wallet()->get_callback(); + if (!pcallback) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + return false; + } + pcallback->on_mw_get_wallets(res.wallets); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_mw_select_wallet(const wallet_public::COMMAND_MW_SELECT_WALLET::request& req, wallet_public::COMMAND_MW_SELECT_WALLET::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + i_wallet2_callback* pcallback = get_wallet()->get_callback(); + if (!pcallback) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + return false; + } + pcallback->on_mw_select_wallet(req.wallet_id); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_sign_message(const wallet_public::COMMAND_SIGN_MESSAGE::request& req, wallet_public::COMMAND_SIGN_MESSAGE::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + std::string buff = epee::string_encoding::base64_decode(req.buff); + get_wallet()->sign_buffer(buff, res.sig); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_validate_signature(const wallet_public::COMMAND_VALIDATE_SIGNATURE::request& req, wallet_public::COMMAND_VALIDATE_SIGNATURE::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + std::string buff = epee::string_encoding::base64_decode(req.buff); + bool r = get_wallet()->validate_sign(buff, req.sig, req.pkey); + if (!r) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT; + er.message = "WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT"; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_encrypt_data(const wallet_public::COMMAND_ENCRYPT_DATA::request& req, wallet_public::COMMAND_ENCRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + std::string buff = epee::string_encoding::base64_decode(req.buff); + bool r = get_wallet()->encrypt_buffer(buff, res.res_buff); + res.res_buff = epee::string_encoding::base64_encode(res.res_buff); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_decrypt_data(const wallet_public::COMMAND_DECRYPT_DATA::request& req, wallet_public::COMMAND_DECRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + std::string buff = epee::string_encoding::base64_decode(req.buff); + bool r = get_wallet()->encrypt_buffer(buff, res.res_buff); + res.res_buff = epee::string_encoding::base64_encode(res.res_buff); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::reset_active_wallet(std::shared_ptr w) + { + m_pwallet = w; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ } // namespace tools diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 1edd325c..b72c93de 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -22,7 +22,7 @@ namespace tools public: typedef epee::net_utils::connection_context_base connection_context; - wallet_rpc_server(wallet2& cr); + wallet_rpc_server(std::shared_ptr wptr); const static command_line::arg_descriptor arg_rpc_bind_port; const static command_line::arg_descriptor arg_rpc_bind_ip; @@ -73,6 +73,20 @@ namespace tools MAP_JON_RPC_WE("atomics_redeem_htlc", on_redeem_htlc, wallet_public::COMMAND_REDEEM_HTLC) MAP_JON_RPC_WE("atomics_check_htlc_redeemed", on_check_htlc_redeemed, wallet_public::COMMAND_CHECK_HTLC_REDEEMED) + //IONIC_SWAPS API + MAP_JON_RPC_WE("ionic_swap_generate_proposal", on_ionic_swap_generate_proposal, wallet_public::COMMAND_IONIC_SWAP_GENERATE_PROPOSAL) + MAP_JON_RPC_WE("ionic_swap_get_proposal_info", on_ionic_swap_get_proposal_info, wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO) + MAP_JON_RPC_WE("ionic_swap_accept_proposal", on_ionic_swap_accept_proposal, wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL) + + //MULTIWALLET APIs + MAP_JON_RPC_WE("mw_get_wallets", on_mw_get_wallets, wallet_public::COMMAND_MW_GET_WALLETS) + MAP_JON_RPC_WE("mw_select_wallet", on_mw_select_wallet, wallet_public::COMMAND_MW_SELECT_WALLET) + + //basic crypto operations + MAP_JON_RPC_WE("sign_message", on_sign_message, wallet_public::COMMAND_SIGN_MESSAGE) + MAP_JON_RPC_WE("validate_signature", on_validate_signature, wallet_public::COMMAND_VALIDATE_SIGNATURE) + MAP_JON_RPC_WE("encrypt_data", on_encrypt_data, wallet_public::COMMAND_ENCRYPT_DATA) + MAP_JON_RPC_WE("decrypt_data", on_decrypt_data, wallet_public::COMMAND_DECRYPT_DATA) END_JSON_RPC_MAP() END_URI_MAP2() @@ -114,11 +128,28 @@ namespace tools bool on_redeem_htlc(const wallet_public::COMMAND_REDEEM_HTLC::request& req, wallet_public::COMMAND_REDEEM_HTLC::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_check_htlc_redeemed(const wallet_public::COMMAND_CHECK_HTLC_REDEEMED::request& req, wallet_public::COMMAND_CHECK_HTLC_REDEEMED::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_ionic_swap_generate_proposal(const wallet_public::COMMAND_IONIC_SWAP_GENERATE_PROPOSAL::request& req, wallet_public::COMMAND_IONIC_SWAP_GENERATE_PROPOSAL::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_ionic_swap_get_proposal_info(const wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO::request& req, wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_ionic_swap_accept_proposal(const wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL::request& req, wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL::response& res, epee::json_rpc::error& er, connection_context& cntx); + + bool on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_mw_select_wallet(const wallet_public::COMMAND_MW_SELECT_WALLET::request& req, wallet_public::COMMAND_MW_SELECT_WALLET::response& res, epee::json_rpc::error& er, connection_context& cntx); + + bool on_sign_message(const wallet_public::COMMAND_SIGN_MESSAGE::request& req, wallet_public::COMMAND_SIGN_MESSAGE::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_validate_signature(const wallet_public::COMMAND_VALIDATE_SIGNATURE::request& req, wallet_public::COMMAND_VALIDATE_SIGNATURE::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_encrypt_data(const wallet_public::COMMAND_ENCRYPT_DATA::request& req, wallet_public::COMMAND_ENCRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_decrypt_data(const wallet_public::COMMAND_DECRYPT_DATA::request& req, wallet_public::COMMAND_DECRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx); + + + + std::shared_ptr get_wallet(); + + bool reset_active_wallet(std::shared_ptr w); bool handle_command_line(const boost::program_options::variables_map& vm); private: - wallet2& m_wallet; + std::weak_ptr m_pwallet; std::string m_port; std::string m_bind_ip; bool m_do_mint; diff --git a/src/wallet/wallets_manager.cpp b/src/wallet/wallets_manager.cpp index a08aa2e2..d8159122 100644 --- a/src/wallet/wallets_manager.cpp +++ b/src/wallet/wallets_manager.cpp @@ -802,7 +802,7 @@ void wallets_manager::init_wallet_entry(wallet_vs_options& wo, uint64_t id) wo.m_pproxy_diagnostig_info = m_rpc_proxy->get_proxy_diagnostic_info(); wo.pview = m_pview; wo.has_related_alias_in_unconfirmed = false; - wo.rpc_wrapper.reset(new tools::wallet_rpc_server(*wo.w.unlocked_get().get())); + wo.rpc_wrapper.reset(new tools::wallet_rpc_server(wo.w.unlocked_get())); if (m_remote_node_mode) wo.core_conf = currency::get_default_core_runtime_config(); else @@ -876,6 +876,80 @@ std::string wallets_manager::get_fav_offers(const std::listget()->create_ionic_swap_proposal(proposal_req.proposal_info, dest_account, proposal); + if (!r) + { + return API_RETURN_CODE_FAIL; + } + else + { + result_proposal_hex = epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(proposal)); + return API_RETURN_CODE_OK; + } + } + catch (...) + { + return API_RETURN_CODE_FAIL; + } + return API_RETURN_CODE_OK; +} + +std::string wallets_manager::get_ionic_swap_proposal_info(uint64_t wallet_id, std::string&raw_tx_template_hex, tools::wallet_public::ionic_swap_proposal_info& proposal) +{ + GET_WALLET_OPT_BY_ID(wallet_id, wo); + try { + std::string raw_tx_template; + bool r = epee::string_tools::parse_hexstr_to_binbuff(raw_tx_template_hex, raw_tx_template); + if (!r) + { + return API_RETURN_CODE_BAD_ARG; + } + + if (!wo.w->get()->get_ionic_swap_proposal_info(raw_tx_template, proposal)) + { + return API_RETURN_CODE_FAIL; + } + } + catch (...) + { + return API_RETURN_CODE_FAIL; + } + return API_RETURN_CODE_OK; +} + +std::string wallets_manager::accept_ionic_swap_proposal(uint64_t wallet_id, std::string&raw_tx_template_hex, std::string& result_raw_tx_hex) +{ + GET_WALLET_OPT_BY_ID(wallet_id, wo); + try { + std::string raw_tx_template; + bool r = epee::string_tools::parse_hexstr_to_binbuff(raw_tx_template_hex, raw_tx_template); + if (!r) + { + return API_RETURN_CODE_BAD_ARG; + } + currency::transaction result_tx = AUTO_VAL_INIT(result_tx); + if (!wo.w->get()->accept_ionic_swap_proposal(raw_tx_template, result_tx)) + { + return API_RETURN_CODE_FAIL; + } + result_raw_tx_hex = epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(result_tx)); + } + catch (...) + { + return API_RETURN_CODE_FAIL; + } + return API_RETURN_CODE_OK; +} std::string wallets_manager::get_my_offers(const bc_services::core_offers_filter& filter, std::list& offers) { if (m_remote_node_mode) @@ -1297,6 +1371,8 @@ std::string wallets_manager::get_alias_info_by_name(const std::string& name, cur if (!r) return API_RETURN_CODE_FAIL; + if (res.status == API_RETURN_CODE_NOT_FOUND) + return API_RETURN_CODE_NOT_FOUND; res_details.alias = name; res_details.details = res.alias_details; @@ -1923,7 +1999,13 @@ void wallets_manager::on_transfer_canceled(size_t wallet_id, const tools::wallet tei.ti = wti; SHARED_CRITICAL_REGION_LOCAL(m_wallets_lock); - auto& w = m_wallets[wallet_id].w; + auto it = m_wallets.find(wallet_id); + if (it == m_wallets.end()) + { + LOG_ERROR(get_wallet_log_prefix(wallet_id) + "on_transfer_canceled() wallet with id = " << wallet_id << " not found"); + return; + } + auto& w = it->second.w; if (w->get() != nullptr) { w->get()->balance(tei.balances, tei.total_mined); @@ -1931,7 +2013,7 @@ void wallets_manager::on_transfer_canceled(size_t wallet_id, const tools::wallet } else { - LOG_ERROR(get_wallet_log_prefix(wallet_id) + "on_transfer() wallet with id = " << wallet_id << " not found"); + LOG_ERROR(get_wallet_log_prefix(wallet_id) + "on_transfer_canceled() wallet with id = " << wallet_id << " has nullptr"); } m_pview->money_transfer_cancel(tei); } @@ -1942,6 +2024,31 @@ void wallets_manager::on_tor_status_change(size_t wallet_id, const std::string& m_pview->update_tor_status(tsu); } +void wallets_manager::on_mw_get_wallets(std::vector& wallets) +{ + std::list opened_wallets; + this->get_opened_wallets(opened_wallets); + wallets.resize(opened_wallets.size()); + size_t i = 0; + for (const auto& item : opened_wallets) + { + wallets[i].wi = item.wi; + wallets[i].wallet_id = item.wallet_id; + i++; + } +} +bool wallets_manager::on_mw_select_wallet(uint64_t wallet_id) +{ + SHARED_CRITICAL_REGION_LOCAL(m_wallets_lock); + auto it = m_wallets.find(wallet_id); + if (it == m_wallets.end()) + return false; + auto& wo = it->second; + //m_wallet_rpc_server.reset_active_wallet(wo.w); + return false; +} + + void wallets_manager::wallet_vs_options::worker_func() { LOG_PRINT_GREEN("[WALLET_HANDLER] Wallet handler thread started, addr: " << w->get()->get_account().get_public_address_str(), LOG_LEVEL_0); diff --git a/src/wallet/wallets_manager.h b/src/wallet/wallets_manager.h index b550857b..b9542b2a 100644 --- a/src/wallet/wallets_manager.h +++ b/src/wallet/wallets_manager.h @@ -136,6 +136,9 @@ public: std::string backup_wallet(uint64_t wallet_id, const std::wstring& path); std::string reset_wallet_password(uint64_t wallet_id, const std::string& pass); std::string is_wallet_password_valid(uint64_t wallet_id, const std::string& pass); + std::string create_ionic_swap_proposal(uint64_t wallet_id, const tools::wallet_public::create_ionic_swap_proposal_request& proposal, std::string& result_proposal_hex); + std::string get_ionic_swap_proposal_info(uint64_t wallet_id, std::string&raw_tx_template_hex, tools::wallet_public::ionic_swap_proposal_info& proposal); + std::string accept_ionic_swap_proposal(uint64_t wallet_id, std::string&raw_tx_template_hex, std::string& result_raw_tx); std::string get_my_offers(const bc_services::core_offers_filter& filter, std::list& offers); std::string get_fav_offers(const std::list& hashes, const bc_services::core_offers_filter& filter, std::list& offers); std::string get_tx_pool_info(currency::COMMAND_RPC_GET_POOL_INFO::response& res); @@ -190,6 +193,10 @@ private: virtual void on_transfer_canceled(size_t wallet_id, const tools::wallet_public::wallet_transfer_info& wti); virtual void on_tor_status_change(size_t wallet_id, const std::string& state); + virtual void on_mw_get_wallets(std::vector& wallets) override; + virtual bool on_mw_select_wallet(uint64_t wallet_id) override; + + //-------- std::thread m_main_worker_thread; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 32ba7264..21bb1fe7 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -2229,7 +2229,10 @@ bool shuffle_source_entries(std::vector& sources) //------------------------------------------------------------------------------ - +test_chain_unit_base::test_chain_unit_base() +{ + m_hardforks = get_default_core_runtime_config().hard_forks; // set default hardforks for tests (will be overriden by test if necessary) +} void test_chain_unit_base::register_callback(const std::string& cb_name, verify_callback cb) { m_callbacks[cb_name] = cb; @@ -2287,8 +2290,6 @@ test_chain_unit_enchanced::test_chain_unit_enchanced() REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_offers_count); REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_hardfork_active); REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_hardfork_inactive); - - m_hardforks = get_default_core_runtime_config().hard_forks; // set default hardforks for tests (will be overriden by test if necessary) } bool test_chain_unit_enchanced::configure_core(currency::core& c, size_t ev_index, const std::vector& events) diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 4662a530..a6222192 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -230,6 +230,8 @@ class test_generator; class test_chain_unit_base { public: + test_chain_unit_base(); + typedef boost::function &events)> verify_callback; typedef std::map callbacks_map; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index eda3300f..7e0c0169 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1085,6 +1085,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(zarcanum_basic_test); GENERATE_AND_PLAY(multiassets_basic_test); + GENERATE_AND_PLAY(ionic_swap_basic_test); GENERATE_AND_PLAY(zarcanum_test_n_inputs_validation); GENERATE_AND_PLAY(zarcanum_gen_time_balance); GENERATE_AND_PLAY(zarcanum_txs_with_big_shuffled_decoy_set_shuffled); diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index cf3fbb7d..a44af329 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -42,3 +42,4 @@ #include "isolate_auditable_and_proof.h" #include "zarcanum_test.h" #include "multiassets_test.h" +#include "ionic_swap_tests.h" diff --git a/tests/core_tests/hard_fork_2.cpp b/tests/core_tests/hard_fork_2.cpp index fc6a96cd..72b21bea 100644 --- a/tests/core_tests/hard_fork_2.cpp +++ b/tests/core_tests/hard_fork_2.cpp @@ -81,7 +81,7 @@ bool hard_fork_2_tx_payer_in_wallet::c1(currency::core& c, size_t ev_index, cons CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, MK_TEST_COINS(12)), false, ""); // wallet RPC server - tools::wallet_rpc_server miner_wlt_rpc(*miner_wlt); + tools::wallet_rpc_server miner_wlt_rpc(miner_wlt); epee::json_rpc::error je; tools::wallet_rpc_server::connection_context ctx; @@ -161,7 +161,7 @@ bool hard_fork_2_tx_payer_in_wallet::c1(currency::core& c, size_t ev_index, cons bob_wlt->callback(std::make_shared()); // clear callback // Before HF2: Bob (auditable address) -> Alice with payer info requested (should NOT put tx_payer or tx_payer_old) - tools::wallet_rpc_server bob_wlt_rpc(*bob_wlt); + tools::wallet_rpc_server bob_wlt_rpc(bob_wlt); tools::wallet_public::COMMAND_RPC_TRANSFER::request req_c = AUTO_VAL_INIT(req_c); req_c.destinations.push_back(tools::wallet_public::transfer_destination{ MK_TEST_COINS(1), m_accounts[ALICE_ACC_IDX].get_public_address_str() }); req_c.fee = TESTS_DEFAULT_FEE; @@ -328,7 +328,7 @@ bool hard_fork_2_tx_receiver_in_wallet::c1(currency::core& c, size_t ev_index, c CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, 0), false, ""); // wallet RPC server - tools::wallet_rpc_server alice_wlt_rpc(*alice_wlt); + tools::wallet_rpc_server alice_wlt_rpc(alice_wlt); epee::json_rpc::error je; tools::wallet_rpc_server::connection_context ctx; diff --git a/tests/core_tests/ionic_swap_tests.cpp b/tests/core_tests/ionic_swap_tests.cpp new file mode 100644 index 00000000..013176d2 --- /dev/null +++ b/tests/core_tests/ionic_swap_tests.cpp @@ -0,0 +1,203 @@ +// Copyright (c) 2014-2022 Zano Project +// Copyright (c) 2014-2018 The Louisdor Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chaingen.h" +#include "ionic_swap_tests.h" +#include "wallet_test_core_proxy.h" + +#include "random_helper.h" +#include "tx_builder.h" + +#define AMOUNT_TO_TRANSFER_MULTIASSETS_BASIC (TESTS_DEFAULT_FEE) + +#define AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC 500000000000000000 + + + +ionic_swap_basic_test::ionic_swap_basic_test() +{ + REGISTER_CALLBACK_METHOD(ionic_swap_basic_test, configure_core); + REGISTER_CALLBACK_METHOD(ionic_swap_basic_test, c1); + + m_hardforks.set_hardfork_height(1, 1); + m_hardforks.set_hardfork_height(2, 1); + m_hardforks.set_hardfork_height(3, 1); + m_hardforks.set_hardfork_height(4, 2); +} + +bool ionic_swap_basic_test::generate(std::vector& events) const +{ + // NOTE: This test is made deterministic to be able to correctly set up checkpoint. + random_state_test_restorer::reset_random(); // random generator's state was previously stored, will be restore on dtor (see also m_random_state_test_restorer) + uint64_t ts = 1450000000; + test_core_time::adjust(ts); + + ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + currency::account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + currency::account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + currency::account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + //account_base& carol_acc = m_accounts[CAROL_ACC_IDX]; carol_acc.generate(); carol_acc.set_createtime(ts); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + miner_wlt->get_account().set_createtime(0); + + //std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + //std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); + + // check passing over the hardfork + CHECK_AND_ASSERT_MES(c.get_blockchain_storage().is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM), false, "ZANO_HARDFORK_04_ZARCANUM is active"); + + + currency::account_base alice_acc; + alice_acc.generate(); + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, alice_acc); + alice_wlt->get_account().set_createtime(0); + + + currency::account_base bob_acc; + bob_acc.generate(); + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, bob_acc); + bob_wlt->get_account().set_createtime(0); + + + miner_wlt->refresh(); + + currency::asset_descriptor_base adb = AUTO_VAL_INIT(adb); + adb.total_max_supply = 1000000000000000000; //1M coins + adb.full_name = "Test coins"; + adb.ticker = "TCT"; + adb.decimal_point = 12; + + std::vector destinations(2); + destinations[0].addr.push_back(bob_wlt->get_account().get_public_address()); + destinations[0].amount = AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC; + destinations[0].asset_id = currency::null_pkey; + destinations[1].addr.push_back(alice_wlt->get_account().get_public_address()); + destinations[1].amount = AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC; + destinations[1].asset_id = currency::null_pkey; + + LOG_PRINT_MAGENTA("destinations[0].asset_id:" << destinations[0].asset_id, LOG_LEVEL_0); + LOG_PRINT_MAGENTA("destinations[1].asset_id:" << destinations[1].asset_id, LOG_LEVEL_0); + LOG_PRINT_MAGENTA("currency::null_pkey:" << currency::null_pkey, LOG_LEVEL_0); + + currency::transaction tx = AUTO_VAL_INIT(tx); + crypto::public_key asset_id = currency::null_pkey; + miner_wlt->publish_new_asset(adb, destinations, tx, asset_id); + LOG_PRINT_L0("Published new asset: " << asset_id << ", tx_id: " << currency::get_transaction_hash(tx)); + + currency::transaction res_tx = AUTO_VAL_INIT(res_tx); + miner_wlt->transfer(COIN, alice_wlt->get_account().get_public_address(), res_tx); + miner_wlt->transfer(COIN, bob_wlt->get_account().get_public_address(), res_tx); + + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + + bob_wlt->refresh(); + alice_wlt->refresh(); + uint64_t mined = 0; + std::unordered_map balances; + bob_wlt->balance(balances, mined); + + auto it_asset = balances.find(asset_id); + auto it_native = balances.find(currency::native_coin_asset_id); + + + CHECK_AND_ASSERT_MES(it_asset != balances.end() && it_native != balances.end(), false, "Failed to find needed asset in result balances"); + CHECK_AND_ASSERT_MES(it_asset->second.total == AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC, false, "Failed to find needed asset in result balances"); + CHECK_AND_ASSERT_MES(it_native->second.total == COIN, false, "Failed to find needed asset in result balances"); + + uint64_t mined_balance = it_native->second.total; + + balances.clear(); + alice_wlt->balance(balances, mined); + + it_asset = balances.find(asset_id); + it_native = balances.find(currency::native_coin_asset_id); + + CHECK_AND_ASSERT_MES(it_asset != balances.end() && it_native != balances.end(), false, "Failed to find needed asset in result balances"); + CHECK_AND_ASSERT_MES(it_native->second.total == COIN, false, "Failed to find needed asset in result balances"); + CHECK_AND_ASSERT_MES(it_asset->second.total == AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC, false, "Failed to find needed asset in result balances"); + + const uint64_t assets_to_exchange = 10 * COIN; + const uint64_t native_tokens_to_exchange = COIN/2; + + //alice_wlt want to trade with bob_wlt, to exchange 10.0 TCT to 1.0 ZANO + view::ionic_swap_proposal_info proposal_details = AUTO_VAL_INIT(proposal_details); + proposal_details.expiration_time = alice_wlt->get_core_runtime_config().get_core_time() + 10 * 60; + proposal_details.fee_paid_by_a = TESTS_DEFAULT_FEE; + proposal_details.mixins = 10; + proposal_details.to_bob.push_back(view::asset_funds{ asset_id , assets_to_exchange }); + proposal_details.to_alice.push_back(view::asset_funds{ currency::native_coin_asset_id , native_tokens_to_exchange }); + + tools::wallet_public::ionic_swap_proposal proposal = AUTO_VAL_INIT(proposal); + alice_wlt->create_ionic_swap_proposal(proposal_details, bob_wlt->get_account().get_public_address(), proposal); + + view::ionic_swap_proposal_info proposal_decoded_info = AUTO_VAL_INIT(proposal_decoded_info); + bob_wlt->get_ionic_swap_proposal_info(proposal, proposal_decoded_info); + + //Validate proposal + if (proposal_decoded_info.to_bob != proposal_details.to_bob + || proposal_decoded_info.to_alice != proposal_details.to_alice + || proposal_decoded_info.fee_paid_by_a != proposal_details.fee_paid_by_a + || proposal_decoded_info.mixins != proposal_details.mixins + ) + { + CHECK_AND_ASSERT_MES(false, false, "proposal actual and proposals decoded mismatch"); + } + + currency::transaction res_tx2 = AUTO_VAL_INIT(res_tx2); + r = bob_wlt->accept_ionic_swap_proposal(proposal, res_tx2); + CHECK_AND_ASSERT_MES(r, false, "Failed to accept ionic proposal"); + + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + bob_wlt->refresh(); + alice_wlt->refresh(); + + + balances.clear(); + alice_wlt->balance(balances, mined); + + it_asset = balances.find(asset_id); + it_native = balances.find(currency::native_coin_asset_id); + + CHECK_AND_ASSERT_MES(it_asset != balances.end(), false, "Failed to find needed asset in result balances"); + CHECK_AND_ASSERT_MES(it_native->second.total == native_tokens_to_exchange + COIN - TESTS_DEFAULT_FEE, false, "Failed to find needed asset in result balances"); + CHECK_AND_ASSERT_MES(it_asset->second.total == AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC - assets_to_exchange, false, "Failed to find needed asset in result balances"); + + + balances.clear(); + bob_wlt->balance(balances, mined); + it_asset = balances.find(asset_id); + it_native = balances.find(currency::native_coin_asset_id); + + + CHECK_AND_ASSERT_MES(it_asset != balances.end(), false, "Failed to find needed asset in result balances"); + CHECK_AND_ASSERT_MES(it_native->second.total == COIN - native_tokens_to_exchange, false, "Failed to find needed asset in result balances"); + CHECK_AND_ASSERT_MES(it_asset->second.total == AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC + assets_to_exchange, false, "Failed to find needed asset in result balances"); + + + //TODO: + // add fee paid by bob scenario + // add transfer of tokens without native coins + // different fail combination + return true; +} + diff --git a/tests/core_tests/ionic_swap_tests.h b/tests/core_tests/ionic_swap_tests.h new file mode 100644 index 00000000..576c796c --- /dev/null +++ b/tests/core_tests/ionic_swap_tests.h @@ -0,0 +1,18 @@ +// Copyright (c) 2014-2022 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once +#include "chaingen.h" +#include "wallet_tests_basic.h" +#include "random_helper.h" + +struct ionic_swap_basic_test : public wallet_test +{ + ionic_swap_basic_test(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); + +private: + random_state_test_restorer m_random_state_test_restorer; +}; + diff --git a/tests/core_tests/isolate_auditable_and_proof.cpp b/tests/core_tests/isolate_auditable_and_proof.cpp index 2bf086f5..d079a022 100644 --- a/tests/core_tests/isolate_auditable_and_proof.cpp +++ b/tests/core_tests/isolate_auditable_and_proof.cpp @@ -111,7 +111,7 @@ bool isolate_auditable_and_proof::c1(currency::core& c, size_t ev_index, const s epee::json_rpc::error je; tools::wallet_rpc_server::connection_context ctx; - tools::wallet_rpc_server miner_wlt_rpc(*auditable_test_instance); + tools::wallet_rpc_server miner_wlt_rpc(auditable_test_instance); tools::wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request req = AUTO_VAL_INIT(req); tools::wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response res = AUTO_VAL_INIT(res); req.count = 100; diff --git a/tests/core_tests/multiassets_test.cpp b/tests/core_tests/multiassets_test.cpp index 06beb95a..64b861d0 100644 --- a/tests/core_tests/multiassets_test.cpp +++ b/tests/core_tests/multiassets_test.cpp @@ -70,14 +70,14 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v std::vector destinations(2); destinations[0].addr.push_back(miner_wlt->get_account().get_public_address()); destinations[0].amount = AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC; - destinations[0].asset_id = currency::ffff_pkey; + destinations[0].asset_id = currency::null_pkey; destinations[1].addr.push_back(alice_wlt->get_account().get_public_address()); destinations[1].amount = AMOUNT_ASSETS_TO_TRANSFER_MULTIASSETS_BASIC; - destinations[1].asset_id = currency::ffff_pkey; + destinations[1].asset_id = currency::null_pkey; LOG_PRINT_MAGENTA("destinations[0].asset_id:" << destinations[0].asset_id, LOG_LEVEL_0); LOG_PRINT_MAGENTA("destinations[1].asset_id:" << destinations[1].asset_id, LOG_LEVEL_0); - LOG_PRINT_MAGENTA("currency::ffff_pkey: " << currency::ffff_pkey, LOG_LEVEL_0); + LOG_PRINT_MAGENTA("currency::null_pkey: " << currency::null_pkey, LOG_LEVEL_0); currency::transaction tx = AUTO_VAL_INIT(tx); crypto::public_key asset_id = currency::null_pkey; diff --git a/tests/core_tests/multisig_wallet_tests.cpp b/tests/core_tests/multisig_wallet_tests.cpp index ec2abfaa..acf4d959 100644 --- a/tests/core_tests/multisig_wallet_tests.cpp +++ b/tests/core_tests/multisig_wallet_tests.cpp @@ -66,8 +66,9 @@ void transfer_multisig(tools::wallet2& w, ftp.tx_version = tx_version; - - w.prepare_transaction(ctp, ftp, tx); + tools::mode_separate_context emode_separate = AUTO_VAL_INIT(emode_separate); + emode_separate.tx_for_mode_separate = tx; + w.prepare_transaction(ctp, ftp, emode_separate); crypto::secret_key sk = AUTO_VAL_INIT(sk); w.finalize_transaction(ftp, tx, sk, false); diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index ec867687..4db60da5 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -28,7 +28,7 @@ bool wallet_rpc_integrated_address::generate(std::vector& even CREATE_TEST_WALLET(miner_wlt, miner_acc, blk_0); // wallet RPC server - tools::wallet_rpc_server miner_wlt_rpc(*miner_wlt); + tools::wallet_rpc_server miner_wlt_rpc(miner_wlt); epee::json_rpc::error je; tools::wallet_rpc_server::connection_context ctx; @@ -110,7 +110,7 @@ bool wallet_rpc_integrated_address_transfer::c1(currency::core& c, size_t ev_ind std::string alice_integrated_address = get_account_address_and_payment_id_as_str(m_accounts[ALICE_ACC_IDX].get_public_address(), payment_id); // wallet RPC server - tools::wallet_rpc_server miner_wlt_rpc(*miner_wlt); + tools::wallet_rpc_server miner_wlt_rpc(miner_wlt); epee::json_rpc::error je; tools::wallet_rpc_server::connection_context ctx; @@ -226,7 +226,7 @@ bool wallet_rpc_transfer::c1(currency::core& c, size_t ev_index, const std::vect miner_wlt->refresh(); // wallet RPC server - tools::wallet_rpc_server miner_wlt_rpc(*miner_wlt); + tools::wallet_rpc_server miner_wlt_rpc(miner_wlt); epee::json_rpc::error je; tools::wallet_rpc_server::connection_context ctx; diff --git a/tests/core_tests/zarcanum_test.cpp b/tests/core_tests/zarcanum_test.cpp index 44a8a964..79d783d8 100644 --- a/tests/core_tests/zarcanum_test.cpp +++ b/tests/core_tests/zarcanum_test.cpp @@ -1,5 +1,4 @@ // Copyright (c) 2014-2022 Zano Project -// Copyright (c) 2014-2018 The Louisdor Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/zarcanum_test.h b/tests/core_tests/zarcanum_test.h index 6b7e575b..4afa4eea 100644 --- a/tests/core_tests/zarcanum_test.h +++ b/tests/core_tests/zarcanum_test.h @@ -1,5 +1,4 @@ // Copyright (c) 2014-2022 Zano Project -// Copyright (c) 2014-2018 The Louisdor Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once