]>
Commit | Line | Data |
---|---|---|
f345b953 | 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 | ||
37592740 SS |
16 | #include <cryptoconditions.h> |
17 | ||
0cb91a8d SS |
18 | #include "hash.h" |
19 | #include "main.h" | |
20 | #include "chain.h" | |
561f3e18 | 21 | #include "streams.h" |
8a8e10f0 | 22 | #include "script/cc.h" |
561f3e18 | 23 | #include "cc/betprotocol.h" |
20c3ac51 SS |
24 | #include "cc/eval.h" |
25 | #include "cc/utils.h" | |
37592740 SS |
26 | #include "primitives/transaction.h" |
27 | ||
28 | ||
561f3e18 | 29 | std::vector<CC*> BetProtocol::PlayerConditions() |
37592740 | 30 | { |
561f3e18 SS |
31 | std::vector<CC*> subs; |
32 | for (int i=0; i<players.size(); i++) | |
33 | subs.push_back(CCNewSecp256k1(players[i])); | |
34 | return subs; | |
37592740 SS |
35 | } |
36 | ||
37 | ||
561f3e18 | 38 | CC* BetProtocol::MakeDisputeCond() |
37592740 | 39 | { |
39c9911e SS |
40 | CC *disputePoker = CCNewEval(E_MARSHAL( |
41 | ss << disputeCode << VARINT(waitBlocks) << vmParams; | |
42 | )); | |
561f3e18 SS |
43 | |
44 | CC *anySig = CCNewThreshold(1, PlayerConditions()); | |
45 | ||
46 | return CCNewThreshold(2, {disputePoker, anySig}); | |
37592740 SS |
47 | } |
48 | ||
49 | ||
56cf273f SS |
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) | |
37592740 SS |
55 | { |
56 | CMutableTransaction mtx; | |
37592740 | 57 | |
561f3e18 | 58 | CC *disputeCond = MakeDisputeCond(); |
56cf273f | 59 | mtx.vout.push_back(CTxOut(spendFee, CCPubKey(disputeCond))); |
37592740 SS |
60 | cc_free(disputeCond); |
61 | ||
62 | for (int i=0; i<players.size(); i++) { | |
561f3e18 | 63 | CC *cond = CCNewSecp256k1(players[i]); |
56cf273f | 64 | mtx.vout.push_back(CTxOut(spendFee, CCPubKey(cond))); |
37592740 SS |
65 | cc_free(cond); |
66 | } | |
67 | return mtx; | |
68 | } | |
69 | ||
70 | ||
561f3e18 | 71 | CMutableTransaction BetProtocol::MakeDisputeTx(uint256 signedSessionTxHash, uint256 vmResultHash) |
37592740 SS |
72 | { |
73 | CMutableTransaction mtx; | |
74 | ||
75 | CC *disputeCond = MakeDisputeCond(); | |
561f3e18 | 76 | mtx.vin.push_back(CTxIn(signedSessionTxHash, 0, CScript())); |
37592740 SS |
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 | ||
561f3e18 | 84 | CMutableTransaction BetProtocol::MakePostEvidenceTx(uint256 signedSessionTxHash, |
37592740 SS |
85 | int playerIdx, std::vector<unsigned char> state) |
86 | { | |
87 | CMutableTransaction mtx; | |
88 | ||
561f3e18 | 89 | mtx.vin.push_back(CTxIn(signedSessionTxHash, playerIdx+1, CScript())); |
37592740 SS |
90 | mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << state)); |
91 | ||
92 | return mtx; | |
93 | } | |
94 | ||
37592740 | 95 | |
561f3e18 | 96 | CC* BetProtocol::MakePayoutCond(uint256 signedSessionTxHash) |
37592740 | 97 | { |
561f3e18 SS |
98 | // TODO: 2/3 majority |
99 | CC* agree = CCNewThreshold(players.size(), PlayerConditions()); | |
37592740 SS |
100 | |
101 | CC *import; | |
102 | { | |
39c9911e SS |
103 | CC *importEval = CCNewEval(E_MARSHAL( |
104 | ss << EVAL_IMPORTPAYOUT << signedSessionTxHash; | |
105 | )); | |
37592740 | 106 | |
561f3e18 | 107 | CC *oneof = CCNewThreshold(1, PlayerConditions()); |
37592740 SS |
108 | |
109 | import = CCNewThreshold(2, {oneof, importEval}); | |
110 | } | |
111 | ||
112 | return CCNewThreshold(1, {agree, import}); | |
113 | } | |
114 | ||
115 | ||
561f3e18 | 116 | CMutableTransaction BetProtocol::MakeStakeTx(CAmount totalPayout, uint256 signedSessionTxHash) |
37592740 SS |
117 | { |
118 | CMutableTransaction mtx; | |
37592740 | 119 | |
561f3e18 | 120 | CC *payoutCond = MakePayoutCond(signedSessionTxHash); |
37592740 SS |
121 | mtx.vout.push_back(CTxOut(totalPayout, CCPubKey(payoutCond))); |
122 | cc_free(payoutCond); | |
123 | ||
124 | return mtx; | |
125 | } | |
126 | ||
127 | ||
561f3e18 SS |
128 | CMutableTransaction BetProtocol::MakeAgreePayoutTx(std::vector<CTxOut> payouts, |
129 | uint256 signedStakeTxHash) | |
37592740 SS |
130 | { |
131 | CMutableTransaction mtx; | |
561f3e18 SS |
132 | mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript())); |
133 | mtx.vout = payouts; | |
37592740 SS |
134 | return mtx; |
135 | } | |
136 | ||
137 | ||
561f3e18 SS |
138 | CMutableTransaction BetProtocol::MakeImportPayoutTx(std::vector<CTxOut> payouts, |
139 | CTransaction signedDisputeTx, uint256 signedStakeTxHash, MoMProof momProof) | |
37592740 | 140 | { |
37592740 | 141 | CMutableTransaction mtx; |
561f3e18 | 142 | mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript())); |
37592740 | 143 | mtx.vout = payouts; |
56cf273f | 144 | CScript proofData; |
39c9911e | 145 | proofData << OP_RETURN << E_MARSHAL(ss << momProof << signedDisputeTx); |
56cf273f | 146 | mtx.vout.insert(mtx.vout.begin(), CTxOut(0, proofData)); |
561f3e18 | 147 | return mtx; |
37592740 | 148 | } |
56cf273f SS |
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 | } | |
0cb91a8d SS |
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 | { | |
06c960d2 | 217 | NotarisationData data(0); |
0cb91a8d SS |
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 | } |