]> Git Repo - VerusCoin.git/blob - src/cc/betprotocol.cpp
Handle partial but not enough rewards funding
[VerusCoin.git] / src / cc / betprotocol.cpp
1 /******************************************************************************
2  * Copyright © 2014-2018 The SuperNET Developers.                             *
3  *                                                                            *
4  * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at                  *
5  * the top-level directory of this distribution for the individual copyright  *
6  * holder information and the developer policies on copyright and licensing.  *
7  *                                                                            *
8  * Unless otherwise agreed in a custom licensing agreement, no part of the    *
9  * SuperNET software, including this file may be copied, modified, propagated *
10  * or distributed except according to the terms contained in the LICENSE file *
11  *                                                                            *
12  * Removal or modification of this copyright notice is prohibited.            *
13  *                                                                            *
14  ******************************************************************************/
15
16 #include <cryptoconditions.h>
17
18 #include "hash.h"
19 #include "main.h"
20 #include "chain.h"
21 #include "streams.h"
22 #include "script/cc.h"
23 #include "cc/betprotocol.h"
24 #include "cc/eval.h"
25 #include "cc/utils.h"
26 #include "primitives/transaction.h"
27
28
29 std::vector<CC*> BetProtocol::PlayerConditions()
30 {
31     std::vector<CC*> subs;
32     for (int i=0; i<players.size(); i++)
33         subs.push_back(CCNewSecp256k1(players[i]));
34     return subs;
35 }
36
37
38 CC* BetProtocol::MakeDisputeCond()
39 {
40     CC *disputePoker = CCNewEval(E_MARSHAL(
41         ss << disputeCode << VARINT(waitBlocks) << vmParams;
42     ));
43
44     CC *anySig = CCNewThreshold(1, PlayerConditions());
45
46     return CCNewThreshold(2, {disputePoker, anySig});
47 }
48
49
50 /*
51  * spendFee is the amount assigned to each output, for the purposes of posting
52  * dispute / evidence.
53  */
54 CMutableTransaction BetProtocol::MakeSessionTx(CAmount spendFee)
55 {
56     CMutableTransaction mtx;
57
58     CC *disputeCond = MakeDisputeCond();
59     mtx.vout.push_back(CTxOut(spendFee, CCPubKey(disputeCond)));
60     cc_free(disputeCond);
61
62     for (int i=0; i<players.size(); i++) {
63         CC *cond = CCNewSecp256k1(players[i]);
64         mtx.vout.push_back(CTxOut(spendFee, CCPubKey(cond)));
65         cc_free(cond);
66     }
67     return mtx;
68 }
69
70
71 CMutableTransaction BetProtocol::MakeDisputeTx(uint256 signedSessionTxHash, uint256 vmResultHash)
72 {
73     CMutableTransaction mtx;
74
75     CC *disputeCond = MakeDisputeCond();
76     mtx.vin.push_back(CTxIn(signedSessionTxHash, 0, CScript()));
77
78     std::vector<unsigned char> result(vmResultHash.begin(), vmResultHash.begin()+32);
79     mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << result));
80     return mtx;
81 }
82
83
84 CMutableTransaction BetProtocol::MakePostEvidenceTx(uint256 signedSessionTxHash,
85         int playerIdx, std::vector<unsigned char> state)
86 {
87     CMutableTransaction mtx;
88
89     mtx.vin.push_back(CTxIn(signedSessionTxHash, playerIdx+1, CScript()));
90     mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << state));
91
92     return mtx;
93 }
94
95
96 CC* BetProtocol::MakePayoutCond(uint256 signedSessionTxHash)
97 {
98     // TODO: 2/3 majority
99     CC* agree = CCNewThreshold(players.size(), PlayerConditions());
100
101     CC *import;
102     {
103         CC *importEval = CCNewEval(E_MARSHAL(
104             ss << EVAL_IMPORTPAYOUT << signedSessionTxHash;
105         ));
106
107         CC *oneof = CCNewThreshold(1, PlayerConditions());
108
109         import = CCNewThreshold(2, {oneof, importEval});
110     }
111
112     return CCNewThreshold(1, {agree, import});
113 }
114
115
116 CMutableTransaction BetProtocol::MakeStakeTx(CAmount totalPayout, uint256 signedSessionTxHash)
117 {
118     CMutableTransaction mtx;
119
120     CC *payoutCond = MakePayoutCond(signedSessionTxHash);
121     mtx.vout.push_back(CTxOut(totalPayout, CCPubKey(payoutCond)));
122     cc_free(payoutCond);
123
124     return mtx;
125 }
126
127
128 CMutableTransaction BetProtocol::MakeAgreePayoutTx(std::vector<CTxOut> payouts,
129         uint256 signedStakeTxHash)
130 {
131     CMutableTransaction mtx;
132     mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
133     mtx.vout = payouts;
134     return mtx;
135 }
136
137
138 CMutableTransaction BetProtocol::MakeImportPayoutTx(std::vector<CTxOut> payouts,
139         CTransaction signedDisputeTx, uint256 signedStakeTxHash, MoMProof momProof)
140 {
141     CMutableTransaction mtx;
142     mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
143     mtx.vout = payouts;
144     CScript proofData;
145     proofData << OP_RETURN << E_MARSHAL(ss << momProof << signedDisputeTx);
146     mtx.vout.insert(mtx.vout.begin(), CTxOut(0, proofData));
147     return mtx;
148 }
149
150
151 bool GetOpReturnHash(CScript script, uint256 &hash)
152 {
153     std::vector<unsigned char> vHash;
154     GetOpReturnData(script, vHash);
155     if (vHash.size() != 32) return false;
156     hash = uint256(vHash);
157     return true;
158 }
159
160
161 /*
162  * Crypto-Condition EVAL method that verifies a payout against a transaction
163  * notarised on another chain.
164  *
165  * IN: params - condition params
166  * IN: importTx - Payout transaction on value chain (KMD)
167  * IN: nIn  - index of input of stake
168  *
169  * importTx: Spends stakeTx with payouts from asset chain
170  *
171  *   in  0:      Spends Stake TX and contains ImportPayout CC
172  *   out 0:      OP_RETURN MomProof, disputeTx
173  *   out 1-:     arbitrary payouts
174  *
175  * disputeTx: Spends sessionTx.0 (opener on asset chain)
176  *
177  *   in 0:       spends sessionTx.0
178  *   in 1-:      anything
179  *   out 0:      OP_RETURN hash of payouts
180  *   out 1-:     anything
181  */
182 bool Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
183 {
184     if (importTx.vout.size() == 0) return Invalid("no-vouts");
185
186     // load data from vout[0]
187     MoMProof proof;
188     CTransaction disputeTx;
189     {
190         std::vector<unsigned char> vopret;
191         GetOpReturnData(importTx.vout[0].scriptPubKey, vopret);
192         if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx))
193             return Invalid("invalid-payload");
194     }
195
196     // Check disputeTx.0 shows correct payouts
197     {
198         uint256 givenPayoutsHash;
199         GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash);
200         std::vector<CTxOut> payouts(importTx.vout.begin() + 1, importTx.vout.end());
201         if (givenPayoutsHash != SerializeHash(payouts))
202             return Invalid("wrong-payouts");
203     }
204
205     // Check disputeTx spends sessionTx.0
206     // condition ImportPayout params is session ID from other chain
207     {
208         uint256 sessionHash;
209         if (!E_UNMARSHAL(params, ss >> sessionHash))
210             return Invalid("malformed-params");
211         if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0))
212             return Invalid("wrong-session");
213     }
214
215     // Check disputeTx solves momproof from vout[0]
216     {
217         NotarisationData data(0);
218         if (!GetNotarisationData(proof.notarisationHash, data))
219             return Invalid("coudnt-load-mom");
220
221         if (data.MoM != proof.branch.Exec(disputeTx.GetHash()))
222             return Invalid("mom-check-fail");
223     }
224
225     return Valid();
226 }
227
228
229 /*
230  * Crypto-Condition EVAL method that resolves a dispute of a session
231  *
232  * IN: vm - AppVM virtual machine to verify states
233  * IN: params - condition params
234  * IN: disputeTx - transaction attempting to resolve dispute
235  * IN: nIn  - index of input of dispute tx
236  *
237  * disputeTx: attempt to resolve a dispute
238  *
239  *   in  0:      Spends Session TX first output, reveals DisputeHeader
240  *   out 0:      OP_RETURN hash of payouts
241  */
242 bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
243 {
244     if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
245
246     // get payouts hash
247     uint256 payoutHash;
248     if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
249         return Invalid("invalid-payout-hash");
250
251     // load params
252     uint16_t waitBlocks;
253     std::vector<uint8_t> vmParams;
254     if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams))
255         return Invalid("malformed-params");
256
257     // ensure that enough time has passed
258     {
259         CTransaction sessionTx;
260         CBlockIndex sessionBlock;
261         
262         // if unconformed its too soon
263         if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
264             return Error("couldnt-get-parent");
265
266         if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks)
267             return Invalid("dispute-too-soon");  // Not yet
268     }
269
270     // get spends
271     std::vector<CTransaction> spends;
272     if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends))
273         return Error("couldnt-get-spends");
274
275     // verify result from VM
276     int maxLength = -1;
277     uint256 bestPayout;
278     for (int i=1; i<spends.size(); i++)
279     {
280         std::vector<unsigned char> vmState;
281         if (spends[i].vout.size() == 0) continue;
282         if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue;
283         auto out = vm.evaluate(vmParams, vmState);
284         uint256 resultHash = SerializeHash(out.second);
285         if (out.first > maxLength) {
286             maxLength = out.first;
287             bestPayout = resultHash;
288         }
289         // The below means that if for any reason there is a draw, the first dispute wins
290         else if (out.first == maxLength) {
291             if (bestPayout != payoutHash) {
292                 fprintf(stderr, "WARNING: VM has multiple solutions of same length\n");
293                 bestPayout = resultHash;
294             }
295         }
296     }
297
298     if (maxLength == -1) return Invalid("no-evidence");
299
300     return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
301 }
This page took 0.040395 seconds and 4 git commands to generate.