diff --git a/src/blsct/wallet/txfactory.cpp b/src/blsct/wallet/txfactory.cpp index 978e712b0ce75..1c9384df6f689 100644 --- a/src/blsct/wallet/txfactory.cpp +++ b/src/blsct/wallet/txfactory.cpp @@ -13,15 +13,23 @@ using Scalars = Elements; namespace blsct { -void TxFactoryBase::AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id, const CreateTransactionType& type, const CAmount& minStake) +void TxFactoryBase::AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id, const CreateTransactionType& type, const CAmount& minStake, const bool& fSubtractFeeFromAmount) { UnsignedOutput out; + out = CreateOutput(destination.GetKeys(), nAmount, sMemo, token_id, Scalar::Rand(), type, minStake); + CAmount nFee = 0; + + if (fSubtractFeeFromAmount) { + nFee = GetTransactioOutputWeight(out.out) * BLSCT_DEFAULT_FEE; + out = CreateOutput(destination.GetKeys(), nAmount - nFee, sMemo, token_id, Scalar::Rand(), type, minStake); + }; + if (nAmounts.count(token_id) == 0) nAmounts[token_id] = {0, 0}; - nAmounts[token_id].nFromOutputs += nAmount; + nAmounts[token_id].nFromOutputs += nAmount - nFee; if (vOutputs.count(token_id) == 0) vOutputs[token_id] = std::vector(); @@ -47,55 +55,77 @@ bool TxFactoryBase::AddInput(const CAmount& amount, const MclScalar& gamma, cons std::optional TxFactoryBase::BuildTx(const blsct::DoublePublicKey& changeDestination, const CAmount& minStake, const CreateTransactionType& type, const bool& fSubtractedFee) { - CAmount nFee = BLSCT_DEFAULT_FEE * (vInputs.size() + vOutputs.size()); + this->tx = CMutableTransaction(); + + std::vector outputSignatures; + Scalar outputGammas; + CAmount nFee = 0; + + for (auto& out_ : vOutputs) { + for (auto& out : out_.second) { + this->tx.vout.push_back(out.out); + outputGammas = outputGammas - out.gamma; + outputSignatures.push_back(PrivateKey(out.blindingKey).Sign(out.out.GetHash())); + } + } while (true) { - CMutableTransaction tx; + CMutableTransaction tx = this->tx; tx.nVersion |= CTransaction::BLSCT_MARKER; - Scalar gammaAcc; + Scalar gammaAcc = outputGammas; std::map mapChange; - std::vector txSigs; - - for (auto& amounts : nAmounts) { - if (amounts.second.nFromInputs < amounts.second.nFromOutputs + nFee) - return std::nullopt; - mapChange[amounts.first] = amounts.second.nFromInputs - amounts.second.nFromOutputs - nFee; - } + std::map mapInputs; + std::vector txSigs = outputSignatures; for (auto& in_ : vInputs) { + auto tokenFee = (in_.first == TokenId() ? nFee : 0); + for (auto& in : in_.second) { tx.vin.push_back(in.in); gammaAcc = gammaAcc + in.gamma; txSigs.push_back(in.sk.Sign(in.in.GetHash())); + + if (!mapInputs[in_.first]) mapInputs[in_.first] = 0; + + mapInputs[in_.first] += in.value.GetUint64(); + + if (mapInputs[in_.first] > nAmounts[in_.first].nFromOutputs + nFee) break; } } - for (auto& out_ : vOutputs) { - for (auto& out : out_.second) { - tx.vout.push_back(out.out); - gammaAcc = gammaAcc - out.gamma; - txSigs.push_back(PrivateKey(out.blindingKey).Sign(out.out.GetHash())); - } + for (auto& amounts : nAmounts) { + auto tokenFee = (amounts.first == TokenId() ? nFee : 0); + + auto nFromInputs = mapInputs[amounts.first]; + + if (nFromInputs < amounts.second.nFromOutputs + tokenFee) return std::nullopt; + + mapChange[amounts.first] = nFromInputs - amounts.second.nFromOutputs - tokenFee; } for (auto& change : mapChange) { if (change.second == 0) continue; + auto changeOutput = CreateOutput(changeDestination, change.second, "Change", change.first, MclScalar::Rand(), type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE ? STAKED_COMMITMENT : NORMAL, minStake); - tx.vout.push_back(changeOutput.out); + gammaAcc = gammaAcc - changeOutput.gamma; + + tx.vout.push_back(changeOutput.out); txSigs.push_back(PrivateKey(changeOutput.blindingKey).Sign(changeOutput.out.GetHash())); } - if (nFee == (long long)(BLSCT_DEFAULT_FEE * (tx.vin.size() + tx.vout.size()))) { + if (nFee == GetTransactionWeight(CTransaction(tx)) * BLSCT_DEFAULT_FEE) { CTxOut fee_out{nFee, CScript(OP_RETURN)}; + tx.vout.push_back(fee_out); txSigs.push_back(PrivateKey(gammaAcc).SignBalance()); tx.txSig = Signature::Aggregate(txSigs); + return tx; } - nFee = BLSCT_DEFAULT_FEE * (tx.vin.size() + tx.vout.size()); + nFee = GetTransactionWeight(CTransaction(tx)) * BLSCT_DEFAULT_FEE; } return std::nullopt; @@ -108,45 +138,31 @@ std::optional TxFactoryBase::CreateTransaction(const std::v if (type == STAKED_COMMITMENT) { CAmount inputFromStakedCommitments = 0; - for (const auto& output : inputCandidates) { - if (!output.is_staked_commitment) - continue; - - tx.AddInput(output.amount, output.gamma, output.spendingKey, output.token_id, COutPoint(output.outpoint.hash, output.outpoint.n)); - inputFromStakedCommitments += output.amount; - } for (const auto& output : inputCandidates) { if (output.is_staked_commitment) - continue; + inputFromStakedCommitments += output.amount; + if (!output.is_staked_commitment) + inAmount += output.amount; tx.AddInput(output.amount, output.gamma, output.spendingKey, output.token_id, COutPoint(output.outpoint.hash, output.outpoint.n)); - - inAmount += output.amount; - - if (tx.nAmounts[token_id].nFromInputs - inputFromStakedCommitments > nAmount + (long long)(BLSCT_DEFAULT_FEE * (tx.vInputs.size() + 2))) - break; } if (nAmount + inputFromStakedCommitments < minStake) { throw std::runtime_error(strprintf("A minimum of %s is required to stake", FormatMoney(minStake))); } - tx.AddOutput(destination, nAmount + inputFromStakedCommitments, sMemo, token_id, type, minStake); + bool fSubtractFeeFromAmount = false; // nAmount == inAmount + inputFromStakedCommitments; + + tx.AddOutput(destination, nAmount + inputFromStakedCommitments, sMemo, token_id, type, minStake, fSubtractFeeFromAmount); } else { for (const auto& output : inputCandidates) { tx.AddInput(output.amount, output.gamma, output.spendingKey, output.token_id, COutPoint(output.outpoint.hash, output.outpoint.n)); - inAmount += output.amount; - if (tx.nAmounts[token_id].nFromInputs > nAmount + (long long)(BLSCT_DEFAULT_FEE * (tx.vInputs.size() + 2))) break; } - CAmount subtract = 0; - bool fChangeNeeded = inAmount > nAmount; - - if (type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE) - subtract = (BLSCT_DEFAULT_FEE * (tx.vInputs.size() + 1 + fChangeNeeded)); + bool fSubtractFeeFromAmount = false; // type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE; - tx.AddOutput(destination, nAmount - subtract, sMemo, token_id, type, minStake); + tx.AddOutput(destination, nAmount, sMemo, token_id, type, minStake, fSubtractFeeFromAmount); } return tx.BuildTx(changeDestination, minStake, type); diff --git a/src/blsct/wallet/txfactory.h b/src/blsct/wallet/txfactory.h index 9e727a79a0e13..f1ddaec4f50d0 100644 --- a/src/blsct/wallet/txfactory.h +++ b/src/blsct/wallet/txfactory.h @@ -35,7 +35,7 @@ class TxFactoryBase public: TxFactoryBase(){}; - void AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id = TokenId(), const CreateTransactionType& type = NORMAL, const CAmount& minStake = 0); + void AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id = TokenId(), const CreateTransactionType& type = NORMAL, const CAmount& minStake = 0, const bool& fSubtractFeeFromAmount = false); bool AddInput(const CAmount& amount, const MclScalar& gamma, const blsct::PrivateKey& spendingKey, const TokenId& token_id, const COutPoint& outpoint, const bool& rbf = false); std::optional BuildTx(const blsct::DoublePublicKey& changeDestination, const CAmount& minStake = 0, const CreateTransactionType& type = NORMAL, const bool& fSubtractedFee = false); static std::optional CreateTransaction(const std::vector& inputCandidates, const blsct::DoublePublicKey& changeDestination, const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id = TokenId(), const CreateTransactionType& type = NORMAL, const CAmount& minStake = 0); diff --git a/src/blsct/wallet/txfactory_global.h b/src/blsct/wallet/txfactory_global.h index 9252cdf5e533f..86fa31ab71f02 100644 --- a/src/blsct/wallet/txfactory_global.h +++ b/src/blsct/wallet/txfactory_global.h @@ -15,7 +15,7 @@ using Points = Elements; using Scalar = T::Scalar; using Scalars = Elements; -#define BLSCT_DEFAULT_FEE 200000 +#define BLSCT_DEFAULT_FEE 50 namespace blsct { struct UnsignedOutput { @@ -68,6 +68,15 @@ enum CreateTransactionType { CTransactionRef AggregateTransactions(const std::vector& txs); UnsignedOutput CreateOutput(const blsct::DoublePublicKey& destination, const CAmount& nAmount, std::string sMemo, const TokenId& tokenId = TokenId(), const Scalar& blindingKey = Scalar::Rand(), const CreateTransactionType& type = NORMAL, const CAmount& minStake = 0); +int32_t GetTransactionWeight(const CTransaction& tx) +{ + return ::GetSerializeSize(TX_WITH_WITNESS(tx)); +} + +int32_t GetTransactioOutputWeight(const CTxOut& out) +{ + return ::GetSerializeSize(out); +} } // namespace blsct #endif // TXFACTORY_GLOBAL_H