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"
20 extern int32_t VERUS_MIN_STAKEAGE;
22 bool IsData(opcodetype opcode)
24 return (opcode >= 0 && opcode <= OP_PUSHDATA4) || (opcode >= OP_1 && opcode <= OP_16);
27 bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector<std::vector<unsigned char>> &vData)
29 bool isValid = stakeTx.vout[stakeTx.vout.size() - 1].scriptPubKey.GetOpretData(vData);
31 if (isValid && vData.size() == 1)
33 CScript data = CScript(vData[0].begin(), vData[0].end());
37 CScript::const_iterator pc = data.begin();
38 std::vector<unsigned char> vch = std::vector<unsigned char>();
42 for (bytesTotal = vch.size();
43 bytesTotal <= nMaxDatacarrierBytes && !(isValid = (pc == data.end())) && (moreData = data.GetOp(pc, op, vch)) && IsData(op);
44 bytesTotal += vch.size())
46 if (op >= OP_1 && op <= OP_16)
49 vch[0] = (op - OP_1) + 1;
54 // if we ran out of data, we're ok
55 if (isValid && (vData.size() >= CStakeParams::STAKE_MINPARAMS) && (vData.size() <= CStakeParams::STAKE_MAXPARAMS))
63 CStakeParams::CStakeParams(const std::vector<std::vector<unsigned char>> &vData)
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
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)))
79 for (int i = 0, size = vData[1].size(); i < size; i++)
81 srcHeight = srcHeight | vData[1][i] << (8 * i);
83 for (int i = 0, size = vData[2].size(); i < size; i++)
85 blkHeight = blkHeight | vData[2][i] << (8 * i);
88 prevHash = uint256(vData[3]);
90 if (vData.size() == 4)
94 else if (vData[4].size() == 33)
96 pk = CPubKey(vData[4]);
111 bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams)
113 std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
115 //printf("opret stake script: %s\nvalue at scriptPubKey[0]: %x\n", stakeTx.vout[1].scriptPubKey.ToString().c_str(), stakeTx.vout[1].scriptPubKey[0]);
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))
123 stakeParams = CStakeParams(vData);
124 return stakeParams.IsValid();
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)
138 std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
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))
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();
150 if (myGetTransaction(stakeTx.vin[0].prevout.hash, srcTx, blkHash))
152 BlockMap::const_iterator it = mapBlockIndex.find(blkHash);
153 if (it != mapBlockIndex.end() && (pindex = it->second) != NULL)
155 std::vector<std::vector<unsigned char>> vAddr = std::vector<std::vector<unsigned char>>();
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))
161 if (txType == TX_PUBKEY && !stakeParams.pk.IsValid())
163 stakeParams.pk = CPubKey(vAddr[0]);
165 if ((txType == TX_PUBKEY) || (txType == TX_PUBKEYHASH && stakeParams.pk.IsFullyValid()))
167 auto consensusBranchId = CurrentEpochBranchId(stakeParams.blkHeight, Params().GetConsensus());
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),
185 bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout)
187 CCcontract_info *cp, C;
188 cp = CCinit(&C,EVAL_STAKEGUARD);
190 CPubKey ccAddress = CPubKey(ParseHex(cp->CChexstr));
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);
196 std::vector<CTxDestination> vKeys;
197 vKeys.push_back(dest);
198 vKeys.push_back(ccAddress);
200 std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
202 CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
204 hw << stakeTx.vin[0].prevout.hash;
205 hw << stakeTx.vin[0].prevout.n;
207 uint256 utxo = hw.GetHash();
208 vData.push_back(std::vector<unsigned char>(utxo.begin(), utxo.end()));
211 if (GetStakeParams(stakeTx, p))
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++)
218 height[i] = (p.blkHeight >> (8 * i)) & 0xff;
220 vData.push_back(height);
222 COptCCParams ccp = COptCCParams(COptCCParams::VERSION_V1, EVAL_STAKEGUARD, 1, 2, vKeys, vData);
224 vout.scriptPubKey << ccp.AsVector() << OP_DROP;
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)
235 // an invalid or non-matching stake transaction cannot cheat
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);
240 if (ccTx.IsCoinBase())
243 if (ValidateStakeTransaction(stakeTx, p))
245 std::vector<std::vector<unsigned char>> vParams = std::vector<std::vector<unsigned char>>();
248 if (ccTx.vout[voutNum].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() > 0)
250 COptCCParams ccp = COptCCParams(vParams[0]);
251 if (ccp.IsValid() & ccp.vData.size() >= 3 && ccp.vData[2].size() <= 4)
253 CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
255 hw << stakeTx.vin[0].prevout.hash;
256 hw << stakeTx.vin[0].prevout.n;
257 uint256 utxo = hw.GetHash();
260 int i, dataLen = ccp.vData[2].size();
261 for (i = dataLen - 1; i >= 0; i--)
263 height = (height << 8) + ccp.vData[2][i];
265 // for debugging strange issue
266 // printf("iterator: %d, height: %d, datalen: %d\n", i, height, dataLen);
268 if (utxo == uint256(ccp.vData[0]))
270 if (p.prevHash != uint256(ccp.vData[1]) && p.blkHeight >= height)
275 // if block height is equal and we are at the else, prevHash must have been equal
276 else if (p.blkHeight == height)
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)
291 std::vector<unsigned char> vch;
292 CDataStream s = CDataStream(SER_DISK, PROTOCOL_VERSION);
293 bool isCheater = false;
295 if (ValidateMatchingStake(ccTx, voutNum, cheatTx, isCheater) && isCheater)
297 CTxOut vOut = CTxOut();
298 int64_t opretype_stakecheat = OPRETTYPE_STAKECHEAT;
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;
307 // printf("Script encoding inner:\n%s\nouter:\n%s\n", vData.ToString().c_str(), vOut.scriptPubKey.ToString().c_str());
310 mtx.vout.push_back(vOut);
315 typedef struct ccFulfillmentCheck {
316 std::vector<CPubKey> &vPK;
317 std::vector<uint32_t> &vCount;
318 } ccFulfillmentCheck;
320 // to figure out which node is signed
321 int CCFulfillmentVisitor(CC *cc, struct CCVisitor visitor)
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));
326 if (strcmp(cc_typeName(cc), "secp256k1-sha-256") == 0)
328 cJSON *json = cc_conditionToJSON(cc);
331 cJSON *pubKeyNode = json->child->next;
332 if (strcmp(pubKeyNode->string, "publicKey") == 0)
334 ccFulfillmentCheck *pfc = (ccFulfillmentCheck *)(visitor.context);
336 //printf("public key: %s\n", pubKeyNode->valuestring);
337 CPubKey pubKey = CPubKey(ParseHex(pubKeyNode->valuestring));
339 for (int i = 0; i < pfc->vPK.size(); i++)
341 if (i < pfc->vCount.size() && (pfc->vPK[i] == pubKey))
353 int IsCCFulfilled(CC *cc, ccFulfillmentCheck *ctx)
355 struct CCVisitor visitor = {&CCFulfillmentVisitor, NULL, 0, (void *)ctx};
356 cc_visit(cc, visitor);
358 //printf("count key 1: %d, count key 2: %d\n", ctx->vCount[0], ctx->vCount[1]);
359 return ctx->vCount[0];
362 bool StakeGuardValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
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
370 // first, check to see if the spending contract is signed by the default destination address
371 // if so, success and we are done
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>>();
378 bool signedByFirstKey = false;
379 bool validCheat = false;
381 CC *cc = GetCryptoCondition(tx.vin[nIn].scriptSig);
386 signedByFirstKey = false;
389 // tx is the spending tx, the cc transaction comes back in txOut
390 if (GetCCParams(eval, tx, nIn, txOut, preConditions, params))
392 if (preConditions.size() > 0)
394 ccp = COptCCParams(preConditions[0]);
397 if (ccp.IsValid() && ccp.m == 1 && ccp.n == 2 && ccp.vKeys.size() == 2)
399 std::vector<uint32_t> vc = {0, 0};
401 std::vector<CPubKey> keys;
402 for (auto pk : ccp.vKeys)
404 uint160 keyID = GetDestinationID(pk);
405 std::vector<unsigned char> vkch = GetDestinationBytes(pk);
406 if (vkch.size() == 33)
408 keys.push_back(CPubKey(vkch));
412 if (keys.size() == 2)
414 ccFulfillmentCheck fc = {keys, vc};
416 signedByFirstKey = (IsCCFulfilled(cc, &fc) != 0);
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)
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;
426 cheatTx.Unserialize(s);
432 if (checkOK && !ValidateMatchingStake(txOut, tx.vin[0].prevout.n, cheatTx, validCheat))
442 if (!(signedByFirstKey || validCheat))
444 eval->Error("error reading coinbase or spending proof invalid\n");
450 bool IsStakeGuardInput(const CScript &scriptSig)
453 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_STAKEGUARD;
456 UniValue StakeGuardInfo()
458 UniValue result(UniValue::VOBJ); char numstr[64];
459 CMutableTransaction mtx;
462 CCcontract_info *cp,C;
464 cp = CCinit(&C,EVAL_STAKEGUARD);
466 result.push_back(Pair("result","success"));
467 result.push_back(Pair("name","StakeGuard"));
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
472 pk = GetUnspendable(cp,0);