]>
Commit | Line | Data |
---|---|---|
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 | |
23 | extern int32_t VERUS_MIN_STAKEAGE; | |
24 | ||
3bfa5e22 | 25 | bool IsData(opcodetype opcode) |
26 | { | |
27 | return (opcode >= 0 && opcode <= OP_PUSHDATA4) || (opcode >= OP_1 && opcode <= OP_16); | |
28 | } | |
29 | ||
06f41160 | 30 | bool 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 | 66 | CStakeParams::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 | 114 | bool 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 | 139 | bool 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 | ||
194 | bool 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 | 242 | bool 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 |
298 | bool 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 | 324 | typedef 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 |
330 | int 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 | ||
362 | int 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 | 371 | bool 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 | 458 | bool IsStakeGuardInput(const CScript &scriptSig) |
459 | { | |
68b309c0 MT |
460 | uint32_t ecode; |
461 | return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_STAKEGUARD; | |
ec8a120b | 462 | } |
463 | ||
ca4a5f26 | 464 | UniValue 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 |