1 /********************************************************************
2 * (C) 2018 Michael Toutonghi
4 * Distributed under the MIT software license, see the accompanying
5 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 * This crypto-condition eval solves the problem of nothing-at-stake
8 * in a proof of stake consensus system.
12 #include "StakeGuard.h"
13 #include "script/script.h"
23 extern int32_t VERUS_MIN_STAKEAGE;
25 bool IsData(opcodetype opcode)
27 return (opcode >= 0 && opcode <= OP_PUSHDATA4) || (opcode >= OP_1 && opcode <= OP_16);
30 bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector<std::vector<unsigned char>> &vData)
32 bool isValid = stakeTx.vout[stakeTx.vout.size() - 1].scriptPubKey.GetOpretData(vData);
34 if (isValid && vData.size() == 1)
36 CScript data = CScript(vData[0].begin(), vData[0].end());
40 CScript::const_iterator pc = data.begin();
41 std::vector<unsigned char> vch = std::vector<unsigned char>();
45 for (bytesTotal = vch.size();
46 bytesTotal <= nMaxDatacarrierBytes && !(isValid = (pc == data.end())) && (moreData = data.GetOp(pc, op, vch)) && IsData(op);
47 bytesTotal += vch.size())
49 if (op >= OP_1 && op <= OP_16)
52 vch[0] = (op - OP_1) + 1;
57 // if we ran out of data, we're ok
58 if (isValid && (vData.size() >= CStakeParams::STAKE_MINPARAMS) && (vData.size() <= CStakeParams::STAKE_MAXPARAMS))
66 CStakeParams::CStakeParams(const std::vector<std::vector<unsigned char>> &vData)
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;
77 if (vData[0].size() == 1 &&
78 vData[0][0] == OPRETTYPE_STAKEPARAMS2 &&
81 ::FromVector(vData[1], *this);
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)))
89 version = VERSION_ORIGINAL;
90 for (int i = 0, size = vData[1].size(); i < size; i++)
92 srcHeight = srcHeight | vData[1][i] << (8 * i);
94 for (int i = 0, size = vData[2].size(); i < size; i++)
96 blkHeight = blkHeight | vData[2][i] << (8 * i);
99 prevHash = uint256(vData[3]);
101 if (vData.size() == 4)
105 else if (vData[4].size() == 33)
107 pk = CPubKey(vData[4]);
112 version = VERSION_INVALID;
119 version = VERSION_INVALID;
124 bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams)
126 std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
128 //printf("opret stake script: %s\nvalue at scriptPubKey[0]: %x\n", stakeTx.vout[1].scriptPubKey.ToString().c_str(), stakeTx.vout[1].scriptPubKey[0]);
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))
136 stakeParams = CStakeParams(vData);
137 return stakeParams.IsValid();
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)
150 std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
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))
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();
162 if (myGetTransaction(stakeTx.vin[0].prevout.hash, srcTx, blkHash))
164 BlockMap::const_iterator it = mapBlockIndex.find(blkHash);
165 if (it != mapBlockIndex.end() && (pindex = it->second) != NULL && chainActive.Contains(pindex))
167 std::vector<std::vector<unsigned char>> vAddr = std::vector<std::vector<unsigned char>>();
168 bool extendedStake = CConstVerusSolutionVector::GetVersionByHeight(stakeParams.blkHeight) >= CActivationHeight::ACTIVATE_EXTENDEDSTAKE;
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) &&
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))))
179 if (!p.IsValid() && txType == TX_PUBKEY && !stakeParams.pk.IsValid())
181 stakeParams.pk = CPubKey(vAddr[0]);
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()))))
188 auto consensusBranchId = CurrentEpochBranchId(stakeParams.blkHeight, Params().GetConsensus());
190 std::map<uint160, pair<int, std::vector<std::vector<unsigned char>>>> idAddressMap;
193 idAddressMap = ServerTransactionSignatureChecker::ExtractIDMap(srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, stakeParams.blkHeight);
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),
212 bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout)
215 if (GetStakeParams(stakeTx, p) && p.IsValid())
217 CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
219 hw << stakeTx.vin[0].prevout.hash;
220 hw << stakeTx.vin[0].prevout.n;
222 uint256 utxo = hw.GetHash();
224 if (p.Version() >= p.VERSION_EXTENDED_STAKE)
226 CCcontract_info *cp, C;
227 cp = CCinit(&C,EVAL_STAKEGUARD);
229 CStakeInfo stakeInfo(p.blkHeight, p.srcHeight, utxo, p.prevHash);
231 MakeMofNCCScript(CConditionObj<CStakeInfo>(EVAL_STAKEGUARD, {dest, CTxDestination(CPubKey(ParseHex(cp->CChexstr)))}, 1, &stakeInfo)));
235 CCcontract_info *cp, C;
236 cp = CCinit(&C,EVAL_STAKEGUARD);
238 CPubKey ccAddress = CPubKey(ParseHex(cp->CChexstr));
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);
244 std::vector<CTxDestination> vKeys;
245 vKeys.push_back(dest);
246 vKeys.push_back(ccAddress);
248 std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
250 vData.push_back(std::vector<unsigned char>(utxo.begin(), utxo.end()));
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++)
257 height[i] = (p.blkHeight >> (8 * i)) & 0xff;
259 vData.push_back(height);
261 COptCCParams ccp = COptCCParams(COptCCParams::VERSION_V1, EVAL_STAKEGUARD, 1, 2, vKeys, vData);
263 vout.scriptPubKey << ccp.AsVector() << OP_DROP;
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)
276 // an invalid or non-matching stake transaction cannot cheat
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);
281 if (ccTx.IsCoinBase())
284 if (ValidateStakeTransaction(stakeTx, p))
286 std::vector<std::vector<unsigned char>> vParams = std::vector<std::vector<unsigned char>>();
289 if (ccTx.vout[voutNum].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() > 0)
291 COptCCParams ccp = COptCCParams(vParams[0]);
292 CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
294 if (p.version >= p.VERSION_EXTENDED_STAKE && ccp.version >= ccp.VERSION_V3 && ccp.vData.size())
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();
301 if (utxo == stakeInfo.utxo)
303 if (p.prevHash != stakeInfo.prevHash && p.blkHeight >= stakeInfo.height)
308 // if block height is equal and we are at the else, prevHash must have been equal
309 else if (p.blkHeight >= stakeInfo.height)
315 else if (p.version < p.VERSION_EXTENDED_STAKE &&
316 ccp.version < ccp.VERSION_V3 &&
318 ccp.vData.size() >= 3 &&
319 ccp.vData[2].size() <= 4)
321 hw << stakeTx.vin[0].prevout.hash;
322 hw << stakeTx.vin[0].prevout.n;
323 uint256 utxo = hw.GetHash();
326 int i, dataLen = ccp.vData[2].size();
327 for (i = dataLen - 1; i >= 0; i--)
329 height = (height << 8) + ccp.vData[2][i];
331 // for debugging strange issue
332 // printf("iterator: %d, height: %d, datalen: %d\n", i, height, dataLen);
334 if (utxo == uint256(ccp.vData[0]))
336 if (p.prevHash != uint256(ccp.vData[1]) && p.blkHeight >= height)
341 // if block height is equal and we are at the else, prevHash must have been equal
342 else if (p.blkHeight == height)
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)
357 std::vector<unsigned char> vch;
358 CDataStream s = CDataStream(SER_DISK, PROTOCOL_VERSION);
359 bool isCheater = false;
361 if (ValidateMatchingStake(ccTx, voutNum, cheatTx, isCheater) && isCheater)
363 CTxOut vOut = CTxOut();
364 int64_t opretype_stakecheat = OPRETTYPE_STAKECHEAT;
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;
373 // printf("Script encoding inner:\n%s\nouter:\n%s\n", vData.ToString().c_str(), vOut.scriptPubKey.ToString().c_str());
376 mtx.vout.push_back(vOut);
381 typedef struct ccFulfillmentCheck {
382 std::vector<CPubKey> &vPK;
383 std::vector<uint32_t> &vCount;
384 } ccFulfillmentCheck;
386 // to figure out which node is signed
387 int CCFulfillmentVisitor(CC *cc, struct CCVisitor visitor)
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));
392 if (strcmp(cc_typeName(cc), "secp256k1-sha-256") == 0)
394 cJSON *json = cc_conditionToJSON(cc);
397 cJSON *pubKeyNode = json->child->next;
398 if (strcmp(pubKeyNode->string, "publicKey") == 0)
400 ccFulfillmentCheck *pfc = (ccFulfillmentCheck *)(visitor.context);
402 //printf("public key: %s\n", pubKeyNode->valuestring);
403 CPubKey pubKey = CPubKey(ParseHex(pubKeyNode->valuestring));
405 for (int i = 0; i < pfc->vPK.size(); i++)
407 if (i < pfc->vCount.size() && (pfc->vPK[i] == pubKey))
419 int IsCCFulfilled(CC *cc, ccFulfillmentCheck *ctx)
421 struct CCVisitor visitor = {&CCFulfillmentVisitor, NULL, 0, (void *)ctx};
422 cc_visit(cc, visitor);
424 //printf("count key 1: %d, count key 2: %d\n", ctx->vCount[0], ctx->vCount[1]);
425 return ctx->vCount[0];
428 bool StakeGuardValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
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
436 // first, check to see if the spending contract is signed by the default destination address
437 // if so, success and we are done
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>>();
444 bool signedByFirstKey = false;
445 bool validCheat = false;
447 CC *cc = GetCryptoCondition(tx.vin[nIn].scriptSig);
449 // tx is the spending tx, the cc transaction comes back in txOut
450 bool validCCParams = GetCCParams(eval, tx, nIn, txOut, preConditions, params);
452 if (preConditions.size() > 0)
454 ccp = COptCCParams(preConditions[0]);
457 if (validCCParams && ccp.IsValid() && ((cc && ccp.version < COptCCParams::VERSION_V3) || (!cc && ccp.version >= COptCCParams::VERSION_V3)))
459 signedByFirstKey = false;
462 if (ccp.m == 1 && ccp.n == 2 && ccp.vKeys.size() == 2)
464 std::vector<uint32_t> vc = {0, 0};
465 std::vector<CPubKey> keys;
467 for (auto pk : ccp.vKeys)
469 uint160 keyID = GetDestinationID(pk);
470 std::vector<unsigned char> vkch = GetDestinationBytes(pk);
471 if (vkch.size() == 33)
473 keys.push_back(CPubKey(vkch));
477 if (keys.size() == 2)
479 if (cc && ccp.version < COptCCParams::VERSION_V3)
481 ccFulfillmentCheck fc = {keys, vc};
482 signedByFirstKey = (IsCCFulfilled(cc, &fc) != 0);
484 else if (ccp.version >= COptCCParams::VERSION_V3)
486 CSmartTransactionSignatures smartSigs;
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)
495 thisKey.Set(keySig.second.pubKeyData.begin(), keySig.second.pubKeyData.end());
496 if (thisKey.GetID() == checkHash)
498 signedByFirstKey = true;
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)
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;
517 cheatTx.Unserialize(s);
523 if (checkOK && !ValidateMatchingStake(txOut, tx.vin[0].prevout.n, cheatTx, validCheat))
535 if (!(signedByFirstKey || validCheat))
537 return eval->Error("error reading coinbase or spending proof invalid\n");
542 bool IsStakeGuardInput(const CScript &scriptSig)
545 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_STAKEGUARD;
548 UniValue StakeGuardInfo()
550 UniValue result(UniValue::VOBJ); char numstr[64];
551 CMutableTransaction mtx;
554 CCcontract_info *cp,C;
556 cp = CCinit(&C,EVAL_STAKEGUARD);
558 result.push_back(Pair("result","success"));
559 result.push_back(Pair("name","StakeGuard"));
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
564 pk = GetUnspendable(cp,0);