]> Git Repo - VerusCoin.git/blob - src/pbaas/notarization.cpp
VDXF protocol fixes
[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"));
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     CIdentitySignature::ESignatureVerification sigResult = idSignature.NewSignature(keyAndIdentity.second, 
97                                 std::vector<uint160>({NotaryConfirmedKey()}), 
98                                 std::vector<uint256>(), 
99                                 systemID, 
100                                 height, 
101                                 "", 
102                                 objHash, 
103                                 &keyStore);
104
105     if (sigResult != CIdentitySignature::SIGNATURE_INVALID)
106     {
107         signatures.insert(std::make_pair(signWithID, idSignature));
108     }
109     return sigResult;
110 }
111
112 CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height)
113 {
114     if (signatures.size() && confirmed)
115     {
116         LogPrintf("%sAttempting to change existing signature from confirmed to rejected\n", __func__);
117         return CIdentitySignature::SIGNATURE_INVALID;
118     }
119
120     std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
121     if (!keyStore.GetIdentity(signWithID, keyAndIdentity, height) && keyAndIdentity.first.CanSign())
122     {
123         LogPrintf("%s: Attempting to sign with notary ID that this wallet does not control\n", __func__);
124         return CIdentitySignature::SIGNATURE_INVALID;
125     }
126
127     COptCCParams p;
128
129     if (txToConfirm.GetHash() != output.hash ||
130         txToConfirm.vout.size() <= output.n ||
131         !txToConfirm.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) ||
132         !p.vData.size() ||
133         !p.vData[0].size() ||
134         p.evalCode == EVAL_NONE)
135     {
136         LogPrintf("%s: Attempting to sign an invalid or incompatible object\n", __func__);
137         return CIdentitySignature::SIGNATURE_INVALID;
138     }
139
140     // write the object to the hash writer without a vector length prefix
141     auto hw = CMMRNode<>::GetHashWriter();
142     uint256 objHash = hw.write((const char *)&(p.vData[0][0]), p.vData[0].size()).GetHash();
143
144     CIdentitySignature idSignature;
145
146     CIdentitySignature::ESignatureVerification sigResult = idSignature.NewSignature(keyAndIdentity.second, 
147                                 std::vector<uint160>({NotaryRejectedKey()}), 
148                                 std::vector<uint256>(), 
149                                 systemID, 
150                                 height, 
151                                 "", 
152                                 objHash, 
153                                 &keyStore);
154
155     if (sigResult != CIdentitySignature::SIGNATURE_INVALID)
156     {
157         signatures.insert(std::make_pair(signWithID, idSignature));
158     }
159     return sigResult;
160 }
161
162 CPBaaSNotarization::CPBaaSNotarization(const CScript &scriptPubKey) :
163                     nVersion(VERSION_INVALID),
164                     flags(0),
165                     notarizationHeight(0),
166                     prevHeight(0)
167 {
168     COptCCParams p;
169     if (scriptPubKey.IsPayToCryptoCondition(p) && 
170         p.IsValid() &&
171         (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
172         p.vData.size())
173     {
174         ::FromVector(p.vData[0], *this);
175     }
176 }
177
178 CPBaaSNotarization::CPBaaSNotarization(const CTransaction &tx, int32_t *pOutIdx) :
179                     nVersion(VERSION_INVALID),
180                     flags(0),
181                     notarizationHeight(0),
182                     prevHeight(0)
183 {
184     // the PBaaS notarization itself is a combination of proper inputs, one output, and
185     // a sequence of opret chain objects as proof of the output values on the chain to which the
186     // notarization refers, the opret can be reconstructed from chain data in order to validate
187     // the txid of a transaction that does not contain the opret itself
188
189     int32_t _outIdx;
190     int32_t &outIdx = pOutIdx ? *pOutIdx : _outIdx;
191     
192     // a notarization must have notarization output that spends to the address indicated by the 
193     // ChainID, an opret, that there is only one, and that it can be properly decoded to a notarization 
194     // output, whether or not validate is true
195     bool found = false;
196     for (int i = 0; i < tx.vout.size(); i++)
197     {
198         COptCCParams p;
199         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && 
200             p.IsValid() &&
201             (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
202             p.vData.size())
203         {
204             if (found)
205             {
206                 nVersion = VERSION_INVALID;
207                 proofRoots.clear();
208                 break;
209             }
210             else
211             {
212                 found = true;
213                 outIdx = i;
214                 ::FromVector(p.vData[0], *this);
215             }
216         }
217     }
218 }
219
220 uint160 ValidateCurrencyName(std::string currencyStr, bool ensureCurrencyValid=false, CCurrencyDefinition *pCurrencyDef=NULL);
221
222 CPBaaSNotarization::CPBaaSNotarization(const UniValue &obj)
223 {
224     nVersion = (uint32_t)uni_get_int(find_value(obj, "version"));
225     flags = FLAGS_NONE;
226     SetDefinitionNotarization(uni_get_bool(find_value(obj, "isdefinition")));
227     SetBlockOneNotarization(uni_get_bool(find_value(obj, "isblockonenotarization")));
228     SetPreLaunch(uni_get_bool(find_value(obj, "prelaunch")));
229     SetLaunchCleared(uni_get_bool(find_value(obj, "launchclear")));
230     SetRefunding(uni_get_bool(find_value(obj, "refunding")));
231     SetLaunchConfirmed(uni_get_bool(find_value(obj, "launchconfirmed")));
232
233     currencyID = ValidateCurrencyName(uni_get_str(find_value(obj, "currencyid")));
234     if (currencyID.IsNull())
235     {
236         nVersion = VERSION_INVALID;
237         return;
238     }
239
240     UniValue transferID = find_value(obj, "proposer");
241     if (transferID.isObject())
242     {
243         proposer = CTransferDestination(transferID);
244     }
245
246     notarizationHeight = uni_get_int(find_value(obj, "notarizationheight"));
247     currencyState = CCoinbaseCurrencyState(find_value(obj, "currencystate"));
248     prevNotarization = CUTXORef(uint256S(uni_get_str(find_value(obj, "hashprevnotarizationobject"))), (uint32_t)uni_get_int(find_value(obj, "prevnotarizationout")));
249     hashPrevNotarization = uint256S(uni_get_str(find_value(obj, "hashprevnotarizationobject")));
250     prevHeight = uni_get_int(find_value(obj, "prevheight"));
251
252     auto curStateArr = find_value(obj, "prevheight");
253     auto proofRootArr = find_value(obj, "prevheight");
254     auto nodesUni = find_value(obj, "nodes");
255
256     if (curStateArr.isArray())
257     {
258         for (int i = 0; i < curStateArr.size(); i++)
259         {
260             std::vector<std::string> keys = curStateArr[i].getKeys();
261             std::vector<UniValue> values = curStateArr[i].getValues();
262             if (keys.size() != 1 or values.size() != 1)
263             {
264                 nVersion = VERSION_INVALID;
265                 return;
266             }
267             currencyStates.insert(std::make_pair(GetDestinationID(DecodeDestination(keys[0])), CCoinbaseCurrencyState(values[0])));
268         }
269     }
270
271     if (proofRootArr.isArray())
272     {
273         for (int i = 0; i < proofRootArr.size(); i++)
274         {
275             std::vector<std::string> keys = proofRootArr[i].getKeys();
276             std::vector<UniValue> values = proofRootArr[i].getValues();
277             if (keys.size() != 1 or values.size() != 1)
278             {
279                 nVersion = VERSION_INVALID;
280                 return;
281             }
282             proofRoots.insert(std::make_pair(GetDestinationID(DecodeDestination(keys[0])), CProofRoot(values[0])));
283         }
284     }
285
286     if (nodesUni.isArray())
287     {
288         vector<UniValue> nodeVec = nodesUni.getValues();
289         for (auto node : nodeVec)
290         {
291             nodes.push_back(CNodeData(uni_get_str(find_value(node, "networkaddress")), uni_get_str(find_value(node, "nodeidentity"))));
292         }
293     }
294 }
295
296 CProofRoot CProofRoot::GetProofRoot(uint32_t blockHeight)
297 {
298     if (blockHeight > chainActive.Height())
299     {
300         return CProofRoot();
301     }
302     auto mmv = chainActive.GetMMV();
303     mmv.resize(blockHeight);
304     return CProofRoot(ASSETCHAINS_CHAINID, 
305                       blockHeight, 
306                       mmv.GetRoot(), 
307                       chainActive[blockHeight]->GetBlockHash(), 
308                       chainActive[blockHeight]->chainPower.CompactChainPower());
309 }
310
311 bool CPBaaSNotarization::GetLastNotarization(const uint160 &currencyID,
312                                              uint32_t eCode, 
313                                              int32_t startHeight,
314                                              int32_t endHeight,
315                                              uint256 *txIDOut,
316                                              CTransaction *txOut)
317 {
318     CPBaaSNotarization notarization;
319     std::vector<CAddressIndexDbEntry> notarizationIndex;
320     // get the last notarization in the indicated height for this currency, which is valid by definition for a token
321     if (GetAddressIndex(CCrossChainRPCData::GetConditionID(currencyID, eCode), CScript::P2IDX, notarizationIndex, startHeight, endHeight))
322     {
323         // filter out all transactions that do not spend from the notarization thread, or originate as the
324         // chain definition
325         for (auto it = notarizationIndex.rbegin(); it != notarizationIndex.rend(); it++)
326         {
327             // first unspent notarization that is valid is the one we want, skip spending
328             if (it->first.spending)
329             {
330                 continue;
331             }
332             LOCK(mempool.cs);
333             CTransaction oneTx;
334             uint256 blkHash;
335             if (myGetTransaction(it->first.txhash, oneTx, blkHash))
336             {
337                 if ((notarization = CPBaaSNotarization(oneTx.vout[it->first.index].scriptPubKey)).IsValid())
338                 {
339                     *this = notarization;
340                     if (txIDOut)
341                     {
342                         *txIDOut = it->first.txhash;
343                     }
344                     if (txOut)
345                     {
346                         *txOut = oneTx;
347                     }
348                     break;
349                 }
350             }
351             else
352             {
353                 LogPrintf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
354                 printf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
355                 continue;
356             }
357         }
358     }
359     return notarization.IsValid();
360 }
361
362 bool CPBaaSNotarization::GetLastUnspentNotarization(const uint160 &currencyID,
363                                                     uint32_t eCode, 
364                                                     uint256 &txIDOut,
365                                                     int32_t &txOutNum,
366                                                     CTransaction *txOut)
367 {
368     CPBaaSNotarization notarization;
369     std::vector<CAddressUnspentDbEntry> notarizationIndex;
370     // get the last notarization in the indicated height for this currency, which is valid by definition for a token
371     if (GetAddressUnspent(CCrossChainRPCData::GetConditionID(currencyID, eCode), CScript::P2IDX, notarizationIndex))
372     {
373         // first valid, unspent notarization found is the one we return
374         for (auto it = notarizationIndex.rbegin(); it != notarizationIndex.rend(); it++)
375         {
376             LOCK(mempool.cs);
377             CTransaction oneTx;
378             uint256 blkHash;
379             if (myGetTransaction(it->first.txhash, oneTx, blkHash))
380             {
381                 if ((notarization = CPBaaSNotarization(oneTx.vout[it->first.index].scriptPubKey)).IsValid())
382                 {
383                     *this = notarization;
384                     txIDOut = it->first.txhash;
385                     txOutNum = it->first.index;
386                     if (txOut)
387                     {
388                         *txOut = oneTx;
389                     }
390                     break;
391                 }
392             }
393             else
394             {
395                 LogPrintf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
396                 printf("%s: error transaction %s not found, may need reindexing\n", __func__, it->first.txhash.GetHex().c_str());
397                 continue;
398             }
399         }
400     }
401     return notarization.IsValid();
402 }
403
404 bool CPBaaSNotarization::NextNotarizationInfo(const CCurrencyDefinition &sourceSystem, 
405                                               const CCurrencyDefinition &destCurrency, 
406                                               uint32_t lastExportHeight, 
407                                               uint32_t currentHeight, 
408                                               std::vector<CReserveTransfer> &exportTransfers,       // both in and out. this may refund conversions
409                                               uint256 &transferHash,
410                                               CPBaaSNotarization &newNotarization,
411                                               std::vector<CTxOut> &importOutputs,
412                                               CCurrencyValueMap &importedCurrency,
413                                               CCurrencyValueMap &gatewayDepositsUsed,
414                                               CCurrencyValueMap &spentCurrencyOut) const
415 {
416     uint160 sourceSystemID = sourceSystem.GetID();
417
418     newNotarization = *this;
419     newNotarization.prevHeight = newNotarization.notarizationHeight;
420     newNotarization.notarizationHeight = currentHeight;
421
422     auto hw = CMMRNode<>::GetHashWriter();
423     hw << *this;
424     newNotarization.hashPrevNotarization = hw.GetHash();
425
426     // if already refunding, numbers don't change
427     if (currencyState.IsRefunding())
428     {
429         return true;
430     }
431
432     hw = CMMRNode<>::GetHashWriter();
433
434     for (int i = 0; i < exportTransfers.size(); i++)
435     {
436         CReserveTransfer &reserveTransfer = exportTransfers[i];
437
438         // add the pre-mutation reserve transfer to the hash
439         hw << reserveTransfer;
440
441         // ensure that any pre-conversions or conversions are all valid, based on mined height and
442         // maximum pre-conversions
443         if (reserveTransfer.IsPreConversion())
444         {
445             if (lastExportHeight >= destCurrency.startBlock)
446             {
447                 //printf("%s: Invalid pre-conversion, mined after start block\n", __func__);
448                 LogPrintf("%s: Invalid pre-conversion, mined after start block\n", __func__);
449                 reserveTransfer = reserveTransfer.GetRefundTransfer();
450             }
451             else
452             {
453                 // check if it exceeds pre-conversion maximums, and refund if so
454                 std::map<uint160, int32_t> currencyMap = destCurrency.GetCurrenciesMap();
455                 CCurrencyValueMap fees = reserveTransfer.CalculateFee(reserveTransfer.flags, reserveTransfer.FirstValue());
456                 CCurrencyValueMap newReserveIn = CCurrencyValueMap(std::vector<uint160>({reserveTransfer.FirstCurrency()}), 
457                                                                    std::vector<int64_t>({reserveTransfer.FirstValue() - fees.valueMap[reserveTransfer.FirstCurrency()]}));
458                 CCurrencyValueMap newTotalReserves = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserves) + newReserveIn;
459                 if (newTotalReserves <= CCurrencyValueMap(destCurrency.currencies, destCurrency.maxPreconvert))
460                 {
461                     newNotarization.currencyState.reserveIn = 
462                         (CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserveIn) + newReserveIn).AsCurrencyVector(destCurrency.currencies);
463                     newNotarization.currencyState.reserves = newTotalReserves.AsCurrencyVector(destCurrency.currencies);
464                 }
465                 else
466                 {
467                     LogPrintf("%s: refunding pre-conversion over maximum\n", __func__);
468                     reserveTransfer = reserveTransfer.GetRefundTransfer();
469                 }
470             }
471         }
472         else if (reserveTransfer.IsConversion())
473         {
474             if (lastExportHeight < destCurrency.startBlock)
475             {
476                 //printf("%s: Invalid conversion, mined before start block\n", __func__);
477                 LogPrintf("%s: Invalid conversion, mined before start block\n", __func__);
478                 reserveTransfer = reserveTransfer.GetRefundTransfer();
479             }
480         }
481     }
482
483     transferHash = hw.GetHash();
484
485     CReserveTransactionDescriptor rtxd;
486
487     // if this is the clear launch notarization after start, make the notarization and determine if we should launch or refund
488     if (destCurrency.launchSystemID == sourceSystemID && lastExportHeight < (destCurrency.startBlock - 1))
489     {
490         // the rest is already updated
491         return true;
492     }
493     else if (destCurrency.launchSystemID == sourceSystemID && lastExportHeight == (destCurrency.startBlock - 1))
494     {
495         bool refunding = false;
496
497         // check if the chain is qualified for a refund
498         CCurrencyValueMap minPreMap, fees;
499         CCurrencyValueMap preConvertedMap = CCurrencyValueMap(destCurrency.currencies, newNotarization.currencyState.reserves).CanonicalMap();
500
501         if (destCurrency.minPreconvert.size() && destCurrency.minPreconvert.size() == destCurrency.currencies.size())
502         {
503             minPreMap = CCurrencyValueMap(destCurrency.currencies, destCurrency.minPreconvert).CanonicalMap();
504         }
505
506         if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
507         {
508             // we force the supply to zero
509             // in any case where there was less than minimum participation,
510             // the result of the supply cannot be zero, enabling us to easily determine that this
511             // represents a failed launch
512             newNotarization.currencyState.supply = 0;
513             newNotarization.currencyState.SetRefunding(true);
514             refunding = true;
515         }
516         else
517         {
518             newNotarization.currencyState.SetLaunchClear();
519             newNotarization.currencyState.SetLaunchConfirmed();
520         }
521         CCurrencyDefinition destSystem = ConnectedChains.GetCachedCurrency(destCurrency.systemID);
522         return rtxd.AddReserveTransferImportOutputs(sourceSystem,
523                                                     destSystem,
524                                                     destCurrency, 
525                                                     newNotarization.currencyState, 
526                                                     exportTransfers, 
527                                                     importOutputs, 
528                                                     importedCurrency,
529                                                     gatewayDepositsUsed, 
530                                                     spentCurrencyOut,
531                                                     &newNotarization.currencyState);
532     }
533     else if (lastExportHeight >= destCurrency.startBlock)
534     {
535         // calculate new state from processing all transfers
536         // we are not refunding, and it is possible that we also have
537         // normal conversions in addition to pre-conversions. add any conversions that may 
538         // be present into the new currency state
539         bool isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem, 
540                                                                   ConnectedChains.ThisChain(),
541                                                                   destCurrency, 
542                                                                   currencyState, 
543                                                                   exportTransfers, 
544                                                                   importOutputs, 
545                                                                   importedCurrency,
546                                                                   gatewayDepositsUsed, 
547                                                                   spentCurrencyOut,
548                                                                   &newNotarization.currencyState);
549         if (isValidExport && destCurrency.IsFractional())
550         {
551             // we want the new price and the old state as a starting point to ensure no rounding error impact
552             // on reserves
553             importedCurrency = CCurrencyValueMap();
554             gatewayDepositsUsed = CCurrencyValueMap();
555             CCoinbaseCurrencyState tempCurState = currencyState;
556             tempCurState.conversionPrice = newNotarization.currencyState.conversionPrice;
557             tempCurState.viaConversionPrice = newNotarization.currencyState.viaConversionPrice;
558             importOutputs.resize(0);
559             rtxd = CReserveTransactionDescriptor();
560             isValidExport = rtxd.AddReserveTransferImportOutputs(sourceSystem, 
561                                                                  ConnectedChains.ThisChain(),
562                                                                  destCurrency, 
563                                                                  tempCurState, 
564                                                                  exportTransfers, 
565                                                                  importOutputs, 
566                                                                  importedCurrency,
567                                                                  gatewayDepositsUsed, 
568                                                                  spentCurrencyOut,
569                                                                  &newNotarization.currencyState);
570         }
571         if (!isValidExport)
572         {
573             LogPrintf("%s: invalid export\n", __func__);
574             return false;
575         }
576         return true;
577     }
578
579     // based on the last notarization and existing
580     return false;
581 }
582
583 CObjectFinalization::CObjectFinalization(const CTransaction &tx, uint32_t *pEcode, int32_t *pFinalizationOutNum)
584 {
585     uint32_t _ecode;
586     uint32_t &ecode = pEcode ? *pEcode : _ecode;
587     int32_t _finalizeOutNum;
588     int32_t &finalizeOutNum = pFinalizationOutNum ? *pFinalizationOutNum : _finalizeOutNum;
589     finalizeOutNum = -1;
590     for (int i = 0; i < tx.vout.size(); i++)
591     {
592         COptCCParams p;
593         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
594         {
595             if (p.evalCode == EVAL_FINALIZE_NOTARIZATION || p.evalCode == EVAL_FINALIZE_EXPORT)
596             {
597                 if (finalizeOutNum != -1)
598                 {
599                     this->version = VERSION_INVALID;
600                     finalizeOutNum = -1;
601                     break;
602                 }
603                 else
604                 {
605                     finalizeOutNum = i;
606                     ecode = p.evalCode;
607                 }
608             }
609         }
610     }
611 }
612
613 CChainNotarizationData::CChainNotarizationData(UniValue &obj)
614 {
615     version = (uint32_t)uni_get_int(find_value(obj, "version"));
616     UniValue vtxUni = find_value(obj, "vtx");
617     if (vtxUni.isArray())
618     {
619         vector<UniValue> vvtx = vtxUni.getValues();
620         for (auto o : vvtx)
621         {
622             vtx.push_back(make_pair(uint256S(uni_get_str(find_value(o, "txid"))), CPBaaSNotarization(find_value(o, "notarization"))));
623         }
624     }
625
626     lastConfirmed = (uint32_t)uni_get_int(find_value(obj, "lastconfirmed"));
627     UniValue forksUni = find_value(obj, "forks");
628     if (forksUni.isArray())
629     {
630         vector<UniValue> forksVec = forksUni.getValues();
631         for (auto fv : forksVec)
632         {
633             if (fv.isArray())
634             {
635                 forks.push_back(vector<int32_t>());
636                 vector<UniValue> forkVec = fv.getValues();
637                 for (auto fidx : forkVec)
638                 {
639                     forks.back().push_back(uni_get_int(fidx));
640                 }
641             }
642         }
643     }
644
645     bestChain = (uint32_t)uni_get_int(find_value(obj, "bestchain"));
646 }
647
648 UniValue CChainNotarizationData::ToUniValue() const
649 {
650     UniValue obj(UniValue::VOBJ);
651     obj.push_back(Pair("version", (int32_t)version));
652     UniValue notarizations(UniValue::VARR);
653     for (int64_t i = 0; i < vtx.size(); i++)
654     {
655         UniValue notarization(UniValue::VOBJ);
656         notarization.push_back(Pair("index", i));
657         notarization.push_back(Pair("txid", vtx[i].first.hash.GetHex()));
658         notarization.push_back(Pair("vout", (int32_t)vtx[i].first.n));
659         notarization.push_back(Pair("notarization", vtx[i].second.ToUniValue()));
660         notarizations.push_back(notarization);
661     }
662     obj.push_back(Pair("notarizations", notarizations));
663     UniValue Forks(UniValue::VARR);
664     for (int32_t i = 0; i < forks.size(); i++)
665     {
666         UniValue Fork(UniValue::VARR);
667         for (int32_t j = 0; j < forks[i].size(); j++)
668         {
669             Fork.push_back(forks[i][j]);
670         }
671         Forks.push_back(Fork);
672     }
673     obj.push_back(Pair("forks", Forks));
674     if (IsConfirmed())
675     {
676         obj.push_back(Pair("lastconfirmedheight", (int32_t)vtx[lastConfirmed].second.notarizationHeight));
677     }
678     obj.push_back(Pair("lastconfirmed", lastConfirmed));
679     obj.push_back(Pair("bestchain", bestChain));
680     return obj;
681 }
682
683 bool CPBaaSNotarization::CreateAcceptedNotarization(const CCurrencyDefinition &externalSystem,
684                                                     const CPBaaSNotarization &earnedNotarization,
685                                                     const CNotaryEvidence &notaryEvidence,
686                                                     CValidationState &state,
687                                                     TransactionBuilder &txBuilder)
688 {
689     std::string errorPrefix(strprintf("%s: ", __func__));
690     std::set<CIdentityID> notaries;
691
692     // now, verify the evidence. accepted notarizations for another system must have at least one
693     // valid piece of evidence, which currently means at least one notary signature
694     if (!notaryEvidence.signatures.size())
695     {
696         return state.Error(errorPrefix + "insufficient notary evidence required to accept notarization");
697     }
698     for (auto &oneSigID : externalSystem.notaries)
699     {
700         notaries.insert(oneSigID);
701     }
702
703     LOCK(cs_main);
704
705     // create an accepted notarization based on the cross-chain notarization provided
706     CPBaaSNotarization newNotarization = earnedNotarization;
707
708     // this should be mirrored for us to continue, if it can't be, it is invalid
709     if (earnedNotarization.IsMirror() || !newNotarization.SetMirror())
710     {
711         return state.Error(errorPrefix + "invalid earned notarization");
712     }
713
714     uint160 SystemID = externalSystem.GetID();
715     uint32_t height = chainActive.Height();
716     CProofRoot ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID];
717
718     CChainNotarizationData cnd;
719     std::vector<std::pair<CTransaction, uint256>> txes;
720     if (!GetNotarizationData(SystemID, cnd, &txes))
721     {
722         return state.Error(errorPrefix + "cannot locate notarization history");
723     }
724
725     // any notarization submitted must include a proof root of this chain that is later than the last confirmed
726     // notarization
727     if (!cnd.IsConfirmed() || 
728         !cnd.vtx[cnd.lastConfirmed].second.proofRoots.count(ASSETCHAINS_CHAINID) || 
729         ourRoot.rootHeight <= cnd.vtx[cnd.lastConfirmed].second.proofRoots.find(ASSETCHAINS_CHAINID)->second.rootHeight)
730     {
731         return state.Error(errorPrefix + "earned notarization proof root is not later than prior confirmed for this chain");
732     }
733
734     auto hw = CMMRNode<>::GetHashWriter();
735     hw << earnedNotarization;
736     uint256 objHash = hw.GetHash();
737
738     for (auto &oneSig : notaryEvidence.signatures)
739     {
740         if (!notaries.count(oneSig.first))
741         {
742             return state.Error(errorPrefix + "unauthorized notary signature");
743         }
744         CIdentity sigIdentity = CIdentity::LookupIdentity(oneSig.first);
745         if (!sigIdentity.IsValidUnrevoked())
746         {
747             return state.Error(errorPrefix + "invalid notary identity");
748         }
749         // we currently require accepted notarizations to be completely authorized by notaries
750         if (oneSig.second.CheckSignature(sigIdentity,
751                                          std::vector<uint160>({notaryEvidence.NotaryConfirmedKey()}), 
752                                          std::vector<uint256>(), 
753                                          SystemID, 
754                                          "", 
755                                          objHash) != oneSig.second.SIGNATURE_COMPLETE)
756         {
757             return state.Error(errorPrefix + "invalid or incomplete notary signature");
758         }
759     }
760
761     auto mmv = chainActive.GetMMV();
762     mmv.resize(ourRoot.rootHeight);
763
764     // we only create accepted notarizations for notarizations that are earned for this chain on another system
765     // currently, we support ethereum and PBaaS types.
766     if (!newNotarization.proofRoots.count(SystemID) ||
767         !newNotarization.proofRoots.count(ASSETCHAINS_CHAINID) ||
768         !(ourRoot = newNotarization.proofRoots[ASSETCHAINS_CHAINID]).IsValid() ||
769         ourRoot.rootHeight > height ||
770         ourRoot.blockHash != chainActive[ourRoot.rootHeight]->GetBlockHash() ||
771         ourRoot.stateRoot != mmv.GetRoot() ||
772         (ourRoot.type != ourRoot.TYPE_PBAAS && ourRoot.type != ourRoot.TYPE_ETHEREUM))
773     {
774         return state.Error(errorPrefix + "can only create accepted notarization from notarization with valid proof root of this chain");
775     }
776
777     // ensure that the data present is valid, as of the height
778     CCoinbaseCurrencyState oldCurState = ConnectedChains.GetCurrencyState(ASSETCHAINS_CHAINID, ourRoot.rootHeight);
779     if (!oldCurState.IsValid() ||
780         ::GetHash(oldCurState) != ::GetHash(earnedNotarization.currencyState))
781     {
782         return state.Error(errorPrefix + "currecy state is invalid in accepted notarization. is:\n" + 
783                                             newNotarization.currencyState.ToUniValue().write(1,2) + 
784                                             "\nshould be:\n" + 
785                                             oldCurState.ToUniValue().write(1,2) + "\n");
786     }
787
788     // ensure that all locally provable info is valid as of our root height
789     // and determine if the new notarization should be already finalized or not
790     for (auto &oneCur : newNotarization.currencyStates)
791     {
792         if (oneCur.first == SystemID)
793         {
794             continue;
795         }
796         else if (oneCur.first != ASSETCHAINS_CHAINID)
797         {
798             // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
799             CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneCur.first);
800             // we must have all currencies
801             if (!curDef.IsValid())
802             {
803                 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
804             }
805             // if the currency is not from this chain, we cannot validate it
806             if (curDef.systemID != ASSETCHAINS_CHAINID)
807             {
808                 continue;
809             }
810             // ensure that the data present is valid, as of the height
811             oldCurState = ConnectedChains.GetCurrencyState(oneCur.first, ourRoot.rootHeight);
812             if (!oldCurState.IsValid() ||
813                 ::GetHash(oldCurState) != ::GetHash(oneCur.second))
814             {
815                 return state.Error(errorPrefix + "currecy state is invalid in accepted notarization. is:\n" + 
816                                                  oneCur.second.ToUniValue().write(1,2) + 
817                                                  "\nshould be:\n" + 
818                                                  oldCurState.ToUniValue().write(1,2) + "\n");
819             }
820         }
821         else
822         {
823             return state.Error(errorPrefix + "cannot accept redundant currency state in notarization for " + ConnectedChains.ThisChain().name);
824         }
825     }
826     for (auto &oneRoot : newNotarization.proofRoots)
827     {
828         if (oneRoot.first == SystemID)
829         {
830             continue;
831         }
832         else
833         {
834             // see if this currency is on our chain, and if so, it must be correct as of the proof root of this chain
835             CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oneRoot.first);
836             // we must have all currencies in this notarization registered
837             if (!curDef.IsValid())
838             {
839                 return state.Error(errorPrefix + "all currencies in accepted notarizatoin must be registered on this chain");
840             }
841             uint160 curDefID = curDef.GetID();
842
843             // only check other currencies on this chain, not the main chain itself
844             if (curDefID != ASSETCHAINS_CHAINID && curDef.systemID == ASSETCHAINS_CHAINID)
845             {
846                 return state.Error(errorPrefix + "proof roots are not accepted for token currencies");
847             }
848         }
849     }
850
851     // now create the new notarization, add the proof, finalize if appropriate, and finish
852
853     // add spend of prior notarization and then outputs
854     CPBaaSNotarization lastUnspentNotarization;
855     uint256 lastTxId;
856     int32_t lastTxOutNum;
857     CTransaction lastTx;
858     if (!lastUnspentNotarization.GetLastUnspentNotarization(SystemID, EVAL_ACCEPTEDNOTARIZATION, lastTxId, lastTxOutNum, &lastTx))
859     {
860         return state.Error(errorPrefix + "invalid prior notarization");
861     }
862
863     // add prior unspent accepted notarization as our input
864     txBuilder.AddTransparentInput(CUTXORef(lastTxId, lastTxOutNum), lastTx.vout[lastTxOutNum].scriptPubKey, lastTx.vout[lastTxOutNum].nValue);
865
866     CCcontract_info CC;
867     CCcontract_info *cp;
868     std::vector<CTxDestination> dests;
869
870     // make the earned notarization output
871     cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
872
873     if (externalSystem.notarizationProtocol == externalSystem.NOTARIZATION_NOTARY_CHAINID)
874     {
875         dests = std::vector<CTxDestination>({CIdentityID(externalSystem.GetID())});
876     }
877     else
878     {
879         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
880     }
881
882     txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &newNotarization)), 0);
883
884     // now add the notary evidence and finalization that uses it to assert validity
885     // make the earned notarization output
886     cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
887     dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
888     txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, &notaryEvidence)), CNotaryEvidence::DEFAULT_OUTPUT_VALUE);
889
890     if (externalSystem.notarizationProtocol != externalSystem.NOTARIZATION_NOTARY_CHAINID)
891     {
892         // make the finalization output
893         cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
894         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
895
896         // we need to store the input that we confirmed if we spent finalization outputs
897         CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, VERUS_CHAINID, uint256(), txBuilder.mtx.vout.size(), height + 15);
898         if (notaryEvidence.signatures.size() >= externalSystem.notaries.size())
899         {
900             of.SetConfirmed();
901             of.evidenceOutputs.push_back(txBuilder.mtx.vout.size() - 1);
902         }
903         txBuilder.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of)), 0);
904     }
905     return true;
906 }
907
908 // create a notarization that is validated as part of the block, generally benefiting the miner or staker if the
909 // cross notarization is valid
910 bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalSystem,
911                                                   const CTransferDestination &Proposer,
912                                                   CValidationState &state,
913                                                   std::vector<CTxOut> &txOutputs,
914                                                   CPBaaSNotarization &notarization)
915 {
916     std::string errorPrefix(strprintf("%s: ", __func__));
917
918     uint32_t height;
919     uint160 SystemID;
920     const CCurrencyDefinition &systemDef = externalSystem.chainDefinition;
921     SystemID = externalSystem.chainDefinition.GetID();
922
923     CChainNotarizationData cnd;
924     std::vector<std::pair<CTransaction, uint256>> txes;
925
926     {
927         LOCK2(cs_main, mempool.cs);
928         height = chainActive.Height();
929
930         // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
931         // we also need to ensure that our notarization would be the first notarization in this notary block period  with which we agree.
932         if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
933         {
934             // technically not a real error
935             return state.Error("no-notary");
936         }
937
938         if (!GetNotarizationData(SystemID, cnd, &txes))
939         {
940             return state.Error(errorPrefix + "no prior notarization found");
941         }
942     }
943
944     // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
945     UniValue proofRootsUni(UniValue::VARR);
946     for (auto &oneNot : cnd.vtx)
947     {
948         auto rootIt = oneNot.second.proofRoots.find(SystemID);
949         if (rootIt != oneNot.second.proofRoots.end())
950         {
951             proofRootsUni.push_back(rootIt->second.ToUniValue());
952         }
953     }
954
955     if (!proofRootsUni.size())
956     {
957         return state.Error(errorPrefix + "no valid prior state root found");
958     }
959
960     // call notary to determine the prior notarization that we agree with
961     UniValue params(UniValue::VARR);
962
963     UniValue oneParam(UniValue::VOBJ);
964     oneParam.push_back(Pair("proofroots", proofRootsUni));
965     oneParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
966     params.push_back(oneParam);
967
968     //printf("%s: about to get cross notarization with %lu notarizations found\n", __func__, cnd.vtx.size());
969
970     UniValue result;
971     try
972     {
973         result = find_value(RPCCallRoot("getbestproofroot", params), "result");
974     } catch (exception e)
975     {
976         result = NullUniValue;
977     }
978
979     int32_t notaryIdx = uni_get_int(find_value(result, "bestproofrootindex"), -1);
980
981     if (result.isNull() || notaryIdx == -1)
982     {
983         return state.Error(result.isNull() ? "no-notary" : "no-matching-proof-roots-found");
984     }
985
986     // now, we have the index for the transaction and notarization we agree with, a list of those we consider invalid,
987     // and the most recent notarization to use when creating the new one
988     const CTransaction &priorNotarizationTx = txes[notaryIdx].first;
989     uint256 priorBlkHash = txes[notaryIdx].second;
990     const CUTXORef &priorUTXO = cnd.vtx[notaryIdx].first;
991     const CPBaaSNotarization &priorNotarization = cnd.vtx[notaryIdx].second;
992
993     // find out the block height holding the last notarization we agree with
994     auto mapBlockIt = mapBlockIndex.find(priorBlkHash);
995     if (mapBlockIt == mapBlockIndex.end() || !chainActive.Contains(mapBlockIt->second))
996     {
997         return state.Error(errorPrefix + "prior notarization not in blockchain");
998     }
999
1000     // first determine if the prior notarization we agree with would make this one moot
1001     int blockPeriodNumber = (height + 1) / BLOCK_NOTARIZATION_MODULO;
1002     int priorBlockPeriod = mapBlockIt->second->GetHeight() / BLOCK_NOTARIZATION_MODULO;
1003
1004     if (blockPeriodNumber <= priorBlockPeriod)
1005     {
1006         return state.Error("ineligible");
1007     }
1008
1009     notarization = priorNotarization;
1010     notarization.proposer = Proposer;
1011     notarization.notarizationHeight = height;
1012
1013     // get the latest notarization information for the new, earned notarization
1014     // one system may provide one proof root and multiple currency states
1015     CProofRoot latestProofRoot = CProofRoot(find_value(result, "latestproofroot"));
1016     if (!latestProofRoot.IsValid())
1017     {
1018         return state.Error("no-latest-proof-root");
1019     }
1020     notarization.proofRoots[SystemID] = latestProofRoot;
1021
1022     UniValue currencyStatesUni = find_value(result, "currencystates");
1023     if (!(currencyStatesUni.isArray() && currencyStatesUni.size()))
1024     {
1025         return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1026     }
1027
1028     // take the lock again, now that we're back from calling out
1029     LOCK2(cs_main, mempool.cs);
1030
1031     // if height changed, we need to fail and possibly try again
1032     if (height != chainActive.Height())
1033     {
1034         return state.Error("stale-block");
1035     }
1036
1037     notarization.currencyStates.clear();
1038     for (int i = 0; i < currencyStatesUni.size(); i++)
1039     {
1040         CCoinbaseCurrencyState oneCurState(currencyStatesUni[i]);
1041         CCurrencyDefinition oneCurDef;
1042         if (!oneCurState.IsValid())
1043         {
1044             return state.Error(errorPrefix + "invalid or missing currency state data from notary");
1045         }
1046         if (!(oneCurDef = ConnectedChains.GetCachedCurrency(oneCurState.GetID())).IsValid())
1047         {
1048             // if we don't have the currency for the state specified, and it isn't critical, ignore
1049             if (oneCurDef.GetID() == SystemID)
1050             {
1051                 return state.Error(errorPrefix + "system currency invalid - possible corruption");
1052             }
1053             continue;
1054         }
1055         if (oneCurDef.systemID == SystemID)
1056         {
1057             uint160 oneCurDefID = oneCurDef.GetID();
1058             if (notarization.currencyID == oneCurDefID)
1059             {
1060                 notarization.currencyState = oneCurState;
1061             }
1062             else
1063             {
1064                 notarization.currencyStates[oneCurDefID] = oneCurState;
1065             }
1066         }
1067     }
1068
1069     // add this blockchain's info, based on the requested height
1070     CBlockIndex &curBlkIndex = *chainActive[height];
1071     auto mmv = chainActive.GetMMV();
1072     if (chainActive.Height() != height)
1073     {
1074         mmv.resize(height);
1075     }
1076     uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1077     notarization.proofRoots[thisChainID] = CProofRoot(thisChainID, 
1078                                                       height, 
1079                                                       mmv.GetRoot(), 
1080                                                       curBlkIndex.GetBlockHash(), 
1081                                                       curBlkIndex.chainPower.CompactChainPower(), 
1082                                                       CProofRoot::TYPE_PBAAS);
1083
1084     // add currency states that we should include and then we're done
1085     // currency states to include are either a gateway currency indicated by the
1086     // gateway or our gateway converter for our PBaaS chain
1087     uint160 gatewayConverterID;
1088     if (systemDef.IsGateway() && !systemDef.gatewayConverterName.empty())
1089     {
1090         gatewayConverterID = CCurrencyDefinition::GetID(systemDef.gatewayConverterName, thisChainID);
1091     }
1092     else if (SystemID == ConnectedChains.FirstNotaryChain().chainDefinition.GetID() && !ConnectedChains.ThisChain().gatewayConverterName.empty())
1093     {
1094         gatewayConverterID = CCurrencyDefinition::GetID(ConnectedChains.ThisChain().gatewayConverterName, thisChainID);
1095     }
1096     if (!gatewayConverterID.IsNull())
1097     {
1098         // get the gateway converter currency from the gateway definition
1099         CChainNotarizationData gatewayCND;
1100         if (GetNotarizationData(gatewayConverterID, gatewayCND) && gatewayCND.vtx.size())
1101         {
1102             notarization.currencyStates[gatewayConverterID] = gatewayCND.vtx[gatewayCND.lastConfirmed].second.currencyState;
1103         }
1104     }
1105
1106     notarization.prevNotarization = cnd.vtx[notaryIdx].first;
1107     auto hw = CMMRNode<>::GetHashWriter();
1108     hw << cnd.vtx[notaryIdx].second;
1109     notarization.hashPrevNotarization = hw.GetHash();
1110     notarization.prevHeight = cnd.vtx[notaryIdx].second.notarizationHeight;
1111
1112     CCcontract_info CC;
1113     CCcontract_info *cp;
1114     std::vector<CTxDestination> dests;
1115
1116     // make the earned notarization output
1117     cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
1118
1119     if (systemDef.notarizationProtocol == systemDef.NOTARIZATION_NOTARY_CHAINID)
1120     {
1121         dests = std::vector<CTxDestination>({CIdentityID(systemDef.GetID())});
1122     }
1123     else
1124     {
1125         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1126     }
1127
1128     txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_EARNEDNOTARIZATION, dests, 1, &notarization))));
1129
1130     if (systemDef.notarizationProtocol != systemDef.NOTARIZATION_NOTARY_CHAINID)
1131     {
1132         // make the finalization output
1133         cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
1134
1135         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1136
1137         // we need to store the input that we confirmed if we spent finalization outputs
1138         CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, VERUS_CHAINID, uint256(), txOutputs.size(), height + 15);
1139         txOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of))));
1140     }
1141     return true;
1142 }
1143
1144 std::vector<std::pair<uint32_t, CInputDescriptor>> CObjectFinalization::GetUnspentNotaryEvidence() const
1145 {
1146     LOCK(mempool.cs);
1147     std::vector<std::pair<uint32_t, CInputDescriptor>> retVal;
1148     std::vector<CAddressUnspentDbEntry> indexUnspent;
1149     std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> mempoolUnspent;
1150
1151     uint160 indexKey = CCrossChainRPCData::GetConditionID(currencyID, ObjectFinalizationConfirmedKey());
1152     if ((GetAddressUnspent(indexKey, CScript::P2IDX, indexUnspent) ||
1153          mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{indexKey, CScript::P2IDX}}), mempoolUnspent)) &&
1154         (indexUnspent.size() || mempoolUnspent.size()))
1155     {
1156         for (auto &oneConfirmed : indexUnspent)
1157         {
1158             retVal.push_back(std::make_pair(oneConfirmed.second.blockHeight,
1159                              CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))));
1160         }
1161         for (auto &oneUnconfirmed : mempoolUnspent)
1162         {
1163             auto txProxy = mempool.mapTx.find(oneUnconfirmed.first.txhash);
1164             if (txProxy != mempool.mapTx.end())
1165             {
1166                 auto &mpEntry = *txProxy;
1167                 auto &tx = mpEntry.GetTx();
1168                 retVal.push_back(std::make_pair(0,
1169                                  CInputDescriptor(tx.vout[oneUnconfirmed.first.index].scriptPubKey,
1170                                                   tx.vout[oneUnconfirmed.first.index].nValue, 
1171                                                   CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1172             }
1173         }
1174     }
1175     return retVal;
1176 }
1177
1178 // this is called by notaries to locate any notarizations of a specific system that they can notarize, to determine if we
1179 // agree with the notarization ni question, and to confirm or reject the notarization
1180 bool CPBaaSNotarization::ConfirmOrRejectNotarizations(const CWallet *pWallet,
1181                                                       const CRPCChainData &externalSystem,
1182                                                       CValidationState &state,
1183                                                       TransactionBuilder &txBuilder,
1184                                                       bool &finalized)
1185 {
1186     std::string errorPrefix(strprintf("%s: ", __func__));
1187
1188     finalized = false;
1189
1190     CChainNotarizationData cnd;
1191     std::vector<std::pair<CTransaction, uint256>> txes;
1192
1193     uint32_t height;
1194     uint160 SystemID = externalSystem.chainDefinition.GetID();
1195
1196     std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> mine;
1197     {
1198         std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> imsigner, watchonly;
1199         LOCK(pWallet->cs_wallet);
1200         // sign with all IDs under our control that are eligible for this currency
1201         pWallet->GetIdentities(externalSystem.chainDefinition.notaries, mine, imsigner, watchonly);
1202         if (!mine.size())
1203         {
1204             return state.Error("no-notary");
1205         }
1206     }
1207
1208     {
1209         LOCK2(cs_main, mempool.cs);
1210         height = chainActive.Height();
1211
1212         // we can only create an earned notarization for a notary chain, so there must be a notary chain and a network connection to it
1213         // we also need to ensure that our notarization would be the first notarization in this notary block period  with which we agree.
1214         if (!externalSystem.IsValid() || externalSystem.rpcHost.empty())
1215         {
1216             // technically not a real error
1217             return state.Error("no-notary");
1218         }
1219
1220         if (!GetNotarizationData(SystemID, cnd, &txes))
1221         {
1222             return state.Error(errorPrefix + "no prior notarization found");
1223         }
1224     }
1225
1226     // all we really want is the system proof roots for each notarization to make the JSON for the API smaller
1227     UniValue proofRootsUni(UniValue::VARR);
1228     for (auto &oneNot : cnd.vtx)
1229     {
1230         auto rootIt = oneNot.second.proofRoots.find(SystemID);
1231         if (rootIt != oneNot.second.proofRoots.end())
1232         {
1233             proofRootsUni.push_back(rootIt->second.ToUniValue());
1234         }
1235     }
1236
1237     if (!proofRootsUni.size())
1238     {
1239         return state.Error(errorPrefix + "no valid prior state root found");
1240     }
1241
1242     UniValue firstParam(UniValue::VOBJ);
1243     firstParam.push_back(Pair("proofroots", proofRootsUni));
1244     firstParam.push_back(Pair("lastconfirmed", cnd.lastConfirmed));
1245
1246     // call notary to determine the notarization that we should notarize
1247     UniValue params(UniValue::VARR);
1248     params.push_back(firstParam);
1249
1250     //printf("%s: about to get cross notarization with %lu notarizations found\n", __func__, cnd.vtx.size());
1251
1252     UniValue result;
1253     try
1254     {
1255         result = find_value(RPCCallRoot("getbestproofroot", params), "result");
1256     } catch (exception e)
1257     {
1258         result = NullUniValue;
1259     }
1260
1261     int32_t notaryIdx = uni_get_int(find_value(result, "bestproofrootindex"), -1);
1262
1263     if (result.isNull() || notaryIdx == -1)
1264     {
1265         return state.Error(result.isNull() ? "no-notary" : "no-matching-notarization-found");
1266     }
1267
1268     // take the lock again, now that we're back from calling out
1269     LOCK2(cs_main, mempool.cs);
1270
1271     // if height changed, we need to fail and possibly try again later
1272     if (height != chainActive.Height())
1273     {
1274         return state.Error("stale-block");
1275     }
1276
1277     // now, get the list of unconfirmed matches, and sign the latest one that
1278     // may be signed
1279     UniValue proofRootArr = find_value(result, "validproofroots");
1280     if (!proofRootArr.isArray() || !proofRootArr.size())
1281     {
1282         return state.Error("no-valid-unconfirmed");
1283     }
1284
1285     // latest height we are eligible to notarize
1286     uint32_t eligibleHeight = height - CPBaaSNotarization::MIN_BLOCKS_BEFORE_NOTARY_FINALIZED;
1287
1288     bool retVal = false;
1289
1290     // look from the latest notarization that may qualify
1291     for (int i = proofRootArr.size() - 1; i >= 0; i--)
1292     {
1293         int idx = uni_get_int(proofRootArr[i]);
1294         if (cnd.vtx[idx].second.notarizationHeight <= eligibleHeight)
1295         {
1296             // this is the one we will notarize
1297             std::set<CInputDescriptor> myIDSigs;
1298
1299             std::set<CIdentityID> myIDSet;
1300             for (auto &oneID : mine)
1301             {
1302                 myIDSet.insert(oneID.first.idID);
1303             }
1304
1305             // before signing the one we are about to, we want to ensure that it isn't already signed sufficiently
1306             // if there are enough signatures to confirm it with out signature, make our signature, then create a finalization
1307             CObjectFinalization of = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION + CObjectFinalization::FINALIZE_CONFIRMED,
1308                                                          SystemID,
1309                                                          cnd.vtx[idx].first.hash,
1310                                                          cnd.vtx[idx].first.n,
1311                                                          eligibleHeight);
1312
1313             std::vector<std::pair<uint32_t, CInputDescriptor>> evidenceOuts = of.GetUnspentNotaryEvidence();
1314             std::set<CInputDescriptor> additionalEvidence;
1315             std::set<CInputDescriptor> evidenceToSpend;
1316
1317             std::set<uint160> sigSet;
1318
1319             // if we might have a confirmed notarization, verify, then post
1320             for (auto &oneEvidenceOut : evidenceOuts)
1321             {
1322                 COptCCParams p;
1323                 CNotaryEvidence evidence;
1324                 if (oneEvidenceOut.second.scriptPubKey.IsPayToCryptoCondition(p) &&
1325                     p.IsValid() &&
1326                     p.evalCode == EVAL_NOTARY_EVIDENCE &&
1327                     p.vData.size() &&
1328                     (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
1329                     evidence.IsNotarySignature())
1330                 {
1331                     if (CUTXORef(evidence.output.hash.IsNull() ? oneEvidenceOut.second.txIn.prevout.hash : evidence.output.hash, evidence.output.n) == of.output &&
1332                         evidence.signatures.size())
1333                     {
1334                         bool hasOurSig = false;
1335                         for (auto &oneSig : evidence.signatures)
1336                         {
1337                             sigSet.insert(oneSig.first);
1338                             if (myIDSet.count(oneSig.first))
1339                             {
1340                                 hasOurSig = true;
1341                                 myIDSet.erase(oneSig.first);
1342                             }
1343                         }
1344                         if (hasOurSig)
1345                         {
1346                             myIDSigs.insert(oneEvidenceOut.second);
1347                         }
1348                         else
1349                         {
1350                             additionalEvidence.insert(oneEvidenceOut.second);
1351                         }
1352                         
1353                     }
1354                     else
1355                     {
1356                         evidenceToSpend.insert(oneEvidenceOut.second);
1357                     }
1358                 }
1359             }
1360
1361             if (evidenceOuts.size() >= (externalSystem.chainDefinition.minNotariesConfirm - 1))
1362             {
1363             }
1364
1365             // we've already signed
1366             if (!myIDSet.size())
1367             {
1368                 return state.Error("ineligible");
1369             }
1370
1371             CCcontract_info CC;
1372             CCcontract_info *cp;
1373             std::vector<CTxDestination> dests;
1374
1375             // make the earned notarization output
1376             cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
1377             dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1378             CNotaryEvidence ne = CNotaryEvidence(ASSETCHAINS_CHAINID, cnd.vtx[idx].first);
1379
1380             {
1381                 LOCK(pWallet->cs_wallet);
1382                 // sign with all IDs under our control that are eligible for this currency
1383                 for (auto &oneID : myIDSet)
1384                 {
1385                     auto signResult = ne.SignConfirmed(*pWallet, txes[idx].first, oneID, height);
1386                     if (signResult == CIdentitySignature::SIGNATURE_PARTIAL || signResult == CIdentitySignature::SIGNATURE_COMPLETE)
1387                     {
1388                         sigSet.insert(oneID);
1389                         retVal = true;
1390                         // if our signatures altogether have provided a complete validation, we can early out
1391                         if ((ne.signatures.size() + myIDSigs.size()) >= externalSystem.chainDefinition.minNotariesConfirm)
1392                         {
1393                             break;
1394                         }
1395                     }
1396                     else
1397                     {
1398                         return state.Error(errorPrefix + "invalid identity signature");
1399                     }
1400                 }
1401             }
1402
1403             if (ne.signatures.size())
1404             {
1405                 CScript evidenceScript = MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, &ne));
1406                 myIDSigs.insert(CInputDescriptor(evidenceScript, 0, CTxIn(COutPoint(uint256(), txBuilder.mtx.vout.size()))));
1407                 txBuilder.AddTransparentOutput(evidenceScript, CNotaryEvidence::DEFAULT_OUTPUT_VALUE);
1408             }
1409
1410             // if we have enough to finalize, do so and include all of our signatures allowed
1411             if (sigSet.size() >= externalSystem.chainDefinition.minNotariesConfirm)
1412             {
1413                 int sigCount = 0;
1414
1415                 // include all of our signatures to improve chances of reward
1416                 if (ne.signatures.size())
1417                 {
1418                     of.evidenceOutputs.push_back(txBuilder.mtx.vout.size() - 1);
1419                     sigCount += ne.signatures.size();
1420                 }
1421
1422                 // spend all priors, and if we need more the new signatures, add them to the finalization evidence
1423                 // prioritizing our signatures
1424                 bool haveNeeded = sigCount >= externalSystem.chainDefinition.minNotariesConfirm;
1425                 for (auto &oneEvidenceOut : myIDSigs)
1426                 {
1427                     // use up evidence with our ID signatures first, and remove from the remainder
1428                     COptCCParams p;
1429                     CNotaryEvidence evidence;
1430                     // validated above
1431                     oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1432                     evidence = CNotaryEvidence(p.vData[0]);
1433                     for (auto &oneSig : evidence.signatures)
1434                     {
1435                         if (sigSet.count(oneSig.first))
1436                         {
1437                             sigCount++;
1438                             sigSet.erase(oneSig.first);
1439                         }
1440                     }
1441                     txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1442                     if (!haveNeeded)
1443                     {
1444                         // until we have enough signatures to confirm, continue to add evidence to the finalization
1445                         of.evidenceInputs.push_back(txBuilder.mtx.vin.size() - 1);
1446                         haveNeeded = sigCount >= externalSystem.chainDefinition.minNotariesConfirm;
1447                     }
1448                 }
1449                 // if we still need more confirmation, add it
1450                 if (!haveNeeded)
1451                 {
1452                     for (auto &oneEvidenceOut : additionalEvidence)
1453                     {
1454                         // use up evidence with our ID signatures first, and remove from the remainder
1455                         COptCCParams p;
1456                         CNotaryEvidence evidence;
1457                         // validated above
1458                         oneEvidenceOut.scriptPubKey.IsPayToCryptoCondition(p);
1459                         evidence = CNotaryEvidence(p.vData[0]);
1460                         for (auto &oneSig : evidence.signatures)
1461                         {
1462                             if (sigSet.count(oneSig.first))
1463                             {
1464                                 sigCount++;
1465                                 sigSet.erase(oneSig.first);
1466                             }
1467                         }
1468                         txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1469                         if (!haveNeeded)
1470                         {
1471                             // until we have enough signatures to confirm, continue to add evidence to the finalization
1472                             of.evidenceInputs.push_back(txBuilder.mtx.vin.size() - 1);
1473                             haveNeeded = sigCount >= externalSystem.chainDefinition.minNotariesConfirm;
1474                         }
1475                     }
1476                 }
1477                 
1478                 if (!haveNeeded)
1479                 {
1480                     // should never get here
1481                     return state.Error(errorPrefix + "Internal error");
1482                 }
1483
1484                 finalized = true;
1485
1486                 CScript finalizeScript = MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &of));
1487                 txBuilder.AddTransparentOutput(finalizeScript, 0);
1488
1489                 // spend all remaining, unnecessary bits of notary evidence for prior finalizations
1490                 for (auto &oneEvidenceOut : evidenceToSpend)
1491                 {
1492                     txBuilder.AddTransparentInput(oneEvidenceOut.txIn.prevout, oneEvidenceOut.scriptPubKey, oneEvidenceOut.nValue);
1493                 }
1494             }
1495         }
1496     }
1497     return retVal;
1498 }
1499
1500
1501 /*
1502  * Validates a notarization output spend by ensuring that the spending transaction fulfills all requirements.
1503  * to accept an earned notarization as valid on the Verus blockchain, it must prove a transaction on the alternate chain, which is 
1504  * either the original chain definition transaction, which CAN and MUST be proven ONLY in block 1, or the latest notarization transaction 
1505  * on the alternate chain that represents an accurate MMR for this chain.
1506  * In addition, any accepted notarization must fullfill the following requirements:
1507  * 1) Must prove either a PoS block from the alternate chain or a merge mined block that is owned by the submitter and in either case, 
1508  *    the block must be exactly 8 blocks behind the submitted MMR used for proof.
1509  * 2) Must prove a chain definition tx and be block 1 or asserts a previous, valid MMR for the notarizing
1510  *    chain and properly prove objects using that MMR.
1511  * 3) Must spend the main notarization thread as well as any finalization outputs of either valid or invalid prior
1512  *    notarizations, and any unspent notarization contributions for this era. May also spend other inputs.
1513  * 4) Must output:
1514  *      a) finalization output of the expected reward amount, which will be sent when finalized
1515  *      b) normal output of reward from validated/finalized input if present, 50% to recipient / 50% to block miner less miner fee this tx
1516  *      c) main notarization thread output with remaining funds, no other output or fee deduction
1517  * 
1518  */
1519 bool ValidateAcceptedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
1520 {
1521     // TODO: this validates the spending transaction
1522     // check the following things:
1523     // 1. It represents a valid PoS or merge mined block on the other chain, and contains the header in the opret
1524     // 2. The MMR and proof provided for the currently asserted block can prove the provided header. The provided
1525     //    header can prove the last block referenced.
1526     // 3. This notarization is not a superset of an earlier notarization posted before it that it does not
1527     //    reference. If that is the case, it is rejected.
1528     // 4. Has all relevant inputs, including finalizes all necessary transactions, both confirmed and orphaned
1529     //printf("ValidateAcceptedNotarization\n");
1530     return true;
1531 }
1532
1533 bool IsAcceptedNotarizationInput(const CScript &scriptSig)
1534 {
1535     uint32_t ecode;
1536     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_ACCEPTEDNOTARIZATION;
1537 }
1538
1539
1540 /*
1541  * Ensures that a spend in an earned notarization of either an OpRet support transaction or summary notarization
1542  * are valid with respect to this chain. Any transaction that spends from an opret trasaction is either disconnected,
1543  * or contains the correct hashes of each object and transaction data except for the opret, which can be validated by
1544  * reconstructing the opret from the hashes on the other chain and verifying that it hashes to the same input value. This
1545  * enables full validation without copying redundant data back to its original chain.
1546  * 
1547  * In addition, each earned notarization must reference the last earned notarization with which it agrees and prove the last
1548  * accepted notarization on the alternate chain with the latest MMR. The earned notarization will not be accepted if there is
1549  * a later notarization that agrees with it already present in the alternate chain when it is submitted. 
1550  * 
1551  */
1552 bool ValidateEarnedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
1553 {
1554     // this needs to validate that the block is mined or staked, that the notarization is properly formed,
1555     // cryptographically correct, and that it spends the proper finalization outputs
1556     // if the notarization causes a fork, it must include additional proof of blocks and their
1557     // power based on random block hash bits
1558     //printf("ValidateEarnedNotarization\n");
1559     return true;
1560 }
1561 bool IsEarnedNotarizationInput(const CScript &scriptSig)
1562 {
1563     // this is an output check, and is incorrect. need to change to input
1564     uint32_t ecode;
1565     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_EARNEDNOTARIZATION;
1566 }
1567
1568 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx=nullptr, uint32_t *pHeight=nullptr);
1569 CObjectFinalization GetOldFinalization(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx, uint32_t *pHeight)
1570 {
1571     CTransaction _sourceTx;
1572     CTransaction &sourceTx(pSourceTx ? *pSourceTx : _sourceTx);
1573
1574     CObjectFinalization oldFinalization;
1575     uint256 blkHash;
1576     if (myGetTransaction(spendingTx.vin[nIn].prevout.hash, sourceTx, blkHash))
1577     {
1578         if (pHeight)
1579         {
1580             auto bIt = mapBlockIndex.find(blkHash);
1581             if (bIt == mapBlockIndex.end() || !bIt->second)
1582             {
1583                 *pHeight = chainActive.Height();
1584             }
1585             else
1586             {
1587                 *pHeight = bIt->second->GetHeight();
1588             }
1589         }
1590         COptCCParams p;
1591         if (sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1592             p.IsValid() && 
1593             p.evalCode == EVAL_IDENTITY_PRIMARY && 
1594             p.version >= COptCCParams::VERSION_V3 &&
1595             p.vData.size() > 1)
1596         {
1597             oldFinalization = CObjectFinalization(p.vData[0]);
1598         }
1599     }
1600     return oldFinalization;
1601 }
1602
1603
1604 /*
1605  * Ensures that the finalization, either as validated or orphaned, is determined by
1606  * 10 confirmations, either of this transaction, or of an alternate transaction on the chain that we do not derive
1607  * from. If the former, then this should be asserted to be validated, otherwise, it should be asserted to be invalidated.
1608  *  
1609  */
1610 bool ValidateFinalizeNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
1611 {
1612     // to validate a finalization spend, we need to validate the spender's assertion of confirmation or rejection as proven
1613
1614
1615     // first, determine our notarization finalization protocol
1616     CTransaction sourceTx;
1617     uint32_t oldHeight;
1618     CObjectFinalization oldFinalization = GetOldFinalization(tx, nIn, &sourceTx, &oldHeight);
1619     if (!oldFinalization.IsValid())
1620     {
1621         return eval->Error("Invalid finalization output");
1622     }
1623
1624     // get currency to determine system and notarization method
1625     CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(oldFinalization.currencyID);
1626     if (!curDef.IsValid())
1627     {
1628         return eval->Error("Invalid currency ID in finalization output");
1629     }
1630     uint160 SystemID = curDef.GetID();
1631
1632     if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
1633     {
1634         // auto-notarization not yet implemented
1635         if (!PBAAS_TESTMODE)
1636         {
1637             return eval->Error("auto-notarization");
1638         }
1639     }
1640     else if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CONFIRM)
1641     {
1642         // get the notarization this finalizes and its index output
1643         int32_t notaryOutNum;
1644         CTransaction notarizationTx;
1645         if (oldFinalization.IsConfirmed() || oldFinalization.IsRejected())
1646         {
1647             return eval->Error("already-finalized");
1648         }
1649         
1650         if (oldFinalization.output.IsOnSameTransaction())
1651         {
1652             notarizationTx = sourceTx;
1653             // output needs non-null hash below
1654             oldFinalization.output.hash = notarizationTx.GetHash();
1655         }
1656         else
1657         {
1658             uint256 blkHash;
1659             if (!oldFinalization.GetOutputTransaction(sourceTx, notarizationTx, blkHash))
1660             {
1661                 return eval->Error("notarization-transaction-not-found");
1662             }
1663         }
1664         if (notarizationTx.vout.size() <= oldFinalization.output.n)
1665         {
1666             return eval->Error("invalid-finalization");
1667         }
1668
1669         CPBaaSNotarization pbn(notarizationTx.vout[oldFinalization.output.n].scriptPubKey);
1670         if (!pbn.IsValid())
1671         {
1672             return eval->Error("invalid-notarization");
1673         }
1674
1675         // now, we have an unconfirmed, non-rejected finalization being spent by a transaction
1676         // confirm that the spender contains one fonalization output either confirming or rejecting
1677         // the finalization. rejection may be implicit by confirming another, later notarization.
1678
1679         // First. make sure the oldFinalization is not referring to an earlier notarization than the 
1680         // one most recently confirmed. If so. then it can be spent by anyone.
1681         CChainNotarizationData cnd;
1682         if (!GetNotarizationData(SystemID, cnd) || !cnd.IsConfirmed())
1683         {
1684             return eval->Error("invalid-notarization");
1685         }
1686
1687
1688
1689         // TODO: now, validate both rejection and confirmation
1690
1691
1692
1693         CObjectFinalization newFinalization;
1694         int finalizationOutNum = -1;
1695         bool foundFinalization = false;
1696         for (int i = 0; i < tx.vout.size(); i++)
1697         {
1698             auto &oneOut = tx.vout[i];
1699             COptCCParams p;
1700             // we can accept only one finalization of this notarization as an output, find it and reject more than one
1701             if (oneOut.scriptPubKey.IsPayToCryptoCondition(p) &&
1702                 p.IsValid() &&
1703                 p.evalCode == EVAL_FINALIZE_NOTARIZATION &&
1704                 p.vData.size() &&
1705                 (newFinalization = CObjectFinalization(p.vData[0])).IsValid() &&
1706                 newFinalization.output == oldFinalization.output)
1707             {
1708                 if (foundFinalization)
1709                 {
1710                     return eval->Error("duplicate-finalization");
1711                 }
1712                 foundFinalization = true;
1713                 finalizationOutNum = i;
1714             }
1715         }
1716
1717         if (!foundFinalization)
1718         {
1719             return eval->Error("invalid-finalization-spend");
1720         }
1721
1722
1723     }
1724     return true;
1725 }
1726
1727 bool IsFinalizeNotarizationInput(const CScript &scriptSig)
1728 {
1729     // this is an output check, and is incorrect. need to change to input
1730     uint32_t ecode;
1731     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_FINALIZE_NOTARIZATION;
1732 }
1733
1734 bool CObjectFinalization::GetOutputTransaction(const CTransaction &initialTx, CTransaction &tx, uint256 &blockHash) const
1735 {
1736     if (output.hash.IsNull())
1737     {
1738         tx = initialTx;
1739         return true;
1740     }
1741     else if (myGetTransaction(output.hash, tx, blockHash) && tx.vout.size() > output.n)
1742     {
1743         return true;
1744     }
1745     return false;
1746 }
1747
1748 // Sign the output object with an ID or signing authority of the ID from the wallet.
1749 CNotaryEvidence CObjectFinalization::SignConfirmed(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
1750 {
1751     CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
1752
1753     AssertLockHeld(cs_main);
1754     uint32_t nHeight = chainActive.Height();
1755
1756     CTransaction tx;
1757     uint256 blockHash;
1758     if (GetOutputTransaction(initialTx, tx, blockHash))
1759     {
1760         retVal.SignConfirmed(*pWallet, tx, signatureID, nHeight);
1761     }
1762     return retVal;
1763 }
1764
1765 CNotaryEvidence CObjectFinalization::SignRejected(const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID) const
1766 {
1767     CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output);
1768
1769     AssertLockHeld(cs_main);
1770     uint32_t nHeight = chainActive.Height();
1771
1772     CTransaction tx;
1773     uint256 blockHash;
1774     if (GetOutputTransaction(initialTx, tx, blockHash))
1775     {
1776         retVal.SignRejected(*pWallet, tx, signatureID, nHeight);
1777     }
1778     return retVal;
1779 }
1780
1781 // Verify that the output object of "p" is signed appropriately with the indicated signature
1782 // and that the signature is fully authorized to sign
1783 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, const COptCCParams &p, uint32_t height) const
1784 {
1785     std::set<uint160> completedSignatures;
1786     std::set<uint160> partialSignatures;
1787
1788     CCurrencyDefinition curDef;
1789     int32_t defHeight;
1790
1791     if (p.IsValid() && 
1792         p.version >= p.VERSION_V3 && 
1793         p.vData.size() &&
1794         GetCurrencyDefinition(currencyID, curDef, &defHeight) &&
1795         curDef.IsValid())
1796     {
1797         uint256 txId = output.hash.IsNull() ? initialTx.GetHash() : output.hash;
1798         std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(currencyID, CNotaryEvidence::NotarySignatureKey(), txId, output.n)};
1799         std::vector<uint256> statements;
1800
1801         // check that signature is of the hashed vData[0] data
1802         auto hw = CMMRNode<>::GetHashWriter();
1803         hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
1804         uint256 msgHash = hw.GetHash();
1805
1806         for (auto &authorizedNotary : curDef.notaries)
1807         {
1808             if (signature.signatures.count(authorizedNotary))
1809             {
1810                 // we might have a partial or complete signature by one notary here
1811                 const CIdentitySignature &oneIDSig = signature.signatures.find(authorizedNotary)->second;
1812
1813                 uint256 sigHash = oneIDSig.IdentitySignatureHash(vdxfCodes, statements, currencyID, height, authorizedNotary, "", msgHash);
1814
1815                 // get identity used to sign
1816                 CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
1817                 if (signer.IsValid())
1818                 {
1819                     std::set<uint160> idAddresses;
1820                     std::set<uint160> verifiedSignatures;
1821
1822                     for (const CTxDestination &oneAddress : signer.primaryAddresses)
1823                     {
1824                         if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
1825                         {
1826                             // currently, can only check secp256k1 signatures
1827                             //return state.Error("Unsupported signature type");
1828                             return CIdentitySignature::SIGNATURE_INVALID;
1829                         }
1830                         idAddresses.insert(GetDestinationID(oneAddress));
1831                     }
1832
1833                     for (auto &oneSig : signature.signatures.find(authorizedNotary)->second.signatures)
1834                     {
1835                         CPubKey pubKey;
1836                         pubKey.RecoverCompact(sigHash, oneSig);
1837                         if (!idAddresses.count(pubKey.GetID()))
1838                         {
1839                             // invalid signature or ID
1840                             return CIdentitySignature::SIGNATURE_INVALID;
1841                         }
1842                         verifiedSignatures.insert(pubKey.GetID());
1843                     }
1844                     if (verifiedSignatures.size() >= signer.minSigs)
1845                     {
1846                         completedSignatures.insert(authorizedNotary);
1847                     }
1848                     else
1849                     {
1850                         partialSignatures.insert(authorizedNotary);
1851                     }
1852                 }
1853                 else
1854                 {
1855                     // invalid signing identity in signature
1856                     return CIdentitySignature::SIGNATURE_INVALID;
1857                 }
1858             }
1859         }
1860         // all IDs in the signature must have been found and either partial or complete signatures
1861         if (partialSignatures.size() + completedSignatures.size() < signature.signatures.size())
1862         {
1863             return CIdentitySignature::SIGNATURE_INVALID;
1864         }
1865
1866         if (completedSignatures.size() >= curDef.minNotariesConfirm)
1867         {
1868             return CIdentitySignature::SIGNATURE_COMPLETE;
1869         }
1870         else if (completedSignatures.size() || partialSignatures.size())
1871         {
1872             return CIdentitySignature::SIGNATURE_PARTIAL;
1873         }
1874     }
1875     // missing or invalid
1876     return CIdentitySignature::SIGNATURE_INVALID;
1877 }
1878
1879 // Verify that the output object is signed with an authorized signing authority
1880 CIdentitySignature::ESignatureVerification CObjectFinalization::VerifyOutputSignature(const CTransaction &initialTx, const CNotaryEvidence &signature, uint32_t height) const
1881 {
1882     // now, get the output to check and check to ensure the signature is good
1883     CTransaction tx;
1884     uint256 blkHash;
1885     COptCCParams p;
1886     if (GetOutputTransaction(initialTx, tx, blkHash) &&
1887         tx.vout.size() > output.n &&
1888         tx.vout[output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1889         p.IsValid() &&
1890         p.vData.size())
1891     {
1892         return VerifyOutputSignature(initialTx, signature, p, height);
1893     }
1894     else
1895     {
1896         return CIdentitySignature::SIGNATURE_INVALID;
1897     }
1898 }
1899
1900 // this ensures that the signature is, in fact, both authorized to sign, and also a
1901 // valid signature of the specified output object. if so, this is accepted and
1902 // results in a valid index entry as a confirmation of the notary signature
1903 // all signatures must be from a valid notary, or this returns false and should be
1904 // considered invalid.
1905 // returns the number of valid, unique notary signatures, enabling a single output
1906 // to be sufficient to authorize.
1907 bool ValidateNotarizationEvidence(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height, int &confirmedCount, bool &provenFalse)
1908 {
1909     // we MUST know that the cs_main lock is held. since it can be held on the validation thread while smart transactions
1910     // execute, we cannot take it or assert here
1911
1912     CNotaryEvidence notarySig;
1913     COptCCParams p;
1914     CCurrencyDefinition curDef;
1915
1916     confirmedCount = 0;         // if a unit of evidence, whether signature or otherwise, is validated as confirming
1917     provenFalse = false;        // if the notarization is proven false
1918
1919     if (tx.vout[outNum].scriptPubKey.IsPayToCryptoCondition(p) && 
1920         p.IsValid() && 
1921         p.version >= p.VERSION_V3 && 
1922         p.vData.size() && 
1923         (notarySig = CNotaryEvidence(p.vData[0])).IsValid() &&
1924         (curDef = ConnectedChains.GetCachedCurrency(notarySig.systemID)).IsValid())
1925     {
1926         // now, get the output to check and ensure the signature is good
1927         CObjectFinalization of;
1928         CPBaaSNotarization notarization;
1929         uint256 notarizationTxId;
1930         CTransaction nTx;
1931         uint256 blkHash;
1932         if (notarySig.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(notarySig.output.hash, nTx, blkHash) &&
1933             nTx.vout.size() > notarySig.output.n &&
1934             nTx.vout[notarySig.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1935             p.IsValid() &&
1936             (p.evalCode == EVAL_FINALIZE_NOTARIZATION) &&
1937             p.vData.size() &&
1938             (of = CObjectFinalization(p.vData[0])).IsValid() &&
1939             of.IsNotarizationFinalization() &&
1940             of.output.hash.IsNull() ? (nTx = tx), true : myGetTransaction(of.output.hash, nTx, blkHash) &&
1941             !(notarizationTxId = nTx.GetHash()).IsNull() &&
1942             nTx.vout.size() > of.output.n &&
1943             nTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1944             p.IsValid() &&
1945             (p.evalCode == EVAL_EARNEDNOTARIZATION || p.evalCode == EVAL_ACCEPTEDNOTARIZATION) &&
1946             p.vData.size() &&
1947             (notarization = CPBaaSNotarization(p.vData[0])).IsValid() &&
1948             notarization.proofRoots.count(notarySig.systemID))
1949         {
1950             // signature is relative only to the notarization, not the finalization
1951             // that way, the information we put into the vdxfCodes have some meaning beyond
1952             // the blockchain on which it was signed, and we do not have to carry the
1953             // finalizatoin mechanism cross-chain.
1954             std::vector<uint160> vdxfCodes = {CCrossChainRPCData::GetConditionID(notarySig.systemID, 
1955                                                                                  CNotaryEvidence::NotarySignatureKey(), 
1956                                                                                  notarizationTxId, 
1957                                                                                  of.output.n)};
1958             std::vector<uint256> statements;
1959
1960             // check that signature is of the hashed vData[0] data
1961             auto hw = CMMRNode<>::GetHashWriter();
1962             hw.write((const char *)&(p.vData[0][0]), p.vData[0].size());
1963             uint256 msgHash = hw.GetHash();
1964
1965             for (auto &authorizedNotary : curDef.notaries)
1966             {
1967                 std::map<CIdentityID, CIdentitySignature>::iterator sigIt = notarySig.signatures.find(authorizedNotary);
1968                 if (sigIt != notarySig.signatures.end())
1969                 {
1970                     // get identity used to sign
1971                     CIdentity signer = CIdentity::LookupIdentity(authorizedNotary, height);
1972                     uint256 sigHash = sigIt->second.IdentitySignatureHash(vdxfCodes, statements, of.currencyID, height, authorizedNotary, "", msgHash);
1973
1974                     if (signer.IsValid())
1975                     {
1976                         std::set<uint160> idAddresses;
1977                         std::set<uint160> verifiedSignatures;
1978                         
1979                         for (const CTxDestination &oneAddress : signer.primaryAddresses)
1980                         {
1981                             if (oneAddress.which() != COptCCParams::ADDRTYPE_PK || oneAddress.which() != COptCCParams::ADDRTYPE_PKH)
1982                             {
1983                                 // currently, can only check secp256k1 signatures
1984                                 return state.Error("Unsupported signature type");
1985                             }
1986                             idAddresses.insert(GetDestinationID(oneAddress));
1987                         }
1988
1989                         for (auto &oneSig : notarySig.signatures[authorizedNotary].signatures)
1990                         {
1991                             CPubKey pubKey;
1992                             pubKey.RecoverCompact(sigHash, oneSig);
1993                             uint160 pkID = pubKey.GetID();
1994                             if (!idAddresses.count(pkID))
1995                             {
1996                                 return state.Error("Mismatched pubkey and ID in signature");
1997                             }
1998                             if (verifiedSignatures.count(pkID))
1999                             {
2000                                 return state.Error("Duplicate key use in ID signature");
2001                             }
2002                             verifiedSignatures.insert(pkID);
2003                         }
2004                         if (verifiedSignatures.size() >= signer.minSigs)
2005                         {
2006                             confirmedCount++;
2007                         }
2008                         else
2009                         {
2010                             return state.Error("Insufficient signatures on behalf of ID: " + signer.name);
2011                         }
2012                     }
2013                     else
2014                     {
2015                         return state.Error("Invalid notary identity or corrupt local state");
2016                     }
2017                 }
2018                 else
2019                 {
2020                     return state.Error("Unauthorized notary");
2021                 }
2022             }
2023         }
2024         else
2025         {
2026             return state.Error("Invalid notarization reference");
2027         }
2028     }
2029     else
2030     {
2031         return state.Error("Invalid or non-evidence output");
2032     }
2033     
2034     if (!confirmedCount)
2035     {
2036         return state.Error("No evidence present");
2037     }
2038     else
2039     {
2040         return true;
2041     }
2042 }
2043
This page took 0.144209 seconds and 4 git commands to generate.