Smart transaction staking fixes
[VerusCoin.git] / src / cc / StakeGuard.cpp
1 /********************************************************************
2  * (C) 2018 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 crypto-condition eval solves the problem of nothing-at-stake
8  * in a proof of stake consensus system.
9  * 
10  */
11
12 #include "StakeGuard.h"
13 #include "script/script.h"
14 #include "main.h"
15 #include "hash.h"
16 #include "key_io.h"
17
18 #include <vector>
19 #include <map>
20
21 #include "streams.h"
22
23 extern int32_t VERUS_MIN_STAKEAGE;
24
25 bool IsData(opcodetype opcode)
26 {
27     return (opcode >= 0 && opcode <= OP_PUSHDATA4) || (opcode >= OP_1 && opcode <= OP_16);
28 }
29
30 bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector<std::vector<unsigned char>> &vData)
31 {
32     bool isValid = stakeTx.vout[stakeTx.vout.size() - 1].scriptPubKey.GetOpretData(vData);
33
34     if (isValid && vData.size() == 1)
35     {
36         CScript data = CScript(vData[0].begin(), vData[0].end());
37         vData.clear();
38
39         uint32_t bytesTotal;
40         CScript::const_iterator pc = data.begin();
41         std::vector<unsigned char> vch = std::vector<unsigned char>();
42         opcodetype op;
43         bool moreData = true;
44
45         for (bytesTotal = vch.size(); 
46              bytesTotal <= nMaxDatacarrierBytes && !(isValid = (pc == data.end())) && (moreData = data.GetOp(pc, op, vch)) && IsData(op); 
47              bytesTotal += vch.size())
48         {
49             if (op >= OP_1 && op <= OP_16)
50             {
51                 vch.resize(1);
52                 vch[0] = (op - OP_1) + 1;
53             }
54             vData.push_back(vch);
55         }
56         
57         // if we ran out of data, we're ok
58         if (isValid && (vData.size() >= CStakeParams::STAKE_MINPARAMS) && (vData.size() <= CStakeParams::STAKE_MAXPARAMS))
59         {
60             return true;
61         }
62     }
63     return false;
64 }
65
66 CStakeParams::CStakeParams(const std::vector<std::vector<unsigned char>> &vData)
67 {
68     // An original format stake OP_RETURN contains:
69     // 1. source block height in little endian 32 bit
70     // 2. target block height in little endian 32 bit
71     // 3. 32 byte prev block hash
72     // 4. 33 byte pubkey, or not present to use same as stake destination
73     // New format serialization and deserialization is handled by normal stream serialization.
74     version = VERSION_INVALID;
75     srcHeight = 0;
76     blkHeight = 0;
77     if (vData[0].size() == 1 && 
78         vData[0][0] == OPRETTYPE_STAKEPARAMS2 &&
79         vData.size() == 2)
80     {
81         ::FromVector(vData[1], *this);
82     }
83     else if (vData[0].size() == 1 && 
84         vData[0][0] == OPRETTYPE_STAKEPARAMS && vData[1].size() <= 4 && 
85         vData[2].size() <= 4 && 
86         vData[3].size() == sizeof(prevHash) &&
87         (vData.size() == STAKE_MINPARAMS || (vData.size() == STAKE_MAXPARAMS && vData[4].size() == 33)))
88     {
89         version = VERSION_ORIGINAL;
90         for (int i = 0, size = vData[1].size(); i < size; i++)
91         {
92             srcHeight = srcHeight | vData[1][i] << (8 * i);
93         }
94         for (int i = 0, size = vData[2].size(); i < size; i++)
95         {
96             blkHeight = blkHeight | vData[2][i] << (8 * i);
97         }
98
99         prevHash = uint256(vData[3]);
100
101         if (vData.size() == 4)
102         {
103             pk = CPubKey();
104         }
105         else if (vData[4].size() == 33)
106         {
107             pk = CPubKey(vData[4]);
108             if (!pk.IsValid())
109             {
110                 // invalidate
111                 srcHeight = 0;
112                 version = VERSION_INVALID;
113             }
114         }
115         else
116         {
117             // invalidate
118             srcHeight = 0;
119             version = VERSION_INVALID;
120         }
121     }
122 }
123
124 bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams)
125 {
126     std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
127
128     //printf("opret stake script: %s\nvalue at scriptPubKey[0]: %x\n", stakeTx.vout[1].scriptPubKey.ToString().c_str(), stakeTx.vout[1].scriptPubKey[0]);
129
130     if (stakeTx.vin.size() == 1 && 
131         stakeTx.vout.size() == 2 && 
132         stakeTx.vout[0].nValue > 0 && 
133         stakeTx.vout[1].scriptPubKey.IsOpReturn() && 
134         UnpackStakeOpRet(stakeTx, vData))
135     {
136         stakeParams = CStakeParams(vData);
137         return stakeParams.IsValid();
138     }
139     return false;
140 }
141
142 // this validates the format of the stake transaction and, optionally, whether or not it is 
143 // properly signed to spend the source stake.
144 // it does not validate the relationship to a coinbase guard, PoS eligibility or the actual stake spend.
145 // the only time it matters is to validate a properly formed stake transaction for either pre-check before PoS validity check, 
146 // or to validate the stake transaction on a fork that will be used to spend a winning stake that cheated by being posted
147 // on two fork chains
148 bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakeParams, bool validateSig)
149 {
150     std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
151
152     // a valid stake transaction has one input and two outputs, one output is the monetary value and one is an op_ret with CStakeParams
153     // stake output #1 must be P2PK or P2PKH, unless a delegate for the coinbase is specified
154     if (GetStakeParams(stakeTx, stakeParams))
155     {
156         // if we have gotten this far and are still valid, we need to validate everything else
157         // even if the utxo is spent, this can succeed, as it only checks that is was ever valid
158         CTransaction srcTx = CTransaction();
159         uint256 blkHash = uint256();
160         txnouttype txType;
161         CBlockIndex *pindex;
162         if (myGetTransaction(stakeTx.vin[0].prevout.hash, srcTx, blkHash))
163         {
164             BlockMap::const_iterator it = mapBlockIndex.find(blkHash);
165             if (it != mapBlockIndex.end() && (pindex = it->second) != NULL && chainActive.Contains(pindex))
166             {
167                 std::vector<std::vector<unsigned char>> vAddr = std::vector<std::vector<unsigned char>>();
168                 bool extendedStake = CConstVerusSolutionVector::GetVersionByHeight(stakeParams.blkHeight) >= CActivationHeight::ACTIVATE_EXTENDEDSTAKE;
169                 COptCCParams p;
170
171                 if (stakeParams.srcHeight == pindex->GetHeight() && 
172                     (stakeParams.blkHeight - stakeParams.srcHeight >= VERUS_MIN_STAKEAGE) &&
173                     ((srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
174                       extendedStake && 
175                       p.IsValid() &&
176                       srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey.IsSpendableOutputType(p)) ||
177                     (!p.IsValid() && Solver(srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, txType, vAddr))))
178                 {
179                     if (!p.IsValid() && txType == TX_PUBKEY && !stakeParams.pk.IsValid())
180                     {
181                         stakeParams.pk = CPubKey(vAddr[0]);
182                     }
183                     // once extended stake hits, we only accept extended form of staking
184                     if (!(extendedStake && stakeParams.Version() < stakeParams.VERSION_EXTENDED_STAKE) &&
185                         !(!extendedStake && stakeParams.Version() >= stakeParams.VERSION_EXTENDED_STAKE) &&
186                         ((extendedStake && p.IsValid()) || (txType == TX_PUBKEY) || (txType == TX_PUBKEYHASH && (extendedStake || stakeParams.pk.IsFullyValid()))))
187                     {
188                         auto consensusBranchId = CurrentEpochBranchId(stakeParams.blkHeight, Params().GetConsensus());
189
190                         std::map<uint160, pair<int, std::vector<std::vector<unsigned char>>>> idAddressMap;
191                         if (validateSig)
192                         {
193                             idAddressMap = ServerTransactionSignatureChecker::ExtractIDMap(srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, stakeParams.blkHeight);
194                         }
195
196                         if (!validateSig || VerifyScript(stakeTx.vin[0].scriptSig, 
197                                             srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, 
198                                             MANDATORY_SCRIPT_VERIFY_FLAGS,
199                                             TransactionSignatureChecker(&stakeTx, (uint32_t)0, srcTx.vout[stakeTx.vin[0].prevout.n].nValue, &idAddressMap),
200                                             consensusBranchId))
201                         {
202                             return true;
203                         }
204                     }
205                 }
206             }
207         }
208     }
209     return false;
210 }
211
212 bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout)
213 {
214     CStakeParams p;
215     if (GetStakeParams(stakeTx, p) && p.IsValid())
216     {
217         CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
218
219         hw << stakeTx.vin[0].prevout.hash;
220         hw << stakeTx.vin[0].prevout.n;
221
222         uint256 utxo = hw.GetHash();
223
224         if (p.Version() >= p.VERSION_EXTENDED_STAKE)
225         {
226             CCcontract_info *cp, C;
227             cp = CCinit(&C,EVAL_STAKEGUARD);
228
229             CStakeInfo stakeInfo(p.blkHeight, p.srcHeight, utxo, p.prevHash);
230             vout = CTxOut(value,
231                     MakeMofNCCScript(CConditionObj<CStakeInfo>(EVAL_STAKEGUARD, {dest, CTxDestination(CPubKey(ParseHex(cp->CChexstr)))}, 1, &stakeInfo)));
232         }
233         else
234         {
235             CCcontract_info *cp, C;
236             cp = CCinit(&C,EVAL_STAKEGUARD);
237
238             CPubKey ccAddress = CPubKey(ParseHex(cp->CChexstr));
239
240             // return an output that is bound to the stake transaction and can be spent by presenting either a signed condition by the original 
241             // destination address or a properly signed stake transaction of the same utxo on a fork
242             vout = MakeCC1of2vout(EVAL_STAKEGUARD, value, dest, ccAddress);
243
244             std::vector<CTxDestination> vKeys;
245             vKeys.push_back(dest);
246             vKeys.push_back(ccAddress);
247             
248             std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
249
250             vData.push_back(std::vector<unsigned char>(utxo.begin(), utxo.end()));
251
252             // prev block hash and height is here to make validation easy
253             vData.push_back(std::vector<unsigned char>(p.prevHash.begin(), p.prevHash.end()));
254             std::vector<unsigned char> height = std::vector<unsigned char>(4);
255             for (int i = 0; i < 4; i++)
256             {
257                 height[i] = (p.blkHeight >> (8 * i)) & 0xff;
258             }
259             vData.push_back(height);
260
261             COptCCParams ccp = COptCCParams(COptCCParams::VERSION_V1, EVAL_STAKEGUARD, 1, 2, vKeys, vData);
262
263             vout.scriptPubKey << ccp.AsVector() << OP_DROP;
264         }
265         
266         return true;
267     }
268     return false;
269 }
270
271 // validates if a stake transaction is both valid and cheating, defined by:
272 // the same exact utxo source, a target block height of later than that of the provided coinbase tx that is also targeting a fork
273 // of the chain. the source transaction must be a coinbase
274 bool ValidateMatchingStake(const CTransaction &ccTx, uint32_t voutNum, const CTransaction &stakeTx, bool &cheating)
275 {
276     // an invalid or non-matching stake transaction cannot cheat
277     cheating = false;
278
279     //printf("ValidateMatchingStake: ccTx.vin[0].prevout.hash: %s, ccTx.vin[0].prevout.n: %d\n", ccTx.vin[0].prevout.hash.GetHex().c_str(), ccTx.vin[0].prevout.n);
280
281     if (ccTx.IsCoinBase())
282     {
283         CStakeParams p;
284         if (ValidateStakeTransaction(stakeTx, p))
285         {
286             std::vector<std::vector<unsigned char>> vParams = std::vector<std::vector<unsigned char>>();
287             CScript dummy;
288
289             if (ccTx.vout[voutNum].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() > 0)
290             {
291                 COptCCParams ccp = COptCCParams(vParams[0]);
292                 CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
293
294                 if (p.version >= p.VERSION_EXTENDED_STAKE && ccp.version >= ccp.VERSION_V3 && ccp.vData.size())
295                 {
296                     CStakeInfo stakeInfo(ccp.vData[0]);
297                     hw << stakeTx.vin[0].prevout.hash;
298                     hw << stakeTx.vin[0].prevout.n;
299                     uint256 utxo = hw.GetHash();
300
301                     if (utxo == stakeInfo.utxo)
302                     {
303                         if (p.prevHash != stakeInfo.prevHash && p.blkHeight >= stakeInfo.height)
304                         {
305                             cheating = true;
306                             return true;
307                         }
308                         // if block height is equal and we are at the else, prevHash must have been equal
309                         else if (p.blkHeight >= stakeInfo.height)
310                         {
311                             return true;                            
312                         }
313                     }
314                 }
315                 else if (p.version < p.VERSION_EXTENDED_STAKE &&
316                          ccp.version < ccp.VERSION_V3 &&
317                          ccp.IsValid() &&
318                          ccp.vData.size() >= 3 && 
319                          ccp.vData[2].size() <= 4)
320                 {
321                     hw << stakeTx.vin[0].prevout.hash;
322                     hw << stakeTx.vin[0].prevout.n;
323                     uint256 utxo = hw.GetHash();
324
325                     uint32_t height = 0;
326                     int i, dataLen = ccp.vData[2].size();
327                     for (i = dataLen - 1; i >= 0; i--)
328                     {
329                         height = (height << 8) + ccp.vData[2][i];
330                     }
331                     // for debugging strange issue
332                     // printf("iterator: %d, height: %d, datalen: %d\n", i, height, dataLen);
333
334                     if (utxo == uint256(ccp.vData[0]))
335                     {
336                         if (p.prevHash != uint256(ccp.vData[1]) && p.blkHeight >= height)
337                         {
338                             cheating = true;
339                             return true;
340                         }
341                         // if block height is equal and we are at the else, prevHash must have been equal
342                         else if (p.blkHeight == height)
343                         {
344                             return true;                            
345                         }
346                     }
347                 }
348             }
349         }
350     }
351     return false;
352 }
353
354 // this attaches an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
355 bool MakeCheatEvidence(CMutableTransaction &mtx, const CTransaction &ccTx, uint32_t voutNum, const CTransaction &cheatTx)
356 {
357     std::vector<unsigned char> vch;
358     CDataStream s = CDataStream(SER_DISK, PROTOCOL_VERSION);
359     bool isCheater = false;
360
361     if (ValidateMatchingStake(ccTx, voutNum, cheatTx, isCheater) && isCheater)
362     {
363         CTxOut vOut = CTxOut();
364         int64_t opretype_stakecheat = OPRETTYPE_STAKECHEAT;
365
366         CScript vData = CScript();
367         cheatTx.Serialize(s);
368         vch = std::vector<unsigned char>(s.begin(), s.end());
369         vData << opretype_stakecheat << vch;
370         vch = std::vector<unsigned char>(vData.begin(), vData.end());
371         vOut.scriptPubKey << OP_RETURN << vch;
372
373         // printf("Script encoding inner:\n%s\nouter:\n%s\n", vData.ToString().c_str(), vOut.scriptPubKey.ToString().c_str());
374
375         vOut.nValue = 0;
376         mtx.vout.push_back(vOut);
377     }
378     return isCheater;
379 }
380
381 typedef struct ccFulfillmentCheck {
382     std::vector<CPubKey> &vPK;
383     std::vector<uint32_t> &vCount;
384 } ccFulfillmentCheck;
385
386 // to figure out which node is signed
387 int CCFulfillmentVisitor(CC *cc, struct CCVisitor visitor)
388 {
389     //printf("cc_typeName: %s, cc_isFulfilled: %x, cc_isAnon: %x, cc_typeMask: %x, cc_condToJSONString:\n%s\n", 
390     //       cc_typeName(cc), cc_isFulfilled(cc), cc_isAnon(cc), cc_typeMask(cc), cc_conditionToJSONString(cc));
391
392     if (strcmp(cc_typeName(cc), "secp256k1-sha-256") == 0)
393     {
394         cJSON *json = cc_conditionToJSON(cc);
395         if (json)
396         {
397             cJSON *pubKeyNode = json->child->next;
398             if (strcmp(pubKeyNode->string, "publicKey") == 0)
399             {
400                 ccFulfillmentCheck *pfc = (ccFulfillmentCheck *)(visitor.context);
401
402                 //printf("public key: %s\n", pubKeyNode->valuestring);
403                 CPubKey pubKey = CPubKey(ParseHex(pubKeyNode->valuestring));
404
405                 for (int i = 0; i < pfc->vPK.size(); i++)
406                 {
407                     if (i < pfc->vCount.size() && (pfc->vPK[i] == pubKey))
408                     {
409                         pfc->vCount[i]++;
410                     }
411                 }
412             }
413             cJSON_free(json);
414         }
415     }
416     return 1;
417 }
418
419 int IsCCFulfilled(CC *cc, ccFulfillmentCheck *ctx)
420 {
421     struct CCVisitor visitor = {&CCFulfillmentVisitor, NULL, 0, (void *)ctx};
422     cc_visit(cc, visitor);
423
424     //printf("count key 1: %d, count key 2: %d\n", ctx->vCount[0], ctx->vCount[1]);
425     return ctx->vCount[0];
426 }
427
428 bool StakeGuardValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
429 {
430     // WARNING: this has not been tested combined with time locks
431     // validate this spend of a transaction with it being past any applicable time lock and one of the following statements being true:
432     //  1. the spend is signed by the original output destination's private key and normal payment requirements, spends as normal
433     //  2. the spend is signed by the private key of the StakeGuard contract and pushes a signed stake transaction
434     //     with the same exact utxo source, a target block height of later than or equal to this tx, and a different prevBlock hash
435
436     // first, check to see if the spending contract is signed by the default destination address
437     // if so, success and we are done
438
439     // get preConditions and parameters
440     std::vector<std::vector<unsigned char>> preConditions = std::vector<std::vector<unsigned char>>();
441     std::vector<std::vector<unsigned char>> params = std::vector<std::vector<unsigned char>>();
442     CTransaction txOut;
443
444     bool signedByFirstKey = false;
445     bool validCheat = false;
446
447     CC *cc = GetCryptoCondition(tx.vin[nIn].scriptSig);
448
449     // tx is the spending tx, the cc transaction comes back in txOut
450     bool validCCParams = GetCCParams(eval, tx, nIn, txOut, preConditions, params);
451     COptCCParams ccp;
452     if (preConditions.size() > 0)
453     {
454         ccp = COptCCParams(preConditions[0]);
455     }
456
457     if (validCCParams && ccp.IsValid() && ((cc && ccp.version < COptCCParams::VERSION_V3) || (!cc && ccp.version >= COptCCParams::VERSION_V3)))
458     {
459         signedByFirstKey = false;
460         validCheat = false;
461
462         if (ccp.m == 1 && ccp.n == 2 && ccp.vKeys.size() == 2)
463         {
464             std::vector<uint32_t> vc = {0, 0};
465             std::vector<CPubKey> keys;
466
467             for (auto pk : ccp.vKeys)
468             {
469                 uint160 keyID = GetDestinationID(pk);
470                 std::vector<unsigned char> vkch = GetDestinationBytes(pk);
471                 if (vkch.size() == 33)
472                 {
473                     keys.push_back(CPubKey(vkch));
474                 }
475             }
476
477             if (keys.size() == 2)
478             {
479                 if (cc && ccp.version < COptCCParams::VERSION_V3)
480                 {
481                     ccFulfillmentCheck fc = {keys, vc};
482                     signedByFirstKey = (IsCCFulfilled(cc, &fc) != 0);
483                 }
484                 else if (ccp.version >= COptCCParams::VERSION_V3)
485                 {
486                     CSmartTransactionSignatures smartSigs;
487                     if (!cc)
488                     {
489                         std::vector<unsigned char> ffVec = GetFulfillmentVector(tx.vin[nIn].scriptSig);
490                         smartSigs = CSmartTransactionSignatures(std::vector<unsigned char>(ffVec.begin(), ffVec.end()));
491                         uint160 checkHash = GetDestinationID(keys[0]);
492                         for (auto &keySig : smartSigs.signatures)
493                         {
494                             CPubKey thisKey;
495                             thisKey.Set(keySig.second.pubKeyData.begin(), keySig.second.pubKeyData.end());
496                             if (thisKey.GetID() == checkHash)
497                             {
498                                 signedByFirstKey = true;
499                                 break;
500                             }
501                         }
502                     }
503                 }
504
505                 if (!signedByFirstKey && 
506                     ccp.evalCode == EVAL_STAKEGUARD && 
507                     ccp.vKeys.size() == 2 &&
508                     params.size() == 2 &&
509                     params[0].size() > 0 && 
510                     params[0][0] == OPRETTYPE_STAKECHEAT)
511                 {
512                     CDataStream s = CDataStream(std::vector<unsigned char>(params[1].begin(), params[1].end()), SER_DISK, PROTOCOL_VERSION);
513                     bool checkOK = false;
514                     CTransaction cheatTx;
515                     try
516                     {
517                         cheatTx.Unserialize(s);
518                         checkOK = true;
519                     }
520                     catch (...)
521                     {
522                     }
523                     if (checkOK && !ValidateMatchingStake(txOut, tx.vin[0].prevout.n, cheatTx, validCheat))
524                     {
525                         validCheat = false;
526                     }
527                 }
528             }
529         }
530     }
531     if (cc)
532     {
533         cc_free(cc);
534     }
535     if (!(signedByFirstKey || validCheat))
536     {
537         return eval->Error("error reading coinbase or spending proof invalid\n");
538     }
539     else return true;
540 }
541
542 bool IsStakeGuardInput(const CScript &scriptSig)
543 {
544     uint32_t ecode;
545     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_STAKEGUARD;
546 }
547
548 UniValue StakeGuardInfo()
549 {
550     UniValue result(UniValue::VOBJ); char numstr[64];
551     CMutableTransaction mtx;
552     CPubKey pk; 
553
554     CCcontract_info *cp,C;
555
556     cp = CCinit(&C,EVAL_STAKEGUARD);
557
558     result.push_back(Pair("result","success"));
559     result.push_back(Pair("name","StakeGuard"));
560
561     // all UTXOs to the contract address that are to any of the wallet addresses are to us
562     // each is spendable as a normal transaction, but the spend may fail if it gets spent out
563     // from under us
564     pk = GetUnspendable(cp,0);
565     return(result);
566 }
567
This page took 0.059115 seconds and 4 git commands to generate.