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"));
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;
96 CIdentitySignature::ESignatureVerification sigResult = idSignature.NewSignature(keyAndIdentity.second,
97 std::vector<uint160>({NotaryConfirmedKey()}),
98 std::vector<uint256>(),
105 if (sigResult != CIdentitySignature::SIGNATURE_INVALID)
107 signatures.insert(std::make_pair(signWithID, idSignature));
112 CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height)
114 if (signatures.size() && confirmed)
116 LogPrintf("%sAttempting to change existing signature from confirmed to rejected\n", __func__);
117 return CIdentitySignature::SIGNATURE_INVALID;
120 std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
121 if (!keyStore.GetIdentity(signWithID, keyAndIdentity, height) && keyAndIdentity.first.CanSign())
123 LogPrintf("%s: Attempting to sign with notary ID that this wallet does not control\n", __func__);
124 return CIdentitySignature::SIGNATURE_INVALID;
129 if (txToConfirm.GetHash() != output.hash ||
130 txToConfirm.vout.size() <= output.n ||
131 !txToConfirm.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) ||
133 !p.vData[0].size() ||
134 p.evalCode == EVAL_NONE)
136 LogPrintf("%s: Attempting to sign an invalid or incompatible object\n", __func__);
137 return CIdentitySignature::SIGNATURE_INVALID;
140 // write the object to the hash writer without a vector length prefix
141 auto hw = CMMRNode<>::GetHashWriter();
142 uint256 objHash = hw.write((const char *)&(p.vData[0][0]), p.vData[0].size()).GetHash();
144 CIdentitySignature idSignature;
146 CIdentitySignature::ESignatureVerification sigResult = idSignature.NewSignature(keyAndIdentity.second,
147 std::vector<uint160>({NotaryRejectedKey()}),
148 std::vector<uint256>(),
155 if (sigResult != CIdentitySignature::SIGNATURE_INVALID)
157 signatures.insert(std::make_pair(signWithID, idSignature));
162 CPBaaSNotarization::CPBaaSNotarization(const CScript &scriptPubKey) :
163 nVersion(VERSION_INVALID),
165 notarizationHeight(0),
169 if (scriptPubKey.IsPayToCryptoCondition(p) &&
171 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
174 ::FromVector(p.vData[0], *this);
178 CPBaaSNotarization::CPBaaSNotarization(const CTransaction &tx, int32_t *pOutIdx) :
179 nVersion(VERSION_INVALID),
181 notarizationHeight(0),
184 // the PBaaS notarization itself is a combination of proper inputs, one output, and
185 // a sequence of opret chain objects as proof of the output values on the chain to which the
186 // notarization refers, the opret can be reconstructed from chain data in order to validate
187 // the txid of a transaction that does not contain the opret itself
190 int32_t &outIdx = pOutIdx ? *pOutIdx : _outIdx;
192 // a notarization must have notarization output that spends to the address indicated by the
193 // ChainID, an opret, that there is only one, and that it can be properly decoded to a notarization
194 // output, whether or not validate is true
196 for (int i = 0; i < tx.vout.size(); i++)
199 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
201 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
206 nVersion = VERSION_INVALID;
214 ::FromVector(p.vData[0], *this);
220 uint160 ValidateCurrencyName(std::string currencyStr, bool ensureCurrencyValid=false, CCurrencyDefinition *pCurrencyDef=NULL);
222 CPBaaSNotarization::CPBaaSNotarization(const UniValue &obj)
224 nVersion = (uint32_t)uni_get_int(find_value(obj, "version"));
226 SetDefinitionNotarization(uni_get_bool(find_value(obj, "isdefinition")));
227 SetBlockOneNotarization(uni_get_bool(find_value(obj, "isblockonenotarization")));
228 SetPreLaunch(uni_get_bool(find_value(obj, "prelaunch")));
229 SetLaunchCleared(uni_get_bool(find_value(obj, "launchclear")));
230 SetRefunding(uni_get_bool(find_value(obj, "refunding")));
231 SetLaunchConfirmed(uni_get_bool(find_value(obj, "launchconfirmed")));
233 currencyID = ValidateCurrencyName(uni_get_str(find_value(obj, "currencyid")));
234 if (currencyID.IsNull())
236 nVersion = VERSION_INVALID;
240 UniValue transferID = find_value(obj, "proposer");
241 if (transferID.isObject())
243 proposer = CTransferDestination(transferID);
246 notarizationHeight = uni_get_int(find_value(obj, "notarizationheight"));
247 currencyState = CCoinbaseCurrencyState(find_value(obj, "currencystate"));
248 prevNotarization = CUTXORef(uint256S(uni_get_str(find_value(obj, "hashprevnotarizationobject"))), (uint32_t)uni_get_int(find_value(obj, "prevnotarizationout")));
249 hashPrevNotarization = uint256S(uni_get_str(find_value(obj, "hashprevnotarizationobject")));
250 prevHeight = uni_get_int(find_value(obj, "prevheight"));
252 auto curStateArr = find_value(obj, "prevheight");
253 auto proofRootArr = find_value(obj, "prevheight");
254 auto nodesUni = find_value(obj, "nodes");
256 if (curStateArr.isArray())
258 for (int i = 0; i < curStateArr.size(); i++)
260 std::vector<std::string> keys = curStateArr[i].getKeys();
261 std::vector<UniValue> values = curStateArr[i].getValues();
262 if (keys.size() != 1 or values.size() != 1)
264 nVersion = VERSION_INVALID;
267 currencyStates.insert(std::make_pair(GetDestinationID(DecodeDestination(keys[0])), CCoinbaseCurrencyState(values[0])));
271 if (proofRootArr.isArray())
273 for (int i = 0; i < proofRootArr.size(); i++)
275 std::vector<std::string> keys = proofRootArr[i].getKeys();
276 std::vector<UniValue> values = proofRootArr[i].getValues();
277 if (keys.size() != 1 or values.size() != 1)
279 nVersion = VERSION_INVALID;
282 proofRoots.insert(std::make_pair(GetDestinationID(DecodeDestination(keys[0])), CProofRoot(values[0])));
286 if (nodesUni.isArray())
288 vector<UniValue> nodeVec = nodesUni.getValues();
289 for (auto node : nodeVec)
291 nodes.push_back(CNodeData(uni_get_str(find_value(node, "networkaddress")), uni_get_str(find_value(node, "nodeidentity"))));
296 CProofRoot CProofRoot::GetProofRoot(uint32_t blockHeight)
298 if (blockHeight > chainActive.Height())
302 auto mmv = chainActive.GetMMV();
303 mmv.resize(blockHeight);
304 return CProofRoot(ASSETCHAINS_CHAINID,
307 chainActive[blockHeight]->GetBlockHash(),
308 chainActive[blockHeight]->chainPower.CompactChainPower());
311 bool CPBaaSNotarization::GetLastNotarization(const uint160 ¤cyID,
318 CPBaaSNotarization notarization;
319 std::vector<CAddressIndexDbEntry> notarizationIndex;
320 // get the last notarization in the indicated height for this currency, which is valid by definition for a token
321 if (GetAddressIndex(CCrossChainRPCData::GetConditionID(currencyID, eCode), CScript::P2IDX, notarizationIndex, startHeight, endHeight))
323 // filter out all transactions that do not spend from the notarization thread, or originate as the
325 for (auto it = notarizationIndex.rbegin(); it != notarizationIndex.rend(); it++)
327 // first unspent notarization that is valid is the one we want, skip spending
328 if (it->first.spending)
335 if (myGetTransaction(it->first.txhash, oneTx, blkHash))
337 if ((notarization = CPBaaSNotarization(oneTx.vout[it->first.index].scriptPubKey)).IsValid())
339 *this = notarization;
342 *txIDOut = it->first.txhash;
353 LogPrintf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
354 printf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
359 return notarization.IsValid();
362 bool CPBaaSNotarization::GetLastUnspentNotarization(const uint160 ¤cyID,
368 CPBaaSNotarization notarization;
369 std::vector<CAddressUnspentDbEntry> notarizationIndex;
370 // get the last notarization in the indicated height for this currency, which is valid by definition for a token
371 if (GetAddressUnspent(CCrossChainRPCData::GetConditionID(currencyID, eCode), CScript::P2IDX, notarizationIndex))
373 // first valid, unspent notarization found is the one we return
374 for (auto it = notarizationIndex.rbegin(); it != notarizationIndex.rend(); it++)
379 if (myGetTransaction(it->first.txhash, oneTx, blkHash))
381 if ((notarization = CPBaaSNotarization(oneTx.vout[it->first.index].scriptPubKey)).IsValid())
383 *this = notarization;
384 txIDOut = it->first.txhash;
385 txOutNum = it->first.index;
395 LogPrintf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
396 printf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
401 return notarization.IsValid();
404 bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceSystem,
405 const CCurrencyDefinition &destCurrency,
406 uint32_t lastExportHeight,
407 uint32_t currentHeight,
408 std::vector<CReserveTransfer> &exportTransfers, // both in and out. this may refund conversions
409 uint256 &transferHash,
410 CPBaaSNotarization &newNotarization,
411 std::vector<CTxOut> &importOutputs,
412 CCurrencyValueMap &importedCurrency,
413 CCurrencyValueMap &gatewayDepositsUsed,
414 CCurrencyValueMap &spentCurrencyOut) const
416 uint160 sourceSystemID = sourceSystem.GetID();
418 newNotarization = *this;
419 newNotarization.prevHeight = newNotarization.notarizationHeight;
420 newNotarization.notarizationHeight = currentHeight;
422 auto hw = CMMRNode<>::GetHashWriter();
424 newNotarization.hashPrevNotarization = hw.GetHash();
426 // if already refunding, numbers don't change
427 if (currencyState.IsRefunding())
432 hw = CMMRNode<>::GetHashWriter();
434 for (int i = 0; i < exportTransfers.size(); i++)
436 CReserveTransfer &reserveTransfer = exportTransfers[i];
438 // add the pre-mutation reserve transfer to the hash
439 hw << reserveTransfer;
441 // ensure that any pre-conversions or conversions are all valid, based on mined height and
442 // maximum pre-conversions
443 if (reserveTransfer.IsPreConversion())
445 if (lastExportHeight >= destCurrency.startBlock)
447 //printf("%s: Invalid pre-conversion, mined after start block\n", __func__);
448 LogPrintf("%s: Invalid pre-conversion, mined after start block\n", __func__);
449 reserveTransfer = reserveTransfer.GetRefundTransfer();
453 // check if it exceeds pre-conversion maximums, and refund if so
454 std::map<uint160, int32_t> currencyMap = destCurrency.GetCurrenciesMap();
455 CCurrencyValueMap fees = reserveTransfer.CalculateFee(reserveTransfer.flags, reserveTransfer.FirstValue());
456 CCurrencyValueMap newReserveIn = CCurrencyValueMap(std::vector<uint160>({reserveTransfer.FirstCurrency()}),
457 std::vector<int64_t>({reserveTransfer.FirstValue() - fees.valueMap[reserveTransfer.FirstCurrency()]}));
458 CCurrencyValueMap newTotalReserves = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserves) + newReserveIn;
459 if (newTotalReserves <= CCurrencyValueMap(destCurrency.currencies, destCurrency.maxPreconvert))
461 newNotarization.currencyState.reserveIn =
462 (CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserveIn) + newReserveIn).AsCurrencyVector(destCurrency.currencies);
463 newNotarization.currencyState.reserves = newTotalReserves.AsCurrencyVector(destCurrency.currencies);
467 LogPrintf("%s: refunding pre-conversion over maximum\n", __func__);
468 reserveTransfer = reserveTransfer.GetRefundTransfer();
472 else if (reserveTransfer.IsConversion())
474 if (lastExportHeight < destCurrency.startBlock)
476 //printf("%s: Invalid conversion, mined before start block\n", __func__);
477 LogPrintf("%s: Invalid conversion, mined before start block\n", __func__);
478 reserveTransfer = reserveTransfer.GetRefundTransfer();
483 transferHash = hw.GetHash();
485 CReserveTransactionDescriptor rtxd;
487 // if this is the clear launch notarization after start, make the notarization and determine if we should launch or refund
488 if (destCurrency.launchSystemID == sourceSystemID && lastExportHeight < (destCurrency.startBlock - 1))
490 // the rest is already updated
493 else if (destCurrency.launchSystemID == sourceSystemID && lastExportHeight == (destCurrency.startBlock - 1))
495 bool refunding = false;
497 // check if the chain is qualified for a refund
498 CCurrencyValueMap minPreMap, fees;
499 CCurrencyValueMap preConvertedMap = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserves).CanonicalMap();
501 if (destCurrency.minPreconvert.size() && destCurrency.minPreconvert.size() == destCurrency.currencies.size())
503 minPreMap = CCurrencyValueMap(destCurrency.currencies, destCurrency.minPreconvert).CanonicalMap();
506 if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
508 // we force the supply to zero
509 // in any case where there was less than minimum participation,
510 // the result of the supply cannot be zero, enabling us to easily determine that this
511 // represents a failed launch
512 newNotarization.currencyState.supply = 0;
513 newNotarization.currencyState.SetRefunding(true);
518 newNotarization.currencyState.SetLaunchClear();
519 newNotarization.currencyState.SetLaunchConfirmed();
521 CCurrencyDefinition destSystem = ConnectedChains.GetCachedCurrency(destCurrency.systemID);
522 return rtxd.AddReserveTransferImportOutputs(sourceSystem,
525 newNotarization.currencyState,
531 &newNotarization.currencyState);
533 else if (lastExportHeight >= destCurrency.startBlock)
535 // calculate new state from processing all transfers
536 // we are not refunding, and it is possible that we also have
537 // normal conversions in addition to pre-conversions. add any conversions that may
538 // be present into the new currency state
539 bool isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem,
540 ConnectedChains.ThisChain(),
548 &newNotarization.currencyState);
549 if (isValidExport && destCurrency.IsFractional())
551 // we want the new price and the old state as a starting point to ensure no rounding error impact
553 importedCurrency = CCurrencyValueMap();
554 gatewayDepositsUsed = CCurrencyValueMap();
555 CCoinbaseCurrencyState tempCurState = currencyState;
556 tempCurState.conversionPrice = newNotarization.currencyState.conversionPrice;
557 tempCurState.viaConversionPrice = newNotarization.currencyState.viaConversionPrice;
558 importOutputs.resize(0);
559 rtxd = CReserveTransactionDescriptor();
560 isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem,
561 ConnectedChains.ThisChain(),
569 &newNotarization.currencyState);
573 LogPrintf("%s: invalid export\n", __func__);
579 // based on the last notarization and existing
583 CObjectFinalization::CObjectFinalization(const CTransaction &tx, uint32_t *pEcode, int32_t *pFinalizationOutNum)
586 uint32_t &ecode = pEcode ? *pEcode : _ecode;
587 int32_t _finalizeOutNum;
588 int32_t &finalizeOutNum = pFinalizationOutNum ? *pFinalizationOutNum : _finalizeOutNum;
590 for (int i = 0; i < tx.vout.size(); i++)
593 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
595 if (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT)
597 if (finalizeOutNum != -1)
599 this->version = VERSION_INVALID;
613 CChainNotarizationData::CChainNotarizationData(UniValue &obj)
615 version = (uint32_t)uni_get_int(find_value(obj, "version"));
616 UniValue vtxUni = find_value(obj, "vtx");
617 if (vtxUni.isArray())
619 vector<UniValue> vvtx = vtxUni.getValues();
622 vtx.push_back(make_pair(uint256S(uni_get_str(find_value(o, "txid"))), CPBaaSNotarization(find_value(o, "notarization"))));
626 lastConfirmed = (uint32_t)uni_get_int(find_value(obj, "lastconfirmed"));
627 UniValue forksUni = find_value(obj, "forks");
628 if (forksUni.isArray())
630 vector<UniValue> forksVec = forksUni.getValues();
631 for (auto fv : forksVec)
635 forks.push_back(vector<int32_t>());
636 vector<UniValue> forkVec = fv.getValues();
637 for (auto fidx : forkVec)
639 forks.back().push_back(uni_get_int(fidx));
645 bestChain = (uint32_t)uni_get_int(find_value(obj, "bestchain"));
648 UniValue CChainNotarizationData::ToUniValue() const
650 UniValue obj(UniValue::VOBJ);
651 obj.push_back(Pair("version", (int32_t)version));
652 UniValue notarizations(UniValue::VARR);
653 for (int64_t i = 0; i < vtx.size(); i++)
655 UniValue notarization(UniValue::VOBJ);
656 notarization.push_back(Pair("index", i));
657 notarization.push_back(Pair("txid", vtx[i].first.hash.GetHex()));
658 notarization.push_back(Pair("vout", (int32_t)vtx[i].first.n));
659 notarization.push_back(Pair("notarization", vtx[i].second.ToUniValue()));
660 notarizations.push_back(notarization);
662 obj.push_back(Pair("notarizations", notarizations));
663 UniValue Forks(UniValue::VARR);
664 for (int32_t i = 0; i < forks.size(); i++)
666 UniValue Fork(UniValue::VARR);
667 for (int32_t j = 0; j < forks[i].size(); j++)
669 Fork.push_back(forks[i][j]);
671 Forks.push_back(Fork);
673 obj.push_back(Pair("forks", Forks));
676 obj.push_back(Pair("lastconfirmedheight", (int32_t)vtx[lastConfirmed].second.notarizationHeight));
678 obj.push_back(Pair("lastconfirmed", lastConfirmed));
679 obj.push_back(Pair("bestchain", bestChain));
683 bool CPBaaSNotarization::CreateAcceptedNotarization(const CCurrencyDefinition &externalSystem,
684 const CPBaaSNotarization &earnedNotarization,
685 const CNotaryEvidence ¬aryEvidence,
686 CValidationState &state,
687 TransactionBuilder &txBuilder)
689 std::string errorPrefix(strprintf("%s: ", __func__));
690 std::set<CIdentityID> notaries;
692 // now, verify the evidence. accepted notarizations for another system must have at least one
693 // valid piece of evidence, which currently means at least one notary signature
694 if (!notaryEvidence.signatures.size())
696 return state.Error(errorPrefix + "insufficient notary evidence required to accept notarization");
698 for (auto &oneSigID : externalSystem.notaries)
700 notaries.insert(oneSigID);
705 // create an accepted notarization based on the cross-chain notarization provided
706 CPBaaSNotarization newNotarization = earnedNotarization;
708 // this should be mirrored for us to continue, if it can't be, it is invalid
709 if (earnedNotarization.IsMirror() || !newNotarization.SetMirror())
711 return state.Error(errorPrefix + "invalid earned notarization");
714 uint160 SystemID = externalSystem.GetID();
715 uint32_t height = chainActive.Height();
716 CProofRoot ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID];
718 CChainNotarizationData cnd;
719 std::vector<std::pair<CTransaction, uint256>> txes;
720 if (!GetNotarizationData(SystemID, cnd, &txes))
722 return state.Error(errorPrefix + "cannot locate notarization history");
725 // any notarization submitted must include a proof root of this chain that is later than the last confirmed
727 if (!cnd.IsConfirmed() ||
728 !cnd.vtx[cnd.lastConfirmed].second.proofRoots.count(ASSETCHAINS_CHAINID) ||
729 ourRoot.rootHeight <= cnd.vtx[cnd.lastConfirmed].second.proofRoots.find(ASSETCHAINS_CHAINID)->second.rootHeight)
731 return state.Error(errorPrefix + "earned notarization proof root is not later than prior confirmed for this chain");
734 auto hw = CMMRNode<>::GetHashWriter();
735 hw << earnedNotarization;
736 uint256 objHash = hw.GetHash();
738 for (auto &oneSig : notaryEvidence.signatures)
740 if (!notaries.count(oneSig.first))
742 return state.Error(errorPrefix + "unauthorized notary signature");
744 CIdentity sigIdentity = CIdentity::LookupIdentity(oneSig.first);
745 if (!sigIdentity.IsValidUnrevoked())
747 return state.Error(errorPrefix + "invalid notary identity");
749 // we currently require accepted notarizations to be completely authorized by notaries
750 if (oneSig.second.CheckSignature(sigIdentity,
751 std::vector<uint160>({notaryEvidence.NotaryConfirmedKey()}),
752 std::vector<uint256>(),
755 objHash) != oneSig.second.SIGNATURE_COMPLETE)
757 return state.Error(errorPrefix + "invalid or incomplete notary signature");
761 auto mmv = chainActive.GetMMV();
762 mmv.resize(ourRoot.rootHeight);
764 // we only create accepted notarizations for notarizations that are earned for this chain on another system
765 // currently, we support ethereum and PBaaS types.
766 if (!newNotarization.proofRoots.count(SystemID) ||
767 !newNotarization.proofRoots.count(ASSETCHAINS_CHAINID) ||
768 !(ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID]).IsValid() ||
769 ourRoot.rootHeight > height ||
770 ourRoot.blockHash != chainActive[ourRoot.rootHeight]->GetBlockHash() ||
771 ourRoot.stateRoot != mmv.GetRoot() ||
772 (ourRoot.type != ourRoot.TYPE_PBAAS && ourRoot.type != ourRoot.TYPE_ETHEREUM))
774 return state.Error(errorPrefix + "can only create accepted notarization from notarization with valid proof root of this chain");
777 // ensure that the data present is valid, as of the height
778 CCoinbaseCurrencyState oldCurState = ConnectedChains.GetCurrencyState(ASSETCHAINS_CHAINID, ourRoot.rootHeight);
779 if (!oldCurState.IsValid() ||
780 ::GetHash(oldCurState) != ::GetHash(earnedNotarization.currencyState))
782 return state.Error(errorPrefix + "currecy state is invalid in accepted notarization. is:\n" +
783 newNotarization.currencyState.ToUniValue().write(1,2) +
785 oldCurState.ToUniValue().write(1,2) + "\n");
788 // ensure that all locally provable info is valid as of our root height
789 // and determine if the new notarization should be already finalized or not
790 for (auto &oneCur : newNotarization.currencyStates)
792 if (oneCur.first == SystemID)
796 else if (oneCur.first != ASSETCHAINS_CHAINID)
798 // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
799 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneCur.first);
800 // we must have all currencies
801 if (!curDef.IsValid())
803 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
805 // if the currency is not from this chain, we cannot validate it
806 if (curDef.systemID != ASSETCHAINS_CHAINID)
810 // ensure that the data present is valid, as of the height
811 oldCurState = ConnectedChains.GetCurrencyState(oneCur.first, ourRoot.rootHeight);
812 if (!oldCurState.IsValid() ||
813 ::GetHash(oldCurState) != ::GetHash(oneCur.second))
815 return state.Error(errorPrefix + "currecy state is invalid in accepted notarization. is:\n" +
816 oneCur.second.ToUniValue().write(1,2) +
818 oldCurState.ToUniValue().write(1,2) + "\n");
823 return state.Error(errorPrefix + "cannot accept redundant currency state in notarization for " + ConnectedChains.ThisChain().name);
826 for (auto &oneRoot : newNotarization.proofRoots)
828 if (oneRoot.first == SystemID)
834 // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
835 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneRoot.first);
836 // we must have all currencies in this notarization registered
837 if (!curDef.IsValid())
839 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
841 uint160 curDefID = curDef.GetID();
843 // only check other currencies on this chain, not the main chain itself
844 if (curDefID != ASSETCHAINS_CHAINID && curDef.systemID == ASSETCHAINS_CHAINID)
846 return state.Error(errorPrefix + "proof roots are not accepted for token currencies");
851 // now create the new notarization, add the proof, finalize if appropriate, and finish
853 // add spend of prior notarization and then outputs
854 CPBaaSNotarization lastUnspentNotarization;
856 int32_t lastTxOutNum;
858 if (!lastUnspentNotarization.GetLastUnspentNotarization(SystemID, EVAL_ACCEPTEDNOTARIZATION, lastTxId, lastTxOutNum, &lastTx))
860 return state.Error(errorPrefix + "invalid prior notarization");
863 // add prior unspent accepted notarization as our input
864 txBuilder.AddTransparentInput(CUTXORef(lastTxId, lastTxOutNum), lastTx.vout[lastTxOutNum].scriptPubKey, lastTx.vout[lastTxOutNum].nValue);
868 std::vector<CTxDestination> dests;
870 // make the earned notarization output
871 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
873 if (externalSystem.notarizationProtocol == externalSystem.NOTARIZATION_NOTARY_CHAINID)
875 dests = std::vector<CTxDestination>({CIdentityID(externalSystem.GetID())});
879 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
882 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &newNotarization)), 0);
884 // now add the notary evidence and finalization that uses it to assert validity
885 // make the earned notarization output
886 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
887 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
888 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, ¬aryEvidence)), CNotaryEvidence::DEFAULT_OUTPUT_VALUE);
890 if (externalSystem.notarizationProtocol != externalSystem.NOTARIZATION_NOTARY_CHAINID)
892 // make the finalization output
893 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
894 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
896 // we need to store the input that we confirmed if we spent finalization outputs
897 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, VERUS_CHAINID, uint256(), txBuilder.mtx.vout.size(), height + 15);
898 if (notaryEvidence.signatures.size() >= externalSystem.notaries.size())
901 of.evidenceOutputs.push_back(txBuilder.mtx.vout.size() - 1);
903 txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of)), 0);
908 // create a notarization that is validated as part of the block, generally benefiting the miner or staker if the
909 // cross notarization is valid
910 bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalSystem,
911 const CTransferDestination &Proposer,
912 CValidationState &state,
913 std::vector<CTxOut> &txOutputs,
914 CPBaaSNotarization ¬arization)
916 std::string errorPrefix(strprintf("%s: ", __func__));
920 const CCurrencyDefinition &systemDef = externalSystem.chainDefinition;
921 SystemID = externalSystem.chainDefinition.GetID();
923 CChainNotarizationData cnd;
924 std::vector<std::pair<CTransaction, uint256>> txes;
927 LOCK2(cs_main, mempool.cs);
928 height = chainActive.Height();
930 // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
931 // we also need to ensure that our notarization would be the first notarization in this notary block period with which we agree.
932 if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
934 // technically not a real error
935 return state.Error("no-notary");
938 if (!GetNotarizationData(SystemID, cnd, &txes))
940 return state.Error(errorPrefix + "no prior notarization found");
944 // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
945 UniValue proofRootsUni(UniValue::VARR);
946 for (auto &oneNot : cnd.vtx)
948 auto rootIt = oneNot.second.proofRoots.find(SystemID);
949 if (rootIt != oneNot.second.proofRoots.end())
951 proofRootsUni.push_back(rootIt->second.ToUniValue());
955 if (!proofRootsUni.size())
957 return state.Error(errorPrefix + "no valid prior state root found");
960 // call notary to determine the prior notarization that we agree with
961 UniValue params(UniValue::VARR);
963 UniValue oneParam(UniValue::VOBJ);
964 oneParam.push_back(Pair("proofroots", proofRootsUni));
965 oneParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
966 params.push_back(oneParam);
968 //printf("%s: about to get cross notarization with %lu notarizations found\n", __func__, cnd.vtx.size());
973 result = find_value(RPCCallRoot("getbestproofroot", params), "result");
974 } catch (exception e)
976 result = NullUniValue;
979 int32_t notaryIdx = uni_get_int(find_value(result, "bestproofrootindex"), -1);
981 if (result.isNull() || notaryIdx == -1)
983 return state.Error(result.isNull() ? "no-notary" : "no-matching-proof-roots-found");
986 // now, we have the index for the transaction and notarization we agree with, a list of those we consider invalid,
987 // and the most recent notarization to use when creating the new one
988 const CTransaction &priorNotarizationTx = txes[notaryIdx].first;
989 uint256 priorBlkHash = txes[notaryIdx].second;
990 const CUTXORef &priorUTXO = cnd.vtx[notaryIdx].first;
991 const CPBaaSNotarization &priorNotarization = cnd.vtx[notaryIdx].second;
993 // find out the block height holding the last notarization we agree with
994 auto mapBlockIt = mapBlockIndex.find(priorBlkHash);
995 if (mapBlockIt == mapBlockIndex.end() || !chainActive.Contains(mapBlockIt->second))
997 return state.Error(errorPrefix + "prior notarization not in blockchain");
1000 // first determine if the prior notarization we agree with would make this one moot
1001 int blockPeriodNumber = (height + 1) / BLOCK_NOTARIZATION_MODULO;
1002 int priorBlockPeriod = mapBlockIt->second->GetHeight() / BLOCK_NOTARIZATION_MODULO;
1004 if (blockPeriodNumber <= priorBlockPeriod)
1006 return state.Error("ineligible");
1009 notarization = priorNotarization;
1010 notarization.proposer = Proposer;
1011 notarization.notarizationHeight = height;
1013 // get the latest notarization information for the new, earned notarization
1014 // one system may provide one proof root and multiple currency states
1015 CProofRoot latestProofRoot = CProofRoot(find_value(result, "latestproofroot"));
1016 if (!latestProofRoot.IsValid())
1018 return state.Error("no-latest-proof-root");
1020 notarization.proofRoots[SystemID] = latestProofRoot;
1022 UniValue currencyStatesUni = find_value(result, "currencystates");
1023 if (!(currencyStatesUni.isArray() && currencyStatesUni.size()))
1025 return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1028 // take the lock again, now that we're back from calling out
1029 LOCK2(cs_main, mempool.cs);
1031 // if height changed, we need to fail and possibly try again
1032 if (height != chainActive.Height())
1034 return state.Error("stale-block");
1037 notarization.currencyStates.clear();
1038 for (int i = 0; i < currencyStatesUni.size(); i++)
1040 CCoinbaseCurrencyState oneCurState(currencyStatesUni[i]);
1041 CCurrencyDefinition oneCurDef;
1042 if (!oneCurState.IsValid())
1044 return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1046 if (!(oneCurDef = ConnectedChains.GetCachedCurrency(oneCurState.GetID())).IsValid())
1048 // if we don't have the currency for the state specified, and it isn't critical, ignore
1049 if (oneCurDef.GetID() == SystemID)
1051 return state.Error(errorPrefix + "system currency invalid - possible corruption");
1055 if (oneCurDef.systemID == SystemID)
1057 uint160 oneCurDefID = oneCurDef.GetID();
1058 if (notarization.currencyID == oneCurDefID)
1060 notarization.currencyState = oneCurState;
1064 notarization.currencyStates[oneCurDefID] = oneCurState;
1069 // add this blockchain's info, based on the requested height
1070 CBlockIndex &curBlkIndex = *chainActive[height];
1071 auto mmv = chainActive.GetMMV();
1072 if (chainActive.Height() != height)
1076 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1077 notarization.proofRoots[thisChainID] = CProofRoot(thisChainID,
1080 curBlkIndex.GetBlockHash(),
1081 curBlkIndex.chainPower.CompactChainPower(),
1082 CProofRoot::TYPE_PBAAS);
1084 // add currency states that we should include and then we're done
1085 // currency states to include are either a gateway currency indicated by the
1086 // gateway or our gateway converter for our PBaaS chain
1087 uint160 gatewayConverterID;
1088 if (systemDef.IsGateway() && !systemDef.gatewayConverterName.empty())
1090 gatewayConverterID = CCurrencyDefinition::GetID(systemDef.gatewayConverterName, thisChainID);
1092 else if (SystemID == ConnectedChains.FirstNotaryChain().chainDefinition.GetID() && !ConnectedChains.ThisChain().gatewayConverterName.empty())
1094 gatewayConverterID = CCurrencyDefinition::GetID(ConnectedChains.ThisChain().gatewayConverterName, thisChainID);
1096 if (!gatewayConverterID.IsNull())
1098 // get the gateway converter currency from the gateway definition
1099 CChainNotarizationData gatewayCND;
1100 if (GetNotarizationData(gatewayConverterID, gatewayCND) && gatewayCND.vtx.size())
1102 notarization.currencyStates[gatewayConverterID] = gatewayCND.vtx[gatewayCND.lastConfirmed].second.currencyState;
1106 notarization.prevNotarization = cnd.vtx[notaryIdx].first;
1107 auto hw = CMMRNode<>::GetHashWriter();
1108 hw << cnd.vtx[notaryIdx].second;
1109 notarization.hashPrevNotarization = hw.GetHash();
1110 notarization.prevHeight = cnd.vtx[notaryIdx].second.notarizationHeight;
1113 CCcontract_info *cp;
1114 std::vector<CTxDestination> dests;
1116 // make the earned notarization output
1117 cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
1119 if (systemDef.notarizationProtocol == systemDef.NOTARIZATION_NOTARY_CHAINID)
1121 dests = std::vector<CTxDestination>({CIdentityID(systemDef.GetID())});
1125 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1128 txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_EARNEDNOTARIZATION, dests, 1, ¬arization))));
1130 if (systemDef.notarizationProtocol != systemDef.NOTARIZATION_NOTARY_CHAINID)
1132 // make the finalization output
1133 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1135 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1137 // we need to store the input that we confirmed if we spent finalization outputs
1138 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, VERUS_CHAINID, uint256(), txOutputs.size(), height + 15);
1139 txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of))));
1144 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentNotaryEvidence() const
1147 std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1148 std::vector<CAddressUnspentDbEntry> indexUnspent;
1149 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1151 uint160 indexKey = CCrossChainRPCData::GetConditionID(currencyID, ObjectFinalizationConfirmedKey());
1152 if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) ||
1153 mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1154 (indexUnspent.size() || mempoolUnspent.size()))
1156 for (auto &oneConfirmed : indexUnspent)
1158 retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1159 CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1161 for (auto &oneUnconfirmed : mempoolUnspent)
1163 auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1164 if (txProxy != mempool.mapTx.end())
1166 auto &mpEntry = *txProxy;
1167 auto &tx = mpEntry.GetTx();
1168 retVal.push_back(std::make_pair(0,
1169 CInputDescriptor(tx.vout[oneUnconfirmed.first.index].scriptPubKey,
1170 tx.vout[oneUnconfirmed.first.index].nValue,
1171 CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1178 // this is called by notaries to locate any notarizations of a specific system that they can notarize, to determine if we
1179 // agree with the notarization ni question, and to confirm or reject the notarization
1180 bool CPBaaSNotarization::ConfirmOrRejectNotarizations(const CWallet *pWallet,
1181 const CRPCChainData &externalSystem,
1182 CValidationState &state,
1183 TransactionBuilder &txBuilder,
1186 std::string errorPrefix(strprintf("%s: ", __func__));
1190 CChainNotarizationData cnd;
1191 std::vector<std::pair<CTransaction, uint256>> txes;
1194 uint160 SystemID = externalSystem.chainDefinition.GetID();
1196 std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> mine;
1198 std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> imsigner, watchonly;
1199 LOCK(pWallet->cs_wallet);
1200 // sign with all IDs under our control that are eligible for this currency
1201 pWallet->GetIdentities(externalSystem.chainDefinition.notaries, mine, imsigner, watchonly);
1204 return state.Error("no-notary");
1209 LOCK2(cs_main, mempool.cs);
1210 height = chainActive.Height();
1212 // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
1213 // we also need to ensure that our notarization would be the first notarization in this notary block period with which we agree.
1214 if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
1216 // technically not a real error
1217 return state.Error("no-notary");
1220 if (!GetNotarizationData(SystemID, cnd, &txes))
1222 return state.Error(errorPrefix + "no prior notarization found");
1226 // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
1227 UniValue proofRootsUni(UniValue::VARR);
1228 for (auto &oneNot : cnd.vtx)
1230 auto rootIt = oneNot.second.proofRoots.find(SystemID);
1231 if (rootIt != oneNot.second.proofRoots.end())
1233 proofRootsUni.push_back(rootIt->second.ToUniValue());
1237 if (!proofRootsUni.size())
1239 return state.Error(errorPrefix + "no valid prior state root found");
1242 UniValue firstParam(UniValue::VOBJ);
1243 firstParam.push_back(Pair("proofroots", proofRootsUni));
1244 firstParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1246 // call notary to determine the notarization that we should notarize
1247 UniValue params(UniValue::VARR);
1248 params.push_back(firstParam);
1250 //printf("%s: about to get cross notarization with %lu notarizations found\n", __func__, cnd.vtx.size());
1255 result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1256 } catch (exception e)
1258 result = NullUniValue;
1261 int32_t notaryIdx = uni_get_int(find_value(result, "bestproofrootindex"), -1);
1263 if (result.isNull() || notaryIdx == -1)
1265 return state.Error(result.isNull() ? "no-notary" : "no-matching-notarization-found");
1268 // take the lock again, now that we're back from calling out
1269 LOCK2(cs_main, mempool.cs);
1271 // if height changed, we need to fail and possibly try again later
1272 if (height != chainActive.Height())
1274 return state.Error("stale-block");
1277 // now, get the list of unconfirmed matches, and sign the latest one that
1279 UniValue proofRootArr = find_value(result, "validproofroots");
1280 if (!proofRootArr.isArray() || !proofRootArr.size())
1282 return state.Error("no-valid-unconfirmed");
1285 // latest height we are eligible to notarize
1286 uint32_t eligibleHeight = height - CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED;
1288 bool retVal = false;
1290 // look from the latest notarization that may qualify
1291 for (int i = proofRootArr.size() - 1; i >= 0; i--)
1293 int idx = uni_get_int(proofRootArr[i]);
1294 if (cnd.vtx[idx].second.notarizationHeight <= eligibleHeight)
1296 // this is the one we will notarize
1297 std::set<CInputDescriptor> myIDSigs;
1299 std::set<CIdentityID> myIDSet;
1300 for (auto &oneID : mine)
1302 myIDSet.insert(oneID.first.idID);
1305 // before signing the one we are about to, we want to ensure that it isn't already signed sufficiently
1306 // if there are enough signatures to confirm it with out signature, make our signature, then create a finalization
1307 CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION + CObjectFinalization::FINALIZE_CONFIRMED,
1309 cnd.vtx[idx].first.hash,
1310 cnd.vtx[idx].first.n,
1313 std::vector<std::pair<uint32_t, CInputDescriptor>> evidenceOuts = of.GetUnspentNotaryEvidence();
1314 std::set<CInputDescriptor> additionalEvidence;
1315 std::set<CInputDescriptor> evidenceToSpend;
1317 std::set<uint160> sigSet;
1319 // if we might have a confirmed notarization, verify, then post
1320 for (auto &oneEvidenceOut : evidenceOuts)
1323 CNotaryEvidence evidence;
1324 if (oneEvidenceOut.second.scriptPubKey.IsPayToCryptoCondition(p) &&
1326 p.evalCode == EVAL_NOTARY_EVIDENCE &&
1328 (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
1329 evidence.IsNotarySignature())
1331 if (CUTXORef(evidence.output.hash.IsNull() ? oneEvidenceOut.second.txIn.prevout.hash : evidence.output.hash, evidence.output.n) == of.output &&
1332 evidence.signatures.size())
1334 bool hasOurSig = false;
1335 for (auto &oneSig : evidence.signatures)
1337 sigSet.insert(oneSig.first);
1338 if (myIDSet.count(oneSig.first))
1341 myIDSet.erase(oneSig.first);
1346 myIDSigs.insert(oneEvidenceOut.second);
1350 additionalEvidence.insert(oneEvidenceOut.second);
1356 evidenceToSpend.insert(oneEvidenceOut.second);
1361 if (evidenceOuts.size() >= (externalSystem.chainDefinition.minNotariesConfirm - 1))
1365 // we've already signed
1366 if (!myIDSet.size())
1368 return state.Error("ineligible");
1372 CCcontract_info *cp;
1373 std::vector<CTxDestination> dests;
1375 // make the earned notarization output
1376 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1377 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1378 CNotaryEvidence ne = CNotaryEvidence(ASSETCHAINS_CHAINID, cnd.vtx[idx].first);
1381 LOCK(pWallet->cs_wallet);
1382 // sign with all IDs under our control that are eligible for this currency
1383 for (auto &oneID : myIDSet)
1385 auto signResult = ne.SignConfirmed(*pWallet, txes[idx].first, oneID, height);
1386 if (signResult == CIdentitySignature::SIGNATURE_PARTIAL || signResult == CIdentitySignature::SIGNATURE_COMPLETE)
1388 sigSet.insert(oneID);
1390 // if our signatures altogether have provided a complete validation, we can early out
1391 if ((ne.signatures.size() + myIDSigs.size()) >= externalSystem.chainDefinition.minNotariesConfirm)
1398 return state.Error(errorPrefix + "invalid identity signature");
1403 if (ne.signatures.size())
1405 CScript evidenceScript = MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, &ne));
1406 myIDSigs.insert(CInputDescriptor(evidenceScript, 0, CTxIn(COutPoint(uint256(), txBuilder.mtx.vout.size()))));
1407 txBuilder.AddTransparentOutput(evidenceScript, CNotaryEvidence::DEFAULT_OUTPUT_VALUE);
1410 // if we have enough to finalize, do so and include all of our signatures allowed
1411 if (sigSet.size() >= externalSystem.chainDefinition.minNotariesConfirm)
1415 // include all of our signatures to improve chances of reward
1416 if (ne.signatures.size())
1418 of.evidenceOutputs.push_back(txBuilder.mtx.vout.size() - 1);
1419 sigCount += ne.signatures.size();
1422 // spend all priors, and if we need more the new signatures, add them to the finalization evidence
1423 // prioritizing our signatures
1424 bool haveNeeded = sigCount >= externalSystem.chainDefinition.minNotariesConfirm;
1425 for (auto &oneEvidenceOut : myIDSigs)
1427 // use up evidence with our ID signatures first, and remove from the remainder
1429 CNotaryEvidence evidence;
1431 oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1432 evidence = CNotaryEvidence(p.vData[0]);
1433 for (auto &oneSig : evidence.signatures)
1435 if (sigSet.count(oneSig.first))
1438 sigSet.erase(oneSig.first);
1441 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1444 // until we have enough signatures to confirm, continue to add evidence to the finalization
1445 of.evidenceInputs.push_back(txBuilder.mtx.vin.size() - 1);
1446 haveNeeded = sigCount >= externalSystem.chainDefinition.minNotariesConfirm;
1449 // if we still need more confirmation, add it
1452 for (auto &oneEvidenceOut : additionalEvidence)
1454 // use up evidence with our ID signatures first, and remove from the remainder
1456 CNotaryEvidence evidence;
1458 oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1459 evidence = CNotaryEvidence(p.vData[0]);
1460 for (auto &oneSig : evidence.signatures)
1462 if (sigSet.count(oneSig.first))
1465 sigSet.erase(oneSig.first);
1468 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1471 // until we have enough signatures to confirm, continue to add evidence to the finalization
1472 of.evidenceInputs.push_back(txBuilder.mtx.vin.size() - 1);
1473 haveNeeded = sigCount >= externalSystem.chainDefinition.minNotariesConfirm;
1480 // should never get here
1481 return state.Error(errorPrefix + "Internal error");
1486 CScript finalizeScript = MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of));
1487 txBuilder.AddTransparentOutput(finalizeScript, 0);
1489 // spend all remaining, unnecessary bits of notary evidence for prior finalizations
1490 for (auto &oneEvidenceOut : evidenceToSpend)
1492 txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1502 * Validates a notarization output spend by ensuring that the spending transaction fulfills all requirements.
1503 * to accept an earned notarization as valid on the Verus blockchain, it must prove a transaction on the alternate chain, which is
1504 * either the original chain definition transaction, which CAN and MUST be proven ONLY in block 1, or the latest notarization transaction
1505 * on the alternate chain that represents an accurate MMR for this chain.
1506 * In addition, any accepted notarization must fullfill the following requirements:
1507 * 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,
1508 * the block must be exactly 8 blocks behind the submitted MMR used for proof.
1509 * 2) Must prove a chain definition tx and be block 1 or asserts a previous, valid MMR for the notarizing
1510 * chain and properly prove objects using that MMR.
1511 * 3) Must spend the main notarization thread as well as any finalization outputs of either valid or invalid prior
1512 * notarizations, and any unspent notarization contributions for this era. May also spend other inputs.
1514 * a) finalization output of the expected reward amount, which will be sent when finalized
1515 * b) normal output of reward from validated/finalized input if present, 50% to recipient / 50% to block miner less miner fee this tx
1516 * c) main notarization thread output with remaining funds, no other output or fee deduction
1519 bool ValidateAcceptedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
1521 // TODO: this validates the spending transaction
1522 // check the following things:
1523 // 1. It represents a valid PoS or merge mined block on the other chain, and contains the header in the opret
1524 // 2. The MMR and proof provided for the currently asserted block can prove the provided header. The provided
1525 // header can prove the last block referenced.
1526 // 3. This notarization is not a superset of an earlier notarization posted before it that it does not
1527 // reference. If that is the case, it is rejected.
1528 // 4. Has all relevant inputs, including finalizes all necessary transactions, both confirmed and orphaned
1529 //printf("ValidateAcceptedNotarization\n");
1533 bool IsAcceptedNotarizationInput(const CScript &scriptSig)
1536 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_ACCEPTEDNOTARIZATION;
1541 * Ensures that a spend in an earned notarization of either an OpRet support transaction or summary notarization
1542 * are valid with respect to this chain. Any transaction that spends from an opret trasaction is either disconnected,
1543 * or contains the correct hashes of each object and transaction data except for the opret, which can be validated by
1544 * reconstructing the opret from the hashes on the other chain and verifying that it hashes to the same input value. This
1545 * enables full validation without copying redundant data back to its original chain.
1547 * In addition, each earned notarization must reference the last earned notarization with which it agrees and prove the last
1548 * accepted notarization on the alternate chain with the latest MMR. The earned notarization will not be accepted if there is
1549 * a later notarization that agrees with it already present in the alternate chain when it is submitted.
1552 bool ValidateEarnedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
1554 // this needs to validate that the block is mined or staked, that the notarization is properly formed,
1555 // cryptographically correct, and that it spends the proper finalization outputs
1556 // if the notarization causes a fork, it must include additional proof of blocks and their
1557 // power based on random block hash bits
1558 //printf("ValidateEarnedNotarization\n");
1561 bool IsEarnedNotarizationInput(const CScript &scriptSig)
1563 // this is an output check, and is incorrect. need to change to input
1565 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_EARNEDNOTARIZATION;
1568 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx=nullptr, uint32_t *pHeight=nullptr);
1569 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx, uint32_t *pHeight)
1571 CTransaction _sourceTx;
1572 CTransaction &sourceTx(pSourceTx ? *pSourceTx : _sourceTx);
1574 CObjectFinalization oldFinalization;
1576 if (myGetTransaction(spendingTx.vin[nIn].prevout.hash, sourceTx, blkHash))
1580 auto bIt = mapBlockIndex.find(blkHash);
1581 if (bIt == mapBlockIndex.end() || !bIt->second)
1583 *pHeight = chainActive.Height();
1587 *pHeight = bIt->second->GetHeight();
1591 if (sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1593 p.evalCode == EVAL_IDENTITY_PRIMARY &&
1594 p.version >= COptCCParams::VERSION_V3 &&
1597 oldFinalization = CObjectFinalization(p.vData[0]);
1600 return oldFinalization;
1605 * Ensures that the finalization, either as validated or orphaned, is determined by
1606 * 10 confirmations, either of this transaction, or of an alternate transaction on the chain that we do not derive
1607 * from. If the former, then this should be asserted to be validated, otherwise, it should be asserted to be invalidated.
1610 bool ValidateFinalizeNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
1612 // to validate a finalization spend, we need to validate the spender's assertion of confirmation or rejection as proven
1615 // first, determine our notarization finalization protocol
1616 CTransaction sourceTx;
1618 CObjectFinalization oldFinalization = GetOldFinalization(tx, nIn, &sourceTx, &oldHeight);
1619 if (!oldFinalization.IsValid())
1621 return eval->Error("Invalid finalization output");
1624 // get currency to determine system and notarization method
1625 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oldFinalization.currencyID);
1626 if (!curDef.IsValid())
1628 return eval->Error("Invalid currency ID in finalization output");
1630 uint160 SystemID = curDef.GetID();
1632 if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
1634 // auto-notarization not yet implemented
1635 if (!PBAAS_TESTMODE)
1637 return eval->Error("auto-notarization");
1640 else if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CONFIRM)
1642 // get the notarization this finalizes and its index output
1643 int32_t notaryOutNum;
1644 CTransaction notarizationTx;
1645 if (oldFinalization.IsConfirmed() || oldFinalization.IsRejected())
1647 return eval->Error("already-finalized");
1650 if (oldFinalization.output.IsOnSameTransaction())
1652 notarizationTx = sourceTx;
1653 // output needs non-null hash below
1654 oldFinalization.output.hash = notarizationTx.GetHash();
1659 if (!oldFinalization.GetOutputTransaction(sourceTx, notarizationTx, blkHash))
1661 return eval->Error("notarization-transaction-not-found");
1664 if (notarizationTx.vout.size() <= oldFinalization.output.n)
1666 return eval->Error("invalid-finalization");
1669 CPBaaSNotarization pbn(notarizationTx.vout[oldFinalization.output.n].scriptPubKey);
1672 return eval->Error("invalid-notarization");
1675 // now, we have an unconfirmed, non-rejected finalization being spent by a transaction
1676 // confirm that the spender contains one fonalization output either confirming or rejecting
1677 // the finalization. rejection may be implicit by confirming another, later notarization.
1679 // First. make sure the oldFinalization is not referring to an earlier notarization than the
1680 // one most recently confirmed. If so. then it can be spent by anyone.
1681 CChainNotarizationData cnd;
1682 if (!GetNotarizationData(SystemID, cnd) || !cnd.IsConfirmed())
1684 return eval->Error("invalid-notarization");
1689 // TODO: now, validate both rejection and confirmation
1693 CObjectFinalization newFinalization;
1694 int finalizationOutNum = -1;
1695 bool foundFinalization = false;
1696 for (int i = 0; i < tx.vout.size(); i++)
1698 auto &oneOut = tx.vout[i];
1700 // we can accept only one finalization of this notarization as an output, find it and reject more than one
1701 if (oneOut.scriptPubKey.IsPayToCryptoCondition(p) &&
1703 p.evalCode == EVAL_FINALIZE_NOTARIZATION &&
1705 (newFinalization = CObjectFinalization(p.vData[0])).IsValid() &&
1706 newFinalization.output == oldFinalization.output)
1708 if (foundFinalization)
1710 return eval->Error("duplicate-finalization");
1712 foundFinalization = true;
1713 finalizationOutNum = i;
1717 if (!foundFinalization)
1719 return eval->Error("invalid-finalization-spend");
1727 bool IsFinalizeNotarizationInput(const CScript &scriptSig)
1729 // this is an output check, and is incorrect. need to change to input
1731 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_FINALIZE_NOTARIZATION;
1734 bool CObjectFinalization::GetOutputTransaction(const CTransaction &initialTx, CTransaction &tx, uint256 &blockHash) const
1736 if (output.hash.IsNull())
1741 else if (myGetTransaction(output.hash, tx, blockHash) && tx.vout.size() > output.n)
1748 // Sign the output object with an ID or signing authority of the ID from the wallet.
1749 CNotaryEvidence CObjectFinalization::SignConfirmed(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
1751 CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
1753 AssertLockHeld(cs_main);
1754 uint32_t nHeight = chainActive.Height();
1758 if (GetOutputTransaction(initialTx, tx, blockHash))
1760 retVal.SignConfirmed(*pWallet, tx, signatureID, nHeight);
1765 CNotaryEvidence CObjectFinalization::SignRejected(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
1767 CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
1769 AssertLockHeld(cs_main);
1770 uint32_t nHeight = chainActive.Height();
1774 if (GetOutputTransaction(initialTx, tx, blockHash))
1776 retVal.SignRejected(*pWallet, tx, signatureID, nHeight);
1781 // Verify that the output object of "p" is signed appropriately with the indicated signature
1782 // and that the signature is fully authorized to sign
1783 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, const COptCCParams &p, uint32_t height) const
1785 std::set<uint160> completedSignatures;
1786 std::set<uint160> partialSignatures;
1788 CCurrencyDefinition curDef;
1792 p.version >= p.VERSION_V3 &&
1794 GetCurrencyDefinition(currencyID, curDef, &defHeight) &&
1797 uint256 txId = output.hash.IsNull() ? initialTx.GetHash() : output.hash;
1798 std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(currencyID, CNotaryEvidence::NotarySignatureKey(), txId, output.n)};
1799 std::vector<uint256> statements;
1801 // check that signature is of the hashed vData[0] data
1802 auto hw = CMMRNode<>::GetHashWriter();
1803 hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
1804 uint256 msgHash = hw.GetHash();
1806 for (auto &authorizedNotary : curDef.notaries)
1808 if (signature.signatures.count(authorizedNotary))
1810 // we might have a partial or complete signature by one notary here
1811 const CIdentitySignature &oneIDSig = signature.signatures.find(authorizedNotary)->second;
1813 uint256 sigHash = oneIDSig.IdentitySignatureHash(vdxfCodes, statements, currencyID, height, authorizedNotary, "", msgHash);
1815 // get identity used to sign
1816 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
1817 if (signer.IsValid())
1819 std::set<uint160> idAddresses;
1820 std::set<uint160> verifiedSignatures;
1822 for (const CTxDestination &oneAddress : signer.primaryAddresses)
1824 if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
1826 // currently, can only check secp256k1 signatures
1827 //return state.Error("Unsupported signature type");
1828 return CIdentitySignature::SIGNATURE_INVALID;
1830 idAddresses.insert(GetDestinationID(oneAddress));
1833 for (auto &oneSig : signature.signatures.find(authorizedNotary)->second.signatures)
1836 pubKey.RecoverCompact(sigHash, oneSig);
1837 if (!idAddresses.count(pubKey.GetID()))
1839 // invalid signature or ID
1840 return CIdentitySignature::SIGNATURE_INVALID;
1842 verifiedSignatures.insert(pubKey.GetID());
1844 if (verifiedSignatures.size() >= signer.minSigs)
1846 completedSignatures.insert(authorizedNotary);
1850 partialSignatures.insert(authorizedNotary);
1855 // invalid signing identity in signature
1856 return CIdentitySignature::SIGNATURE_INVALID;
1860 // all IDs in the signature must have been found and either partial or complete signatures
1861 if (partialSignatures.size() + completedSignatures.size() < signature.signatures.size())
1863 return CIdentitySignature::SIGNATURE_INVALID;
1866 if (completedSignatures.size() >= curDef.minNotariesConfirm)
1868 return CIdentitySignature::SIGNATURE_COMPLETE;
1870 else if (completedSignatures.size() || partialSignatures.size())
1872 return CIdentitySignature::SIGNATURE_PARTIAL;
1875 // missing or invalid
1876 return CIdentitySignature::SIGNATURE_INVALID;
1879 // Verify that the output object is signed with an authorized signing authority
1880 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, uint32_t height) const
1882 // now, get the output to check and check to ensure the signature is good
1886 if (GetOutputTransaction(initialTx, tx, blkHash) &&
1887 tx.vout.size() > output.n &&
1888 tx.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1892 return VerifyOutputSignature(initialTx, signature, p, height);
1896 return CIdentitySignature::SIGNATURE_INVALID;
1900 // this ensures that the signature is, in fact, both authorized to sign, and also a
1901 // valid signature of the specified output object. if so, this is accepted and
1902 // results in a valid index entry as a confirmation of the notary signature
1903 // all signatures must be from a valid notary, or this returns false and should be
1904 // considered invalid.
1905 // returns the number of valid, unique notary signatures, enabling a single output
1906 // to be sufficient to authorize.
1907 bool ValidateNotarizationEvidence(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height, int &confirmedCount, bool &provenFalse)
1909 // we MUST know that the cs_main lock is held. since it can be held on the validation thread while smart transactions
1910 // execute, we cannot take it or assert here
1912 CNotaryEvidence notarySig;
1914 CCurrencyDefinition curDef;
1916 confirmedCount = 0; // if a unit of evidence, whether signature or otherwise, is validated as confirming
1917 provenFalse = false; // if the notarization is proven false
1919 if (tx.vout[outNum].scriptPubKey.IsPayToCryptoCondition(p) &&
1921 p.version >= p.VERSION_V3 &&
1923 (notarySig = CNotaryEvidence(p.vData[0])).IsValid() &&
1924 (curDef = ConnectedChains.GetCachedCurrency(notarySig.systemID)).IsValid())
1926 // now, get the output to check and ensure the signature is good
1927 CObjectFinalization of;
1928 CPBaaSNotarization notarization;
1929 uint256 notarizationTxId;
1932 if (notarySig.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(notarySig.output.hash, nTx, blkHash) &&
1933 nTx.vout.size() > notarySig.output.n &&
1934 nTx.vout[notarySig.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1936 (p.evalCode == EVAL_FINALIZE_NOTARIZATION) &&
1938 (of = CObjectFinalization(p.vData[0])).IsValid() &&
1939 of.IsNotarizationFinalization() &&
1940 of.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(of.output.hash, nTx, blkHash) &&
1941 !(notarizationTxId = nTx.GetHash()).IsNull() &&
1942 nTx.vout.size() > of.output.n &&
1943 nTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1945 (p.evalCode == EVAL_EARNEDNOTARIZATION || p.evalCode == EVAL_ACCEPTEDNOTARIZATION) &&
1947 (notarization = CPBaaSNotarization(p.vData[0])).IsValid() &&
1948 notarization.proofRoots.count(notarySig.systemID))
1950 // signature is relative only to the notarization, not the finalization
1951 // that way, the information we put into the vdxfCodes have some meaning beyond
1952 // the blockchain on which it was signed, and we do not have to carry the
1953 // finalizatoin mechanism cross-chain.
1954 std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(notarySig.systemID,
1955 CNotaryEvidence::NotarySignatureKey(),
1958 std::vector<uint256> statements;
1960 // check that signature is of the hashed vData[0] data
1961 auto hw = CMMRNode<>::GetHashWriter();
1962 hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
1963 uint256 msgHash = hw.GetHash();
1965 for (auto &authorizedNotary : curDef.notaries)
1967 std::map<CIdentityID, CIdentitySignature>::iterator sigIt = notarySig.signatures.find(authorizedNotary);
1968 if (sigIt != notarySig.signatures.end())
1970 // get identity used to sign
1971 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
1972 uint256 sigHash = sigIt->second.IdentitySignatureHash(vdxfCodes, statements, of.currencyID, height, authorizedNotary, "", msgHash);
1974 if (signer.IsValid())
1976 std::set<uint160> idAddresses;
1977 std::set<uint160> verifiedSignatures;
1979 for (const CTxDestination &oneAddress : signer.primaryAddresses)
1981 if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
1983 // currently, can only check secp256k1 signatures
1984 return state.Error("Unsupported signature type");
1986 idAddresses.insert(GetDestinationID(oneAddress));
1989 for (auto &oneSig : notarySig.signatures[authorizedNotary].signatures)
1992 pubKey.RecoverCompact(sigHash, oneSig);
1993 uint160 pkID = pubKey.GetID();
1994 if (!idAddresses.count(pkID))
1996 return state.Error("Mismatched pubkey and ID in signature");
1998 if (verifiedSignatures.count(pkID))
2000 return state.Error("Duplicate key use in ID signature");
2002 verifiedSignatures.insert(pkID);
2004 if (verifiedSignatures.size() >= signer.minSigs)
2010 return state.Error("Insufficient signatures on behalf of ID: " + signer.name);
2015 return state.Error("Invalid notary identity or corrupt local state");
2020 return state.Error("Unauthorized notary");
2026 return state.Error("Invalid notarization reference");
2031 return state.Error("Invalid or non-evidence output");
2034 if (!confirmedCount)
2036 return state.Error("No evidence present");