1 /********************************************************************
2 * (C) 2019 Michael Toutonghi
4 * Distributed under the MIT software license, see the accompanying
5 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 * This implements the public blockchains as a service (PBaaS) notarization protocol, VerusLink.
8 * VerusLink is a new distributed consensus protocol that enables multiple public blockchains
9 * to operate as a decentralized ecosystem of chains, which can interact and easily engage in cross
17 #include "rpc/pbaasrpc.h"
18 #include "transaction_builder.h"
24 extern uint160 VERUS_CHAINID;
25 extern uint160 ASSETCHAINS_CHAINID;
26 extern string VERUS_CHAINNAME;
27 extern string PBAAS_HOST;
28 extern string PBAAS_USERPASS;
29 extern int32_t PBAAS_PORT;
31 CNotaryEvidence::CNotaryEvidence(const UniValue &uni)
33 version = uni_get_int(find_value(uni, "version"), VERSION_CURRENT);
34 type = uni_get_int(find_value(uni, "type"));
35 systemID = GetDestinationID(DecodeDestination(uni_get_str(find_value(uni, "systemid"))));
36 output = CUTXORef(find_value(uni, "output"));
37 confirmed = uni_get_bool(find_value(uni, "confirmed"));
38 UniValue sigArr = find_value(uni, "signatures");
39 UniValue evidenceArr = find_value(uni, "evidence");
40 if (sigArr.isObject())
42 auto sigKeys = sigArr.getKeys();
43 auto sigValues = sigArr.getValues();
44 for (int i = 0; i < sigKeys.size(); i++)
46 CTxDestination destKey = DecodeDestination(sigKeys[i]);
47 if (destKey.which() != COptCCParams::ADDRTYPE_ID)
49 version = VERSION_INVALID;
51 signatures.insert(std::make_pair(CIdentityID(GetDestinationID(destKey)), CIdentitySignature(sigValues[i])));
54 if (evidenceArr.isArray())
56 for (int i = 0; i < evidenceArr.size(); i++)
58 evidence.push_back(CPartialTransactionProof(evidenceArr[i]));
63 CIdentitySignature::ESignatureVerification CNotaryEvidence::SignConfirmed(const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height)
65 if (signatures.size() && !confirmed)
67 LogPrintf("%s: Attempting to change existing signature from rejected to confirmed\n", __func__);
68 return CIdentitySignature::SIGNATURE_INVALID;
71 std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
72 if (!keyStore.GetIdentity(signWithID, keyAndIdentity, height) && keyAndIdentity.first.CanSign())
74 LogPrintf("%s: Attempting to sign with notary ID that this wallet does not control\n", __func__);
75 return CIdentitySignature::SIGNATURE_INVALID;
80 if (txToConfirm.GetHash() != output.hash ||
81 txToConfirm.vout.size() <= output.n ||
82 !txToConfirm.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) ||
85 p.evalCode == EVAL_NONE)
87 LogPrintf("%s: Attempting to sign an invalid or incompatible object\n", __func__);
88 return CIdentitySignature::SIGNATURE_INVALID;
91 // write the object to the hash writer without a vector length prefix
92 auto hw = CMMRNode<>::GetHashWriter();
93 uint256 objHash = hw.write((const char *)&(p.vData[0][0]), p.vData[0].size()).GetHash();
95 CIdentitySignature idSignature;
97 /* CPBaaSNotarization debugNotarization(p.vData[0]);
98 printf("%s: notarization:\n%s\n", __func__, debugNotarization.ToUniValue().write(1,2).c_str());
99 printf("%s: Signing notarization at height %u on system: %s for identity %s\nconfirmedKey: %s\nobjHash: %s\n",
102 EncodeDestination(CIdentityID(systemID)).c_str(),
103 EncodeDestination(CIdentityID(keyAndIdentity.first.idID)).c_str(),
104 NotaryConfirmedKey().GetHex().c_str(),
105 objHash.GetHex().c_str()); */
107 CIdentitySignature::ESignatureVerification sigResult = idSignature.NewSignature(keyAndIdentity.second,
108 std::vector<uint160>({NotaryConfirmedKey()}),
109 std::vector<uint256>(),
116 if (sigResult != CIdentitySignature::SIGNATURE_INVALID)
118 signatures.insert(std::make_pair(signWithID, idSignature));
123 CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height)
125 if (signatures.size() && confirmed)
127 LogPrintf("%sAttempting to change existing signature from confirmed to rejected\n", __func__);
128 return CIdentitySignature::SIGNATURE_INVALID;
131 std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
132 if (!keyStore.GetIdentity(signWithID, keyAndIdentity, height) && keyAndIdentity.first.CanSign())
134 LogPrintf("%s: Attempting to sign with notary ID that this wallet does not control\n", __func__);
135 return CIdentitySignature::SIGNATURE_INVALID;
140 if (txToConfirm.GetHash() != output.hash ||
141 txToConfirm.vout.size() <= output.n ||
142 !txToConfirm.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) ||
144 !p.vData[0].size() ||
145 p.evalCode == EVAL_NONE)
147 LogPrintf("%s: Attempting to sign an invalid or incompatible object\n", __func__);
148 return CIdentitySignature::SIGNATURE_INVALID;
151 // write the object to the hash writer without a vector length prefix
152 auto hw = CMMRNode<>::GetHashWriter();
153 uint256 objHash = hw.write((const char *)&(p.vData[0][0]), p.vData[0].size()).GetHash();
155 CIdentitySignature idSignature;
157 CIdentitySignature::ESignatureVerification sigResult = idSignature.NewSignature(keyAndIdentity.second,
158 std::vector<uint160>({NotaryRejectedKey()}),
159 std::vector<uint256>(),
166 if (sigResult != CIdentitySignature::SIGNATURE_INVALID)
168 signatures.insert(std::make_pair(signWithID, idSignature));
173 CPBaaSNotarization::CPBaaSNotarization(const CScript &scriptPubKey) :
174 nVersion(VERSION_INVALID),
176 notarizationHeight(0),
180 if (scriptPubKey.IsPayToCryptoCondition(p) &&
182 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
185 ::FromVector(p.vData[0], *this);
189 CPBaaSNotarization::CPBaaSNotarization(const CTransaction &tx, int32_t *pOutIdx) :
190 nVersion(VERSION_INVALID),
192 notarizationHeight(0),
195 // the PBaaS notarization itself is a combination of proper inputs, one output, and
196 // a sequence of opret chain objects as proof of the output values on the chain to which the
197 // notarization refers, the opret can be reconstructed from chain data in order to validate
198 // the txid of a transaction that does not contain the opret itself
201 int32_t &outIdx = pOutIdx ? *pOutIdx : _outIdx;
203 // a notarization must have notarization output that spends to the address indicated by the
204 // ChainID, an opret, that there is only one, and that it can be properly decoded to a notarization
205 // output, whether or not validate is true
207 for (int i = 0; i < tx.vout.size(); i++)
210 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
212 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
217 nVersion = VERSION_INVALID;
225 ::FromVector(p.vData[0], *this);
231 uint160 ValidateCurrencyName(std::string currencyStr, bool ensureCurrencyValid=false, CCurrencyDefinition *pCurrencyDef=NULL);
233 CPBaaSNotarization::CPBaaSNotarization(const UniValue &obj)
235 nVersion = (uint32_t)uni_get_int64(find_value(obj, "version"));
237 SetDefinitionNotarization(uni_get_bool(find_value(obj, "isdefinition")));
238 SetBlockOneNotarization(uni_get_bool(find_value(obj, "isblockonenotarization")));
239 SetPreLaunch(uni_get_bool(find_value(obj, "prelaunch")));
240 SetLaunchCleared(uni_get_bool(find_value(obj, "launchcleared")));
241 SetRefunding(uni_get_bool(find_value(obj, "refunding")));
242 SetLaunchConfirmed(uni_get_bool(find_value(obj, "launchconfirmed")));
243 SetLaunchComplete(uni_get_bool(find_value(obj, "launchcomplete")));
244 if (uni_get_bool(find_value(obj, "ismirror")))
246 flags |= FLAG_ACCEPTED_MIRROR;
248 SetSameChain(uni_get_bool(find_value(obj, "samechain")));
250 currencyID = ValidateCurrencyName(uni_get_str(find_value(obj, "currencyid")));
251 if (currencyID.IsNull())
253 nVersion = VERSION_INVALID;
257 UniValue transferID = find_value(obj, "proposer");
258 if (transferID.isObject())
260 proposer = CTransferDestination(transferID);
263 notarizationHeight = (uint32_t)uni_get_int64(find_value(obj, "notarizationheight"));
264 currencyState = CCoinbaseCurrencyState(find_value(obj, "currencystate"));
265 prevNotarization = CUTXORef(uint256S(uni_get_str(find_value(obj, "prevnotarizationtxid"))), (uint32_t)uni_get_int(find_value(obj, "prevnotarizationout")));
266 hashPrevNotarization = uint256S(uni_get_str(find_value(obj, "hashprevnotarizationobject")));
267 prevHeight = (uint32_t)uni_get_int64(find_value(obj, "prevheight"));
269 auto curStateArr = find_value(obj, "currencystates");
270 auto proofRootArr = find_value(obj, "proofroots");
271 auto nodesUni = find_value(obj, "nodes");
273 if (curStateArr.isArray())
275 for (int i = 0; i < curStateArr.size(); i++)
277 std::vector<std::string> keys = curStateArr[i].getKeys();
278 std::vector<UniValue> values = curStateArr[i].getValues();
279 if (keys.size() != 1 or values.size() != 1)
281 nVersion = VERSION_INVALID;
284 currencyStates.insert(std::make_pair(GetDestinationID(DecodeDestination(keys[0])), CCoinbaseCurrencyState(values[0])));
288 if (proofRootArr.isArray())
290 for (int i = 0; i < proofRootArr.size(); i++)
292 CProofRoot oneRoot(proofRootArr[i]);
293 if (!oneRoot.IsValid())
295 nVersion = VERSION_INVALID;
298 proofRoots.insert(std::make_pair(oneRoot.systemID, oneRoot));
302 if (nodesUni.isArray())
304 vector<UniValue> nodeVec = nodesUni.getValues();
305 for (auto node : nodeVec)
307 nodes.push_back(CNodeData(uni_get_str(find_value(node, "networkaddress")), uni_get_str(find_value(node, "nodeidentity"))));
312 bool operator==(const CProofRoot &op1, const CProofRoot &op2)
314 return op1.version == op2.version &&
315 op1.type == op2.type &&
316 op1.rootHeight == op2.rootHeight &&
317 op1.stateRoot == op2.stateRoot &&
318 op1.systemID == op2.systemID &&
319 op1.blockHash == op2.blockHash &&
320 op1.compactPower == op2.compactPower;
323 CProofRoot CProofRoot::GetProofRoot(uint32_t blockHeight)
325 if (blockHeight > chainActive.Height())
329 auto mmv = chainActive.GetMMV();
330 mmv.resize(blockHeight + 1);
331 return CProofRoot(ASSETCHAINS_CHAINID,
334 chainActive[blockHeight]->GetBlockHash(),
335 chainActive[blockHeight]->chainPower.CompactChainPower());
338 bool CPBaaSNotarization::GetLastNotarization(const uint160 ¤cyID,
344 CPBaaSNotarization notarization;
345 std::vector<CAddressIndexDbEntry> notarizationIndex;
346 // get the last notarization in the indicated height for this currency, which is valid by definition for a token
347 if (GetAddressIndex(CCrossChainRPCData::GetConditionID(currencyID, CPBaaSNotarization::NotaryNotarizationKey()), CScript::P2IDX, notarizationIndex, startHeight, endHeight))
349 // filter out all transactions that do not spend from the notarization thread, or originate as the
351 for (auto it = notarizationIndex.rbegin(); it != notarizationIndex.rend(); it++)
353 // first unspent notarization that is valid is the one we want, skip spending
354 if (it->first.spending)
361 if (myGetTransaction(it->first.txhash, oneTx, blkHash))
363 if ((notarization = CPBaaSNotarization(oneTx.vout[it->first.index].scriptPubKey)).IsValid())
365 *this = notarization;
368 *txIDOut = it->first.txhash;
379 LogPrintf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
380 printf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
385 return notarization.IsValid();
388 bool CPBaaSNotarization::GetLastUnspentNotarization(const uint160 ¤cyID,
393 CPBaaSNotarization notarization;
394 std::vector<CAddressUnspentDbEntry> notarizationIndex;
395 // get the last notarization in the indicated height for this currency, which is valid by definition for a token
396 if (GetAddressUnspent(CCrossChainRPCData::GetConditionID(currencyID, CPBaaSNotarization::NotaryNotarizationKey()), CScript::P2IDX, notarizationIndex))
398 // first valid, unspent notarization found is the one we return
399 for (auto it = notarizationIndex.rbegin(); it != notarizationIndex.rend(); it++)
404 if (myGetTransaction(it->first.txhash, oneTx, blkHash))
406 if ((notarization = CPBaaSNotarization(oneTx.vout[it->first.index].scriptPubKey)).IsValid())
408 *this = notarization;
409 txIDOut = it->first.txhash;
410 txOutNum = it->first.index;
420 LogPrintf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
421 printf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
426 return notarization.IsValid();
429 bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceSystem,
430 const CCurrencyDefinition &destCurrency,
431 uint32_t lastExportHeight,
432 uint32_t currentHeight,
433 std::vector<CReserveTransfer> &exportTransfers, // both in and out. this may refund conversions
434 uint256 &transferHash,
435 CPBaaSNotarization &newNotarization,
436 std::vector<CTxOut> &importOutputs,
437 CCurrencyValueMap &importedCurrency,
438 CCurrencyValueMap &gatewayDepositsUsed,
439 CCurrencyValueMap &spentCurrencyOut) const
441 uint160 sourceSystemID = sourceSystem.GetID();
443 newNotarization = *this;
444 newNotarization.SetDefinitionNotarization(false);
445 newNotarization.SetBlockOneNotarization(false);
446 newNotarization.prevNotarization = CUTXORef();
447 newNotarization.prevHeight = newNotarization.notarizationHeight;
448 newNotarization.notarizationHeight = currentHeight;
450 auto hw = CMMRNode<>::GetHashWriter();
452 newNotarization.hashPrevNotarization = hw.GetHash();
454 hw = CMMRNode<>::GetHashWriter();
456 CCurrencyValueMap newPreConversionReservesIn;
458 for (int i = 0; i < exportTransfers.size(); i++)
460 CReserveTransfer &reserveTransfer = exportTransfers[i];
462 // add the pre-mutation reserve transfer to the hash
463 hw << reserveTransfer;
465 if (currencyState.IsRefunding())
467 reserveTransfer = reserveTransfer.GetRefundTransfer();
469 // ensure that any pre-conversions or conversions are all valid, based on mined height and
470 // maximum pre-conversions
471 else if (reserveTransfer.IsPreConversion())
473 if (lastExportHeight >= (destCurrency.startBlock - 1))
475 //printf("%s: Invalid pre-conversion, mined after start block\n", __func__);
476 LogPrintf("%s: Invalid pre-conversion, mined after start block\n", __func__);
477 reserveTransfer = reserveTransfer.GetRefundTransfer();
481 // check if it exceeds pre-conversion maximums, and refund if so
482 CCurrencyValueMap newReserveIn = CCurrencyValueMap(std::vector<uint160>({reserveTransfer.FirstCurrency()}),
483 std::vector<int64_t>({reserveTransfer.FirstValue() - CReserveTransactionDescriptor::CalculateConversionFee(reserveTransfer.FirstValue())}));
484 CCurrencyValueMap newTotalReserves = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserves) + newReserveIn + newPreConversionReservesIn;
485 if (destCurrency.maxPreconvert.size() && newTotalReserves > CCurrencyValueMap(destCurrency.currencies, destCurrency.maxPreconvert))
487 LogPrintf("%s: refunding pre-conversion over maximum\n", __func__);
488 reserveTransfer = reserveTransfer.GetRefundTransfer();
490 newPreConversionReservesIn += newReserveIn;
493 else if (reserveTransfer.IsConversion())
495 if (!((destCurrency.systemID != sourceSystemID && newNotarization.IsLaunchCleared()) ||
496 newNotarization.currencyState.IsLaunchCompleteMarker()))
498 //printf("%s: Invalid conversion, mined before start block\n", __func__);
499 LogPrintf("%s: Invalid conversion, mined before start block\n", __func__);
500 reserveTransfer = reserveTransfer.GetRefundTransfer();
505 if (exportTransfers.size())
507 transferHash = hw.GetHash();
510 CReserveTransactionDescriptor rtxd;
511 std::vector<CTxOut> dummyImportOutputs;
512 bool thisIsLaunchSys = destCurrency.launchSystemID == ASSETCHAINS_CHAINID;
514 // if this is the clear launch notarization after start, make the notarization and determine if we should launch or refund
515 if (destCurrency.launchSystemID == sourceSystemID &&
516 ((thisIsLaunchSys && currentHeight <= (destCurrency.startBlock - 1)) ||
518 destCurrency.systemID == ASSETCHAINS_CHAINID &&
519 currentHeight == 1)))
521 // we get one pre-launch coming through here, initial supply is set and ready for pre-convert
522 if (((thisIsLaunchSys && currentHeight == (destCurrency.startBlock - 1)) || sourceSystemID != ASSETCHAINS_CHAINID) &&
523 newNotarization.IsPreLaunch())
525 // the first block executes the second time through
526 if (newNotarization.IsLaunchCleared())
528 newNotarization.SetPreLaunch(false);
529 newNotarization.currencyState.SetLaunchClear();
530 newNotarization.currencyState.SetPrelaunch(false);
531 newNotarization.currencyState.RevertReservesAndSupply();
535 newNotarization.SetLaunchCleared();
536 newNotarization.currencyState.SetLaunchClear();
538 // first time through is export, second is import, then we finish clearing the launch
539 // check if the chain is qualified to launch or should refund
540 CCurrencyValueMap minPreMap, fees;
541 CCurrencyValueMap preConvertedMap = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserves).CanonicalMap();
543 if (destCurrency.minPreconvert.size() && destCurrency.minPreconvert.size() == destCurrency.currencies.size())
545 minPreMap = CCurrencyValueMap(destCurrency.currencies, destCurrency.minPreconvert).CanonicalMap();
548 if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
550 // we force the reserves and supply to zero
551 // in any case where there was less than minimum participation,
552 newNotarization.currencyState.supply = 0;
553 newNotarization.currencyState.reserves = std::vector<int64_t>(newNotarization.currencyState.reserves.size(), 0);
554 newNotarization.currencyState.SetRefunding(true);
555 newNotarization.SetRefunding(true);
559 newNotarization.SetLaunchConfirmed();
560 newNotarization.currencyState.SetLaunchConfirmed();
564 else if (thisIsLaunchSys && currentHeight < (destCurrency.startBlock - 1))
566 newNotarization.currencyState.SetPrelaunch();
569 CCurrencyDefinition destSystem = newNotarization.IsRefunding() ? ConnectedChains.GetCachedCurrency(destCurrency.launchSystemID) :
570 ConnectedChains.GetCachedCurrency(destCurrency.systemID);
572 CCoinbaseCurrencyState tempState = newNotarization.currencyState;
573 if (destCurrency.IsFractional() &&
574 !(newNotarization.IsLaunchCleared() &&
575 !newNotarization.currencyState.IsLaunchCompleteMarker()) &&
576 exportTransfers.size())
578 // normalize prices on the way in to prevent overflows on first pass
579 std::vector<int64_t> newReservesVector = newPreConversionReservesIn.AsCurrencyVector(tempState.currencies);
580 tempState.reserves = tempState.AddVectors(tempState.reserves, newReservesVector);
581 newNotarization.currencyState.conversionPrice = tempState.PricesInReserve();
582 tempState.reserves = tempState.AddVectors(tempState.reserves, (newPreConversionReservesIn * -1).AsCurrencyVector(tempState.currencies));
585 std::vector<CTxOut> tempOutputs;
586 bool retVal = rtxd.AddReserveTransferImportOutputs(sourceSystem,
589 newNotarization.currencyState,
599 importedCurrency.valueMap.clear();
600 gatewayDepositsUsed.valueMap.clear();
601 spentCurrencyOut.valueMap.clear();
602 newNotarization.currencyState.conversionPrice = tempState.conversionPrice;
603 newNotarization.currencyState.viaConversionPrice = tempState.viaConversionPrice;
604 rtxd = CReserveTransactionDescriptor();
605 retVal = rtxd.AddReserveTransferImportOutputs(sourceSystem,
608 newNotarization.currencyState,
621 // if we are in the pre-launch phase, all reserves in are cumulative and then calculated together at launch
622 // reserves in represent all reserves in, and fees are taken out after launch or refund as well
624 // we add up until the end, then stop adding reserves at launch clear. this gives us the ability to reverse the
625 // pre-launch state for validation. we continue adding fees up to pre-launch to easily get a total of unconverted
626 // fees, which we need when creating a PBaaS chain, as all currency, both reserves and fees exported to the new
627 // chain must be either output to specific addresses, taken as fees by miners, or stored in reserve deposits.
628 if (tempState.IsPrelaunch())
630 tempState.reserveIn = tempState.AddVectors(tempState.reserveIn, this->currencyState.reserveIn);
633 newNotarization.currencyState = tempState;
638 if (sourceSystemID != destCurrency.launchSystemID || lastExportHeight >= destCurrency.startBlock)
640 newNotarization.currencyState.SetLaunchCompleteMarker();
642 newNotarization.currencyState.SetLaunchClear(false);
644 CCurrencyDefinition destSystem;
646 if (destCurrency.systemID != ASSETCHAINS_CHAINID)
648 destSystem = ConnectedChains.GetCachedCurrency(destCurrency.systemID);
649 newNotarization.SetSameChain(false);
653 destSystem = ConnectedChains.ThisChain();
656 // calculate new state from processing all transfers
657 // we are not refunding, and it is possible that we also have
658 // normal conversions in addition to pre-conversions. add any conversions that may
659 // be present into the new currency state
660 CCoinbaseCurrencyState intermediateState = newNotarization.currencyState;
661 bool isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem,
670 &newNotarization.currencyState);
671 if (!newNotarization.currencyState.IsPrelaunch() &&
673 destCurrency.IsFractional())
675 // we want the new price and the old state as a starting point to ensure no rounding error impact
677 importedCurrency = CCurrencyValueMap();
678 gatewayDepositsUsed = CCurrencyValueMap();
679 CCoinbaseCurrencyState tempCurState = intermediateState;
680 tempCurState.conversionPrice = newNotarization.currencyState.conversionPrice;
681 tempCurState.viaConversionPrice = newNotarization.currencyState.viaConversionPrice;
682 rtxd = CReserveTransactionDescriptor();
683 isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem,
692 &newNotarization.currencyState);
695 newNotarization.currencyState.conversionPrice = tempCurState.conversionPrice;
696 newNotarization.currencyState.viaConversionPrice = tempCurState.viaConversionPrice;
699 else if (isValidExport)
701 importOutputs.insert(importOutputs.end(), dummyImportOutputs.begin(), dummyImportOutputs.end());
706 LogPrintf("%s: invalid export\n", __func__);
712 // based on the last notarization and existing
716 CObjectFinalization::CObjectFinalization(const CScript &script) : version(VERSION_INVALID)
719 if (script.IsPayToCryptoCondition(p) && p.IsValid())
721 if ((p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT) &&
724 *this = CObjectFinalization(p.vData[0]);
729 CObjectFinalization::CObjectFinalization(const CTransaction &tx, uint32_t *pEcode, int32_t *pFinalizationOutNum)
732 uint32_t &ecode = pEcode ? *pEcode : _ecode;
733 int32_t _finalizeOutNum;
734 int32_t &finalizeOutNum = pFinalizationOutNum ? *pFinalizationOutNum : _finalizeOutNum;
736 for (int i = 0; i < tx.vout.size(); i++)
739 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
741 if (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT)
743 if (finalizeOutNum != -1)
745 this->version = VERSION_INVALID;
759 CChainNotarizationData::CChainNotarizationData(UniValue &obj)
761 version = (uint32_t)uni_get_int(find_value(obj, "version"));
762 UniValue vtxUni = find_value(obj, "notarizations");
763 if (vtxUni.isArray())
765 vector<UniValue> vvtx = vtxUni.getValues();
768 vtx.push_back(make_pair(CUTXORef(uint256S(uni_get_str(find_value(o, "txid"))),
769 uni_get_int(find_value(o, "vout"))),
770 CPBaaSNotarization(find_value(o, "notarization"))));
774 lastConfirmed = (uint32_t)uni_get_int(find_value(obj, "lastconfirmed"));
775 UniValue forksUni = find_value(obj, "forks");
776 if (forksUni.isArray())
778 vector<UniValue> forksVec = forksUni.getValues();
779 for (auto fv : forksVec)
783 forks.push_back(vector<int32_t>());
784 vector<UniValue> forkVec = fv.getValues();
785 for (auto fidx : forkVec)
787 forks.back().push_back(uni_get_int(fidx));
793 bestChain = (uint32_t)uni_get_int(find_value(obj, "bestchain"));
796 UniValue CChainNotarizationData::ToUniValue() const
798 UniValue obj(UniValue::VOBJ);
799 obj.push_back(Pair("version", (int32_t)version));
800 UniValue notarizations(UniValue::VARR);
801 for (int64_t i = 0; i < vtx.size(); i++)
803 UniValue notarization(UniValue::VOBJ);
804 notarization.push_back(Pair("index", i));
805 notarization.push_back(Pair("txid", vtx[i].first.hash.GetHex()));
806 notarization.push_back(Pair("vout", (int32_t)vtx[i].first.n));
807 notarization.push_back(Pair("notarization", vtx[i].second.ToUniValue()));
808 notarizations.push_back(notarization);
810 obj.push_back(Pair("notarizations", notarizations));
811 UniValue Forks(UniValue::VARR);
812 for (int32_t i = 0; i < forks.size(); i++)
814 UniValue Fork(UniValue::VARR);
815 for (int32_t j = 0; j < forks[i].size(); j++)
817 Fork.push_back(forks[i][j]);
819 Forks.push_back(Fork);
821 obj.push_back(Pair("forks", Forks));
824 obj.push_back(Pair("lastconfirmedheight", (int32_t)vtx[lastConfirmed].second.notarizationHeight));
826 obj.push_back(Pair("lastconfirmed", lastConfirmed));
827 obj.push_back(Pair("bestchain", bestChain));
831 bool CPBaaSNotarization::CreateAcceptedNotarization(const CCurrencyDefinition &externalSystem,
832 const CPBaaSNotarization &earnedNotarization,
833 const CNotaryEvidence ¬aryEvidence,
834 CValidationState &state,
835 TransactionBuilder &txBuilder)
837 std::string errorPrefix(strprintf("%s: ", __func__));
838 std::set<CIdentityID> notaries;
840 int minimumNotariesConfirm = (externalSystem.GetID() == ConnectedChains.FirstNotaryChain().GetID()) ?
841 minimumNotariesConfirm = ConnectedChains.ThisChain().minNotariesConfirm :
842 minimumNotariesConfirm = ConnectedChains.FirstNotaryChain().chainDefinition.minNotariesConfirm;
844 // now, verify the evidence. accepted notarizations for another system must have at least one
845 // valid piece of evidence, which currently means at least one notary signature
846 if (!notaryEvidence.signatures.size())
848 return state.Error(errorPrefix + "insufficient notary evidence required to accept notarization");
850 for (auto &oneSigID : externalSystem.notaries)
852 notaries.insert(oneSigID);
857 // create an accepted notarization based on the cross-chain notarization provided
858 CPBaaSNotarization newNotarization = earnedNotarization;
860 // this should be mirrored for us to continue, if it can't be, it is invalid
861 if (earnedNotarization.IsMirror() || !newNotarization.SetMirror())
863 return state.Error(errorPrefix + "invalid earned notarization");
866 uint160 SystemID = externalSystem.GetID();
867 uint32_t height = chainActive.Height();
868 CProofRoot ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID];
870 CChainNotarizationData cnd;
871 std::vector<std::pair<CTransaction, uint256>> txes;
872 if (!GetNotarizationData(SystemID, cnd, &txes))
874 return state.Error(errorPrefix + "cannot locate notarization history");
877 // any notarization submitted must include a proof root of this chain that is later than the last confirmed
879 uint32_t lastHeight = cnd.vtx[cnd.lastConfirmed].second.IsPreLaunch() ?
880 cnd.vtx[cnd.lastConfirmed].second.notarizationHeight :
881 cnd.vtx[cnd.lastConfirmed].second.proofRoots.count(ASSETCHAINS_CHAINID) ?
882 cnd.vtx[cnd.lastConfirmed].second.proofRoots.find(ASSETCHAINS_CHAINID)->second.rootHeight :
884 if (!cnd.IsConfirmed() ||
885 ourRoot.rootHeight <= lastHeight)
887 return state.Error(errorPrefix + "earned notarization proof root cannot be verified as later than prior confirmed for this chain");
890 auto hw = CMMRNode<>::GetHashWriter();
892 std::vector<unsigned char> notarizationVec = ::AsVector(earnedNotarization);
893 uint256 objHash = hw.write((const char *)&(notarizationVec[0]), notarizationVec.size()).GetHash();
896 for (auto &oneSig : notaryEvidence.signatures)
898 if (!notaries.count(oneSig.first))
900 return state.Error(errorPrefix + "unauthorized notary signature");
902 CIdentity sigIdentity = CIdentity::LookupIdentity(oneSig.first);
903 if (!sigIdentity.IsValidUnrevoked())
905 return state.Error(errorPrefix + "invalid notary identity");
908 // we currently require accepted notarizations to be completely authorized by notaries
909 /* printf("%s: Checking signature at sign height %u on system: %s for identity %s\nconfirmedKey: %s\nobjHash %s\n",
911 oneSig.second.blockHeight,
912 EncodeDestination(CIdentityID(SystemID)).c_str(),
913 EncodeDestination(CIdentityID(oneSig.first)).c_str(),
914 notaryEvidence.NotaryConfirmedKey().GetHex().c_str(),
915 objHash.GetHex().c_str());
916 printf("%s: notarization:\n%s\n", __func__, earnedNotarization.ToUniValue().write(1,2).c_str());
917 printf("%s: hex:\n%s\n", __func__, HexBytes(&(notarizationVec[0]), notarizationVec.size()).c_str()); */
919 if (oneSig.second.CheckSignature(sigIdentity,
920 std::vector<uint160>({notaryEvidence.NotaryConfirmedKey()}),
921 std::vector<uint256>(),
924 objHash) != oneSig.second.SIGNATURE_COMPLETE)
926 return state.Error(errorPrefix + "invalid or incomplete notary signature");
931 auto mmv = chainActive.GetMMV();
932 mmv.resize(ourRoot.rootHeight + 1);
934 // we only create accepted notarizations for notarizations that are earned for this chain on another system
935 // currently, we support ethereum and PBaaS types.
936 if (!newNotarization.proofRoots.count(SystemID) ||
937 !newNotarization.proofRoots.count(ASSETCHAINS_CHAINID) ||
938 !(ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID]).IsValid() ||
939 ourRoot.rootHeight > height ||
940 ourRoot.blockHash != chainActive[ourRoot.rootHeight]->GetBlockHash() ||
941 ourRoot.stateRoot != mmv.GetRoot() ||
942 (ourRoot.type != ourRoot.TYPE_PBAAS && ourRoot.type != ourRoot.TYPE_ETHEREUM))
944 return state.Error(errorPrefix + "can only create accepted notarization from notarization with valid proof root of this chain");
947 // ensure that the data present is valid, as of the height
948 CCoinbaseCurrencyState oldCurState = ConnectedChains.GetCurrencyState(ASSETCHAINS_CHAINID, ourRoot.rootHeight);
949 if (!oldCurState.IsValid() ||
950 ::GetHash(oldCurState) != ::GetHash(earnedNotarization.currencyState))
952 return state.Error(errorPrefix + "currency state is invalid in accepted notarization. is:\n" +
953 newNotarization.currencyState.ToUniValue().write(1,2) +
955 oldCurState.ToUniValue().write(1,2) + "\n");
958 // ensure that all locally provable info is valid as of our root height
959 // and determine if the new notarization should be already finalized or not
960 for (auto &oneCur : newNotarization.currencyStates)
962 if (oneCur.first == SystemID)
964 return state.Error(errorPrefix + "cannot accept redundant currency state in notarization for " + EncodeDestination(CIdentityID(SystemID)));
966 else if (oneCur.first != ASSETCHAINS_CHAINID)
968 // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
969 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneCur.first);
970 // we must have all currencies
971 if (!curDef.IsValid())
973 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
975 // if the currency is not from this chain, we cannot validate it
976 if (curDef.systemID != ASSETCHAINS_CHAINID)
980 // ensure that the data present is valid, as of the height
981 oldCurState = ConnectedChains.GetCurrencyState(oneCur.first, ourRoot.rootHeight);
982 if (!oldCurState.IsValid() ||
983 ::GetHash(oldCurState) != ::GetHash(oneCur.second))
985 return state.Error(errorPrefix + "currecy state is invalid in accepted notarization. is:\n" +
986 oneCur.second.ToUniValue().write(1,2) +
988 oldCurState.ToUniValue().write(1,2) + "\n");
993 // already checked above
997 for (auto &oneRoot : newNotarization.proofRoots)
999 if (oneRoot.first == SystemID)
1005 // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
1006 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneRoot.first);
1007 // we must have all currencies in this notarization registered
1008 if (!curDef.IsValid())
1010 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
1012 uint160 curDefID = curDef.GetID();
1014 // only check other currencies on this chain, not the main chain itself
1015 if (curDefID != ASSETCHAINS_CHAINID && curDef.systemID == ASSETCHAINS_CHAINID)
1017 return state.Error(errorPrefix + "proof roots are not accepted for token currencies");
1022 // now create the new notarization, add the proof, finalize if appropriate, and finish
1024 // add spend of prior notarization and then outputs
1025 CPBaaSNotarization lastUnspentNotarization;
1027 int32_t lastTxOutNum;
1028 CTransaction lastTx;
1029 if (!lastUnspentNotarization.GetLastUnspentNotarization(SystemID, lastTxId, lastTxOutNum, &lastTx))
1031 return state.Error(errorPrefix + "invalid prior notarization");
1034 // add prior unspent accepted notarization as our input
1035 txBuilder.AddTransparentInput(CUTXORef(lastTxId, lastTxOutNum), lastTx.vout[lastTxOutNum].scriptPubKey, lastTx.vout[lastTxOutNum].nValue);
1038 CCcontract_info *cp;
1039 std::vector<CTxDestination> dests;
1041 // make the earned notarization output
1042 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
1044 if (externalSystem.notarizationProtocol == externalSystem.NOTARIZATION_NOTARY_CHAINID)
1046 dests = std::vector<CTxDestination>({CIdentityID(externalSystem.GetID())});
1050 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1053 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &newNotarization)), 0);
1055 // now add the notary evidence and finalization that uses it to assert validity
1056 // make the earned notarization output
1057 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1058 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1059 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, ¬aryEvidence)), 0);
1061 if (externalSystem.notarizationProtocol != externalSystem.NOTARIZATION_NOTARY_CHAINID)
1063 // make the finalization output
1064 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1065 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1067 // we need to store the input that we confirmed if we spent finalization outputs
1068 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, newNotarization.currencyID, uint256(), txBuilder.mtx.vout.size() - 2, height + 15);
1069 if (notaryEvidence.signatures.size() >= minimumNotariesConfirm)
1072 of.evidenceOutputs.push_back(txBuilder.mtx.vout.size() - 1);
1074 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of)), 0);
1079 extern void CopyNodeStats(std::vector<CNodeStats>& vstats);
1081 std::vector<CNodeData> GetGoodNodes(int maxNum)
1083 // good nodes ordered by time connected
1084 std::map<int64_t, CNode *> goodNodes;
1085 std::vector<CNodeData> retVal;
1088 for (auto oneNode : vNodes)
1090 if (!oneNode->fInbound)
1092 if (oneNode->fWhitelisted)
1094 goodNodes.insert(std::make_pair(oneNode->nTimeConnected, oneNode));
1096 else if (oneNode->GetTotalBytesRecv() > (1024 * 1024))
1098 goodNodes.insert(std::make_pair(oneNode->nTimeConnected, oneNode));
1102 if (goodNodes.size() > 1)
1104 seed_insecure_rand();
1105 for (auto &oneNode : goodNodes)
1107 if (insecure_rand() & 1)
1109 retVal.push_back(CNodeData(oneNode.second->addr.ToStringIPPort(), oneNode.second->hashPaymentAddress));
1110 if (retVal.size() >= maxNum)
1118 retVal.push_back(CNodeData(goodNodes.begin()->second->addr.ToStringIPPort(), goodNodes.begin()->second->hashPaymentAddress));
1121 else if (goodNodes.size())
1123 retVal.push_back(CNodeData(goodNodes.begin()->second->addr.ToStringIPPort(), goodNodes.begin()->second->hashPaymentAddress));
1128 // create a notarization that is validated as part of the block, generally benefiting the miner or staker if the
1129 // cross notarization is valid
1130 bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalSystem,
1131 const CTransferDestination &Proposer,
1132 CValidationState &state,
1133 std::vector<CTxOut> &txOutputs,
1134 CPBaaSNotarization ¬arization)
1136 std::string errorPrefix(strprintf("%s: ", __func__));
1140 const CCurrencyDefinition &systemDef = externalSystem.chainDefinition;
1141 SystemID = externalSystem.chainDefinition.GetID();
1143 CChainNotarizationData cnd;
1144 std::vector<std::pair<CTransaction, uint256>> txes;
1147 LOCK2(cs_main, mempool.cs);
1148 height = chainActive.Height();
1150 // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
1151 // we also need to ensure that our notarization would be the first notarization in this notary block period with which we agree.
1152 if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
1154 // technically not a real error
1155 return state.Error("no-notary");
1158 if (!GetNotarizationData(SystemID, cnd, &txes))
1160 return state.Error(errorPrefix + "no prior notarization found");
1164 // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
1165 UniValue proofRootsUni(UniValue::VARR);
1166 for (auto &oneNot : cnd.vtx)
1168 auto rootIt = oneNot.second.proofRoots.find(SystemID);
1169 if (rootIt != oneNot.second.proofRoots.end())
1171 proofRootsUni.push_back(rootIt->second.ToUniValue());
1175 if (!proofRootsUni.size())
1177 return state.Error(errorPrefix + "no valid prior state root found");
1180 // call notary to determine the prior notarization that we agree with
1181 UniValue params(UniValue::VARR);
1183 UniValue oneParam(UniValue::VOBJ);
1184 oneParam.push_back(Pair("proofroots", proofRootsUni));
1185 oneParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1186 params.push_back(oneParam);
1188 //printf("%s: about to get cross notarization with %lu notarizations found\n", __func__, cnd.vtx.size());
1193 result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1194 } catch (exception e)
1196 result = NullUniValue;
1199 int32_t notaryIdx = uni_get_int(find_value(result, "bestindex"), -1);
1201 if (result.isNull() || notaryIdx == -1)
1203 return state.Error(result.isNull() ? "no-notary" : "no-matching-proof-roots-found");
1206 // now, we have the index for the transaction and notarization we agree with, a list of those we consider invalid,
1207 // and the most recent notarization to use when creating the new one
1208 const CTransaction &priorNotarizationTx = txes[notaryIdx].first;
1209 uint256 priorBlkHash = txes[notaryIdx].second;
1210 const CUTXORef &priorUTXO = cnd.vtx[notaryIdx].first;
1211 const CPBaaSNotarization &priorNotarization = cnd.vtx[notaryIdx].second;
1213 // find out the block height holding the last notarization we agree with
1214 auto mapBlockIt = mapBlockIndex.find(priorBlkHash);
1215 if (mapBlockIt == mapBlockIndex.end() || !chainActive.Contains(mapBlockIt->second))
1217 return state.Error(errorPrefix + "prior notarization not in blockchain");
1220 // first determine if the prior notarization we agree with would make this one moot
1221 int blockPeriodNumber = (height + 1) / BLOCK_NOTARIZATION_MODULO;
1222 int priorBlockPeriod = mapBlockIt->second->GetHeight() / BLOCK_NOTARIZATION_MODULO;
1224 if (blockPeriodNumber <= priorBlockPeriod)
1226 return state.Error("ineligible");
1229 notarization = priorNotarization;
1230 notarization.SetBlockOneNotarization(false);
1231 notarization.SetDefinitionNotarization(false);
1232 notarization.proposer = Proposer;
1233 notarization.prevHeight = priorNotarization.notarizationHeight;
1235 // get the latest notarization information for the new, earned notarization
1236 // one system may provide one proof root and multiple currency states
1237 CProofRoot latestProofRoot = CProofRoot(find_value(result, "latestproofroot"));
1238 if (!latestProofRoot.IsValid() || notarization.proofRoots[latestProofRoot.systemID].rootHeight >= latestProofRoot.rootHeight)
1240 return state.Error("no-new-latest-proof-root");
1242 notarization.proofRoots[latestProofRoot.systemID] = latestProofRoot;
1243 notarization.notarizationHeight = latestProofRoot.rootHeight;
1245 UniValue currencyStatesUni = find_value(result, "currencystates");
1246 if (!(currencyStatesUni.isArray() && currencyStatesUni.size()))
1248 return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1251 // take the lock again, now that we're back from calling out
1252 LOCK2(cs_main, mempool.cs);
1254 // if height changed, we need to fail and possibly try again
1255 if (height != chainActive.Height())
1257 return state.Error("stale-block");
1260 notarization.currencyStates.clear();
1261 for (int i = 0; i < currencyStatesUni.size(); i++)
1263 CCoinbaseCurrencyState oneCurState(currencyStatesUni[i]);
1264 CCurrencyDefinition oneCurDef;
1265 if (!oneCurState.IsValid())
1267 return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1269 if (!(oneCurDef = ConnectedChains.GetCachedCurrency(oneCurState.GetID())).IsValid())
1271 // if we don't have the currency for the state specified, and it isn't critical, ignore
1272 if (oneCurDef.GetID() == SystemID)
1274 return state.Error(errorPrefix + "system currency invalid - possible corruption");
1278 if (oneCurDef.systemID == SystemID)
1280 uint160 oneCurDefID = oneCurDef.GetID();
1281 if (notarization.currencyID == oneCurDefID)
1283 notarization.currencyState = oneCurState;
1287 notarization.currencyStates[oneCurDefID] = oneCurState;
1291 notarization.currencyStates[ASSETCHAINS_CHAINID] = ConnectedChains.GetCurrencyState(height);
1292 if (!systemDef.GatewayConverterID().IsNull())
1294 notarization.currencyStates[systemDef.GatewayConverterID()] = ConnectedChains.GetCurrencyState(systemDef.GatewayConverterID(), height);
1297 // add this blockchain's info, based on the requested height
1298 CBlockIndex &curBlkIndex = *chainActive[height];
1299 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1300 notarization.proofRoots[thisChainID] = CProofRoot::GetProofRoot(height);
1302 // add currency states that we should include and then we're done
1303 // currency states to include are either a gateway currency indicated by the
1304 // gateway or our gateway converter for our PBaaS chain
1305 uint160 gatewayConverterID;
1306 if (systemDef.IsGateway() && !systemDef.gatewayConverterName.empty())
1308 gatewayConverterID = CCurrencyDefinition::GetID(systemDef.gatewayConverterName, thisChainID);
1310 else if (SystemID == ConnectedChains.FirstNotaryChain().chainDefinition.GetID() && !ConnectedChains.ThisChain().gatewayConverterName.empty())
1312 gatewayConverterID = CCurrencyDefinition::GetID(ConnectedChains.ThisChain().gatewayConverterName, thisChainID);
1314 if (!gatewayConverterID.IsNull())
1316 // get the gateway converter currency from the gateway definition
1317 CChainNotarizationData gatewayCND;
1318 if (GetNotarizationData(gatewayConverterID, gatewayCND) && gatewayCND.vtx.size())
1320 notarization.currencyStates[gatewayConverterID] = gatewayCND.vtx[gatewayCND.lastConfirmed].second.currencyState;
1324 notarization.nodes = GetGoodNodes(CPBaaSNotarization::MAX_NODES);
1326 notarization.prevNotarization = cnd.vtx[notaryIdx].first;
1327 auto hw = CMMRNode<>::GetHashWriter();
1328 hw << cnd.vtx[notaryIdx].second;
1329 notarization.hashPrevNotarization = hw.GetHash();
1330 notarization.prevHeight = cnd.vtx[notaryIdx].second.notarizationHeight;
1333 CCcontract_info *cp;
1334 std::vector<CTxDestination> dests;
1336 // make the earned notarization output
1337 cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
1339 if (systemDef.notarizationProtocol == systemDef.NOTARIZATION_NOTARY_CHAINID)
1341 dests = std::vector<CTxDestination>({CIdentityID(systemDef.GetID())});
1345 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1348 txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_EARNEDNOTARIZATION, dests, 1, ¬arization))));
1350 if (systemDef.notarizationProtocol != systemDef.NOTARIZATION_NOTARY_CHAINID)
1352 // make the finalization output
1353 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1355 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1357 // we need to store the input that we confirmed if we spent finalization outputs
1358 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, VERUS_CHAINID, uint256(), txOutputs.size() - 1, height + 15);
1359 txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of))));
1364 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentConfirmedFinalizations(const uint160 ¤cyID)
1367 std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1368 std::vector<CAddressUnspentDbEntry> indexUnspent;
1369 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1372 uint160 indexKey = CCrossChainRPCData::GetConditionID(
1373 CCrossChainRPCData::GetConditionID(currencyID, CObjectFinalization::ObjectFinalizationNotarizationKey()),
1374 ObjectFinalizationConfirmedKey());
1375 if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1376 mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1377 (indexUnspent.size() || mempoolUnspent.size()))
1379 /* printf("%s: confirmedNotarizationKey: %s / 0x%s\nconfirmed finalizations\n",
1381 EncodeDestination(CIdentityID(indexKey)).c_str(),
1382 indexKey.GetHex().c_str()); */
1384 for (auto &oneConfirmed : indexUnspent)
1386 //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1387 retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1388 CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1390 std::set<std::pair<uint256,int>> mempoolSpent;
1391 for (auto &oneConfirmed : mempoolUnspent)
1393 if (oneConfirmed.first.spending)
1395 if (retVal.size() &&
1396 retVal.back().second.txIn.prevout == COutPoint(oneConfirmed.first.txhash, oneConfirmed.first.index))
1398 // remove it if spent
1399 //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index);
1402 mempoolSpent.insert(std::make_pair(oneConfirmed.first.txhash, oneConfirmed.first.index));
1405 for (auto &oneConfirmed : mempoolUnspent)
1407 if (mempoolSpent.count(std::make_pair(oneConfirmed.first.txhash, oneConfirmed.first.index)))
1412 auto txProxy = mempool.mapTx.find(oneConfirmed.first.txhash);
1413 if (txProxy != mempool.mapTx.end())
1415 auto &mpEntry = *txProxy;
1416 auto &tx = mpEntry.GetTx();
1417 //printf("%s: txid: %s, vout: %u\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index);
1418 retVal.push_back(std::make_pair(0,
1419 CInputDescriptor(tx.vout[oneConfirmed.first.index].scriptPubKey,
1420 tx.vout[oneConfirmed.first.index].nValue,
1421 CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1428 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentPendingFinalizations(const uint160 ¤cyID)
1431 std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1432 std::vector<CAddressUnspentDbEntry> indexUnspent;
1433 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1435 uint160 indexKey = CCrossChainRPCData::GetConditionID(
1436 CCrossChainRPCData::GetConditionID(currencyID, CObjectFinalization::ObjectFinalizationNotarizationKey()),
1437 ObjectFinalizationPendingKey());
1438 if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1439 mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1440 (indexUnspent.size() || mempoolUnspent.size()))
1442 /* printf("%s: pendingNotarizationKey: %s / 0x%s\npending finalizations\n",
1444 EncodeDestination(CIdentityID(indexKey)).c_str(),
1445 indexKey.GetHex().c_str()); */
1447 for (auto &oneConfirmed : indexUnspent)
1449 //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1450 retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1451 CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1453 std::set<std::pair<uint256,int>> mempoolSpent;
1454 for (auto &oneUnconfirmed : mempoolUnspent)
1456 if (oneUnconfirmed.first.spending)
1458 if (retVal.size() &&
1459 retVal.back().second.txIn.prevout == COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))
1461 // remove it if spent
1462 //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneUnconfirmed.first.txhash.GetHex().c_str(), oneUnconfirmed.first.index);
1465 mempoolSpent.insert(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1468 for (auto &oneUnconfirmed : mempoolUnspent)
1470 if (mempoolSpent.count(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)))
1475 auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1476 if (txProxy != mempool.mapTx.end())
1478 auto &mpEntry = *txProxy;
1479 auto &tx = mpEntry.GetTx();
1480 retVal.push_back(std::make_pair(0,
1481 CInputDescriptor(tx.vout[oneUnconfirmed.first.index].scriptPubKey,
1482 tx.vout[oneUnconfirmed.first.index].nValue,
1483 CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1490 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentEvidence(const uint160 ¤cyID,
1491 const uint256 ¬arizationTxId,
1492 int32_t notarizationOutNum)
1495 std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1496 std::vector<CAddressUnspentDbEntry> indexUnspent;
1497 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1498 uint160 indexKey = CCrossChainRPCData::GetConditionID(currencyID, CNotaryEvidence::NotarySignatureKey(), notarizationTxId, notarizationOutNum);
1500 if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1501 mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1502 (indexUnspent.size() || mempoolUnspent.size()))
1504 /* printf("%s: unspentEvidenceKey: %s / 0x%s\nunspent evidence\n",
1506 EncodeDestination(CIdentityID(indexKey)).c_str(),
1507 indexKey.GetHex().c_str()); */
1509 for (auto &oneConfirmed : indexUnspent)
1511 //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1512 retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1513 CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1515 std::set<std::pair<uint256,int>> mempoolSpent;
1516 for (auto &oneUnconfirmed : mempoolUnspent)
1518 if (oneUnconfirmed.first.spending)
1520 if (retVal.size() &&
1521 retVal.back().second.txIn.prevout == COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))
1523 // remove it if spent
1524 //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneUnconfirmed.first.txhash.GetHex().c_str(), oneUnconfirmed.first.index);
1527 mempoolSpent.insert(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1530 for (auto &oneUnconfirmed : mempoolUnspent)
1532 if (mempoolSpent.count(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)))
1537 auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1538 if (txProxy != mempool.mapTx.end())
1540 auto &mpEntry = *txProxy;
1541 auto &tx = mpEntry.GetTx();
1542 retVal.push_back(std::make_pair(0,
1543 CInputDescriptor(tx.vout[oneUnconfirmed.first.index].scriptPubKey,
1544 tx.vout[oneUnconfirmed.first.index].nValue,
1545 CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1552 // this is called by notaries to locate any notarizations of a specific system that they can notarize, to determine if we
1553 // agree with the notarization in question, and to confirm or reject the notarization
1554 bool CPBaaSNotarization::ConfirmOrRejectNotarizations(const CWallet *pWallet,
1555 const CRPCChainData &externalSystem,
1556 CValidationState &state,
1557 TransactionBuilder &txBuilder,
1560 std::string errorPrefix(strprintf("%s: ", __func__));
1564 CChainNotarizationData cnd;
1565 std::vector<std::pair<CTransaction, uint256>> txes;
1568 uint160 SystemID = externalSystem.chainDefinition.GetID();
1570 std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> mine;
1572 std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> imsigner, watchonly;
1573 LOCK(pWallet->cs_wallet);
1574 // sign with all IDs under our control that are eligible for this currency
1575 pWallet->GetIdentities(ConnectedChains.ThisChain().notaries, mine, imsigner, watchonly);
1578 return state.Error("no-notary");
1583 LOCK2(cs_main, mempool.cs);
1584 height = chainActive.Height();
1586 // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
1587 // we also need to ensure that our notarization would be the first notarization in this notary block period with which we agree.
1588 if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
1590 // technically not a real error
1591 return state.Error("no-notary");
1594 if (!GetNotarizationData(SystemID, cnd, &txes))
1596 return state.Error(errorPrefix + "no prior notarization found");
1600 if (cnd.IsConfirmed() && cnd.vtx.size() == 1)
1602 return state.Error("no-unconfirmed");
1605 if (height <= (CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED + 1))
1607 return state.Error(errorPrefix + "too early");
1610 // latest height we are eligible to notarize
1611 uint32_t eligibleHeight = height - CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED;
1613 // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
1614 UniValue proofRootsUni(UniValue::VARR);
1615 for (auto &oneNot : cnd.vtx)
1617 auto rootIt = oneNot.second.proofRoots.find(SystemID);
1618 if (rootIt != oneNot.second.proofRoots.end())
1620 proofRootsUni.push_back(rootIt->second.ToUniValue());
1624 if (!proofRootsUni.size())
1626 return state.Error(errorPrefix + "no valid prior state root found");
1629 UniValue firstParam(UniValue::VOBJ);
1630 firstParam.push_back(Pair("proofroots", proofRootsUni));
1631 firstParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1633 // call notary to determine the notarization that we should notarize
1634 UniValue params(UniValue::VARR);
1635 params.push_back(firstParam);
1637 //printf("%s: about to getbestproofroot with:\n%s\n", __func__, params.write(1,2).c_str());
1642 result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1643 } catch (exception e)
1645 result = NullUniValue;
1648 int32_t notaryIdx = uni_get_int(find_value(result, "bestindex"), -1);
1650 if (result.isNull() || notaryIdx == -1)
1652 return state.Error(result.isNull() ? "no-notary" : "no-matching-notarization-found");
1655 // take the lock again, now that we're back from calling out
1656 LOCK2(cs_main, mempool.cs);
1658 // if height changed, we need to fail and possibly try again later
1659 if (height != chainActive.Height())
1661 return state.Error("stale-block");
1664 // now, get the list of unconfirmed matches, and sign the latest one that
1666 UniValue proofRootArr = find_value(result, "validindexes");
1667 if (!proofRootArr.isArray() || !proofRootArr.size())
1669 return state.Error("no-valid-unconfirmed");
1672 if (!proofRootArr.isArray() || !proofRootArr.size())
1674 return state.Error("no-valid-unconfirmed");
1677 bool retVal = false;
1679 // look from the latest notarization that may qualify
1680 for (int i = proofRootArr.size() - 1; i >= 0; i--)
1682 int idx = uni_get_int(proofRootArr[i]);
1683 auto proofIt = cnd.vtx[idx].second.proofRoots.find(ASSETCHAINS_CHAINID);
1684 if (proofIt != cnd.vtx[idx].second.proofRoots.end() &&
1685 proofIt->second.rootHeight <= eligibleHeight)
1687 // if confirmed, we have no more to check
1688 if (cnd.lastConfirmed == idx)
1693 // this is the one we will notarize
1694 std::vector<CInputDescriptor> myIDSigs;
1696 std::set<CIdentityID> myIDSet;
1697 for (auto &oneID : mine)
1699 myIDSet.insert(oneID.first.idID);
1702 // a pending finalization may be either an actual notarization finalization or an earned notarization
1703 // a confirmed finalization may be either a finalization or a notarization
1704 std::vector<std::pair<uint32_t, CInputDescriptor>> unspentConfirmed = CObjectFinalization::GetUnspentConfirmedFinalizations(SystemID);
1705 std::vector<std::pair<uint32_t, CInputDescriptor>> unspentPending = CObjectFinalization::GetUnspentPendingFinalizations(SystemID);
1707 // if we finalize and confirm a notarization, we will spend all prior confirmed and
1708 // pending outputs as part of finalization.
1710 // if we only sign and add to a confirmation but cannot meet finalization requirements, we can
1711 // spend and roll up pending outputs into ours as combined
1713 // CUTXORef = vtx UTXORef
1715 // int = index into the vtx vector
1716 // vector of pairs {uint32_t = blockHeight, CInputDescriptor for evidence}
1719 std::map<CUTXORef, std::pair<int, std::vector<std::pair<uint32_t, CInputDescriptor>>>> vtxFinalizations;
1720 for (int j = 0; j < cnd.vtx.size(); j++)
1724 // if any of the notarizations are earned notarizations, we will spend those behind us if we finalize
1725 if (txes[j].first.vout[cnd.vtx[j].first.n].scriptPubKey.IsPayToCryptoCondition(oneP) &&
1727 oneP.evalCode == EVAL_EARNEDNOTARIZATION)
1729 // printf("%s: adding %s to vtxFinalizations\n", __func__, cnd.vtx[j].first.ToUniValue().write(1,2).c_str());
1730 vtxFinalizations.insert(std::make_pair(cnd.vtx[j].first, std::make_pair(j, std::vector<std::pair<uint32_t, CInputDescriptor>>())));
1734 for (auto &oneConfirmed : unspentConfirmed)
1736 CObjectFinalization oneOf;
1738 if (oneConfirmed.second.scriptPubKey.IsPayToCryptoCondition(oneP) &&
1740 oneP.evalCode != EVAL_EARNEDNOTARIZATION &&
1741 oneP.evalCode != EVAL_ACCEPTEDNOTARIZATION)
1743 oneOf = CObjectFinalization(oneConfirmed.second.scriptPubKey);
1744 if (!oneOf.IsValid())
1746 printf("%s: invalid index entry at %s : %u\n",
1748 oneConfirmed.second.txIn.prevout.hash.GetHex().c_str(),
1749 oneConfirmed.second.txIn.prevout.n);
1758 CUTXORef oneConfirmedTarget = CUTXORef(oneOf.output.hash.IsNull() ? oneConfirmed.second.txIn.prevout.hash : oneOf.output.hash,
1761 // if the notarization we have selected to confirm is already confirmed, we are done
1762 if (vtxFinalizations[oneConfirmedTarget].first == idx)
1764 return state.Error("notarization-already-confirmed");
1766 /* printf("%s: adding %s to entry #%d in vtxFinalizations\n",
1768 CUTXORef(oneConfirmed.second.txIn.prevout).ToUniValue().write(1,2).c_str(),
1769 vtxFinalizations[oneConfirmedTarget].first); */
1770 vtxFinalizations[oneConfirmedTarget].second.push_back(oneConfirmed);
1773 // unspent evidence is specific to the target notarization
1774 std::vector<std::pair<uint32_t, CInputDescriptor>> unspentEvidence = CObjectFinalization::GetUnspentEvidence(ASSETCHAINS_CHAINID,
1775 cnd.vtx[idx].first.hash,
1776 cnd.vtx[idx].first.n);
1777 std::vector<CInputDescriptor> cleanupSpend;
1779 for (auto &onePending : unspentPending)
1781 CObjectFinalization oneOf;
1783 if (onePending.second.scriptPubKey.IsPayToCryptoCondition(oneP) &&
1786 if (oneP.evalCode == EVAL_EARNEDNOTARIZATION ||
1787 oneP.evalCode == EVAL_ACCEPTEDNOTARIZATION)
1791 if (oneP.evalCode == EVAL_FINALIZE_NOTARIZATION && oneP.vData.size())
1793 oneOf = CObjectFinalization(oneP.vData[0]);
1795 if (!oneOf.IsValid())
1797 printf("%s: invalid index entry at %s : %u\n",
1799 onePending.second.txIn.prevout.hash.GetHex().c_str(),
1800 onePending.second.txIn.prevout.n);
1805 CUTXORef onePendingTarget = CUTXORef(oneOf.output.hash.IsNull() ? onePending.second.txIn.prevout.hash : oneOf.output.hash,
1807 if (!vtxFinalizations.count(onePendingTarget))
1809 CTransaction checkTx;
1811 if (myGetTransaction(onePendingTarget.hash, checkTx, blockHash))
1813 /* UniValue jsonTx(UniValue::VOBJ);
1814 TxToUniv(checkTx, blockHash, jsonTx);
1815 printf("%s: cleanup forked tx: %s\noutput #%u\nvtxFinalizations:\n", __func__, jsonTx.write(1,2).c_str(), onePendingTarget.n); */
1816 cleanupSpend.push_back(onePending.second);
1820 printf("%s: pending notarization finalization with no notarization matching (%s) found - may be mempool only\n", __func__, onePendingTarget.ToUniValue().write(1,2).c_str());
1822 vtxFinalizations[onePendingTarget].first = -1;
1824 vtxFinalizations[onePendingTarget].second.push_back(onePending);
1827 // we need to have a pending finalization to spend to add confirmations
1828 if (!vtxFinalizations[cnd.vtx[idx].first].second.size())
1830 printf("%s: ERROR: pending notarization (%s) with no matching finalization\n", __func__, cnd.vtx[idx].first.ToUniValue().write(1,2).c_str());
1834 // before signing the one we are about to, we want to ensure that it isn't already signed sufficiently
1835 // if there are enough signatures to confirm it without signature, make our signature, then create a finalization
1836 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION,
1838 cnd.vtx[idx].first.hash,
1839 cnd.vtx[idx].first.n,
1842 std::vector<CInputDescriptor> additionalEvidence;
1844 std::set<uint160> sigSet;
1846 int minimumNotariesConfirm = (externalSystem.GetID() == ConnectedChains.FirstNotaryChain().GetID()) ?
1847 minimumNotariesConfirm = ConnectedChains.ThisChain().minNotariesConfirm :
1848 minimumNotariesConfirm = ConnectedChains.FirstNotaryChain().chainDefinition.minNotariesConfirm;
1850 // if we might have a confirmed notarization, verify, then post
1851 for (auto &oneEvidenceOut : unspentEvidence)
1854 CNotaryEvidence evidence;
1855 if (oneEvidenceOut.second.scriptPubKey.IsPayToCryptoCondition(p) &&
1857 p.evalCode == EVAL_NOTARY_EVIDENCE &&
1859 (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
1860 evidence.IsNotarySignature())
1862 if (CUTXORef(evidence.output.hash.IsNull() ? oneEvidenceOut.second.txIn.prevout.hash : evidence.output.hash, evidence.output.n) == of.output &&
1863 evidence.signatures.size())
1865 bool hasOurSig = false;
1866 for (auto &oneSig : evidence.signatures)
1868 sigSet.insert(oneSig.first);
1869 if (myIDSet.count(oneSig.first))
1872 myIDSet.erase(oneSig.first);
1877 myIDSigs.push_back(oneEvidenceOut.second);
1881 additionalEvidence.push_back(oneEvidenceOut.second);
1886 printf("%s: ERROR - unexpected code path, no assert thrown for debugging\n", __func__);
1887 cleanupSpend.push_back(oneEvidenceOut.second);
1892 CNotaryEvidence ne(ASSETCHAINS_CHAINID, cnd.vtx[idx].first);
1894 CCcontract_info *cp;
1895 std::vector<CTxDestination> dests;
1897 // if we can still sign, do so before final check of evidence
1900 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1901 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1904 LOCK(pWallet->cs_wallet);
1905 // sign with all IDs under our control that are eligible for this currency
1906 for (auto &oneID : myIDSet)
1908 auto signResult = ne.SignConfirmed(*pWallet, txes[idx].first, oneID, height);
1909 if (signResult == CIdentitySignature::SIGNATURE_PARTIAL || signResult == CIdentitySignature::SIGNATURE_COMPLETE)
1911 sigSet.insert(oneID);
1913 // if our signatures altogether have provided a complete validation, we can early out
1914 if ((ne.signatures.size() + myIDSigs.size()) >= minimumNotariesConfirm)
1921 return state.Error(errorPrefix + "invalid identity signature");
1926 if (ne.signatures.size())
1928 /* for (auto &debugOut : ne.signatures)
1930 printf("%s: onesig - ID: %s, signature: %s\n", __func__, EncodeDestination(debugOut.first).c_str(), debugOut.second.ToUniValue().write(1,2).c_str());
1934 CScript evidenceScript = MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, &ne));
1935 myIDSigs.push_back(CInputDescriptor(evidenceScript, 0, CTxIn(COutPoint(uint256(), txBuilder.mtx.vout.size()))));
1936 of.evidenceOutputs.push_back(txBuilder.mtx.vout.size());
1937 txBuilder.AddTransparentOutput(evidenceScript, CNotaryEvidence::DEFAULT_OUTPUT_VALUE);
1941 // if we have enough to finalize, do so as a combination of pre-existing evidence and this
1942 if (sigSet.size() >= minimumNotariesConfirm)
1948 // spend all priors, and if we need more signatures, add them to the finalization evidence
1949 // prioritizing our signatures
1950 bool haveNeeded = false;
1951 std::vector<CInputDescriptor> inputSigs;
1952 for (auto &oneEvidenceOut : myIDSigs)
1954 // use up evidence with our ID signatures first, and remove from the remainder
1956 CNotaryEvidence evidence;
1959 oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1960 evidence = CNotaryEvidence(p.vData[0]);
1961 for (auto &oneSig : evidence.signatures)
1963 if (sigSet.count(oneSig.first))
1966 sigSet.erase(oneSig.first);
1972 haveNeeded = sigCount >= minimumNotariesConfirm;
1974 // until we have enough signatures to confirm, continue to add evidence to the finalization
1975 if (!oneEvidenceOut.txIn.prevout.hash.IsNull())
1977 of.evidenceInputs.push_back(txBuilder.mtx.vin.size());
1978 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1983 // add additional evidence spends
1984 for (auto &oneEvidenceOut : additionalEvidence)
1986 // use up evidence with our ID signatures first, and remove from the remainder
1988 CNotaryEvidence evidence;
1990 oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1991 evidence = CNotaryEvidence(p.vData[0]);
1992 for (auto &oneSig : evidence.signatures)
1994 if (sigSet.count(oneSig.first))
1997 sigSet.erase(oneSig.first);
2000 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
2003 // until we have enough signatures to confirm, continue to add evidence to the finalization
2004 of.evidenceInputs.push_back(txBuilder.mtx.vin.size() - 1);
2005 haveNeeded = sigCount >= minimumNotariesConfirm;
2009 // remove when this vector is confirmed as unnecessary
2010 for (auto &oneEvidenceOut : cleanupSpend)
2012 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
2015 // if we have confirmed this, we will output a new, confirmed finalization for this notarization and
2016 // spend all prior finalizations, confirmed or unconfirmed, for prior notarizations. all prior notarizations
2017 // behind a confirmed notarization are considered final in their current confirmed/unconfirmed state
2018 // we include our current pending finalization as well
2019 for (int j = idx; j >= 0; j--)
2021 for (auto &oneInputDescPair : vtxFinalizations[cnd.vtx[j].first].second)
2023 txBuilder.AddTransparentInput(oneInputDescPair.second.txIn.prevout,
2024 oneInputDescPair.second.scriptPubKey,
2025 oneInputDescPair.second.nValue);
2031 // should never get here
2032 return state.Error(errorPrefix + "Internal error");
2037 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2038 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2040 CScript finalizeScript = MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of));
2041 txBuilder.AddTransparentOutput(finalizeScript, 0);
2053 bool CallNotary(const CRPCChainData ¬arySystem, std::string command, const UniValue ¶ms, UniValue &result, UniValue &error);
2055 // look for finalized notarizations either on chain or in the mempool, which are eligible for submission
2056 // and submit them to the notary chain.
2057 std::vector<uint256> CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPCChainData &externalSystem,
2058 CValidationState &state)
2060 // look for finalized notarizations for our notary chains in recent blocks
2061 // if we find any and are connected, submit them
2062 uint160 systemID = externalSystem.chainDefinition.GetID();
2063 std::vector<std::pair<CPBaaSNotarization, CNotaryEvidence>> notarizations;
2066 LOCK2(cs_main, mempool.cs);
2068 uint32_t nHeight = chainActive.Height();
2069 uint160 finalizeConfirmedKey =
2070 CCrossChainRPCData::GetConditionID(
2071 CCrossChainRPCData::GetConditionID(systemID, CObjectFinalization::ObjectFinalizationNotarizationKey()),
2072 CObjectFinalization::ObjectFinalizationConfirmedKey());
2074 std::vector<CAddressIndexDbEntry> finalizedNotarizations;
2075 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> finalizedInMempool;
2077 if ((GetAddressIndex(finalizeConfirmedKey, CScript::P2IDX, finalizedNotarizations, nHeight - 50) &&
2078 mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{finalizeConfirmedKey, CScript::P2IDX}}), finalizedInMempool)) &&
2079 (finalizedNotarizations.size() || finalizedInMempool.size()))
2081 // get the recent finalized notarizations and submit. the daemon we submit to may early out immediately if
2082 // the notarization is already posted or in mempool on the notary system
2083 std::vector<CUTXORef> finalizedNotarizationUTXOs;
2085 for (auto &oneFinalization : finalizedNotarizations)
2087 if (oneFinalization.first.spending)
2091 finalizedNotarizationUTXOs.push_back(CUTXORef(oneFinalization.first.txhash, oneFinalization.first.index));
2093 for (auto &oneFinalization : finalizedInMempool)
2095 if (oneFinalization.first.spending)
2099 finalizedNotarizationUTXOs.push_back(CUTXORef(oneFinalization.first.txhash, oneFinalization.first.index));
2101 for (auto &oneFinalization : finalizedNotarizationUTXOs)
2103 // now, we have a finalized notarization for the target network
2104 // prepare an accepted notarization and submit
2105 CTransaction finTx, nTx;
2108 CObjectFinalization of;
2109 CPBaaSNotarization pbn;
2110 if (!myGetTransaction(oneFinalization.hash, finTx, blkHash))
2112 // set error, but continue processing
2113 state.Error("inaccessible-transaction");
2115 if (!((finTx.vout.size() > oneFinalization.n &&
2116 finTx.vout[oneFinalization.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2118 ((p.evalCode == EVAL_FINALIZE_NOTARIZATION &&
2120 (of = CObjectFinalization(p.vData[0])).IsValid() &&
2121 of.GetOutputTransaction(finTx, nTx, blkHash) &&
2122 nTx.vout.size() > of.output.n &&
2123 nTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2125 p.evalCode == EVAL_EARNEDNOTARIZATION) ||
2126 (!nTx.vout.size() &&
2127 p.evalCode == EVAL_EARNEDNOTARIZATION &&
2128 (of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION,
2130 (nTx = finTx).GetHash(),
2131 oneFinalization.n)).IsValid()))) &&
2133 (pbn = CPBaaSNotarization(p.vData[0])).IsValid()))
2135 // this is almost certainly a corrupt local database, so we will start to return errors
2136 // rather than attempt to continue
2137 state.Error("invalid-or-corrupt-notarization");
2138 return std::vector<uint256>();
2141 // get all notarization evidence and submit the notarization + evidence
2142 CNotaryEvidence allEvidence(CNotaryEvidence::TYPE_NOTARY_SIGNATURE), scratchEvidence(CNotaryEvidence::TYPE_NOTARY_SIGNATURE);
2143 allEvidence.systemID = ASSETCHAINS_CHAINID;
2144 allEvidence.output = CUTXORef(nTx.GetHash(), of.output.n);
2146 // get all inputs with evidence and add it to our evidence
2147 for (auto &oneEvidenceIn : of.evidenceInputs)
2149 CTransaction evidenceTx;
2153 if (!(finTx.vin.size() > oneEvidenceIn &&
2154 myGetTransaction(finTx.vin[oneEvidenceIn].prevout.hash, evidenceTx, eBlkHash) &&
2155 evidenceTx.vout.size() > finTx.vin[oneEvidenceIn].prevout.n &&
2156 evidenceTx.vout[finTx.vin[oneEvidenceIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(eP) &&
2158 eP.evalCode == EVAL_NOTARY_EVIDENCE &&
2160 (scratchEvidence = CNotaryEvidence(eP.vData[0])).IsValid()))
2162 state.Error("innaccessible-evidence");
2163 return std::vector<uint256>();
2165 allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
2166 for (auto &oneSig : scratchEvidence.signatures)
2168 if (oneSig.second.signatures.size())
2170 allEvidence.signatures[oneSig.first].version = oneSig.second.version;
2171 allEvidence.signatures[oneSig.first].blockHeight = oneSig.second.blockHeight;
2172 for (auto &oneSigEntry : oneSig.second.signatures)
2174 allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
2180 // get all outputs with evidence and add
2181 for (auto oneEvidenceOut : of.evidenceOutputs)
2183 if (!(finTx.vout.size() > oneEvidenceOut &&
2184 finTx.vout[oneEvidenceOut].scriptPubKey.IsPayToCryptoCondition(p) &&
2186 (p.evalCode == EVAL_NOTARY_EVIDENCE) &&
2188 (scratchEvidence = CNotaryEvidence(p.vData[0])).IsValid()))
2190 state.Error("invalid-evidence");
2191 return std::vector<uint256>();
2193 allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
2194 for (auto &oneSig : scratchEvidence.signatures)
2196 if (oneSig.second.signatures.size())
2198 allEvidence.signatures[oneSig.first].version = oneSig.second.version;
2199 allEvidence.signatures[oneSig.first].blockHeight = oneSig.second.blockHeight;
2200 for (auto &oneSigEntry : oneSig.second.signatures)
2202 allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
2207 // The first, block one notarization requires no evidence or signatures to be valid on this hain, so it will be skipped
2209 if (!allEvidence.evidence.size() && !allEvidence.signatures.size())
2213 notarizations.push_back(std::make_pair(pbn, allEvidence));
2218 std::vector<uint256> retVal;
2219 // collect them holding the lock, release it, then call out
2220 for (auto &oneNotarization : notarizations)
2222 // now, we should have enough evidence to prove
2223 // the notarization. the API call will ensure that we do
2224 UniValue params(UniValue::VARR);
2225 UniValue result, error;
2226 std::string strTxId;
2227 params.push_back(oneNotarization.first.ToUniValue());
2228 params.push_back(oneNotarization.second.ToUniValue());
2230 /* for (auto &debugOut : oneNotarization.second.signatures)
2232 printf("%s: onesig - ID: %s, signature: %s\n", __func__, EncodeDestination(debugOut.first).c_str(), debugOut.second.ToUniValue().write(1,2).c_str());
2234 printf("%s: submitting notarization with parameters:\n%s\n%s\n", __func__, params[0].write(1,2).c_str(), params[1].write(1,2).c_str());
2235 printf("%s: initial notarization:\n%s\n", __func__, oneNotarization.first.ToUniValue().write(1,2).c_str());
2236 std::vector<unsigned char> notVec1 = ::AsVector(oneNotarization.first);
2237 CPBaaSNotarization checkNotarization(oneNotarization.first.ToUniValue());
2238 std::vector<unsigned char> notVec2 = ::AsVector(checkNotarization);
2239 printf("%s: processed notarization:\n%s\n", __func__, checkNotarization.ToUniValue().write(1,2).c_str());
2240 std::vector<unsigned char> notVec3 = ::AsVector(CPBaaSNotarization(notVec1));
2241 printf("%s: hex before univalue:\n%s\n", __func__, HexBytes(&(notVec1[0]), notVec1.size()).c_str());
2242 printf("%s: hex after univalue:\n%s\n", __func__, HexBytes(&(notVec2[0]), notVec2.size()).c_str());
2243 printf("%s: hex after reserialization:\n%s\n", __func__, HexBytes(&(notVec3[0]), notVec3.size()).c_str()); */
2245 if (!CallNotary(externalSystem, "submitacceptednotarization", params, result, error) ||
2247 (strTxId = uni_get_str(result)).empty() ||
2252 // store the transaction ID and accepted notariation to prevent redundant submits
2253 retVal.push_back(uint256S(strTxId));
2259 * Validates a notarization output spend by ensuring that the spending transaction fulfills all requirements.
2260 * to accept an earned notarization as valid on the Verus blockchain, it must prove a transaction on the alternate chain, which is
2261 * either the original chain definition transaction, which CAN and MUST be proven ONLY in block 1, or the latest notarization transaction
2262 * on the alternate chain that represents an accurate MMR for this chain.
2263 * In addition, any accepted notarization must fullfill the following requirements:
2264 * 1) Must prove either a PoS block from the alternate chain or a merge mined block that is owned by the submitter and in either case,
2265 * the block must be exactly 8 blocks behind the submitted MMR used for proof.
2266 * 2) Must prove a chain definition tx and be block 1 or asserts a previous, valid MMR for the notarizing
2267 * chain and properly prove objects using that MMR.
2268 * 3) Must spend the main notarization thread as well as any finalization outputs of either valid or invalid prior
2269 * notarizations, and any unspent notarization contributions for this era. May also spend other inputs.
2271 * a) finalization output of the expected reward amount, which will be sent when finalized
2272 * b) normal output of reward from validated/finalized input if present, 50% to recipient / 50% to block miner less miner fee this tx
2273 * c) main notarization thread output with remaining funds, no other output or fee deduction
2276 bool ValidateAcceptedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2278 // TODO: this validates the spending transaction
2279 // check the following things:
2280 // 1. It represents a valid PoS or merge mined block on the other chain, and contains the header in the opret
2281 // 2. The MMR and proof provided for the currently asserted block can prove the provided header. The provided
2282 // header can prove the last block referenced.
2283 // 3. This notarization is not a superset of an earlier notarization posted before it that it does not
2284 // reference. If that is the case, it is rejected.
2285 // 4. Has all relevant inputs, including finalizes all necessary transactions, both confirmed and orphaned
2286 //printf("ValidateAcceptedNotarization\n");
2290 bool IsAcceptedNotarizationInput(const CScript &scriptSig)
2293 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_ACCEPTEDNOTARIZATION;
2298 * Ensures that a spend in an earned notarization of either an OpRet support transaction or summary notarization
2299 * are valid with respect to this chain. Any transaction that spends from an opret trasaction is either disconnected,
2300 * or contains the correct hashes of each object and transaction data except for the opret, which can be validated by
2301 * reconstructing the opret from the hashes on the other chain and verifying that it hashes to the same input value. This
2302 * enables full validation without copying redundant data back to its original chain.
2304 * In addition, each earned notarization must reference the last earned notarization with which it agrees and prove the last
2305 * accepted notarization on the alternate chain with the latest MMR. The earned notarization will not be accepted if there is
2306 * a later notarization that agrees with it already present in the alternate chain when it is submitted.
2309 bool ValidateEarnedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2311 // this needs to validate that the block is mined or staked, that the notarization is properly formed,
2312 // cryptographically correct, and that it spends the proper finalization outputs
2313 // if the notarization causes a fork, it must include additional proof of blocks and their
2314 // power based on random block hash bits
2315 //printf("ValidateEarnedNotarization\n");
2318 bool IsEarnedNotarizationInput(const CScript &scriptSig)
2320 // this is an output check, and is incorrect. need to change to input
2322 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_EARNEDNOTARIZATION;
2325 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx=nullptr, uint32_t *pHeight=nullptr);
2326 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx, uint32_t *pHeight)
2328 CTransaction _sourceTx;
2329 CTransaction &sourceTx(pSourceTx ? *pSourceTx : _sourceTx);
2331 CObjectFinalization oldFinalization;
2333 if (myGetTransaction(spendingTx.vin[nIn].prevout.hash, sourceTx, blkHash))
2337 auto bIt = mapBlockIndex.find(blkHash);
2338 if (bIt == mapBlockIndex.end() || !bIt->second)
2340 *pHeight = chainActive.Height();
2344 *pHeight = bIt->second->GetHeight();
2348 if (sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2350 (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT) &&
2351 p.version >= COptCCParams::VERSION_V3 &&
2354 oldFinalization = CObjectFinalization(p.vData[0]);
2357 return oldFinalization;
2362 * Ensures that the finalization, either as validated or orphaned, is determined by
2363 * 10 confirmations, either of this transaction, or of an alternate transaction on the chain that we do not derive
2364 * from. If the former, then this should be asserted to be validated, otherwise, it should be asserted to be invalidated.
2367 bool ValidateFinalizeNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2369 // to validate a finalization spend, we need to validate the spender's assertion of confirmation or rejection as proven
2371 // first, determine our notarization finalization protocol
2372 CTransaction sourceTx;
2374 CObjectFinalization oldFinalization = GetOldFinalization(tx, nIn, &sourceTx, &oldHeight);
2375 if (!oldFinalization.IsValid())
2377 return eval->Error("Invalid finalization output");
2380 // get currency to determine system and notarization method
2381 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oldFinalization.currencyID);
2382 if (!curDef.IsValid())
2384 return eval->Error("Invalid currency ID in finalization output");
2386 uint160 SystemID = curDef.GetID();
2388 // for now, auto notarization uses defined notaries. it can be updated later, while leaving those that
2389 // select notary confirm explicitly to remain on that protocol
2390 if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CONFIRM || curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2392 // get the notarization this finalizes and its index output
2393 int32_t notaryOutNum;
2394 CTransaction notarizationTx;
2396 if (oldFinalization.output.IsOnSameTransaction())
2398 notarizationTx = sourceTx;
2399 // output needs non-null hash below
2400 oldFinalization.output.hash = notarizationTx.GetHash();
2405 if (!oldFinalization.GetOutputTransaction(sourceTx, notarizationTx, blkHash))
2407 return eval->Error("notarization-transaction-not-found");
2410 if (notarizationTx.vout.size() <= oldFinalization.output.n)
2412 return eval->Error("invalid-finalization");
2415 CPBaaSNotarization pbn(notarizationTx.vout[oldFinalization.output.n].scriptPubKey);
2418 return eval->Error("invalid-notarization");
2421 // now, we have an unconfirmed, non-rejected finalization being spent by a transaction
2422 // confirm that the spender contains one finalization output either confirming or rejecting
2423 // the finalization. rejection may be implicit by confirming another, later notarization.
2425 // First. make sure the oldFinalization is not referring to an earlier notarization than the
2426 // one most recently confirmed. If so. then it can be spent by anyone.
2427 CChainNotarizationData cnd;
2428 if (!GetNotarizationData(SystemID, cnd) || !cnd.IsConfirmed())
2430 return eval->Error("invalid-notarization");
2433 // TODO: now, validate both rejection and confirmation
2435 CObjectFinalization newFinalization;
2436 int finalizationOutNum = -1;
2437 bool foundFinalization = false;
2438 for (int i = 0; i < tx.vout.size(); i++)
2440 auto &oneOut = tx.vout[i];
2442 // we can accept only one finalization of this notarization as an output, find it and reject more than one
2444 // TODO: HARDENING - ensure that the output of the finalization we are spending is either a confirmed, earlier
2445 // output or invalidated alternate to the one we are finalizing
2446 if (oneOut.scriptPubKey.IsPayToCryptoCondition(p) &&
2448 p.evalCode == EVAL_FINALIZE_NOTARIZATION &&
2450 (newFinalization = CObjectFinalization(p.vData[0])).IsValid())
2452 if (foundFinalization)
2454 return eval->Error("duplicate-finalization");
2456 foundFinalization = true;
2457 finalizationOutNum = i;
2461 if (!foundFinalization)
2463 return eval->Error("invalid-finalization-spend");
2469 bool IsFinalizeNotarizationInput(const CScript &scriptSig)
2471 // this is an output check, and is incorrect. need to change to input
2473 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_FINALIZE_NOTARIZATION;
2476 bool CObjectFinalization::GetOutputTransaction(const CTransaction &initialTx, CTransaction &tx, uint256 &blockHash) const
2478 if (output.hash.IsNull())
2483 else if (myGetTransaction(output.hash, tx, blockHash) && tx.vout.size() > output.n)
2490 // Sign the output object with an ID or signing authority of the ID from the wallet.
2491 CNotaryEvidence CObjectFinalization::SignConfirmed(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
2493 CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
2495 AssertLockHeld(cs_main);
2496 uint32_t nHeight = chainActive.Height();
2500 if (GetOutputTransaction(initialTx, tx, blockHash))
2502 retVal.SignConfirmed(*pWallet, tx, signatureID, nHeight);
2507 CNotaryEvidence CObjectFinalization::SignRejected(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
2509 CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
2511 AssertLockHeld(cs_main);
2512 uint32_t nHeight = chainActive.Height();
2516 if (GetOutputTransaction(initialTx, tx, blockHash))
2518 retVal.SignRejected(*pWallet, tx, signatureID, nHeight);
2523 // Verify that the output object of "p" is signed appropriately with the indicated signature
2524 // and that the signature is fully authorized to sign
2525 // TODO: THIS SHOULD BE UPDATED TO REFLECT USAGE, WHICH IT DOESN'T YET HAVE
2526 // SPECIFICALLY, THE currencyID, WHICH IS USED, SHOULD BE SEPARATED FROM minimum signatures required
2527 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, const COptCCParams &p, uint32_t height) const
2529 std::set<uint160> completedSignatures;
2530 std::set<uint160> partialSignatures;
2532 CCurrencyDefinition curDef;
2536 p.version >= p.VERSION_V3 &&
2538 GetCurrencyDefinition(currencyID, curDef, &defHeight) &&
2541 uint256 txId = output.hash.IsNull() ? initialTx.GetHash() : output.hash;
2542 std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(currencyID, CNotaryEvidence::NotarySignatureKey(), txId, output.n)};
2543 std::vector<uint256> statements;
2545 // check that signature is of the hashed vData[0] data
2546 auto hw = CMMRNode<>::GetHashWriter();
2547 hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
2548 uint256 msgHash = hw.GetHash();
2550 for (auto &authorizedNotary : curDef.notaries)
2552 if (signature.signatures.count(authorizedNotary))
2554 // we might have a partial or complete signature by one notary here
2555 const CIdentitySignature &oneIDSig = signature.signatures.find(authorizedNotary)->second;
2557 uint256 sigHash = oneIDSig.IdentitySignatureHash(vdxfCodes, statements, currencyID, height, authorizedNotary, "", msgHash);
2559 // get identity used to sign
2560 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
2561 if (signer.IsValid())
2563 std::set<uint160> idAddresses;
2564 std::set<uint160> verifiedSignatures;
2566 for (const CTxDestination &oneAddress : signer.primaryAddresses)
2568 if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
2570 // currently, can only check secp256k1 signatures
2571 //return state.Error("Unsupported signature type");
2572 return CIdentitySignature::SIGNATURE_INVALID;
2574 idAddresses.insert(GetDestinationID(oneAddress));
2577 for (auto &oneSig : signature.signatures.find(authorizedNotary)->second.signatures)
2580 pubKey.RecoverCompact(sigHash, oneSig);
2581 if (!idAddresses.count(pubKey.GetID()))
2583 // invalid signature or ID
2584 return CIdentitySignature::SIGNATURE_INVALID;
2586 verifiedSignatures.insert(pubKey.GetID());
2588 if (verifiedSignatures.size() >= signer.minSigs)
2590 completedSignatures.insert(authorizedNotary);
2594 partialSignatures.insert(authorizedNotary);
2599 // invalid signing identity in signature
2600 return CIdentitySignature::SIGNATURE_INVALID;
2604 // all IDs in the signature must have been found and either partial or complete signatures
2605 if (partialSignatures.size() + completedSignatures.size() < signature.signatures.size())
2607 return CIdentitySignature::SIGNATURE_INVALID;
2610 if (completedSignatures.size() >= curDef.minNotariesConfirm)
2612 return CIdentitySignature::SIGNATURE_COMPLETE;
2614 else if (completedSignatures.size() || partialSignatures.size())
2616 return CIdentitySignature::SIGNATURE_PARTIAL;
2619 // missing or invalid
2620 return CIdentitySignature::SIGNATURE_INVALID;
2623 // Verify that the output object is signed with an authorized signing authority
2624 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, uint32_t height) const
2626 // now, get the output to check and check to ensure the signature is good
2630 if (GetOutputTransaction(initialTx, tx, blkHash) &&
2631 tx.vout.size() > output.n &&
2632 tx.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2636 return VerifyOutputSignature(initialTx, signature, p, height);
2640 return CIdentitySignature::SIGNATURE_INVALID;
2644 // this ensures that the signature is, in fact, both authorized to sign, and also a
2645 // valid signature of the specified output object. if so, this is accepted and
2646 // results in a valid index entry as a confirmation of the notary signature
2647 // all signatures must be from a valid notary, or this returns false and should be
2648 // considered invalid.
2649 // returns the number of valid, unique notary signatures, enabling a single output
2650 // to be sufficient to authorize.
2651 bool ValidateNotarizationEvidence(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height, int &confirmedCount, bool &provenFalse)
2653 // we MUST know that the cs_main lock is held. since it can be held on the validation thread while smart transactions
2654 // execute, we cannot take it or assert here
2656 CNotaryEvidence notarySig;
2658 CCurrencyDefinition curDef;
2660 confirmedCount = 0; // if a unit of evidence, whether signature or otherwise, is validated as confirming
2661 provenFalse = false; // if the notarization is proven false
2663 if (tx.vout[outNum].scriptPubKey.IsPayToCryptoCondition(p) &&
2665 p.version >= p.VERSION_V3 &&
2667 (notarySig = CNotaryEvidence(p.vData[0])).IsValid() &&
2668 (curDef = ConnectedChains.GetCachedCurrency(notarySig.systemID)).IsValid())
2670 // now, get the output to check and ensure the signature is good
2671 CObjectFinalization of;
2672 CPBaaSNotarization notarization;
2673 uint256 notarizationTxId;
2676 if (notarySig.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(notarySig.output.hash, nTx, blkHash) &&
2677 nTx.vout.size() > notarySig.output.n &&
2678 nTx.vout[notarySig.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2680 (p.evalCode == EVAL_FINALIZE_NOTARIZATION) &&
2682 (of = CObjectFinalization(p.vData[0])).IsValid() &&
2683 of.IsNotarizationFinalization() &&
2684 of.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(of.output.hash, nTx, blkHash) &&
2685 !(notarizationTxId = nTx.GetHash()).IsNull() &&
2686 nTx.vout.size() > of.output.n &&
2687 nTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2689 (p.evalCode == EVAL_EARNEDNOTARIZATION || p.evalCode == EVAL_ACCEPTEDNOTARIZATION) &&
2691 (notarization = CPBaaSNotarization(p.vData[0])).IsValid() &&
2692 notarization.proofRoots.count(notarySig.systemID))
2694 // signature is relative only to the notarization, not the finalization
2695 // that way, the information we put into the vdxfCodes have some meaning beyond
2696 // the blockchain on which it was signed, and we do not have to carry the
2697 // finalizatoin mechanism cross-chain.
2698 std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(notarySig.systemID,
2699 CNotaryEvidence::NotarySignatureKey(),
2702 std::vector<uint256> statements;
2704 // check that signature is of the hashed vData[0] data
2705 auto hw = CMMRNode<>::GetHashWriter();
2706 hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
2707 uint256 msgHash = hw.GetHash();
2709 for (auto &authorizedNotary : curDef.notaries)
2711 std::map<CIdentityID, CIdentitySignature>::iterator sigIt = notarySig.signatures.find(authorizedNotary);
2712 if (sigIt != notarySig.signatures.end())
2714 // get identity used to sign
2715 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
2716 uint256 sigHash = sigIt->second.IdentitySignatureHash(vdxfCodes, statements, of.currencyID, height, authorizedNotary, "", msgHash);
2718 if (signer.IsValid())
2720 std::set<uint160> idAddresses;
2721 std::set<uint160> verifiedSignatures;
2723 for (const CTxDestination &oneAddress : signer.primaryAddresses)
2725 if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
2727 // currently, can only check secp256k1 signatures
2728 return state.Error("Unsupported signature type");
2730 idAddresses.insert(GetDestinationID(oneAddress));
2733 for (auto &oneSig : notarySig.signatures[authorizedNotary].signatures)
2736 pubKey.RecoverCompact(sigHash, oneSig);
2737 uint160 pkID = pubKey.GetID();
2738 if (!idAddresses.count(pkID))
2740 return state.Error("Mismatched pubkey and ID in signature");
2742 if (verifiedSignatures.count(pkID))
2744 return state.Error("Duplicate key use in ID signature");
2746 verifiedSignatures.insert(pkID);
2748 if (verifiedSignatures.size() >= signer.minSigs)
2754 return state.Error("Insufficient signatures on behalf of ID: " + signer.name);
2759 return state.Error("Invalid notary identity or corrupt local state");
2764 return state.Error("Unauthorized notary");
2770 return state.Error("Invalid notarization reference");
2775 return state.Error("Invalid or non-evidence output");
2778 if (!confirmedCount)
2780 return state.Error("No evidence present");