dPriority += (double)nValueIn * 1000; // flat multiplier
} else {
// separate limit orders to be added later, we add them at the end, failed fill or kills are normal transactions, consider them reserve txs
- if (isReserve && rtxd.IsReserveExchange() && rtxd.IsLimit() && !rtxd.IsFillOrKillFail())
+ if (isReserve && rtxd.IsReserveExchange() && rtxd.IsLimit())
{
// if we might expire, refresh and check again
if (rtxd.IsFillOrKill())
else
{
numBuys += 1;
- reserveConversionFees += fee;
- reserveOutConverted += rex.nValue - fee;
- reserveOut += rex.nValue - fee;
+ if (!(flags & IS_IMPORT))
+ {
+ reserveConversionFees += fee;
+ reserveOutConverted += rex.nValue - fee;
+ reserveOut += rex.nValue - fee;
+ }
}
}
vRex.push_back(std::make_pair(outputIndex, rex));
flags |= IS_REJECT;
return;
}
-
AddReserveExchange(rex, i, nHeight);
if (IsReject())
chainObjs[0]->objectType == CHAINOBJ_TRANSACTION &&
chainObjs[1]->objectType == CHAINOBJ_CROSSCHAINPROOF))
{
- ccx = CCrossChainExport(((CChainObject<CTransaction> *)chainObjs[0])->object);
+ CTransaction &exportTx = ((CChainObject<CTransaction> *)chainObjs[0])->object;
+ std::vector<CBaseChainObject *> exportTransfers;
+ if ((ccx = CCrossChainExport(exportTx)).IsValid() &&
+ exportTx.vout.back().scriptPubKey.IsOpReturn() &&
+ (exportTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey)).size() &&
+ exportTransfers[0]->objectType == CHAINOBJ_RESERVETRANSFER)
+ {
+ // an import transaction is built from the export list
+ // we can rebuild it and confirm that it matches exactly
+ // we can also subtract all pre-converted reserve from input
+ // and verify that the output matches the proper conversion
+
+ // get the chain definition of the chain we are importing from
+ std::vector<CTxOut> checkOutputs;
+
+ if (!AddReserveTransferImportOutputs(ConnectedChains.ThisChain(), exportTransfers, checkOutputs))
+ {
+ flags |= IS_REJECT;
+ }
+ // TODO:PBAAS - hardening - validate all the outputs we got back as the same as what is in the transaction
+ }
+ else
+ {
+ flags |= IS_REJECT;
+ }
+ }
+ else
+ {
+ flags |= IS_REJECT;
}
DeleteOpRetObjects(chainObjs);
}
case EVAL_CROSSCHAIN_EXPORT:
{
// cross chain export is incompatible with reserve exchange outputs
- if (IsReserveExchange())
+ if (IsReserveExchange() || (flags & IS_IMPORT))
{
flags |= IS_REJECT;
return;
}
flags |= IS_EXPORT;
- return;
}
break;
}
}
}
+bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CPBaaSChainDefinition &chainDef, const std::vector<CBaseChainObject *> &exportObjects, std::vector<CTxOut> &vOutputs)
+{
+ // we do not change native in or conversion fees, but we define all of these members
+ nativeIn = 0;
+ nativeOut = 0;
+ reserveIn = 0;
+ reserveOut = 0;
+ reserveOutConverted = 0;
+ reserveConversionFees = 0;
+ numTransfers = 0;
+ bool isVerusActive = IsVerusActive();
+
+ CCcontract_info CC;
+ CCcontract_info *cp;
+ CPubKey pk;
+ uint160 chainID = chainDef.GetChainID();
+
+ for (int i = 0; i < exportObjects.size(); i++)
+ {
+ if (exportObjects[i]->objectType == CHAINOBJ_RESERVETRANSFER)
+ {
+ CReserveTransfer &curTransfer = ((CChainObject<CReserveTransfer> *)exportObjects[i])->object;
+
+ if (curTransfer.IsValid())
+ {
+ CTxOut newOut;
+ numTransfers += 1;
+
+ if (curTransfer.flags & curTransfer.FEE_OUTPUT)
+ {
+ // fee comes after everything else, so we should have all numbers ready to calculate
+ // check to be sure that the fee output matches the import fee expected
+ if (i != exportObjects.size() - 1)
+ {
+ // invalid
+ printf("%s: Error with fee output transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
+ LogPrintf("%s: Error with fee output transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
+ return false;
+ }
+ CCrossChainExport ccx(chainID, numTransfers - 1, reserveOut, ReserveFees());
+ if (ccx.CalculateExportFee() < curTransfer.nValue || curTransfer.nFees < 0)
+ {
+ // invalid
+ printf("%s: Too much fee taken for export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
+ LogPrintf("%s: Too much fee taken for export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
+ return false;
+ }
+ }
+
+ if ((curTransfer.flags & curTransfer.CONVERT) && !(curTransfer.flags & curTransfer.PRECONVERT))
+ {
+ // emit a reserve exchange output
+ cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
+ pk = CPubKey(ParseHex(CC.CChexstr));
+
+ std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(curTransfer.destination)});
+ CReserveExchange rex = CReserveExchange(CReserveExchange::VALID, curTransfer.nValue);
+
+ reserveConversionFees += CalculateConversionFee(curTransfer.nValue);
+ reserveIn += curTransfer.nValue;
+ reserveOutConverted += curTransfer.nValue - reserveConversionFees;
+ reserveOut += curTransfer.nValue - reserveConversionFees;
+ newOut = MakeCC1of1Vout(EVAL_RESERVE_EXCHANGE, 0, pk, dests, rex);
+ }
+ else if (curTransfer.flags & curTransfer.PRECONVERT)
+ {
+ // output the amount, minus conversion fees, and generate a normal output that spends the net input of the import as native
+ // difference between all potential value out and what we took unconverted as a fee in our fee output
+ CAmount nativeConverted = CCurrencyState::ReserveToNative(curTransfer.nValue, chainDef.conversion);
+ if (curTransfer.nFees < CalculateConversionFee(curTransfer.nValue + curTransfer.nFees))
+ {
+ // invalid
+ printf("%s: Error insufficient conversion fee in transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
+ LogPrintf("%s: Error insufficient conversion fee in transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
+ return false;
+ }
+ reserveIn += curTransfer.nFees;
+ nativeIn += nativeConverted;
+ nativeOut += nativeConverted;
+ newOut = CTxOut(nativeConverted, GetScriptForDestination(curTransfer.destination));
+ }
+ else if ((curTransfer.flags & curTransfer.SEND_BACK) && curTransfer.nValue > (curTransfer.DEFAULT_PER_STEP_FEE << 2))
+ {
+ // generate a reserve transfer back to the source chain if we have at least double the fee, otherwise leave it on
+ // this chain to be claimed
+ cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
+ pk = CPubKey(ParseHex(CC.CChexstr));
+
+ std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_RESERVE_TRANSFER)), CKeyID(chainID)});
+ CAmount fees = curTransfer.DEFAULT_PER_STEP_FEE << 1;
+ CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID, curTransfer.nValue - fees, fees, curTransfer.destination);
+
+ reserveIn += curTransfer.nFees + curTransfer.nValue;
+ reserveOut += curTransfer.nValue;
+ newOut = MakeCC1of1Vout(EVAL_RESERVE_TRANSFER, 0, pk, dests, rt);
+ }
+ else
+ {
+ // if Verus is active, we are creating a reserve import for a PBaaS reserve chain
+ if (isVerusActive)
+ {
+ // generate a reserve output of the amount indicated, less fees
+ // emit a reserve exchange output
+ // we will send using a reserve output, fee will be paid by converting from reserve
+ cp = CCinit(&CC, EVAL_RESERVE_OUTPUT);
+
+ std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(curTransfer.destination)});
+ CReserveOutput ro = CReserveOutput(CReserveExchange::VALID, curTransfer.nValue);
+
+ newOut = MakeCC0of0Vout(EVAL_RESERVE_OUTPUT, 0, dests, ro);
+
+ reserveIn += curTransfer.nFees + curTransfer.nValue;
+ reserveOut += curTransfer.nValue;
+ }
+ else
+ {
+ // we are creating an import for the Verus chain to spend from a PBaaS account of a reserve currency, move the value specified, which will spend from
+ // the RESERVE_DEPOSIT outputs
+ newOut = CTxOut(curTransfer.nValue, GetScriptForDestination(curTransfer.destination));
+ reserveIn += curTransfer.nFees + curTransfer.nValue;
+ reserveOut += curTransfer.nValue;
+ }
+ }
+ vOutputs.push_back(newOut);
+ }
+ else
+ {
+ printf("%s: Invalid reserve transfer on export\n", __func__);
+ LogPrintf("%s: Invalid reserve transfer on export\n", __func__);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
CMutableTransaction &CReserveTransactionDescriptor::AddConversionInOuts(CMutableTransaction &conversionTx, std::vector<CInputDescriptor> &conversionInputs, CAmount exchangeRate, const CCurrencyState *pCurrencyState) const
{
if (!IsReserveExchange() || IsFillOrKillFail())
using boost::multiprecision::cpp_dec_float_50;
class CCoinsViewCache;
class CInputDescriptor;
+class CBaseChainObject;
// reserve output is a special kind of token output that does not carry it's identifier, as it
// is always assumed to be the reserve currency of the current chain.
void AddReserveOutput(CReserveOutput &ro)
{
flags |= IS_RESERVE;
- reserveOut += ro.nValue;
+ if (!(flags & IS_IMPORT))
+ {
+ reserveOut += ro.nValue;
+ }
}
// is boolean, since it can fail, which would render the tx invalid
void AddReserveTransfer(CReserveTransfer &rt)
{
flags |= IS_RESERVE;
- reserveOut += rt.nValue;
- numTransfers++;
+ if (!(flags & IS_IMPORT))
+ {
+ reserveOut += rt.nValue;
+ numTransfers++;
+ }
}
CMutableTransaction &AddConversionInOuts(CMutableTransaction &conversionTx, std::vector<CInputDescriptor> &conversionInputs, CAmount exchangeRate=0, const CCurrencyState *pCurrencyState=NULL) const;
+ bool AddReserveTransferImportOutputs(const CPBaaSChainDefinition &chainDef, const std::vector<CBaseChainObject *> &exportObjects, std::vector<CTxOut> &vOutputs);
};
class CCurrencyState
// aixIt has an input from the export thread of last transaction, an optional deposit to the reserve, and an opret of all outputs + 1 fee
assert(aixIt->second.second.vout.back().scriptPubKey.IsOpReturn());
- std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
-
CCrossChainExport ccx(aixIt->second.second);
lastCCI = CCrossChainImport(lastImport);
if (!lastCCI.IsValid() || !ccx.IsValid())
CCcontract_info *cp;
CPubKey pk;
- CAmount totalNativeOut = 0;
- CAmount totalReserveOut = 0;
- CAmount totalImport = 0;
bool isVerusActive = IsVerusActive();
CAmount availableNative = lastImport.vout[newImportTx.vin[0].prevout.n].nValue;
CAmount importFees = ccx.CalculateImportFee();
CAmount feesOut = 0;
- for (auto pRT : exportOutputs)
- {
- if (pRT->objectType != CHAINOBJ_RESERVETRANSFER)
- {
- LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
- printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
- return false;
- }
- CReserveTransfer &curTransfer = ((CChainObject<CReserveTransfer> *)pRT)->object;
-
- // DEBUGGING
- printf("%s\n", curTransfer.ToUniValue().write().c_str());
- LogPrintf("%s\n", curTransfer.ToUniValue().write().c_str());
-
- if (curTransfer.IsValid())
- {
- CTxOut newOut;
+ CReserveTransactionDescriptor rtxd;
- totalImport += curTransfer.nValue;
+ std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
- if (curTransfer.flags & curTransfer.FEE_OUTPUT)
- {
- feesOut += curTransfer.nValue;
- }
- if (curTransfer.flags & curTransfer.CONVERT)
- {
- // emit a reserve exchange output
- // we will send using a reserve output, fee will be paid when converting from reserve
- cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
- pk = CPubKey(ParseHex(CC.CChexstr));
-
- std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(curTransfer.destination)});
- CReserveExchange rex = CReserveExchange(CReserveExchange::VALID, curTransfer.nValue);
-
- newOut = MakeCC1of1Vout(EVAL_RESERVE_EXCHANGE, 0, pk, dests, rex);
- totalReserveOut += rex.nValue;
- printf("%s: Outputting reserve exchange conversion %s\n", __func__, rex.ToUniValue().write().c_str());
- LogPrintf("%s: Outputting reserve exchange conversion %s\n", __func__, rex.ToUniValue().write().c_str());
- }
- else if (curTransfer.flags & curTransfer.PRECONVERT)
- {
- // output the amount, minus conversion fees, and generate a normal output that spends the net input of the import as native
- // difference between all potential value out and what we took unconverted as a fee in our fee output
- CAmount nativeConverted = CCurrencyState::ReserveToNative(curTransfer.nValue, chainDef.conversion);
- newOut = CTxOut(nativeConverted, GetScriptForDestination(curTransfer.destination));
- totalNativeOut += nativeConverted;
- printf("%s: Outputting native output pre-conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
- LogPrintf("%s: Outputting native output pre-conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
- }
- else if ((curTransfer.flags & curTransfer.SEND_BACK) && curTransfer.nValue > (curTransfer.DEFAULT_PER_STEP_FEE << 2))
- {
- // generate a reserve transfer back to the source chain if we have at least double the fee, otherwise leave it on
- // this chain to be claimed
- cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
- pk = CPubKey(ParseHex(CC.CChexstr));
-
- std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_RESERVE_TRANSFER)), CKeyID(lastCCI.chainID)});
- CAmount fees = curTransfer.DEFAULT_PER_STEP_FEE << 1;
- CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID, curTransfer.nValue - fees, fees, curTransfer.destination);
-
- newOut = MakeCC1of1Vout(EVAL_RESERVE_TRANSFER, 0, pk, dests, rt);
- totalReserveOut += curTransfer.nValue;
- printf("%s: Outputting reserve to send back %s\n", __func__, rt.ToUniValue().write().c_str());
- LogPrintf("%s: Outputting reserve to send back %s\n", __func__, rt.ToUniValue().write().c_str());
- }
- else
- {
- // if Verus is active, we are creating a reserve import for a PBaaS reserve chain
- if (isVerusActive)
- {
- // generate a reserve output of the amount indicated, less fees
- // emit a reserve exchange output
- // we will send using a reserve output, fee will be paid by converting from reserve
- cp = CCinit(&CC, EVAL_RESERVE_OUTPUT);
-
- std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(curTransfer.destination)});
- CReserveOutput ro = CReserveOutput(CReserveExchange::VALID, curTransfer.nValue);
-
- newOut = MakeCC0of0Vout(EVAL_RESERVE_OUTPUT, 0, dests, ro);
- totalReserveOut += curTransfer.nValue;
- printf("%s: Outputting reserve import from Verus chain %s\n", __func__, ro.ToUniValue().write().c_str());
- LogPrintf("%s: Outputting reserve import from Verus chain %s\n", __func__, ro.ToUniValue().write().c_str());
- }
- else
- {
- // we are creating an import for the Verus chain to spend from a PBaaS account of a reserve currency, move the value specified, which will spend from
- // the RESERVE_DEPOSIT outputs
- newOut = CTxOut(curTransfer.nValue, GetScriptForDestination(curTransfer.destination));
- totalNativeOut += newOut.nValue;
- printf("%s: Outputting reserve import back to Verus chain %s\n", __func__, curTransfer.ToUniValue().write().c_str());
- LogPrintf("%s: Outputting reserve import back to Verus chain %s\n", __func__, curTransfer.ToUniValue().write().c_str());
- }
- }
- newImportTx.vout.push_back(newOut);
- }
- else
- {
- printf("%s: Invalid reserve transfer on export\n", __func__);
- LogPrintf("%s: Invalid reserve transfer on export\n", __func__);
- }
+ if (!rtxd.AddReserveTransferImportOutputs(chainDef, exportOutputs, newImportTx.vout))
+ {
+ LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
+ printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
+ // free the memory
+ DeleteOpRetObjects(exportOutputs);
+ return false;
}
// free the memory
pk = CPubKey(ParseHex(CC.CChexstr));
- if (totalImport + importFees != ccx.totalAmount + ccx.totalFees)
+ if (rtxd.ReserveFees() != (ccx.totalFees - exportFees))
{
- LogPrintf("%s: ERROR - import does not match amount, totalImport=%lu, importFees=%lu, ccx.totalAmount=%lu, ccx.totalFees=%lu\n", __func__, totalImport, importFees, ccx.totalAmount, ccx.totalFees);
- printf("%s: ERROR - import does not match amount, totalImport=%lu, importFees=%lu, ccx.totalAmount=%lu, ccx.totalFees=%lu\n", __func__, totalImport, importFees, ccx.totalAmount, ccx.totalFees);
+ LogPrintf("%s: ERROR - import does not match amount, totalImport=%lu, importFees=%lu, ccx.totalAmount=%lu, ccx.totalFees=%lu\n", __func__, ccx.totalAmount + ccx.totalFees, importFees, ccx.totalAmount, ccx.totalFees);
+ printf("%s: ERROR - import does not match amount, totalImport=%lu, importFees=%lu, ccx.totalAmount=%lu, ccx.totalFees=%lu\n", __func__, ccx.totalAmount + ccx.totalFees, importFees, ccx.totalAmount, ccx.totalFees);
}
std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_CROSSCHAIN_IMPORT)))});
CCrossChainImport cci = CCrossChainImport(ConnectedChains.ThisChain().GetChainID(), ccx.totalAmount + ccx.totalFees);
- newImportTx.vout[0] = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, availableNative - totalNativeOut, pk, dests, cci);
+ newImportTx.vout[0] = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, availableNative - rtxd.nativeIn, pk, dests, cci);
if (newImportTx.vout[0].nValue < 0)
{