}
}
+bool operator==(const CProofRoot &op1, const CProofRoot &op2)
+{
+ return op1.version == op2.version &&
+ op1.type == op2.type &&
+ op1.rootHeight == op2.rootHeight &&
+ op1.stateRoot == op2.stateRoot &&
+ op1.systemID == op2.systemID &&
+ op1.blockHash == op2.blockHash &&
+ op1.compactPower == op2.compactPower;
+}
+
CProofRoot CProofRoot::GetProofRoot(uint32_t blockHeight)
{
if (blockHeight > chainActive.Height())
return retVal;
}
+bool CallNotary(const CRPCChainData ¬arySystem, std::string command, const UniValue ¶ms, UniValue &result, UniValue &error);
+
+// look for finalized notarizations either on chain or in the mempool, which are eligible for submission
+// and submit them to the notary chain.
+std::vector<uint256> CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPCChainData &externalSystem,
+ CValidationState &state)
+{
+ // look for finalized notarizations for our notary chains in recent blocks
+ // if we find any and are connected, submit them
+ std::vector<CAddressIndexDbEntry> finalizedNotarizations;
+ uint160 systemID = externalSystem.chainDefinition.GetID();
+ std::vector<std::pair<CPBaaSNotarization, CNotaryEvidence>> notarizations;
+
+ {
+ LOCK2(cs_main, mempool.cs);
+
+ uint32_t nHeight = chainActive.Height();
+ uint160 finalizeConfirmedKey =
+ CCrossChainRPCData::GetConditionID(
+ CCrossChainRPCData::GetConditionID(systemID, CObjectFinalization::ObjectFinalizationNotarizationKey()),
+ CObjectFinalization::ObjectFinalizationConfirmedKey());
+
+ if (GetAddressIndex(finalizeConfirmedKey, CScript::P2IDX, finalizedNotarizations, nHeight-10) && finalizedNotarizations.size())
+ {
+ // get the recent finalized notarizations and submit. the daemon we submit to may early out immediately if
+ // the notarization is already posted or in mempool
+ for (auto &oneFinalization : finalizedNotarizations)
+ {
+ if (oneFinalization.first.spending)
+ {
+ continue;
+ }
+ // now, we have a finalized notarization for the target network
+ // prepare an accepted notarization and submit
+ CTransaction finTx, nTx;
+ uint256 blkHash;
+ COptCCParams p;
+ CObjectFinalization of;
+ CPBaaSNotarization pbn;
+ if (!myGetTransaction(oneFinalization.first.txhash, finTx, blkHash))
+ {
+ // set error, but continue processing
+ state.Error("inaccessible-transaction");
+ }
+ if (!(finTx.vout.size() > oneFinalization.first.index &&
+ finTx.vout[oneFinalization.first.index].scriptPubKey.IsPayToCryptoCondition(p) &&
+ p.IsValid() &&
+ (p.evalCode == EVAL_FINALIZE_NOTARIZATION) &&
+ p.vData.size() &&
+ (of = CObjectFinalization(p.vData[0])).IsValid() &&
+ of.GetOutputTransaction(finTx, nTx, blkHash) &&
+ nTx.vout.size() > of.output.n &&
+ nTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
+ p.IsValid() &&
+ p.evalCode == EVAL_EARNEDNOTARIZATION &&
+ p.vData.size() &&
+ (pbn = CPBaaSNotarization(p.vData[0])).IsValid()))
+ {
+ // this is almost certainly a corrupt local database, so we will start to return errors
+ // rather than attempt to continue
+ state.Error("invalid-or-corrupt-notarization");
+ return std::vector<uint256>();
+ }
+
+ // get all notarization evidence and submit the notarization + evidence
+ CNotaryEvidence allEvidence, scratchEvidence;
+
+ // get all inputs with evidence and add it to our evidence
+ for (auto &oneEvidenceIn : of.evidenceInputs)
+ {
+ CTransaction evidenceTx;
+ uint256 eBlkHash;
+ COptCCParams eP;
+ if (!(finTx.vin.size() > oneEvidenceIn &&
+ myGetTransaction(finTx.vin[oneEvidenceIn].prevout.hash, evidenceTx, eBlkHash) &&
+ evidenceTx.vout.size() > finTx.vin[oneEvidenceIn].prevout.n &&
+ evidenceTx.vout[finTx.vin[oneEvidenceIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(eP) &&
+ eP.IsValid() &&
+ eP.evalCode == EVAL_NOTARY_EVIDENCE &&
+ eP.vData.size() &&
+ (scratchEvidence = CNotaryEvidence(eP.vData[0])).IsValid()))
+ {
+ state.Error("innaccessible-evidence");
+ return std::vector<uint256>();
+ }
+ allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
+ for (auto &oneSig : scratchEvidence.signatures)
+ {
+ if (oneSig.second.signatures.size())
+ {
+ for (auto &oneSigEntry : oneSig.second.signatures)
+ {
+ allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
+ }
+ }
+ }
+ }
+ // get all outputs with evidence and add
+ for (auto oneEvidenceOut : of.evidenceOutputs)
+ {
+ if (!(finTx.vout.size() > oneEvidenceOut &&
+ finTx.vout[oneEvidenceOut].scriptPubKey.IsPayToCryptoCondition(p) &&
+ p.IsValid() &&
+ (p.evalCode == EVAL_NOTARY_EVIDENCE) &&
+ p.vData.size() &&
+ (scratchEvidence = CNotaryEvidence(p.vData[0])).IsValid()))
+ {
+ state.Error("invalid-evidence");
+ return std::vector<uint256>();
+ }
+ allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
+ for (auto &oneSig : scratchEvidence.signatures)
+ {
+ if (oneSig.second.signatures.size())
+ {
+ for (auto &oneSigEntry : oneSig.second.signatures)
+ {
+ allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
+ }
+ }
+ }
+ }
+ notarizations.push_back(std::make_pair(pbn, allEvidence));
+ }
+ }
+ }
+
+ std::vector<uint256> retVal;
+ // collect them holding the lock, release it, then call out
+ for (auto &oneNotarization : notarizations)
+ {
+ // now, we should have enough evidence to prove
+ // the notarization. the API call will ensure that we do
+ UniValue params(UniValue::VARR);
+ UniValue result, error;
+ std::string strTxId;
+ params.push_back(oneNotarization.first.ToUniValue());
+ params.push_back(oneNotarization.second.ToUniValue());
+ if (!CallNotary(externalSystem, "submitacceptednotarization", params, result, error) ||
+ !error.isNull() ||
+ (strTxId = uni_get_str(result)).empty() ||
+ !IsHex(strTxId))
+ {
+ continue;
+ }
+ // store the transaction ID and accepted notariation to prevent redundant submits
+ retVal.push_back(uint256S(strTxId));
+ }
+ return retVal;
+}
/*
* Validates a notarization output spend by ensuring that the spending transaction fulfills all requirements.
ClearForNextBlock();
}
-// From a vector of transaction pointers, match all that are valid orders and can be matched,
-// put them into a vector of reserve transaction descriptors, and return a new fractional reserve state,
-// if pConversionTx is present, this will add one output to it for each transaction included
-// and consider its size wen comparing to maxSerializeSize
-CCoinbaseCurrencyState CCoinbaseCurrencyState::MatchOrders(const std::vector<CReserveTransactionDescriptor> &orders,
- std::vector<CReserveTransactionDescriptor> &reserveFills,
- std::vector<CReserveTransactionDescriptor> &noFills,
- std::vector<const CReserveTransactionDescriptor *> &expiredFillOrKills,
- std::vector<const CReserveTransactionDescriptor *> &rejects,
- std::vector<CAmount> &prices,
- int32_t height, std::vector<CInputDescriptor> &conversionInputs,
- int64_t maxSerializeSize, int64_t *pInOutTotalSerializeSize, CMutableTransaction *pConversionTx,
- bool feesAsReserve) const
-{
- // synthetic order book of limitBuys and limitSells sorted by limit, order of preference beyond limit sorting is random
- std::map<uint160, std::multimap<CAmount, CReserveTransactionDescriptor>> limitBuys;
- std::map<uint160, std::multimap<CAmount, CReserveTransactionDescriptor>> limitSells; // limit orders are prioritized by limit
- std::multimap<CAmount, CReserveTransactionDescriptor> marketOrders; // prioritized by fee rate
- CAmount totalNativeConversionFees = 0;
- CCurrencyValueMap totalReserveConversionFees;
- std::vector<CAmount> exchangeRates(PricesInReserve());
- CCurrencyValueMap exchangeRateMap = CCurrencyValueMap(currencies, exchangeRates);
-
- CMutableTransaction mConversionTx = pConversionTx ? *pConversionTx : CMutableTransaction();
- std::vector<CInputDescriptor> tmpConversionInputs(conversionInputs);
-
- int64_t totalSerializedSize = pInOutTotalSerializeSize ? *pInOutTotalSerializeSize + CCurrencyState::CONVERSION_TX_SIZE_MIN : CCurrencyState::CONVERSION_TX_SIZE_MIN;
- int64_t conversionSizeOverhead = 0;
-
- if (!(flags & FLAG_FRACTIONAL))
- {
- return CCoinbaseCurrencyState();
- }
-
- uint32_t tipTime = (chainActive.Height() >= height) ? chainActive[height]->nTime : chainActive.LastTip()->nTime;
- CCoinsViewCache view(pcoinsTip);
-
- // organize all valid reserve exchange transactions into multimaps, limitBuys, limitSells, and market orders
- // orders that should be treated as normal/failed fill or kill will go into refunds and invalid transactions into rejects
- for (int i = 0; i < orders.size(); i++)
- {
- if (orders[i].IsValid() && orders[i].IsReserveExchange())
- {
- // if this is a market order, put it in, if limit, put it in an order book, sorted by limit
- if (orders[i].IsMarket())
- {
- CAmount fee = orders[i].AllFeesAsNative(*this) + ReserveToNative(orders[i].ReserveConversionFeesMap()) + orders[i].nativeConversionFees;
- CFeeRate feeRate = CFeeRate(fee, GetSerializeSize(CDataStream(SER_NETWORK, PROTOCOL_VERSION), *orders[i].ptx));
- marketOrders.insert(std::make_pair(feeRate.GetFeePerK(), orders[i]));
- }
- else
- {
- assert(orders[i].IsLimit());
- // limit order, so put it in buy or sell
- if (orders[i].numBuys)
- {
- limitBuys[orders[i].vRex[0].second.FirstCurrency()].insert(std::make_pair(orders[i].vRex[0].second.nLimit, orders[i]));
- }
- else
- {
- assert(orders[i].numSells);
- limitSells[orders[i].vRex[0].second.FirstCurrency()].insert(std::make_pair(orders[i].vRex[0].second.nLimit, orders[i]));
- }
- }
- }
- else if (orders[i].IsValid())
- {
- expiredFillOrKills.push_back(&(orders[i]));
- }
- else
- {
- rejects.push_back(&(orders[i]));
- }
- }
-
- // now we have all market orders in marketOrders multimap, sorted by feePerK, rejects that are expired or invalid in rejects
- // first, prune as many market orders as possible up to the maximum storage space available for market orders, which we calculate
- // as a functoin of the total space available and precentage of total orders that are market orders
- int64_t numLimitOrders = limitBuys.size() + limitSells.size();
- int64_t numMarketOrders = marketOrders.size();
-
- // if nothing to do, we are done, don't update anything
- if (!(reserveFills.size() + numLimitOrders + numMarketOrders))
- {
- return *this;
- }
-
- // 1. start from the current state and calculate what the price would be with market orders that fit, leaving room for limit orders
- // 2. add limit orders, first buys, as many as we can from the highest value and number downward, one at a time, until we run out or
- // cannot add any more due to not meeting the price. then we do the same for sells if there are any available, and alternate until
- // we either run out or cannot add from either side. within a specific limit, orders are sorted by largest first, which means
- // there is no point in retracing if an element fails to be added
- // 3. calculate a final order price
- // 4. create and return a new, updated CCoinbaseCurrencyState
- CCoinbaseCurrencyState newState(*(CCurrencyState *)this);
-
- conversionSizeOverhead = GetSerializeSize(mConversionTx, SER_NETWORK, PROTOCOL_VERSION);
-
- int64_t curSpace = maxSerializeSize - (totalSerializedSize + conversionSizeOverhead);
-
- if (curSpace < 0)
- {
- printf("%s: no space available in block to include reserve orders\n", __func__);
- LogPrintf("%s: no space available in block to include reserve orders\n", __func__);
- return *this;
- }
-
- // the ones that are passed in reserveFills may be any valid reserve related transaction
- // we need to first process those with the assumption that they are included
- for (auto it = reserveFills.begin(); it != reserveFills.end(); )
- {
- auto &txDesc = *it;
-
- // add up the starting point for conversions
- if (txDesc.IsReserveExchange())
- {
- CMutableTransaction mtx;
- mtx = mConversionTx;
- txDesc.AddConversionInOuts(mtx, tmpConversionInputs, exchangeRateMap);
- conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
- int64_t tmpSpace = maxSerializeSize - (totalSerializedSize + conversionSizeOverhead);
-
- if (tmpSpace < 0)
- {
- // can't fit, so it's a noFill
- noFills.push_back(txDesc);
- it = reserveFills.erase(it);
- }
- else
- {
- curSpace = tmpSpace;
-
- totalNativeConversionFees += txDesc.nativeConversionFees;
- totalReserveConversionFees += txDesc.ReserveConversionFeesMap();
-
- if (feesAsReserve)
- {
- newState.reserveIn = AddVectors(txDesc.ReserveOutConvertedVec(*this), newState.reserveIn);
- newState.nativeIn = AddVectors(txDesc.NativeOutConvertedVec(*this), newState.nativeIn);
- newState.nativeIn[0] += txDesc.NativeFees() + txDesc.nativeConversionFees;
- }
- else
- {
- newState.reserveIn = AddVectors(
- AddVectors(txDesc.ReserveOutConvertedVec(*this), newState.reserveIn),
- AddVectors(txDesc.ReserveFees().AsCurrencyVector(currencies), txDesc.ReserveConversionFeesVec(*this))
- );
- newState.nativeIn = AddVectors(txDesc.NativeOutConvertedVec(*this), newState.nativeIn);
- }
-
- mConversionTx = mtx;
- it++;
- }
- }
- else
- {
- if (feesAsReserve && newState.nativeIn.size())
- {
- newState.nativeIn[0] += txDesc.NativeFees();
- }
- else
- {
- newState.reserveIn = AddVectors(newState.reserveIn, txDesc.ReserveFees().AsCurrencyVector(currencies));
- }
- it++;
- }
- }
-
- int64_t marketOrdersSizeLimit = curSpace;
- int64_t limitOrdersSizeLimit = curSpace;
- if (numLimitOrders + numMarketOrders)
- {
- marketOrdersSizeLimit = ((arith_uint256(numMarketOrders) * arith_uint256(curSpace)) / arith_uint256(numLimitOrders + numMarketOrders)).GetLow64();
- limitOrdersSizeLimit = curSpace - marketOrdersSizeLimit;
- }
-
- if (limitOrdersSizeLimit < 1024 && maxSerializeSize > 2048)
- {
- marketOrdersSizeLimit = maxSerializeSize - 1024;
- limitOrdersSizeLimit = 1024;
- }
-
- for (auto marketOrder : marketOrders)
- {
- // add as many as we can fit, if we are able to, consider the output transaction overhead as well
- int64_t thisSerializeSize = GetSerializeSize(*marketOrder.second.ptx, SER_NETWORK, PROTOCOL_VERSION);
- CMutableTransaction mtx;
- mtx = mConversionTx;
- marketOrder.second.AddConversionInOuts(mtx, tmpConversionInputs, exchangeRateMap);
- conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
- if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= marketOrdersSizeLimit)
- {
- totalNativeConversionFees += marketOrder.second.nativeConversionFees;
- totalReserveConversionFees += marketOrder.second.ReserveConversionFeesMap();
- mConversionTx = mtx;
-
- if (feesAsReserve)
- {
- newState.reserveIn = AddVectors(marketOrder.second.ReserveOutConvertedVec(*this), newState.reserveIn);
- newState.nativeIn = AddVectors(marketOrder.second.NativeOutConvertedVec(*this), newState.nativeIn);
- newState.nativeIn[0] += marketOrder.second.NativeFees() + marketOrder.second.nativeConversionFees;
- }
- else
- {
- newState.reserveIn = AddVectors(
- AddVectors(marketOrder.second.ReserveOutConvertedVec(*this), newState.reserveIn),
- AddVectors(marketOrder.second.ReserveFees().AsCurrencyVector(currencies), marketOrder.second.ReserveConversionFeesVec(*this))
- );
- newState.nativeIn = AddVectors(marketOrder.second.NativeOutConvertedVec(*this), newState.nativeIn);
- }
-
- reserveFills.push_back(marketOrder.second);
- totalSerializedSize += thisSerializeSize;
- }
- else
- {
- // can't fit, no fill
- noFills.push_back(marketOrder.second);
- }
- }
-
- // iteratively add limit orders first buy, then sell, until we no longer have anything to add
- // this must iterate because each time we add a buy, it may put another sell's limit within reach and
- // vice versa
- std::map<uint160, std::pair<std::multimap<CAmount, CReserveTransactionDescriptor>::iterator, CAmount>> buyPartial; // valid in loop for partial fill
- std::map<uint160, std::pair<std::multimap<CAmount, CReserveTransactionDescriptor>::iterator, CAmount>> sellPartial;
-
- limitOrdersSizeLimit = maxSerializeSize - totalSerializedSize;
- int64_t buyLimitSizeLimit = numLimitOrders ? totalSerializedSize + ((arith_uint256(limitBuys.size()) * arith_uint256(limitOrdersSizeLimit)) / arith_uint256(numLimitOrders)).GetLow64() : limitOrdersSizeLimit;
-
- CCurrencyState latestState = newState;
-
- // TODO - finish limit orders, which are significantly more complex after moving to
- // multi-reserves. until then, skip limit orders.
- /*
- for (bool tryagain = true; tryagain; )
- {
- tryagain = false;
-
- exchangeRates = ConvertAmounts(newState.reserveIn, newState.nativeIn, latestState);
-
- // starting with that exchange rate, add buys one at a time until we run out of buys to add or reach the limit,
- // possibly in a partial fill, then see if we can add any sells. we go back and forth until we have stopped being able to add
- // new orders. it would be possible to recognize a state where we are able to simultaneously fill a buy and a sell that are
- // the the same or very minimally overlapping limits
-
- for (auto limitBuysIt = limitBuys.rbegin(); limitBuysIt != limitBuys.rend() && exchangeRate > limitBuysIt->second.vRex.front().second.nLimit; limitBuysIt = limitBuys.rend())
- {
- // any time there are entries above the lower bound, we only actually look at the end, since we mutate the
- // search space each iteration and remove anything we've already added, making the end always the most in-the-money
- CReserveTransactionDescriptor ¤tBuy = limitBuysIt->second;
-
- // it must first fit, space-wise
- int64_t thisSerializeSize = GetSerializeSize(*(currentBuy.ptx), SER_NETWORK, PROTOCOL_VERSION);
-
- CMutableTransaction mtx;
- if (pConversionTx)
- {
- mtx = *pConversionTx;
- currentBuy.AddConversionInOuts(mtx, tmpConversionInputs);
- conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
- }
- if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= buyLimitSizeLimit)
- {
- CAmount newReserveIn, newNativeIn;
- if (feesAsReserve)
- {
- newReserveIn = currentBuy.reserveOutConverted;
- newNativeIn = currentBuy.nativeOutConverted + currentBuy.NativeFees() + currentBuy.nativeConversionFees;
- }
- else
- {
- newReserveIn = currentBuy.reserveOutConverted + currentBuy.ReserveFees() + currentBuy.reserveConversionFees;
- newNativeIn = currentBuy.nativeOutConverted;
- }
-
- // calculate fresh with all conversions together to see if we still meet the limit
- CAmount newExchange = ConvertAmounts(newState.ReserveIn + newReserveIn, newState.NativeIn + newNativeIn, latestState);
-
- if (newExchange <= currentBuy.vRex[0].second.nLimit)
- {
- totalNativeConversionFees += currentBuy.nativeConversionFees;
- totalReserveConversionFees += currentBuy.reserveConversionFees;
-
- // update conversion transaction if we have one
- if (pConversionTx)
- {
- *pConversionTx = mtx;
- }
-
- // add to the current buys, we will never do something to disqualify this, since all orders left are either
- // the same limit or lower
- reserveFills.push_back(currentBuy);
- totalSerializedSize += thisSerializeSize;
-
- newState.ReserveIn += newReserveIn;
- newState.NativeIn += newNativeIn;
-
- exchangeRate = newExchange;
- limitBuys.erase(--limitBuys.end());
- }
- else
- {
- // TODO:PBAAS support partial fills
- break;
- }
- }
- }
-
- // now, iterate from lowest/most qualified sell limit first to highest/least qualified and attempt to put all in
- // if we can only fill an order partially, then do so
- for (auto limitSellsIt = limitSells.begin(); limitSellsIt != limitSells.end() && exchangeRate > limitSellsIt->second.vRex.front().second.nLimit; limitSellsIt = limitSells.begin())
- {
- CReserveTransactionDescriptor ¤tSell = limitSellsIt->second;
-
- int64_t thisSerializeSize = GetSerializeSize(*currentSell.ptx, SER_NETWORK, PROTOCOL_VERSION);
-
- CMutableTransaction mtx;
- if (pConversionTx)
- {
- mtx = *pConversionTx;
- currentSell.AddConversionInOuts(mtx, tmpConversionInputs);
- conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
- }
-
- if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= maxSerializeSize)
- {
- CAmount newReserveIn, newNativeIn;
- if (feesAsReserve)
- {
- newReserveIn = currentSell.reserveOutConverted;
- newNativeIn = currentSell.nativeOutConverted + currentSell.NativeFees() + currentSell.nativeConversionFees;
- }
- else
- {
- newReserveIn = currentSell.reserveOutConverted + currentSell.ReserveFees() + currentSell.reserveConversionFees;
- newNativeIn = currentSell.nativeOutConverted;
- }
-
- // calculate fresh with all conversions together to see if we still meet the limit
- CAmount newExchange = ConvertAmounts(newState.ReserveIn + newReserveIn, newState.NativeIn + newNativeIn, latestState);
- if (newExchange >= currentSell.vRex.front().second.nLimit)
- {
- totalNativeConversionFees += currentSell.nativeConversionFees;
- totalReserveConversionFees += currentSell.reserveConversionFees;
-
- // update conversion transaction if we have one
- if (pConversionTx)
- {
- *pConversionTx = mtx;
- }
-
- // add to the current sells, we will never do something to disqualify this, since all orders left are either
- // the same limit or higher
- reserveFills.push_back(currentSell);
- totalSerializedSize += thisSerializeSize;
- exchangeRate = newExchange;
-
- newState.ReserveIn += newReserveIn;
- newState.NativeIn += newNativeIn;
-
- limitSells.erase(limitSellsIt);
- tryagain = true;
- }
- else
- {
- // we must be done with buys whether we created a partial or not
- break;
- }
- }
- }
- buyLimitSizeLimit = maxSerializeSize - totalSerializedSize;
- }
- */
-
- // we can calculate total fees now, but to avoid a rounding error when converting native to reserve or vice versa on fees,
- // we must loop through once more to calculate all fees for the currency state
- auto curMap = GetReserveMap();
- CCurrencyValueMap totalReserveFees;
- CAmount totalNativeFees = 0;
- CCurrencyValueMap reserveOutVal;
- CMutableTransaction mtx = pConversionTx ? *pConversionTx : CMutableTransaction();
- for (auto fill : reserveFills)
- {
- fill.AddConversionInOuts(mtx, conversionInputs, exchangeRateMap);
- reserveOutVal += NativeToReserveRaw(fill.NativeOutConvertedVec(latestState), exchangeRates);
- if (feesAsReserve)
- {
- totalReserveFees += fill.AllFeesAsReserve(newState, exchangeRates);
- }
- else
- {
- totalNativeFees += fill.AllFeesAsNative(newState, exchangeRates);
- }
- }
- if (pConversionTx)
- {
- *pConversionTx = mtx;
- }
- if (feesAsReserve)
- {
- totalReserveConversionFees.valueMap[currencies[0]] += NativeToReserve(totalNativeConversionFees, exchangeRates[0]);
- reserveOutVal += totalReserveConversionFees;
- totalReserveFees += totalReserveConversionFees;
- }
- else
- {
- totalNativeConversionFees = totalNativeConversionFees + ReserveToNativeRaw(totalReserveConversionFees, exchangeRates);
- totalNativeFees += totalNativeConversionFees;
- }
-
- newState = CCoinbaseCurrencyState(latestState, 0,
- totalNativeFees, totalNativeConversionFees,
- newState.reserveIn,
- newState.nativeIn,
- newState.reserveOut,
- exchangeRates,
- totalReserveFees.AsCurrencyVector(currencies),
- totalReserveConversionFees.AsCurrencyVector(currencies));
-
- prices = exchangeRates;
-
- for (auto &curBuys : limitBuys)
- {
- for (auto &entry : curBuys.second)
- {
- noFills.push_back(entry.second);
- }
- }
-
- for (auto &curSells : limitSells)
- {
- for (auto &entry : curSells.second)
- {
- noFills.push_back(entry.second);
- }
- }
-
- // if no matches, no state updates
- if (!reserveFills.size())
- {
- return *this;
- }
- else
- {
- if (pInOutTotalSerializeSize)
- {
- *pInOutTotalSerializeSize = totalSerializedSize;
- }
- printf("%s: %s\n", __func__, newState.ToUniValue().write(1, 2).c_str());
- return newState;
- }
-}
-
CAmount CCurrencyState::CalculateConversionFee(CAmount inputAmount, bool convertToNative, int currencyIndex) const
{
arith_uint256 bigAmount(inputAmount);
}
}
+UniValue getbestproofroot(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() != 1 || params[0].getKeys().size() < 2)
+ {
+ throw runtime_error(
+ "getbestproofroot '{\"proofroots\":[\"version\":n,\"type\":n,\"systemid\":\"currencyidorname\",\"height\":n,\n"
+ " \"stateroot\":\"hex\",\"blockhash\":\"hex\",\"power\":\"hex\"],\"lastconfirmed\":n}'\n"
+ "\nDetermines and returns the index of the best (most recent, valid, qualified) proof root in the list of proof roots,\n"
+ "and the most recent, valid proof root.\n"
+
+ "\nArguments\n"
+ "{\n"
+ " \"proofroots\": (array, required/may be empty) ordered array of proof roots, indexed on return\n"
+ " [\n"
+ " {\n"
+ " \"version\":n (int, required) version of this proof root data structure\n"
+ " \"type\":n (int, required) type of proof root (chain or system specific)\n"
+ " \"systemid\":\"hexstr\" (hexstr, required) system the proof root is for\n"
+ " \"height\":n (uint32_t, required) height of this proof root\n"
+ " \"stateroot\":\"hexstr\" (hexstr, required) Merkle or merkle-style tree root for the specified block/sequence\n"
+ " \"blockhash\":\"hexstr\" (hexstr, required) hash identifier for the specified block/sequence\n"
+ " \"power\":\"hexstr\" (hexstr, required) work, stake, or combination of the two for most-work/most-power rule\n"
+ " }\n"
+ " .\n"
+ " .\n"
+ " .\n"
+ " ]\n"
+ " \"currencies\":[\"id1\"] (array, optional) currencies to query for currency states\n"
+ " \"lastconfirmed\":n (int, required) index into the proof root array indicating the last confirmed root"
+ "}\n"
+
+ "\nResult:\n"
+ "\"bestproofrootindex\" (int) index of best proof root not confirmed that is provided, confirmed index, or -1"
+ "\"latestproofroot\" (object) latest valid proof root of chain"
+ "\"currencystates\" (int) currency states of target currency and published bridges"
+
+ "\nExamples:\n"
+ + HelpExampleCli("getbestproofroot", "\"{\"proofroots\":[\"version\":n,\"type\":n,\"systemid\":\"currencyidorname\",\"height\":n,\"stateroot\":\"hex\",\"blockhash\":\"hex\",\"power\":\"hex\"],\"lastconfirmed\":n}\"")
+ + HelpExampleRpc("getbestproofroot", "\"{\"proofroots\":[\"version\":n,\"type\":n,\"systemid\":\"currencyidorname\",\"height\":n,\"stateroot\":\"hex\",\"blockhash\":\"hex\",\"power\":\"hex\"],\"lastconfirmed\":n}\"")
+ );
+ }
+
+ CheckPBaaSAPIsValid();
+
+ std::vector<std::string> paramKeys = params[0].getKeys();
+ UniValue currenciesUni;
+ if (paramKeys.size() > 3 ||
+ (paramKeys.size() == 3) && !(currenciesUni = find_value(params[0], "currencies")).isArray())
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "too many members in object or invalid currencies array");
+ }
+
+ int lastConfirmed = uni_get_int(find_value(params[0], "lastconfirmed"), -1);
+ if (lastConfirmed == -1)
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid lastconfirmed");
+ }
+
+ std::vector<CProofRoot> proofRootArr;
+ UniValue uniProofRoots = find_value(params[0], "proofroots");
+ if (!uniProofRoots.isArray())
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid proof root array parameter");
+ }
+
+ LOCK(cs_main);
+
+ uint32_t nHeight = chainActive.Height();
+
+ uint32_t curHeight = 0;
+ std::map<uint32_t, int32_t> validRoots; // height, index (only return the first valid at each height)
+ for (int i = 0; i < proofRootArr.size(); i++)
+ {
+ CProofRoot checkRoot = CProofRoot(proofRootArr[i]);
+ // proof roots must be valid and in height order, though heights can overlap
+ if (!checkRoot.IsValid() ||
+ checkRoot.rootHeight < curHeight ||
+ checkRoot.systemID != ASSETCHAINS_CHAINID)
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid proof root array parameter for %s", EncodeDestination(CIdentityID(ASSETCHAINS_CHAINID))));
+ }
+ // ignore potential dups
+ if (validRoots.count(checkRoot.rootHeight))
+ {
+ continue;
+ }
+ if (checkRoot == checkRoot.GetProofRoot(checkRoot.rootHeight))
+ {
+ validRoots.insert(std::make_pair(checkRoot.rootHeight, i));
+ }
+ }
+
+ UniValue retVal(UniValue::VOBJ);
+
+ // get the latest proof root and currency states
+ retVal.pushKV("latestproofroot", CProofRoot::GetProofRoot(nHeight).ToUniValue());
+
+ std::vector<UniValue> currencyStatesUni;
+ for (int i = 0; i < currenciesUni.size(); i++)
+ {
+ CCurrencyDefinition targetCur;
+ uint160 targetCurID;
+ if ((targetCurID = ValidateCurrencyName(uni_get_str(currenciesUni[i]), true, &targetCur)).IsNull())
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid currency state request for %s", uni_get_str(currenciesUni[i])));
+ }
+ currencyStatesUni.push_back(ConnectedChains.GetCurrencyState(targetCur, nHeight).ToUniValue());
+ }
+
+ return NullUniValue;
+}
+
UniValue submitacceptednotarization(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 2)
"\nIf successful in submitting the transaction based on all rules, a transaction ID is returned, otherwise, NULL.\n"
"\nArguments\n"
- "\"earnednotarizaton\" (object, required) notarization earned on the other system, which is the basis for this"
- "\"notaryevidence\" (object, required) evidence and notary signatures validating the notarization"
+ "\"earnednotarization\" (object, required) notarization earned on the other system, which is the basis for this\n"
+ "\"notaryevidence\" (object, required) evidence and notary signatures validating the notarization\n"
"\nResult:\n"
"txid (hexstring) transaction ID of submitted transaction\n"
{ "multichain", "getcurrencyconverters", &getcurrencyconverters, true },
{ "multichain", "getcurrency", &getcurrency, true },
{ "multichain", "getnotarizationdata", &getnotarizationdata, true },
+ { "multichain", "getbestproofroot", &getbestproofroot, true },
{ "multichain", "submitacceptednotarization", &submitacceptednotarization, true },
{ "multichain", "getinitialcurrencystate", &getinitialcurrencystate, true },
{ "multichain", "getcurrencystate", &getcurrencystate, true },