diff --git a/src/pbaas/crosschainrpc.cpp b/src/pbaas/crosschainrpc.cpp index 53e24e45cf5..4f9cba9c847 100644 --- a/src/pbaas/crosschainrpc.cpp +++ b/src/pbaas/crosschainrpc.cpp @@ -53,7 +53,7 @@ extern std::string VERUS_CHAINNAME; uint32_t PBAAS_TESTFORK2_TIME = 1684281600; uint32_t PBAAS_TESTFORK3_TIME = 1685379600; -uint32_t PBAAS_TESTFORK4_TIME = 1685984400; +uint32_t PBAAS_TESTFORK4_TIME = 1699344000; uint32_t PBAAS_MAINDEFI3_HEIGHT = 2553500; uint32_t PBAAS_ENFORCE_CORRECT_EVIDENCE_TIME = 1684359650; diff --git a/src/pbaas/notarization.cpp b/src/pbaas/notarization.cpp index 86d5ca66b57..48606b126fa 100644 --- a/src/pbaas/notarization.cpp +++ b/src/pbaas/notarization.cpp @@ -1147,6 +1147,8 @@ bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceS 1 : chainActive.Height() + 1; + bool improvedMinCheck = ConnectedChains.CheckZeroViaOnlyPostLaunch(currentHeight); + CTransferDestination notaryPayee; if (!externalSystemID.IsNull()) @@ -1161,10 +1163,22 @@ bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceS CNativeHashWriter hw(hashType); CCurrencyValueMap newPreConversionReservesIn; + CCurrencyValueMap prelaunchReserveIn; if (!IsPreLaunch() && !IsLaunchComplete()) { newPreConversionReservesIn = CCurrencyValueMap(currencyState.currencies, currencyState.primaryCurrencyIn); + prelaunchReserveIn = newPreConversionReservesIn; + } + else if (improvedMinCheck && IsPreLaunch()) + { + prelaunchReserveIn = CCurrencyValueMap(currencyState.currencies, currencyState.reserveIn); + } + + CCurrencyValueMap maxPreconvertMap = CCurrencyValueMap(destCurrency.currencies, destCurrency.maxPreconvert); + if (destCurrency.maxPreconvert.size()) + { + maxPreconvertMap = CCurrencyValueMap(destCurrency.currencies, destCurrency.maxPreconvert); } for (int i = 0; i < exportTransfers.size(); i++) @@ -1200,14 +1214,21 @@ bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceS CCurrencyValueMap newTotalReserves; if (IsPreLaunch()) { - newTotalReserves = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserves) + newReserveIn + newPreConversionReservesIn; + newTotalReserves = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserves) + newReserveIn + prelaunchReserveIn; } else { - newTotalReserves = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.primaryCurrencyIn) + newReserveIn + newPreConversionReservesIn; + newTotalReserves = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.primaryCurrencyIn) + newReserveIn + prelaunchReserveIn; } - if (destCurrency.maxPreconvert.size() && newTotalReserves > CCurrencyValueMap(destCurrency.currencies, destCurrency.maxPreconvert)) + if (improvedMinCheck) + { + newTotalReserves = newTotalReserves.IntersectingValues(newReserveIn); + } + + if (destCurrency.maxPreconvert.size() && + ((!improvedMinCheck && newTotalReserves > maxPreconvertMap) || + (improvedMinCheck && (maxPreconvertMap - newTotalReserves).HasNegative()))) { LogPrintf("%s: refunding pre-conversion over maximum\n", __func__); reserveTransfer = reserveTransfer.GetRefundTransfer(); @@ -1215,6 +1236,7 @@ bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceS else { newPreConversionReservesIn += newReserveIn; + prelaunchReserveIn += newReserveIn; } } } @@ -1298,9 +1320,12 @@ bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceS // check our currency and any co-launch currency to determine our eligibility, as ALL // co-launch currencies must launch for one to launch + CCurrencyValueMap coLaunchReserveIn = CCurrencyValueMap(coLaunchCurrency.currencies, coLaunchState.reserveIn); + CCurrencyValueMap coLaunchMinMap = CCurrencyValueMap(coLaunchCurrency.currencies, coLaunchCurrency.minPreconvert); if (coLaunchState.IsRefunding() || !coLaunchState.ValidateConversionLimits(true) || - CCurrencyValueMap(coLaunchCurrency.currencies, coLaunchState.reserveIn) < CCurrencyValueMap(coLaunchCurrency.currencies, coLaunchCurrency.minPreconvert) || + (!improvedMinCheck && coLaunchReserveIn < coLaunchMinMap) || + (improvedMinCheck && (coLaunchReserveIn - coLaunchMinMap).HasNegative()) || (coLaunchCurrency.IsFractional() && CCurrencyValueMap(coLaunchCurrency.currencies, coLaunchState.reserveIn).CanonicalMap().valueMap.size() != coLaunchCurrency.currencies.size())) { @@ -1319,10 +1344,10 @@ bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceS minPreMap = CCurrencyValueMap(destCurrency.currencies, destCurrency.minPreconvert).CanonicalMap(); } - bool improvedMinCheck = ConnectedChains.CheckZeroViaOnlyPostLaunch(currentHeight); if (forcedRefund || (minPreMap.valueMap.size() && - ((!improvedMinCheck && preConvertedMap < minPreMap) || (improvedMinCheck && (preConvertedMap - minPreMap).HasNegative()))) || + ((!improvedMinCheck && preConvertedMap < minPreMap) || + (improvedMinCheck && (preConvertedMap - minPreMap).HasNegative()))) || (destCurrency.IsFractional() && (CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserveIn) + newPreConversionReservesIn).CanonicalMap().valueMap.size() != destCurrency.currencies.size())) @@ -1445,14 +1470,36 @@ bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceS { // accumulate reserves during pre-conversions import to enforce max pre-convert CCurrencyValueMap tempReserves; + CCurrencyValueMap cumulativeReserves; + auto currencyIdxMap = tempState.GetReserveMap(); + bool newCumulative = improvedMinCheck && tempState.IsFractional(); for (auto &oneCurrencyID : tempState.currencies) { if (rtxd.currencies.count(oneCurrencyID)) { - int64_t reservesConverted = rtxd.currencies[oneCurrencyID].nativeOutConverted; - if (reservesConverted) + int64_t reservesIn = newCumulative ? + oneCurrencyID == ASSETCHAINS_CHAINID ? + (rtxd.nativeIn - rtxd.nativeOut) : + (rtxd.currencies[oneCurrencyID].reserveIn - (rtxd.currencies[oneCurrencyID].reserveConversionFees + rtxd.currencies[oneCurrencyID].reserveOut)) : + rtxd.currencies[oneCurrencyID].nativeOutConverted; + + if (newCumulative) { - tempReserves.valueMap[oneCurrencyID] = reservesConverted; + int idx = currencyIdxMap[oneCurrencyID]; + if (oneCurrencyID == ASSETCHAINS_CHAINID) + { + tempState.primaryCurrencyIn[idx] = + (this->currencyState.primaryCurrencyIn[idx] + rtxd.nativeIn + tempState.reserveOut[idx]) - rtxd.nativeOut; + } + else + { + tempState.primaryCurrencyIn[idx] = this->currencyState.primaryCurrencyIn[idx] + reservesIn; + } + } + + if (reservesIn) + { + tempReserves.valueMap[oneCurrencyID] = reservesIn; } } } @@ -1461,7 +1508,10 @@ bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceS // reverting supply and reserves, we end up with what we started, after prelaunch and // before all post launch functions are complete, we use primaryCurrencyIn to accumulate // reserves to enforce maxPreconvert - tempState.primaryCurrencyIn = tempState.AddVectors(this->currencyState.primaryCurrencyIn, tempReserves.AsCurrencyVector(tempState.currencies)); + if (!newCumulative) + { + tempState.primaryCurrencyIn = tempState.AddVectors(this->currencyState.primaryCurrencyIn, tempReserves.AsCurrencyVector(tempState.currencies)); + } tempState.reserveOut = tempState.AddVectors(tempState.reserveOut, (CCurrencyValueMap(tempState.currencies, tempState.reserveIn) * -1).AsCurrencyVector(tempState.currencies)); @@ -10254,10 +10304,12 @@ bool PreCheckFinalizeNotarization(const CTransaction &tx, int32_t outNum, CValid return state.Error("Invalid notarization output for finalization"); } - if (!(!txBlockHash.IsNull() && - (notaTxBlockIt = mapBlockIndex.find(txBlockHash)) != mapBlockIndex.end() && - chainActive.Contains(notaTxBlockIt->second)) && - (!PBAAS_TESTMODE || chainActive[height - 1]->nTime >= PBAAS_TESTFORK4_TIME)) + if ((!PBAAS_TESTMODE || chainActive[height - 1]->nTime >= PBAAS_TESTFORK4_TIME) && + currentFinalization.IsConfirmed() && + height != 1 && + !(!txBlockHash.IsNull() && + (notaTxBlockIt = mapBlockIndex.find(txBlockHash)) != mapBlockIndex.end() && + chainActive.Contains(notaTxBlockIt->second))) { return state.Error("Uncommitted notarization output is invalid for finalization"); } diff --git a/src/pbaas/pbaas.cpp b/src/pbaas/pbaas.cpp index 298c5fa5667..0672255cf38 100644 --- a/src/pbaas/pbaas.cpp +++ b/src/pbaas/pbaas.cpp @@ -2797,6 +2797,7 @@ bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTrans } bool isClearLaunch = ccxSource.IsClearLaunch(); + std::vector vOutputs; if (ConnectedChains.CheckZeroViaOnlyPostLaunch(nHeight) && isClearLaunch && @@ -2829,9 +2830,43 @@ bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTrans priorNotar.currencyID == mainImport.importCurrencyID) { checkState = priorNotar.currencyState; + checkState.SetPrelaunch(false); - validNotarization = true; - break; + // clear launch export is not clear launch import + checkState.SetLaunchClear(false); + CCoinbaseCurrencyState pricingState; + CCurrencyValueMap dummyCurrency, dummyCurrencyUsed, dummyCurrencyOut; + + if (rtxd.AddReserveTransferImportOutputs(sourceSysDef, + destSysDef, + destCurDef, + checkState, + reserveTransfers, + nHeight, + vOutputs, + dummyCurrency, + dummyCurrencyUsed, + dummyCurrencyOut, + &pricingState, + ccxSource.exporter, + importNotarization.proposer, + EntropyHashFromHeight(CBlockIndex::BlockEntropyKey(), importNotarization.notarizationHeight, destCurDef.GetID()))) + { + checkState.conversionPrice = pricingState.conversionPrice; + checkState.viaConversionPrice = pricingState.viaConversionPrice; + validNotarization = true; + vOutputs.clear(); + rtxd = CReserveTransactionDescriptor(); + break; + } + else + { + if (LogAcceptCategory("defi")) + { + LogPrintf("%s: Invalid currency state for import: %s\n", __func__, checkState.ToUniValue().write(1,2).c_str()); + } + return eval->Error(std::string(__func__) + ": invalid prior notarization for clear launch import: " + mainImport.ToUniValue().write(1,2)); + } } } if (!validNotarization) @@ -2842,7 +2877,7 @@ bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTrans } return eval->Error(std::string(__func__) + ": invalid prior notarization for clear launch import: " + mainImport.ToUniValue().write(1,2)); } - checkState.SetLaunchClear(false); + //checkState.SetLaunchClear(false); } else { @@ -2856,7 +2891,6 @@ bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTrans } } - std::vector vOutputs; CCurrencyValueMap importedCurrency, gatewayCurrencyUsed, spentCurrencyOut; if (!rtxd.AddReserveTransferImportOutputs(sourceSysDef, @@ -5620,8 +5654,8 @@ bool CConnectedChains::CheckZeroViaOnlyPostLaunch(uint32_t height) const { if (IsVerusActive()) { - if ((PBAAS_TESTMODE && height > 61982) || - (!PBAAS_TESTMODE && height > 2570264)) + if ((PBAAS_TESTMODE && height > 62378) || + (!PBAAS_TESTMODE && height > 2573055)) { return true; } @@ -7410,6 +7444,13 @@ bool CConnectedChains::CreateLatestImports(const CCurrencyDefinition &sourceSyst // pay the fee out to the miner CReserveTransactionDescriptor rtxd(tb.mtx, view, nHeight + 1); + if (!rtxd.IsValid()) + { + printf("%s: Created invalid import transaction for currency %s\n", __func__, EncodeDestination(CIdentityID(ccx.destCurrencyID)).c_str()); + LogPrintf("%s: Created invalid import transaction for currency %s\n", __func__, EncodeDestination(CIdentityID(ccx.destCurrencyID)).c_str()); + return false; + } + tb.SetFee(rtxd.nativeIn - rtxd.nativeOut); CCurrencyValueMap intersectMap; diff --git a/src/pbaas/reserves.cpp b/src/pbaas/reserves.cpp index b231e2767a6..a0387745e46 100644 --- a/src/pbaas/reserves.cpp +++ b/src/pbaas/reserves.cpp @@ -1782,6 +1782,78 @@ CAmount CalculateReserveOut(CAmount FractionalIn, CAmount Supply, CAmount Normal return reserveOut; } + +void DumpConvertData(const std::vector &_inputReserves, + const std::vector &_inputFractional, + CCurrencyState &_newState, + std::vector> const *pCrossConversions, + std::vector *pViaPrices) +{ + LogPrintf("inputReserves: \n"); + for (int i = 0; i < _inputReserves.size(); i++) + { + LogPrintf("%ld", _inputReserves[i]); + if ((i + 1) == _inputReserves.size()) + { + LogPrintf("\n"); + } + else + { + LogPrintf(", "); + } + } + LogPrintf("inputFractional: \n"); + for (int i = 0; i < _inputFractional.size(); i++) + { + LogPrintf("%ld", _inputFractional[i]); + if ((i + 1) == _inputFractional.size()) + { + LogPrintf("\n"); + } + else + { + LogPrintf(", "); + } + } + if (pViaPrices) + { + LogPrintf("viaPrices: \n"); + for (int i = 0; i < pViaPrices->size(); i++) + { + LogPrintf("%ld", (*pViaPrices)[i]); + if ((i + 1) == pViaPrices->size()) + { + LogPrintf("\n"); + } + else + { + LogPrintf(", "); + } + } + } + if (pCrossConversions) + { + LogPrintf("crossConversions: \n"); + for (int i = 0; i < pCrossConversions->size(); i++) + { + LogPrintf("conversions vector %d:\n", i); + for (int j = 0; j < (*pCrossConversions)[i].size(); j++) + { + LogPrintf("%ld", (*pCrossConversions)[i][j]); + if ((j + 1) == (*pCrossConversions)[i].size()) + { + LogPrintf("\n"); + } + else + { + LogPrintf(", "); + } + } + } + } + LogPrintf("currencystate: %s\n", _newState.ToUniValue().write(1,2).c_str()); +} + // This can handle multiple aggregated, bidirectional conversions in one block of transactions. To determine the conversion price, it // takes both input amounts of any number of reserves and the fractional currencies targeting those reserves to merge the conversion into one // merged calculation with the same price across currencies for all transactions in the block. It returns the newly calculated @@ -1789,6 +1861,7 @@ CAmount CalculateReserveOut(CAmount FractionalIn, CAmount Supply, CAmount Normal std::vector CCurrencyState::ConvertAmounts(const std::vector &_inputReserves, const std::vector &_inputFractional, CCurrencyState &_newState, + CValidationState &state, std::vector> const *pCrossConversions, std::vector *pViaPrices) const { @@ -1879,6 +1952,8 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & if (failed) { + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " invalid starting conditions"); return initialRates; } @@ -2055,7 +2130,13 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & } CAmount curAmt = bigCurAmt.GetLow64(); it->second.first -= curAmt; - assert(it->second.first >= 0); + if (it->second.first < 0) + { + printf("%s: it->second.first < 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " it->second.first < 0"); + return initialRates; + } fractionalLayersOut[frIdx].first += weight; fractionalLayersOut[frIdx].second.first += curAmt; @@ -2106,10 +2187,22 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & } supplyAfterBuy = supply + addSupply; - assert(supplyAfterBuy >= 0); + if (supplyAfterBuy < 0) + { + printf("%s: supplyAfterBuy < 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " supplyAfterBuy < 0"); + return initialRates; + } reserveAfterBuy = supply + addNormalizedReserves; - assert(reserveAfterBuy >= 0); + if (reserveAfterBuy < 0) + { + printf("%s: reserveAfterBuy < 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " reserveAfterBuy < 0"); + return initialRates; + } addSupply = 0; addNormalizedReserves = 0; @@ -2153,16 +2246,40 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & } supplyAfterSell = supply + addSupply; - assert(supplyAfterSell >= 0); + if (supplyAfterSell < 0) + { + printf("%s: supplyAfterSell < 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " supplyAfterSell < 0"); + return initialRates; + } supplyAfterBuySell = supplyAfterBuy + addSupply; - assert(supplyAfterBuySell >= 0); + if (supplyAfterBuySell < 0) + { + printf("%s: supplyAfterBuySell < 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " supplyAfterBuySell < 0"); + return initialRates; + } reserveAfterSell = supply + addNormalizedReservesBB; - assert(reserveAfterSell >= 0); + if (reserveAfterSell < 0) + { + printf("%s: reserveAfterSell < 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " reserveAfterSell < 0"); + return initialRates; + } reserveAfterBuySell = reserveAfterBuy + addNormalizedReservesAB; - assert(reserveAfterBuySell >= 0); + if (reserveAfterBuySell < 0) + { + printf("%s: reserveAfterBuySell < 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " reserveAfterBuySell < 0"); + return initialRates; + } addSupply = 0; addNormalizedReserves = 0; @@ -2180,7 +2297,13 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & { auto idIT = fractionalOutMap.find(id); - assert(idIT != fractionalOutMap.end()); + if (idIT == fractionalOutMap.end()) + { + printf("%s: idIT == fractionalOutMap.end()\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " idIT == fractionalOutMap.end()"); + return initialRates; + } idIT->second.second += ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64(); } @@ -2211,7 +2334,14 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & { arith_uint256 bigFractionDelta(fractionalOutIT->second.first); fractionDelta = ((bigFractionDelta + arith_uint256(fractionalOutIT->second.second)) >> 1).GetLow64(); - assert(inputFraction + fractionDelta > 0); + + if (inputFraction + fractionDelta <= 0) + { + printf("%s: inputFraction + fractionDelta <= 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " inputFraction + fractionDelta <= 0"); + return initialRates; + } fractionalSizes[i] += fractionDelta; rates[i] = ((arith_uint256(inputReserve) * bigSatoshi) / arith_uint256(fractionalSizes[i])).GetLow64(); @@ -2227,7 +2357,14 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & arith_uint256 bigReserveDelta(fractionalInIT->second.first); CAmount adjustedReserveDelta = NativeToReserve(((bigReserveDelta + arith_uint256(fractionalInIT->second.second)) >> 1).GetLow64(), i); reserveSizes[i] += adjustedReserveDelta; - assert(inputFraction > 0); + + if (inputFraction <= 0) + { + printf("%s: inputFraction <= 0\n", __func__); + DumpConvertData(_inputReserves, _inputFractional, _newState, pCrossConversions, pViaPrices); + state.Error(std::string(__func__) + " inputFraction <= 0"); + return initialRates; + } rates[i] = ((arith_uint256(reserveSizes[i]) * bigSatoshi) / arith_uint256(inputFraction)).GetLow64(); @@ -2285,7 +2422,7 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & std::vector _viaPrices; std::vector &viaPrices(pViaPrices ? *pViaPrices : _viaPrices); CCurrencyState intermediateState = newState; - viaPrices = intermediateState.ConvertAmounts(scratchValues, fractionsToConvert, newState); + viaPrices = intermediateState.ConvertAmounts(scratchValues, fractionsToConvert, newState, state); } } @@ -2304,24 +2441,6 @@ std::vector CCurrencyState::ConvertAmounts(const std::vector & return rates; } -CAmount CCurrencyState::ConvertAmounts(CAmount inputReserve, CAmount inputFraction, CCurrencyState &newState, int32_t reserveIndex) const -{ - int32_t numCurrencies = currencies.size(); - if (reserveIndex >= numCurrencies) - { - printf("%s: reserve index out of range\n", __func__); - return 0; - } - std::vector inputReserves(numCurrencies); - inputReserves[reserveIndex] = inputReserve; - std::vector inputFractional(numCurrencies); - inputFractional[reserveIndex] = inputFraction; - std::vector retVal = ConvertAmounts(inputReserves, - inputFractional, - newState); - return retVal[reserveIndex]; -} - UniValue CReserveInOuts::ToUniValue() const { UniValue retVal(UniValue::VOBJ); @@ -2765,9 +2884,16 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction bool isClearLaunch = (ccx.IsClearLaunch() && ccx.sourceSystemID == importCurrencyDef.launchSystemID); CReserveTransactionDescriptor rtxd = *this; + rtxd.currencies.clear(); + rtxd.nativeIn = 0; + rtxd.nativeConversionFees = 0; + rtxd.nativeOut = 0; + uint256 weakEntropyHash = EntropyHashFromHeight(CBlockIndex::BlockEntropyKey(), importNotarization.notarizationHeight, importCurrencyDef.GetID()); - if (ConnectedChains.CheckZeroViaOnlyPostLaunch(nHeight) && + bool updatedChecks = ConnectedChains.CheckZeroViaOnlyPostLaunch(nHeight); + + if (updatedChecks && isClearLaunch && importTransfers.size()) { @@ -2788,6 +2914,7 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction return; } bool validNotarization = false; + // get the prior output notarization for (int o = priorOutNum; o < priorTx.vout.size(); o++) { @@ -2801,6 +2928,10 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction { checkState = priorNotar.currencyState; checkState.SetPrelaunch(false); + + // clear launch export is not clear launch import + checkState.SetLaunchClear(false); + if (rtxd.AddReserveTransferImportOutputs(sourceSystemDef, ConnectedChains.thisChain, importCurrencyDef, @@ -2834,7 +2965,6 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction flags |= IS_REJECT; return; } - checkState.SetLaunchClear(false); } else { @@ -2872,6 +3002,10 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction } rtxd = *this; + rtxd.currencies.clear(); + rtxd.nativeIn = 0; + rtxd.nativeConversionFees = 0; + rtxd.nativeOut = 0; if (!rtxd.AddReserveTransferImportOutputs(sourceSystemDef, ConnectedChains.thisChain, @@ -2906,6 +3040,87 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction } // these affect comparison, but not calculations + if (newState.reserveIn != importNotarization.currencyState.reserveIn || + newState.reserveOut != importNotarization.currencyState.reserveOut || + newState.primaryCurrencyIn != importNotarization.currencyState.primaryCurrencyIn) + { + if (LogAcceptCategory("defi")) + { + LogPrintf("%s: Expected: %s\nActual: %s\n", __func__, newState.ToUniValue().write(1,2).c_str(), importNotarization.currencyState.ToUniValue().write(1,2).c_str()); + } + if (updatedChecks && !checkState.IsRefunding()) + { + if (!(checkState.IsPrelaunch() || checkState.IsLaunchCompleteMarker())) + { + // accumulate reserves during pre-conversions import to enforce max pre-convert + CCurrencyValueMap tempReserves; + auto currencyIdxMap = newState.GetReserveMap(); + bool newCumulative = newState.IsFractional(); + for (auto &oneCurrencyID : checkState.currencies) + { + if (rtxd.currencies.count(oneCurrencyID)) + { + int64_t reservesIn = newCumulative ? + (oneCurrencyID == ASSETCHAINS_CHAINID ? + (rtxd.nativeIn - rtxd.nativeOut) : + rtxd.currencies[oneCurrencyID].reserveIn - + (rtxd.currencies[oneCurrencyID].reserveConversionFees + rtxd.currencies[oneCurrencyID].reserveOut)) : + rtxd.currencies[oneCurrencyID].nativeOutConverted; + + if (newCumulative) + { + int idx = currencyIdxMap[oneCurrencyID]; + if (oneCurrencyID == ASSETCHAINS_CHAINID) + { + newState.primaryCurrencyIn[idx] = + (checkState.primaryCurrencyIn[idx] + rtxd.nativeIn + newState.reserveOut[idx]) - rtxd.nativeOut; + } + else + { + newState.primaryCurrencyIn[idx] = checkState.primaryCurrencyIn[idx] + reservesIn; + } + } + + if (reservesIn) + { + tempReserves.valueMap[oneCurrencyID] = reservesIn; + } + } + } + + // use double entry to enable pass through of the accumulated reserve such that when + // reverting supply and reserves, we end up with what we started, after prelaunch and + // before all post launch functions are complete, we use primaryCurrencyIn to accumulate + // reserves to enforce maxPreconvert + if (!newCumulative) + { + newState.primaryCurrencyIn = + newState.AddVectors(checkState.primaryCurrencyIn, tempReserves.AsCurrencyVector(newState.currencies)); + } + newState.reserveOut = + newState.AddVectors(newState.reserveOut, + (CCurrencyValueMap(newState.currencies, newState.reserveIn) * -1).AsCurrencyVector(newState.currencies)); + if (!isClearLaunch) + { + newState.reserveIn = tempReserves.AsCurrencyVector(newState.currencies); + } + } + if (newState.reserveIn != importNotarization.currencyState.reserveIn || + newState.reserveOut != importNotarization.currencyState.reserveOut || + newState.primaryCurrencyIn != importNotarization.currencyState.primaryCurrencyIn) + { + if (LogAcceptCategory("defi")) + { + LogPrintf("%s: Mismatched currency states - Expected: %s\nActual: %s\n", __func__, newState.ToUniValue().write(1,2).c_str(), importNotarization.currencyState.ToUniValue().write(1,2).c_str()); + /* + flags &= ~IS_VALID; + flags |= IS_REJECT; + return; + */ + } + } + } + } newState.reserveIn = importNotarization.currencyState.reserveIn; newState.reserveOut = importNotarization.currencyState.reserveOut; newState.primaryCurrencyIn = importNotarization.currencyState.primaryCurrencyIn; @@ -3863,6 +4078,12 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre CCurrencyValueMap convertedFees; // post conversion transfer fees CCurrencyValueMap liquidityFees; // for fractionals, this value is added to the currency itself + CCurrencyValueMap maxPreconvert; + if (importCurrencyDef.maxPreconvert.size()) + { + maxPreconvert = CCurrencyValueMap(importCurrencyDef.currencies, importCurrencyDef.maxPreconvert); + } + bool feeOutputStart = false; // fee outputs must come after all others, this indicates they have started int nFeeOutputs = 0; // number of fee outputs @@ -4098,6 +4319,7 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre std::make_pair(systemDestID, convertedFractionalFee))); } } + // loop through, subtract "from" and add "to" convertedFees = transferFees; if (feeConversions.size()) @@ -4346,7 +4568,7 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre } // enforce maximum if present - if (curTransfer.IsPreConversion() && importCurrencyDef.maxPreconvert.size()) + if (curTransfer.IsPreConversion() && maxPreconvert.valueMap.size()) { CCurrencyValueMap newReserveIn = CCurrencyValueMap(std::vector({curTransfer.FirstCurrency()}), std::vector({curTransfer.FirstValue() - CReserveTransactionDescriptor::CalculateConversionFee(curTransfer.FirstValue())})); @@ -4355,13 +4577,16 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre { CCurrencyValueMap cumulativeReservesIn = importCurrencyDef.IsFractional() ? - CCurrencyValueMap(importCurrencyState.currencies, importCurrencyState.primaryCurrencyIn) : + (importCurrencyState.IsPrelaunch() ? + CCurrencyValueMap(importCurrencyState.currencies, importCurrencyState.reserveIn) : + CCurrencyValueMap(importCurrencyState.currencies, importCurrencyState.primaryCurrencyIn)) : importCurrencyState.NativeToReserveRaw(importCurrencyState.primaryCurrencyIn, importCurrencyState.conversionPrice); // check if it exceeds pre-conversion maximums, and refund if so - CCurrencyValueMap newTotalReserves = cumulativeReservesIn + newReserveIn + preConvertedReserves; + CCurrencyValueMap newTotalReserves = (cumulativeReservesIn + newReserveIn + preConvertedReserves).IntersectingValues(newReserveIn); - if ((CCurrencyValueMap(importCurrencyDef.currencies, importCurrencyDef.maxPreconvert) - newTotalReserves).HasNegative()) + // check without regard to other currencies + if ((maxPreconvert - newTotalReserves).HasNegative()) { LogPrint("defi", "%s: refunding pre-conversion over maximum: %s\n", __func__, curTransfer.ToUniValue().write(1,2).c_str()); curTransfer = curTransfer.GetRefundTransfer(); @@ -4369,7 +4594,7 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre } else { - CCurrencyValueMap cumulativeReservesIn = importCurrencyState.NativeToReserveRaw(importCurrencyState.primaryCurrencyIn, importCurrencyState.conversionPrice); + CCurrencyValueMap cumulativeReservesIn = CCurrencyValueMap(importCurrencyState.currencies, importCurrencyState.primaryCurrencyIn); CCurrencyValueMap newTotalReserves = cumulativeReservesIn + newReserveIn + preConvertedReserves; @@ -4762,7 +4987,13 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre if (newCurrencyConverted == -1) { - // if we have an overflow, this isn't going to work + // if we have an overflow, this isn't going to work, so, return error + printf("%s: ERROR - conversion overflow in reserve transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str()); + LogPrintf("%s: ERROR - conversion overflow in reserve transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str()); + if (updatedPostLaunch) + { + return false; + } newCurrencyConverted = 0; } @@ -5326,12 +5557,20 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre if (adjustedReserveConverted.CanonicalMap().valueMap.size() || fractionalConverted.CanonicalMap().valueMap.size()) { CCurrencyState dummyCurState; + CValidationState state; std::vector newPrices = scratchCurrencyState.ConvertAmounts(adjustedReserveConverted.AsCurrencyVector(importCurrencyState.currencies), fractionalConverted.AsCurrencyVector(importCurrencyState.currencies), dummyCurState, + state, &crossConversions, &newCurrencyState.viaConversionPrice); + if (state.IsError()) + { + printf("%s: Invalid currency conversions for import to %s : %s\n", __func__, importCurrencyDef.name.c_str(), EncodeDestination(CIdentityID(importCurrencyDef.GetID())).c_str()); + LogPrintf("%s: Invalid currency conversions for import to %s : %s\n", __func__, importCurrencyDef.name.c_str(), EncodeDestination(CIdentityID(importCurrencyDef.GetID())).c_str()); + return false; + } bool hasCrossConversions = false; for (auto &oneConversionVec : crossConversions) { diff --git a/src/pbaas/reserves.h b/src/pbaas/reserves.h index a77413ba9c8..007601fe9a0 100644 --- a/src/pbaas/reserves.h +++ b/src/pbaas/reserves.h @@ -1338,15 +1338,13 @@ class CCurrencyState std::vector PricesInReserve(bool roundUp=false) const; - // This considers one currency at a time - CAmount ConvertAmounts(CAmount inputReserve, CAmount inputFractional, CCurrencyState &newState, int32_t reserveIndex=0) const; - // convert amounts for multi-reserve fractional reserve currencies // one entry in the vector for each currency in and one fractional input for each // currency expected as output std::vector ConvertAmounts(const std::vector &inputReserve, // reserves to convert to fractional const std::vector &inputFractional, // fractional to convert to each reserve CCurrencyState &newState, + CValidationState &validationState, const std::vector> *pCrossConversions=nullptr, std::vector *pViaPrices=nullptr) const;