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