git / moonbite-core / src/mweb/mweb_transact.cpp · raw
#include <mweb/mweb_transact.h>#include <mw/models/tx/PegOutCoin.h>#include <mw/wallet/TxBuilder.h>#include <key_io.h>#include <policy/policy.h>#include <util/translation.h>#include <wallet/txassembler.h>using namespace MWEB;TxType MWEB::GetTxType(const std::vector<CRecipient>& recipients, const std::set<CInputCoin>& input_coins){ assert(!recipients.empty()); static auto is_ltc = [](const CInputCoin& input) { return !input.IsMWEB(); }; static auto is_mweb = [](const CInputCoin& input) { return input.IsMWEB(); }; if (recipients.front().IsMWEB()) { // If any inputs are non-MWEB inputs, this is a peg-in transaction. // Otherwise, it's a simple MWEB-to-MWEB transaction. if (std::any_of(input_coins.cbegin(), input_coins.cend(), is_ltc)) { return TxType::PEGIN; } else { return TxType::MWEB_TO_MWEB; } } else { // If any inputs are MWEB inputs, this is a peg-out transaction. // NOTE: This does not exclude the possibility that it's also pegging-in in addition to the pegout. // Otherwise, if there are no MWEB inputs, it's a simple LTC-to-LTC transaction. if (std::any_of(input_coins.cbegin(), input_coins.cend(), is_mweb)) { return TxType::PEGOUT; } else { return TxType::LTC_TO_LTC; } }}bool MWEB::ContainsPegIn(const TxType& mweb_type, const std::set<CInputCoin>& input_coins){ if (mweb_type == MWEB::TxType::PEGIN) { return true; } if (mweb_type == MWEB::TxType::PEGOUT) { return std::any_of( input_coins.cbegin(), input_coins.cend(), [](const CInputCoin& coin) { return !coin.IsMWEB(); }); } return false;}bool MWEB::IsChangeOnMWEB(const CWallet& wallet, const MWEB::TxType& mweb_type, const std::vector<CRecipient>& recipients, const CTxDestination& dest_change){ if (mweb_type == MWEB::TxType::MWEB_TO_MWEB || mweb_type == MWEB::TxType::PEGOUT) { return true; } if (mweb_type == MWEB::TxType::PEGIN) { return dest_change.type() == typeid(CNoDestination) || dest_change.type() == typeid(StealthAddress); } return false;}uint64_t MWEB::CalcMWEBWeight(const MWEB::TxType& mweb_type, const bool change_on_mweb, const std::vector<CRecipient>& recipients){ uint64_t mweb_weight = 0; if (mweb_type == MWEB::TxType::PEGIN || mweb_type == MWEB::TxType::MWEB_TO_MWEB) { mweb_weight += mw::STANDARD_OUTPUT_WEIGHT; // MW: FUTURE - Look at actual recipients list, but for now we only support 1 MWEB recipient. } if (change_on_mweb) { mweb_weight += mw::STANDARD_OUTPUT_WEIGHT; } if (mweb_type != MWEB::TxType::LTC_TO_LTC) { CScript pegout_script = (mweb_type == MWEB::TxType::PEGOUT) ? recipients.front().GetScript() : CScript(); mweb_weight += Weight::CalcKernelWeight(true, pegout_script); } return mweb_weight;}int64_t MWEB::CalcPegOutBytes(const TxType& mweb_type, const std::vector<CRecipient>& recipients){ if (mweb_type == MWEB::TxType::PEGOUT) { CTxOut pegout_output(recipients.front().nAmount, recipients.front().receiver.GetScript()); int64_t pegout_weight = (int64_t)::GetSerializeSize(pegout_output, PROTOCOL_VERSION) * WITNESS_SCALE_FACTOR; return GetVirtualTransactionSize(pegout_weight, 0, 0); } return 0;}void Transact::AddMWEBTx(InProcessTx& new_tx){ // Add recipients std::vector<mw::Recipient> receivers; std::vector<PegOutCoin> pegouts; for (const CRecipient& recipient : new_tx.recipients) { CAmount recipient_amount = recipient.nAmount; if (recipient.fSubtractFeeFromAmount) { recipient_amount -= new_tx.total_fee; } if (recipient_amount < 0) { throw CreateTxError(_("The transaction amount is too small to pay the fee")); } if (recipient.IsMWEB()) { receivers.push_back(mw::Recipient{recipient_amount, recipient.GetMWEBAddress()}); } else { PegOutCoin pegout_recipient( recipient_amount, recipient.receiver.GetScript() ); pegouts.push_back(std::move(pegout_recipient)); } } // Lookup the change paid on the LTC side CAmount ltc_change = 0; if (new_tx.change_position != -1) { assert(new_tx.tx.vout.size() > (size_t)new_tx.change_position); ltc_change = new_tx.tx.vout[new_tx.change_position].nValue; } // Calculate pegin_amount boost::optional<CAmount> pegin_amount = boost::none; CAmount ltc_input_amount = std::accumulate( new_tx.selected_coins.cbegin(), new_tx.selected_coins.cend(), CAmount(0), [](CAmount amt, const CInputCoin& input) { return amt + (input.IsMWEB() ? 0 : input.GetAmount()); } ); if (ltc_input_amount > 0) { const CAmount ltc_fee = new_tx.total_fee - new_tx.mweb_fee; assert(ltc_fee <= ltc_input_amount); pegin_amount = (ltc_input_amount - (ltc_fee + ltc_change)); } // Add Change if (new_tx.change_on_mweb) { receivers.push_back(BuildChangeRecipient(new_tx, pegin_amount, ltc_change)); } std::vector<mw::Coin> input_coins; for (const auto& coin : new_tx.selected_coins) { if (coin.IsMWEB()) { input_coins.push_back(coin.GetMWEBCoin()); } } std::vector<mw::Coin> output_coins; try { // Create the MWEB transaction new_tx.tx.mweb_tx = TxBuilder::BuildTx( input_coins, receivers, pegouts, pegin_amount, new_tx.mweb_fee, output_coins ); } catch (std::exception& e) { throw CreateTxError(Untranslated(e.what())); } if (!output_coins.empty()) { m_wallet.GetMWWallet()->SaveToWallet(output_coins); } // TxBuilder::BuildTx only builds partial coins. // We still need to rewind them to populate any remaining fields, like address index. m_wallet.GetMWWallet()->RewindOutputs(CTransaction(new_tx.tx)); // Update pegin output auto pegins = new_tx.tx.mweb_tx.GetPegIns(); if (!pegins.empty()) { for (size_t i = 0; i < new_tx.tx.vout.size(); i++) { if (IsPegInOutput(CTransaction(new_tx.tx).GetOutput(i))) { new_tx.tx.vout[i].nValue = pegins.front().GetAmount(); new_tx.tx.vout[i].scriptPubKey = GetScriptForPegin(pegins.front().GetKernelID()); break; } } }}mw::Recipient Transact::BuildChangeRecipient(const InProcessTx& new_tx, const boost::optional<CAmount>& pegin_amount, const CAmount& ltc_change){ CAmount recipient_amount = std::accumulate( new_tx.recipients.cbegin(), new_tx.recipients.cend(), CAmount(0), [&new_tx](CAmount amount, const CRecipient& recipient) { return amount + (recipient.nAmount - (recipient.fSubtractFeeFromAmount ? new_tx.total_fee : 0)); } ); CAmount mweb_input_amount = std::accumulate( new_tx.selected_coins.cbegin(), new_tx.selected_coins.cend(), CAmount(0), [](CAmount amt, const CInputCoin& input) { return amt + (input.IsMWEB() ? input.GetAmount() : 0); } ); CAmount change_amount = (pegin_amount.value_or(0) + mweb_input_amount) - (recipient_amount + new_tx.mweb_fee + ltc_change); if (change_amount < 0) { throw CreateTxError(_("MWEB change calculation failed")); } StealthAddress change_address; if (!m_wallet.GetMWWallet()->GetStealthAddress(mw::CHANGE_INDEX, change_address)) { throw CreateTxError(_("Failed to retrieve change stealth address")); } return mw::Recipient{change_amount, change_address};}