]> Git Repo - VerusCoin.git/blob - src/pbaas/notarization.cpp
Notarization 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() &&
575             !newNotarization.currencyState.IsLaunchCompleteMarker()) &&
576             exportTransfers.size())
577         {
578             // normalize prices on the way in to prevent overflows on first pass
579             std::vector<int64_t> newReservesVector = newPreConversionReservesIn.AsCurrencyVector(tempState.currencies);
580             tempState.reserves = tempState.AddVectors(tempState.reserves, newReservesVector);
581             newNotarization.currencyState.conversionPrice = tempState.PricesInReserve();
582             tempState.reserves = tempState.AddVectors(tempState.reserves, (newPreConversionReservesIn * -1).AsCurrencyVector(tempState.currencies));
583         }
584
585         std::vector<CTxOut> tempOutputs;
586         bool retVal = rtxd.AddReserveTransferImportOutputs(sourceSystem,
587                                                            destSystem,
588                                                            destCurrency, 
589                                                            newNotarization.currencyState, 
590                                                            exportTransfers, 
591                                                            tempOutputs, 
592                                                            importedCurrency,
593                                                            gatewayDepositsUsed, 
594                                                            spentCurrencyOut,
595                                                            &tempState);
596
597         if (retVal)
598         {
599             importedCurrency.valueMap.clear();
600             gatewayDepositsUsed.valueMap.clear();
601             spentCurrencyOut.valueMap.clear();
602             newNotarization.currencyState.conversionPrice = tempState.conversionPrice;
603             newNotarization.currencyState.viaConversionPrice = tempState.viaConversionPrice;
604             rtxd = CReserveTransactionDescriptor();
605             retVal = rtxd.AddReserveTransferImportOutputs(sourceSystem,
606                                                            destSystem,
607                                                            destCurrency, 
608                                                            newNotarization.currencyState, 
609                                                            exportTransfers, 
610                                                            importOutputs, 
611                                                            importedCurrency,
612                                                            gatewayDepositsUsed, 
613                                                            spentCurrencyOut,
614                                                            &tempState);
615         }
616         else
617         {
618             return retVal;
619         }
620
621         // if we are in the pre-launch phase, all reserves in are cumulative and then calculated together at launch
622         // reserves in represent all reserves in, and fees are taken out after launch or refund as well
623         //
624         // we add up until the end, then stop adding reserves at launch clear. this gives us the ability to reverse the 
625         // pre-launch state for validation. we continue adding fees up to pre-launch to easily get a total of unconverted 
626         // fees, which we need when creating a PBaaS chain, as all currency, both reserves and fees exported to the new
627         // chain must be either output to specific addresses, taken as fees by miners, or stored in reserve deposits.
628         if (tempState.IsPrelaunch())
629         {
630             tempState.reserveIn = tempState.AddVectors(tempState.reserveIn, this->currencyState.reserveIn);
631         }
632
633         newNotarization.currencyState = tempState;
634         return retVal;
635     }
636     else
637     {
638         if (sourceSystemID != destCurrency.launchSystemID || lastExportHeight >= destCurrency.startBlock)
639         {
640             newNotarization.currencyState.SetLaunchCompleteMarker();
641         }
642         newNotarization.currencyState.SetLaunchClear(false);
643
644         CCurrencyDefinition destSystem;
645
646         if (destCurrency.systemID != ASSETCHAINS_CHAINID)
647         {
648             destSystem = ConnectedChains.GetCachedCurrency(destCurrency.systemID);
649             newNotarization.SetSameChain(false);
650         }
651         else
652         {
653             destSystem = ConnectedChains.ThisChain();
654         }
655
656         // calculate new state from processing all transfers
657         // we are not refunding, and it is possible that we also have
658         // normal conversions in addition to pre-conversions. add any conversions that may 
659         // be present into the new currency state
660         CCoinbaseCurrencyState intermediateState = newNotarization.currencyState;
661         bool isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem, 
662                                                                   destSystem,
663                                                                   destCurrency, 
664                                                                   intermediateState, 
665                                                                   exportTransfers, 
666                                                                   dummyImportOutputs, 
667                                                                   importedCurrency,
668                                                                   gatewayDepositsUsed, 
669                                                                   spentCurrencyOut,
670                                                                   &newNotarization.currencyState);
671         if (!newNotarization.currencyState.IsPrelaunch() &&
672             isValidExport &&
673             destCurrency.IsFractional())
674         {
675             // we want the new price and the old state as a starting point to ensure no rounding error impact
676             // on reserves
677             importedCurrency = CCurrencyValueMap();
678             gatewayDepositsUsed = CCurrencyValueMap();
679             CCoinbaseCurrencyState tempCurState = intermediateState;
680             tempCurState.conversionPrice = newNotarization.currencyState.conversionPrice;
681             tempCurState.viaConversionPrice = newNotarization.currencyState.viaConversionPrice;
682             rtxd = CReserveTransactionDescriptor();
683             isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem, 
684                                                                  destSystem,
685                                                                  destCurrency, 
686                                                                  tempCurState, 
687                                                                  exportTransfers, 
688                                                                  importOutputs, 
689                                                                  importedCurrency,
690                                                                  gatewayDepositsUsed, 
691                                                                  spentCurrencyOut,
692                                                                  &newNotarization.currencyState);
693             if (isValidExport)
694             {
695                 newNotarization.currencyState.conversionPrice = tempCurState.conversionPrice;
696                 newNotarization.currencyState.viaConversionPrice = tempCurState.viaConversionPrice;
697             }
698         }
699         else if (isValidExport)
700         {
701             importOutputs.insert(importOutputs.end(), dummyImportOutputs.begin(), dummyImportOutputs.end());
702         }
703         
704         if (!isValidExport)
705         {
706             LogPrintf("%s: invalid export\n", __func__);
707             return false;
708         }
709         return true;
710     }
711
712     // based on the last notarization and existing
713     return false;
714 }
715
716 CObjectFinalization::CObjectFinalization(const CScript &script) : version(VERSION_INVALID)
717 {
718     COptCCParams p;
719     if (script.IsPayToCryptoCondition(p) && p.IsValid())
720     {
721         if ((p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT) &&
722             p.vData.size())
723         {
724             *this = CObjectFinalization(p.vData[0]);
725         }
726     }
727 }
728
729 CObjectFinalization::CObjectFinalization(const CTransaction &tx, uint32_t *pEcode, int32_t *pFinalizationOutNum)
730 {
731     uint32_t _ecode;
732     uint32_t &ecode = pEcode ? *pEcode : _ecode;
733     int32_t _finalizeOutNum;
734     int32_t &finalizeOutNum = pFinalizationOutNum ? *pFinalizationOutNum : _finalizeOutNum;
735     finalizeOutNum = -1;
736     for (int i = 0; i < tx.vout.size(); i++)
737     {
738         COptCCParams p;
739         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
740         {
741             if (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT)
742             {
743                 if (finalizeOutNum != -1)
744                 {
745                     this->version = VERSION_INVALID;
746                     finalizeOutNum = -1;
747                     break;
748                 }
749                 else
750                 {
751                     finalizeOutNum = i;
752                     ecode = p.evalCode;
753                 }
754             }
755         }
756     }
757 }
758
759 CChainNotarizationData::CChainNotarizationData(UniValue &obj)
760 {
761     version = (uint32_t)uni_get_int(find_value(obj, "version"));
762     UniValue vtxUni = find_value(obj, "notarizations");
763     if (vtxUni.isArray())
764     {
765         vector<UniValue> vvtx = vtxUni.getValues();
766         for (auto o : vvtx)
767         {
768             vtx.push_back(make_pair(CUTXORef(uint256S(uni_get_str(find_value(o, "txid"))),
769                                              uni_get_int(find_value(o, "vout"))),
770                                     CPBaaSNotarization(find_value(o, "notarization"))));
771         }
772     }
773
774     lastConfirmed = (uint32_t)uni_get_int(find_value(obj, "lastconfirmed"));
775     UniValue forksUni = find_value(obj, "forks");
776     if (forksUni.isArray())
777     {
778         vector<UniValue> forksVec = forksUni.getValues();
779         for (auto fv : forksVec)
780         {
781             if (fv.isArray())
782             {
783                 forks.push_back(vector<int32_t>());
784                 vector<UniValue> forkVec = fv.getValues();
785                 for (auto fidx : forkVec)
786                 {
787                     forks.back().push_back(uni_get_int(fidx));
788                 }
789             }
790         }
791     }
792
793     bestChain = (uint32_t)uni_get_int(find_value(obj, "bestchain"));
794 }
795
796 UniValue CChainNotarizationData::ToUniValue() const
797 {
798     UniValue obj(UniValue::VOBJ);
799     obj.push_back(Pair("version", (int32_t)version));
800     UniValue notarizations(UniValue::VARR);
801     for (int64_t i = 0; i < vtx.size(); i++)
802     {
803         UniValue notarization(UniValue::VOBJ);
804         notarization.push_back(Pair("index", i));
805         notarization.push_back(Pair("txid", vtx[i].first.hash.GetHex()));
806         notarization.push_back(Pair("vout", (int32_t)vtx[i].first.n));
807         notarization.push_back(Pair("notarization", vtx[i].second.ToUniValue()));
808         notarizations.push_back(notarization);
809     }
810     obj.push_back(Pair("notarizations", notarizations));
811     UniValue Forks(UniValue::VARR);
812     for (int32_t i = 0; i < forks.size(); i++)
813     {
814         UniValue Fork(UniValue::VARR);
815         for (int32_t j = 0; j < forks[i].size(); j++)
816         {
817             Fork.push_back(forks[i][j]);
818         }
819         Forks.push_back(Fork);
820     }
821     obj.push_back(Pair("forks", Forks));
822     if (IsConfirmed())
823     {
824         obj.push_back(Pair("lastconfirmedheight", (int32_t)vtx[lastConfirmed].second.notarizationHeight));
825     }
826     obj.push_back(Pair("lastconfirmed", lastConfirmed));
827     obj.push_back(Pair("bestchain", bestChain));
828     return obj;
829 }
830
831 bool CPBaaSNotarization::CreateAcceptedNotarization(const CCurrencyDefinition &externalSystem,
832                                                     const CPBaaSNotarization &earnedNotarization,
833                                                     const CNotaryEvidence &notaryEvidence,
834                                                     CValidationState &state,
835                                                     TransactionBuilder &txBuilder)
836 {
837     std::string errorPrefix(strprintf("%s: ", __func__));
838     std::set<CIdentityID> notaries;
839
840     int minimumNotariesConfirm = (externalSystem.GetID() == ConnectedChains.FirstNotaryChain().GetID()) ?
841                                  minimumNotariesConfirm = ConnectedChains.ThisChain().minNotariesConfirm :
842                                  minimumNotariesConfirm = ConnectedChains.FirstNotaryChain().chainDefinition.minNotariesConfirm;
843
844     // now, verify the evidence. accepted notarizations for another system must have at least one
845     // valid piece of evidence, which currently means at least one notary signature
846     if (!notaryEvidence.signatures.size())
847     {
848         return state.Error(errorPrefix + "insufficient notary evidence required to accept notarization");
849     }
850     for (auto &oneSigID : externalSystem.notaries)
851     {
852         notaries.insert(oneSigID);
853     }
854
855     LOCK(cs_main);
856
857     // create an accepted notarization based on the cross-chain notarization provided
858     CPBaaSNotarization newNotarization = earnedNotarization;
859
860     // this should be mirrored for us to continue, if it can't be, it is invalid
861     if (earnedNotarization.IsMirror() || !newNotarization.SetMirror())
862     {
863         return state.Error(errorPrefix + "invalid earned notarization");
864     }
865
866     uint160 SystemID = externalSystem.GetID();
867     uint32_t height = chainActive.Height();
868     CProofRoot ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID];
869
870     CChainNotarizationData cnd;
871     std::vector<std::pair<CTransaction, uint256>> txes;
872     if (!GetNotarizationData(SystemID, cnd, &txes))
873     {
874         return state.Error(errorPrefix + "cannot locate notarization history");
875     }
876
877     // any notarization submitted must include a proof root of this chain that is later than the last confirmed
878     // notarization
879     uint32_t lastHeight = cnd.vtx[cnd.lastConfirmed].second.IsPreLaunch() ? 
880                             cnd.vtx[cnd.lastConfirmed].second.notarizationHeight :
881                             cnd.vtx[cnd.lastConfirmed].second.proofRoots.count(ASSETCHAINS_CHAINID) ?
882                                 cnd.vtx[cnd.lastConfirmed].second.proofRoots.find(ASSETCHAINS_CHAINID)->second.rootHeight :
883                                 UINT32_MAX;
884     if (!cnd.IsConfirmed() || 
885         ourRoot.rootHeight <= lastHeight)
886     {
887         return state.Error(errorPrefix + "earned notarization proof root cannot be verified as later than prior confirmed for this chain");
888     }
889
890     auto hw = CMMRNode<>::GetHashWriter();
891
892     std::vector<unsigned char> notarizationVec = ::AsVector(earnedNotarization);
893     uint256 objHash = hw.write((const char *)&(notarizationVec[0]), notarizationVec.size()).GetHash();
894
895     int sigCount = 0;
896     for (auto &oneSig : notaryEvidence.signatures)
897     {
898         if (!notaries.count(oneSig.first))
899         {
900             return state.Error(errorPrefix + "unauthorized notary signature");
901         }
902         CIdentity sigIdentity = CIdentity::LookupIdentity(oneSig.first);
903         if (!sigIdentity.IsValidUnrevoked())
904         {
905             return state.Error(errorPrefix + "invalid notary identity");
906         }
907
908         // we currently require accepted notarizations to be completely authorized by notaries
909         /* printf("%s: Checking signature at sign height %u on system: %s for identity %s\nconfirmedKey: %s\nobjHash %s\n", 
910             __func__,
911             oneSig.second.blockHeight,
912             EncodeDestination(CIdentityID(SystemID)).c_str(),
913             EncodeDestination(CIdentityID(oneSig.first)).c_str(),
914             notaryEvidence.NotaryConfirmedKey().GetHex().c_str(),
915             objHash.GetHex().c_str());
916         printf("%s: notarization:\n%s\n", __func__, earnedNotarization.ToUniValue().write(1,2).c_str());
917         printf("%s: hex:\n%s\n", __func__, HexBytes(&(notarizationVec[0]), notarizationVec.size()).c_str()); */
918
919         if (oneSig.second.CheckSignature(sigIdentity,
920                                          std::vector<uint160>({notaryEvidence.NotaryConfirmedKey()}), 
921                                          std::vector<uint256>(), 
922                                          SystemID, 
923                                          "", 
924                                          objHash) != oneSig.second.SIGNATURE_COMPLETE)
925         {
926             return state.Error(errorPrefix + "invalid or incomplete notary signature");
927         }
928         sigCount++;
929     }
930
931     auto mmv = chainActive.GetMMV();
932     mmv.resize(ourRoot.rootHeight + 1);
933
934     // we only create accepted notarizations for notarizations that are earned for this chain on another system
935     // currently, we support ethereum and PBaaS types.
936     if (!newNotarization.proofRoots.count(SystemID) ||
937         !newNotarization.proofRoots.count(ASSETCHAINS_CHAINID) ||
938         !(ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID]).IsValid() ||
939         ourRoot.rootHeight > height ||
940         ourRoot.blockHash != chainActive[ourRoot.rootHeight]->GetBlockHash() ||
941         ourRoot.stateRoot != mmv.GetRoot() ||
942         (ourRoot.type != ourRoot.TYPE_PBAAS && ourRoot.type != ourRoot.TYPE_ETHEREUM))
943     {
944         return state.Error(errorPrefix + "can only create accepted notarization from notarization with valid proof root of this chain");
945     }
946
947     // ensure that the data present is valid, as of the height
948     CCoinbaseCurrencyState oldCurState = ConnectedChains.GetCurrencyState(ASSETCHAINS_CHAINID, ourRoot.rootHeight);
949     if (!oldCurState.IsValid() ||
950         ::GetHash(oldCurState) != ::GetHash(earnedNotarization.currencyState))
951     {
952         return state.Error(errorPrefix + "currency state is invalid in accepted notarization. is:\n" + 
953                                             newNotarization.currencyState.ToUniValue().write(1,2) + 
954                                             "\nshould be:\n" + 
955                                             oldCurState.ToUniValue().write(1,2) + "\n");
956     }
957
958     // ensure that all locally provable info is valid as of our root height
959     // and determine if the new notarization should be already finalized or not
960     for (auto &oneCur : newNotarization.currencyStates)
961     {
962         if (oneCur.first == SystemID)
963         {
964             return state.Error(errorPrefix + "cannot accept redundant currency state in notarization for " + EncodeDestination(CIdentityID(SystemID)));
965         }
966         else if (oneCur.first != ASSETCHAINS_CHAINID)
967         {
968             // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
969             CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneCur.first);
970             // we must have all currencies
971             if (!curDef.IsValid())
972             {
973                 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
974             }
975             // if the currency is not from this chain, we cannot validate it
976             if (curDef.systemID != ASSETCHAINS_CHAINID)
977             {
978                 continue;
979             }
980             // ensure that the data present is valid, as of the height
981             oldCurState = ConnectedChains.GetCurrencyState(oneCur.first, ourRoot.rootHeight);
982             if (!oldCurState.IsValid() ||
983                 ::GetHash(oldCurState) != ::GetHash(oneCur.second))
984             {
985                 return state.Error(errorPrefix + "currecy state is invalid in accepted notarization. is:\n" + 
986                                                  oneCur.second.ToUniValue().write(1,2) + 
987                                                  "\nshould be:\n" + 
988                                                  oldCurState.ToUniValue().write(1,2) + "\n");
989             }
990         }
991         else
992         {
993             // already checked above
994             continue;
995         }
996     }
997     for (auto &oneRoot : newNotarization.proofRoots)
998     {
999         if (oneRoot.first == SystemID)
1000         {
1001             continue;
1002         }
1003         else
1004         {
1005             // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
1006             CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneRoot.first);
1007             // we must have all currencies in this notarization registered
1008             if (!curDef.IsValid())
1009             {
1010                 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
1011             }
1012             uint160 curDefID = curDef.GetID();
1013
1014             // only check other currencies on this chain, not the main chain itself
1015             if (curDefID != ASSETCHAINS_CHAINID && curDef.systemID == ASSETCHAINS_CHAINID)
1016             {
1017                 return state.Error(errorPrefix + "proof roots are not accepted for token currencies");
1018             }
1019         }
1020     }
1021
1022     // now create the new notarization, add the proof, finalize if appropriate, and finish
1023
1024     // add spend of prior notarization and then outputs
1025     CPBaaSNotarization lastUnspentNotarization;
1026     uint256 lastTxId;
1027     int32_t lastTxOutNum;
1028     CTransaction lastTx;
1029     if (!lastUnspentNotarization.GetLastUnspentNotarization(SystemID, lastTxId, lastTxOutNum, &lastTx))
1030     {
1031         return state.Error(errorPrefix + "invalid prior notarization");
1032     }
1033
1034     // add prior unspent accepted notarization as our input
1035     txBuilder.AddTransparentInput(CUTXORef(lastTxId, lastTxOutNum), lastTx.vout[lastTxOutNum].scriptPubKey, lastTx.vout[lastTxOutNum].nValue);
1036
1037     CCcontract_info CC;
1038     CCcontract_info *cp;
1039     std::vector<CTxDestination> dests;
1040
1041     // make the earned notarization output
1042     cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
1043
1044     if (externalSystem.notarizationProtocol == externalSystem.NOTARIZATION_NOTARY_CHAINID)
1045     {
1046         dests = std::vector<CTxDestination>({CIdentityID(externalSystem.GetID())});
1047     }
1048     else
1049     {
1050         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1051     }
1052
1053     txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &newNotarization)), 0);
1054
1055     // now add the notary evidence and finalization that uses it to assert validity
1056     // make the earned notarization output
1057     cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1058     dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1059     txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, &notaryEvidence)), 0);
1060
1061     if (externalSystem.notarizationProtocol != externalSystem.NOTARIZATION_NOTARY_CHAINID)
1062     {
1063         // make the finalization output
1064         cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1065         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1066
1067         // we need to store the input that we confirmed if we spent finalization outputs
1068         CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, newNotarization.currencyID, uint256(), txBuilder.mtx.vout.size() - 2, height + 15);
1069         if (notaryEvidence.signatures.size() >= minimumNotariesConfirm)
1070         {
1071             of.SetConfirmed();
1072             of.evidenceOutputs.push_back(txBuilder.mtx.vout.size() - 1);
1073         }
1074         txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of)), 0);
1075     }
1076     return true;
1077 }
1078
1079 extern void CopyNodeStats(std::vector<CNodeStats>& vstats);
1080
1081 std::vector<CNodeData> GetGoodNodes(int maxNum)
1082 {
1083     // good nodes ordered by time connected
1084     std::map<int64_t, CNode *> goodNodes;
1085     std::vector<CNodeData> retVal;
1086
1087     LOCK(cs_vNodes);
1088     for (auto oneNode : vNodes)
1089     {
1090         if (!oneNode->fInbound)
1091         {
1092             if (oneNode->fWhitelisted)
1093             {
1094                 goodNodes.insert(std::make_pair(oneNode->nTimeConnected, oneNode));
1095             }
1096             else if (oneNode->GetTotalBytesRecv() > (1024 * 1024))
1097             {
1098                 goodNodes.insert(std::make_pair(oneNode->nTimeConnected, oneNode));
1099             }
1100         }
1101     }
1102     if (goodNodes.size() > 1)
1103     {
1104         seed_insecure_rand();
1105         for (auto &oneNode : goodNodes)
1106         {
1107             if (insecure_rand() & 1)
1108             {
1109                 retVal.push_back(CNodeData(oneNode.second->addr.ToStringIPPort(), oneNode.second->hashPaymentAddress));
1110                 if (retVal.size() >= maxNum)
1111                 {
1112                     break;
1113                 }
1114             }
1115         }
1116         if (!retVal.size())
1117         {
1118             retVal.push_back(CNodeData(goodNodes.begin()->second->addr.ToStringIPPort(), goodNodes.begin()->second->hashPaymentAddress));
1119         }
1120     }
1121     else if (goodNodes.size())
1122     {
1123         retVal.push_back(CNodeData(goodNodes.begin()->second->addr.ToStringIPPort(), goodNodes.begin()->second->hashPaymentAddress));
1124     }
1125     return retVal;
1126 }
1127
1128 // create a notarization that is validated as part of the block, generally benefiting the miner or staker if the
1129 // cross notarization is valid
1130 bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalSystem,
1131                                                   const CTransferDestination &Proposer,
1132                                                   CValidationState &state,
1133                                                   std::vector<CTxOut> &txOutputs,
1134                                                   CPBaaSNotarization &notarization)
1135 {
1136     std::string errorPrefix(strprintf("%s: ", __func__));
1137
1138     uint32_t height;
1139     uint160 SystemID;
1140     const CCurrencyDefinition &systemDef = externalSystem.chainDefinition;
1141     SystemID = externalSystem.chainDefinition.GetID();
1142
1143     CChainNotarizationData cnd;
1144     std::vector<std::pair<CTransaction, uint256>> txes;
1145
1146     {
1147         LOCK2(cs_main, mempool.cs);
1148         height = chainActive.Height();
1149
1150         // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
1151         // we also need to ensure that our notarization would be the first notarization in this notary block period  with which we agree.
1152         if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
1153         {
1154             // technically not a real error
1155             return state.Error("no-notary");
1156         }
1157
1158         if (!GetNotarizationData(SystemID, cnd, &txes))
1159         {
1160             return state.Error(errorPrefix + "no prior notarization found");
1161         }
1162     }
1163
1164     // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
1165     UniValue proofRootsUni(UniValue::VARR);
1166     for (auto &oneNot : cnd.vtx)
1167     {
1168         auto rootIt = oneNot.second.proofRoots.find(SystemID);
1169         if (rootIt != oneNot.second.proofRoots.end())
1170         {
1171             proofRootsUni.push_back(rootIt->second.ToUniValue());
1172         }
1173     }
1174
1175     if (!proofRootsUni.size())
1176     {
1177         return state.Error(errorPrefix + "no valid prior state root found");
1178     }
1179
1180     // call notary to determine the prior notarization that we agree with
1181     UniValue params(UniValue::VARR);
1182
1183     UniValue oneParam(UniValue::VOBJ);
1184     oneParam.push_back(Pair("proofroots", proofRootsUni));
1185     oneParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1186     params.push_back(oneParam);
1187  
1188     //printf("%s: about to get cross notarization with %lu notarizations found\n", __func__, cnd.vtx.size());
1189
1190     UniValue result;
1191     try
1192     {
1193         result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1194     } catch (exception e)
1195     {
1196         result = NullUniValue;
1197     }
1198
1199     int32_t notaryIdx = uni_get_int(find_value(result, "bestindex"), -1);
1200
1201     if (result.isNull() || notaryIdx == -1)
1202     {
1203         return state.Error(result.isNull() ? "no-notary" : "no-matching-proof-roots-found");
1204     }
1205
1206     // now, we have the index for the transaction and notarization we agree with, a list of those we consider invalid,
1207     // and the most recent notarization to use when creating the new one
1208     const CTransaction &priorNotarizationTx = txes[notaryIdx].first;
1209     uint256 priorBlkHash = txes[notaryIdx].second;
1210     const CUTXORef &priorUTXO = cnd.vtx[notaryIdx].first;
1211     const CPBaaSNotarization &priorNotarization = cnd.vtx[notaryIdx].second;
1212
1213     // find out the block height holding the last notarization we agree with
1214     auto mapBlockIt = mapBlockIndex.find(priorBlkHash);
1215     if (mapBlockIt == mapBlockIndex.end() || !chainActive.Contains(mapBlockIt->second))
1216     {
1217         return state.Error(errorPrefix + "prior notarization not in blockchain");
1218     }
1219
1220     // first determine if the prior notarization we agree with would make this one moot
1221     int blockPeriodNumber = (height + 1) / BLOCK_NOTARIZATION_MODULO;
1222     int priorBlockPeriod = mapBlockIt->second->GetHeight() / BLOCK_NOTARIZATION_MODULO;
1223
1224     if (blockPeriodNumber <= priorBlockPeriod)
1225     {
1226         return state.Error("ineligible");
1227     }
1228
1229     notarization = priorNotarization;
1230     notarization.SetBlockOneNotarization(false);
1231     notarization.SetDefinitionNotarization(false);
1232     notarization.proposer = Proposer;
1233     notarization.prevHeight = priorNotarization.notarizationHeight;
1234
1235     // get the latest notarization information for the new, earned notarization
1236     // one system may provide one proof root and multiple currency states
1237     CProofRoot latestProofRoot = CProofRoot(find_value(result, "latestproofroot"));
1238     if (!latestProofRoot.IsValid() || notarization.proofRoots[latestProofRoot.systemID].rootHeight >= latestProofRoot.rootHeight)
1239     {
1240         return state.Error("no-new-latest-proof-root");
1241     }
1242     notarization.proofRoots[latestProofRoot.systemID] = latestProofRoot;
1243     notarization.notarizationHeight = latestProofRoot.rootHeight;
1244
1245     UniValue currencyStatesUni = find_value(result, "currencystates");
1246     if (!(currencyStatesUni.isArray() && currencyStatesUni.size()))
1247     {
1248         return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1249     }
1250
1251     // take the lock again, now that we're back from calling out
1252     LOCK2(cs_main, mempool.cs);
1253
1254     // if height changed, we need to fail and possibly try again
1255     if (height != chainActive.Height())
1256     {
1257         return state.Error("stale-block");
1258     }
1259
1260     notarization.currencyStates.clear();
1261     for (int i = 0; i < currencyStatesUni.size(); i++)
1262     {
1263         CCoinbaseCurrencyState oneCurState(currencyStatesUni[i]);
1264         CCurrencyDefinition oneCurDef;
1265         if (!oneCurState.IsValid())
1266         {
1267             return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1268         }
1269         if (!(oneCurDef = ConnectedChains.GetCachedCurrency(oneCurState.GetID())).IsValid())
1270         {
1271             // if we don't have the currency for the state specified, and it isn't critical, ignore
1272             if (oneCurDef.GetID() == SystemID)
1273             {
1274                 return state.Error(errorPrefix + "system currency invalid - possible corruption");
1275             }
1276             continue;
1277         }
1278         if (oneCurDef.systemID == SystemID)
1279         {
1280             uint160 oneCurDefID = oneCurDef.GetID();
1281             if (notarization.currencyID == oneCurDefID)
1282             {
1283                 notarization.currencyState = oneCurState;
1284             }
1285             else
1286             {
1287                 notarization.currencyStates[oneCurDefID] = oneCurState;
1288             }
1289         }
1290     }
1291     notarization.currencyStates[ASSETCHAINS_CHAINID] = ConnectedChains.GetCurrencyState(height);
1292     if (!systemDef.GatewayConverterID().IsNull())
1293     {
1294         notarization.currencyStates[systemDef.GatewayConverterID()] = ConnectedChains.GetCurrencyState(systemDef.GatewayConverterID(), height);
1295     }
1296
1297     // add this blockchain's info, based on the requested height
1298     CBlockIndex &curBlkIndex = *chainActive[height];
1299     uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1300     notarization.proofRoots[thisChainID] = CProofRoot::GetProofRoot(height);
1301
1302     // add currency states that we should include and then we're done
1303     // currency states to include are either a gateway currency indicated by the
1304     // gateway or our gateway converter for our PBaaS chain
1305     uint160 gatewayConverterID;
1306     if (systemDef.IsGateway() && !systemDef.gatewayConverterName.empty())
1307     {
1308         gatewayConverterID = CCurrencyDefinition::GetID(systemDef.gatewayConverterName, thisChainID);
1309     }
1310     else if (SystemID == ConnectedChains.FirstNotaryChain().chainDefinition.GetID() && !ConnectedChains.ThisChain().gatewayConverterName.empty())
1311     {
1312         gatewayConverterID = CCurrencyDefinition::GetID(ConnectedChains.ThisChain().gatewayConverterName, thisChainID);
1313     }
1314     if (!gatewayConverterID.IsNull())
1315     {
1316         // get the gateway converter currency from the gateway definition
1317         CChainNotarizationData gatewayCND;
1318         if (GetNotarizationData(gatewayConverterID, gatewayCND) && gatewayCND.vtx.size())
1319         {
1320             notarization.currencyStates[gatewayConverterID] = gatewayCND.vtx[gatewayCND.lastConfirmed].second.currencyState;
1321         }
1322     }
1323
1324     notarization.nodes = GetGoodNodes(CPBaaSNotarization::MAX_NODES);
1325
1326     notarization.prevNotarization = cnd.vtx[notaryIdx].first;
1327     auto hw = CMMRNode<>::GetHashWriter();
1328     hw << cnd.vtx[notaryIdx].second;
1329     notarization.hashPrevNotarization = hw.GetHash();
1330     notarization.prevHeight = cnd.vtx[notaryIdx].second.notarizationHeight;
1331
1332     CCcontract_info CC;
1333     CCcontract_info *cp;
1334     std::vector<CTxDestination> dests;
1335
1336     // make the earned notarization output
1337     cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
1338
1339     if (systemDef.notarizationProtocol == systemDef.NOTARIZATION_NOTARY_CHAINID)
1340     {
1341         dests = std::vector<CTxDestination>({CIdentityID(systemDef.GetID())});
1342     }
1343     else
1344     {
1345         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1346     }
1347
1348     txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_EARNEDNOTARIZATION, dests, 1, &notarization))));
1349
1350     if (systemDef.notarizationProtocol != systemDef.NOTARIZATION_NOTARY_CHAINID)
1351     {
1352         // make the finalization output
1353         cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1354
1355         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1356
1357         // we need to store the input that we confirmed if we spent finalization outputs
1358         CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, VERUS_CHAINID, uint256(), txOutputs.size() - 1, height + 15);
1359         txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of))));
1360     }
1361     return true;
1362 }
1363
1364 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentConfirmedFinalizations(const uint160 &currencyID)
1365 {
1366     LOCK(mempool.cs);
1367     std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1368     std::vector<CAddressUnspentDbEntry> indexUnspent;
1369     std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1370
1371     
1372     uint160 indexKey = CCrossChainRPCData::GetConditionID(
1373         CCrossChainRPCData::GetConditionID(currencyID, CObjectFinalization::ObjectFinalizationNotarizationKey()), 
1374         ObjectFinalizationConfirmedKey());
1375     if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1376          mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1377         (indexUnspent.size() || mempoolUnspent.size()))
1378     {
1379         /* printf("%s: confirmedNotarizationKey: %s / 0x%s\nconfirmed finalizations\n",
1380             __func__, 
1381             EncodeDestination(CIdentityID(indexKey)).c_str(),
1382             indexKey.GetHex().c_str()); */
1383
1384         for (auto &oneConfirmed : indexUnspent)
1385         {
1386             //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1387             retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1388                              CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1389         }
1390         std::set<std::pair<uint256,int>> mempoolSpent;
1391         for (auto &oneConfirmed : mempoolUnspent)
1392         {
1393             if (oneConfirmed.first.spending)
1394             {
1395                 if (retVal.size() &&
1396                     retVal.back().second.txIn.prevout == COutPoint(oneConfirmed.first.txhash, oneConfirmed.first.index))
1397                 {
1398                     // remove it if spent
1399                     //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index);
1400                     retVal.pop_back();
1401                 }
1402                 mempoolSpent.insert(std::make_pair(oneConfirmed.first.txhash, oneConfirmed.first.index));
1403             }
1404         }
1405         for (auto &oneConfirmed : mempoolUnspent)
1406         {
1407             if (mempoolSpent.count(std::make_pair(oneConfirmed.first.txhash, oneConfirmed.first.index)))
1408             {
1409                 continue;
1410             }
1411
1412             auto txProxy = mempool.mapTx.find(oneConfirmed.first.txhash);
1413             if (txProxy != mempool.mapTx.end())
1414             {
1415                 auto &mpEntry = *txProxy;
1416                 auto &tx = mpEntry.GetTx();
1417                 //printf("%s: txid: %s, vout: %u\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index);
1418                 retVal.push_back(std::make_pair(0,
1419                                  CInputDescriptor(tx.vout[oneConfirmed.first.index].scriptPubKey,
1420                                                   tx.vout[oneConfirmed.first.index].nValue, 
1421                                                   CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1422             }
1423         }
1424     }
1425     return retVal;
1426 }
1427
1428 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentPendingFinalizations(const uint160 &currencyID)
1429 {
1430     LOCK(mempool.cs);
1431     std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1432     std::vector<CAddressUnspentDbEntry> indexUnspent;
1433     std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1434
1435     uint160 indexKey = CCrossChainRPCData::GetConditionID(
1436         CCrossChainRPCData::GetConditionID(currencyID, CObjectFinalization::ObjectFinalizationNotarizationKey()), 
1437         ObjectFinalizationPendingKey());
1438     if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1439          mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1440         (indexUnspent.size() || mempoolUnspent.size()))
1441     {
1442         /* printf("%s: pendingNotarizationKey: %s / 0x%s\npending finalizations\n",
1443             __func__, 
1444             EncodeDestination(CIdentityID(indexKey)).c_str(),
1445             indexKey.GetHex().c_str()); */
1446
1447         for (auto &oneConfirmed : indexUnspent)
1448         {
1449             //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1450             retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1451                              CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1452         }
1453         std::set<std::pair<uint256,int>> mempoolSpent;
1454         for (auto &oneUnconfirmed : mempoolUnspent)
1455         {
1456             if (oneUnconfirmed.first.spending)
1457             {
1458                 if (retVal.size() &&
1459                     retVal.back().second.txIn.prevout == COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))
1460                 {
1461                     // remove it if spent
1462                     //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneUnconfirmed.first.txhash.GetHex().c_str(), oneUnconfirmed.first.index);
1463                     retVal.pop_back();
1464                 }
1465                 mempoolSpent.insert(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1466             }
1467         }
1468         for (auto &oneUnconfirmed : mempoolUnspent)
1469         {
1470             if (mempoolSpent.count(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)))
1471             {
1472                 continue;
1473             }
1474
1475             auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1476             if (txProxy != mempool.mapTx.end())
1477             {
1478                 auto &mpEntry = *txProxy;
1479                 auto &tx = mpEntry.GetTx();
1480                 retVal.push_back(std::make_pair(0,
1481                                  CInputDescriptor(tx.vout[oneUnconfirmed.first.index].scriptPubKey,
1482                                                   tx.vout[oneUnconfirmed.first.index].nValue, 
1483                                                   CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1484             }
1485         }
1486     }
1487     return retVal;
1488 }
1489
1490 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentEvidence(const uint160 &currencyID,
1491                                                                                                 const uint256 &notarizationTxId,
1492                                                                                                 int32_t notarizationOutNum)
1493 {
1494     LOCK(mempool.cs);
1495     std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1496     std::vector<CAddressUnspentDbEntry> indexUnspent;
1497     std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1498     uint160 indexKey = CCrossChainRPCData::GetConditionID(currencyID, CNotaryEvidence::NotarySignatureKey(), notarizationTxId, notarizationOutNum);
1499
1500     if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) &&
1501          mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1502         (indexUnspent.size() || mempoolUnspent.size()))
1503     {
1504         /* printf("%s: unspentEvidenceKey: %s / 0x%s\nunspent evidence\n",
1505             __func__, 
1506             EncodeDestination(CIdentityID(indexKey)).c_str(),
1507             indexKey.GetHex().c_str()); */
1508
1509         for (auto &oneConfirmed : indexUnspent)
1510         {
1511             //printf("%s: txid: %s, vout: %lu, blockheight: %d\n", __func__, oneConfirmed.first.txhash.GetHex().c_str(), oneConfirmed.first.index, oneConfirmed.second.blockHeight);
1512             retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1513                              CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1514         }
1515         std::set<std::pair<uint256,int>> mempoolSpent;
1516         for (auto &oneUnconfirmed : mempoolUnspent)
1517         {
1518             if (oneUnconfirmed.first.spending)
1519             {
1520                 if (retVal.size() &&
1521                     retVal.back().second.txIn.prevout == COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))
1522                 {
1523                     // remove it if spent
1524                     //printf("%s: REMOVING txid: %s, vout: %u\n", __func__, oneUnconfirmed.first.txhash.GetHex().c_str(), oneUnconfirmed.first.index);
1525                     retVal.pop_back();
1526                 }
1527                 mempoolSpent.insert(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1528             }
1529         }
1530         for (auto &oneUnconfirmed : mempoolUnspent)
1531         {
1532             if (mempoolSpent.count(std::make_pair(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)))
1533             {
1534                 continue;
1535             }
1536
1537             auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1538             if (txProxy != mempool.mapTx.end())
1539             {
1540                 auto &mpEntry = *txProxy;
1541                 auto &tx = mpEntry.GetTx();
1542                 retVal.push_back(std::make_pair(0,
1543                                  CInputDescriptor(tx.vout[oneUnconfirmed.first.index].scriptPubKey,
1544                                                   tx.vout[oneUnconfirmed.first.index].nValue, 
1545                                                   CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1546             }
1547         }
1548     }
1549     return retVal;
1550 }
1551
1552 // this is called by notaries to locate any notarizations of a specific system that they can notarize, to determine if we
1553 // agree with the notarization in question, and to confirm or reject the notarization
1554 bool CPBaaSNotarization::ConfirmOrRejectNotarizations(const CWallet *pWallet,
1555                                                       const CRPCChainData &externalSystem,
1556                                                       CValidationState &state,
1557                                                       TransactionBuilder &txBuilder,
1558                                                       bool &finalized)
1559 {
1560     std::string errorPrefix(strprintf("%s: ", __func__));
1561
1562     finalized = false;
1563
1564     CChainNotarizationData cnd;
1565     std::vector<std::pair<CTransaction, uint256>> txes;
1566
1567     uint32_t height;
1568     uint160 SystemID = externalSystem.chainDefinition.GetID();
1569
1570     std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> mine;
1571     {
1572         std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> imsigner, watchonly;
1573         LOCK(pWallet->cs_wallet);
1574         // sign with all IDs under our control that are eligible for this currency
1575         pWallet->GetIdentities(ConnectedChains.ThisChain().notaries, mine, imsigner, watchonly);
1576         if (!mine.size())
1577         {
1578             return state.Error("no-notary");
1579         }
1580     }
1581
1582     {
1583         LOCK2(cs_main, mempool.cs);
1584         height = chainActive.Height();
1585
1586         // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
1587         // we also need to ensure that our notarization would be the first notarization in this notary block period  with which we agree.
1588         if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
1589         {
1590             // technically not a real error
1591             return state.Error("no-notary");
1592         }
1593
1594         if (!GetNotarizationData(SystemID, cnd, &txes))
1595         {
1596             return state.Error(errorPrefix + "no prior notarization found");
1597         }
1598     }
1599
1600     if (cnd.IsConfirmed() && cnd.vtx.size() == 1)
1601     {
1602         return state.Error("no-unconfirmed");
1603     }
1604
1605     if (height <= (CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED + 1))
1606     {
1607         return state.Error(errorPrefix + "too early");
1608     }
1609
1610     // latest height we are eligible to notarize
1611     uint32_t eligibleHeight = height - CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED;
1612
1613     // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
1614     UniValue proofRootsUni(UniValue::VARR);
1615     for (auto &oneNot : cnd.vtx)
1616     {
1617         auto rootIt = oneNot.second.proofRoots.find(SystemID);
1618         if (rootIt != oneNot.second.proofRoots.end())
1619         {
1620             proofRootsUni.push_back(rootIt->second.ToUniValue());
1621         }
1622     }
1623
1624     if (!proofRootsUni.size())
1625     {
1626         return state.Error(errorPrefix + "no valid prior state root found");
1627     }
1628
1629     UniValue firstParam(UniValue::VOBJ);
1630     firstParam.push_back(Pair("proofroots", proofRootsUni));
1631     firstParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1632
1633     // call notary to determine the notarization that we should notarize
1634     UniValue params(UniValue::VARR);
1635     params.push_back(firstParam);
1636
1637     //printf("%s: about to getbestproofroot with:\n%s\n", __func__, params.write(1,2).c_str());
1638
1639     UniValue result;
1640     try
1641     {
1642         result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1643     } catch (exception e)
1644     {
1645         result = NullUniValue;
1646     }
1647
1648     int32_t notaryIdx = uni_get_int(find_value(result, "bestindex"), -1);
1649
1650     if (result.isNull() || notaryIdx == -1)
1651     {
1652         return state.Error(result.isNull() ? "no-notary" : "no-matching-notarization-found");
1653     }
1654
1655     // take the lock again, now that we're back from calling out
1656     LOCK2(cs_main, mempool.cs);
1657
1658     // if height changed, we need to fail and possibly try again later
1659     if (height != chainActive.Height())
1660     {
1661         return state.Error("stale-block");
1662     }
1663
1664     // now, get the list of unconfirmed matches, and sign the latest one that
1665     // may be signed
1666     UniValue proofRootArr = find_value(result, "validindexes");
1667     if (!proofRootArr.isArray() || !proofRootArr.size())
1668     {
1669         return state.Error("no-valid-unconfirmed");
1670     }
1671
1672     if (!proofRootArr.isArray() || !proofRootArr.size())
1673     {
1674         return state.Error("no-valid-unconfirmed");
1675     }
1676
1677     bool retVal = false;
1678
1679     // look from the latest notarization that may qualify
1680     for (int i = proofRootArr.size() - 1; i >= 0; i--)
1681     {
1682         int idx = uni_get_int(proofRootArr[i]);
1683         auto proofIt = cnd.vtx[idx].second.proofRoots.find(ASSETCHAINS_CHAINID);
1684         if (proofIt != cnd.vtx[idx].second.proofRoots.end() &&
1685             proofIt->second.rootHeight <= eligibleHeight)
1686         {
1687             // if confirmed, we have no more to check
1688             if (cnd.lastConfirmed == idx)
1689             {
1690                 break;
1691             }
1692
1693             // this is the one we will notarize
1694             std::vector<CInputDescriptor> myIDSigs;
1695
1696             std::set<CIdentityID> myIDSet;
1697             for (auto &oneID : mine)
1698             {
1699                 myIDSet.insert(oneID.first.idID);
1700             }
1701
1702             // a pending finalization may be either an actual notarization finalization or an earned notarization
1703             // a confirmed finalization may be either a finalization or a notarization
1704             std::vector<std::pair<uint32_t, CInputDescriptor>> unspentConfirmed = CObjectFinalization::GetUnspentConfirmedFinalizations(SystemID);
1705             std::vector<std::pair<uint32_t, CInputDescriptor>> unspentPending = CObjectFinalization::GetUnspentPendingFinalizations(SystemID);
1706
1707             // if we finalize and confirm a notarization, we will spend all prior confirmed and
1708             // pending outputs as part of finalization.
1709
1710             // if we only sign and add to a confirmation but cannot meet finalization requirements, we can 
1711             // spend and roll up pending outputs into ours as combined
1712
1713             // CUTXORef = vtx UTXORef
1714             // pair {
1715             //    int = index into the vtx vector
1716             //    vector of pairs {uint32_t = blockHeight, CInputDescriptor for evidence}
1717             // }
1718             // 
1719             std::map<CUTXORef, std::pair<int, std::vector<std::pair<uint32_t, CInputDescriptor>>>> vtxFinalizations;
1720             for (int j = 0; j < cnd.vtx.size(); j++)
1721             {
1722                 COptCCParams oneP;
1723                 
1724                 // if any of the notarizations are earned notarizations, we will spend those behind us if we finalize
1725                 if (txes[j].first.vout[cnd.vtx[j].first.n].scriptPubKey.IsPayToCryptoCondition(oneP) &&
1726                     oneP.IsValid() &&
1727                     oneP.evalCode == EVAL_EARNEDNOTARIZATION)
1728                 {
1729                     // printf("%s: adding %s to vtxFinalizations\n", __func__, cnd.vtx[j].first.ToUniValue().write(1,2).c_str());
1730                     vtxFinalizations.insert(std::make_pair(cnd.vtx[j].first, std::make_pair(j, std::vector<std::pair<uint32_t, CInputDescriptor>>())));
1731                 }
1732             }
1733
1734             for (auto &oneConfirmed : unspentConfirmed)
1735             {
1736                 CObjectFinalization oneOf;
1737                 COptCCParams oneP;
1738                 if (oneConfirmed.second.scriptPubKey.IsPayToCryptoCondition(oneP) &&
1739                     oneP.IsValid() &&
1740                     oneP.evalCode != EVAL_EARNEDNOTARIZATION &&
1741                     oneP.evalCode != EVAL_ACCEPTEDNOTARIZATION)
1742                 {
1743                     oneOf = CObjectFinalization(oneConfirmed.second.scriptPubKey);
1744                     if (!oneOf.IsValid())
1745                     {
1746                         printf("%s: invalid index entry at %s : %u\n", 
1747                             __func__, 
1748                             oneConfirmed.second.txIn.prevout.hash.GetHex().c_str(),
1749                             oneConfirmed.second.txIn.prevout.n);
1750                         break;
1751                     }
1752                 }
1753                 else
1754                 {
1755                     continue;
1756                 }
1757
1758                 CUTXORef oneConfirmedTarget = CUTXORef(oneOf.output.hash.IsNull() ? oneConfirmed.second.txIn.prevout.hash : oneOf.output.hash,
1759                                                        oneOf.output.n);
1760
1761                 // if the notarization we have selected to confirm is already confirmed, we are done
1762                 if (vtxFinalizations[oneConfirmedTarget].first == idx)
1763                 {
1764                     return state.Error("notarization-already-confirmed");
1765                 }
1766                 /* printf("%s: adding %s to entry #%d in vtxFinalizations\n", 
1767                     __func__, 
1768                     CUTXORef(oneConfirmed.second.txIn.prevout).ToUniValue().write(1,2).c_str(), 
1769                     vtxFinalizations[oneConfirmedTarget].first); */
1770                 vtxFinalizations[oneConfirmedTarget].second.push_back(oneConfirmed);
1771             }
1772
1773             // unspent evidence is specific to the target notarization
1774             std::vector<std::pair<uint32_t, CInputDescriptor>> unspentEvidence = CObjectFinalization::GetUnspentEvidence(ASSETCHAINS_CHAINID,
1775                                                                                                                          cnd.vtx[idx].first.hash,
1776                                                                                                                          cnd.vtx[idx].first.n);
1777             std::vector<CInputDescriptor> cleanupSpend;
1778
1779             for (auto &onePending : unspentPending)
1780             {
1781                 CObjectFinalization oneOf;
1782                 COptCCParams oneP;
1783                 if (onePending.second.scriptPubKey.IsPayToCryptoCondition(oneP) &&
1784                     oneP.IsValid())
1785                 {
1786                     if (oneP.evalCode == EVAL_EARNEDNOTARIZATION ||
1787                         oneP.evalCode == EVAL_ACCEPTEDNOTARIZATION)
1788                     {
1789                         continue;
1790                     }
1791                     if (oneP.evalCode == EVAL_FINALIZE_NOTARIZATION && oneP.vData.size())
1792                     {
1793                         oneOf = CObjectFinalization(oneP.vData[0]);
1794                     }
1795                     if (!oneOf.IsValid())
1796                     {
1797                         printf("%s: invalid index entry at %s : %u\n", 
1798                             __func__, 
1799                             onePending.second.txIn.prevout.hash.GetHex().c_str(),
1800                             onePending.second.txIn.prevout.n);
1801                         break;
1802                     }
1803                 }
1804
1805                 CUTXORef onePendingTarget = CUTXORef(oneOf.output.hash.IsNull() ? onePending.second.txIn.prevout.hash : oneOf.output.hash,
1806                                                      oneOf.output.n);
1807                 if (!vtxFinalizations.count(onePendingTarget))
1808                 {
1809                     CTransaction checkTx;
1810                     uint256 blockHash;
1811                     if (myGetTransaction(onePendingTarget.hash, checkTx, blockHash))
1812                     {
1813                         /* UniValue jsonTx(UniValue::VOBJ);
1814                         TxToUniv(checkTx, blockHash, jsonTx);
1815                         printf("%s: cleanup forked tx: %s\noutput #%u\nvtxFinalizations:\n", __func__, jsonTx.write(1,2).c_str(), onePendingTarget.n); */
1816                         cleanupSpend.push_back(onePending.second);
1817                     }
1818                     else
1819                     {
1820                         printf("%s: pending notarization finalization with no notarization matching (%s) found - may be mempool only\n", __func__, onePendingTarget.ToUniValue().write(1,2).c_str());
1821                     }
1822                     vtxFinalizations[onePendingTarget].first = -1;
1823                 }
1824                 vtxFinalizations[onePendingTarget].second.push_back(onePending);
1825             }
1826
1827             // we need to have a pending finalization to spend to add confirmations
1828             if (!vtxFinalizations[cnd.vtx[idx].first].second.size())
1829             {
1830                 printf("%s: ERROR: pending notarization (%s) with no matching finalization\n", __func__, cnd.vtx[idx].first.ToUniValue().write(1,2).c_str());
1831                 break;
1832             }
1833
1834             // before signing the one we are about to, we want to ensure that it isn't already signed sufficiently
1835             // if there are enough signatures to confirm it without signature, make our signature, then create a finalization
1836             CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION,
1837                                                          SystemID,
1838                                                          cnd.vtx[idx].first.hash,
1839                                                          cnd.vtx[idx].first.n,
1840                                                          eligibleHeight);
1841
1842             std::vector<CInputDescriptor> additionalEvidence;
1843
1844             std::set<uint160> sigSet;
1845
1846             int minimumNotariesConfirm = (externalSystem.GetID() == ConnectedChains.FirstNotaryChain().GetID()) ?
1847                                         minimumNotariesConfirm = ConnectedChains.ThisChain().minNotariesConfirm :
1848                                         minimumNotariesConfirm = ConnectedChains.FirstNotaryChain().chainDefinition.minNotariesConfirm;
1849
1850             // if we might have a confirmed notarization, verify, then post
1851             for (auto &oneEvidenceOut : unspentEvidence)
1852             {
1853                 COptCCParams p;
1854                 CNotaryEvidence evidence;
1855                 if (oneEvidenceOut.second.scriptPubKey.IsPayToCryptoCondition(p) &&
1856                     p.IsValid() &&
1857                     p.evalCode == EVAL_NOTARY_EVIDENCE &&
1858                     p.vData.size() &&
1859                     (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
1860                     evidence.IsNotarySignature())
1861                 {
1862                     if (CUTXORef(evidence.output.hash.IsNull() ? oneEvidenceOut.second.txIn.prevout.hash : evidence.output.hash, evidence.output.n) == of.output &&
1863                         evidence.signatures.size())
1864                     {
1865                         bool hasOurSig = false;
1866                         for (auto &oneSig : evidence.signatures)
1867                         {
1868                             sigSet.insert(oneSig.first);
1869                             if (myIDSet.count(oneSig.first))
1870                             {
1871                                 hasOurSig = true;
1872                                 myIDSet.erase(oneSig.first);
1873                             }
1874                         }
1875                         if (hasOurSig)
1876                         {
1877                             myIDSigs.push_back(oneEvidenceOut.second);
1878                         }
1879                         else
1880                         {
1881                             additionalEvidence.push_back(oneEvidenceOut.second);
1882                         }
1883                     }
1884                     else
1885                     {
1886                         printf("%s: ERROR - unexpected code path, no assert thrown for debugging\n", __func__);
1887                         cleanupSpend.push_back(oneEvidenceOut.second);
1888                     }
1889                 }
1890             }
1891
1892             CNotaryEvidence ne(ASSETCHAINS_CHAINID, cnd.vtx[idx].first);
1893             CCcontract_info CC;
1894             CCcontract_info *cp;
1895             std::vector<CTxDestination> dests;
1896
1897             // if we can still sign, do so before final check of evidence
1898             if (myIDSet.size())
1899             {
1900                 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1901                 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1902
1903                 {
1904                     LOCK(pWallet->cs_wallet);
1905                     // sign with all IDs under our control that are eligible for this currency
1906                     for (auto &oneID : myIDSet)
1907                     {
1908                         auto signResult = ne.SignConfirmed(*pWallet, txes[idx].first, oneID, height);
1909                         if (signResult == CIdentitySignature::SIGNATURE_PARTIAL || signResult == CIdentitySignature::SIGNATURE_COMPLETE)
1910                         {
1911                             sigSet.insert(oneID);
1912                             retVal = true;
1913                             // if our signatures altogether have provided a complete validation, we can early out
1914                             if ((ne.signatures.size() + myIDSigs.size()) >= minimumNotariesConfirm)
1915                             {
1916                                 break;
1917                             }
1918                         }
1919                         else
1920                         {
1921                             return state.Error(errorPrefix + "invalid identity signature");
1922                         }
1923                     }
1924                 }
1925
1926                 if (ne.signatures.size())
1927                 {
1928                     /* for (auto &debugOut : ne.signatures)
1929                     {
1930                         printf("%s: onesig - ID: %s, signature: %s\n", __func__, EncodeDestination(debugOut.first).c_str(), debugOut.second.ToUniValue().write(1,2).c_str());
1931                     } */
1932
1933                     retVal = true;
1934                     CScript evidenceScript = MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, &ne));
1935                     myIDSigs.push_back(CInputDescriptor(evidenceScript, 0, CTxIn(COutPoint(uint256(), txBuilder.mtx.vout.size()))));
1936                     of.evidenceOutputs.push_back(txBuilder.mtx.vout.size());
1937                     txBuilder.AddTransparentOutput(evidenceScript, CNotaryEvidence::DEFAULT_OUTPUT_VALUE);
1938                 }
1939             }
1940
1941             // if we have enough to finalize, do so as a combination of pre-existing evidence and this
1942             if (sigSet.size() >= minimumNotariesConfirm)
1943             {
1944                 int sigCount = 0;
1945
1946                 of.SetConfirmed();
1947
1948                 // spend all priors, and if we need more signatures, add them to the finalization evidence
1949                 // prioritizing our signatures
1950                 bool haveNeeded = false;
1951                 std::vector<CInputDescriptor> inputSigs;
1952                 for (auto &oneEvidenceOut : myIDSigs)
1953                 {
1954                     // use up evidence with our ID signatures first, and remove from the remainder
1955                     COptCCParams p;
1956                     CNotaryEvidence evidence;
1957
1958                     // validated above
1959                     oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1960                     evidence = CNotaryEvidence(p.vData[0]);
1961                     for (auto &oneSig : evidence.signatures)
1962                     {
1963                         if (sigSet.count(oneSig.first))
1964                         {
1965                             sigCount++;
1966                             sigSet.erase(oneSig.first);
1967                         }
1968                     }
1969
1970                     if (!haveNeeded)
1971                     {
1972                         haveNeeded = sigCount >= minimumNotariesConfirm;
1973
1974                         // until we have enough signatures to confirm, continue to add evidence to the finalization
1975                         if (!oneEvidenceOut.txIn.prevout.hash.IsNull())
1976                         {
1977                             of.evidenceInputs.push_back(txBuilder.mtx.vin.size());
1978                             txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1979                         }
1980                     }
1981                 }
1982
1983                 // add additional evidence spends
1984                 for (auto &oneEvidenceOut : additionalEvidence)
1985                 {
1986                     // use up evidence with our ID signatures first, and remove from the remainder
1987                     COptCCParams p;
1988                     CNotaryEvidence evidence;
1989                     // validated above
1990                     oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1991                     evidence = CNotaryEvidence(p.vData[0]);
1992                     for (auto &oneSig : evidence.signatures)
1993                     {
1994                         if (sigSet.count(oneSig.first))
1995                         {
1996                             sigCount++;
1997                             sigSet.erase(oneSig.first);
1998                         }
1999                     }
2000                     txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
2001                     if (!haveNeeded)
2002                     {
2003                         // until we have enough signatures to confirm, continue to add evidence to the finalization
2004                         of.evidenceInputs.push_back(txBuilder.mtx.vin.size() - 1);
2005                         haveNeeded = sigCount >= minimumNotariesConfirm;
2006                     }
2007                 }
2008
2009                 // remove when this vector is confirmed as unnecessary
2010                 for (auto &oneEvidenceOut : cleanupSpend)
2011                 {
2012                     txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
2013                 }
2014
2015                 // if we have confirmed this, we will output a new, confirmed finalization for this notarization and
2016                 // spend all prior finalizations, confirmed or unconfirmed, for prior notarizations. all prior notarizations
2017                 // behind a confirmed notarization are considered final in their current confirmed/unconfirmed state
2018                 // we include our current pending finalization as well
2019                 for (int j = idx; j >= 0; j--)
2020                 {
2021                     for (auto &oneInputDescPair : vtxFinalizations[cnd.vtx[j].first].second)
2022                     {
2023                         txBuilder.AddTransparentInput(oneInputDescPair.second.txIn.prevout, 
2024                                                       oneInputDescPair.second.scriptPubKey,
2025                                                       oneInputDescPair.second.nValue);
2026                     }
2027                 }
2028
2029                 if (!haveNeeded)
2030                 {
2031                     // should never get here
2032                     return state.Error(errorPrefix + "Internal error");
2033                 }
2034
2035                 retVal = true;
2036                 finalized = true;
2037                 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2038                 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2039
2040                 CScript finalizeScript = MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of));
2041                 txBuilder.AddTransparentOutput(finalizeScript, 0);
2042             }
2043             break;
2044         }
2045         if (finalized)
2046         {
2047             break;
2048         }
2049     }
2050     return retVal;
2051 }
2052
2053 bool CallNotary(const CRPCChainData &notarySystem, std::string command, const UniValue &params, UniValue &result, UniValue &error);
2054
2055 // look for finalized notarizations either on chain or in the mempool, which are eligible for submission
2056 // and submit them to the notary chain.
2057 std::vector<uint256> CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPCChainData &externalSystem,
2058                                                                       CValidationState &state)
2059 {
2060     // look for finalized notarizations for our notary chains in recent blocks
2061     // if we find any and are connected, submit them
2062     uint160 systemID = externalSystem.chainDefinition.GetID();
2063     std::vector<std::pair<CPBaaSNotarization, CNotaryEvidence>> notarizations;
2064
2065     {
2066         LOCK2(cs_main, mempool.cs);
2067
2068         uint32_t nHeight = chainActive.Height();
2069         uint160 finalizeConfirmedKey = 
2070             CCrossChainRPCData::GetConditionID(
2071                 CCrossChainRPCData::GetConditionID(systemID, CObjectFinalization::ObjectFinalizationNotarizationKey()), 
2072                 CObjectFinalization::ObjectFinalizationConfirmedKey());
2073
2074         std::vector<CAddressIndexDbEntry> finalizedNotarizations;
2075         std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> finalizedInMempool;
2076
2077         if ((GetAddressIndex(finalizeConfirmedKey, CScript::P2IDX, finalizedNotarizations, nHeight - 50) &&
2078             mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{finalizeConfirmedKey, CScript::P2IDX}}), finalizedInMempool)) &&
2079             (finalizedNotarizations.size() || finalizedInMempool.size()))
2080         {
2081             // get the recent finalized notarizations and submit. the daemon we submit to may early out immediately if
2082             // the notarization is already posted or in mempool on the notary system
2083             std::vector<CUTXORef> finalizedNotarizationUTXOs;
2084
2085             for (auto &oneFinalization : finalizedNotarizations)
2086             {
2087                 if (oneFinalization.first.spending)
2088                 {
2089                     continue;
2090                 }
2091                 finalizedNotarizationUTXOs.push_back(CUTXORef(oneFinalization.first.txhash, oneFinalization.first.index));
2092             }
2093             for (auto &oneFinalization : finalizedInMempool)
2094             {
2095                 if (oneFinalization.first.spending)
2096                 {
2097                     continue;
2098                 }
2099                 finalizedNotarizationUTXOs.push_back(CUTXORef(oneFinalization.first.txhash, oneFinalization.first.index));
2100             }
2101             for (auto &oneFinalization : finalizedNotarizationUTXOs)
2102             {
2103                 // now, we have a finalized notarization for the target network
2104                 // prepare an accepted notarization and submit
2105                 CTransaction finTx, nTx;
2106                 uint256 blkHash;
2107                 COptCCParams p;
2108                 CObjectFinalization of;
2109                 CPBaaSNotarization pbn;
2110                 if (!myGetTransaction(oneFinalization.hash, finTx, blkHash))
2111                 {
2112                     // set error, but continue processing
2113                     state.Error("inaccessible-transaction");
2114                 }
2115                 if (!((finTx.vout.size() > oneFinalization.n &&
2116                        finTx.vout[oneFinalization.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2117                        p.IsValid() &&
2118                        ((p.evalCode == EVAL_FINALIZE_NOTARIZATION &&
2119                          p.vData.size() &&
2120                          (of = CObjectFinalization(p.vData[0])).IsValid() &&
2121                          of.GetOutputTransaction(finTx, nTx, blkHash) &&
2122                          nTx.vout.size() > of.output.n &&
2123                          nTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2124                          p.IsValid() &&
2125                          p.evalCode == EVAL_EARNEDNOTARIZATION) ||
2126                        (!nTx.vout.size() &&
2127                         p.evalCode == EVAL_EARNEDNOTARIZATION &&
2128                         (of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, 
2129                                                   systemID, 
2130                                                   (nTx = finTx).GetHash(),
2131                                                   oneFinalization.n)).IsValid()))) &&
2132                       p.vData.size() &&
2133                       (pbn = CPBaaSNotarization(p.vData[0])).IsValid()))
2134                 {
2135                     // this is almost certainly a corrupt local database, so we will start to return errors
2136                     // rather than attempt to continue
2137                     state.Error("invalid-or-corrupt-notarization");
2138                     return std::vector<uint256>();
2139                 }
2140
2141                 // get all notarization evidence and submit the notarization + evidence
2142                 CNotaryEvidence allEvidence(CNotaryEvidence::TYPE_NOTARY_SIGNATURE), scratchEvidence(CNotaryEvidence::TYPE_NOTARY_SIGNATURE);
2143                 allEvidence.systemID = ASSETCHAINS_CHAINID;
2144                 allEvidence.output = CUTXORef(nTx.GetHash(), of.output.n);
2145
2146                 // get all inputs with evidence and add it to our evidence
2147                 for (auto &oneEvidenceIn : of.evidenceInputs)
2148                 {
2149                     CTransaction evidenceTx;
2150                     uint256 eBlkHash;
2151                     COptCCParams eP;
2152
2153                     if (!(finTx.vin.size() > oneEvidenceIn &&
2154                           myGetTransaction(finTx.vin[oneEvidenceIn].prevout.hash, evidenceTx, eBlkHash) &&
2155                           evidenceTx.vout.size() > finTx.vin[oneEvidenceIn].prevout.n &&
2156                           evidenceTx.vout[finTx.vin[oneEvidenceIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(eP) &&
2157                           eP.IsValid() &&
2158                           eP.evalCode == EVAL_NOTARY_EVIDENCE &&
2159                           eP.vData.size() &&
2160                           (scratchEvidence = CNotaryEvidence(eP.vData[0])).IsValid()))
2161                     {
2162                         state.Error("innaccessible-evidence");
2163                         return std::vector<uint256>();
2164                     }
2165                     allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
2166                     for (auto &oneSig : scratchEvidence.signatures)
2167                     {
2168                         if (oneSig.second.signatures.size())
2169                         {
2170                             allEvidence.signatures[oneSig.first].version = oneSig.second.version;
2171                             allEvidence.signatures[oneSig.first].blockHeight = oneSig.second.blockHeight;
2172                             for (auto &oneSigEntry : oneSig.second.signatures)
2173                             {
2174                                 allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
2175                             }
2176                         }
2177                     }
2178                 }
2179
2180                 // get all outputs with evidence and add
2181                 for (auto oneEvidenceOut : of.evidenceOutputs)
2182                 {
2183                     if (!(finTx.vout.size() > oneEvidenceOut &&
2184                         finTx.vout[oneEvidenceOut].scriptPubKey.IsPayToCryptoCondition(p) &&
2185                         p.IsValid() &&
2186                         (p.evalCode == EVAL_NOTARY_EVIDENCE) &&
2187                         p.vData.size() &&
2188                         (scratchEvidence = CNotaryEvidence(p.vData[0])).IsValid()))
2189                     {
2190                         state.Error("invalid-evidence");
2191                         return std::vector<uint256>();
2192                     }
2193                     allEvidence.evidence.insert(allEvidence.evidence.end(), scratchEvidence.evidence.begin(), scratchEvidence.evidence.end());
2194                     for (auto &oneSig : scratchEvidence.signatures)
2195                     {
2196                         if (oneSig.second.signatures.size())
2197                         {
2198                             allEvidence.signatures[oneSig.first].version = oneSig.second.version;
2199                             allEvidence.signatures[oneSig.first].blockHeight = oneSig.second.blockHeight;
2200                             for (auto &oneSigEntry : oneSig.second.signatures)
2201                             {
2202                                 allEvidence.signatures[oneSig.first].signatures.insert(oneSigEntry);
2203                             }
2204                         }
2205                     }
2206                 }
2207                 // The first, block one notarization requires no evidence or signatures to be valid on this hain, so it will be skipped
2208                 // here as well.
2209                 if (!allEvidence.evidence.size() && !allEvidence.signatures.size())
2210                 {
2211                     continue;
2212                 }
2213                 notarizations.push_back(std::make_pair(pbn, allEvidence));
2214             }
2215         }
2216     }
2217
2218     std::vector<uint256> retVal;
2219     // collect them holding the lock, release it, then call out
2220     for (auto &oneNotarization : notarizations)
2221     {
2222         // now, we should have enough evidence to prove
2223         // the notarization. the API call will ensure that we do
2224         UniValue params(UniValue::VARR);
2225         UniValue result, error;
2226         std::string strTxId;
2227         params.push_back(oneNotarization.first.ToUniValue());
2228         params.push_back(oneNotarization.second.ToUniValue());
2229
2230         /* for (auto &debugOut : oneNotarization.second.signatures)
2231         {
2232             printf("%s: onesig - ID: %s, signature: %s\n", __func__, EncodeDestination(debugOut.first).c_str(), debugOut.second.ToUniValue().write(1,2).c_str());
2233         }
2234         printf("%s: submitting notarization with parameters:\n%s\n%s\n", __func__, params[0].write(1,2).c_str(), params[1].write(1,2).c_str());
2235         printf("%s: initial notarization:\n%s\n", __func__, oneNotarization.first.ToUniValue().write(1,2).c_str());
2236         std::vector<unsigned char> notVec1 = ::AsVector(oneNotarization.first);
2237         CPBaaSNotarization checkNotarization(oneNotarization.first.ToUniValue());
2238         std::vector<unsigned char> notVec2 = ::AsVector(checkNotarization);
2239         printf("%s: processed notarization:\n%s\n", __func__, checkNotarization.ToUniValue().write(1,2).c_str());
2240         std::vector<unsigned char> notVec3 = ::AsVector(CPBaaSNotarization(notVec1));
2241         printf("%s: hex before univalue:\n%s\n", __func__, HexBytes(&(notVec1[0]), notVec1.size()).c_str());
2242         printf("%s: hex after univalue:\n%s\n", __func__, HexBytes(&(notVec2[0]), notVec2.size()).c_str());
2243         printf("%s: hex after reserialization:\n%s\n", __func__, HexBytes(&(notVec3[0]), notVec3.size()).c_str()); */
2244
2245         if (!CallNotary(externalSystem, "submitacceptednotarization", params, result, error) ||
2246             !error.isNull() ||
2247             (strTxId = uni_get_str(result)).empty() ||
2248             !IsHex(strTxId))
2249         {
2250             continue;
2251         }
2252         // store the transaction ID and accepted notariation to prevent redundant submits
2253         retVal.push_back(uint256S(strTxId));
2254     }
2255     return retVal;
2256 }
2257
2258 /*
2259  * Validates a notarization output spend by ensuring that the spending transaction fulfills all requirements.
2260  * to accept an earned notarization as valid on the Verus blockchain, it must prove a transaction on the alternate chain, which is 
2261  * either the original chain definition transaction, which CAN and MUST be proven ONLY in block 1, or the latest notarization transaction 
2262  * on the alternate chain that represents an accurate MMR for this chain.
2263  * In addition, any accepted notarization must fullfill the following requirements:
2264  * 1) Must prove either a PoS block from the alternate chain or a merge mined block that is owned by the submitter and in either case, 
2265  *    the block must be exactly 8 blocks behind the submitted MMR used for proof.
2266  * 2) Must prove a chain definition tx and be block 1 or asserts a previous, valid MMR for the notarizing
2267  *    chain and properly prove objects using that MMR.
2268  * 3) Must spend the main notarization thread as well as any finalization outputs of either valid or invalid prior
2269  *    notarizations, and any unspent notarization contributions for this era. May also spend other inputs.
2270  * 4) Must output:
2271  *      a) finalization output of the expected reward amount, which will be sent when finalized
2272  *      b) normal output of reward from validated/finalized input if present, 50% to recipient / 50% to block miner less miner fee this tx
2273  *      c) main notarization thread output with remaining funds, no other output or fee deduction
2274  * 
2275  */
2276 bool ValidateAcceptedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2277 {
2278     // TODO: this validates the spending transaction
2279     // check the following things:
2280     // 1. It represents a valid PoS or merge mined block on the other chain, and contains the header in the opret
2281     // 2. The MMR and proof provided for the currently asserted block can prove the provided header. The provided
2282     //    header can prove the last block referenced.
2283     // 3. This notarization is not a superset of an earlier notarization posted before it that it does not
2284     //    reference. If that is the case, it is rejected.
2285     // 4. Has all relevant inputs, including finalizes all necessary transactions, both confirmed and orphaned
2286     //printf("ValidateAcceptedNotarization\n");
2287     return true;
2288 }
2289
2290 bool IsAcceptedNotarizationInput(const CScript &scriptSig)
2291 {
2292     uint32_t ecode;
2293     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_ACCEPTEDNOTARIZATION;
2294 }
2295
2296
2297 /*
2298  * Ensures that a spend in an earned notarization of either an OpRet support transaction or summary notarization
2299  * are valid with respect to this chain. Any transaction that spends from an opret trasaction is either disconnected,
2300  * or contains the correct hashes of each object and transaction data except for the opret, which can be validated by
2301  * reconstructing the opret from the hashes on the other chain and verifying that it hashes to the same input value. This
2302  * enables full validation without copying redundant data back to its original chain.
2303  * 
2304  * In addition, each earned notarization must reference the last earned notarization with which it agrees and prove the last
2305  * accepted notarization on the alternate chain with the latest MMR. The earned notarization will not be accepted if there is
2306  * a later notarization that agrees with it already present in the alternate chain when it is submitted. 
2307  * 
2308  */
2309 bool ValidateEarnedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2310 {
2311     // this needs to validate that the block is mined or staked, that the notarization is properly formed,
2312     // cryptographically correct, and that it spends the proper finalization outputs
2313     // if the notarization causes a fork, it must include additional proof of blocks and their
2314     // power based on random block hash bits
2315     //printf("ValidateEarnedNotarization\n");
2316     return true;
2317 }
2318 bool IsEarnedNotarizationInput(const CScript &scriptSig)
2319 {
2320     // this is an output check, and is incorrect. need to change to input
2321     uint32_t ecode;
2322     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_EARNEDNOTARIZATION;
2323 }
2324
2325 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx=nullptr, uint32_t *pHeight=nullptr);
2326 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx, uint32_t *pHeight)
2327 {
2328     CTransaction _sourceTx;
2329     CTransaction &sourceTx(pSourceTx ? *pSourceTx : _sourceTx);
2330
2331     CObjectFinalization oldFinalization;
2332     uint256 blkHash;
2333     if (myGetTransaction(spendingTx.vin[nIn].prevout.hash, sourceTx, blkHash))
2334     {
2335         if (pHeight)
2336         {
2337             auto bIt = mapBlockIndex.find(blkHash);
2338             if (bIt == mapBlockIndex.end() || !bIt->second)
2339             {
2340                 *pHeight = chainActive.Height();
2341             }
2342             else
2343             {
2344                 *pHeight = bIt->second->GetHeight();
2345             }
2346         }
2347         COptCCParams p;
2348         if (sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2349             p.IsValid() && 
2350             (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT) && 
2351             p.version >= COptCCParams::VERSION_V3 &&
2352             p.vData.size() > 1)
2353         {
2354             oldFinalization = CObjectFinalization(p.vData[0]);
2355         }
2356     }
2357     return oldFinalization;
2358 }
2359
2360
2361 /*
2362  * Ensures that the finalization, either as validated or orphaned, is determined by
2363  * 10 confirmations, either of this transaction, or of an alternate transaction on the chain that we do not derive
2364  * from. If the former, then this should be asserted to be validated, otherwise, it should be asserted to be invalidated.
2365  *
2366  */
2367 bool ValidateFinalizeNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
2368 {
2369     // to validate a finalization spend, we need to validate the spender's assertion of confirmation or rejection as proven
2370
2371     // first, determine our notarization finalization protocol
2372     CTransaction sourceTx;
2373     uint32_t oldHeight;
2374     CObjectFinalization oldFinalization = GetOldFinalization(tx, nIn, &sourceTx, &oldHeight);
2375     if (!oldFinalization.IsValid())
2376     {
2377         return eval->Error("Invalid finalization output");
2378     }
2379
2380     // get currency to determine system and notarization method
2381     CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oldFinalization.currencyID);
2382     if (!curDef.IsValid())
2383     {
2384         return eval->Error("Invalid currency ID in finalization output");
2385     }
2386     uint160 SystemID = curDef.GetID();
2387
2388     // for now, auto notarization uses defined notaries. it can be updated later, while leaving those that
2389     // select notary confirm explicitly to remain on that protocol
2390     if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CONFIRM || curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2391     {
2392         // get the notarization this finalizes and its index output
2393         int32_t notaryOutNum;
2394         CTransaction notarizationTx;
2395         
2396         if (oldFinalization.output.IsOnSameTransaction())
2397         {
2398             notarizationTx = sourceTx;
2399             // output needs non-null hash below
2400             oldFinalization.output.hash = notarizationTx.GetHash();
2401         }
2402         else
2403         {
2404             uint256 blkHash;
2405             if (!oldFinalization.GetOutputTransaction(sourceTx, notarizationTx, blkHash))
2406             {
2407                 return eval->Error("notarization-transaction-not-found");
2408             }
2409         }
2410         if (notarizationTx.vout.size() <= oldFinalization.output.n)
2411         {
2412             return eval->Error("invalid-finalization");
2413         }
2414
2415         CPBaaSNotarization pbn(notarizationTx.vout[oldFinalization.output.n].scriptPubKey);
2416         if (!pbn.IsValid())
2417         {
2418             return eval->Error("invalid-notarization");
2419         }
2420
2421         // now, we have an unconfirmed, non-rejected finalization being spent by a transaction
2422         // confirm that the spender contains one finalization output either confirming or rejecting
2423         // the finalization. rejection may be implicit by confirming another, later notarization.
2424
2425         // First. make sure the oldFinalization is not referring to an earlier notarization than the 
2426         // one most recently confirmed. If so. then it can be spent by anyone.
2427         CChainNotarizationData cnd;
2428         if (!GetNotarizationData(SystemID, cnd) || !cnd.IsConfirmed())
2429         {
2430             return eval->Error("invalid-notarization");
2431         }
2432
2433         // TODO: now, validate both rejection and confirmation
2434
2435         CObjectFinalization newFinalization;
2436         int finalizationOutNum = -1;
2437         bool foundFinalization = false;
2438         for (int i = 0; i < tx.vout.size(); i++)
2439         {
2440             auto &oneOut = tx.vout[i];
2441             COptCCParams p;
2442             // we can accept only one finalization of this notarization as an output, find it and reject more than one
2443
2444             // TODO: HARDENING - ensure that the output of the finalization we are spending is either a confirmed, earlier
2445             // output or invalidated alternate to the one we are finalizing
2446             if (oneOut.scriptPubKey.IsPayToCryptoCondition(p) &&
2447                 p.IsValid() &&
2448                 p.evalCode == EVAL_FINALIZE_NOTARIZATION &&
2449                 p.vData.size() &&
2450                 (newFinalization = CObjectFinalization(p.vData[0])).IsValid())
2451             {
2452                 if (foundFinalization)
2453                 {
2454                     return eval->Error("duplicate-finalization");
2455                 }
2456                 foundFinalization = true;
2457                 finalizationOutNum = i;
2458             }
2459         }
2460
2461         if (!foundFinalization)
2462         {
2463             return eval->Error("invalid-finalization-spend");
2464         }
2465     }
2466     return true;
2467 }
2468
2469 bool IsFinalizeNotarizationInput(const CScript &scriptSig)
2470 {
2471     // this is an output check, and is incorrect. need to change to input
2472     uint32_t ecode;
2473     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_FINALIZE_NOTARIZATION;
2474 }
2475
2476 bool CObjectFinalization::GetOutputTransaction(const CTransaction &initialTx, CTransaction &tx, uint256 &blockHash) const
2477 {
2478     if (output.hash.IsNull())
2479     {
2480         tx = initialTx;
2481         return true;
2482     }
2483     else if (myGetTransaction(output.hash, tx, blockHash) && tx.vout.size() > output.n)
2484     {
2485         return true;
2486     }
2487     return false;
2488 }
2489
2490 // Sign the output object with an ID or signing authority of the ID from the wallet.
2491 CNotaryEvidence CObjectFinalization::SignConfirmed(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
2492 {
2493     CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
2494
2495     AssertLockHeld(cs_main);
2496     uint32_t nHeight = chainActive.Height();
2497
2498     CTransaction tx;
2499     uint256 blockHash;
2500     if (GetOutputTransaction(initialTx, tx, blockHash))
2501     {
2502         retVal.SignConfirmed(*pWallet, tx, signatureID, nHeight);
2503     }
2504     return retVal;
2505 }
2506
2507 CNotaryEvidence CObjectFinalization::SignRejected(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
2508 {
2509     CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
2510
2511     AssertLockHeld(cs_main);
2512     uint32_t nHeight = chainActive.Height();
2513
2514     CTransaction tx;
2515     uint256 blockHash;
2516     if (GetOutputTransaction(initialTx, tx, blockHash))
2517     {
2518         retVal.SignRejected(*pWallet, tx, signatureID, nHeight);
2519     }
2520     return retVal;
2521 }
2522
2523 // Verify that the output object of "p" is signed appropriately with the indicated signature
2524 // and that the signature is fully authorized to sign
2525 // TODO: THIS SHOULD BE UPDATED TO REFLECT USAGE, WHICH IT DOESN'T YET HAVE
2526 // SPECIFICALLY, THE currencyID, WHICH IS USED, SHOULD BE SEPARATED FROM minimum signatures required
2527 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, const COptCCParams &p, uint32_t height) const
2528 {
2529     std::set<uint160> completedSignatures;
2530     std::set<uint160> partialSignatures;
2531
2532     CCurrencyDefinition curDef;
2533     int32_t defHeight;
2534
2535     if (p.IsValid() && 
2536         p.version >= p.VERSION_V3 && 
2537         p.vData.size() &&
2538         GetCurrencyDefinition(currencyID, curDef, &defHeight) &&
2539         curDef.IsValid())
2540     {
2541         uint256 txId = output.hash.IsNull() ? initialTx.GetHash() : output.hash;
2542         std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(currencyID, CNotaryEvidence::NotarySignatureKey(), txId, output.n)};
2543         std::vector<uint256> statements;
2544
2545         // check that signature is of the hashed vData[0] data
2546         auto hw = CMMRNode<>::GetHashWriter();
2547         hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
2548         uint256 msgHash = hw.GetHash();
2549
2550         for (auto &authorizedNotary : curDef.notaries)
2551         {
2552             if (signature.signatures.count(authorizedNotary))
2553             {
2554                 // we might have a partial or complete signature by one notary here
2555                 const CIdentitySignature &oneIDSig = signature.signatures.find(authorizedNotary)->second;
2556
2557                 uint256 sigHash = oneIDSig.IdentitySignatureHash(vdxfCodes, statements, currencyID, height, authorizedNotary, "", msgHash);
2558
2559                 // get identity used to sign
2560                 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
2561                 if (signer.IsValid())
2562                 {
2563                     std::set<uint160> idAddresses;
2564                     std::set<uint160> verifiedSignatures;
2565
2566                     for (const CTxDestination &oneAddress : signer.primaryAddresses)
2567                     {
2568                         if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
2569                         {
2570                             // currently, can only check secp256k1 signatures
2571                             //return state.Error("Unsupported signature type");
2572                             return CIdentitySignature::SIGNATURE_INVALID;
2573                         }
2574                         idAddresses.insert(GetDestinationID(oneAddress));
2575                     }
2576
2577                     for (auto &oneSig : signature.signatures.find(authorizedNotary)->second.signatures)
2578                     {
2579                         CPubKey pubKey;
2580                         pubKey.RecoverCompact(sigHash, oneSig);
2581                         if (!idAddresses.count(pubKey.GetID()))
2582                         {
2583                             // invalid signature or ID
2584                             return CIdentitySignature::SIGNATURE_INVALID;
2585                         }
2586                         verifiedSignatures.insert(pubKey.GetID());
2587                     }
2588                     if (verifiedSignatures.size() >= signer.minSigs)
2589                     {
2590                         completedSignatures.insert(authorizedNotary);
2591                     }
2592                     else
2593                     {
2594                         partialSignatures.insert(authorizedNotary);
2595                     }
2596                 }
2597                 else
2598                 {
2599                     // invalid signing identity in signature
2600                     return CIdentitySignature::SIGNATURE_INVALID;
2601                 }
2602             }
2603         }
2604         // all IDs in the signature must have been found and either partial or complete signatures
2605         if (partialSignatures.size() + completedSignatures.size() < signature.signatures.size())
2606         {
2607             return CIdentitySignature::SIGNATURE_INVALID;
2608         }
2609
2610         if (completedSignatures.size() >= curDef.minNotariesConfirm)
2611         {
2612             return CIdentitySignature::SIGNATURE_COMPLETE;
2613         }
2614         else if (completedSignatures.size() || partialSignatures.size())
2615         {
2616             return CIdentitySignature::SIGNATURE_PARTIAL;
2617         }
2618     }
2619     // missing or invalid
2620     return CIdentitySignature::SIGNATURE_INVALID;
2621 }
2622
2623 // Verify that the output object is signed with an authorized signing authority
2624 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, uint32_t height) const
2625 {
2626     // now, get the output to check and check to ensure the signature is good
2627     CTransaction tx;
2628     uint256 blkHash;
2629     COptCCParams p;
2630     if (GetOutputTransaction(initialTx, tx, blkHash) &&
2631         tx.vout.size() > output.n &&
2632         tx.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2633         p.IsValid() &&
2634         p.vData.size())
2635     {
2636         return VerifyOutputSignature(initialTx, signature, p, height);
2637     }
2638     else
2639     {
2640         return CIdentitySignature::SIGNATURE_INVALID;
2641     }
2642 }
2643
2644 // this ensures that the signature is, in fact, both authorized to sign, and also a
2645 // valid signature of the specified output object. if so, this is accepted and
2646 // results in a valid index entry as a confirmation of the notary signature
2647 // all signatures must be from a valid notary, or this returns false and should be
2648 // considered invalid.
2649 // returns the number of valid, unique notary signatures, enabling a single output
2650 // to be sufficient to authorize.
2651 bool ValidateNotarizationEvidence(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height, int &confirmedCount, bool &provenFalse)
2652 {
2653     // we MUST know that the cs_main lock is held. since it can be held on the validation thread while smart transactions
2654     // execute, we cannot take it or assert here
2655
2656     CNotaryEvidence notarySig;
2657     COptCCParams p;
2658     CCurrencyDefinition curDef;
2659
2660     confirmedCount = 0;         // if a unit of evidence, whether signature or otherwise, is validated as confirming
2661     provenFalse = false;        // if the notarization is proven false
2662
2663     if (tx.vout[outNum].scriptPubKey.IsPayToCryptoCondition(p) && 
2664         p.IsValid() && 
2665         p.version >= p.VERSION_V3 && 
2666         p.vData.size() && 
2667         (notarySig = CNotaryEvidence(p.vData[0])).IsValid() &&
2668         (curDef = ConnectedChains.GetCachedCurrency(notarySig.systemID)).IsValid())
2669     {
2670         // now, get the output to check and ensure the signature is good
2671         CObjectFinalization of;
2672         CPBaaSNotarization notarization;
2673         uint256 notarizationTxId;
2674         CTransaction nTx;
2675         uint256 blkHash;
2676         if (notarySig.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(notarySig.output.hash, nTx, blkHash) &&
2677             nTx.vout.size() > notarySig.output.n &&
2678             nTx.vout[notarySig.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2679             p.IsValid() &&
2680             (p.evalCode == EVAL_FINALIZE_NOTARIZATION) &&
2681             p.vData.size() &&
2682             (of = CObjectFinalization(p.vData[0])).IsValid() &&
2683             of.IsNotarizationFinalization() &&
2684             of.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(of.output.hash, nTx, blkHash) &&
2685             !(notarizationTxId = nTx.GetHash()).IsNull() &&
2686             nTx.vout.size() > of.output.n &&
2687             nTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
2688             p.IsValid() &&
2689             (p.evalCode == EVAL_EARNEDNOTARIZATION || p.evalCode == EVAL_ACCEPTEDNOTARIZATION) &&
2690             p.vData.size() &&
2691             (notarization = CPBaaSNotarization(p.vData[0])).IsValid() &&
2692             notarization.proofRoots.count(notarySig.systemID))
2693         {
2694             // signature is relative only to the notarization, not the finalization
2695             // that way, the information we put into the vdxfCodes have some meaning beyond
2696             // the blockchain on which it was signed, and we do not have to carry the
2697             // finalizatoin mechanism cross-chain.
2698             std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(notarySig.systemID, 
2699                                                                                  CNotaryEvidence::NotarySignatureKey(), 
2700                                                                                  notarizationTxId, 
2701                                                                                  of.output.n)};
2702             std::vector<uint256> statements;
2703
2704             // check that signature is of the hashed vData[0] data
2705             auto hw = CMMRNode<>::GetHashWriter();
2706             hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
2707             uint256 msgHash = hw.GetHash();
2708
2709             for (auto &authorizedNotary : curDef.notaries)
2710             {
2711                 std::map<CIdentityID, CIdentitySignature>::iterator sigIt = notarySig.signatures.find(authorizedNotary);
2712                 if (sigIt != notarySig.signatures.end())
2713                 {
2714                     // get identity used to sign
2715                     CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
2716                     uint256 sigHash = sigIt->second.IdentitySignatureHash(vdxfCodes, statements, of.currencyID, height, authorizedNotary, "", msgHash);
2717
2718                     if (signer.IsValid())
2719                     {
2720                         std::set<uint160> idAddresses;
2721                         std::set<uint160> verifiedSignatures;
2722                         
2723                         for (const CTxDestination &oneAddress : signer.primaryAddresses)
2724                         {
2725                             if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
2726                             {
2727                                 // currently, can only check secp256k1 signatures
2728                                 return state.Error("Unsupported signature type");
2729                             }
2730                             idAddresses.insert(GetDestinationID(oneAddress));
2731                         }
2732
2733                         for (auto &oneSig : notarySig.signatures[authorizedNotary].signatures)
2734                         {
2735                             CPubKey pubKey;
2736                             pubKey.RecoverCompact(sigHash, oneSig);
2737                             uint160 pkID = pubKey.GetID();
2738                             if (!idAddresses.count(pkID))
2739                             {
2740                                 return state.Error("Mismatched pubkey and ID in signature");
2741                             }
2742                             if (verifiedSignatures.count(pkID))
2743                             {
2744                                 return state.Error("Duplicate key use in ID signature");
2745                             }
2746                             verifiedSignatures.insert(pkID);
2747                         }
2748                         if (verifiedSignatures.size() >= signer.minSigs)
2749                         {
2750                             confirmedCount++;
2751                         }
2752                         else
2753                         {
2754                             return state.Error("Insufficient signatures on behalf of ID: " + signer.name);
2755                         }
2756                     }
2757                     else
2758                     {
2759                         return state.Error("Invalid notary identity or corrupt local state");
2760                     }
2761                 }
2762                 else
2763                 {
2764                     return state.Error("Unauthorized notary");
2765                 }
2766             }
2767         }
2768         else
2769         {
2770             return state.Error("Invalid notarization reference");
2771         }
2772     }
2773     else
2774     {
2775         return state.Error("Invalid or non-evidence output");
2776     }
2777     
2778     if (!confirmedCount)
2779     {
2780         return state.Error("No evidence present");
2781     }
2782     else
2783     {
2784         return true;
2785     }
2786 }
2787
This page took 0.197522 seconds and 4 git commands to generate.