]> Git Repo - VerusCoin.git/blob - src/pbaas/notarization.cpp
Build fix
[VerusCoin.git] / src / pbaas / notarization.cpp
1 /********************************************************************
2  * (C) 2019 Michael Toutonghi
3  * 
4  * Distributed under the MIT software license, see the accompanying
5  * file COPYING or http://www.opensource.org/licenses/mit-license.php.
6  * 
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 
10  * chain transactions.
11  * 
12  */
13
14 #include <univalue.h>
15 #include "main.h"
16 #include "txdb.h"
17 #include "rpc/pbaasrpc.h"
18 #include "transaction_builder.h"
19
20 #include <assert.h>
21
22 using namespace std;
23
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;
30
31 CNotaryEvidence::CNotaryEvidence(const UniValue &uni)
32 {
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())
41     {
42         auto sigKeys = sigArr.getKeys();
43         auto sigValues = sigArr.getValues();
44         for (int i = 0; i < sigKeys.size(); i++)
45         {
46             CTxDestination destKey = DecodeDestination(sigKeys[i]);
47             if (destKey.which() != COptCCParams::ADDRTYPE_ID)
48             {
49                 version = VERSION_INVALID;
50             }
51             signatures.insert(std::make_pair(CIdentityID(GetDestinationID(destKey)), CIdentitySignature(sigValues[i])));
52         }
53     }
54     if (evidenceArr.isArray())
55     {
56         for (int i = 0; i < evidenceArr.size(); i++)
57         {
58             evidence.push_back(CPartialTransactionProof(evidenceArr[i]));
59         }
60     }
61 }
62
63 CIdentitySignature::ESignatureVerification CNotaryEvidence::SignConfirmed(const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height)
64 {
65     if (signatures.size() && !confirmed)
66     {
67         LogPrintf("%s: Attempting to change existing signature from rejected to confirmed\n", __func__);
68         return CIdentitySignature::SIGNATURE_INVALID;
69     }
70
71     std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
72     if (!keyStore.GetIdentity(signWithID, keyAndIdentity, height) && keyAndIdentity.first.CanSign())
73     {
74         LogPrintf("%s: Attempting to sign with notary ID that this wallet does not control\n", __func__);
75         return CIdentitySignature::SIGNATURE_INVALID;
76     }
77
78     COptCCParams p;
79
80     if (txToConfirm.GetHash() != output.hash ||
81         txToConfirm.vout.size() <= output.n ||
82         !txToConfirm.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) ||
83         !p.vData.size() ||
84         !p.vData[0].size() ||
85         p.evalCode == EVAL_NONE)
86     {
87         LogPrintf("%s: Attempting to sign an invalid or incompatible object\n", __func__);
88         return CIdentitySignature::SIGNATURE_INVALID;
89     }
90
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();
94
95     CIdentitySignature idSignature;
96
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", 
100         __func__,
101         height,
102         EncodeDestination(CIdentityID(systemID)).c_str(),
103         EncodeDestination(CIdentityID(keyAndIdentity.first.idID)).c_str(),
104         NotaryConfirmedKey().GetHex().c_str(),
105         objHash.GetHex().c_str()); */
106
107     CIdentitySignature::ESignatureVerification sigResult = idSignature.NewSignature(keyAndIdentity.second, 
108                                                                                     std::vector<uint160>({NotaryConfirmedKey()}), 
109                                                                                     std::vector<uint256>(), 
110                                                                                     systemID, 
111                                                                                     height, 
112                                                                                     "", 
113                                                                                     objHash, 
114                                                                                     &keyStore);
115
116     if (sigResult != CIdentitySignature::SIGNATURE_INVALID)
117     {
118         signatures.insert(std::make_pair(signWithID, idSignature));
119     }
120     return sigResult;
121 }
122
123 CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height)
124 {
125     if (signatures.size() && confirmed)
126     {
127         LogPrintf("%sAttempting to change existing signature from confirmed to rejected\n", __func__);
128         return CIdentitySignature::SIGNATURE_INVALID;
129     }
130
131     std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
132     if (!keyStore.GetIdentity(signWithID, keyAndIdentity, height) && keyAndIdentity.first.CanSign())
133     {
134         LogPrintf("%s: Attempting to sign with notary ID that this wallet does not control\n", __func__);
135         return CIdentitySignature::SIGNATURE_INVALID;
136     }
137
138     COptCCParams p;
139
140     if (txToConfirm.GetHash() != output.hash ||
141         txToConfirm.vout.size() <= output.n ||
142         !txToConfirm.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) ||
143         !p.vData.size() ||
144         !p.vData[0].size() ||
145         p.evalCode == EVAL_NONE)
146     {
147         LogPrintf("%s: Attempting to sign an invalid or incompatible object\n", __func__);
148         return CIdentitySignature::SIGNATURE_INVALID;
149     }
150
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();
154
155     CIdentitySignature idSignature;
156
157     CIdentitySignature::ESignatureVerification sigResult = idSignature.NewSignature(keyAndIdentity.second, 
158                                 std::vector<uint160>({NotaryRejectedKey()}), 
159                                 std::vector<uint256>(), 
160                                 systemID, 
161                                 height, 
162                                 "", 
163                                 objHash, 
164                                 &keyStore);
165
166     if (sigResult != CIdentitySignature::SIGNATURE_INVALID)
167     {
168         signatures.insert(std::make_pair(signWithID, idSignature));
169     }
170     return sigResult;
171 }
172
173 CPBaaSNotarization::CPBaaSNotarization(const CScript &scriptPubKey) :
174                     nVersion(VERSION_INVALID),
175                     flags(0),
176                     notarizationHeight(0),
177                     prevHeight(0)
178 {
179     COptCCParams p;
180     if (scriptPubKey.IsPayToCryptoCondition(p) && 
181         p.IsValid() &&
182         (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
183         p.vData.size())
184     {
185         ::FromVector(p.vData[0], *this);
186     }
187 }
188
189 CPBaaSNotarization::CPBaaSNotarization(const CTransaction &tx, int32_t *pOutIdx) :
190                     nVersion(VERSION_INVALID),
191                     flags(0),
192                     notarizationHeight(0),
193                     prevHeight(0)
194 {
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
199
200     int32_t _outIdx;
201     int32_t &outIdx = pOutIdx ? *pOutIdx : _outIdx;
202     
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
206     bool found = false;
207     for (int i = 0; i < tx.vout.size(); i++)
208     {
209         COptCCParams p;
210         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && 
211             p.IsValid() &&
212             (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
213             p.vData.size())
214         {
215             if (found)
216             {
217                 nVersion = VERSION_INVALID;
218                 proofRoots.clear();
219                 break;
220             }
221             else
222             {
223                 found = true;
224                 outIdx = i;
225                 ::FromVector(p.vData[0], *this);
226             }
227         }
228     }
229 }
230
231 uint160 ValidateCurrencyName(std::string currencyStr, bool ensureCurrencyValid=false, CCurrencyDefinition *pCurrencyDef=NULL);
232
233 CPBaaSNotarization::CPBaaSNotarization(const UniValue &obj)
234 {
235     nVersion = (uint32_t)uni_get_int64(find_value(obj, "version"));
236     flags = FLAGS_NONE;
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")))
245     {
246         flags |= FLAG_ACCEPTED_MIRROR;
247     }
248     SetSameChain(uni_get_bool(find_value(obj, "samechain")));
249
250     currencyID = ValidateCurrencyName(uni_get_str(find_value(obj, "currencyid")));
251     if (currencyID.IsNull())
252     {
253         nVersion = VERSION_INVALID;
254         return;
255     }
256
257     UniValue transferID = find_value(obj, "proposer");
258     if (transferID.isObject())
259     {
260         proposer = CTransferDestination(transferID);
261     }
262
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"));
268
269     auto curStateArr = find_value(obj, "currencystates");
270     auto proofRootArr = find_value(obj, "proofroots");
271     auto nodesUni = find_value(obj, "nodes");
272
273     if (curStateArr.isArray())
274     {
275         for (int i = 0; i < curStateArr.size(); i++)
276         {
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)
280             {
281                 nVersion = VERSION_INVALID;
282                 return;
283             }
284             currencyStates.insert(std::make_pair(GetDestinationID(DecodeDestination(keys[0])), CCoinbaseCurrencyState(values[0])));
285         }
286     }
287
288     if (proofRootArr.isArray())
289     {
290         for (int i = 0; i < proofRootArr.size(); i++)
291         {
292             CProofRoot oneRoot(proofRootArr[i]);
293             if (!oneRoot.IsValid())
294             {
295                 nVersion = VERSION_INVALID;
296                 return;
297             }
298             proofRoots.insert(std::make_pair(oneRoot.systemID, oneRoot));
299         }
300     }
301
302     if (nodesUni.isArray())
303     {
304         vector<UniValue> nodeVec = nodesUni.getValues();
305         for (auto node : nodeVec)
306         {
307             nodes.push_back(CNodeData(uni_get_str(find_value(node, "networkaddress")), uni_get_str(find_value(node, "nodeidentity"))));
308         }
309     }
310 }
311
312 bool operator==(const CProofRoot &op1, const CProofRoot &op2)
313 {
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;
321 }
322
323 CProofRoot CProofRoot::GetProofRoot(uint32_t blockHeight)
324 {
325     if (blockHeight > chainActive.Height())
326     {
327         return CProofRoot();
328     }
329     auto mmv = chainActive.GetMMV();
330     mmv.resize(blockHeight + 1);
331     return CProofRoot(ASSETCHAINS_CHAINID, 
332                       blockHeight, 
333                       mmv.GetRoot(), 
334                       chainActive[blockHeight]->GetBlockHash(), 
335                       chainActive[blockHeight]->chainPower.CompactChainPower());
336 }
337
338 bool CPBaaSNotarization::GetLastNotarization(const uint160 &currencyID,
339                                              int32_t startHeight,
340                                              int32_t endHeight,
341                                              uint256 *txIDOut,
342                                              CTransaction *txOut)
343 {
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))
348     {
349         // filter out all transactions that do not spend from the notarization thread, or originate as the
350         // chain definition
351         for (auto it = notarizationIndex.rbegin(); it != notarizationIndex.rend(); it++)
352         {
353             // first unspent notarization that is valid is the one we want, skip spending
354             if (it->first.spending)
355             {
356                 continue;
357             }
358             LOCK(mempool.cs);
359             CTransaction oneTx;
360             uint256 blkHash;
361             if (myGetTransaction(it->first.txhash, oneTx, blkHash))
362             {
363                 if ((notarization = CPBaaSNotarization(oneTx.vout[it->first.index].scriptPubKey)).IsValid())
364                 {
365                     *this = notarization;
366                     if (txIDOut)
367                     {
368                         *txIDOut = it->first.txhash;
369                     }
370                     if (txOut)
371                     {
372                         *txOut = oneTx;
373                     }
374                     break;
375                 }
376             }
377             else
378             {
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());
381                 continue;
382             }
383         }
384     }
385     return notarization.IsValid();
386 }
387
388 bool CPBaaSNotarization::GetLastUnspentNotarization(const uint160 &currencyID,
389                                                     uint256 &txIDOut,
390                                                     int32_t &txOutNum,
391                                                     CTransaction *txOut)
392 {
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))
397     {
398         // first valid, unspent notarization found is the one we return
399         for (auto it = notarizationIndex.rbegin(); it != notarizationIndex.rend(); it++)
400         {
401             LOCK(mempool.cs);
402             CTransaction oneTx;
403             uint256 blkHash;
404             if (myGetTransaction(it->first.txhash, oneTx, blkHash))
405             {
406                 if ((notarization = CPBaaSNotarization(oneTx.vout[it->first.index].scriptPubKey)).IsValid())
407                 {
408                     *this = notarization;
409                     txIDOut = it->first.txhash;
410                     txOutNum = it->first.index;
411                     if (txOut)
412                     {
413                         *txOut = oneTx;
414                     }
415                     break;
416                 }
417             }
418             else
419             {
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());
422                 continue;
423             }
424         }
425     }
426     return notarization.IsValid();
427 }
428
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
440 {
441     uint160 sourceSystemID = sourceSystem.GetID();
442
443     newNotarization = *this;
444     newNotarization.SetDefinitionNotarization(false);
445     newNotarization.SetBlockOneNotarization(false);
446     newNotarization.prevNotarization = CUTXORef();
447     newNotarization.prevHeight = newNotarization.notarizationHeight;
448     newNotarization.notarizationHeight = currentHeight;
449
450     auto hw = CMMRNode<>::GetHashWriter();
451     hw << *this;
452     newNotarization.hashPrevNotarization = hw.GetHash();
453
454     hw = CMMRNode<>::GetHashWriter();
455
456     CCurrencyValueMap newPreConversionReservesIn;
457
458     for (int i = 0; i < exportTransfers.size(); i++)
459     {
460         CReserveTransfer &reserveTransfer = exportTransfers[i];
461
462         // add the pre-mutation reserve transfer to the hash
463         hw << reserveTransfer;
464
465         if (currencyState.IsRefunding())
466         {
467             reserveTransfer = reserveTransfer.GetRefundTransfer();
468         }
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())
472         {
473             if (lastExportHeight >= (destCurrency.startBlock - 1))
474             {
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();
478             }
479             else
480             {
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))
486                 {
487                     LogPrintf("%s: refunding pre-conversion over maximum\n", __func__);
488                     reserveTransfer = reserveTransfer.GetRefundTransfer();
489                 }
490                 newPreConversionReservesIn += newReserveIn;
491             }
492         }
493         else if (reserveTransfer.IsConversion())
494         {
495             if (!((destCurrency.systemID != sourceSystemID && newNotarization.IsLaunchCleared()) || 
496                   newNotarization.currencyState.IsLaunchCompleteMarker()))
497             {
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();
501             }
502         }
503     }
504
505     if (exportTransfers.size())
506     {
507         transferHash = hw.GetHash();
508     }
509
510     CReserveTransactionDescriptor rtxd;
511     std::vector<CTxOut> dummyImportOutputs;
512     bool thisIsLaunchSys = destCurrency.launchSystemID == ASSETCHAINS_CHAINID;
513
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)) ||
517          (!thisIsLaunchSys &&
518           destCurrency.systemID == ASSETCHAINS_CHAINID &&
519           currentHeight == 1)))
520     {
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())
524         {
525             // the first block executes the second time through
526             if (newNotarization.IsLaunchCleared())
527             {
528                 newNotarization.SetPreLaunch(false);
529                 newNotarization.currencyState.SetLaunchClear();
530                 newNotarization.currencyState.SetPrelaunch(false);
531                 newNotarization.currencyState.RevertReservesAndSupply();
532             }
533             else
534             {
535                 newNotarization.SetLaunchCleared();
536                 newNotarization.currencyState.SetLaunchClear();
537
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();
542
543                 if (destCurrency.minPreconvert.size() && destCurrency.minPreconvert.size() == destCurrency.currencies.size())
544                 {
545                     minPreMap = CCurrencyValueMap(destCurrency.currencies, destCurrency.minPreconvert).CanonicalMap();
546                 }
547
548                 if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
549                 {
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);
556                 }
557                 else
558                 {
559                     newNotarization.SetLaunchConfirmed();
560                     newNotarization.currencyState.SetLaunchConfirmed();
561                 }
562             }
563         }
564         else if (thisIsLaunchSys && currentHeight < (destCurrency.startBlock - 1))
565         {
566             newNotarization.currencyState.SetPrelaunch();
567         }
568
569         CCurrencyDefinition destSystem = newNotarization.IsRefunding() ? ConnectedChains.GetCachedCurrency(destCurrency.launchSystemID) : 
570                                                                          ConnectedChains.GetCachedCurrency(destCurrency.systemID);
571
572         CCoinbaseCurrencyState tempState = newNotarization.currencyState;
573         if (destCurrency.IsFractional() &&
574             !(newNotarization.IsLaunchCleared() && !newNotarization.currencyState.IsLaunchCompleteMarker()) &&
575             exportTransfers.size())
576         {
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();
581
582         }
583
584         std::vector<CTxOut> tempOutputs;
585         bool retVal = rtxd.AddReserveTransferImportOutputs(sourceSystem,
586                                                            destSystem,
587                                                            destCurrency, 
588                                                            newNotarization.currencyState, 
589                                                            exportTransfers, 
590                                                            tempOutputs, 
591                                                            importedCurrency,
592                                                            gatewayDepositsUsed, 
593                                                            spentCurrencyOut,
594                                                            &tempState);
595
596         if (retVal)
597         {
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,
605                                                            destSystem,
606                                                            destCurrency, 
607                                                            newNotarization.currencyState, 
608                                                            exportTransfers, 
609                                                            importOutputs, 
610                                                            importedCurrency,
611                                                            gatewayDepositsUsed, 
612                                                            spentCurrencyOut,
613                                                            &tempState);
614         }
615         else
616         {
617             return retVal;
618         }
619
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
622         //
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())
628         {
629             tempState.reserveIn = tempState.AddVectors(tempState.reserveIn, this->currencyState.reserveIn);
630         }
631
632         newNotarization.currencyState = tempState;
633         return retVal;
634     }
635     else
636     {
637         if (sourceSystemID != destCurrency.launchSystemID || lastExportHeight >= destCurrency.startBlock)
638         {
639             newNotarization.currencyState.SetLaunchCompleteMarker();
640         }
641         newNotarization.currencyState.SetLaunchClear(false);
642
643         CCurrencyDefinition destSystem;
644
645         if (destCurrency.systemID != ASSETCHAINS_CHAINID)
646         {
647             destSystem = ConnectedChains.GetCachedCurrency(destCurrency.systemID);
648             newNotarization.SetSameChain(false);
649         }
650         else
651         {
652             destSystem = ConnectedChains.ThisChain();
653         }
654
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, 
661                                                                   destSystem,
662                                                                   destCurrency, 
663                                                                   intermediateState, 
664                                                                   exportTransfers, 
665                                                                   dummyImportOutputs, 
666                                                                   importedCurrency,
667                                                                   gatewayDepositsUsed, 
668                                                                   spentCurrencyOut,
669                                                                   &newNotarization.currencyState);
670         if (!newNotarization.currencyState.IsPrelaunch() &&
671             isValidExport &&
672             destCurrency.IsFractional())
673         {
674             // we want the new price and the old state as a starting point to ensure no rounding error impact
675             // on reserves
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, 
683                                                                  destSystem,
684                                                                  destCurrency, 
685                                                                  tempCurState, 
686                                                                  exportTransfers, 
687                                                                  importOutputs, 
688                                                                  importedCurrency,
689                                                                  gatewayDepositsUsed, 
690                                                                  spentCurrencyOut,
691                                                                  &newNotarization.currencyState);
692             if (isValidExport)
693             {
694                 newNotarization.currencyState.conversionPrice = tempCurState.conversionPrice;
695                 newNotarization.currencyState.viaConversionPrice = tempCurState.viaConversionPrice;
696             }
697         }
698         else if (isValidExport)
699         {
700             importOutputs.insert(importOutputs.end(), dummyImportOutputs.begin(), dummyImportOutputs.end());
701         }
702         
703         if (!isValidExport)
704         {
705             LogPrintf("%s: invalid export\n", __func__);
706             return false;
707         }
708         return true;
709     }
710
711     // based on the last notarization and existing
712     return false;
713 }
714
715 CObjectFinalization::CObjectFinalization(const CScript &script) : version(VERSION_INVALID)
716 {
717     COptCCParams p;
718     if (script.IsPayToCryptoCondition(p) && p.IsValid())
719     {
720         if ((p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT) &&
721             p.vData.size())
722         {
723             *this = CObjectFinalization(p.vData[0]);
724         }
725     }
726 }
727
728 CObjectFinalization::CObjectFinalization(const CTransaction &tx, uint32_t *pEcode, int32_t *pFinalizationOutNum)
729 {
730     uint32_t _ecode;
731     uint32_t &ecode = pEcode ? *pEcode : _ecode;
732     int32_t _finalizeOutNum;
733     int32_t &finalizeOutNum = pFinalizationOutNum ? *pFinalizationOutNum : _finalizeOutNum;
734     finalizeOutNum = -1;
735     for (int i = 0; i < tx.vout.size(); i++)
736     {
737         COptCCParams p;
738         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
739         {
740             if (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT)
741             {
742                 if (finalizeOutNum != -1)
743                 {
744                     this->version = VERSION_INVALID;
745                     finalizeOutNum = -1;
746                     break;
747                 }
748                 else
749                 {
750                     finalizeOutNum = i;
751                     ecode = p.evalCode;
752                 }
753             }
754         }
755     }
756 }
757
758 CChainNotarizationData::CChainNotarizationData(UniValue &obj)
759 {
760     version = (uint32_t)uni_get_int(find_value(obj, "version"));
761     UniValue vtxUni = find_value(obj, "notarizations");
762     if (vtxUni.isArray())
763     {
764         vector<UniValue> vvtx = vtxUni.getValues();
765         for (auto o : vvtx)
766         {
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"))));
770         }
771     }
772
773     lastConfirmed = (uint32_t)uni_get_int(find_value(obj, "lastconfirmed"));
774     UniValue forksUni = find_value(obj, "forks");
775     if (forksUni.isArray())
776     {
777         vector<UniValue> forksVec = forksUni.getValues();
778         for (auto fv : forksVec)
779         {
780             if (fv.isArray())
781             {
782                 forks.push_back(vector<int32_t>());
783                 vector<UniValue> forkVec = fv.getValues();
784                 for (auto fidx : forkVec)
785                 {
786                     forks.back().push_back(uni_get_int(fidx));
787                 }
788             }
789         }
790     }
791
792     bestChain = (uint32_t)uni_get_int(find_value(obj, "bestchain"));
793 }
794
795 UniValue CChainNotarizationData::ToUniValue() const
796 {
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++)
801     {
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);
808     }
809     obj.push_back(Pair("notarizations", notarizations));
810     UniValue Forks(UniValue::VARR);
811     for (int32_t i = 0; i < forks.size(); i++)
812     {
813         UniValue Fork(UniValue::VARR);
814         for (int32_t j = 0; j < forks[i].size(); j++)
815         {
816             Fork.push_back(forks[i][j]);
817         }
818         Forks.push_back(Fork);
819     }
820     obj.push_back(Pair("forks", Forks));
821     if (IsConfirmed())
822     {
823         obj.push_back(Pair("lastconfirmedheight", (int32_t)vtx[lastConfirmed].second.notarizationHeight));
824     }
825     obj.push_back(Pair("lastconfirmed", lastConfirmed));
826     obj.push_back(Pair("bestchain", bestChain));
827     return obj;
828 }
829
830 bool CPBaaSNotarization::CreateAcceptedNotarization(const CCurrencyDefinition &externalSystem,
831                                                     const CPBaaSNotarization &earnedNotarization,
832                                                     const CNotaryEvidence &notaryEvidence,
833                                                     CValidationState &state,
834                                                     TransactionBuilder &txBuilder)
835 {
836     std::string errorPrefix(strprintf("%s: ", __func__));
837     std::set<CIdentityID> notaries;
838
839     int minimumNotariesConfirm = (externalSystem.GetID() == ConnectedChains.FirstNotaryChain().GetID()) ?
840                                  minimumNotariesConfirm = ConnectedChains.ThisChain().minNotariesConfirm :
841                                  minimumNotariesConfirm = ConnectedChains.FirstNotaryChain().chainDefinition.minNotariesConfirm;
842
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())
846     {
847         return state.Error(errorPrefix + "insufficient notary evidence required to accept notarization");
848     }
849     for (auto &oneSigID : externalSystem.notaries)
850     {
851         notaries.insert(oneSigID);
852     }
853
854     LOCK(cs_main);
855
856     // create an accepted notarization based on the cross-chain notarization provided
857     CPBaaSNotarization newNotarization = earnedNotarization;
858
859     // this should be mirrored for us to continue, if it can't be, it is invalid
860     if (earnedNotarization.IsMirror() || !newNotarization.SetMirror())
861     {
862         return state.Error(errorPrefix + "invalid earned notarization");
863     }
864
865     uint160 SystemID = externalSystem.GetID();
866     uint32_t height = chainActive.Height();
867     CProofRoot ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID];
868
869     CChainNotarizationData cnd;
870     std::vector<std::pair<CTransaction, uint256>> txes;
871     if (!GetNotarizationData(SystemID, cnd, &txes))
872     {
873         return state.Error(errorPrefix + "cannot locate notarization history");
874     }
875
876     // any notarization submitted must include a proof root of this chain that is later than the last confirmed
877     // notarization
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 :
882                                 UINT32_MAX;
883     if (!cnd.IsConfirmed() || 
884         ourRoot.rootHeight <= lastHeight)
885     {
886         return state.Error(errorPrefix + "earned notarization proof root cannot be verified as later than prior confirmed for this chain");
887     }
888
889     auto hw = CMMRNode<>::GetHashWriter();
890
891     std::vector<unsigned char> notarizationVec = ::AsVector(earnedNotarization);
892     uint256 objHash = hw.write((const char *)&(notarizationVec[0]), notarizationVec.size()).GetHash();
893
894     int sigCount = 0;
895     for (auto &oneSig : notaryEvidence.signatures)
896     {
897         if (!notaries.count(oneSig.first))
898         {
899             return state.Error(errorPrefix + "unauthorized notary signature");
900         }
901         CIdentity sigIdentity = CIdentity::LookupIdentity(oneSig.first);
902         if (!sigIdentity.IsValidUnrevoked())
903         {
904             return state.Error(errorPrefix + "invalid notary identity");
905         }
906
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", 
909             __func__,
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()); */
917
918         if (oneSig.second.CheckSignature(sigIdentity,
919                                          std::vector<uint160>({notaryEvidence.NotaryConfirmedKey()}), 
920                                          std::vector<uint256>(), 
921                                          SystemID, 
922                                          "", 
923                                          objHash) != oneSig.second.SIGNATURE_COMPLETE)
924         {
925             return state.Error(errorPrefix + "invalid or incomplete notary signature");
926         }
927         sigCount++;
928     }
929
930     auto mmv = chainActive.GetMMV();
931     mmv.resize(ourRoot.rootHeight + 1);
932
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))
942     {
943         return state.Error(errorPrefix + "can only create accepted notarization from notarization with valid proof root of this chain");
944     }
945
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))
950     {
951         return state.Error(errorPrefix + "currency state is invalid in accepted notarization. is:\n" + 
952                                             newNotarization.currencyState.ToUniValue().write(1,2) + 
953                                             "\nshould be:\n" + 
954                                             oldCurState.ToUniValue().write(1,2) + "\n");
955     }
956
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)
960     {
961         if (oneCur.first == SystemID)
962         {
963             return state.Error(errorPrefix + "cannot accept redundant currency state in notarization for " + EncodeDestination(CIdentityID(SystemID)));
964         }
965         else if (oneCur.first != ASSETCHAINS_CHAINID)
966         {
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())
971             {
972                 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
973             }
974             // if the currency is not from this chain, we cannot validate it
975             if (curDef.systemID != ASSETCHAINS_CHAINID)
976             {
977                 continue;
978             }
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))
983             {
984                 return state.Error(errorPrefix + "currecy state is invalid in accepted notarization. is:\n" + 
985                                                  oneCur.second.ToUniValue().write(1,2) + 
986                                                  "\nshould be:\n" + 
987                                                  oldCurState.ToUniValue().write(1,2) + "\n");
988             }
989         }
990         else
991         {
992             // already checked above
993             continue;
994         }
995     }
996     for (auto &oneRoot : newNotarization.proofRoots)
997     {
998         if (oneRoot.first == SystemID)
999         {
1000             continue;
1001         }
1002         else
1003         {
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())
1008             {
1009                 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
1010             }
1011             uint160 curDefID = curDef.GetID();
1012
1013             // only check other currencies on this chain, not the main chain itself
1014             if (curDefID != ASSETCHAINS_CHAINID && curDef.systemID == ASSETCHAINS_CHAINID)
1015             {
1016                 return state.Error(errorPrefix + "proof roots are not accepted for token currencies");
1017             }
1018         }
1019     }
1020
1021     // now create the new notarization, add the proof, finalize if appropriate, and finish
1022
1023     // add spend of prior notarization and then outputs
1024     CPBaaSNotarization lastUnspentNotarization;
1025     uint256 lastTxId;
1026     int32_t lastTxOutNum;
1027     CTransaction lastTx;
1028     if (!lastUnspentNotarization.GetLastUnspentNotarization(SystemID, lastTxId, lastTxOutNum, &lastTx))
1029     {
1030         return state.Error(errorPrefix + "invalid prior notarization");
1031     }
1032
1033     // add prior unspent accepted notarization as our input
1034     txBuilder.AddTransparentInput(CUTXORef(lastTxId, lastTxOutNum), lastTx.vout[lastTxOutNum].scriptPubKey, lastTx.vout[lastTxOutNum].nValue);
1035
1036     CCcontract_info CC;
1037     CCcontract_info *cp;
1038     std::vector<CTxDestination> dests;
1039
1040     // make the earned notarization output
1041     cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
1042
1043     if (externalSystem.notarizationProtocol == externalSystem.NOTARIZATION_NOTARY_CHAINID)
1044     {
1045         dests = std::vector<CTxDestination>({CIdentityID(externalSystem.GetID())});
1046     }
1047     else
1048     {
1049         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1050     }
1051
1052     txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &newNotarization)), 0);
1053
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, &notaryEvidence)), 0);
1059
1060     if (externalSystem.notarizationProtocol != externalSystem.NOTARIZATION_NOTARY_CHAINID)
1061     {
1062         // make the finalization output
1063         cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1064         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1065
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)
1069         {
1070             of.SetConfirmed();
1071             of.evidenceOutputs.push_back(txBuilder.mtx.vout.size() - 1);
1072         }
1073         txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of)), 0);
1074     }
1075     return true;
1076 }
1077
1078 extern void CopyNodeStats(std::vector<CNodeStats>& vstats);
1079
1080 std::vector<CNodeData> GetGoodNodes(int maxNum)
1081 {
1082     // good nodes ordered by time connected
1083     std::map<int64_t, CNode *> goodNodes;
1084     std::vector<CNodeData> retVal;
1085
1086     LOCK(cs_vNodes);
1087     for (auto oneNode : vNodes)
1088     {
1089         if (!oneNode->fInbound)
1090         {
1091             if (oneNode->fWhitelisted)
1092             {
1093                 goodNodes.insert(std::make_pair(oneNode->nTimeConnected, oneNode));
1094             }
1095             else if (oneNode->GetTotalBytesRecv() > (1024 * 1024))
1096             {
1097                 goodNodes.insert(std::make_pair(oneNode->nTimeConnected, oneNode));
1098             }
1099         }
1100     }
1101     if (goodNodes.size() > 1)
1102     {
1103         seed_insecure_rand();
1104         for (auto &oneNode : goodNodes)
1105         {
1106             if (insecure_rand() & 1)
1107             {
1108                 retVal.push_back(CNodeData(oneNode.second->addr.ToStringIPPort(), oneNode.second->hashPaymentAddress));
1109                 if (retVal.size() >= maxNum)
1110                 {
1111                     break;
1112                 }
1113             }
1114         }
1115         if (!retVal.size())
1116         {
1117             retVal.push_back(CNodeData(goodNodes.begin()->second->addr.ToStringIPPort(), goodNodes.begin()->second->hashPaymentAddress));
1118         }
1119     }
1120     else if (goodNodes.size())
1121     {
1122         retVal.push_back(CNodeData(goodNodes.begin()->second->addr.ToStringIPPort(), goodNodes.begin()->second->hashPaymentAddress));
1123     }
1124     return retVal;
1125 }
1126
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 &notarization)
1134 {
1135     std::string errorPrefix(strprintf("%s: ", __func__));
1136
1137     uint32_t height;
1138     uint160 SystemID;
1139     const CCurrencyDefinition &systemDef = externalSystem.chainDefinition;
1140     SystemID = externalSystem.chainDefinition.GetID();
1141
1142     CChainNotarizationData cnd;
1143     std::vector<std::pair<CTransaction, uint256>> txes;
1144
1145     {
1146         LOCK2(cs_main, mempool.cs);
1147         height = chainActive.Height();
1148
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())
1152         {
1153             // technically not a real error
1154             return state.Error("no-notary");
1155         }
1156
1157         if (!GetNotarizationData(SystemID, cnd, &txes))
1158         {
1159             return state.Error(errorPrefix + "no prior notarization found");
1160         }
1161     }
1162
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)
1166     {
1167         auto rootIt = oneNot.second.proofRoots.find(SystemID);
1168         if (rootIt != oneNot.second.proofRoots.end())
1169         {
1170             proofRootsUni.push_back(rootIt->second.ToUniValue());
1171         }
1172     }
1173
1174     if (!proofRootsUni.size())
1175     {
1176         return state.Error(errorPrefix + "no valid prior state root found");
1177     }
1178
1179     // call notary to determine the prior notarization that we agree with
1180     UniValue params(UniValue::VARR);
1181
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);
1186  
1187     //printf("%s: about to get cross notarization with %lu notarizations found\n", __func__, cnd.vtx.size());
1188
1189     UniValue result;
1190     try
1191     {
1192         result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1193     } catch (exception e)
1194     {
1195         result = NullUniValue;
1196     }
1197
1198     int32_t notaryIdx = uni_get_int(find_value(result, "bestindex"), -1);
1199
1200     if (result.isNull() || notaryIdx == -1)
1201     {
1202         return state.Error(result.isNull() ? "no-notary" : "no-matching-proof-roots-found");
1203     }
1204
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;
1211
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))
1215     {
1216         return state.Error(errorPrefix + "prior notarization not in blockchain");
1217     }
1218
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;
1222
1223     if (blockPeriodNumber <= priorBlockPeriod)
1224     {
1225         return state.Error("ineligible");
1226     }
1227
1228     notarization = priorNotarization;
1229     notarization.SetBlockOneNotarization(false);
1230     notarization.SetDefinitionNotarization(false);
1231     notarization.proposer = Proposer;
1232     notarization.prevHeight = priorNotarization.notarizationHeight;
1233
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)
1238     {
1239         return state.Error("no-new-latest-proof-root");
1240     }
1241     notarization.proofRoots[latestProofRoot.systemID] = latestProofRoot;
1242     notarization.notarizationHeight = latestProofRoot.rootHeight;
1243
1244     UniValue currencyStatesUni = find_value(result, "currencystates");
1245     if (!(currencyStatesUni.isArray() && currencyStatesUni.size()))
1246     {
1247         return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1248     }
1249
1250     // take the lock again, now that we're back from calling out
1251     LOCK2(cs_main, mempool.cs);
1252
1253     // if height changed, we need to fail and possibly try again
1254     if (height != chainActive.Height())
1255     {
1256         return state.Error("stale-block");
1257     }
1258
1259     notarization.currencyStates.clear();
1260     for (int i = 0; i < currencyStatesUni.size(); i++)
1261     {
1262         CCoinbaseCurrencyState oneCurState(currencyStatesUni[i]);
1263         CCurrencyDefinition oneCurDef;
1264         if (!oneCurState.IsValid())
1265         {
1266             return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1267         }
1268         if (!(oneCurDef = ConnectedChains.GetCachedCurrency(oneCurState.GetID())).IsValid())
1269         {
1270             // if we don't have the currency for the state specified, and it isn't critical, ignore
1271             if (oneCurDef.GetID() == SystemID)
1272             {
1273                 return state.Error(errorPrefix + "system currency invalid - possible corruption");
1274             }
1275             continue;
1276         }
1277         if (oneCurDef.systemID == SystemID)
1278         {
1279             uint160 oneCurDefID = oneCurDef.GetID();
1280             if (notarization.currencyID == oneCurDefID)
1281             {
1282                 notarization.currencyState = oneCurState;
1283             }
1284             else
1285             {
1286                 notarization.currencyStates[oneCurDefID] = oneCurState;
1287             }
1288         }
1289     }
1290     notarization.currencyStates[ASSETCHAINS_CHAINID] = ConnectedChains.GetCurrencyState(height);
1291     if (!systemDef.GatewayConverterID().IsNull())
1292     {
1293         notarization.currencyStates[systemDef.GatewayConverterID()] = ConnectedChains.GetCurrencyState(systemDef.GatewayConverterID(), height);
1294     }
1295
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);
1300
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())
1306     {
1307         gatewayConverterID = CCurrencyDefinition::GetID(systemDef.gatewayConverterName, thisChainID);
1308     }
1309     else if (SystemID == ConnectedChains.FirstNotaryChain().chainDefinition.GetID() && !ConnectedChains.ThisChain().gatewayConverterName.empty())
1310     {
1311         gatewayConverterID = CCurrencyDefinition::GetID(ConnectedChains.ThisChain().gatewayConverterName, thisChainID);
1312     }
1313     if (!gatewayConverterID.IsNull())
1314     {
1315         // get the gateway converter currency from the gateway definition
1316         CChainNotarizationData gatewayCND;
1317         if (GetNotarizationData(gatewayConverterID, gatewayCND) && gatewayCND.vtx.size())
1318         {
1319             notarization.currencyStates[gatewayConverterID] = gatewayCND.vtx[gatewayCND.lastConfirmed].second.currencyState;
1320         }
1321     }
1322
1323     notarization.nodes = GetGoodNodes(CPBaaSNotarization::MAX_NODES);
1324
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;
1330
1331     CCcontract_info CC;
1332     CCcontract_info *cp;
1333     std::vector<CTxDestination> dests;
1334
1335     // make the earned notarization output
1336     cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
1337
1338     if (systemDef.notarizationProtocol == systemDef.NOTARIZATION_NOTARY_CHAINID)
1339     {
1340         dests = std::vector<CTxDestination>({CIdentityID(systemDef.GetID())});
1341     }
1342     else
1343     {
1344         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1345     }
1346
1347     txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_EARNEDNOTARIZATION, dests, 1, &notarization))));
1348
1349     if (systemDef.notarizationProtocol != systemDef.NOTARIZATION_NOTARY_CHAINID)
1350     {
1351         // make the finalization output
1352         cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1353
1354         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1355
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))));
1359     }
1360     return true;
1361 }
1362
1363 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentConfirmedFinalizations(const uint160 &currencyID)
1364 {
1365     LOCK(mempool.cs);
1366     std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1367     std::vector<CAddressUnspentDbEntry> indexUnspent;
1368     std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1369
1370     
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()))
1377     {
1378         /* printf("%s: confirmedNotarizationKey: %s / 0x%s\nconfirmed finalizations\n",
1379             __func__, 
1380             EncodeDestination(CIdentityID(indexKey)).c_str(),
1381             indexKey.GetHex().c_str()); */
1382
1383         for (auto &oneConfirmed : indexUnspent)
1384         {
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))));
1388         }
1389         std::set<std::pair<uint256,int>> mempoolSpent;
1390         for (auto &oneConfirmed : mempoolUnspent)
1391         {
1392             if (oneConfirmed.first.spending)
1393             {
1394                 if (retVal.size() &&
1395                     retVal.back().second.txIn.prevout == COutPoint(oneConfirmed.first.txhash, oneConfirmed.first.index))
1396                 {
1397                     // remove it if spent
1398                     //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index);
1399                     retVal.pop_back();
1400                 }
1401                 mempoolSpent.insert(std::make_pair(oneConfirmed.first.txhash, oneConfirmed.first.index));
1402             }
1403         }
1404         for (auto &oneConfirmed : mempoolUnspent)
1405         {
1406             if (mempoolSpent.count(std::make_pair(oneConfirmed.first.txhash, oneConfirmed.first.index)))
1407             {
1408                 continue;
1409             }
1410
1411             auto txProxy = mempool.mapTx.find(oneConfirmed.first.txhash);
1412             if (txProxy != mempool.mapTx.end())
1413             {
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))));
1421             }
1422         }
1423     }
1424     return retVal;
1425 }
1426
1427 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentPendingFinalizations(const uint160 &currencyID)
1428 {
1429     LOCK(mempool.cs);
1430     std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1431     std::vector<CAddressUnspentDbEntry> indexUnspent;
1432     std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1433
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()))
1440     {
1441         /* printf("%s: pendingNotarizationKey: %s / 0x%s\npending finalizations\n",
1442             __func__, 
1443             EncodeDestination(CIdentityID(indexKey)).c_str(),
1444             indexKey.GetHex().c_str()); */
1445
1446         for (auto &oneConfirmed : indexUnspent)
1447         {
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))));
1451         }
1452         std::set<std::pair<uint256,int>> mempoolSpent;
1453         for (auto &oneUnconfirmed : mempoolUnspent)
1454         {
1455             if (oneUnconfirmed.first.spending)
1456             {
1457                 if (retVal.size() &&
1458                     retVal.back().second.txIn.prevout == COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))
1459                 {
1460                     // remove it if spent
1461                     //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneUnconfirmed.first.txhash.GetHex().c_str(), oneUnconfirmed.first.index);
1462                     retVal.pop_back();
1463                 }
1464                 mempoolSpent.insert(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1465             }
1466         }
1467         for (auto &oneUnconfirmed : mempoolUnspent)
1468         {
1469             if (mempoolSpent.count(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)))
1470             {
1471                 continue;
1472             }
1473
1474             auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1475             if (txProxy != mempool.mapTx.end())
1476             {
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))));
1483             }
1484         }
1485     }
1486     return retVal;
1487 }
1488
1489 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentEvidence(const uint160 &currencyID,
1490                                                                                                 const uint256 &notarizationTxId,
1491                                                                                                 int32_t notarizationOutNum)
1492 {
1493     LOCK(mempool.cs);
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);
1498
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()))
1502     {
1503         /* printf("%s: unspentEvidenceKey: %s / 0x%s\nunspent evidence\n",
1504             __func__, 
1505             EncodeDestination(CIdentityID(indexKey)).c_str(),
1506             indexKey.GetHex().c_str()); */
1507
1508         for (auto &oneConfirmed : indexUnspent)
1509         {
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))));
1513         }
1514         std::set<std::pair<uint256,int>> mempoolSpent;
1515         for (auto &oneUnconfirmed : mempoolUnspent)
1516         {
1517             if (oneUnconfirmed.first.spending)
1518             {
1519                 if (retVal.size() &&
1520                     retVal.back().second.txIn.prevout == COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))
1521                 {
1522                     // remove it if spent
1523                     //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneUnconfirmed.first.txhash.GetHex().c_str(), oneUnconfirmed.first.index);
1524                     retVal.pop_back();
1525                 }
1526                 mempoolSpent.insert(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1527             }
1528         }
1529         for (auto &oneUnconfirmed : mempoolUnspent)
1530         {
1531             if (mempoolSpent.count(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)))
1532             {
1533                 continue;
1534             }
1535
1536             auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1537             if (txProxy != mempool.mapTx.end())
1538             {
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))));
1545             }
1546         }
1547     }
1548     return retVal;
1549 }
1550
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,
1557                                                       bool &finalized)
1558 {
1559     std::string errorPrefix(strprintf("%s: ", __func__));
1560
1561     finalized = false;
1562
1563     CChainNotarizationData cnd;
1564     std::vector<std::pair<CTransaction, uint256>> txes;
1565
1566     uint32_t height;
1567     uint160 SystemID = externalSystem.chainDefinition.GetID();
1568
1569     std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> mine;
1570     {
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);
1575         if (!mine.size())
1576         {
1577             return state.Error("no-notary");
1578         }
1579     }
1580
1581     {
1582         LOCK2(cs_main, mempool.cs);
1583         height = chainActive.Height();
1584
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())
1588         {
1589             // technically not a real error
1590             return state.Error("no-notary");
1591         }
1592
1593         if (!GetNotarizationData(SystemID, cnd, &txes))
1594         {
1595             return state.Error(errorPrefix + "no prior notarization found");
1596         }
1597     }
1598
1599     if (cnd.IsConfirmed() && cnd.vtx.size() == 1)
1600     {
1601         return state.Error("no-unconfirmed");
1602     }
1603
1604     if (height <= (CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED + 1))
1605     {
1606         return state.Error(errorPrefix + "too early");
1607     }
1608
1609     // latest height we are eligible to notarize
1610     uint32_t eligibleHeight = height - CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED;
1611
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)
1615     {
1616         auto rootIt = oneNot.second.proofRoots.find(SystemID);
1617         if (rootIt != oneNot.second.proofRoots.end())
1618         {
1619             proofRootsUni.push_back(rootIt->second.ToUniValue());
1620         }
1621     }
1622
1623     if (!proofRootsUni.size())
1624     {
1625         return state.Error(errorPrefix + "no valid prior state root found");
1626     }
1627
1628     UniValue firstParam(UniValue::VOBJ);
1629     firstParam.push_back(Pair("proofroots", proofRootsUni));
1630     firstParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1631
1632     // call notary to determine the notarization that we should notarize
1633     UniValue params(UniValue::VARR);
1634     params.push_back(firstParam);
1635
1636     //printf("%s: about to getbestproofroot with:\n%s\n", __func__, params.write(1,2).c_str());
1637
1638     UniValue result;
1639     try
1640     {
1641         result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1642     } catch (exception e)
1643     {
1644         result = NullUniValue;
1645     }
1646
1647     int32_t notaryIdx = uni_get_int(find_value(result, "bestindex"), -1);
1648
1649     if (result.isNull() || notaryIdx == -1)
1650     {
1651         return state.Error(result.isNull() ? "no-notary" : "no-matching-notarization-found");
1652     }
1653
1654     // take the lock again, now that we're back from calling out
1655     LOCK2(cs_main, mempool.cs);
1656
1657     // if height changed, we need to fail and possibly try again later
1658     if (height != chainActive.Height())
1659     {
1660         return state.Error("stale-block");
1661     }
1662
1663     // now, get the list of unconfirmed matches, and sign the latest one that
1664     // may be signed
1665     UniValue proofRootArr = find_value(result, "validindexes");
1666     if (!proofRootArr.isArray() || !proofRootArr.size())
1667     {
1668         return state.Error("no-valid-unconfirmed");
1669     }
1670
1671     if (!proofRootArr.isArray() || !proofRootArr.size())
1672     {
1673         return state.Error("no-valid-unconfirmed");
1674     }
1675
1676     bool retVal = false;
1677
1678     // look from the latest notarization that may qualify
1679     for (int i = proofRootArr.size() - 1; i >= 0; i--)
1680     {
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)
1685         {
1686             // if confirmed, we have no more to check
1687             if (cnd.lastConfirmed == idx)
1688             {
1689                 break;
1690             }
1691
1692             // this is the one we will notarize
1693             std::vector<CInputDescriptor> myIDSigs;
1694
1695             std::set<CIdentityID> myIDSet;
1696             for (auto &oneID : mine)
1697             {
1698                 myIDSet.insert(oneID.first.idID);
1699             }
1700
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);
1705
1706             // if we finalize and confirm a notarization, we will spend all prior confirmed and
1707             // pending outputs as part of finalization.
1708
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
1711
1712             // CUTXORef = vtx UTXORef
1713             // pair {
1714             //    int = index into the vtx vector
1715             //    vector of pairs {uint32_t = blockHeight, CInputDescriptor for evidence}
1716             // }
1717             // 
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++)
1720             {
1721                 COptCCParams oneP;
1722                 
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) &&
1725                     oneP.IsValid() &&
1726                     oneP.evalCode == EVAL_EARNEDNOTARIZATION)
1727                 {
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>>())));
1730                 }
1731             }
1732
1733             for (auto &oneConfirmed : unspentConfirmed)
1734             {
1735                 CObjectFinalization oneOf;
1736                 COptCCParams oneP;
1737                 if (oneConfirmed.second.scriptPubKey.IsPayToCryptoCondition(oneP) &&
1738                     oneP.IsValid() &&
1739                     oneP.evalCode != EVAL_EARNEDNOTARIZATION &&
1740                     oneP.evalCode != EVAL_ACCEPTEDNOTARIZATION)
1741                 {
1742                     oneOf = CObjectFinalization(oneConfirmed.second.scriptPubKey);
1743                     if (!oneOf.IsValid())
1744                     {
1745                         printf("%s: invalid index entry at %s : %u\n", 
1746                             __func__, 
1747                             oneConfirmed.second.txIn.prevout.hash.GetHex().c_str(),
1748                             oneConfirmed.second.txIn.prevout.n);
1749                         break;
1750                     }
1751                 }
1752                 else
1753                 {
1754                     continue;
1755                 }
1756
1757                 CUTXORef oneConfirmedTarget = CUTXORef(oneOf.output.hash.IsNull() ? oneConfirmed.second.txIn.prevout.hash : oneOf.output.hash,
1758                                                        oneOf.output.n);
1759
1760                 // if the notarization we have selected to confirm is already confirmed, we are done
1761                 if (vtxFinalizations[oneConfirmedTarget].first == idx)
1762                 {
1763                     return state.Error("notarization-already-confirmed");
1764                 }
1765                 /* printf("%s: adding %s to entry #%d in vtxFinalizations\n", 
1766                     __func__, 
1767                     CUTXORef(oneConfirmed.second.txIn.prevout).ToUniValue().write(1,2).c_str(), 
1768                     vtxFinalizations[oneConfirmedTarget].first); */
1769                 vtxFinalizations[oneConfirmedTarget].second.push_back(oneConfirmed);
1770             }
1771
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;
1777
1778             for (auto &onePending : unspentPending)
1779             {
1780                 CObjectFinalization oneOf;
1781                 COptCCParams oneP;
1782                 if (onePending.second.scriptPubKey.IsPayToCryptoCondition(oneP) &&
1783                     oneP.IsValid())
1784                 {
1785                     if (oneP.evalCode == EVAL_EARNEDNOTARIZATION ||
1786                         oneP.evalCode == EVAL_ACCEPTEDNOTARIZATION)
1787                     {
1788                         continue;
1789                     }
1790                     if (oneP.evalCode == EVAL_FINALIZE_NOTARIZATION && oneP.vData.size())
1791                     {
1792                         oneOf = CObjectFinalization(oneP.vData[0]);
1793                     }
1794                     if (!oneOf.IsValid())
1795                     {
1796                         printf("%s: invalid index entry at %s : %u\n", 
1797                             __func__, 
1798                             onePending.second.txIn.prevout.hash.GetHex().c_str(),
1799                             onePending.second.txIn.prevout.n);
1800                         break;
1801                     }
1802                 }
1803
1804                 CUTXORef onePendingTarget = CUTXORef(oneOf.output.hash.IsNull() ? onePending.second.txIn.prevout.hash : oneOf.output.hash,
1805                                                      oneOf.output.n);
1806                 if (!vtxFinalizations.count(onePendingTarget))
1807                 {
1808                     CTransaction checkTx;
1809                     uint256 blockHash;
1810                     if (myGetTransaction(onePendingTarget.hash, checkTx, blockHash))
1811                     {
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);
1816                     }
1817                     else
1818                     {
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());
1820                     }
1821                     vtxFinalizations[onePendingTarget].first = -1;
1822                 }
1823                 vtxFinalizations[onePendingTarget].second.push_back(onePending);
1824             }
1825
1826             // we need to have a pending finalization to spend to add confirmations
1827             if (!vtxFinalizations[cnd.vtx[idx].first].second.size())
1828             {
1829                 printf("%s: ERROR: pending notarization (%s) with no matching finalization\n", __func__, cnd.vtx[idx].first.ToUniValue().write(1,2).c_str());
1830                 break;
1831             }
1832
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,
1836                                                          SystemID,
1837                                                          cnd.vtx[idx].first.hash,
1838                                                          cnd.vtx[idx].first.n,
1839                                                          eligibleHeight);
1840
1841             std::vector<CInputDescriptor> additionalEvidence;
1842
1843             std::set<uint160> sigSet;
1844
1845             int minimumNotariesConfirm = (externalSystem.GetID() == ConnectedChains.FirstNotaryChain().GetID()) ?
1846                                         minimumNotariesConfirm = ConnectedChains.ThisChain().minNotariesConfirm :
1847                                         minimumNotariesConfirm = ConnectedChains.FirstNotaryChain().chainDefinition.minNotariesConfirm;
1848
1849             // if we might have a confirmed notarization, verify, then post
1850             for (auto &oneEvidenceOut : unspentEvidence)
1851             {
1852                 COptCCParams p;
1853                 CNotaryEvidence evidence;
1854                 if (oneEvidenceOut.second.scriptPubKey.IsPayToCryptoCondition(p) &&
1855                     p.IsValid() &&
1856                     p.evalCode == EVAL_NOTARY_EVIDENCE &&
1857                     p.vData.size() &&
1858                     (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
1859                     evidence.IsNotarySignature())
1860                 {
1861                     if (CUTXORef(evidence.output.hash.IsNull() ? oneEvidenceOut.second.txIn.prevout.hash : evidence.output.hash, evidence.output.n) == of.output &&
1862                         evidence.signatures.size())
1863                     {
1864                         bool hasOurSig = false;
1865                         for (auto &oneSig : evidence.signatures)
1866                         {
1867                             sigSet.insert(oneSig.first);
1868                             if (myIDSet.count(oneSig.first))
1869                             {
1870                                 hasOurSig = true;
1871                                 myIDSet.erase(oneSig.first);
1872                             }
1873                         }
1874                         if (hasOurSig)
1875                         {
1876                             myIDSigs.push_back(oneEvidenceOut.second);
1877                         }
1878                         else
1879                         {
1880                             additionalEvidence.push_back(oneEvidenceOut.second);
1881                         }
1882                     }
1883                     else
1884                     {
1885                         printf("%s: ERROR - unexpected code path, no assert thrown for debugging\n", __func__);
1886                         cleanupSpend.push_back(oneEvidenceOut.second);
1887                     }
1888                 }
1889             }
1890
1891             CNotaryEvidence ne(ASSETCHAINS_CHAINID, cnd.vtx[idx].first);
1892             CCcontract_info CC;
1893             CCcontract_info *cp;
1894             std::vector<CTxDestination> dests;
1895
1896             // if we can still sign, do so before final check of evidence
1897             if (myIDSet.size())
1898             {
1899                 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1900                 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1901
1902                 {
1903                     LOCK(pWallet->cs_wallet);
1904                     // sign with all IDs under our control that are eligible for this currency
1905                     for (auto &oneID : myIDSet)
1906                     {
1907                         auto signResult = ne.SignConfirmed(*pWallet, txes[idx].first, oneID, height);
1908                         if (signResult == CIdentitySignature::SIGNATURE_PARTIAL || signResult == CIdentitySignature::SIGNATURE_COMPLETE)
1909                         {
1910                             sigSet.insert(oneID);
1911                             retVal = true;
1912                             // if our signatures altogether have provided a complete validation, we can early out
1913                             if ((ne.signatures.size() + myIDSigs.size()) >= minimumNotariesConfirm)
1914                             {
1915                                 break;
1916                             }
1917                         }
1918                         else
1919                         {
1920                             return state.Error(errorPrefix + "invalid identity signature");
1921                         }
1922                     }
1923                 }
1924
1925                 if (ne.signatures.size())
1926                 {
1927                     /* for (auto &debugOut : ne.signatures)
1928                     {
1929                         printf("%s: onesig - ID: %s, signature: %s\n", __func__, EncodeDestination(debugOut.first).c_str(), debugOut.second.ToUniValue().write(1,2).c_str());
1930                     } */
1931
1932                     retVal = true;
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);
1937                 }
1938             }
1939
1940             // if we have enough to finalize, do so as a combination of pre-existing evidence and this
1941             if (sigSet.size() >= minimumNotariesConfirm)
1942             {
1943                 int sigCount = 0;
1944
1945                 of.SetConfirmed();
1946
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)
1952                 {
1953                     // use up evidence with our ID signatures first, and remove from the remainder
1954                     COptCCParams p;
1955                     CNotaryEvidence evidence;
1956
1957                     // validated above
1958                     oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1959                     evidence = CNotaryEvidence(p.vData[0]);
1960                     for (auto &oneSig : evidence.signatures)
1961                     {
1962                         if (sigSet.count(oneSig.first))
1963                         {
1964                             sigCount++;
1965                             sigSet.erase(oneSig.first);
1966                         }
1967                     }
1968
1969                     if (!haveNeeded)
1970                     {
1971                         haveNeeded = sigCount >= minimumNotariesConfirm;
1972
1973                         // until we have enough signatures to confirm, continue to add evidence to the finalization
1974                         if (!oneEvidenceOut.txIn.prevout.hash.IsNull())
1975                         {
1976                             of.evidenceInputs.push_back(txBuilder.mtx.vin.size());
1977                             txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1978                         }
1979                     }
1980                 }
1981
1982                 // add additional evidence spends
1983                 for (auto &oneEvidenceOut : additionalEvidence)
1984                 {
1985                     // use up evidence with our ID signatures first, and remove from the remainder
1986                     COptCCParams p;
1987                     CNotaryEvidence evidence;
1988                     // validated above
1989                     oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1990                     evidence = CNotaryEvidence(p.vData[0]);
1991                     for (auto &oneSig : evidence.signatures)
1992                     {
1993                         if (sigSet.count(oneSig.first))
1994                         {
1995                             sigCount++;
1996                             sigSet.erase(oneSig.first);
1997                         }
1998                     }
1999                     txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
2000                     if (!haveNeeded)
2001                     {
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;
2005                     }
2006                 }
2007
2008                 // remove when this vector is confirmed as unnecessary
2009                 for (auto &oneEvidenceOut : cleanupSpend)
2010                 {
2011                     txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
2012                 }
2013
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--)
2019                 {
2020                     for (auto &oneInputDescPair : vtxFinalizations[cnd.vtx[j].first].second)
2021                     {
2022                         txBuilder.AddTransparentInput(oneInputDescPair.second.txIn.prevout, 
2023                                                       oneInputDescPair.second.scriptPubKey,
2024                                                       oneInputDescPair.second.nValue);
2025                     }
2026                 }
2027
2028                 if (!haveNeeded)
2029                 {
2030                     // should never get here
2031                     return state.Error(errorPrefix + "Internal error");
2032                 }
2033
2034                 retVal = true;
2035                 finalized = true;
2036                 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2037                 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2038
2039                 CScript finalizeScript = MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of));
2040                 txBuilder.AddTransparentOutput(finalizeScript, 0);
2041             }
2042             break;
2043         }
2044         if (finalized)
2045         {
2046             break;
2047         }
2048     }
2049     return retVal;
2050 }
2051
2052 bool CallNotary(const CRPCChainData &notarySystem, std::string command, const UniValue &params, UniValue &result, UniValue &error);
2053
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)
2058 {
2059     std::vector<uint256> retVal;
2060
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);
2070
2071     bool confirmedFound = false;
2072
2073     {
2074         LOCK2(cs_main, mempool.cs);
2075
2076         uint32_t nHeight = chainActive.Height();
2077         uint160 finalizeConfirmedKey = 
2078             CCrossChainRPCData::GetConditionID(
2079                 CCrossChainRPCData::GetConditionID(systemID, CObjectFinalization::ObjectFinalizationNotarizationKey()), 
2080                 CObjectFinalization::ObjectFinalizationConfirmedKey());
2081
2082         std::vector<CAddressUnspentDbEntry> unspentFinalizations;
2083         if (GetAddressUnspent(finalizeConfirmedKey, CScript::P2IDX, unspentFinalizations) && unspentFinalizations.size())
2084         {
2085             // get the latest, confirmed notarization
2086             auto bestIt = unspentFinalizations.begin();
2087             for (auto oneIt = bestIt; oneIt != unspentFinalizations.end(); oneIt++)
2088             {
2089                 if (oneIt->second.blockHeight > bestIt->second.blockHeight)
2090                 {
2091                     bestIt = oneIt;
2092                 }
2093             }
2094
2095             COptCCParams p;
2096             if (!bestIt->second.script.IsPayToCryptoCondition(p) || 
2097                 !p.IsValid() ||
2098                 !(p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION || p.evalCode == EVAL_ACCEPTEDNOTARIZATION) ||
2099                 !p.vData.size())
2100             {
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);
2103                 return retVal;
2104             }
2105
2106             // if this is actually a finalization, get the notarization it is for
2107             if (p.evalCode == EVAL_FINALIZE_NOTARIZATION)
2108             {
2109                 confirmedFinalization = CObjectFinalization(p.vData[0]);
2110                 if (confirmedFinalization.output.hash.IsNull())
2111                 {
2112                     confirmedFinalization.output.hash = bestIt->first.txhash;
2113                 }
2114
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)))
2121                 {
2122                     lastConfirmedNotarization = CPBaaSNotarization(nTx.vout[confirmedFinalization.output.n].scriptPubKey);
2123                     if (!lastConfirmedNotarization.IsValid())
2124                     {
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);
2129                         return retVal;
2130                     }
2131                     confirmedFound = true;
2132                 }
2133             }
2134         }
2135
2136         // get all notarization evidence and submit the notarization + evidence
2137         allEvidence.systemID = ASSETCHAINS_CHAINID;
2138         allEvidence.output = CUTXORef(nTx.GetHash(), confirmedFinalization.output.n);
2139
2140         // get all inputs with evidence and add it to our evidence
2141         for (auto &oneEvidenceIn : confirmedFinalization.evidenceInputs)
2142         {
2143             CTransaction evidenceTx;
2144             uint256 eBlkHash;
2145             COptCCParams eP;
2146
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) &&
2151                     eP.IsValid() &&
2152                     eP.evalCode == EVAL_NOTARY_EVIDENCE &&
2153                     eP.vData.size() &&
2154                     (scratchEvidence = CNotaryEvidence(eP.vData[0])).IsValid()))
2155             {
2156                 state.Error("innaccessible-evidence");
2157                 return std::vector<uint256>();
2158             }
2159             allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
2160             for (auto &oneSig : scratchEvidence.signatures)
2161             {
2162                 if (oneSig.second.signatures.size())
2163                 {
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)
2167                     {
2168                         allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
2169                     }
2170                 }
2171             }
2172         }
2173
2174         // get all outputs with evidence and add
2175         for (auto oneEvidenceOut : confirmedFinalization.evidenceOutputs)
2176         {
2177             COptCCParams p;
2178             if (!(finTx.vout.size() > oneEvidenceOut &&
2179                 finTx.vout[oneEvidenceOut].scriptPubKey.IsPayToCryptoCondition(p) &&
2180                 p.IsValid() &&
2181                 (p.evalCode == EVAL_NOTARY_EVIDENCE) &&
2182                 p.vData.size() &&
2183                 (scratchEvidence = CNotaryEvidence(p.vData[0])).IsValid()))
2184             {
2185                 state.Error("invalid-evidence");
2186                 return std::vector<uint256>();
2187             }
2188             allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
2189             for (auto &oneSig : scratchEvidence.signatures)
2190             {
2191                 if (oneSig.second.signatures.size())
2192                 {
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)
2196                     {
2197                         allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
2198                     }
2199                 }
2200             }
2201         }
2202
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())
2206         {
2207             return retVal;
2208         }
2209     }
2210
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());
2218
2219     /* for (auto &debugOut : oneNotarization.second.signatures)
2220     {
2221         printf("%s: onesig - ID: %s, signature: %s\n", __func__, EncodeDestination(debugOut.first).c_str(), debugOut.second.ToUniValue().write(1,2).c_str());
2222     }
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()); */
2233
2234     if (!CallNotary(externalSystem, "submitacceptednotarization", params, result, error) ||
2235         !error.isNull() ||
2236         (strTxId = uni_get_str(result)).empty() ||
2237         !IsHex(strTxId))
2238     {
2239         return retVal;
2240     }
2241     // store the transaction ID and accepted notariation to prevent redundant submits
2242     retVal.push_back(uint256S(strTxId));
2243     return retVal;
2244 }
2245
2246 /*
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.
2258  * 4) Must output:
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
2262  * 
2263  */
2264 bool ValidateAcceptedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2265 {
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");
2275     return true;
2276 }
2277
2278 bool IsAcceptedNotarizationInput(const CScript &scriptSig)
2279 {
2280     uint32_t ecode;
2281     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_ACCEPTEDNOTARIZATION;
2282 }
2283
2284
2285 /*
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.
2291  * 
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. 
2295  * 
2296  */
2297 bool ValidateEarnedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2298 {
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");
2304     return true;
2305 }
2306 bool IsEarnedNotarizationInput(const CScript &scriptSig)
2307 {
2308     // this is an output check, and is incorrect. need to change to input
2309     uint32_t ecode;
2310     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_EARNEDNOTARIZATION;
2311 }
2312
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)
2315 {
2316     CTransaction _sourceTx;
2317     CTransaction &sourceTx(pSourceTx ? *pSourceTx : _sourceTx);
2318
2319     CObjectFinalization oldFinalization;
2320     uint256 blkHash;
2321     if (myGetTransaction(spendingTx.vin[nIn].prevout.hash, sourceTx, blkHash))
2322     {
2323         if (pHeight)
2324         {
2325             auto bIt = mapBlockIndex.find(blkHash);
2326             if (bIt == mapBlockIndex.end() || !bIt->second)
2327             {
2328                 *pHeight = chainActive.Height();
2329             }
2330             else
2331             {
2332                 *pHeight = bIt->second->GetHeight();
2333             }
2334         }
2335         COptCCParams p;
2336         if (sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2337             p.IsValid() && 
2338             (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT) && 
2339             p.version >= COptCCParams::VERSION_V3 &&
2340             p.vData.size() > 1)
2341         {
2342             oldFinalization = CObjectFinalization(p.vData[0]);
2343         }
2344     }
2345     return oldFinalization;
2346 }
2347
2348
2349 /*
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.
2353  *
2354  */
2355 bool ValidateFinalizeNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2356 {
2357     // to validate a finalization spend, we need to validate the spender's assertion of confirmation or rejection as proven
2358
2359     // first, determine our notarization finalization protocol
2360     CTransaction sourceTx;
2361     uint32_t oldHeight;
2362     CObjectFinalization oldFinalization = GetOldFinalization(tx, nIn, &sourceTx, &oldHeight);
2363     if (!oldFinalization.IsValid())
2364     {
2365         return eval->Error("Invalid finalization output");
2366     }
2367
2368     // get currency to determine system and notarization method
2369     CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oldFinalization.currencyID);
2370     if (!curDef.IsValid())
2371     {
2372         return eval->Error("Invalid currency ID in finalization output");
2373     }
2374     uint160 SystemID = curDef.GetID();
2375
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)
2379     {
2380         // get the notarization this finalizes and its index output
2381         int32_t notaryOutNum;
2382         CTransaction notarizationTx;
2383         
2384         if (oldFinalization.output.IsOnSameTransaction())
2385         {
2386             notarizationTx = sourceTx;
2387             // output needs non-null hash below
2388             oldFinalization.output.hash = notarizationTx.GetHash();
2389         }
2390         else
2391         {
2392             uint256 blkHash;
2393             if (!oldFinalization.GetOutputTransaction(sourceTx, notarizationTx, blkHash))
2394             {
2395                 return eval->Error("notarization-transaction-not-found");
2396             }
2397         }
2398         if (notarizationTx.vout.size() <= oldFinalization.output.n)
2399         {
2400             return eval->Error("invalid-finalization");
2401         }
2402
2403         CPBaaSNotarization pbn(notarizationTx.vout[oldFinalization.output.n].scriptPubKey);
2404         if (!pbn.IsValid())
2405         {
2406             return eval->Error("invalid-notarization");
2407         }
2408
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.
2412
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())
2417         {
2418             return eval->Error("invalid-notarization");
2419         }
2420
2421         // TODO: now, validate both rejection and confirmation
2422
2423         CObjectFinalization newFinalization;
2424         int finalizationOutNum = -1;
2425         bool foundFinalization = false;
2426         for (int i = 0; i < tx.vout.size(); i++)
2427         {
2428             auto &oneOut = tx.vout[i];
2429             COptCCParams p;
2430             // we can accept only one finalization of this notarization as an output, find it and reject more than one
2431
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) &&
2435                 p.IsValid() &&
2436                 p.evalCode == EVAL_FINALIZE_NOTARIZATION &&
2437                 p.vData.size() &&
2438                 (newFinalization = CObjectFinalization(p.vData[0])).IsValid())
2439             {
2440                 if (foundFinalization)
2441                 {
2442                     return eval->Error("duplicate-finalization");
2443                 }
2444                 foundFinalization = true;
2445                 finalizationOutNum = i;
2446             }
2447         }
2448
2449         if (!foundFinalization)
2450         {
2451             return eval->Error("invalid-finalization-spend");
2452         }
2453     }
2454     return true;
2455 }
2456
2457 bool IsFinalizeNotarizationInput(const CScript &scriptSig)
2458 {
2459     // this is an output check, and is incorrect. need to change to input
2460     uint32_t ecode;
2461     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_FINALIZE_NOTARIZATION;
2462 }
2463
2464 bool CObjectFinalization::GetOutputTransaction(const CTransaction &initialTx, CTransaction &tx, uint256 &blockHash) const
2465 {
2466     if (output.hash.IsNull())
2467     {
2468         tx = initialTx;
2469         return true;
2470     }
2471     else if (myGetTransaction(output.hash, tx, blockHash) && tx.vout.size() > output.n)
2472     {
2473         return true;
2474     }
2475     return false;
2476 }
2477
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
2480 {
2481     CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
2482
2483     AssertLockHeld(cs_main);
2484     uint32_t nHeight = chainActive.Height();
2485
2486     CTransaction tx;
2487     uint256 blockHash;
2488     if (GetOutputTransaction(initialTx, tx, blockHash))
2489     {
2490         retVal.SignConfirmed(*pWallet, tx, signatureID, nHeight);
2491     }
2492     return retVal;
2493 }
2494
2495 CNotaryEvidence CObjectFinalization::SignRejected(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
2496 {
2497     CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
2498
2499     AssertLockHeld(cs_main);
2500     uint32_t nHeight = chainActive.Height();
2501
2502     CTransaction tx;
2503     uint256 blockHash;
2504     if (GetOutputTransaction(initialTx, tx, blockHash))
2505     {
2506         retVal.SignRejected(*pWallet, tx, signatureID, nHeight);
2507     }
2508     return retVal;
2509 }
2510
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
2516 {
2517     std::set<uint160> completedSignatures;
2518     std::set<uint160> partialSignatures;
2519
2520     CCurrencyDefinition curDef;
2521     int32_t defHeight;
2522
2523     if (p.IsValid() && 
2524         p.version >= p.VERSION_V3 && 
2525         p.vData.size() &&
2526         GetCurrencyDefinition(currencyID, curDef, &defHeight) &&
2527         curDef.IsValid())
2528     {
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;
2532
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();
2537
2538         for (auto &authorizedNotary : curDef.notaries)
2539         {
2540             if (signature.signatures.count(authorizedNotary))
2541             {
2542                 // we might have a partial or complete signature by one notary here
2543                 const CIdentitySignature &oneIDSig = signature.signatures.find(authorizedNotary)->second;
2544
2545                 uint256 sigHash = oneIDSig.IdentitySignatureHash(vdxfCodes, statements, currencyID, height, authorizedNotary, "", msgHash);
2546
2547                 // get identity used to sign
2548                 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
2549                 if (signer.IsValid())
2550                 {
2551                     std::set<uint160> idAddresses;
2552                     std::set<uint160> verifiedSignatures;
2553
2554                     for (const CTxDestination &oneAddress : signer.primaryAddresses)
2555                     {
2556                         if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
2557                         {
2558                             // currently, can only check secp256k1 signatures
2559                             //return state.Error("Unsupported signature type");
2560                             return CIdentitySignature::SIGNATURE_INVALID;
2561                         }
2562                         idAddresses.insert(GetDestinationID(oneAddress));
2563                     }
2564
2565                     for (auto &oneSig : signature.signatures.find(authorizedNotary)->second.signatures)
2566                     {
2567                         CPubKey pubKey;
2568                         pubKey.RecoverCompact(sigHash, oneSig);
2569                         if (!idAddresses.count(pubKey.GetID()))
2570                         {
2571                             // invalid signature or ID
2572                             return CIdentitySignature::SIGNATURE_INVALID;
2573                         }
2574                         verifiedSignatures.insert(pubKey.GetID());
2575                     }
2576                     if (verifiedSignatures.size() >= signer.minSigs)
2577                     {
2578                         completedSignatures.insert(authorizedNotary);
2579                     }
2580                     else
2581                     {
2582                         partialSignatures.insert(authorizedNotary);
2583                     }
2584                 }
2585                 else
2586                 {
2587                     // invalid signing identity in signature
2588                     return CIdentitySignature::SIGNATURE_INVALID;
2589                 }
2590             }
2591         }
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())
2594         {
2595             return CIdentitySignature::SIGNATURE_INVALID;
2596         }
2597
2598         if (completedSignatures.size() >= curDef.minNotariesConfirm)
2599         {
2600             return CIdentitySignature::SIGNATURE_COMPLETE;
2601         }
2602         else if (completedSignatures.size() || partialSignatures.size())
2603         {
2604             return CIdentitySignature::SIGNATURE_PARTIAL;
2605         }
2606     }
2607     // missing or invalid
2608     return CIdentitySignature::SIGNATURE_INVALID;
2609 }
2610
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
2613 {
2614     // now, get the output to check and check to ensure the signature is good
2615     CTransaction tx;
2616     uint256 blkHash;
2617     COptCCParams p;
2618     if (GetOutputTransaction(initialTx, tx, blkHash) &&
2619         tx.vout.size() > output.n &&
2620         tx.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2621         p.IsValid() &&
2622         p.vData.size())
2623     {
2624         return VerifyOutputSignature(initialTx, signature, p, height);
2625     }
2626     else
2627     {
2628         return CIdentitySignature::SIGNATURE_INVALID;
2629     }
2630 }
2631
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)
2640 {
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
2643
2644     CNotaryEvidence notarySig;
2645     COptCCParams p;
2646     CCurrencyDefinition curDef;
2647
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
2650
2651     if (tx.vout[outNum].scriptPubKey.IsPayToCryptoCondition(p) && 
2652         p.IsValid() && 
2653         p.version >= p.VERSION_V3 && 
2654         p.vData.size() && 
2655         (notarySig = CNotaryEvidence(p.vData[0])).IsValid() &&
2656         (curDef = ConnectedChains.GetCachedCurrency(notarySig.systemID)).IsValid())
2657     {
2658         // now, get the output to check and ensure the signature is good
2659         CObjectFinalization of;
2660         CPBaaSNotarization notarization;
2661         uint256 notarizationTxId;
2662         CTransaction nTx;
2663         uint256 blkHash;
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) &&
2667             p.IsValid() &&
2668             (p.evalCode == EVAL_FINALIZE_NOTARIZATION) &&
2669             p.vData.size() &&
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) &&
2676             p.IsValid() &&
2677             (p.evalCode == EVAL_EARNEDNOTARIZATION || p.evalCode == EVAL_ACCEPTEDNOTARIZATION) &&
2678             p.vData.size() &&
2679             (notarization = CPBaaSNotarization(p.vData[0])).IsValid() &&
2680             notarization.proofRoots.count(notarySig.systemID))
2681         {
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(), 
2688                                                                                  notarizationTxId, 
2689                                                                                  of.output.n)};
2690             std::vector<uint256> statements;
2691
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();
2696
2697             for (auto &authorizedNotary : curDef.notaries)
2698             {
2699                 std::map<CIdentityID, CIdentitySignature>::iterator sigIt = notarySig.signatures.find(authorizedNotary);
2700                 if (sigIt != notarySig.signatures.end())
2701                 {
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);
2705
2706                     if (signer.IsValid())
2707                     {
2708                         std::set<uint160> idAddresses;
2709                         std::set<uint160> verifiedSignatures;
2710                         
2711                         for (const CTxDestination &oneAddress : signer.primaryAddresses)
2712                         {
2713                             if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
2714                             {
2715                                 // currently, can only check secp256k1 signatures
2716                                 return state.Error("Unsupported signature type");
2717                             }
2718                             idAddresses.insert(GetDestinationID(oneAddress));
2719                         }
2720
2721                         for (auto &oneSig : notarySig.signatures[authorizedNotary].signatures)
2722                         {
2723                             CPubKey pubKey;
2724                             pubKey.RecoverCompact(sigHash, oneSig);
2725                             uint160 pkID = pubKey.GetID();
2726                             if (!idAddresses.count(pkID))
2727                             {
2728                                 return state.Error("Mismatched pubkey and ID in signature");
2729                             }
2730                             if (verifiedSignatures.count(pkID))
2731                             {
2732                                 return state.Error("Duplicate key use in ID signature");
2733                             }
2734                             verifiedSignatures.insert(pkID);
2735                         }
2736                         if (verifiedSignatures.size() >= signer.minSigs)
2737                         {
2738                             confirmedCount++;
2739                         }
2740                         else
2741                         {
2742                             return state.Error("Insufficient signatures on behalf of ID: " + signer.name);
2743                         }
2744                     }
2745                     else
2746                     {
2747                         return state.Error("Invalid notary identity or corrupt local state");
2748                     }
2749                 }
2750                 else
2751                 {
2752                     return state.Error("Unauthorized notary");
2753                 }
2754             }
2755         }
2756         else
2757         {
2758             return state.Error("Invalid notarization reference");
2759         }
2760     }
2761     else
2762     {
2763         return state.Error("Invalid or non-evidence output");
2764     }
2765     
2766     if (!confirmedCount)
2767     {
2768         return state.Error("No evidence present");
2769     }
2770     else
2771     {
2772         return true;
2773     }
2774 }
2775
This page took 0.180311 seconds and 4 git commands to generate.