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