diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 58f3eb27..b06448cd 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) @@ -2271,7 +2271,7 @@ namespace currency { 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) + 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, @@ -2317,8 +2317,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 @@ -2357,10 +2365,9 @@ namespace currency 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()) @@ -2369,6 +2376,7 @@ 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.input_amounts[i_ + input_starter_index] = source_entry.amount; } else { diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 5179e0f2..195e1cb3 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -142,6 +142,7 @@ namespace currency struct finalize_tx_param { + uint64_t unlock_time; std::vector extra; std::vector attachments; @@ -156,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 @@ -174,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() diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index 2137054b..24600d5f 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() }; //--------------------------------------------------------------- @@ -218,6 +225,7 @@ namespace currency asset_id_blinding_masks.resize(outs_count); amounts.resize(outs_count); amount_blinding_masks.resize(outs_count); + 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 input_amounts; // all inputs, including non ZC // common data: inputs crypto::point_t pseudo_out_amount_commitments_sum = crypto::c_point_0; // generate_tx_balance_proof generate_ZC_sig @@ -273,6 +282,7 @@ namespace currency 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(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) @@ -298,6 +308,7 @@ namespace currency FIELD(pseudo_outs_blinded_asset_ids) FIELD((std::vector&)(pseudo_outs_plus_real_out_blinding_masks)) FIELD(real_zc_ins_asset_ids) + FIELD(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) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ac4838f6..06eac0ae 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5011,34 +5011,38 @@ bool wallet2::build_ionic_swap_template(const wallet_public::ionic_swap_proposal construct_tx_param ctp = get_default_construct_tx_param(); ctp.fake_outputs_count = proposal_detais.mixins; - ctp.fee = 0; + 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.from.size() + proposal_detais.to.size()); + 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.from.size(); i++) + for (; i != proposal_detais.to_bob.size(); i++) { - ctp.dsts[i].amount = proposal_detais.from[i].amount; - ctp.dsts[i].amount_to_provide = proposal_detais.from[i].amount; + 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.from[i].asset_id; + 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.size(); j++, i++) + for (size_t j = 0; j != proposal_detais.to_alice.size(); j++, i++) { - ctp.dsts[i].amount = proposal_detais.to[j].amount; + 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[j].asset_id; + 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); @@ -5053,7 +5057,7 @@ bool wallet2::build_ionic_swap_template(const wallet_public::ionic_swap_proposal 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(proposal_context_blob, finalize_result.derivation); + proposal.encrypted_context = crypto::chacha_crypt(static_cast(proposal_context_blob), finalize_result.derivation); return true; } //---------------------------------------------------------------------------------------------------- @@ -5093,53 +5097,115 @@ bool wallet2::get_ionic_swap_proposal_info(const wallet_public::ionic_swap_propo 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 ammounts_to; - std::unordered_map ammounts_from; - std::vector third_party_outs; - size_t i = 0; + 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() > i, "Tx gen context has mismatch with tx(asset_ids) "); - THROW_IF_FALSE_WALLET_INT_ERR_EX(ionic_context.gen_context.asset_ids[i].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[i].m_u64[0] == o.amount, "Tx gen context has mismatch with tx(amount != amount)"); + 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[o.asset_id] += o.amount; - third_party_outs[i] = false; - i++; + 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 (!third_party_outs[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_from[asset_id] += amount; + ammounts_to_a[asset_id] += amount; } - for (const auto& a : ammounts_to) - proposal_info.to.push_back(view::asset_funds{ a.first, a.second }); - - for (const auto& a : ammounts_from) - proposal_info.from.push_back(view::asset_funds{ a.first, a.second }); - - for (const auto&in : tx.vin) + //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++) { - if (in.type() != typeid(currency::txin_zc_input)) + 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.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; - size_t mx = boost::get(in).key_offsets.size() - 1; + } + amounts_provided_by_a[in_asset_id] += amount; + if (proposal_info.mixins == 0 || proposal_info.mixins > mx) { proposal_info.mixins = mx; } + } - proposal_info.fee = currency::get_tx_fee(tx); + //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)) { @@ -5174,7 +5240,7 @@ bool wallet2::accept_ionic_swap_proposal(const wallet_public::ionic_swap_proposa this->balance(balances, mined); //validate balances needed uint64_t native_amount_required = 0; - for (const auto& item : msc.proposal_info.to) + for (const auto& item : msc.proposal_info.to_alice) { if (balances[item.asset_id].unlocked < item.amount) { @@ -5188,9 +5254,9 @@ bool wallet2::accept_ionic_swap_proposal(const wallet_public::ionic_swap_proposa // balances is ok, check if fee is added to tx uint64_t additional_fee = 0; - if (msc.proposal_info.fee < m_core_runtime_config.tx_default_fee) + 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; + 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; @@ -5540,11 +5606,16 @@ assets_selection_context wallet2::get_needed_money(uint64_t fee, const std::vect 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); } @@ -6253,7 +6324,7 @@ void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx 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"); - for (const auto& el : mode_separatemode_separate.proposal_info.to) + for (const auto& el : mode_separatemode_separate.proposal_info.to_alice) { needed_money_map[el.asset_id].needed_amount += el.amount; } diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 411413cf..75539c96 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -1195,18 +1195,18 @@ namespace wallet_public struct ionic_swap_proposal_info { - std::vector from; - std::vector to; + 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; + uint64_t fee_paid_by_a; uint64_t expiration_time; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(from) - KV_SERIALIZE(to) + KV_SERIALIZE(to_bob) + KV_SERIALIZE(to_alice) KV_SERIALIZE(mixins) - KV_SERIALIZE(fee) + KV_SERIALIZE(fee_paid_by_a) KV_SERIALIZE(expiration_time) END_KV_SERIALIZE_MAP() diff --git a/tests/core_tests/ionic_swap_tests.cpp b/tests/core_tests/ionic_swap_tests.cpp index c7aa91a6..0fe5f725 100644 --- a/tests/core_tests/ionic_swap_tests.cpp +++ b/tests/core_tests/ionic_swap_tests.cpp @@ -29,7 +29,12 @@ ionic_swap_basic_test::ionic_swap_basic_test() bool ionic_swap_basic_test::generate(std::vector& events) const { - uint64_t ts = test_core_time::get_time(); + // 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); @@ -126,19 +131,23 @@ bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::ve //alice_wlt want to trade with miner_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 = TESTS_DEFAULT_FEE; + proposal_details.fee_paid_by_a = TESTS_DEFAULT_FEE; proposal_details.mixins = 10; - proposal_details.from.push_back(view::asset_funds{ asset_id , assets_to_exchange }); - proposal_details.to.push_back(view::asset_funds{ currency::native_coin_asset_id , native_tokens_to_exchange }); + 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, miner_wlt->get_account().get_public_address(), proposal); - view::ionic_swap_proposal_info proposal_decoded_info; + view::ionic_swap_proposal_info proposal_decoded_info = AUTO_VAL_INIT(proposal_decoded_info); miner_wlt->get_ionic_swap_proposal_info(proposal, proposal_decoded_info); //Validate proposal - if (proposal_decoded_info.from != proposal_details.from || proposal_decoded_info.to != proposal_details.to) + 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"); } @@ -171,6 +180,12 @@ bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::ve 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 == mined_balance - 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 index 389191ae..576c796c 100644 --- a/tests/core_tests/ionic_swap_tests.h +++ b/tests/core_tests/ionic_swap_tests.h @@ -4,12 +4,15 @@ #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; };