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