std::map<uint160, int64_t> valueMap;
CCurrencyValueMap() {}
+ CCurrencyValueMap(const CCurrencyValueMap &operand) : valueMap(operand.valueMap) {}
CCurrencyValueMap(const std::map<uint160, int64_t> &vMap) : valueMap(vMap) {}
CCurrencyValueMap(const std::vector<uint160> ¤cyIDs, const std::vector<int64_t> &amounts);
CCurrencyValueMap(const UniValue &uni);
friend CCurrencyValueMap operator-(const CCurrencyValueMap& a, int b);
friend CCurrencyValueMap operator*(const CCurrencyValueMap& a, int b);
+ const CCurrencyValueMap &operator=(const CCurrencyValueMap& operand)
+ {
+ valueMap = operand.valueMap;
+ return *this;
+ }
const CCurrencyValueMap &operator-=(const CCurrencyValueMap& operand);
const CCurrencyValueMap &operator+=(const CCurrencyValueMap& operand);
}
bool CConnectedChains::CreateNextExport(const CCurrencyDefinition &_curDef,
+ const std::pair<int, CInputDescriptor> &lastExportInput,
+ const std::vector<CInputDescriptor> &reserveDeposits,
const std::vector<ChainTransferData> _txInputs,
+ const CTxDestination &feeOutput,
uint32_t nHeight,
TransactionBuilder &exportBuilder,
int32_t &inputsConsumed,
const CPBaaSNotarization &lastNotarization,
CPBaaSNotarization &newNotarization,
- CCurrencyValueMap *arbitragePrices,
- CCurrencyValueMap *arbitrageFunds,
- CCurrencyValueMap *arbitrageThresholds,
- bool *inputTxAdded,
- CTransaction *newInputTx)
+ const ChainTransferData *addInputTx)
{
// this function accepts all reserve transfer inputs to a particular currency destination,
// from a particular block as well as a height, which is used as a limiting factor in the
// blocks, it must be selected from the first block containing chain transfers,
// following the last block from which chain transfers, are present in the export.
//
- // If the input tx boolean, new input tx pointer, and arbitrage information is included,
- // this function may create a new transaction and conversion input, which is added to the
- // export transaction created.
+ // If the addInputTx is included, this function will add it to the export transaction created.
+
+ uint160 currencyID = _curDef.GetID();
+
+ // get the inputs to include
+ std::vector<ChainTransferData> txInputs;
+ uint32_t curHeight = _txInputs.size() ? std::get<0>(_txInputs[0]) : nHeight;
+ for (int inputNum = 0; inputNum < _txInputs.size(); inputNum++)
+ {
+ uint32_t nextHeight = std::get<0>(_txInputs[inputNum]);
+ if (curHeight != nextHeight)
+ {
+ // if we have skipped to the next block, and we have enough to make an export, we cannot take any more
+ // except the optional one to add
+ if (inputNum >= CCrossChainExport::MIN_INPUTS)
+ {
+ break;
+ }
+ txInputs.push_back(_txInputs[inputNum]);
+ }
+ }
+
+ // we will consume this many inputs from the list
+ int numInputs = txInputs.size();
+
+ // check to see if we need to add the optional input, not counted as "consumed"
+ if (addInputTx)
+ {
+ uint32_t rtHeight = std::get<0>(*addInputTx);
+ CReserveTransfer reserveTransfer = std::get<2>(*addInputTx);
+ // ensure that any pre-conversions or conversions are all valid, based on mined height and
+ if (reserveTransfer.IsPreConversion())
+ {
+ printf("%s: Invalid optional pre-conversion\n", __func__);
+ LogPrintf("%s: Invalid optional pre-conversion\n", __func__);
+ }
+ else if (reserveTransfer.IsConversion() && rtHeight < _curDef.startBlock)
+ {
+ printf("%s: Invalid optional conversion added before start block\n", __func__);
+ LogPrintf("%s: Invalid optional conversion added before start block\n", __func__);
+ }
+ else
+ {
+ txInputs.push_back(*addInputTx);
+ }
+ }
+
+ // if we have no inputs, including the optional one, we are done
+ if (txInputs.size() == 0)
+ {
+ return false;
+ }
+
+ // now we have all inputs to use. make an export and new notarization from the old notarization
+
+ // inputs can be:
+ // 1. transfers of reserve or tokens for fractional reserve chains
+ // 2. pre-conversions for pre-launch participation in the premine
+ // 3. reserve market conversions
+ //
+ CAmount exportOutVal = lastExportInput.second.nValue; // native export output amount
+
+ // add the last export input, then reserve deposits, then txInputs to the new export
+ exportBuilder.AddTransparentInput(COutPoint(lastExportInput.second.txIn.prevout.hash, lastExportInput.second.txIn.prevout.n),
+ lastExportInput.second.scriptPubKey,
+ lastExportInput.second.nValue);
+
+ // combine any reserve deposits from the last export
+ // into the reserve deposit inputs for this export and spend them
+ CCurrencyValueMap currentReserveDeposits;
+ CAmount nativeReserveDeposit = 0;
+
+ for (auto &oneReserveDeposit : reserveDeposits)
+ {
+ COptCCParams p;
+ currentReserveDeposits += oneReserveDeposit.scriptPubKey.ReserveOutValue(p);
+ assert(p.evalCode == EVAL_RESERVE_DEPOSIT);
+ if (oneReserveDeposit.nValue)
+ {
+ nativeReserveDeposit += oneReserveDeposit.nValue;
+ }
+ exportBuilder.AddTransparentInput(COutPoint(oneReserveDeposit.txIn.prevout.hash, oneReserveDeposit.txIn.prevout.n),
+ oneReserveDeposit.scriptPubKey,
+ oneReserveDeposit.nValue);
+ }
+
+ CCurrencyValueMap totalTxFees; // all fees taken from reserveTransfers
+ CCurrencyValueMap totalAmounts; // total amounts exported
+ std::vector<CBaseChainObject *> chainObjects; // exports that will be part of the opRet
+ std::vector<CReserveTransfer *> preConversions; // if we have pre-conversions, store them here
+
+ for (int i = 0; i < txInputs.size(); i++)
+ {
+ uint32_t rtHeight = std::get<0>(txInputs[i]);
+ CInputDescriptor inputDesc = std::get<1>(txInputs[i]);
+ CReserveTransfer reserveTransfer = std::get<2>(txInputs[i]);
+
+ // ensure that any pre-conversions or conversions are all valid, based on mined height and
+ if (reserveTransfer.IsPreConversion())
+ {
+ //
+ if (rtHeight >= _curDef.startBlock)
+ {
+ printf("%s: Invalid pre-conversion, mined after start block\n", __func__);
+ LogPrintf("%s: Invalid pre-conversion, mined after start block\n", __func__);
+ reserveTransfer = reserveTransfer.GetRefundTransfer();
+ }
+ else
+ {
+ preConversions.push_back(&reserveTransfer);
+ }
+ }
+ else if (reserveTransfer.IsConversion() && rtHeight < _curDef.startBlock)
+ {
+ printf("%s: Invalid conversion, mined before start block\n", __func__);
+ LogPrintf("%s: Invalid conversion, mined before start block\n", __func__);
+ reserveTransfer = reserveTransfer.GetRefundTransfer();
+ }
+
+ exportBuilder.AddTransparentInput(inputDesc.txIn.prevout, inputDesc.scriptPubKey, inputDesc.nValue, inputDesc.txIn.nSequence);
+ CCurrencyValueMap newTransferInput = inputDesc.scriptPubKey.ReserveOutValue();
+ newTransferInput.valueMap[ASSETCHAINS_CHAINID] = inputDesc.nValue;
+
+ totalTxFees += reserveTransfer.CalculateFee(reserveTransfer.flags, reserveTransfer.nValue, _curDef.systemID);
+ totalAmounts += newTransferInput;
+ chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(reserveTransfer), reserveTransfer));
+ }
+
+ // make a fee output as placeholder
+ CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
+ _curDef.systemID, 0, 0, _curDef.systemID, DestinationToTransferDestination(feeOutput));
+ chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
+
+ //printf("%s: total export amounts:\n%s\n", __func__, totalAmounts.ToUniValue().write().c_str());
+ CCrossChainExport ccx(currencyID, txInputs.size(), totalAmounts.CanonicalMap(), totalTxFees.CanonicalMap());
+
+ CAmount nativeExportAmount = 0;
+ if (ccx.totalAmounts.valueMap.count(ASSETCHAINS_CHAINID))
+ {
+ nativeExportAmount = ccx.totalAmounts.valueMap[ASSETCHAINS_CHAINID];
+ nativeReserveDeposit += nativeExportAmount;
+ currentReserveDeposits += ccx.totalAmounts;
+ currentReserveDeposits.valueMap.erase(ASSETCHAINS_CHAINID);
+ }
+ else
+ {
+ currentReserveDeposits += ccx.totalAmounts;
+ }
+
+ if (chainObjects.size())
+ {
+ CScript opRet = StoreOpRetArray(chainObjects);
+ DeleteOpRetObjects(chainObjects);
+
+ CCcontract_info CC;
+ CCcontract_info *cp;
+
+ if (nativeReserveDeposit || currentReserveDeposits.valueMap.size())
+ {
+ CScript opRet = StoreOpRetArray(chainObjects);
+ DeleteOpRetObjects(chainObjects);
+
+ // now send transferred currencies to a reserve deposit
+ cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
+
+ // send the entire amount to a reserve deposit output of the specific chain
+ // we receive our fee on the other chain, when it comes back, or if a token,
+ // when it gets imported back to the chain
+ std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
+ CReserveDeposit rd = CReserveDeposit(currencyID, currentReserveDeposits);
+ exportBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CReserveDeposit>(EVAL_RESERVE_DEPOSIT, dests, 1, &rd)),
+ nativeReserveDeposit);
+ }
+
+ cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
+
+ // send native amount of zero to a cross chain export output of the specific chain
+ std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
+
+ exportBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx)),
+ exportOutVal);
+
+ // when exports are confirmed as having been imported, they are finalized
+ // until then, a finalization UTXO enables an index search to only find transactions
+ // that have work to complete on this chain, or have not had their cross-chain import
+ // acknowledged
+ cp = CCinit(&CC, EVAL_FINALIZE_EXPORT);
+ CTransactionFinalization finalization(CTransactionFinalization::FINALIZE_EXPORT, currencyID, 0);
+
+ //printf("%s: Finalizing export with index dest %s\n", __func__, EncodeDestination(CKeyID(CCrossChainRPCData::GetConditionID(lastChainDef.systemID, EVAL_FINALIZE_EXPORT))).c_str());
+
+ dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
+ exportBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_EXPORT, dests, 1, &finalization)), 0);
+
+ exportBuilder.AddOpRet(opRet);
+ exportBuilder.SetFee(0);
+
+ CCrossChainExport lastCCX;
+ COptCCParams p;
+
+ if (!(lastExportInput.second.scriptPubKey.IsPayToCryptoCondition(p) &&
+ p.IsValid() &&
+ p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
+ p.vData.size() &&
+ (lastCCX = CCrossChainExport(p.vData[0])).IsValid()))
+ {
+ printf("%s: ERROR: Invalid last export input for %s currency\n", __func__, _curDef.name.c_str());
+ LogPrintf("%s: ERROR: Invalid last export input for %s currency\n", __func__, _curDef.name.c_str());
+ return false;
+ }
+
+ CTransaction lastImport;
+ CPartialTransactionProof exportProof;
+ CCrossChainImport ccImport;
+ CCrossChainExport ccExport;
+ if (!GetLastImport(currencyID, lastImport, exportProof, ccImport, ccExport))
+ {
+ printf("%s: ERROR: Cannot find last import transaction for %s currency\n", __func__, _curDef.name.c_str());
+ LogPrintf("%s: ERROR: Cannot find last import transaction for %s currency\n", __func__, _curDef.name.c_str());
+ return false;
+ }
+
+ //get new notarization, as of when this imports
+ CTransaction simulatedExport(exportBuilder.mtx);
+ CMutableTransaction mtx;
+ CCoinbaseCurrencyState newState;
+ CCoinbaseCurrencyState oldState = lastNotarization.currencyState;
+ CPBaaSNotarization newImportNotarization;
+ if (NewImportNotarization(_curDef, nHeight, lastImport, nHeight, simulatedExport, mtx, oldState, newState))
+ {
+ newImportNotarization = CPBaaSNotarization(mtx);
+ // check if we have gone over the currency limit or maximum pre-conversion contribution
+ // if over on conversion or pre-conversion contribution, determine how much we need to remove
+ // and loop through to remove it
+
+ // if we are still calculating pre-conversion, check against pre-conversion amounts
+
+ // if post start, ensure that resulting currency supply is at or under the maximum
+ // if not, loop through to remove what is needed, largest first
+ }
+
+ /* UniValue uni(UniValue::VOBJ);
+ TxToUniv(exportBuilder.mtx, uint256(), uni);
+ printf("%s: about to send reserve deposits with tx:\n%s\n", __func__, uni.write(1,2).c_str()); */
+
+ // we have build a transaction builder and provided a new notarization for analysis or use
+ return true;
+ }
+
return false;
}
CCrossChainExport &ccCrossExport);
bool CreateNextExport(const CCurrencyDefinition &_curDef,
- const std::vector<ChainTransferData> _txInputs,
+ const std::pair<int, CInputDescriptor> &lastExportInput,
+ const std::vector<CInputDescriptor> &reserveDeposits,
+ const std::vector<ChainTransferData> txInputs,
+ const CTxDestination &feeOutput,
uint32_t nHeight,
TransactionBuilder &exportBuilder,
int32_t &inputsConsumed,
const CPBaaSNotarization &lastNotarization,
CPBaaSNotarization &newNotarization,
- CCurrencyValueMap *arbitragePrices=nullptr,
- CCurrencyValueMap *arbitrageFunds=nullptr,
- CCurrencyValueMap *arbitrageThresholds=nullptr,
- bool *inputTxAdded=nullptr,
- CTransaction *newInputTx=nullptr);
+ const ChainTransferData *addInputTx=nullptr);
// returns newly created import transactions to the specified chain from exports on this chain specified chain
bool CreateLatestImports(const CCurrencyDefinition &chainDef,
return retVal;
}
-CReserveTransfer RefundExport(const CBaseChainObject *objPtr)
+CReserveTransfer CReserveTransfer::GetRefundTransfer() const
{
- if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
+ CReserveTransfer rt = *this;
+
+ // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
+ if (rt.destination.type == CTransferDestination::DEST_FULLID)
{
- CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)objPtr)->object;
+ CIdentity(rt.destination.destination);
+ rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
+ }
- // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
- if (rt.destination.type == CTransferDestination::DEST_FULLID)
- {
- CIdentity(rt.destination.destination);
- rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
- }
+ // turn it into a normal transfer, which will create an unconverted output
+ rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
- // turn it into a normal transfer, which will create an unconverted output
- rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
+ if (rt.flags & (CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY))
+ {
+ rt.flags &= ~(CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY);
+ rt.nValue = 0;
+ }
+ rt.destCurrencyID = rt.currencyID;
+ return rt;
+}
- if (rt.flags & (CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY))
- {
- rt.flags &= ~(CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY);
- rt.nValue = 0;
- }
- rt.destCurrencyID = rt.currencyID;
- return rt;
+CReserveTransfer RefundExport(const CBaseChainObject *objPtr)
+{
+ if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
+ {
+ return ((CChainObject<CReserveTransfer> *)objPtr)->object.GetRefundTransfer();
}
return CReserveTransfer();
}
BURN_CHANGE_WEIGHT = 0x100, // this output is being burned on import and will change the reserve ratio
IMPORT_TO_SOURCE = 0x200, // set when the source currency, not destination is the import currency
RESERVE_TO_RESERVE = 0x400, // for arbitrage or transient conversion, 2 stage solving (2nd from new fractional to reserves)
- FEE_SOURCE_NATIVE = 0x800, // for arbitrage or transient conversion, 2 stage solving (2nd from new fractional to reserves)
- FEE_DEST_NATIVE = 0x1000 // for arbitrage or transient conversion, 2 stage solving (2nd from new fractional to reserves)
+ FEE_SOURCE_NATIVE = 0x800, // fee is paid in native currency of source system
+ FEE_DEST_NATIVE = 0x1000, // fee is paid in native currency of destination system
+ REFUND = 0x2000 // this transfer should be refunded, individual property when conversions exceed limits
};
enum EConstants
{
return flags & RESERVE_TO_RESERVE;
}
+
+ CReserveTransfer GetRefundTransfer() const;
};
class CReserveDeposit : public CMultiOutput
int commonNum = currencyIDs.size() >= amounts.size() ? amounts.size() : currencyIDs.size();
for (int i = 0; i < commonNum; i++)
{
- valueMap[currencyIDs[i]] = amounts[i];
+ if (amounts[i])
+ {
+ valueMap[currencyIDs[i]] = amounts[i];
+ }
}
}
{
// to be less than means, in this order:
// 1. To have fewer non-zero currencies.
- // 2. If not fewer currencies, to be unable to be subtracted from the one being checked
- // without creating negative values
+ // 2. If not fewer currencies, all present currencies must be less in a than b
if (!a.valueMap.size() && !b.valueMap.size())
{
return false;
}
+
bool isaltb = false;
+ // ensure that we are smaller than all those present in b
for (auto &oneVal : b.valueMap)
{
if (oneVal.second)
{
auto it = a.valueMap.find(oneVal.first);
- if (it == a.valueMap.end() || it->second < oneVal.second)
+
+ // negative is less than not present, which is equivalent to 0
+ if ((it == a.valueMap.end() && oneVal.second > 0) || (it != a.valueMap.end() && it->second < oneVal.second))
{
isaltb = true;
}
}
}
+
+ // ensure that for all the currencies we have, b does not have less
+ for (auto &oneVal : a.valueMap)
+ {
+ if (oneVal.second)
+ {
+ auto it = b.valueMap.find(oneVal.first);
+
+ if ((it == b.valueMap.end() && oneVal.second > 0) || (it != b.valueMap.end() && it->second < oneVal.second))
+ {
+ isaltb = false;
+ }
+ }
+ }
return isaltb;
}
bool operator<=(const CCurrencyValueMap& a, const CCurrencyValueMap& b)
{
- return (a < b) || (a == b);
+ // to be less or equal to than means, in this order:
+ // 1. To have fewer non-zero or both zero currencies.
+ // 2. If not fewer or both zero currencies, all present currencies must be less or equal to in a with respect to b
+ if (!a.valueMap.size() && !b.valueMap.size())
+ {
+ return true;
+ }
+
+ bool isalteb = false;
+
+ // ensure that we are smaller than all those present in b
+ for (auto &oneVal : b.valueMap)
+ {
+ if (oneVal.second)
+ {
+ auto it = a.valueMap.find(oneVal.first);
+
+ if ((it == a.valueMap.end() && oneVal.second >= 0) || (it != a.valueMap.end() && it->second <= oneVal.second))
+ {
+ isalteb = true;
+ }
+ }
+ }
+
+ // ensure that for all the currencies we have, b has equal or more
+ for (auto &oneVal : a.valueMap)
+ {
+ if (oneVal.second)
+ {
+ auto it = b.valueMap.find(oneVal.first);
+
+ if ((it == b.valueMap.end() && oneVal.second > 0) || (it != b.valueMap.end() && it->second < oneVal.second))
+ {
+ isalteb = false;
+ }
+ }
+ }
+ return isalteb;
}
bool operator>=(const CCurrencyValueMap& a, const CCurrencyValueMap& b)
CCurrencyValueMap CCurrencyValueMap::NonIntersectingValues(const CCurrencyValueMap& operand) const
{
- CCurrencyValueMap retVal = operand;
+ CCurrencyValueMap retVal = *this;
if (valueMap.size() && operand.valueMap.size())
{
for (auto &oneVal : valueMap)
{
- auto it = operand.valueMap.find(oneVal.first);
- if (it != operand.valueMap.end())
+ if (oneVal.second)
{
- if (it->second > 0 && oneVal.second > 0)
+ auto it = operand.valueMap.find(oneVal.first);
+ if (it != operand.valueMap.end() && it->second != 0)
{
- retVal.valueMap.erase(it);
+ retVal.valueMap.erase(it->first);
}
}
+ else
+ {
+ retVal.valueMap.erase(oneVal.first);
+ }
}
}
return retVal;
// subtract, but do not subtract to negative values
CCurrencyValueMap CCurrencyValueMap::SubtractToZero(const CCurrencyValueMap& operand) const
{
- CCurrencyValueMap retVal = *this;
+ CCurrencyValueMap retVal(*this);
std::vector<uint160> toRemove;
if (valueMap.size() && operand.valueMap.size())
{
auto it = operand.valueMap.find(oneVal.first);
if (it != operand.valueMap.end())
{
- oneVal.second = oneVal.second - it->second;
+ oneVal.second -= it->second;
if (oneVal.second <= 0)
{
toRemove.push_back(oneVal.first);
}
for (auto tIn : tIns) {
change += tIn.value;
+ printf("tIn.scriptPubKey.ReserveOutValue():\n%s\n", tIn.scriptPubKey.ReserveOutValue().ToUniValue().write(1,2).c_str());
reserveChange += tIn.scriptPubKey.ReserveOutValue();
}
if (reserveChange.valueMap.size())
}
}
+ // printf("Transparent funds, have %s, need %s", t_all_inputs_total.ToUniValue().write(1,2).c_str(), targetAllAmounts.ToUniValue().write(1,2).c_str());
+
t_all_inputs_total.valueMap[ASSETCHAINS_CHAINID] = t_inputs_total;
t_all_inputs_total = t_all_inputs_total.CanonicalMap();
// sanity check, since we don't always hold locks
for (int i = 0 ; i < t_inputs_.size(); i++)
{
- if (!pwalletMain->mapWallet.count(t_inputs_txids_[i]))
+ auto pit = pwalletMain->mapWallet.find(t_inputs_txids_[i]);
+ if (pit == pwalletMain->mapWallet.end() || t_inputs_[i].tx != &pit->second)
{
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet transaction was deleted from wallet after selection and before tx was made.");
}
success =
pwalletMain->SelectReserveCoinsMinConf(targetReserveAmounts, targetNativeAmount, 0, 0, t_inputs_, setCoinsRet, reserveValueRet, nativeValueRet);
+
+ /* if (success)
+ {
+ printf("value returned:\n%s, native: %s\n", reserveValueRet.ToUniValue().write(1,2).c_str(), ValueFromAmount(nativeValueRet).write(1,2).c_str());
+ for (auto &oneOutput : setCoinsRet)
+ {
+ printf("Output %s : %d, for native: %s\n reserve: %s\n\n",
+ oneOutput.first->GetHash().GetHex().c_str(),
+ oneOutput.second,
+ ValueFromAmount(oneOutput.first->vout[oneOutput.second].nValue).write().c_str(),
+ oneOutput.first->vout[oneOutput.second].ReserveOutValue().ToUniValue().write(1,2).c_str());
+ }
+ }
+ else
+ {
+ printf("%s: selection failed\n", __func__);
+ } */
}
if (!success)
if (selectedUTXOAmount >= targetNativeAmount) {
// Select another utxo if there is change less than the dust threshold.
dustChange = selectedUTXOAmount - targetNativeAmount;
- if (dustChange == 0 || dustChange >= dustThreshold) {
- break;
- }
}
}
}
}
- const CScript &scriptPubKey = out.tx->vout[out.i].scriptPubKey;
-
t_inputs_.push_back(out);
- t_inputs_txids_.push_back(out.tx->GetHash());
}
// sort in ascending order, so smaller utxos appear first
return ( i.tx->vout[i.i].nValue < j.tx->vout[j.i].nValue );
});
+ // get txids for verification between holding locks
+ for (auto &out : t_inputs_)
+ {
+ t_inputs_txids_.push_back(out.tx->GetHash());
+ }
+
return t_inputs_.size() > 0;
}
}
};
-struct CompareValueMap
+class CompareValueMap
{
- bool operator()(const CCurrencyValueMap &m1,
- const CCurrencyValueMap &m2) const
+public:
+ CCurrencyValueMap totalTargetValues;
+ CompareValueMap() {}
+ CompareValueMap(const CCurrencyValueMap &targetValues) : totalTargetValues(targetValues) {}
+ bool CompareMaps(const CCurrencyValueMap &m1,
+ const CCurrencyValueMap &m2) const
{
- if (m1 < m2 && m2 < m1)
+ // if we have a target to compare against,
+ // check to see if one leaves less change after meeting the target
+ if (totalTargetValues.valueMap.size())
{
- // this is used for sorting
- // what we care about most in this case is that we always give the same answer,
- // so, run a repeatable check, regardless of the order of operands. we'd also want
- // to be as close to right as possible.
- CCurrencyValueMap checkMap1 = m1.IntersectingValues(m2);
- CCurrencyValueMap checkMap2;
- // where they intersect, they are empty, no way to know which is less for sorting
- if (!(checkMap2 < checkMap1))
- {
- return false;
- }
- checkMap2 = checkMap1 - m2.IntersectingValues(m1);
- CAmount total = 0;
- for (auto &oneCur : checkMap2.valueMap)
- {
- total += oneCur.second;
- }
- if (total < 0)
- {
- return true;
- }
- else
+ CCurrencyValueMap leftover1 = m1.SubtractToZero(totalTargetValues);
+ CCurrencyValueMap leftover2 = m2.SubtractToZero(totalTargetValues);
+
+ if (leftover1 < leftover2 && leftover2 < leftover1)
{
- return false;
+ if (leftover1.valueMap.size() < leftover2.valueMap.size())
+ {
+ return true;
+ }
+ else if (leftover2.valueMap.size() < leftover1.valueMap.size())
+ {
+ return false;
+ }
}
+ return leftover1 < leftover2;
}
- return m1 < m2;
- }
- bool operator()(const pair<CCurrencyValueMap, pair<const CWalletTx*, unsigned int> >& t1,
- const pair<CCurrencyValueMap, pair<const CWalletTx*, unsigned int> >& t2) const
- {
- if (t1.first < t2.first && t2.first < t1.first)
+ else if (m1 < m2 && m2 < m1)
{
// this is used for sorting
// what we care about most in this case is that we always give the same answer,
// so, run a repeatable check, regardless of the order of operands. we'd also want
// to be as close to right as possible.
- CCurrencyValueMap checkMap1 = t1.first.IntersectingValues(t2.first);
+ CCurrencyValueMap checkMap1 = m1.IntersectingValues(m2);
CCurrencyValueMap checkMap2;
// where they intersect, they are empty, no way to know which is less for sorting
if (!(checkMap2 < checkMap1))
{
return false;
}
- checkMap2 = checkMap1 - t2.first.IntersectingValues(t1.first);
+ checkMap2 = checkMap1 - m2.IntersectingValues(m1);
CAmount total = 0;
for (auto &oneCur : checkMap2.valueMap)
{
return false;
}
}
- return t1.first < t2.first;
+ return m1 < m2;
}
};
bool CloserToTarget(const CCurrencyValueMap &target, const CCurrencyValueMap ¤t, const CCurrencyValueMap &candidate)
{
CCurrencyValueMap workingTarget = target.SubtractToZero(current); // whatever is left is what we still need
- //printf("with candidate: \n%s\nprior target: %s\n", workingTarget.SubtractToZero(candidate).ToUniValue().write(1,2).c_str(),
- // workingTarget.ToUniValue().write(1,2).c_str());
return workingTarget.SubtractToZero(candidate) < workingTarget;
}
{
vfIncluded.assign(vValue.size(), false);
CCurrencyValueMap totals;
+ std::set<uint160> satisfied;
bool fReachedTarget = false;
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
{
+ CCurrencyValueMap adjustedTarget(targetValues);
+ CCurrencyValueMap presentValues;
for (unsigned int i = 0; i < vValue.size(); i++)
{
//The solver here uses a randomized algorithm,
vValue[i].first.ToUniValue().write(1,2).c_str());
printf("iscloser: %d\n", CloserToTarget(targetValues, totals, vValue[i].first));
*/
+
if ((nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) && CloserToTarget(targetValues, totals, vValue[i].first))
{
- totals += vValue[i].first;
+ totals += vValue[i].first.IntersectingValues(targetValues);
vfIncluded[i] = true;
// we reached the target if we fulfill all currencies
- CCurrencyValueMap curLeft = totals - targetValues;
- if (!curLeft.HasNegative())
+
+ adjustedTarget = targetValues.SubtractToZero(totals);
+
+ // loop through all those that have been zeroed in the adjusted target, and mark as satisfied
+ for (auto &oneCur : targetValues.NonIntersectingValues(adjustedTarget).valueMap)
+ {
+ satisfied.insert(oneCur.first);
+ }
+
+ if (satisfied.size() == targetValues.valueMap.size())
{
fReachedTarget = true;
- if (CompareValueMap()(totals, bestTotals))
+ CompareValueMap comparator(targetValues);
+ if (comparator.CompareMaps(totals, bestTotals))
{
bestTotals = totals;
vfBest = vfIncluded;
// for each currency type being looked for, store the lowest larger outputs found in order, up to a maximum of the number of
// different currencies being looked for
std::map<uint160, std::multimap<CAmount, CReserveOutSelectionInfo>> coinsLowestLarger;
- std::set<const COutput *> largerOuts; // all those that are >= than amount requested in at least one currency
+ std::map<std::pair<const CWalletTx *, int>, CCurrencyValueMap> largerOuts; // all those that are >= than amount requested in at least one currency
std::multimap<int, std::pair<std::vector<uint160>, CReserveOutSelectionInfo>> multiSatisfy; // for outputs that satisfy >= one currency
CCurrencyValueMap largerTotal;
std::map<uint160, std::multimap<CAmount, CReserveOutSelectionInfo>> coinsLargestLower;
- std::set<const COutput *> lowerOuts; // all those that are lower in all requested amounts
+ std::map<std::pair<const CWalletTx *, int>, CCurrencyValueMap> lowerOuts; // all those that are lower or unneeded for larger and helpful
CCurrencyValueMap lowerTotal;
CCurrencyValueMap nativeCent(std::vector<uint160>({ASSETCHAINS_CHAINID}), std::vector<CAmount>({CENT}));
//printf("totaltarget: %s\n", nTotalTarget.ToUniValue().write().c_str());
- BOOST_FOREACH(const COutput &output, vCoins)
+ // currencies in the target that are satisfied x4 in the lower list
+ std::set<uint160> satisfied_x4;
+ CCurrencyValueMap targetx4(nTotalTarget * 4 + nativeCent);
+
+ for (const COutput &output : vCoins)
{
if (!output.fSpendable)
continue;
int i = output.i;
CCurrencyValueMap nAll(pcoin->vout[i].scriptPubKey.ReserveOutValue()); // all currencies, whether in target or not
- CCurrencyValueMap n = nAll.IntersectingValues(targetValues); // get only those reserve currencies that are also in target
- CCurrencyValueMap nTotal = n; // nTotal will be all currencies, including native, that are also in target
+ CCurrencyValueMap nTotal = nAll.IntersectingValues(targetValues); // nTotal will be all currencies, including native, that are also in target
CAmount nativeN = pcoin->vout[i].nValue;
if (nativeN)
{
//printf("nTotal: %s\n", nTotal.ToUniValue().write().c_str());
- vOutputsToOptimize.push_back(std::make_pair(nAll, std::make_pair(output.tx, output.i)));
-
CReserveOutSelectionInfo coin(pcoin, i, nAll);
// if all values are equivalent to targets, we've found the perfect output, no more searching needed
// TODO: should we early out, even if we have extra currencies? If so, use nTotal to commpare
- if (nAll == nTotalTarget)
+ if (nTotal == nTotalTarget)
{
setCoinsRet.insert(std::make_pair(coin.pWtx, coin.n));
valueRet = pcoin->vout[i].scriptPubKey.ReserveOutValue();
return true;
}
+ CCurrencyValueMap subtractedFromTarget(nTotalTarget.SubtractToZero(nTotal));
+
// now, we need to loop through all targets to see if this satisfies any single currency requirement completely
// if so, we will include it in the largest lower list for that currency
int numLarger = 0;
std::vector<uint160> multiCurrencies;
- for (auto &oneCur : nTotal.valueMap)
+
+ COutput sanitizedOutput(output.tx, output.i, 0, true);
+
+ // if we have some entries larger than target
+ if (subtractedFromTarget.valueMap.size() < nTotalTarget.valueMap.size())
{
- // see if we are larger than or equal to at least one required currency,
- // we can get duplicates in multiple currency lists this way, and we will reconcile that later
- if (oneCur.second >= nTotalTarget.valueMap[oneCur.first])
+ //printf("subtractedFromTarget:\n%s\nnTotalTarget:\n%s\nnTotal.NonIntersectingValues(subtractedFromTarget):\n%s\n", subtractedFromTarget.ToUniValue().write().c_str(), nTotal.ToUniValue().write().c_str(), nTotalTarget.NonIntersectingValues(subtractedFromTarget).ToUniValue().write().c_str());
+ for (auto oneCur : nTotal.NonIntersectingValues(subtractedFromTarget).valueMap)
{
- if (coinsLowestLarger.count(oneCur.first))
- {
- coinsLowestLarger[oneCur.first].emplace(std::make_pair(oneCur.second, coin));
- }
- else
- {
- coinsLowestLarger.emplace(std::make_pair(oneCur.first,
- std::multimap<CAmount, CReserveOutSelectionInfo>({{oneCur.second, coin}})));
- }
- numLarger++;
+ coinsLowestLarger[oneCur.first].insert(std::make_pair(oneCur.second, CReserveOutSelectionInfo(output.tx, output.i, nAll)));
multiCurrencies.push_back(oneCur.first);
+ numLarger++;
}
}
-
- // if any currencies here are larger than or equal to what is needed for one currency
if (numLarger)
{
- largerOuts.insert(&output);
+ largerOuts.insert(std::make_pair(std::make_pair(output.tx, output.i), nAll));
largerTotal += nTotal;
multiSatisfy.insert(std::make_pair(numLarger, std::make_pair(multiCurrencies, coin)));
}
else
{
+ bool neededCurrency = false;
for (auto &oneCur : nTotal.valueMap)
{
- if (coinsLargestLower.count(oneCur.first))
- {
- coinsLargestLower[oneCur.first].emplace(std::make_pair(oneCur.second, coin));
- }
- else
+ if (satisfied_x4.count(oneCur.first))
{
- coinsLargestLower.emplace(std::make_pair(oneCur.first,
- std::multimap<CAmount, CReserveOutSelectionInfo>({{oneCur.second, coin}})));
+ continue;
}
+ neededCurrency = true;
+ coinsLargestLower[oneCur.first].insert(std::make_pair(oneCur.second, coin));
+ }
+ if (!neededCurrency)
+ {
+ continue;
}
- lowerOuts.insert(&output);
+
+ lowerOuts.insert(std::make_pair(std::make_pair(output.tx, output.i), nAll));
lowerTotal += nTotal;
- if (lowerTotal > ((nTotalTarget * 4) + nativeCent))
+
+ CCurrencyValueMap adjTargetx4 = targetx4.SubtractToZero(lowerTotal);
+ //printf("targetx4:\n%s\nadjTargetx4:\n%s\n", targetx4.ToUniValue().write().c_str(), adjTargetx4.ToUniValue().write().c_str());
+
+ // loop through all those that have been zeroed in the adjusted target, and mark as satisfied
+ for (auto &oneCur : targetx4.NonIntersectingValues(adjTargetx4).valueMap)
+ {
+ //printf("satisfied x 4: %s\n", EncodeDestination(CIdentityID(oneCur.first)).c_str());
+ satisfied_x4.insert(oneCur.first);
+ }
+
+ if (satisfied_x4.size() == nTotalTarget.valueMap.size())
{
- //fprintf(stderr,"why bother with other utxos if we have much more than what is needed?\n");
+ //printf("short circuit lower: lowerTotal:\n%s\nTotalTarget:\n%s\n", lowerTotal.ToUniValue().write().c_str(), nTotalTarget.ToUniValue().write().c_str());
break;
}
}
}
+ std::set<uint160> satisfied_larger;
+
+ CCurrencyValueMap newLargerTotal;
+ CCurrencyValueMap adjTotalTarget;
+ std::map<std::pair<const CWalletTx *, int>, CCurrencyValueMap> largerCoins; // int is the index into the vOutputsToOptimize to remove
+
// if our lower total + larger total are not enough, no way we have enough
if ((lowerTotal + largerTotal) < nTotalTarget)
{
return false;
}
- //printf("lowerTotal:\n%s\nlargerTotal:\n%s\n", lowerTotal.ToUniValue().write().c_str(), largerTotal.ToUniValue().write().c_str());
+ //printf("\nlowerTotal:\n%s\nlargerTotal:\n%s\nnewLargerTotal:\n%s\nTotalTarget:\n%s\n", lowerTotal.ToUniValue().write().c_str(), largerTotal.ToUniValue().write().c_str(), newLargerTotal.ToUniValue().write().c_str(), nTotalTarget.ToUniValue().write().c_str());
+
+ for (auto &lowerOut : lowerOuts)
+ {
+ totalToOptimize += lowerOut.second;
+ vOutputsToOptimize.push_back(std::make_pair(lowerOut.second, std::make_pair(lowerOut.first.first, lowerOut.first.second)));
+ }
// if all the lower amounts are just what we need, and we don't add too many inputs in the process, use them all
size_t numInputsLimit = (size_t)GetArg("-mempooltxinputlimit", MAX_NUM_INPUTS_LIMIT);
if ((lowerTotal >= nTotalTarget && lowerTotal <= (nTotalTarget + nativeCent)) && lowerOuts.size() <= numInputsLimit)
{
+ //printf("selecting all lowers\nlowerTotal:\n%s\nTotalTarget:\n%s\n", lowerTotal.ToUniValue().write().c_str(), nTotalTarget.ToUniValue().write().c_str());
+
for (auto oneOut : lowerOuts)
{
- setCoinsRet.insert(std::make_pair(oneOut->tx, oneOut->i));
- valueRet += oneOut->tx->vout[oneOut->i].ReserveOutValue();
- nativeValueRet += oneOut->tx->vout[oneOut->i].nValue;
+ setCoinsRet.insert(std::make_pair(oneOut.first.first, oneOut.first.second));
+ valueRet += oneOut.first.first->vout[oneOut.first.second].ReserveOutValue();
+ nativeValueRet += oneOut.first.first->vout[oneOut.first.second].nValue;
}
return true;
}
+ //printf("\nlowerTotal:\n%s\nlargerTotal:\n%s\nTotalTarget:\n%s\n", lowerTotal.ToUniValue().write().c_str(), largerTotal.ToUniValue().write().c_str(), nTotalTarget.ToUniValue().write().c_str());
+
std::map<std::pair<const CWalletTx *, int>, CReserveOutSelectionInfo> added;
- CCurrencyValueMap totalAdded;
- CCurrencyValueMap adjustedTarget = nTotalTarget;
+ largerTotal.valueMap.clear();
+ CCurrencyValueMap adjustedTarget;
std::set<uint160> satisfied;
// short circuit best fit check with any exact amounts we may have
for (auto multiIt = multiSatisfy.rbegin(); multiIt != multiSatisfy.rend(); multiIt++)
{
// if we have 0 left, we're done
- if ((nTotalTarget.valueMap.size() - satisfied.size()) == 0)
+ if (nTotalTarget.valueMap.size() == satisfied.size())
{
+ //printf("satisfied all currencies. lowerTotal:\n%s\n", largerTotal.ToUniValue().write().c_str());
break;
}
if (!satisfied.count(oneCurID) &&
multiIt->second.second.outVal.valueMap[oneCurID] == adjustedTarget.valueMap[oneCurID])
{
- satisfied.insert(oneCurID);
newFound++;
}
}
+ std::pair<const CWalletTx *, unsigned int> outPair({multiIt->second.second.pWtx, multiIt->second.second.n});
+
// if we don't satisfy any new currency with this output, don't add it as we care more if singles are lower as a priotity
- if (!newFound)
+ if (!newFound || added.count(outPair))
{
continue;
}
// this satisfies at least 1 new currency, so use it and also reduce other currencies by all amounts that it includes
// don't check it again when looking later
- added.insert(std::make_pair(std::make_pair(multiIt->second.second.pWtx, multiIt->second.second.n), multiIt->second.second));
+ added.insert(std::make_pair(outPair, multiIt->second.second));
// add all currency values in the transaction, as some may partially satisfy, and we should early out when we have enough
- totalAdded += multiIt->second.second.outVal.IntersectingValues(nTotalTarget);
-
- adjustedTarget.SubtractToZero(totalAdded);
+ // printf("multiIt->second.second.outVal:\n%s\n", multiIt->second.second.outVal.ToUniValue().write().c_str());
+ CCurrencyValueMap newAdded(multiIt->second.second.outVal.IntersectingValues(nTotalTarget));
+ largerTotal += newAdded;
+ largerOuts.erase(outPair);
+
+ // printf("adjustedTarget:\n%s\ntotalAdded:\n%s\n", adjustedTarget.ToUniValue().write().c_str(), totalAdded.ToUniValue().write().c_str());
+
+ // printf("adjustedTarget:\n%s\n", adjustedTarget.ToUniValue().write().c_str());
+ // printf("nTotalTarget.NonIntersectingValues(adjustedTarget):\n%s\n", nTotalTarget.NonIntersectingValues(adjustedTarget).ToUniValue().write().c_str());
+
+ adjustedTarget = nTotalTarget.SubtractToZero(largerTotal);
+
// loop through all those that have been zeroed in the adjusted target, and mark as satisfied
for (auto &oneCur : nTotalTarget.NonIntersectingValues(adjustedTarget).valueMap)
{
+ //printf("satisfied: %s\n", EncodeDestination(CIdentityID(oneCur.first)).c_str());
satisfied.insert(oneCur.first);
}
}
return true;
}
+ // fill up lower outputs with larger as well to ensure fill
+ // those we add from multisatisfy check will be removed from optimized selection
+ for (auto &oneCurID : satisfied)
+ {
+ satisfied_x4.insert(oneCurID);
+ }
+ for (auto &largerOut : largerOuts)
+ {
+ COutput thisOutput(largerOut.first.first, largerOut.first.second, 0, true);
+ if (lowerOuts.count(std::make_pair(largerOut.first.first, largerOut.first.second)))
+ {
+ continue;
+ }
+ // if we have more, they only go into the lower, if they have
+ // coins in the currencies where we are not satisfied
+
+ //printf("targetx4:\n%s\nlowerTotal:\n%s\nlargerOut.second:\n%s\n", targetx4.ToUniValue().write().c_str(), lowerTotal.ToUniValue().write().c_str(), largerOut.second.ToUniValue().write().c_str());
+
+ bool useThis = false;
+ for (auto &oneCur : largerOut.second.IntersectingValues(nTotalTarget).valueMap)
+ {
+ if (!satisfied.count(oneCur.first) && !satisfied_x4.count(oneCur.first))
+ {
+ useThis = true;
+ }
+ }
+
+ if (useThis)
+ {
+ CReserveOutSelectionInfo coin(largerOut.first.first, largerOut.first.second, largerOut.second);
+
+ for (auto &oneCur : largerOut.second.valueMap)
+ {
+ coinsLargestLower[oneCur.first].insert(std::make_pair(oneCur.second, coin));
+ }
+
+ lowerOuts.insert(std::make_pair(std::make_pair(largerOut.first.first, largerOut.first.second), largerOut.second));
+
+ lowerTotal += largerOut.second;
+
+ CCurrencyValueMap adjTargetx4 = targetx4.SubtractToZero(lowerTotal);
+ //printf("targetx4:\n%s\nadjTargetx4:\n%s\n", targetx4.ToUniValue().write().c_str(), adjTargetx4.ToUniValue().write().c_str());
+
+ // loop through all those that have been zeroed in the adjusted target, and mark as satisfied
+ for (auto &oneCur : targetx4.NonIntersectingValues(adjTargetx4).valueMap)
+ {
+ // don't consider it satisfied x4, unless we have at least 4 entries to choose from
+ if (coinsLargestLower.count(oneCur.first) && coinsLargestLower[oneCur.first].size() >= 4)
+ {
+ //printf("satisfied x 4: %s\n", EncodeDestination(CIdentityID(oneCur.first)).c_str());
+ satisfied_x4.insert(oneCur.first);
+ }
+ }
+ totalToOptimize += largerOut.second;
+ vOutputsToOptimize.push_back(std::make_pair(largerOut.second, std::make_pair(largerOut.first.first, largerOut.first.second)));
+ }
+ }
+
+ //printf("\nlargerTotal:\n%s\n", largerTotal.ToUniValue().write().c_str());
+ // printf("adjustedTarget:\n%s\n", adjustedTarget.ToUniValue().write().c_str());
+
// make new vector without those we have added due to exact fit, and use remaining and adjusted target to satisfy requests
std::vector<int> vOutputsToRemove;
CCurrencyValueMap removedValue;
- for (int i = 0; i < vOutputsToOptimize.size(); i++)
+ if (added.size())
{
- if (added.count(vOutputsToOptimize[i].second))
+ for (int i = 0; i < vOutputsToOptimize.size(); i++)
+ {
+ if (added.count(vOutputsToOptimize[i].second))
+ {
+ vOutputsToRemove.push_back(i);
+ removedValue += vOutputsToOptimize[i].first;
+ }
+ }
+
+ for (auto &oneOutput : added)
{
- vOutputsToRemove.push_back(i);
- removedValue += vOutputsToOptimize[i].first;
+ setCoinsRet.insert(std::make_pair(oneOutput.second.pWtx, oneOutput.second.n));
+ valueRet += oneOutput.second.outVal;
+ }
+ auto vRetIt = valueRet.valueMap.find(ASSETCHAINS_CHAINID);
+ if (vRetIt != valueRet.valueMap.end())
+ {
+ nativeValueRet = vRetIt->second;
+ valueRet.valueMap.erase(vRetIt);
}
}
vOutputsToOptimize.erase(vOutputsToOptimize.begin() + vOutputsToRemove[i]);
}
- totalToOptimize = largerTotal.SubtractToZero(removedValue) + lowerTotal;
- CCurrencyValueMap newOptimizationTarget = nTotalTarget.SubtractToZero(removedValue);
+ totalToOptimize = totalToOptimize.SubtractToZero(removedValue);
+ CCurrencyValueMap newOptimizationTarget = nTotalTarget.SubtractToZero(largerTotal);
- /* printf("totalToOptimize:\n%s\nnewOptimizationTarget:\n%s\n", totalToOptimize.ToUniValue().write().c_str(), newOptimizationTarget.ToUniValue().write().c_str());
- for (int i = 0; i < vOutputsToOptimize.size(); i++)
+ //printf("totalToOptimize:\n%s\nnewOptimizationTarget:\n%s\n", totalToOptimize.ToUniValue().write().c_str(), newOptimizationTarget.ToUniValue().write().c_str());
+ /* for (int i = 0; i < vOutputsToOptimize.size(); i++)
{
printf("output #%d:\nreserves:\n%s\nnative:\n%s\n",
i,
CCurrencyValueMap bestTotals;
ApproximateBestReserveSubset(vOutputsToOptimize, totalToOptimize, newOptimizationTarget, vfBest, bestTotals, 1000);
- if (bestTotals != targetValues && totalToOptimize >= targetValues + nativeCent)
+ if (bestTotals != newOptimizationTarget && totalToOptimize >= newOptimizationTarget + nativeCent)
{
+ //printf("bestTotals:\n%s\ntotalToOptimize:\n%s\nnewOptimizationTarget:\n%s\n", bestTotals.ToUniValue().write().c_str(), totalToOptimize.ToUniValue().write().c_str(), (newOptimizationTarget + nativeCent).ToUniValue().write().c_str());
ApproximateBestReserveSubset(vOutputsToOptimize, totalToOptimize, newOptimizationTarget + nativeCent, vfBest, bestTotals, 1000);
}
setCoinsRet.insert(vOutputsToOptimize[i].second);
valueRet += vOutputsToOptimize[i].second.first->vout[vOutputsToOptimize[i].second.second].ReserveOutValue();
nativeValueRet += vOutputsToOptimize[i].second.first->vout[vOutputsToOptimize[i].second.second].nValue;
+ /* printf("one selected\ntxid: %s, output: %d\nvalueOut: %s\n",
+ vOutputsToOptimize[i].second.first->GetHash().GetHex().c_str(),
+ vOutputsToOptimize[i].second.second,
+ vOutputsToOptimize[i].first.ToUniValue().write(1,2).c_str()); */
}
}
CCurrencyValueMap checkReturn(valueRet);
checkReturn.valueMap[ASSETCHAINS_CHAINID] = nativeValueRet;
- //printf("valueRet:\n%s\n", checkReturn.ToUniValue().write().c_str());
-
if (checkReturn.IntersectingValues(nTotalTarget) < nTotalTarget)
{
return false;