]> Git Repo - VerusCoin.git/blob - src/pbaas/identity.cpp
Build fix
[VerusCoin.git] / src / pbaas / identity.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 provides support for PBaaS identity definition,
8  * 
9  * This is a decentralized identity class that provides the minimum
10  * basic function needed to enable persistent DID-similar identities, 
11  * needed for signatories, that will eventually bridge compatibly to
12  * DID identities.
13  * 
14  * 
15  */
16 #include "main.h"
17 #include "pbaas/pbaas.h"
18 #include "identity.h"
19
20 extern CTxMemPool mempool;
21
22 CCommitmentHash::CCommitmentHash(const CTransaction &tx)
23 {
24     for (auto txOut : tx.vout)
25     {
26         COptCCParams p;
27         if (txOut.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_IDENTITY_COMMITMENT && p.vData.size())
28         {
29             ::FromVector(p.vData[0], *this);
30             break;
31         }
32     }
33 }
34
35 UniValue CNameReservation::ToUniValue() const
36 {
37     UniValue ret(UniValue::VOBJ);
38     ret.push_back(Pair("name", name));
39     ret.push_back(Pair("salt", salt.GetHex()));
40     ret.push_back(Pair("referral", referral.IsNull() ? "" : EncodeDestination(referral)));
41
42     if (IsVerusActive())
43     {
44         ret.push_back(Pair("parent", ""));
45         ret.push_back(Pair("nameid", EncodeDestination(DecodeDestination(name + "@"))));
46     }
47     else
48     {
49         ret.push_back(Pair("parent", ConnectedChains.ThisChain().name));
50         ret.push_back(Pair("nameid", EncodeDestination(DecodeDestination(name + "." + ConnectedChains.ThisChain().name + "@"))));
51     }
52
53     return ret;
54 }
55
56 CIdentity::CIdentity(const CTransaction &tx, int *voutNum)
57 {
58     std::set<uint160> ids;
59     int idIndex;
60     bool found = false;
61
62     nVersion = PBAAS_VERSION_INVALID;
63     for (int i = 0; i < tx.vout.size(); i++)
64     {
65         CIdentity foundIdentity(tx.vout[i].scriptPubKey);
66         if (foundIdentity.IsValid() && !found)
67         {
68             *this = foundIdentity;
69             found = true;
70             idIndex = i;
71         }
72         else if (foundIdentity.IsValid())
73         {
74             *this = CIdentity();
75         }
76     }
77     if (voutNum && IsValid())
78     {
79         *voutNum = idIndex;
80     }
81 }
82
83 CIdentity CIdentity::LookupIdentity(const CIdentityID &nameID, uint32_t height, uint32_t *pHeightOut, CTxIn *pIdTxIn)
84 {
85     LOCK(mempool.cs);
86
87     CIdentity ret;
88
89     uint32_t heightOut = 0;
90
91     if (!pHeightOut)
92     {
93         pHeightOut = &heightOut;
94     }
95     else
96     {
97         *pHeightOut = 0;
98     }
99
100     CTxIn _idTxIn;
101     if (!pIdTxIn)
102     {
103         pIdTxIn = &_idTxIn;
104     }
105     CTxIn &idTxIn = *pIdTxIn;
106
107     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs, unspentNewIDX;
108
109     uint160 keyID(CCrossChainRPCData::GetConditionID(nameID, EVAL_IDENTITY_PRIMARY));
110
111     if (GetAddressUnspent(keyID, CScript::P2IDX, unspentNewIDX) && GetAddressUnspent(keyID, CScript::P2PKH, unspentOutputs))
112     {
113         // combine searches into 1 vector
114         unspentOutputs.insert(unspentOutputs.begin(), unspentNewIDX.begin(), unspentNewIDX.end());
115         CCoinsViewCache view(pcoinsTip);
116
117         for (auto it = unspentOutputs.begin(); !ret.IsValid() && it != unspentOutputs.end(); it++)
118         {
119             CCoins coins;
120
121             if (view.GetCoins(it->first.txhash, coins))
122             {
123                 if (coins.IsAvailable(it->first.index))
124                 {
125                     // check the mempool for spent/modified
126                     CSpentIndexKey key(it->first.txhash, it->first.index);
127                     CSpentIndexValue value;
128
129                     COptCCParams p;
130                     if (coins.vout[it->first.index].scriptPubKey.IsPayToCryptoCondition(p) &&
131                         p.IsValid() && 
132                         p.evalCode == EVAL_IDENTITY_PRIMARY && 
133                         (ret = CIdentity(coins.vout[it->first.index].scriptPubKey)).IsValid())
134                     {
135                         if (ret.GetID() == nameID)
136                         {
137                             idTxIn = CTxIn(it->first.txhash, it->first.index);
138                             *pHeightOut = it->second.blockHeight;
139                         }
140                         else
141                         {
142                             // got an identity masquerading as another, clear it
143                             ret = CIdentity();
144                         }
145                     }
146                 }
147             }
148         }
149
150         if (height != 0 && (*pHeightOut > height || (height == 1 && *pHeightOut == height)))
151         {
152             *pHeightOut = 0;
153
154             // if we must check up to a specific height that is less than the latest height, do so
155             std::vector<CAddressIndexDbEntry> addressIndex, addressIndex2;
156
157             if (GetAddressIndex(keyID, CScript::P2PKH, addressIndex, 0, height) &&
158                 GetAddressIndex(keyID, CScript::P2IDX, addressIndex2, 0, height) &&
159                 (addressIndex.size() || addressIndex2.size()))
160             {
161                 if (addressIndex2.size())
162                 {
163                     addressIndex.insert(addressIndex.begin(), addressIndex2.begin(), addressIndex2.end());
164                 }
165                 int txIndex = 0;
166                 // look from last backward to find the first valid ID
167                 for (int i = addressIndex.size() - 1; i >= 0; i--)
168                 {
169                     if (addressIndex[i].first.blockHeight < *pHeightOut)
170                     {
171                         break;
172                     }
173                     CTransaction idTx;
174                     uint256 blkHash;
175                     COptCCParams p;
176                     LOCK(mempool.cs);
177                     if (!addressIndex[i].first.spending &&
178                         addressIndex[i].first.txindex > txIndex &&    // always select the latest in a block, if there can be more than one
179                         myGetTransaction(addressIndex[i].first.txhash, idTx, blkHash) &&
180                         idTx.vout[addressIndex[i].first.index].scriptPubKey.IsPayToCryptoCondition(p) &&
181                         p.IsValid() && 
182                         p.evalCode == EVAL_IDENTITY_PRIMARY && 
183                         (ret = CIdentity(idTx.vout[addressIndex[i].first.index].scriptPubKey)).IsValid())
184                     {
185                         idTxIn = CTxIn(addressIndex[i].first.txhash, addressIndex[i].first.index);
186                         *pHeightOut = addressIndex[i].first.blockHeight;
187                         txIndex = addressIndex[i].first.txindex;
188                     }
189                 }
190             }
191             else
192             {
193                 // not found at that height
194                 ret = CIdentity();
195                 idTxIn = CTxIn();
196             }
197         }
198     }
199     return ret;
200 }
201
202 CIdentity CIdentity::LookupIdentity(const std::string &name, uint32_t height, uint32_t *pHeightOut, CTxIn *idTxIn)
203 {
204     return LookupIdentity(GetID(name), height, pHeightOut, idTxIn);
205 }
206
207 CIdentity CIdentity::LookupFirstIdentity(const CIdentityID &idID, uint32_t *pHeightOut, CTxIn *pIdTxIn, CTransaction *pidTx)
208 {
209     CIdentity ret;
210
211     uint32_t heightOut = 0;
212
213     if (!pHeightOut)
214     {
215         pHeightOut = &heightOut;
216     }
217     else
218     {
219         *pHeightOut = 0;
220     }
221
222     CTxIn _idTxIn;
223     if (!pIdTxIn)
224     {
225         pIdTxIn = &_idTxIn;
226     }
227     CTxIn &idTxIn = *pIdTxIn;
228
229     std::vector<CAddressUnspentDbEntry> unspentOutputs, unspentNewIDX;
230
231     CKeyID keyID(CCrossChainRPCData::GetConditionID(idID, EVAL_IDENTITY_RESERVATION));
232
233     if (GetAddressUnspent(keyID, CScript::P2IDX, unspentNewIDX) && GetAddressUnspent(keyID, CScript::P2PKH, unspentOutputs))
234     {
235         // combine searches into 1 vector
236         unspentOutputs.insert(unspentOutputs.begin(), unspentNewIDX.begin(), unspentNewIDX.end());
237         CCoinsViewCache view(pcoinsTip);
238
239         for (auto it = unspentOutputs.begin(); !ret.IsValid() && it != unspentOutputs.end(); it++)
240         {
241             CCoins coins;
242
243             if (view.GetCoins(it->first.txhash, coins))
244             {
245                 if (coins.IsAvailable(it->first.index))
246                 {
247                     COptCCParams p;
248                     if (coins.vout[it->first.index].scriptPubKey.IsPayToCryptoCondition(p) &&
249                         p.IsValid() && 
250                         p.evalCode == EVAL_IDENTITY_RESERVATION)
251                     {
252                         CTransaction idTx;
253                         uint256 blkHash;
254                         if (myGetTransaction(it->first.txhash, idTx, blkHash) && (ret = CIdentity(idTx)).IsValid() && ret.GetID() == idID)
255                         {
256                             int i;
257                             for (i = 0; i < idTx.vout.size(); i++)
258                             {
259                                 COptCCParams p;
260                                 if (idTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.version >= p.VERSION_V3 && p.evalCode == EVAL_IDENTITY_PRIMARY)
261                                 {
262                                     break;
263                                 }
264                             }
265
266                             if (i < idTx.vout.size())
267                             {
268                                 if (pidTx)
269                                 {
270                                     *pidTx = idTx;
271                                 }
272                                 idTxIn = CTxIn(it->first.txhash, i);
273                                 *pHeightOut = it->second.blockHeight;
274                             }
275                         }
276                     }
277                 }
278             }
279         }
280     }
281     return ret;
282 }
283
284 // this enables earliest rejection of invalid CC transactions
285 bool ValidateSpendingIdentityReservation(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height, CCurrencyDefinition issuingChain)
286 {
287     // CHECK #1 - there is only one reservation output, and there is also one identity output that matches the reservation.
288     //            the identity output must come first and have from 0 to 3 referral outputs between it and the reservation.
289     int numReferrers = 0;
290     int identityCount = 0;
291     int reservationCount = 0;
292     CIdentity newIdentity;
293     CNameReservation newName;
294     std::vector<CTxDestination> referrers;
295     bool valid = true;
296
297     bool isPBaaS = CConstVerusSolutionVector::GetVersionByHeight(height) >= CActivationHeight::ACTIVATE_PBAAS;
298
299     for (auto &txout : tx.vout)
300     {
301         COptCCParams p;
302         if (txout.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.version >= p.VERSION_V3)
303         {
304             if (p.evalCode == EVAL_IDENTITY_PRIMARY)
305             {
306                 if (identityCount++ || p.vData.size() < 2)
307                 {
308                     valid = false;
309                     break;
310                 }
311                 newIdentity = CIdentity(p.vData[0]);
312             }
313             else if (p.evalCode == EVAL_IDENTITY_RESERVATION)
314             {
315                 if (reservationCount++ || p.vData.size() < 2)
316                 {
317                     valid = false;
318                     break;
319                 }
320                 newName = CNameReservation(p.vData[0]);
321             }
322             else if (identityCount && !reservationCount)
323             {
324                 if (!issuingChain.IDReferralLevels() || 
325                     p.vKeys.size() < 1 || 
326                     referrers.size() > (issuingChain.IDReferralLevels() - 1) || 
327                     p.evalCode != 0 || 
328                     p.n > 1 || 
329                     p.m != 1 || 
330                     txout.nValue < issuingChain.IDReferralAmount())
331                 {
332                     valid = false;
333                     break;
334                 }
335                 referrers.push_back(p.vKeys[0]);
336             }
337             else if (identityCount != reservationCount)
338             {
339                 valid = false;
340                 break;
341             }
342         }
343     }
344
345     // we can close a commitment UTXO without an identity
346     if (valid && !identityCount)
347     {
348         return state.Error("Transaction may not have an identity reservation without a matching identity");
349     }
350     else if (!valid)
351     {
352         return state.Error("Improperly formed identity definition transaction");
353     }
354
355     std::vector<CTxDestination> dests;
356     int minSigs;
357     txnouttype outType;
358     if (ExtractDestinations(tx.vout[outNum].scriptPubKey, outType, dests, minSigs))
359     {
360         uint160 thisID = newIdentity.GetID();
361         for (auto &dest : dests)
362         {
363             uint160 oneDestID;
364             if (dest.which() == COptCCParams::ADDRTYPE_ID && (oneDestID = GetDestinationID(dest)) != thisID && !CIdentity::LookupIdentity(CIdentityID(oneDestID)).IsValid())
365             {
366                 return state.Error("Destination includes invalid identity");
367             }
368         }
369     }
370
371     // CHECK #2 - must be rooted in this chain
372     if (newIdentity.parent != ConnectedChains.ThisChain().GetID() &&
373         !(isPBaaS && newIdentity.GetID() == ASSETCHAINS_CHAINID && IsVerusActive()))
374     {
375         return state.Error("Identity parent of new identity must be current chain");
376     }
377
378     // CHECK #3 - if dupID is valid, we need to be spending it to recover. redefinition is invalid
379     CTxIn idTxIn;
380     uint32_t priorHeightOut;
381     CIdentity dupID = newIdentity.LookupIdentity(newIdentity.GetID(), height - 1, &priorHeightOut, &idTxIn);
382
383     // CHECK #3a - if dupID is invalid, ensure we spend a matching name commitment
384     if (dupID.IsValid())
385     {
386         return state.Error("Identity already exists");
387     }
388
389     int commitmentHeight = 0;
390     const CCoins *coins;
391     CCoinsView dummy;
392     CCoinsViewCache view(&dummy);
393
394     LOCK(mempool.cs);
395
396     CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
397     view.SetBackend(viewMemPool);
398
399     CCommitmentHash ch;
400     int idx = -1;
401
402     CAmount nValueIn = 0;
403     {
404         // from here, we must spend a matching name commitment
405         std::map<uint256, const CCoins *> txMap;
406         for (auto &oneTxIn : tx.vin)
407         {
408             coins = txMap[oneTxIn.prevout.hash];
409             if (!coins && !(coins = view.AccessCoins(oneTxIn.prevout.hash)))
410             {
411                 //LogPrintf("Cannot access input from output %u of transaction %s in transaction %s\n", oneTxIn.prevout.n, oneTxIn.prevout.hash.GetHex().c_str(), tx.GetHash().GetHex().c_str());
412                 //printf("Cannot access input from output %u of transaction %s in transaction %s\n", oneTxIn.prevout.n, oneTxIn.prevout.hash.GetHex().c_str(), tx.GetHash().GetHex().c_str());
413                 return state.Error("Cannot access input");
414             }
415             txMap[oneTxIn.prevout.hash] = coins;
416
417             if (oneTxIn.prevout.n >= coins->vout.size())
418             {
419                 //extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
420                 //UniValue uniTx;
421                 //TxToJSON(tx, uint256(), uniTx);
422                 //printf("%s\n", uniTx.write(1, 2).c_str());
423                 return state.Error("Input index out of range");
424             }
425
426             COptCCParams p;
427             if (idx == -1 && 
428                 coins->vout[oneTxIn.prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && 
429                 p.IsValid() && 
430                 p.evalCode == EVAL_IDENTITY_COMMITMENT && 
431                 p.vData.size())
432             {
433                 idx = oneTxIn.prevout.n;
434                 ::FromVector(p.vData[0], ch);
435                 commitmentHeight = coins->nHeight;
436                 // this needs to already be in a prior block, or we can't consider it valid
437                 if (!commitmentHeight || commitmentHeight == -1)
438                 {
439                     return state.Error("ID commitment was not already in blockchain");
440                 }
441             }
442         }
443     }
444
445     if (idx == -1 || ch.hash.IsNull())
446     {
447         std::string specificMsg = "Invalid identity commitment in tx: " + tx.GetHash().GetHex();
448         return state.Error(specificMsg);
449     }
450
451     // are we spending a matching name commitment?
452     if (ch.hash != newName.GetCommitment().hash)
453     {
454         return state.Error("Mismatched identity commitment");
455     }
456
457     if (!newName.referral.IsNull() && issuingChain.IDReferralLevels() && !(CIdentity::LookupIdentity(newName.referral, commitmentHeight).IsValid()))
458     {
459         // invalid referral identity
460         return state.Error("Invalid referral identity specified");
461     }
462
463     CReserveTransactionDescriptor rtxd(tx, view, height);
464
465     // CHECK #4 - if blockchain referrals are not enabled or if there is no referring identity, make sure the fees of this transaction are full price for an identity, 
466     // all further checks only if referrals are enabled and there is a referrer
467     if (!issuingChain.IDReferralLevels() || newName.referral.IsNull())
468     {
469         // make sure the fees of this transaction are full price for an identity, all further checks only if there is a referrer
470         if (rtxd.NativeFees() < issuingChain.IDFullRegistrationAmount())
471         {
472             return state.Error("Invalid identity registration - insufficient fee");
473         }
474         return true;
475     }
476
477     // CHECK #5 - ensure that the first referring output goes to the referring identity followed by up 
478     //            to two identities that come from the original definition transaction of the referring identity. account for all outputs between
479     //            identity out and reservation out and ensure that they are correct and pay 20% of the price of an identity
480     uint32_t heightOut = 0;
481     CTransaction referralTx;
482
483     CIdentity firstReferralIdentity = CIdentity::LookupFirstIdentity(newName.referral, &heightOut, &idTxIn, &referralTx);
484
485     // referrer must be mined in when this transaction is put into the mem pool
486     if (heightOut >= height || !firstReferralIdentity.IsValid() || firstReferralIdentity.parent != ASSETCHAINS_CHAINID)
487     {
488         return state.Error("Invalid identity registration referral");
489     }
490
491     bool isReferral = false;
492     std::vector<CTxDestination> checkReferrers = std::vector<CTxDestination>({newName.referral});
493     if (heightOut != 1)
494     {
495         for (auto &txout : referralTx.vout)
496         {
497             COptCCParams p;
498             if (txout.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.version >= p.VERSION_V3)
499             {
500                 if (p.evalCode == EVAL_IDENTITY_PRIMARY)
501                 {
502                     isReferral = true;
503                 }
504                 else if (p.evalCode == EVAL_IDENTITY_RESERVATION)
505                 {
506                     break;
507                 }
508                 else if (isReferral)
509                 {
510                     if (p.vKeys.size() == 0 || p.vKeys[0].which() != COptCCParams::ADDRTYPE_ID)
511                     {
512                         // invalid referral
513                         return state.Error("Invalid identity registration referral outputs");
514                     }
515                     else
516                     {
517                         checkReferrers.push_back(p.vKeys[0]);
518                         if (checkReferrers.size() == issuingChain.IDReferralLevels())
519                         {
520                             break;
521                         }
522                     }
523                 }
524             }
525         }
526     }
527
528     if (referrers.size() != checkReferrers.size())
529     {
530         return state.Error("Invalid identity registration - incorrect referral payments");
531     }
532
533     // make sure all paid referrers are correct
534     for (int i = 0; i < referrers.size(); i++)
535     {
536         if (referrers[i] != checkReferrers[i])
537         {
538             return state.Error("Invalid identity registration - incorrect referral payments");
539         }
540     }
541
542     // CHECK #6 - ensure that the transaction pays the correct mining and refferal fees
543     if (rtxd.NativeFees() < (issuingChain.IDReferredRegistrationAmount() - (referrers.size() * issuingChain.IDReferralAmount())))
544     {
545         return state.Error("Invalid identity registration - insufficient fee");
546     }
547
548     return true;
549 }
550
551 bool PrecheckIdentityReservation(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
552 {
553     CCurrencyDefinition &thisChain = ConnectedChains.ThisChain();
554
555     int numReferrers = 0;
556     int identityCount = 0;
557     int reservationCount = 0;
558     CIdentity newIdentity;
559     CNameReservation newName;
560     std::vector<CTxDestination> referrers;
561     bool valid = true;
562
563     AssertLockHeld(cs_main);
564
565     for (auto &txout : tx.vout)
566     {
567         COptCCParams p;
568         if (txout.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.version >= p.VERSION_V3)
569         {
570             if (p.evalCode == EVAL_IDENTITY_PRIMARY)
571             {
572                 if (identityCount++ || p.vData.size() < 2)
573                 {
574                     valid = false;
575                     break;
576                 }
577                 newIdentity = CIdentity(p.vData[0]);
578             }
579             else if (p.evalCode == EVAL_IDENTITY_RESERVATION)
580             {
581                 if (reservationCount++ || p.vData.size() < 2)
582                 {
583                     valid = false;
584                     break;
585                 }
586                 newName = CNameReservation(p.vData[0]);
587             }
588             else if (identityCount && !reservationCount)
589             {
590                 if (!thisChain.IDReferralLevels() || 
591                     p.vKeys.size() < 1 || 
592                     referrers.size() > (thisChain.IDReferralLevels() - 1) || 
593                     p.evalCode != 0 || 
594                     p.n > 1 || 
595                     p.m != 1 || 
596                     txout.nValue < thisChain.IDReferralAmount())
597                 {
598                     valid = false;
599                     break;
600                 }
601                 referrers.push_back(p.vKeys[0]);
602             }
603             else if (identityCount != reservationCount)
604             {
605                 valid = false;
606                 break;
607             }
608         }
609     }
610
611     // we can close a commitment UTXO without an identity
612     if (valid && !identityCount)
613     {
614         return state.Error("Transaction may not have an identity reservation without a matching identity");
615     }
616     else if (!valid)
617     {
618         return state.Error("Improperly formed identity definition transaction");
619     }
620
621     int commitmentHeight = 0;
622
623     LOCK2(cs_main, mempool.cs);
624
625     CCommitmentHash ch;
626     int idx = -1;
627
628     CAmount nValueIn = 0;
629     {
630         LOCK2(cs_main, mempool.cs);
631
632         // from here, we must spend a matching name commitment
633         std::map<uint256, CTransaction> txMap;
634         uint256 hashBlk;
635         for (auto &oneTxIn : tx.vin)
636         {
637             CTransaction sourceTx = txMap[oneTxIn.prevout.hash];
638             if (sourceTx.nVersion <= sourceTx.SPROUT_MIN_CURRENT_VERSION && !myGetTransaction(oneTxIn.prevout.hash, sourceTx, hashBlk))
639             {
640                 //LogPrintf("Cannot access input from output %u of transaction %s in transaction %s\n", oneTxIn.prevout.n, oneTxIn.prevout.hash.GetHex().c_str(), tx.GetHash().GetHex().c_str());
641                 //printf("Cannot access input from output %u of transaction %s in transaction %s\n", oneTxIn.prevout.n, oneTxIn.prevout.hash.GetHex().c_str(), tx.GetHash().GetHex().c_str());
642                 return state.Error("Cannot access input");
643             }
644             txMap[oneTxIn.prevout.hash] = sourceTx;
645
646             if (oneTxIn.prevout.n >= sourceTx.vout.size())
647             {
648                 //extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
649                 //UniValue uniTx;
650                 //TxToJSON(tx, uint256(), uniTx);
651                 //printf("%s\n", uniTx.write(1, 2).c_str());
652                 return state.Error("Input index out of range");
653             }
654
655             COptCCParams p;
656             if (idx == -1 && 
657                 sourceTx.vout[oneTxIn.prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && 
658                 p.IsValid() && 
659                 p.evalCode == EVAL_IDENTITY_COMMITMENT && 
660                 p.vData.size())
661             {
662                 idx = oneTxIn.prevout.n;
663                 ::FromVector(p.vData[0], ch);
664             }
665         }
666     }
667
668     if (idx == -1 || ch.hash.IsNull())
669     {
670         std::string specificMsg = "Invalid identity commitment in tx: " + tx.GetHash().GetHex();
671         return state.Error(specificMsg);
672     }
673
674     // are we spending a matching name commitment?
675     if (ch.hash != newName.GetCommitment().hash)
676     {
677         return state.Error("Mismatched identity commitment");
678     }
679
680     return true;
681 }
682
683 // with the thorough check for an identity reservation, the only thing we need to check is that either 1) this transaction includes an identity reservation output or 2)
684 // this transaction spends a prior identity transaction that does not create a clearly invalid mutation between the two
685 bool PrecheckIdentityPrimary(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
686 {
687     AssertLockHeld(cs_main);
688
689     bool validReservation = false;
690     bool validIdentity = false;
691
692     CNameReservation nameRes;
693     CIdentity identity;
694     COptCCParams p, identityP;
695
696     uint32_t networkVersion = CConstVerusSolutionVector::GetVersionByHeight(height);
697     bool isPBaaS = networkVersion >= CActivationHeight::ACTIVATE_PBAAS;
698
699     for (int i = 0; i < tx.vout.size(); i++)
700     {
701         CIdentity checkIdentity;
702         auto &output = tx.vout[i];
703         if (output.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.version >= COptCCParams::VERSION_V3 && p.evalCode == EVAL_IDENTITY_RESERVATION && p.vData.size() > 1 && (nameRes = CNameReservation(p.vData[0])).IsValid())
704         {
705             // twice through makes it invalid
706             if (validReservation)
707             {
708                 return state.Error("Invalid multiple identity reservations on one transaction");
709             }
710             validReservation = true;
711         }
712         else if (p.IsValid() && p.version >= COptCCParams::VERSION_V3 && p.evalCode == EVAL_IDENTITY_PRIMARY && p.vData.size() > 1 && (checkIdentity = CIdentity(p.vData[0])).IsValid())
713         {
714             // twice through makes it invalid
715             if (!(isPBaaS && height == 1) && validIdentity)
716             {
717                 return state.Error("Invalid multiple identity definitions on one transaction");
718             }
719             if (i == outNum)
720             {
721                 identityP = p;
722                 identity = checkIdentity;
723             }
724             validIdentity = true;
725         }
726     }
727
728     if (!validIdentity)
729     {
730         return state.Error("Invalid identity definition");
731     }
732
733     CIdentityID idID = identity.GetID();
734     p = identityP;
735
736     bool advancedIdentity = CConstVerusSolutionVector::GetVersionByHeight(height) >= CActivationHeight::ACTIVATE_PBAAS;
737     if (advancedIdentity)
738     {
739         COptCCParams master;
740         if (identity.nVersion < identity.VERSION_PBAAS)
741         {
742             return state.Error("Inadequate identity version for post-PBaaS activation");
743         }
744         if (p.vData.size() < 3 ||
745             !(master = COptCCParams(p.vData.back())).IsValid() ||
746             master.evalCode != 0 ||
747             master.m != 1)
748         {
749             return state.Error("Invalid identity output destinations and/or configuration");
750         }
751
752         // we need to have at least 2 of 3 authority spend conditions mandatory at a top level for any primary ID output
753         bool revocation = false, recovery = false;
754         bool primaryValid = false, revocationValid = false, recoveryValid = false;
755         for (auto dest : p.vKeys)
756         {
757             if (dest.which() == COptCCParams::ADDRTYPE_ID && (idID == GetDestinationID(dest)) && dest.which() == COptCCParams::ADDRTYPE_ID)
758             {
759                 primaryValid = true;
760             }
761         }
762         if (!primaryValid)
763         {
764             std::string errorOut = "Primary identity output condition of \"" + identity.name + "\" is not spendable by self";
765             return state.Error(errorOut.c_str());
766         }
767         for (int i = 1; i < p.vData.size() - 1; i++)
768         {
769             COptCCParams oneP(p.vData[i]);
770             // must be valid and composable
771             if (!oneP.IsValid() || oneP.version < oneP.VERSION_V3)
772             {
773                 std::string errorOut = "Invalid output condition from identity: \"" + identity.name + "\"";
774                 return state.Error(errorOut.c_str());
775             }
776
777             if (oneP.evalCode == EVAL_IDENTITY_REVOKE)
778             {
779                 // no dups
780                 if (!revocation)
781                 {
782                     revocation = true;
783                     if (oneP.vKeys.size() == 1 &&
784                         oneP.m == 1 &&
785                         oneP.n == 1 &&
786                         oneP.vKeys[0].which() == COptCCParams::ADDRTYPE_ID &&
787                         identity.revocationAuthority == GetDestinationID(oneP.vKeys[0]))
788                     {
789                         revocationValid = true;
790                     }
791                 }
792                 else
793                 {
794                     revocationValid = false;
795                 }
796             }
797             else if (oneP.evalCode == EVAL_IDENTITY_RECOVER)
798             {
799                 if (!recovery)
800                 {
801                     recovery = true;
802                     if (oneP.vKeys.size() == 1 &&
803                         oneP.m == 1 &&
804                         oneP.n == 1 &&
805                         oneP.vKeys[0].which() == COptCCParams::ADDRTYPE_ID &&
806                         identity.recoveryAuthority == GetDestinationID(oneP.vKeys[0]))
807                     {
808                         recoveryValid = true;
809                     }
810                 }
811                 else
812                 {
813                     recoveryValid = false;
814                 }
815             }
816         }
817
818         // we need separate spend conditions for both revoke and recover in all cases
819         bool isRevoked = identity.IsRevoked();
820         if ((!isRevoked && !revocationValid) || (isRevoked && !recoveryValid))
821         {
822             std::string errorOut = "Primary identity output \"" + identity.name + "\" must be spendable by revocation and recovery authorities";
823             return state.Error(errorOut.c_str());
824         }
825     }
826     else
827     {
828         if (identity.nVersion >= identity.VERSION_PBAAS)
829         {
830             return state.Error("Invalid identity version before PBaaS activation");
831         }
832
833         // ensure that we have all required spend conditions for primary, revocation, and recovery
834         // if there are additional spend conditions, their addition or removal is checked for validity
835         // depending on which of the mandatory spend conditions is authorized.
836         COptCCParams master;
837         if (p.vData.size() < 4 || !(master = COptCCParams(p.vData.back())).IsValid() || master.evalCode != 0 || master.m != 1)
838         {
839             // we need to have 3 authority spend conditions mandatory at a top level for any primary ID output
840             bool primary = false, revocation = false, recovery = false;
841
842             for (auto dest : p.vKeys)
843             {
844                 if (dest.which() == COptCCParams::ADDRTYPE_ID && (idID == GetDestinationID(dest)))
845                 {
846                     primary = true;
847                 }
848             }
849             if (!primary)
850             {
851                 std::string errorOut = "Primary identity output condition of \"" + identity.name + "\" is not spendable by self";
852                 return state.Error(errorOut.c_str());
853             }
854             for (int i = 1; i < p.vData.size() - 1; i++)
855             {
856                 COptCCParams oneP(p.vData[i]);
857                 // must be valid and composable
858                 if (!oneP.IsValid() || oneP.version < oneP.VERSION_V3)
859                 {
860                     std::string errorOut = "Invalid output condition from identity: \"" + identity.name + "\"";
861                     return state.Error(errorOut.c_str());
862                 }
863
864                 if (oneP.evalCode == EVAL_IDENTITY_REVOKE || oneP.evalCode == EVAL_IDENTITY_RECOVER)
865                 {
866                     for (auto dest : oneP.vKeys)
867                     {
868                         if (dest.which() == COptCCParams::ADDRTYPE_ID)
869                         {
870                             if (oneP.evalCode == EVAL_IDENTITY_REVOKE && (identity.revocationAuthority == GetDestinationID(dest)))
871                             {
872                                 revocation = true;
873                             }
874                             else if (oneP.evalCode == EVAL_IDENTITY_RECOVER && (identity.recoveryAuthority == GetDestinationID(dest)))
875                             {
876                                 recovery = true;
877                             }
878                         }
879                     }
880                 }
881             }
882
883             // we need separate spend conditions for both revoke and recover in all cases
884             if (!revocation || !recovery)
885             {
886                 std::string errorOut = "Primary identity output \"" + identity.name + "\" must be spendable by revocation and recovery authorities";
887                 return state.Error(errorOut.c_str());
888             }
889         }
890     }
891
892     extern uint160 VERUS_CHAINID;
893     extern std::string VERUS_CHAINNAME;
894
895     // compare commitment without regard to case or other textual transformations that are irrelevant to matching
896     uint160 parentChain = ConnectedChains.ThisChain().GetID();
897     if (isPBaaS && identity.GetID() == ASSETCHAINS_CHAINID && IsVerusActive())
898     {
899         parentChain.SetNull();
900     }
901     if (validReservation && identity.GetID(nameRes.name, parentChain) == identity.GetID())
902     {
903         return true;
904     }
905
906     // if we made it to here without an early, positive exit, we must determine that we are spending a matching identity, and if so, all is fine so far
907     CTransaction inTx;
908     uint256 blkHash;
909     LOCK(mempool.cs);
910     for (auto &input : tx.vin)
911     {
912         // first time through may be null
913         if ((!input.prevout.hash.IsNull() && input.prevout.hash == inTx.GetHash()) || myGetTransaction(input.prevout.hash, inTx, blkHash))
914         {
915             if (inTx.vout[input.prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_IDENTITY_PRIMARY && p.vData.size() > 1 && (identity = CIdentity(p.vData[0])).IsValid())
916             {
917                 return true;
918             }
919         }
920     }
921
922     // TODO: HARDENING at block one, a new PBaaS chain can mint IDs
923     // ensure they are valid as per the launch parameters
924     if (isPBaaS && height == 1)
925     {
926         return true;
927     }
928
929     return state.Error("Invalid primary identity - does not include identity reservation or spend matching identity");
930 }
931
932 CIdentity GetOldIdentity(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx=nullptr, uint32_t *pHeight=nullptr);
933 CIdentity GetOldIdentity(const CTransaction &spendingTx, uint32_t nIn, CTransaction *pSourceTx, uint32_t *pHeight)
934 {
935     CTransaction _sourceTx;
936     CTransaction &sourceTx(pSourceTx ? *pSourceTx : _sourceTx);
937
938     // if not fulfilled, ensure that no part of the primary identity is modified
939     CIdentity oldIdentity;
940     uint256 blkHash;
941     if (myGetTransaction(spendingTx.vin[nIn].prevout.hash, sourceTx, blkHash))
942     {
943         if (pHeight)
944         {
945             auto bIt = mapBlockIndex.find(blkHash);
946             if (bIt == mapBlockIndex.end() || !bIt->second)
947             {
948                 *pHeight = chainActive.Height();
949             }
950             else
951             {
952                 *pHeight = bIt->second->GetHeight();
953             }
954         }
955         COptCCParams p;
956         if (sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
957             p.IsValid() && 
958             p.evalCode == EVAL_IDENTITY_PRIMARY && 
959             p.version >= COptCCParams::VERSION_V3 &&
960             p.vData.size() > 1)
961         {
962             oldIdentity = CIdentity(p.vData[0]);
963         }
964     }
965     return oldIdentity;
966 }
967
968 bool ValidateIdentityPrimary(struct CCcontract_info *cp, Eval* eval, const CTransaction &spendingTx, uint32_t nIn, bool fulfilled)
969 {
970     CTransaction sourceTx;
971     CIdentity oldIdentity = GetOldIdentity(spendingTx, nIn, &sourceTx);
972
973     if (!oldIdentity.IsValid())
974     {
975         return eval->Error("Spending invalid identity");
976     }
977
978     int idIndex;
979     CIdentity newIdentity(spendingTx, &idIndex);
980     if (!newIdentity.IsValid())
981     {
982         return eval->Error("Attempting to define invalid identity");
983     }
984
985     if (!chainActive.LastTip())
986     {
987         return eval->Error("unable to find chain tip");
988     }
989     uint32_t height = chainActive.LastTip()->GetHeight() + 1;
990
991     if (oldIdentity.IsInvalidMutation(newIdentity, height, spendingTx.nExpiryHeight))
992     {
993         LogPrintf("Invalid identity modification %s\n", spendingTx.GetHash().GetHex().c_str());
994         return eval->Error("Invalid identity modification");
995     }
996
997     // if not fullfilled and not revoked, we are responsible for rejecting any modification of
998     // data under primary authority control
999     if (!fulfilled && !oldIdentity.IsRevoked())
1000     {
1001         if (oldIdentity.IsPrimaryMutation(newIdentity, height))
1002         {
1003             return eval->Error("Unauthorized identity modification");
1004         }
1005         // make sure that the primary spend conditions are not modified
1006         COptCCParams p, q;
1007         sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p);
1008         spendingTx.vout[idIndex].scriptPubKey.IsPayToCryptoCondition(q);
1009
1010         if (q.evalCode != EVAL_IDENTITY_PRIMARY ||
1011             p.version > q.version ||
1012             p.m != q.m ||
1013             p.n != q.n ||
1014             p.vKeys != q.vKeys)
1015         {
1016             return eval->Error("Unauthorized modification of identity primary spend condition");
1017         }
1018     }
1019
1020     return true;
1021 }
1022
1023 bool ValidateIdentityRevoke(struct CCcontract_info *cp, Eval* eval, const CTransaction &spendingTx, uint32_t nIn, bool fulfilled)
1024 {
1025     CTransaction sourceTx;
1026     CIdentity oldIdentity = GetOldIdentity(spendingTx, nIn, &sourceTx);
1027     if (!oldIdentity.IsValid())
1028     {
1029         return eval->Error("Invalid source identity");
1030     }
1031
1032     int idIndex;
1033     CIdentity newIdentity(spendingTx, &idIndex);
1034     if (!newIdentity.IsValid())
1035     {
1036         return eval->Error("Attempting to replace identity with one that is invalid");
1037     }
1038
1039     if (!chainActive.LastTip())
1040     {
1041         return eval->Error("unable to find chain tip");
1042     }
1043     uint32_t height = chainActive.LastTip()->GetHeight() + 1;
1044
1045     if (oldIdentity.IsInvalidMutation(newIdentity, height, spendingTx.nExpiryHeight))
1046     {
1047         return eval->Error("Invalid identity modification");
1048     }
1049
1050     if (oldIdentity.IsRevocation(newIdentity) && oldIdentity.recoveryAuthority == oldIdentity.GetID())
1051     {
1052         return eval->Error("Cannot revoke an identity with self as the recovery authority");
1053     }
1054
1055     // make sure that spend conditions are valid and revocation spend conditions are not modified
1056     COptCCParams p, q;
1057
1058     spendingTx.vout[idIndex].scriptPubKey.IsPayToCryptoCondition(q);
1059
1060     if (q.evalCode != EVAL_IDENTITY_PRIMARY)
1061     {
1062         return eval->Error("Invalid identity output in spending transaction");
1063     }
1064
1065     bool advanced = newIdentity.nVersion >= newIdentity.VERSION_PBAAS;
1066
1067     if (advanced)
1068     {
1069         // if not fulfilled, neither recovery data nor its spend condition may be modified
1070         if (!fulfilled && !oldIdentity.IsRevoked())
1071         {
1072             if (oldIdentity.IsRevocation(newIdentity) || oldIdentity.IsRevocationMutation(newIdentity, height))
1073             {
1074                 return eval->Error("Unauthorized modification of revocation information");
1075             }
1076         }
1077         // aside from that, validity of spend conditions is done in advanced precheck
1078         return true;
1079     }
1080
1081     COptCCParams oldRevokeP, newRevokeP;
1082
1083     for (int i = 1; i < q.vData.size() - 1; i++)
1084     {
1085         COptCCParams oneP(q.vData[i]);
1086         // must be valid and composable
1087         if (!oneP.IsValid() || oneP.version < oneP.VERSION_V3)
1088         {
1089             std::string errorOut = "Invalid output condition from identity: \"" + newIdentity.name + "\"";
1090             return eval->Error(errorOut.c_str());
1091         }
1092
1093         if (oneP.evalCode == EVAL_IDENTITY_REVOKE)
1094         {
1095             if (newRevokeP.IsValid())
1096             {
1097                 std::string errorOut = "Invalid output condition from identity: \"" + newIdentity.name + "\", more than one revocation condition";
1098                 return eval->Error(errorOut.c_str());
1099             }
1100             newRevokeP = oneP;
1101         }
1102     }
1103
1104     if (!newRevokeP.IsValid())
1105     {
1106         std::string errorOut = "Invalid revocation output condition for identity: \"" + newIdentity.name + "\"";
1107         return eval->Error(errorOut.c_str());
1108     }
1109
1110     // if not fulfilled, neither revocation data nor its spend condition may be modified
1111     if (!fulfilled)
1112     {
1113         sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p);
1114
1115         if (oldIdentity.IsRevocation(newIdentity) || oldIdentity.IsRevocationMutation(newIdentity, height))
1116         {
1117             return eval->Error("Unauthorized modification of revocation information");
1118         }
1119
1120         for (int i = 1; i < p.vData.size() - 1; i++)
1121         {
1122             COptCCParams oneP(p.vData[i]);
1123             if (oneP.evalCode == EVAL_IDENTITY_REVOKE)
1124             {
1125                 oldRevokeP = oneP;
1126             }
1127         }
1128
1129         if (!oldRevokeP.IsValid() || 
1130             !newRevokeP.IsValid() ||
1131             oldRevokeP.version > newRevokeP.version ||
1132             oldRevokeP.m != newRevokeP.m ||
1133             oldRevokeP.n != newRevokeP.n ||
1134             oldRevokeP.vKeys != newRevokeP.vKeys)
1135         {
1136             return eval->Error("Unauthorized modification of identity revocation spend condition");
1137         }
1138     }
1139
1140     return true;
1141 }
1142
1143 bool ValidateIdentityRecover(struct CCcontract_info *cp, Eval* eval, const CTransaction &spendingTx, uint32_t nIn, bool fulfilled)
1144 {
1145     CTransaction sourceTx;
1146     CIdentity oldIdentity = GetOldIdentity(spendingTx, nIn, &sourceTx);
1147     if (!oldIdentity.IsValid())
1148     {
1149         return eval->Error("Invalid source identity");
1150     }
1151
1152     int idIndex;
1153     CIdentity newIdentity(spendingTx, &idIndex);
1154     if (!newIdentity.IsValid())
1155     {
1156         return eval->Error("Attempting to replace identity with one that is invalid");
1157     }
1158
1159     if (!chainActive.LastTip())
1160     {
1161         return eval->Error("unable to find chain tip");
1162     }
1163     uint32_t height = chainActive.LastTip()->GetHeight() + 1;
1164
1165     if (oldIdentity.IsInvalidMutation(newIdentity, height, spendingTx.nExpiryHeight))
1166     {
1167         return eval->Error("Invalid identity modification");
1168     }
1169
1170     // make sure that spend conditions are valid and revocation spend conditions are not modified
1171     COptCCParams p, q;
1172
1173     spendingTx.vout[idIndex].scriptPubKey.IsPayToCryptoCondition(q);
1174
1175     if (q.evalCode != EVAL_IDENTITY_PRIMARY)
1176     {
1177         return eval->Error("Invalid identity output in spending transaction");
1178     }
1179
1180     bool advanced = newIdentity.nVersion >= newIdentity.VERSION_PBAAS;
1181
1182     if (advanced)
1183     {
1184         // if not fulfilled, neither recovery data nor its spend condition may be modified
1185         if (!fulfilled)
1186         {
1187             if (oldIdentity.IsRecovery(newIdentity) || oldIdentity.IsRecoveryMutation(newIdentity, height))
1188             {
1189                 return eval->Error("Unauthorized modification of recovery information");
1190             }
1191
1192             // if revoked, only fulfilled recovery condition allows any mutation
1193             if (oldIdentity.IsRevoked() &&
1194                 (oldIdentity.IsPrimaryMutation(newIdentity, height) ||
1195                  oldIdentity.IsRevocationMutation(newIdentity, height)))
1196             {
1197                 return eval->Error("Unauthorized modification of revoked identity without recovery authority");
1198             }
1199         }
1200         // aside from that, validity of spend conditions is done in advanced precheck
1201         return true;
1202     }
1203
1204     COptCCParams oldRecoverP, newRecoverP;
1205
1206     for (int i = 1; i < q.vData.size() - 1; i++)
1207     {
1208         COptCCParams oneP(q.vData[i]);
1209         // must be valid and composable
1210         if (!oneP.IsValid() || oneP.version < oneP.VERSION_V3)
1211         {
1212             std::string errorOut = "Invalid output condition from identity: \"" + newIdentity.name + "\"";
1213             return eval->Error(errorOut.c_str());
1214         }
1215
1216         if (oneP.evalCode == EVAL_IDENTITY_RECOVER)
1217         {
1218             if (newRecoverP.IsValid())
1219             {
1220                 std::string errorOut = "Invalid output condition from identity: \"" + newIdentity.name + "\", more than one recovery condition";
1221                 return eval->Error(errorOut.c_str());
1222             }
1223             newRecoverP = oneP;
1224         }
1225     }
1226
1227     if (!newRecoverP.IsValid())
1228     {
1229         std::string errorOut = "Invalid recovery output condition for identity: \"" + newIdentity.name + "\"";
1230         return eval->Error(errorOut.c_str());
1231     }
1232
1233     // if not fulfilled, neither recovery data nor its spend condition may be modified
1234     if (!fulfilled)
1235     {
1236         // if revoked, only fulfilled recovery condition allows primary mutation
1237         if (oldIdentity.IsRevoked() && (oldIdentity.IsPrimaryMutation(newIdentity, height)))
1238         {
1239             return eval->Error("Unauthorized modification of revoked identity without recovery authority");
1240         }
1241
1242         if (oldIdentity.IsRecovery(newIdentity) || oldIdentity.IsRecoveryMutation(newIdentity, height))
1243         {
1244             return eval->Error("Unauthorized modification of recovery information");
1245         }
1246
1247         sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p);
1248
1249         for (int i = 1; i < p.vData.size() - 1; i++)
1250         {
1251             COptCCParams oneP(p.vData[i]);
1252             if (oneP.evalCode == EVAL_IDENTITY_RECOVER)
1253             {
1254                 oldRecoverP = oneP;
1255             }
1256         }
1257
1258         if (!oldRecoverP.IsValid() || 
1259             !newRecoverP.IsValid() ||
1260             oldRecoverP.version > newRecoverP.version ||
1261             oldRecoverP.m != newRecoverP.m ||
1262             oldRecoverP.n != newRecoverP.n ||
1263             oldRecoverP.vKeys != newRecoverP.vKeys)
1264         {
1265             return eval->Error("Unauthorized modification of identity recovery spend condition");
1266         }
1267     }
1268     return true;
1269 }
1270
1271 bool ValidateIdentityCommitment(struct CCcontract_info *cp, Eval* eval, const CTransaction &spendingTx, uint32_t nIn, bool fulfilled)
1272 {
1273     // if not fulfilled, fail
1274     if (!fulfilled)
1275     {
1276         return eval->Error("missing required signature to spend");
1277     }
1278
1279     if (!chainActive.LastTip())
1280     {
1281         return eval->Error("unable to find chain tip");
1282     }
1283     uint32_t height = chainActive.LastTip()->GetHeight() + 1;
1284
1285     CCommitmentHash ch;
1286     CNameReservation reservation;
1287     CTransaction sourceTx;
1288     uint256 blkHash;
1289
1290     LOCK(mempool.cs);
1291     if (myGetTransaction(spendingTx.vin[nIn].prevout.hash, sourceTx, blkHash))
1292     {
1293         COptCCParams p;
1294         if (sourceTx.vout[spendingTx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
1295             p.IsValid() && 
1296             p.evalCode == EVAL_IDENTITY_COMMITMENT && 
1297             p.version >= COptCCParams::VERSION_V3 &&
1298             p.vData.size() > 1 &&
1299             !blkHash.IsNull())
1300         {
1301             ch = CCommitmentHash(p.vData[0]);
1302         }
1303         else
1304         {
1305             return eval->Error("Invalid source commitment output");
1306         }
1307
1308         int i;
1309         int outputNum = -1;
1310         for (i = 0; i < spendingTx.vout.size(); i++)
1311         {
1312             auto &output = spendingTx.vout[i];
1313             if (output.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_IDENTITY_RESERVATION && p.vData.size() > 1)
1314             {
1315                 if (reservation.IsValid())
1316                 {
1317                     return eval->Error("Invalid identity reservation output spend");
1318                 }
1319                 else
1320                 {
1321                     reservation = CNameReservation(p.vData[0]);
1322                     if (!reservation.IsValid() || reservation.GetCommitment().hash != ch.hash)
1323                     {
1324                         return eval->Error("Identity reservation output spend does not match commitment");
1325                     }
1326
1327                     outputNum = i;
1328                 }
1329             }
1330         }
1331         if (outputNum != -1)
1332         {
1333             // can only be spent by a matching name reservation if validated
1334             // if there is no matching name reservation, it can be spent just by a valid signature
1335             CCurrencyDefinition &thisChain = ConnectedChains.ThisChain();
1336             return ValidateSpendingIdentityReservation(spendingTx, outputNum, eval->state, height, thisChain);
1337         }
1338     }
1339     else
1340     {
1341         printf("%s: error getting transaction %s to spend\n", __func__, spendingTx.vin[nIn].prevout.hash.GetHex().c_str());
1342     }
1343     
1344     return true;
1345 }
1346
1347 bool ValidateIdentityReservation(struct CCcontract_info *cp, Eval* eval, const CTransaction &spendingTx, uint32_t nIn, bool fulfilled)
1348 {
1349     // identity reservations are unspendable
1350     return eval->Error("Identity reservations are unspendable");
1351 }
1352
1353 // quantum key outputs can be spent without restriction
1354 bool ValidateQuantumKeyOut(struct CCcontract_info *cp, Eval* eval, const CTransaction &spendingTx, uint32_t nIn, bool fulfilled)
1355 {
1356     return true;
1357 }
1358
1359 bool IsQuantumKeyOutInput(const CScript &scriptSig)
1360 {
1361     return false;
1362 }
1363
1364 bool PrecheckQuantumKeyOut(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
1365 {
1366     // inactive for now
1367     return false;
1368 }
1369
1370 bool IsIdentityInput(const CScript &scriptSig)
1371 {
1372     return false;
1373 }
1374
1375 bool ValidateFinalizeExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
1376 {
1377     return true;
1378 }
1379
1380 bool IsFinalizeExportInput(const CScript &scriptSig)
1381 {
1382     return false;
1383 }
1384
1385 bool FinalizeExportContextualPreCheck(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
1386 {
1387     return true;
1388 }
1389
This page took 0.102802 seconds and 4 git commands to generate.