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() && !newNotarization.currencyState.IsLaunchCompleteMarker()) &&
575 exportTransfers.size())
577 // normalize prices on the way in to prevent overflows on first pass
578 std::vector<int64_t> newReservesVector = newPreConversionReservesIn.AsCurrencyVector(tempState.currencies);
579 tempState.reserves = tempState.AddVectors(tempState.reserves, newReservesVector);
580 newNotarization.currencyState.conversionPrice = tempState.PricesInReserve();
584 std::vector<CTxOut> tempOutputs;
585 bool retVal = rtxd.AddReserveTransferImportOutputs(sourceSystem,
588 newNotarization.currencyState,
598 importedCurrency.valueMap.clear();
599 gatewayDepositsUsed.valueMap.clear();
600 spentCurrencyOut.valueMap.clear();
601 newNotarization.currencyState.conversionPrice = tempState.conversionPrice;
602 newNotarization.currencyState.viaConversionPrice = tempState.viaConversionPrice;
603 rtxd = CReserveTransactionDescriptor();
604 retVal = rtxd.AddReserveTransferImportOutputs(sourceSystem,
607 newNotarization.currencyState,
620 // if we are in the pre-launch phase, all reserves in are cumulative and then calculated together at launch
621 // reserves in represent all reserves in, and fees are taken out after launch or refund as well
623 // we add up until the end, then stop adding reserves at launch clear. this gives us the ability to reverse the
624 // pre-launch state for validation. we continue adding fees up to pre-launch to easily get a total of unconverted
625 // fees, which we need when creating a PBaaS chain, as all currency, both reserves and fees exported to the new
626 // chain must be either output to specific addresses, taken as fees by miners, or stored in reserve deposits.
627 if (tempState.IsPrelaunch())
629 tempState.reserveIn = tempState.AddVectors(tempState.reserveIn, this->currencyState.reserveIn);
632 newNotarization.currencyState = tempState;
637 if (sourceSystemID != destCurrency.launchSystemID || lastExportHeight >= destCurrency.startBlock)
639 newNotarization.currencyState.SetLaunchCompleteMarker();
641 newNotarization.currencyState.SetLaunchClear(false);
643 CCurrencyDefinition destSystem;
645 if (destCurrency.systemID != ASSETCHAINS_CHAINID)
647 destSystem = ConnectedChains.GetCachedCurrency(destCurrency.systemID);
648 newNotarization.SetSameChain(false);
652 destSystem = ConnectedChains.ThisChain();
655 // calculate new state from processing all transfers
656 // we are not refunding, and it is possible that we also have
657 // normal conversions in addition to pre-conversions. add any conversions that may
658 // be present into the new currency state
659 CCoinbaseCurrencyState intermediateState = newNotarization.currencyState;
660 bool isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem,
669 &newNotarization.currencyState);
670 if (!newNotarization.currencyState.IsPrelaunch() &&
672 destCurrency.IsFractional())
674 // we want the new price and the old state as a starting point to ensure no rounding error impact
676 importedCurrency = CCurrencyValueMap();
677 gatewayDepositsUsed = CCurrencyValueMap();
678 CCoinbaseCurrencyState tempCurState = intermediateState;
679 tempCurState.conversionPrice = newNotarization.currencyState.conversionPrice;
680 tempCurState.viaConversionPrice = newNotarization.currencyState.viaConversionPrice;
681 rtxd = CReserveTransactionDescriptor();
682 isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem,
691 &newNotarization.currencyState);
694 newNotarization.currencyState.conversionPrice = tempCurState.conversionPrice;
695 newNotarization.currencyState.viaConversionPrice = tempCurState.viaConversionPrice;
698 else if (isValidExport)
700 importOutputs.insert(importOutputs.end(), dummyImportOutputs.begin(), dummyImportOutputs.end());
705 LogPrintf("%s: invalid export\n", __func__);
711 // based on the last notarization and existing
715 CObjectFinalization::CObjectFinalization(const CScript &script) : version(VERSION_INVALID)
718 if (script.IsPayToCryptoCondition(p) && p.IsValid())
720 if ((p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT) &&
723 *this = CObjectFinalization(p.vData[0]);
728 CObjectFinalization::CObjectFinalization(const CTransaction &tx, uint32_t *pEcode, int32_t *pFinalizationOutNum)
731 uint32_t &ecode = pEcode ? *pEcode : _ecode;
732 int32_t _finalizeOutNum;
733 int32_t &finalizeOutNum = pFinalizationOutNum ? *pFinalizationOutNum : _finalizeOutNum;
735 for (int i = 0; i < tx.vout.size(); i++)
738 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
740 if (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT)
742 if (finalizeOutNum != -1)
744 this->version = VERSION_INVALID;
758 CChainNotarizationData::CChainNotarizationData(UniValue &obj)
760 version = (uint32_t)uni_get_int(find_value(obj, "version"));
761 UniValue vtxUni = find_value(obj, "notarizations");
762 if (vtxUni.isArray())
764 vector<UniValue> vvtx = vtxUni.getValues();
767 vtx.push_back(make_pair(CUTXORef(uint256S(uni_get_str(find_value(o, "txid"))),
768 uni_get_int(find_value(o, "vout"))),
769 CPBaaSNotarization(find_value(o, "notarization"))));
773 lastConfirmed = (uint32_t)uni_get_int(find_value(obj, "lastconfirmed"));
774 UniValue forksUni = find_value(obj, "forks");
775 if (forksUni.isArray())
777 vector<UniValue> forksVec = forksUni.getValues();
778 for (auto fv : forksVec)
782 forks.push_back(vector<int32_t>());
783 vector<UniValue> forkVec = fv.getValues();
784 for (auto fidx : forkVec)
786 forks.back().push_back(uni_get_int(fidx));
792 bestChain = (uint32_t)uni_get_int(find_value(obj, "bestchain"));
795 UniValue CChainNotarizationData::ToUniValue() const
797 UniValue obj(UniValue::VOBJ);
798 obj.push_back(Pair("version", (int32_t)version));
799 UniValue notarizations(UniValue::VARR);
800 for (int64_t i = 0; i < vtx.size(); i++)
802 UniValue notarization(UniValue::VOBJ);
803 notarization.push_back(Pair("index", i));
804 notarization.push_back(Pair("txid", vtx[i].first.hash.GetHex()));
805 notarization.push_back(Pair("vout", (int32_t)vtx[i].first.n));
806 notarization.push_back(Pair("notarization", vtx[i].second.ToUniValue()));
807 notarizations.push_back(notarization);
809 obj.push_back(Pair("notarizations", notarizations));
810 UniValue Forks(UniValue::VARR);
811 for (int32_t i = 0; i < forks.size(); i++)
813 UniValue Fork(UniValue::VARR);
814 for (int32_t j = 0; j < forks[i].size(); j++)
816 Fork.push_back(forks[i][j]);
818 Forks.push_back(Fork);
820 obj.push_back(Pair("forks", Forks));
823 obj.push_back(Pair("lastconfirmedheight", (int32_t)vtx[lastConfirmed].second.notarizationHeight));
825 obj.push_back(Pair("lastconfirmed", lastConfirmed));
826 obj.push_back(Pair("bestchain", bestChain));
830 bool CPBaaSNotarization::CreateAcceptedNotarization(const CCurrencyDefinition &externalSystem,
831 const CPBaaSNotarization &earnedNotarization,
832 const CNotaryEvidence ¬aryEvidence,
833 CValidationState &state,
834 TransactionBuilder &txBuilder)
836 std::string errorPrefix(strprintf("%s: ", __func__));
837 std::set<CIdentityID> notaries;
839 int minimumNotariesConfirm = (externalSystem.GetID() == ConnectedChains.FirstNotaryChain().GetID()) ?
840 minimumNotariesConfirm = ConnectedChains.ThisChain().minNotariesConfirm :
841 minimumNotariesConfirm = ConnectedChains.FirstNotaryChain().chainDefinition.minNotariesConfirm;
843 // now, verify the evidence. accepted notarizations for another system must have at least one
844 // valid piece of evidence, which currently means at least one notary signature
845 if (!notaryEvidence.signatures.size())
847 return state.Error(errorPrefix + "insufficient notary evidence required to accept notarization");
849 for (auto &oneSigID : externalSystem.notaries)
851 notaries.insert(oneSigID);
856 // create an accepted notarization based on the cross-chain notarization provided
857 CPBaaSNotarization newNotarization = earnedNotarization;
859 // this should be mirrored for us to continue, if it can't be, it is invalid
860 if (earnedNotarization.IsMirror() || !newNotarization.SetMirror())
862 return state.Error(errorPrefix + "invalid earned notarization");
865 uint160 SystemID = externalSystem.GetID();
866 uint32_t height = chainActive.Height();
867 CProofRoot ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID];
869 CChainNotarizationData cnd;
870 std::vector<std::pair<CTransaction, uint256>> txes;
871 if (!GetNotarizationData(SystemID, cnd, &txes))
873 return state.Error(errorPrefix + "cannot locate notarization history");
876 // any notarization submitted must include a proof root of this chain that is later than the last confirmed
878 uint32_t lastHeight = cnd.vtx[cnd.lastConfirmed].second.IsPreLaunch() ?
879 cnd.vtx[cnd.lastConfirmed].second.notarizationHeight :
880 cnd.vtx[cnd.lastConfirmed].second.proofRoots.count(ASSETCHAINS_CHAINID) ?
881 cnd.vtx[cnd.lastConfirmed].second.proofRoots.find(ASSETCHAINS_CHAINID)->second.rootHeight :
883 if (!cnd.IsConfirmed() ||
884 ourRoot.rootHeight <= lastHeight)
886 return state.Error(errorPrefix + "earned notarization proof root cannot be verified as later than prior confirmed for this chain");
889 auto hw = CMMRNode<>::GetHashWriter();
891 std::vector<unsigned char> notarizationVec = ::AsVector(earnedNotarization);
892 uint256 objHash = hw.write((const char *)&(notarizationVec[0]), notarizationVec.size()).GetHash();
895 for (auto &oneSig : notaryEvidence.signatures)
897 if (!notaries.count(oneSig.first))
899 return state.Error(errorPrefix + "unauthorized notary signature");
901 CIdentity sigIdentity = CIdentity::LookupIdentity(oneSig.first);
902 if (!sigIdentity.IsValidUnrevoked())
904 return state.Error(errorPrefix + "invalid notary identity");
907 // we currently require accepted notarizations to be completely authorized by notaries
908 /* printf("%s: Checking signature at sign height %u on system: %s for identity %s\nconfirmedKey: %s\nobjHash %s\n",
910 oneSig.second.blockHeight,
911 EncodeDestination(CIdentityID(SystemID)).c_str(),
912 EncodeDestination(CIdentityID(oneSig.first)).c_str(),
913 notaryEvidence.NotaryConfirmedKey().GetHex().c_str(),
914 objHash.GetHex().c_str());
915 printf("%s: notarization:\n%s\n", __func__, earnedNotarization.ToUniValue().write(1,2).c_str());
916 printf("%s: hex:\n%s\n", __func__, HexBytes(&(notarizationVec[0]), notarizationVec.size()).c_str()); */
918 if (oneSig.second.CheckSignature(sigIdentity,
919 std::vector<uint160>({notaryEvidence.NotaryConfirmedKey()}),
920 std::vector<uint256>(),
923 objHash) != oneSig.second.SIGNATURE_COMPLETE)
925 return state.Error(errorPrefix + "invalid or incomplete notary signature");
930 auto mmv = chainActive.GetMMV();
931 mmv.resize(ourRoot.rootHeight + 1);
933 // we only create accepted notarizations for notarizations that are earned for this chain on another system
934 // currently, we support ethereum and PBaaS types.
935 if (!newNotarization.proofRoots.count(SystemID) ||
936 !newNotarization.proofRoots.count(ASSETCHAINS_CHAINID) ||
937 !(ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID]).IsValid() ||
938 ourRoot.rootHeight > height ||
939 ourRoot.blockHash != chainActive[ourRoot.rootHeight]->GetBlockHash() ||
940 ourRoot.stateRoot != mmv.GetRoot() ||
941 (ourRoot.type != ourRoot.TYPE_PBAAS && ourRoot.type != ourRoot.TYPE_ETHEREUM))
943 return state.Error(errorPrefix + "can only create accepted notarization from notarization with valid proof root of this chain");
946 // ensure that the data present is valid, as of the height
947 CCoinbaseCurrencyState oldCurState = ConnectedChains.GetCurrencyState(ASSETCHAINS_CHAINID, ourRoot.rootHeight);
948 if (!oldCurState.IsValid() ||
949 ::GetHash(oldCurState) != ::GetHash(earnedNotarization.currencyState))
951 return state.Error(errorPrefix + "currency state is invalid in accepted notarization. is:\n" +
952 newNotarization.currencyState.ToUniValue().write(1,2) +
954 oldCurState.ToUniValue().write(1,2) + "\n");
957 // ensure that all locally provable info is valid as of our root height
958 // and determine if the new notarization should be already finalized or not
959 for (auto &oneCur : newNotarization.currencyStates)
961 if (oneCur.first == SystemID)
963 return state.Error(errorPrefix + "cannot accept redundant currency state in notarization for " + EncodeDestination(CIdentityID(SystemID)));
965 else if (oneCur.first != ASSETCHAINS_CHAINID)
967 // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
968 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneCur.first);
969 // we must have all currencies
970 if (!curDef.IsValid())
972 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
974 // if the currency is not from this chain, we cannot validate it
975 if (curDef.systemID != ASSETCHAINS_CHAINID)
979 // ensure that the data present is valid, as of the height
980 oldCurState = ConnectedChains.GetCurrencyState(oneCur.first, ourRoot.rootHeight);
981 if (!oldCurState.IsValid() ||
982 ::GetHash(oldCurState) != ::GetHash(oneCur.second))
984 return state.Error(errorPrefix + "currecy state is invalid in accepted notarization. is:\n" +
985 oneCur.second.ToUniValue().write(1,2) +
987 oldCurState.ToUniValue().write(1,2) + "\n");
992 // already checked above
996 for (auto &oneRoot : newNotarization.proofRoots)
998 if (oneRoot.first == SystemID)
1004 // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
1005 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneRoot.first);
1006 // we must have all currencies in this notarization registered
1007 if (!curDef.IsValid())
1009 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
1011 uint160 curDefID = curDef.GetID();
1013 // only check other currencies on this chain, not the main chain itself
1014 if (curDefID != ASSETCHAINS_CHAINID && curDef.systemID == ASSETCHAINS_CHAINID)
1016 return state.Error(errorPrefix + "proof roots are not accepted for token currencies");
1021 // now create the new notarization, add the proof, finalize if appropriate, and finish
1023 // add spend of prior notarization and then outputs
1024 CPBaaSNotarization lastUnspentNotarization;
1026 int32_t lastTxOutNum;
1027 CTransaction lastTx;
1028 if (!lastUnspentNotarization.GetLastUnspentNotarization(SystemID, lastTxId, lastTxOutNum, &lastTx))
1030 return state.Error(errorPrefix + "invalid prior notarization");
1033 // add prior unspent accepted notarization as our input
1034 txBuilder.AddTransparentInput(CUTXORef(lastTxId, lastTxOutNum), lastTx.vout[lastTxOutNum].scriptPubKey, lastTx.vout[lastTxOutNum].nValue);
1037 CCcontract_info *cp;
1038 std::vector<CTxDestination> dests;
1040 // make the earned notarization output
1041 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
1043 if (externalSystem.notarizationProtocol == externalSystem.NOTARIZATION_NOTARY_CHAINID)
1045 dests = std::vector<CTxDestination>({CIdentityID(externalSystem.GetID())});
1049 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1052 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &newNotarization)), 0);
1054 // now add the notary evidence and finalization that uses it to assert validity
1055 // make the earned notarization output
1056 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1057 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1058 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, ¬aryEvidence)), 0);
1060 if (externalSystem.notarizationProtocol != externalSystem.NOTARIZATION_NOTARY_CHAINID)
1062 // make the finalization output
1063 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1064 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1066 // we need to store the input that we confirmed if we spent finalization outputs
1067 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, newNotarization.currencyID, uint256(), txBuilder.mtx.vout.size() - 2, height + 15);
1068 if (notaryEvidence.signatures.size() >= minimumNotariesConfirm)
1071 of.evidenceOutputs.push_back(txBuilder.mtx.vout.size() - 1);
1073 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of)), 0);
1078 extern void CopyNodeStats(std::vector<CNodeStats>& vstats);
1080 std::vector<CNodeData> GetGoodNodes(int maxNum)
1082 // good nodes ordered by time connected
1083 std::map<int64_t, CNode *> goodNodes;
1084 std::vector<CNodeData> retVal;
1087 for (auto oneNode : vNodes)
1089 if (!oneNode->fInbound)
1091 if (oneNode->fWhitelisted)
1093 goodNodes.insert(std::make_pair(oneNode->nTimeConnected, oneNode));
1095 else if (oneNode->GetTotalBytesRecv() > (1024 * 1024))
1097 goodNodes.insert(std::make_pair(oneNode->nTimeConnected, oneNode));
1101 if (goodNodes.size() > 1)
1103 seed_insecure_rand();
1104 for (auto &oneNode : goodNodes)
1106 if (insecure_rand() & 1)
1108 retVal.push_back(CNodeData(oneNode.second->addr.ToStringIPPort(), oneNode.second->hashPaymentAddress));
1109 if (retVal.size() >= maxNum)
1117 retVal.push_back(CNodeData(goodNodes.begin()->second->addr.ToStringIPPort(), goodNodes.begin()->second->hashPaymentAddress));
1120 else if (goodNodes.size())
1122 retVal.push_back(CNodeData(goodNodes.begin()->second->addr.ToStringIPPort(), goodNodes.begin()->second->hashPaymentAddress));
1127 // create a notarization that is validated as part of the block, generally benefiting the miner or staker if the
1128 // cross notarization is valid
1129 bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalSystem,
1130 const CTransferDestination &Proposer,
1131 CValidationState &state,
1132 std::vector<CTxOut> &txOutputs,
1133 CPBaaSNotarization ¬arization)
1135 std::string errorPrefix(strprintf("%s: ", __func__));
1139 const CCurrencyDefinition &systemDef = externalSystem.chainDefinition;
1140 SystemID = externalSystem.chainDefinition.GetID();
1142 CChainNotarizationData cnd;
1143 std::vector<std::pair<CTransaction, uint256>> txes;
1146 LOCK2(cs_main, mempool.cs);
1147 height = chainActive.Height();
1149 // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
1150 // we also need to ensure that our notarization would be the first notarization in this notary block period with which we agree.
1151 if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
1153 // technically not a real error
1154 return state.Error("no-notary");
1157 if (!GetNotarizationData(SystemID, cnd, &txes))
1159 return state.Error(errorPrefix + "no prior notarization found");
1163 // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
1164 UniValue proofRootsUni(UniValue::VARR);
1165 for (auto &oneNot : cnd.vtx)
1167 auto rootIt = oneNot.second.proofRoots.find(SystemID);
1168 if (rootIt != oneNot.second.proofRoots.end())
1170 proofRootsUni.push_back(rootIt->second.ToUniValue());
1174 if (!proofRootsUni.size())
1176 return state.Error(errorPrefix + "no valid prior state root found");
1179 // call notary to determine the prior notarization that we agree with
1180 UniValue params(UniValue::VARR);
1182 UniValue oneParam(UniValue::VOBJ);
1183 oneParam.push_back(Pair("proofroots", proofRootsUni));
1184 oneParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1185 params.push_back(oneParam);
1187 //printf("%s: about to get cross notarization with %lu notarizations found\n", __func__, cnd.vtx.size());
1192 result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1193 } catch (exception e)
1195 result = NullUniValue;
1198 int32_t notaryIdx = uni_get_int(find_value(result, "bestindex"), -1);
1200 if (result.isNull() || notaryIdx == -1)
1202 return state.Error(result.isNull() ? "no-notary" : "no-matching-proof-roots-found");
1205 // now, we have the index for the transaction and notarization we agree with, a list of those we consider invalid,
1206 // and the most recent notarization to use when creating the new one
1207 const CTransaction &priorNotarizationTx = txes[notaryIdx].first;
1208 uint256 priorBlkHash = txes[notaryIdx].second;
1209 const CUTXORef &priorUTXO = cnd.vtx[notaryIdx].first;
1210 const CPBaaSNotarization &priorNotarization = cnd.vtx[notaryIdx].second;
1212 // find out the block height holding the last notarization we agree with
1213 auto mapBlockIt = mapBlockIndex.find(priorBlkHash);
1214 if (mapBlockIt == mapBlockIndex.end() || !chainActive.Contains(mapBlockIt->second))
1216 return state.Error(errorPrefix + "prior notarization not in blockchain");
1219 // first determine if the prior notarization we agree with would make this one moot
1220 int blockPeriodNumber = (height + 1) / BLOCK_NOTARIZATION_MODULO;
1221 int priorBlockPeriod = mapBlockIt->second->GetHeight() / BLOCK_NOTARIZATION_MODULO;
1223 if (blockPeriodNumber <= priorBlockPeriod)
1225 return state.Error("ineligible");
1228 notarization = priorNotarization;
1229 notarization.SetBlockOneNotarization(false);
1230 notarization.SetDefinitionNotarization(false);
1231 notarization.proposer = Proposer;
1232 notarization.prevHeight = priorNotarization.notarizationHeight;
1234 // get the latest notarization information for the new, earned notarization
1235 // one system may provide one proof root and multiple currency states
1236 CProofRoot latestProofRoot = CProofRoot(find_value(result, "latestproofroot"));
1237 if (!latestProofRoot.IsValid() || notarization.proofRoots[latestProofRoot.systemID].rootHeight >= latestProofRoot.rootHeight)
1239 return state.Error("no-new-latest-proof-root");
1241 notarization.proofRoots[latestProofRoot.systemID] = latestProofRoot;
1242 notarization.notarizationHeight = latestProofRoot.rootHeight;
1244 UniValue currencyStatesUni = find_value(result, "currencystates");
1245 if (!(currencyStatesUni.isArray() && currencyStatesUni.size()))
1247 return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1250 // take the lock again, now that we're back from calling out
1251 LOCK2(cs_main, mempool.cs);
1253 // if height changed, we need to fail and possibly try again
1254 if (height != chainActive.Height())
1256 return state.Error("stale-block");
1259 notarization.currencyStates.clear();
1260 for (int i = 0; i < currencyStatesUni.size(); i++)
1262 CCoinbaseCurrencyState oneCurState(currencyStatesUni[i]);
1263 CCurrencyDefinition oneCurDef;
1264 if (!oneCurState.IsValid())
1266 return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1268 if (!(oneCurDef = ConnectedChains.GetCachedCurrency(oneCurState.GetID())).IsValid())
1270 // if we don't have the currency for the state specified, and it isn't critical, ignore
1271 if (oneCurDef.GetID() == SystemID)
1273 return state.Error(errorPrefix + "system currency invalid - possible corruption");
1277 if (oneCurDef.systemID == SystemID)
1279 uint160 oneCurDefID = oneCurDef.GetID();
1280 if (notarization.currencyID == oneCurDefID)
1282 notarization.currencyState = oneCurState;
1286 notarization.currencyStates[oneCurDefID] = oneCurState;
1290 notarization.currencyStates[ASSETCHAINS_CHAINID] = ConnectedChains.GetCurrencyState(height);
1291 if (!systemDef.GatewayConverterID().IsNull())
1293 notarization.currencyStates[systemDef.GatewayConverterID()] = ConnectedChains.GetCurrencyState(systemDef.GatewayConverterID(), height);
1296 // add this blockchain's info, based on the requested height
1297 CBlockIndex &curBlkIndex = *chainActive[height];
1298 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1299 notarization.proofRoots[thisChainID] = CProofRoot::GetProofRoot(height);
1301 // add currency states that we should include and then we're done
1302 // currency states to include are either a gateway currency indicated by the
1303 // gateway or our gateway converter for our PBaaS chain
1304 uint160 gatewayConverterID;
1305 if (systemDef.IsGateway() && !systemDef.gatewayConverterName.empty())
1307 gatewayConverterID = CCurrencyDefinition::GetID(systemDef.gatewayConverterName, thisChainID);
1309 else if (SystemID == ConnectedChains.FirstNotaryChain().chainDefinition.GetID() && !ConnectedChains.ThisChain().gatewayConverterName.empty())
1311 gatewayConverterID = CCurrencyDefinition::GetID(ConnectedChains.ThisChain().gatewayConverterName, thisChainID);
1313 if (!gatewayConverterID.IsNull())
1315 // get the gateway converter currency from the gateway definition
1316 CChainNotarizationData gatewayCND;
1317 if (GetNotarizationData(gatewayConverterID, gatewayCND) && gatewayCND.vtx.size())
1319 notarization.currencyStates[gatewayConverterID] = gatewayCND.vtx[gatewayCND.lastConfirmed].second.currencyState;
1323 notarization.nodes = GetGoodNodes(CPBaaSNotarization::MAX_NODES);
1325 notarization.prevNotarization = cnd.vtx[notaryIdx].first;
1326 auto hw = CMMRNode<>::GetHashWriter();
1327 hw << cnd.vtx[notaryIdx].second;
1328 notarization.hashPrevNotarization = hw.GetHash();
1329 notarization.prevHeight = cnd.vtx[notaryIdx].second.notarizationHeight;
1332 CCcontract_info *cp;
1333 std::vector<CTxDestination> dests;
1335 // make the earned notarization output
1336 cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
1338 if (systemDef.notarizationProtocol == systemDef.NOTARIZATION_NOTARY_CHAINID)
1340 dests = std::vector<CTxDestination>({CIdentityID(systemDef.GetID())});
1344 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1347 txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_EARNEDNOTARIZATION, dests, 1, ¬arization))));
1349 if (systemDef.notarizationProtocol != systemDef.NOTARIZATION_NOTARY_CHAINID)
1351 // make the finalization output
1352 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1354 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1356 // we need to store the input that we confirmed if we spent finalization outputs
1357 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, VERUS_CHAINID, uint256(), txOutputs.size() - 1, height + 15);
1358 txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of))));
1363 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentConfirmedFinalizations(const uint160 ¤cyID)
1366 std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1367 std::vector<CAddressUnspentDbEntry> indexUnspent;
1368 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1371 uint160 indexKey = CCrossChainRPCData::GetConditionID(
1372 CCrossChainRPCData::GetConditionID(currencyID, CObjectFinalization::ObjectFinalizationNotarizationKey()),
1373 ObjectFinalizationConfirmedKey());
1374 if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1375 mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1376 (indexUnspent.size() || mempoolUnspent.size()))
1378 /* printf("%s: confirmedNotarizationKey: %s / 0x%s\nconfirmed finalizations\n",
1380 EncodeDestination(CIdentityID(indexKey)).c_str(),
1381 indexKey.GetHex().c_str()); */
1383 for (auto &oneConfirmed : indexUnspent)
1385 //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1386 retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1387 CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1389 std::set<std::pair<uint256,int>> mempoolSpent;
1390 for (auto &oneConfirmed : mempoolUnspent)
1392 if (oneConfirmed.first.spending)
1394 if (retVal.size() &&
1395 retVal.back().second.txIn.prevout == COutPoint(oneConfirmed.first.txhash, oneConfirmed.first.index))
1397 // remove it if spent
1398 //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index);
1401 mempoolSpent.insert(std::make_pair(oneConfirmed.first.txhash, oneConfirmed.first.index));
1404 for (auto &oneConfirmed : mempoolUnspent)
1406 if (mempoolSpent.count(std::make_pair(oneConfirmed.first.txhash, oneConfirmed.first.index)))
1411 auto txProxy = mempool.mapTx.find(oneConfirmed.first.txhash);
1412 if (txProxy != mempool.mapTx.end())
1414 auto &mpEntry = *txProxy;
1415 auto &tx = mpEntry.GetTx();
1416 //printf("%s: txid: %s, vout: %u\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index);
1417 retVal.push_back(std::make_pair(0,
1418 CInputDescriptor(tx.vout[oneConfirmed.first.index].scriptPubKey,
1419 tx.vout[oneConfirmed.first.index].nValue,
1420 CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1427 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentPendingFinalizations(const uint160 ¤cyID)
1430 std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1431 std::vector<CAddressUnspentDbEntry> indexUnspent;
1432 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1434 uint160 indexKey = CCrossChainRPCData::GetConditionID(
1435 CCrossChainRPCData::GetConditionID(currencyID, CObjectFinalization::ObjectFinalizationNotarizationKey()),
1436 ObjectFinalizationPendingKey());
1437 if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1438 mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1439 (indexUnspent.size() || mempoolUnspent.size()))
1441 /* printf("%s: pendingNotarizationKey: %s / 0x%s\npending finalizations\n",
1443 EncodeDestination(CIdentityID(indexKey)).c_str(),
1444 indexKey.GetHex().c_str()); */
1446 for (auto &oneConfirmed : indexUnspent)
1448 //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1449 retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1450 CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1452 std::set<std::pair<uint256,int>> mempoolSpent;
1453 for (auto &oneUnconfirmed : mempoolUnspent)
1455 if (oneUnconfirmed.first.spending)
1457 if (retVal.size() &&
1458 retVal.back().second.txIn.prevout == COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))
1460 // remove it if spent
1461 //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneUnconfirmed.first.txhash.GetHex().c_str(), oneUnconfirmed.first.index);
1464 mempoolSpent.insert(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1467 for (auto &oneUnconfirmed : mempoolUnspent)
1469 if (mempoolSpent.count(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)))
1474 auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1475 if (txProxy != mempool.mapTx.end())
1477 auto &mpEntry = *txProxy;
1478 auto &tx = mpEntry.GetTx();
1479 retVal.push_back(std::make_pair(0,
1480 CInputDescriptor(tx.vout[oneUnconfirmed.first.index].scriptPubKey,
1481 tx.vout[oneUnconfirmed.first.index].nValue,
1482 CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1489 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentEvidence(const uint160 ¤cyID,
1490 const uint256 ¬arizationTxId,
1491 int32_t notarizationOutNum)
1494 std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1495 std::vector<CAddressUnspentDbEntry> indexUnspent;
1496 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1497 uint160 indexKey = CCrossChainRPCData::GetConditionID(currencyID, CNotaryEvidence::NotarySignatureKey(), notarizationTxId, notarizationOutNum);
1499 if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1500 mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1501 (indexUnspent.size() || mempoolUnspent.size()))
1503 /* printf("%s: unspentEvidenceKey: %s / 0x%s\nunspent evidence\n",
1505 EncodeDestination(CIdentityID(indexKey)).c_str(),
1506 indexKey.GetHex().c_str()); */
1508 for (auto &oneConfirmed : indexUnspent)
1510 //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1511 retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1512 CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1514 std::set<std::pair<uint256,int>> mempoolSpent;
1515 for (auto &oneUnconfirmed : mempoolUnspent)
1517 if (oneUnconfirmed.first.spending)
1519 if (retVal.size() &&
1520 retVal.back().second.txIn.prevout == COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))
1522 // remove it if spent
1523 //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneUnconfirmed.first.txhash.GetHex().c_str(), oneUnconfirmed.first.index);
1526 mempoolSpent.insert(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1529 for (auto &oneUnconfirmed : mempoolUnspent)
1531 if (mempoolSpent.count(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)))
1536 auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1537 if (txProxy != mempool.mapTx.end())
1539 auto &mpEntry = *txProxy;
1540 auto &tx = mpEntry.GetTx();
1541 retVal.push_back(std::make_pair(0,
1542 CInputDescriptor(tx.vout[oneUnconfirmed.first.index].scriptPubKey,
1543 tx.vout[oneUnconfirmed.first.index].nValue,
1544 CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1551 // this is called by notaries to locate any notarizations of a specific system that they can notarize, to determine if we
1552 // agree with the notarization in question, and to confirm or reject the notarization
1553 bool CPBaaSNotarization::ConfirmOrRejectNotarizations(const CWallet *pWallet,
1554 const CRPCChainData &externalSystem,
1555 CValidationState &state,
1556 TransactionBuilder &txBuilder,
1559 std::string errorPrefix(strprintf("%s: ", __func__));
1563 CChainNotarizationData cnd;
1564 std::vector<std::pair<CTransaction, uint256>> txes;
1567 uint160 SystemID = externalSystem.chainDefinition.GetID();
1569 std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> mine;
1571 std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> imsigner, watchonly;
1572 LOCK(pWallet->cs_wallet);
1573 // sign with all IDs under our control that are eligible for this currency
1574 pWallet->GetIdentities(ConnectedChains.ThisChain().notaries, mine, imsigner, watchonly);
1577 return state.Error("no-notary");
1582 LOCK2(cs_main, mempool.cs);
1583 height = chainActive.Height();
1585 // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
1586 // we also need to ensure that our notarization would be the first notarization in this notary block period with which we agree.
1587 if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
1589 // technically not a real error
1590 return state.Error("no-notary");
1593 if (!GetNotarizationData(SystemID, cnd, &txes))
1595 return state.Error(errorPrefix + "no prior notarization found");
1599 if (cnd.IsConfirmed() && cnd.vtx.size() == 1)
1601 return state.Error("no-unconfirmed");
1604 if (height <= (CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED + 1))
1606 return state.Error(errorPrefix + "too early");
1609 // latest height we are eligible to notarize
1610 uint32_t eligibleHeight = height - CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED;
1612 // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
1613 UniValue proofRootsUni(UniValue::VARR);
1614 for (auto &oneNot : cnd.vtx)
1616 auto rootIt = oneNot.second.proofRoots.find(SystemID);
1617 if (rootIt != oneNot.second.proofRoots.end())
1619 proofRootsUni.push_back(rootIt->second.ToUniValue());
1623 if (!proofRootsUni.size())
1625 return state.Error(errorPrefix + "no valid prior state root found");
1628 UniValue firstParam(UniValue::VOBJ);
1629 firstParam.push_back(Pair("proofroots", proofRootsUni));
1630 firstParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1632 // call notary to determine the notarization that we should notarize
1633 UniValue params(UniValue::VARR);
1634 params.push_back(firstParam);
1636 //printf("%s: about to getbestproofroot with:\n%s\n", __func__, params.write(1,2).c_str());
1641 result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1642 } catch (exception e)
1644 result = NullUniValue;
1647 int32_t notaryIdx = uni_get_int(find_value(result, "bestindex"), -1);
1649 if (result.isNull() || notaryIdx == -1)
1651 return state.Error(result.isNull() ? "no-notary" : "no-matching-notarization-found");
1654 // take the lock again, now that we're back from calling out
1655 LOCK2(cs_main, mempool.cs);
1657 // if height changed, we need to fail and possibly try again later
1658 if (height != chainActive.Height())
1660 return state.Error("stale-block");
1663 // now, get the list of unconfirmed matches, and sign the latest one that
1665 UniValue proofRootArr = find_value(result, "validindexes");
1666 if (!proofRootArr.isArray() || !proofRootArr.size())
1668 return state.Error("no-valid-unconfirmed");
1671 if (!proofRootArr.isArray() || !proofRootArr.size())
1673 return state.Error("no-valid-unconfirmed");
1676 bool retVal = false;
1678 // look from the latest notarization that may qualify
1679 for (int i = proofRootArr.size() - 1; i >= 0; i--)
1681 int idx = uni_get_int(proofRootArr[i]);
1682 auto proofIt = cnd.vtx[idx].second.proofRoots.find(ASSETCHAINS_CHAINID);
1683 if (proofIt != cnd.vtx[idx].second.proofRoots.end() &&
1684 proofIt->second.rootHeight <= eligibleHeight)
1686 // if confirmed, we have no more to check
1687 if (cnd.lastConfirmed == idx)
1692 // this is the one we will notarize
1693 std::vector<CInputDescriptor> myIDSigs;
1695 std::set<CIdentityID> myIDSet;
1696 for (auto &oneID : mine)
1698 myIDSet.insert(oneID.first.idID);
1701 // a pending finalization may be either an actual notarization finalization or an earned notarization
1702 // a confirmed finalization may be either a finalization or a notarization
1703 std::vector<std::pair<uint32_t, CInputDescriptor>> unspentConfirmed = CObjectFinalization::GetUnspentConfirmedFinalizations(SystemID);
1704 std::vector<std::pair<uint32_t, CInputDescriptor>> unspentPending = CObjectFinalization::GetUnspentPendingFinalizations(SystemID);
1706 // if we finalize and confirm a notarization, we will spend all prior confirmed and
1707 // pending outputs as part of finalization.
1709 // if we only sign and add to a confirmation but cannot meet finalization requirements, we can
1710 // spend and roll up pending outputs into ours as combined
1712 // CUTXORef = vtx UTXORef
1714 // int = index into the vtx vector
1715 // vector of pairs {uint32_t = blockHeight, CInputDescriptor for evidence}
1718 std::map<CUTXORef, std::pair<int, std::vector<std::pair<uint32_t, CInputDescriptor>>>> vtxFinalizations;
1719 for (int j = 0; j < cnd.vtx.size(); j++)
1723 // if any of the notarizations are earned notarizations, we will spend those behind us if we finalize
1724 if (txes[j].first.vout[cnd.vtx[j].first.n].scriptPubKey.IsPayToCryptoCondition(oneP) &&
1726 oneP.evalCode == EVAL_EARNEDNOTARIZATION)
1728 // printf("%s: adding %s to vtxFinalizations\n", __func__, cnd.vtx[j].first.ToUniValue().write(1,2).c_str());
1729 vtxFinalizations.insert(std::make_pair(cnd.vtx[j].first, std::make_pair(j, std::vector<std::pair<uint32_t, CInputDescriptor>>())));
1733 for (auto &oneConfirmed : unspentConfirmed)
1735 CObjectFinalization oneOf;
1737 if (oneConfirmed.second.scriptPubKey.IsPayToCryptoCondition(oneP) &&
1739 oneP.evalCode != EVAL_EARNEDNOTARIZATION &&
1740 oneP.evalCode != EVAL_ACCEPTEDNOTARIZATION)
1742 oneOf = CObjectFinalization(oneConfirmed.second.scriptPubKey);
1743 if (!oneOf.IsValid())
1745 printf("%s: invalid index entry at %s : %u\n",
1747 oneConfirmed.second.txIn.prevout.hash.GetHex().c_str(),
1748 oneConfirmed.second.txIn.prevout.n);
1757 CUTXORef oneConfirmedTarget = CUTXORef(oneOf.output.hash.IsNull() ? oneConfirmed.second.txIn.prevout.hash : oneOf.output.hash,
1760 // if the notarization we have selected to confirm is already confirmed, we are done
1761 if (vtxFinalizations[oneConfirmedTarget].first == idx)
1763 return state.Error("notarization-already-confirmed");
1765 /* printf("%s: adding %s to entry #%d in vtxFinalizations\n",
1767 CUTXORef(oneConfirmed.second.txIn.prevout).ToUniValue().write(1,2).c_str(),
1768 vtxFinalizations[oneConfirmedTarget].first); */
1769 vtxFinalizations[oneConfirmedTarget].second.push_back(oneConfirmed);
1772 // unspent evidence is specific to the target notarization
1773 std::vector<std::pair<uint32_t, CInputDescriptor>> unspentEvidence = CObjectFinalization::GetUnspentEvidence(ASSETCHAINS_CHAINID,
1774 cnd.vtx[idx].first.hash,
1775 cnd.vtx[idx].first.n);
1776 std::vector<CInputDescriptor> cleanupSpend;
1778 for (auto &onePending : unspentPending)
1780 CObjectFinalization oneOf;
1782 if (onePending.second.scriptPubKey.IsPayToCryptoCondition(oneP) &&
1785 if (oneP.evalCode == EVAL_EARNEDNOTARIZATION ||
1786 oneP.evalCode == EVAL_ACCEPTEDNOTARIZATION)
1790 if (oneP.evalCode == EVAL_FINALIZE_NOTARIZATION && oneP.vData.size())
1792 oneOf = CObjectFinalization(oneP.vData[0]);
1794 if (!oneOf.IsValid())
1796 printf("%s: invalid index entry at %s : %u\n",
1798 onePending.second.txIn.prevout.hash.GetHex().c_str(),
1799 onePending.second.txIn.prevout.n);
1804 CUTXORef onePendingTarget = CUTXORef(oneOf.output.hash.IsNull() ? onePending.second.txIn.prevout.hash : oneOf.output.hash,
1806 if (!vtxFinalizations.count(onePendingTarget))
1808 CTransaction checkTx;
1810 if (myGetTransaction(onePendingTarget.hash, checkTx, blockHash))
1812 /* UniValue jsonTx(UniValue::VOBJ);
1813 TxToUniv(checkTx, blockHash, jsonTx);
1814 printf("%s: cleanup forked tx: %s\noutput #%u\nvtxFinalizations:\n", __func__, jsonTx.write(1,2).c_str(), onePendingTarget.n); */
1815 cleanupSpend.push_back(onePending.second);
1819 printf("%s: pending notarization finalization with no notarization matching (%s) found - may be mempool only\n", __func__, onePendingTarget.ToUniValue().write(1,2).c_str());
1821 vtxFinalizations[onePendingTarget].first = -1;
1823 vtxFinalizations[onePendingTarget].second.push_back(onePending);
1826 // we need to have a pending finalization to spend to add confirmations
1827 if (!vtxFinalizations[cnd.vtx[idx].first].second.size())
1829 printf("%s: ERROR: pending notarization (%s) with no matching finalization\n", __func__, cnd.vtx[idx].first.ToUniValue().write(1,2).c_str());
1833 // before signing the one we are about to, we want to ensure that it isn't already signed sufficiently
1834 // if there are enough signatures to confirm it without signature, make our signature, then create a finalization
1835 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION,
1837 cnd.vtx[idx].first.hash,
1838 cnd.vtx[idx].first.n,
1841 std::vector<CInputDescriptor> additionalEvidence;
1843 std::set<uint160> sigSet;
1845 int minimumNotariesConfirm = (externalSystem.GetID() == ConnectedChains.FirstNotaryChain().GetID()) ?
1846 minimumNotariesConfirm = ConnectedChains.ThisChain().minNotariesConfirm :
1847 minimumNotariesConfirm = ConnectedChains.FirstNotaryChain().chainDefinition.minNotariesConfirm;
1849 // if we might have a confirmed notarization, verify, then post
1850 for (auto &oneEvidenceOut : unspentEvidence)
1853 CNotaryEvidence evidence;
1854 if (oneEvidenceOut.second.scriptPubKey.IsPayToCryptoCondition(p) &&
1856 p.evalCode == EVAL_NOTARY_EVIDENCE &&
1858 (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
1859 evidence.IsNotarySignature())
1861 if (CUTXORef(evidence.output.hash.IsNull() ? oneEvidenceOut.second.txIn.prevout.hash : evidence.output.hash, evidence.output.n) == of.output &&
1862 evidence.signatures.size())
1864 bool hasOurSig = false;
1865 for (auto &oneSig : evidence.signatures)
1867 sigSet.insert(oneSig.first);
1868 if (myIDSet.count(oneSig.first))
1871 myIDSet.erase(oneSig.first);
1876 myIDSigs.push_back(oneEvidenceOut.second);
1880 additionalEvidence.push_back(oneEvidenceOut.second);
1885 printf("%s: ERROR - unexpected code path, no assert thrown for debugging\n", __func__);
1886 cleanupSpend.push_back(oneEvidenceOut.second);
1891 CNotaryEvidence ne(ASSETCHAINS_CHAINID, cnd.vtx[idx].first);
1893 CCcontract_info *cp;
1894 std::vector<CTxDestination> dests;
1896 // if we can still sign, do so before final check of evidence
1899 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1900 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1903 LOCK(pWallet->cs_wallet);
1904 // sign with all IDs under our control that are eligible for this currency
1905 for (auto &oneID : myIDSet)
1907 auto signResult = ne.SignConfirmed(*pWallet, txes[idx].first, oneID, height);
1908 if (signResult == CIdentitySignature::SIGNATURE_PARTIAL || signResult == CIdentitySignature::SIGNATURE_COMPLETE)
1910 sigSet.insert(oneID);
1912 // if our signatures altogether have provided a complete validation, we can early out
1913 if ((ne.signatures.size() + myIDSigs.size()) >= minimumNotariesConfirm)
1920 return state.Error(errorPrefix + "invalid identity signature");
1925 if (ne.signatures.size())
1927 /* for (auto &debugOut : ne.signatures)
1929 printf("%s: onesig - ID: %s, signature: %s\n", __func__, EncodeDestination(debugOut.first).c_str(), debugOut.second.ToUniValue().write(1,2).c_str());
1933 CScript evidenceScript = MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, &ne));
1934 myIDSigs.push_back(CInputDescriptor(evidenceScript, 0, CTxIn(COutPoint(uint256(), txBuilder.mtx.vout.size()))));
1935 of.evidenceOutputs.push_back(txBuilder.mtx.vout.size());
1936 txBuilder.AddTransparentOutput(evidenceScript, CNotaryEvidence::DEFAULT_OUTPUT_VALUE);
1940 // if we have enough to finalize, do so as a combination of pre-existing evidence and this
1941 if (sigSet.size() >= minimumNotariesConfirm)
1947 // spend all priors, and if we need more signatures, add them to the finalization evidence
1948 // prioritizing our signatures
1949 bool haveNeeded = false;
1950 std::vector<CInputDescriptor> inputSigs;
1951 for (auto &oneEvidenceOut : myIDSigs)
1953 // use up evidence with our ID signatures first, and remove from the remainder
1955 CNotaryEvidence evidence;
1958 oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1959 evidence = CNotaryEvidence(p.vData[0]);
1960 for (auto &oneSig : evidence.signatures)
1962 if (sigSet.count(oneSig.first))
1965 sigSet.erase(oneSig.first);
1971 haveNeeded = sigCount >= minimumNotariesConfirm;
1973 // until we have enough signatures to confirm, continue to add evidence to the finalization
1974 if (!oneEvidenceOut.txIn.prevout.hash.IsNull())
1976 of.evidenceInputs.push_back(txBuilder.mtx.vin.size());
1977 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1982 // add additional evidence spends
1983 for (auto &oneEvidenceOut : additionalEvidence)
1985 // use up evidence with our ID signatures first, and remove from the remainder
1987 CNotaryEvidence evidence;
1989 oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1990 evidence = CNotaryEvidence(p.vData[0]);
1991 for (auto &oneSig : evidence.signatures)
1993 if (sigSet.count(oneSig.first))
1996 sigSet.erase(oneSig.first);
1999 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
2002 // until we have enough signatures to confirm, continue to add evidence to the finalization
2003 of.evidenceInputs.push_back(txBuilder.mtx.vin.size() - 1);
2004 haveNeeded = sigCount >= minimumNotariesConfirm;
2008 // remove when this vector is confirmed as unnecessary
2009 for (auto &oneEvidenceOut : cleanupSpend)
2011 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
2014 // if we have confirmed this, we will output a new, confirmed finalization for this notarization and
2015 // spend all prior finalizations, confirmed or unconfirmed, for prior notarizations. all prior notarizations
2016 // behind a confirmed notarization are considered final in their current confirmed/unconfirmed state
2017 // we include our current pending finalization as well
2018 for (int j = idx; j >= 0; j--)
2020 for (auto &oneInputDescPair : vtxFinalizations[cnd.vtx[j].first].second)
2022 txBuilder.AddTransparentInput(oneInputDescPair.second.txIn.prevout,
2023 oneInputDescPair.second.scriptPubKey,
2024 oneInputDescPair.second.nValue);
2030 // should never get here
2031 return state.Error(errorPrefix + "Internal error");
2036 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2037 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2039 CScript finalizeScript = MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of));
2040 txBuilder.AddTransparentOutput(finalizeScript, 0);
2052 bool CallNotary(const CRPCChainData ¬arySystem, std::string command, const UniValue ¶ms, UniValue &result, UniValue &error);
2054 // look for finalized notarizations either on chain or in the mempool, which are eligible for submission
2055 // and submit them to the notary chain.
2056 std::vector<uint256> CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPCChainData &externalSystem,
2057 CValidationState &state)
2059 std::vector<uint256> retVal;
2061 // look for finalized notarizations for our notary chains in recent blocks
2062 // if we find any and are connected, submit them
2063 uint160 systemID = externalSystem.chainDefinition.GetID();
2064 CChainNotarizationData cnd;
2065 CPBaaSNotarization lastConfirmedNotarization;
2066 CObjectFinalization confirmedFinalization;
2067 CTransaction finTx, nTx;
2068 uint256 finBlkHash, notBlkHash;
2069 CNotaryEvidence allEvidence(CNotaryEvidence::TYPE_NOTARY_SIGNATURE), scratchEvidence(CNotaryEvidence::TYPE_NOTARY_SIGNATURE);
2071 bool confirmedFound = false;
2074 LOCK2(cs_main, mempool.cs);
2076 uint32_t nHeight = chainActive.Height();
2077 uint160 finalizeConfirmedKey =
2078 CCrossChainRPCData::GetConditionID(
2079 CCrossChainRPCData::GetConditionID(systemID, CObjectFinalization::ObjectFinalizationNotarizationKey()),
2080 CObjectFinalization::ObjectFinalizationConfirmedKey());
2082 std::vector<CAddressUnspentDbEntry> unspentFinalizations;
2083 if (GetAddressUnspent(finalizeConfirmedKey, CScript::P2IDX, unspentFinalizations) && unspentFinalizations.size())
2085 // get the latest, confirmed notarization
2086 auto bestIt = unspentFinalizations.begin();
2087 for (auto oneIt = bestIt; oneIt != unspentFinalizations.end(); oneIt++)
2089 if (oneIt->second.blockHeight > bestIt->second.blockHeight)
2096 if (!bestIt->second.script.IsPayToCryptoCondition(p) ||
2098 !(p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION || p.evalCode == EVAL_ACCEPTEDNOTARIZATION) ||
2101 LogPrintf("Invalid finalization or notarization on transaction %s, output %ld may need to reindex\n", bestIt->first.txhash.GetHex().c_str(), bestIt->first.index);
2102 printf("Invalid finalization or notarization on transaction %s, output %ld may need to reindex\n", bestIt->first.txhash.GetHex().c_str(), bestIt->first.index);
2106 // if this is actually a finalization, get the notarization it is for
2107 if (p.evalCode == EVAL_FINALIZE_NOTARIZATION)
2109 confirmedFinalization = CObjectFinalization(p.vData[0]);
2110 if (confirmedFinalization.output.hash.IsNull())
2112 confirmedFinalization.output.hash = bestIt->first.txhash;
2115 if (confirmedFinalization.IsValid() &&
2116 myGetTransaction(bestIt->first.txhash, finTx, finBlkHash) && finTx.vout.size() > bestIt->first.index &&
2117 ((bestIt->first.txhash == confirmedFinalization.output.hash &&
2118 (nTx = finTx, notBlkHash = finBlkHash, nTx.vout.size() > confirmedFinalization.output.n)) ||
2119 (myGetTransaction(confirmedFinalization.output.hash, nTx, finBlkHash) &&
2120 nTx.vout.size() > confirmedFinalization.output.n)))
2122 lastConfirmedNotarization = CPBaaSNotarization(nTx.vout[confirmedFinalization.output.n].scriptPubKey);
2123 if (!lastConfirmedNotarization.IsValid())
2125 LogPrintf("Invalid notarization on transaction %s, output %u may need to reindex\n",
2126 confirmedFinalization.output.hash.GetHex().c_str(), confirmedFinalization.output.n);
2127 printf("Invalid finalization on transaction %s, output %u may need to reindex\n",
2128 confirmedFinalization.output.hash.GetHex().c_str(), confirmedFinalization.output.n);
2131 confirmedFound = true;
2136 // get all notarization evidence and submit the notarization + evidence
2137 allEvidence.systemID = ASSETCHAINS_CHAINID;
2138 allEvidence.output = CUTXORef(nTx.GetHash(), confirmedFinalization.output.n);
2140 // get all inputs with evidence and add it to our evidence
2141 for (auto &oneEvidenceIn : confirmedFinalization.evidenceInputs)
2143 CTransaction evidenceTx;
2147 if (!(finTx.vin.size() > oneEvidenceIn &&
2148 myGetTransaction(finTx.vin[oneEvidenceIn].prevout.hash, evidenceTx, eBlkHash) &&
2149 evidenceTx.vout.size() > finTx.vin[oneEvidenceIn].prevout.n &&
2150 evidenceTx.vout[finTx.vin[oneEvidenceIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(eP) &&
2152 eP.evalCode == EVAL_NOTARY_EVIDENCE &&
2154 (scratchEvidence = CNotaryEvidence(eP.vData[0])).IsValid()))
2156 state.Error("innaccessible-evidence");
2157 return std::vector<uint256>();
2159 allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
2160 for (auto &oneSig : scratchEvidence.signatures)
2162 if (oneSig.second.signatures.size())
2164 allEvidence.signatures[oneSig.first].version = oneSig.second.version;
2165 allEvidence.signatures[oneSig.first].blockHeight = oneSig.second.blockHeight;
2166 for (auto &oneSigEntry : oneSig.second.signatures)
2168 allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
2174 // get all outputs with evidence and add
2175 for (auto oneEvidenceOut : confirmedFinalization.evidenceOutputs)
2178 if (!(finTx.vout.size() > oneEvidenceOut &&
2179 finTx.vout[oneEvidenceOut].scriptPubKey.IsPayToCryptoCondition(p) &&
2181 (p.evalCode == EVAL_NOTARY_EVIDENCE) &&
2183 (scratchEvidence = CNotaryEvidence(p.vData[0])).IsValid()))
2185 state.Error("invalid-evidence");
2186 return std::vector<uint256>();
2188 allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
2189 for (auto &oneSig : scratchEvidence.signatures)
2191 if (oneSig.second.signatures.size())
2193 allEvidence.signatures[oneSig.first].version = oneSig.second.version;
2194 allEvidence.signatures[oneSig.first].blockHeight = oneSig.second.blockHeight;
2195 for (auto &oneSigEntry : oneSig.second.signatures)
2197 allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
2203 // The first, block one notarization requires no evidence or signatures to be valid on this hain, so it will be skipped
2204 // here as well. This is not an error.
2205 if (!allEvidence.evidence.size() && !allEvidence.signatures.size())
2211 // now, we should have enough evidence to prove
2212 // the notarization. the API call will ensure that we do
2213 UniValue params(UniValue::VARR);
2214 UniValue result, error;
2215 std::string strTxId;
2216 params.push_back(lastConfirmedNotarization.ToUniValue());
2217 params.push_back(allEvidence.ToUniValue());
2219 /* for (auto &debugOut : oneNotarization.second.signatures)
2221 printf("%s: onesig - ID: %s, signature: %s\n", __func__, EncodeDestination(debugOut.first).c_str(), debugOut.second.ToUniValue().write(1,2).c_str());
2223 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());
2224 printf("%s: initial notarization:\n%s\n", __func__, oneNotarization.first.ToUniValue().write(1,2).c_str());
2225 std::vector<unsigned char> notVec1 = ::AsVector(oneNotarization.first);
2226 CPBaaSNotarization checkNotarization(oneNotarization.first.ToUniValue());
2227 std::vector<unsigned char> notVec2 = ::AsVector(checkNotarization);
2228 printf("%s: processed notarization:\n%s\n", __func__, checkNotarization.ToUniValue().write(1,2).c_str());
2229 std::vector<unsigned char> notVec3 = ::AsVector(CPBaaSNotarization(notVec1));
2230 printf("%s: hex before univalue:\n%s\n", __func__, HexBytes(&(notVec1[0]), notVec1.size()).c_str());
2231 printf("%s: hex after univalue:\n%s\n", __func__, HexBytes(&(notVec2[0]), notVec2.size()).c_str());
2232 printf("%s: hex after reserialization:\n%s\n", __func__, HexBytes(&(notVec3[0]), notVec3.size()).c_str()); */
2234 if (!CallNotary(externalSystem, "submitacceptednotarization", params, result, error) ||
2236 (strTxId = uni_get_str(result)).empty() ||
2241 // store the transaction ID and accepted notariation to prevent redundant submits
2242 retVal.push_back(uint256S(strTxId));
2247 * Validates a notarization output spend by ensuring that the spending transaction fulfills all requirements.
2248 * to accept an earned notarization as valid on the Verus blockchain, it must prove a transaction on the alternate chain, which is
2249 * either the original chain definition transaction, which CAN and MUST be proven ONLY in block 1, or the latest notarization transaction
2250 * on the alternate chain that represents an accurate MMR for this chain.
2251 * In addition, any accepted notarization must fullfill the following requirements:
2252 * 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,
2253 * the block must be exactly 8 blocks behind the submitted MMR used for proof.
2254 * 2) Must prove a chain definition tx and be block 1 or asserts a previous, valid MMR for the notarizing
2255 * chain and properly prove objects using that MMR.
2256 * 3) Must spend the main notarization thread as well as any finalization outputs of either valid or invalid prior
2257 * notarizations, and any unspent notarization contributions for this era. May also spend other inputs.
2259 * a) finalization output of the expected reward amount, which will be sent when finalized
2260 * b) normal output of reward from validated/finalized input if present, 50% to recipient / 50% to block miner less miner fee this tx
2261 * c) main notarization thread output with remaining funds, no other output or fee deduction
2264 bool ValidateAcceptedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2266 // TODO: this validates the spending transaction
2267 // check the following things:
2268 // 1. It represents a valid PoS or merge mined block on the other chain, and contains the header in the opret
2269 // 2. The MMR and proof provided for the currently asserted block can prove the provided header. The provided
2270 // header can prove the last block referenced.
2271 // 3. This notarization is not a superset of an earlier notarization posted before it that it does not
2272 // reference. If that is the case, it is rejected.
2273 // 4. Has all relevant inputs, including finalizes all necessary transactions, both confirmed and orphaned
2274 //printf("ValidateAcceptedNotarization\n");
2278 bool IsAcceptedNotarizationInput(const CScript &scriptSig)
2281 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_ACCEPTEDNOTARIZATION;
2286 * Ensures that a spend in an earned notarization of either an OpRet support transaction or summary notarization
2287 * are valid with respect to this chain. Any transaction that spends from an opret trasaction is either disconnected,
2288 * or contains the correct hashes of each object and transaction data except for the opret, which can be validated by
2289 * reconstructing the opret from the hashes on the other chain and verifying that it hashes to the same input value. This
2290 * enables full validation without copying redundant data back to its original chain.
2292 * In addition, each earned notarization must reference the last earned notarization with which it agrees and prove the last
2293 * accepted notarization on the alternate chain with the latest MMR. The earned notarization will not be accepted if there is
2294 * a later notarization that agrees with it already present in the alternate chain when it is submitted.
2297 bool ValidateEarnedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2299 // this needs to validate that the block is mined or staked, that the notarization is properly formed,
2300 // cryptographically correct, and that it spends the proper finalization outputs
2301 // if the notarization causes a fork, it must include additional proof of blocks and their
2302 // power based on random block hash bits
2303 //printf("ValidateEarnedNotarization\n");
2306 bool IsEarnedNotarizationInput(const CScript &scriptSig)
2308 // this is an output check, and is incorrect. need to change to input
2310 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_EARNEDNOTARIZATION;
2313 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx=nullptr, uint32_t *pHeight=nullptr);
2314 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx, uint32_t *pHeight)
2316 CTransaction _sourceTx;
2317 CTransaction &sourceTx(pSourceTx ? *pSourceTx : _sourceTx);
2319 CObjectFinalization oldFinalization;
2321 if (myGetTransaction(spendingTx.vin[nIn].prevout.hash, sourceTx, blkHash))
2325 auto bIt = mapBlockIndex.find(blkHash);
2326 if (bIt == mapBlockIndex.end() || !bIt->second)
2328 *pHeight = chainActive.Height();
2332 *pHeight = bIt->second->GetHeight();
2336 if (sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2338 (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT) &&
2339 p.version >= COptCCParams::VERSION_V3 &&
2342 oldFinalization = CObjectFinalization(p.vData[0]);
2345 return oldFinalization;
2350 * Ensures that the finalization, either as validated or orphaned, is determined by
2351 * 10 confirmations, either of this transaction, or of an alternate transaction on the chain that we do not derive
2352 * from. If the former, then this should be asserted to be validated, otherwise, it should be asserted to be invalidated.
2355 bool ValidateFinalizeNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2357 // to validate a finalization spend, we need to validate the spender's assertion of confirmation or rejection as proven
2359 // first, determine our notarization finalization protocol
2360 CTransaction sourceTx;
2362 CObjectFinalization oldFinalization = GetOldFinalization(tx, nIn, &sourceTx, &oldHeight);
2363 if (!oldFinalization.IsValid())
2365 return eval->Error("Invalid finalization output");
2368 // get currency to determine system and notarization method
2369 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oldFinalization.currencyID);
2370 if (!curDef.IsValid())
2372 return eval->Error("Invalid currency ID in finalization output");
2374 uint160 SystemID = curDef.GetID();
2376 // for now, auto notarization uses defined notaries. it can be updated later, while leaving those that
2377 // select notary confirm explicitly to remain on that protocol
2378 if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CONFIRM || curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2380 // get the notarization this finalizes and its index output
2381 int32_t notaryOutNum;
2382 CTransaction notarizationTx;
2384 if (oldFinalization.output.IsOnSameTransaction())
2386 notarizationTx = sourceTx;
2387 // output needs non-null hash below
2388 oldFinalization.output.hash = notarizationTx.GetHash();
2393 if (!oldFinalization.GetOutputTransaction(sourceTx, notarizationTx, blkHash))
2395 return eval->Error("notarization-transaction-not-found");
2398 if (notarizationTx.vout.size() <= oldFinalization.output.n)
2400 return eval->Error("invalid-finalization");
2403 CPBaaSNotarization pbn(notarizationTx.vout[oldFinalization.output.n].scriptPubKey);
2406 return eval->Error("invalid-notarization");
2409 // now, we have an unconfirmed, non-rejected finalization being spent by a transaction
2410 // confirm that the spender contains one finalization output either confirming or rejecting
2411 // the finalization. rejection may be implicit by confirming another, later notarization.
2413 // First. make sure the oldFinalization is not referring to an earlier notarization than the
2414 // one most recently confirmed. If so. then it can be spent by anyone.
2415 CChainNotarizationData cnd;
2416 if (!GetNotarizationData(SystemID, cnd) || !cnd.IsConfirmed())
2418 return eval->Error("invalid-notarization");
2421 // TODO: now, validate both rejection and confirmation
2423 CObjectFinalization newFinalization;
2424 int finalizationOutNum = -1;
2425 bool foundFinalization = false;
2426 for (int i = 0; i < tx.vout.size(); i++)
2428 auto &oneOut = tx.vout[i];
2430 // we can accept only one finalization of this notarization as an output, find it and reject more than one
2432 // TODO: HARDENING - ensure that the output of the finalization we are spending is either a confirmed, earlier
2433 // output or invalidated alternate to the one we are finalizing
2434 if (oneOut.scriptPubKey.IsPayToCryptoCondition(p) &&
2436 p.evalCode == EVAL_FINALIZE_NOTARIZATION &&
2438 (newFinalization = CObjectFinalization(p.vData[0])).IsValid())
2440 if (foundFinalization)
2442 return eval->Error("duplicate-finalization");
2444 foundFinalization = true;
2445 finalizationOutNum = i;
2449 if (!foundFinalization)
2451 return eval->Error("invalid-finalization-spend");
2457 bool IsFinalizeNotarizationInput(const CScript &scriptSig)
2459 // this is an output check, and is incorrect. need to change to input
2461 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_FINALIZE_NOTARIZATION;
2464 bool CObjectFinalization::GetOutputTransaction(const CTransaction &initialTx, CTransaction &tx, uint256 &blockHash) const
2466 if (output.hash.IsNull())
2471 else if (myGetTransaction(output.hash, tx, blockHash) && tx.vout.size() > output.n)
2478 // Sign the output object with an ID or signing authority of the ID from the wallet.
2479 CNotaryEvidence CObjectFinalization::SignConfirmed(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
2481 CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
2483 AssertLockHeld(cs_main);
2484 uint32_t nHeight = chainActive.Height();
2488 if (GetOutputTransaction(initialTx, tx, blockHash))
2490 retVal.SignConfirmed(*pWallet, tx, signatureID, nHeight);
2495 CNotaryEvidence CObjectFinalization::SignRejected(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
2497 CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
2499 AssertLockHeld(cs_main);
2500 uint32_t nHeight = chainActive.Height();
2504 if (GetOutputTransaction(initialTx, tx, blockHash))
2506 retVal.SignRejected(*pWallet, tx, signatureID, nHeight);
2511 // Verify that the output object of "p" is signed appropriately with the indicated signature
2512 // and that the signature is fully authorized to sign
2513 // TODO: THIS SHOULD BE UPDATED TO REFLECT USAGE, WHICH IT DOESN'T YET HAVE
2514 // SPECIFICALLY, THE currencyID, WHICH IS USED, SHOULD BE SEPARATED FROM minimum signatures required
2515 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, const COptCCParams &p, uint32_t height) const
2517 std::set<uint160> completedSignatures;
2518 std::set<uint160> partialSignatures;
2520 CCurrencyDefinition curDef;
2524 p.version >= p.VERSION_V3 &&
2526 GetCurrencyDefinition(currencyID, curDef, &defHeight) &&
2529 uint256 txId = output.hash.IsNull() ? initialTx.GetHash() : output.hash;
2530 std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(currencyID, CNotaryEvidence::NotarySignatureKey(), txId, output.n)};
2531 std::vector<uint256> statements;
2533 // check that signature is of the hashed vData[0] data
2534 auto hw = CMMRNode<>::GetHashWriter();
2535 hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
2536 uint256 msgHash = hw.GetHash();
2538 for (auto &authorizedNotary : curDef.notaries)
2540 if (signature.signatures.count(authorizedNotary))
2542 // we might have a partial or complete signature by one notary here
2543 const CIdentitySignature &oneIDSig = signature.signatures.find(authorizedNotary)->second;
2545 uint256 sigHash = oneIDSig.IdentitySignatureHash(vdxfCodes, statements, currencyID, height, authorizedNotary, "", msgHash);
2547 // get identity used to sign
2548 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
2549 if (signer.IsValid())
2551 std::set<uint160> idAddresses;
2552 std::set<uint160> verifiedSignatures;
2554 for (const CTxDestination &oneAddress : signer.primaryAddresses)
2556 if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
2558 // currently, can only check secp256k1 signatures
2559 //return state.Error("Unsupported signature type");
2560 return CIdentitySignature::SIGNATURE_INVALID;
2562 idAddresses.insert(GetDestinationID(oneAddress));
2565 for (auto &oneSig : signature.signatures.find(authorizedNotary)->second.signatures)
2568 pubKey.RecoverCompact(sigHash, oneSig);
2569 if (!idAddresses.count(pubKey.GetID()))
2571 // invalid signature or ID
2572 return CIdentitySignature::SIGNATURE_INVALID;
2574 verifiedSignatures.insert(pubKey.GetID());
2576 if (verifiedSignatures.size() >= signer.minSigs)
2578 completedSignatures.insert(authorizedNotary);
2582 partialSignatures.insert(authorizedNotary);
2587 // invalid signing identity in signature
2588 return CIdentitySignature::SIGNATURE_INVALID;
2592 // all IDs in the signature must have been found and either partial or complete signatures
2593 if (partialSignatures.size() + completedSignatures.size() < signature.signatures.size())
2595 return CIdentitySignature::SIGNATURE_INVALID;
2598 if (completedSignatures.size() >= curDef.minNotariesConfirm)
2600 return CIdentitySignature::SIGNATURE_COMPLETE;
2602 else if (completedSignatures.size() || partialSignatures.size())
2604 return CIdentitySignature::SIGNATURE_PARTIAL;
2607 // missing or invalid
2608 return CIdentitySignature::SIGNATURE_INVALID;
2611 // Verify that the output object is signed with an authorized signing authority
2612 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, uint32_t height) const
2614 // now, get the output to check and check to ensure the signature is good
2618 if (GetOutputTransaction(initialTx, tx, blkHash) &&
2619 tx.vout.size() > output.n &&
2620 tx.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2624 return VerifyOutputSignature(initialTx, signature, p, height);
2628 return CIdentitySignature::SIGNATURE_INVALID;
2632 // this ensures that the signature is, in fact, both authorized to sign, and also a
2633 // valid signature of the specified output object. if so, this is accepted and
2634 // results in a valid index entry as a confirmation of the notary signature
2635 // all signatures must be from a valid notary, or this returns false and should be
2636 // considered invalid.
2637 // returns the number of valid, unique notary signatures, enabling a single output
2638 // to be sufficient to authorize.
2639 bool ValidateNotarizationEvidence(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height, int &confirmedCount, bool &provenFalse)
2641 // we MUST know that the cs_main lock is held. since it can be held on the validation thread while smart transactions
2642 // execute, we cannot take it or assert here
2644 CNotaryEvidence notarySig;
2646 CCurrencyDefinition curDef;
2648 confirmedCount = 0; // if a unit of evidence, whether signature or otherwise, is validated as confirming
2649 provenFalse = false; // if the notarization is proven false
2651 if (tx.vout[outNum].scriptPubKey.IsPayToCryptoCondition(p) &&
2653 p.version >= p.VERSION_V3 &&
2655 (notarySig = CNotaryEvidence(p.vData[0])).IsValid() &&
2656 (curDef = ConnectedChains.GetCachedCurrency(notarySig.systemID)).IsValid())
2658 // now, get the output to check and ensure the signature is good
2659 CObjectFinalization of;
2660 CPBaaSNotarization notarization;
2661 uint256 notarizationTxId;
2664 if (notarySig.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(notarySig.output.hash, nTx, blkHash) &&
2665 nTx.vout.size() > notarySig.output.n &&
2666 nTx.vout[notarySig.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2668 (p.evalCode == EVAL_FINALIZE_NOTARIZATION) &&
2670 (of = CObjectFinalization(p.vData[0])).IsValid() &&
2671 of.IsNotarizationFinalization() &&
2672 of.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(of.output.hash, nTx, blkHash) &&
2673 !(notarizationTxId = nTx.GetHash()).IsNull() &&
2674 nTx.vout.size() > of.output.n &&
2675 nTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2677 (p.evalCode == EVAL_EARNEDNOTARIZATION || p.evalCode == EVAL_ACCEPTEDNOTARIZATION) &&
2679 (notarization = CPBaaSNotarization(p.vData[0])).IsValid() &&
2680 notarization.proofRoots.count(notarySig.systemID))
2682 // signature is relative only to the notarization, not the finalization
2683 // that way, the information we put into the vdxfCodes have some meaning beyond
2684 // the blockchain on which it was signed, and we do not have to carry the
2685 // finalizatoin mechanism cross-chain.
2686 std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(notarySig.systemID,
2687 CNotaryEvidence::NotarySignatureKey(),
2690 std::vector<uint256> statements;
2692 // check that signature is of the hashed vData[0] data
2693 auto hw = CMMRNode<>::GetHashWriter();
2694 hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
2695 uint256 msgHash = hw.GetHash();
2697 for (auto &authorizedNotary : curDef.notaries)
2699 std::map<CIdentityID, CIdentitySignature>::iterator sigIt = notarySig.signatures.find(authorizedNotary);
2700 if (sigIt != notarySig.signatures.end())
2702 // get identity used to sign
2703 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
2704 uint256 sigHash = sigIt->second.IdentitySignatureHash(vdxfCodes, statements, of.currencyID, height, authorizedNotary, "", msgHash);
2706 if (signer.IsValid())
2708 std::set<uint160> idAddresses;
2709 std::set<uint160> verifiedSignatures;
2711 for (const CTxDestination &oneAddress : signer.primaryAddresses)
2713 if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
2715 // currently, can only check secp256k1 signatures
2716 return state.Error("Unsupported signature type");
2718 idAddresses.insert(GetDestinationID(oneAddress));
2721 for (auto &oneSig : notarySig.signatures[authorizedNotary].signatures)
2724 pubKey.RecoverCompact(sigHash, oneSig);
2725 uint160 pkID = pubKey.GetID();
2726 if (!idAddresses.count(pkID))
2728 return state.Error("Mismatched pubkey and ID in signature");
2730 if (verifiedSignatures.count(pkID))
2732 return state.Error("Duplicate key use in ID signature");
2734 verifiedSignatures.insert(pkID);
2736 if (verifiedSignatures.size() >= signer.minSigs)
2742 return state.Error("Insufficient signatures on behalf of ID: " + signer.name);
2747 return state.Error("Invalid notary identity or corrupt local state");
2752 return state.Error("Unauthorized notary");
2758 return state.Error("Invalid notarization reference");
2763 return state.Error("Invalid or non-evidence output");
2766 if (!confirmedCount)
2768 return state.Error("No evidence present");