]>
Commit | Line | Data |
---|---|---|
b2a98c42 MT |
1 | /******************************************************************** |
2 | * (C) 2019 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 provides support for PBaaS initialization, notarization, and cross-chain token | |
8 | * transactions and enabling liquid or non-liquid tokens across the | |
9 | * Verus ecosystem. | |
10 | * | |
11 | */ | |
12 | ||
13 | #include "pbaas/pbaas.h" | |
14 | #include "pbaas/notarization.h" | |
15 | #include "rpc/pbaasrpc.h" | |
16 | #include "pbaas/crosschainrpc.h" | |
17 | #include "base58.h" | |
85c51d62 | 18 | #include "timedata.h" |
b2a98c42 MT |
19 | |
20 | using namespace std; | |
21 | ||
22 | CConnectedChains ConnectedChains; | |
23 | ||
24 | bool IsVerusActive() | |
25 | { | |
26 | return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0); | |
27 | } | |
28 | ||
29 | // this adds an opret to a mutable transaction and returns the voutnum if it could be added | |
30 | int32_t AddOpRetOutput(CMutableTransaction &mtx, const CScript &opRetScript) | |
31 | { | |
32 | if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY) | |
33 | { | |
34 | CTxOut vOut = CTxOut(); | |
35 | vOut.scriptPubKey = opRetScript; | |
36 | vOut.nValue = 0; | |
37 | mtx.vout.push_back(vOut); | |
38 | return mtx.vout.size() - 1; | |
39 | } | |
40 | else | |
41 | { | |
42 | return -1; | |
43 | } | |
44 | } | |
45 | ||
46 | // returns a pointer to a base chain object, which can be cast to the | |
47 | // object type indicated in its objType member | |
48 | uint256 GetChainObjectHash(const CBaseChainObject &bo) | |
49 | { | |
50 | union { | |
51 | const CChainObject<CBlockHeader> *pNewHeader; | |
52 | const CChainObject<CTransaction> *pNewTx; | |
53 | const CChainObject<CMerkleBranch> *pNewProof; | |
54 | const CChainObject<CHeaderRef> *pNewHeaderRef; | |
55 | const CChainObject<CTransactionRef> *pNewTxRef; | |
56 | const CChainObject<COpRetRef> *pNewOpRetRef; | |
57 | const CBaseChainObject *retPtr; | |
58 | }; | |
59 | ||
60 | retPtr = &bo; | |
61 | ||
62 | switch(bo.objectType) | |
63 | { | |
64 | case CHAINOBJ_HEADER: | |
65 | return pNewHeader->GetHash(); | |
66 | ||
67 | case CHAINOBJ_TRANSACTION: | |
68 | return pNewTx->GetHash(); | |
69 | ||
70 | case CHAINOBJ_PROOF: | |
71 | return pNewProof->GetHash(); | |
72 | ||
73 | case CHAINOBJ_HEADER_REF: | |
74 | return pNewHeaderRef->GetHash(); | |
75 | ||
76 | case CHAINOBJ_TRANSACTION_REF: | |
77 | return pNewTxRef->GetHash(); | |
78 | ||
79 | case CHAINOBJ_OPRET_REF: | |
80 | return pNewOpRetRef->GetHash(); | |
81 | } | |
82 | return uint256(); | |
83 | } | |
84 | ||
85 | // used to export coins from one chain to another, if they are not native, they are represented on the other | |
86 | // chain as tokens | |
87 | bool ValidateChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) | |
88 | { | |
89 | ||
90 | } | |
91 | ||
92 | // used to validate import of coins from one chain to another. if they are not native and are supported, | |
93 | // they are represented o the chain as tokens | |
94 | bool ValidateChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) | |
95 | { | |
96 | ||
97 | } | |
98 | ||
99 | // used to validate a specific service reward based on the spending transaction | |
100 | bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) | |
101 | { | |
102 | ||
103 | } | |
104 | ||
105 | // used as a proxy token output for a reserve currency on its fractional reserve chain | |
106 | bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) | |
107 | { | |
108 | ||
109 | } | |
110 | ||
111 | // used to convert a fractional reserve currency into its reserve and back | |
112 | bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) | |
113 | { | |
114 | ||
115 | } | |
116 | ||
117 | // used for distribution of premine | |
118 | bool ValidatePremineOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) | |
119 | { | |
120 | ||
121 | } | |
122 | ||
123 | /* | |
124 | * Verifies that the input objects match the hashes and returns the transaction. | |
125 | * | |
126 | * If the opRetTx has the op ret, this calculates based on the actual transaction and | |
127 | * validates the hashes. If the opRetTx does not have the opRet itself, this validates | |
128 | * by ensuring that all objects are present on this chain, composing the opRet, and | |
129 | * ensuring that the transaction then hashes to the correct txid. | |
130 | * | |
131 | */ | |
132 | bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof) | |
133 | { | |
134 | // enumerate through the objects and validate that they are objects of the expected type that hash | |
135 | // to the value expected. return true if so | |
136 | ||
137 | } | |
138 | ||
139 | int8_t ObjTypeCode(COpRetProof &obj) | |
140 | { | |
141 | return CHAINOBJ_OPRETPROOF; | |
142 | } | |
143 | ||
144 | int8_t ObjTypeCode(CBlockHeader &obj) | |
145 | { | |
146 | return CHAINOBJ_HEADER; | |
147 | } | |
148 | ||
149 | int8_t ObjTypeCode(CMerkleBranch &obj) | |
150 | { | |
151 | return CHAINOBJ_PROOF; | |
152 | } | |
153 | ||
154 | int8_t ObjTypeCode(CTransaction &obj) | |
155 | { | |
156 | return CHAINOBJ_TRANSACTION; | |
157 | } | |
158 | ||
159 | int8_t ObjTypeCode(CHeaderRef &obj) | |
160 | { | |
161 | return CHAINOBJ_HEADER_REF; | |
162 | } | |
163 | ||
164 | int8_t ObjTypeCode(CTransactionRef &obj) | |
165 | { | |
166 | return CHAINOBJ_TRANSACTION_REF; | |
167 | } | |
168 | ||
169 | int8_t ObjTypeCode(COpRetRef &obj) | |
170 | { | |
171 | return CHAINOBJ_OPRET_REF; | |
172 | } | |
173 | ||
174 | // this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction | |
f8a22472 | 175 | CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs) |
b2a98c42 MT |
176 | { |
177 | CScript vData; | |
178 | CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION); | |
179 | bool error = false; | |
180 | ||
181 | for (auto pobj : objPtrs) | |
182 | { | |
183 | if (!DehydrateChainObject(s, pobj)) | |
184 | { | |
185 | error = true; | |
186 | break; | |
187 | } | |
188 | } | |
189 | ||
190 | std::vector<unsigned char> vch(s.begin(), s.end()); | |
191 | ||
192 | vData << OPRETTYPE_OBJECTARR << vch; | |
193 | vch = std::vector<unsigned char>(vData.begin(), vData.end()); | |
194 | return CScript() << OP_RETURN << vch; | |
195 | } | |
196 | ||
197 | std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript) | |
198 | { | |
199 | std::vector<unsigned char> vch; | |
200 | std::vector<CBaseChainObject *> vRet; | |
201 | if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0) | |
202 | { | |
203 | CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION); | |
204 | ||
205 | CBaseChainObject *pobj; | |
206 | while (!s.empty() && (pobj = RehydrateChainObject(s))) | |
207 | { | |
208 | vRet.push_back(pobj); | |
209 | } | |
210 | } | |
211 | return vRet; | |
212 | } | |
213 | ||
9f0c14b2 MT |
214 | CNodeData::CNodeData(UniValue &obj) |
215 | { | |
4fa3b13d | 216 | networkAddress = uni_get_str(find_value(obj, "networkaddress")); |
f8f61a6d | 217 | paymentAddress = uni_get_str(find_value(obj, "paymentaddress")); |
9f0c14b2 MT |
218 | } |
219 | ||
220 | UniValue CNodeData::ToUniValue() const | |
221 | { | |
222 | UniValue obj(UniValue::VOBJ); | |
223 | obj.push_back(Pair("networkaddress", networkAddress)); | |
f8f61a6d | 224 | obj.push_back(Pair("paymentaddress", paymentAddress)); |
9f0c14b2 MT |
225 | return obj; |
226 | } | |
227 | ||
f8f61a6d | 228 | CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj) |
9f0c14b2 | 229 | { |
f8f61a6d | 230 | nVersion = PBAAS_VERSION; |
4fa3b13d | 231 | name = uni_get_str(find_value(obj, "name")); |
f8f61a6d | 232 | address = uni_get_str(find_value(obj, "address")); |
4fa3b13d | 233 | premine = uni_get_int64(find_value(obj, "premine")); |
f8f61a6d | 234 | conversion = uni_get_int64(find_value(obj, "conversion")); |
235 | launchFee = uni_get_int64(find_value(obj, "launchfee")); | |
4fa3b13d MT |
236 | startBlock = uni_get_int(find_value(obj, "startblock")); |
237 | endBlock = uni_get_int(find_value(obj, "endblock")); | |
9f0c14b2 | 238 | |
4fa3b13d | 239 | auto vEras = uni_getValues(find_value(obj, "eras")); |
f8f61a6d | 240 | if (vEras.size() > ASSETCHAINS_MAX_ERAS) |
241 | { | |
242 | vEras.resize(ASSETCHAINS_MAX_ERAS); | |
243 | } | |
244 | eras = !vEras.size() ? 1 : vEras.size(); | |
9f0c14b2 MT |
245 | |
246 | for (auto era : vEras) | |
247 | { | |
4fa3b13d MT |
248 | rewards.push_back(uni_get_int64(find_value(era, "reward"))); |
249 | rewardsDecay.push_back(uni_get_int64(find_value(era, "decay"))); | |
250 | halving.push_back(uni_get_int64(find_value(era, "halving"))); | |
251 | eraEnd.push_back(uni_get_int64(find_value(era, "eraend"))); | |
252 | eraOptions.push_back(uni_get_int64(find_value(era, "eraoptions"))); | |
9f0c14b2 MT |
253 | } |
254 | ||
4fa3b13d MT |
255 | billingPeriod = uni_get_int(find_value(obj, "billingperiod")); |
256 | notarizationReward = uni_get_int64(find_value(obj, "notarizationreward")); | |
9f0c14b2 | 257 | |
4fa3b13d | 258 | auto nodeVec = uni_getValues(find_value(obj, "nodes")); |
9f0c14b2 MT |
259 | for (auto node : nodeVec) |
260 | { | |
4fa3b13d | 261 | nodes.push_back(CNodeData(node)); |
9f0c14b2 MT |
262 | } |
263 | } | |
264 | ||
b2a98c42 MT |
265 | CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool validate) |
266 | { | |
267 | bool definitionFound = false; | |
268 | nVersion = PBAAS_VERSION_INVALID; | |
269 | for (auto out : tx.vout) | |
270 | { | |
271 | uint32_t ecode; | |
272 | if (out.scriptPubKey.IsPayToCryptoCondition(&ecode)) | |
273 | { | |
274 | if (ecode == EVAL_PBAASDEFINITION) | |
275 | { | |
276 | if (definitionFound) | |
277 | { | |
278 | nVersion = PBAAS_VERSION_INVALID; | |
279 | } | |
280 | else | |
281 | { | |
282 | COptCCParams p; | |
283 | definitionFound = true; | |
284 | if (!IsPayToCryptoCondition(out.scriptPubKey, p, *this)) | |
285 | { | |
286 | nVersion = PBAAS_VERSION_INVALID; | |
287 | } | |
288 | } | |
289 | } | |
290 | } | |
291 | } | |
292 | ||
293 | if (validate) | |
294 | { | |
295 | ||
296 | } | |
297 | } | |
298 | ||
299 | uint160 CPBaaSChainDefinition::GetChainID(std::string name) | |
300 | { | |
301 | const char *chainName = name.c_str(); | |
302 | uint256 chainHash = Hash(chainName, chainName + strlen(chainName)); | |
303 | return Hash160(chainHash.begin(), chainHash.end()); | |
304 | } | |
305 | ||
306 | uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition) | |
307 | { | |
57055854 | 308 | return CCrossChainRPCData::GetConditionID(name, condition); |
b2a98c42 MT |
309 | } |
310 | ||
9f0c14b2 MT |
311 | UniValue CPBaaSChainDefinition::ToUniValue() const |
312 | { | |
313 | UniValue obj(UniValue::VOBJ); | |
314 | obj.push_back(Pair("version", (int64_t)nVersion)); | |
315 | obj.push_back(Pair("name", name)); | |
f8f61a6d | 316 | obj.push_back(Pair("address", address)); |
9f0c14b2 | 317 | obj.push_back(Pair("premine", (int64_t)premine)); |
f8f61a6d | 318 | obj.push_back(Pair("conversion", (int64_t)conversion)); |
319 | obj.push_back(Pair("launchfee", (int64_t)launchFee)); | |
320 | obj.push_back(Pair("conversionpercent", (double)conversion / 100000000)); | |
321 | obj.push_back(Pair("launchfeepercent", ((double)launchFee / 100000000) * 100)); | |
4fa3b13d MT |
322 | obj.push_back(Pair("startblock", (int32_t)startBlock)); |
323 | obj.push_back(Pair("endblock", (int32_t)endBlock)); | |
9f0c14b2 MT |
324 | |
325 | UniValue eraArr(UniValue::VARR); | |
326 | for (int i = 0; i < eras; i++) | |
327 | { | |
328 | UniValue era(UniValue::VOBJ); | |
4fa3b13d MT |
329 | era.push_back(Pair("reward", rewards.size() > i ? rewards[i] : (int64_t)0)); |
330 | era.push_back(Pair("decay", rewardsDecay.size() > i ? rewardsDecay[i] : (int64_t)0)); | |
331 | era.push_back(Pair("halving", halving.size() > i ? (int32_t)halving[i] : (int32_t)0)); | |
332 | era.push_back(Pair("eraend", eraEnd.size() > i ? (int32_t)eraEnd[i] : (int32_t)0)); | |
333 | era.push_back(Pair("eraoptions", eraOptions.size() > i ? (int32_t)eraOptions[i] : (int32_t)0)); | |
9f0c14b2 MT |
334 | eraArr.push_back(era); |
335 | } | |
336 | obj.push_back(Pair("eras", eraArr)); | |
337 | ||
4fa3b13d MT |
338 | obj.push_back(Pair("billingperiod", billingPeriod)); |
339 | obj.push_back(Pair("notarizationreward", notarizationReward)); | |
9f0c14b2 MT |
340 | |
341 | UniValue nodeArr(UniValue::VARR); | |
342 | for (auto node : nodes) | |
343 | { | |
344 | nodeArr.push_back(node.ToUniValue()); | |
345 | } | |
346 | obj.push_back(Pair("nodes", nodeArr)); | |
347 | ||
348 | return obj; | |
349 | } | |
350 | ||
5d5737c1 MT |
351 | int CPBaaSChainDefinition::GetDefinedPort() const |
352 | { | |
353 | int port; | |
354 | string host; | |
8d2c835d | 355 | for (auto node : nodes) |
5d5737c1 MT |
356 | { |
357 | SplitHostPort(node.networkAddress, port, host); | |
358 | if (port) | |
359 | { | |
360 | return port; | |
361 | } | |
362 | } | |
363 | return 0; | |
364 | } | |
365 | ||
f8f61a6d | 366 | #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff |
344a051d | 367 | extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO; |
f8f61a6d | 368 | extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3]; |
344a051d | 369 | extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS; |
f8f61a6d | 370 | extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA; |
f2d873d0 | 371 | extern std::string VERUS_CHAINNAME; |
f8f61a6d | 372 | |
ccac80a3 | 373 | // adds the chain definition for this chain and nodes as well |
374 | // this also sets up the notarization chain, if there is one | |
f8f61a6d | 375 | bool SetThisChain(UniValue &chainDefinition) |
9f0c14b2 | 376 | { |
ccac80a3 | 377 | ConnectedChains.ThisChain() = CPBaaSChainDefinition(chainDefinition); |
f8f61a6d | 378 | |
379 | if (ConnectedChains.ThisChain().IsValid()) | |
9f0c14b2 | 380 | { |
f8f61a6d | 381 | // set all command line parameters into mapArgs from chain definition |
382 | vector<string> nodeStrs; | |
383 | for (auto node : ConnectedChains.ThisChain().nodes) | |
384 | { | |
385 | nodeStrs.push_back(node.networkAddress); | |
386 | } | |
387 | if (nodeStrs.size()) | |
388 | { | |
389 | mapMultiArgs["-seednode"] = nodeStrs; | |
390 | } | |
8d2c835d | 391 | if (int port = ConnectedChains.ThisChain().GetDefinedPort()) |
5d5737c1 MT |
392 | { |
393 | mapArgs["-port"] = to_string(port); | |
394 | } | |
f8f61a6d | 395 | |
396 | ASSETCHAINS_SUPPLY = ConnectedChains.ThisChain().premine; | |
397 | ASSETCHAINS_ALGO = ASSETCHAINS_VERUSHASH; | |
398 | ASSETCHAINS_LWMAPOS = 50; | |
399 | ||
400 | ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF; | |
401 | ASSETCHAINS_TIMEUNLOCKFROM = 0; | |
402 | ASSETCHAINS_TIMEUNLOCKTO = 0; | |
403 | ||
404 | auto numEras = ConnectedChains.ThisChain().eras; | |
405 | ASSETCHAINS_LASTERA = numEras - 1; | |
406 | mapArgs["-ac_eras"] = to_string(numEras); | |
407 | ||
408 | mapArgs["-ac_end"] = ""; | |
409 | mapArgs["-ac_reward"] = ""; | |
410 | mapArgs["-ac_halving"] = ""; | |
411 | mapArgs["-ac_decay"] = ""; | |
412 | ||
413 | for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++) | |
414 | { | |
415 | if (j > ASSETCHAINS_LASTERA) | |
416 | { | |
417 | ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1]; | |
418 | ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1]; | |
419 | ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1]; | |
420 | ASSETCHAINS_ENDSUBSIDY[j] = 0; | |
421 | } | |
422 | else | |
423 | { | |
424 | ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j]; | |
425 | ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j]; | |
426 | ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j]; | |
427 | ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j]; | |
428 | if (j == 0) | |
429 | { | |
430 | mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]); | |
431 | mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]); | |
432 | mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]); | |
433 | mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]); | |
434 | } | |
435 | else | |
436 | { | |
437 | mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]); | |
438 | mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]); | |
439 | mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]); | |
440 | mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]); | |
441 | } | |
442 | } | |
443 | } | |
444 | ||
445 | PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock; | |
446 | mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK); | |
447 | PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock; | |
448 | mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK); | |
449 | ||
450 | return true; | |
451 | } | |
452 | else | |
453 | { | |
454 | return false; | |
9f0c14b2 MT |
455 | } |
456 | } | |
457 | ||
b2a98c42 MT |
458 | // ensures that the chain definition is valid and that there are no other definitions of the same name |
459 | // that have been confirmed. | |
460 | bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) | |
461 | { | |
462 | // the chain definition output can be spent when the chain is at the end of its life and only then | |
463 | // TODO | |
464 | return false; | |
465 | } | |
466 | ||
467 | // ensures that the chain definition is valid and that there are no other definitions of the same name | |
468 | // that have been confirmed. | |
469 | bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) | |
470 | { | |
471 | // checked before a chain definition output script is accepted as a valid transaction | |
472 | ||
473 | // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have: | |
474 | // 1) valid chain definition output with parameters in proper ranges and no duplicate name | |
475 | // 2) notarization output with conformant values | |
476 | // 3) finalization output | |
477 | // 3) notarization funding | |
478 | // | |
479 | ||
480 | // get the source transaction | |
481 | uint256 blkHash; | |
482 | CTransaction thisTx; | |
483 | if (!myGetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash)) | |
484 | { | |
485 | LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str()); | |
486 | return false; | |
487 | } | |
488 | ||
489 | CPBaaSChainDefinition chainDef(thisTx, true); | |
490 | CPBaaSNotarization notarization(thisTx, true); | |
491 | CNotarizationFinalization finalization(thisTx, true); | |
492 | ||
493 | if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid()) | |
494 | { | |
495 | LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str()); | |
496 | return false; | |
497 | } | |
498 | ||
499 | CPBaaSChainDefinition prior; | |
500 | // this ensures that there is no other definition of the same name already on the blockchain | |
501 | if (!GetChainDefinition(chainDef.name, prior)) | |
502 | { | |
503 | LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str()); | |
504 | return false; | |
505 | } | |
506 | ||
507 | return true; | |
508 | } | |
509 | ||
510 | bool CConnectedChains::RemoveMergedBlock(uint160 chainID) | |
511 | { | |
2fd1f0fb | 512 | bool retval = false; |
b2a98c42 MT |
513 | LOCK(cs_mergemining); |
514 | auto chainIt = mergeMinedChains.find(chainID); | |
515 | if (chainIt != mergeMinedChains.end()) | |
516 | { | |
517 | arith_uint256 target; | |
518 | target.SetCompact(chainIt->second.block.nBits); | |
519 | for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++) | |
520 | { | |
521 | // make sure we don't just match by target | |
522 | if (removeRange.first->second->GetChainID() == chainID) | |
523 | { | |
524 | mergeMinedTargets.erase(removeRange.first); | |
525 | break; | |
526 | } | |
527 | } | |
528 | mergeMinedChains.erase(chainID); | |
2fd1f0fb | 529 | dirty = retval = true; |
b2a98c42 MT |
530 | |
531 | // if we get to 0, give the thread a kick to stop waiting for mining | |
2fd1f0fb | 532 | //if (!mergeMinedChains.size()) |
533 | //{ | |
534 | // sem_submitthread.post(); | |
535 | //} | |
b2a98c42 | 536 | } |
2fd1f0fb | 537 | return retval; |
b2a98c42 MT |
538 | } |
539 | ||
540 | // remove merge mined chains added and not updated since a specific time | |
541 | uint32_t CConnectedChains::PruneOldChains(uint32_t pruneBefore) | |
542 | { | |
543 | vector<uint160> toRemove; | |
544 | ||
545 | LOCK(cs_mergemining); | |
546 | for (auto blkData : mergeMinedChains) | |
547 | { | |
548 | if (blkData.second.block.nTime < pruneBefore) | |
549 | { | |
550 | toRemove.push_back(blkData.first); | |
551 | } | |
552 | } | |
553 | ||
554 | for (auto id : toRemove) | |
555 | { | |
556 | RemoveMergedBlock(id); | |
557 | } | |
558 | } | |
559 | ||
560 | // adds or updates merge mined blocks | |
561 | // returns false if failed to add | |
562 | bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData) | |
563 | { | |
b2a98c42 MT |
564 | // determine if we should replace one or add to the merge mine vector |
565 | { | |
566 | LOCK(cs_mergemining); | |
567 | ||
2fd1f0fb | 568 | arith_uint256 target; |
b2a98c42 MT |
569 | uint160 cID = blkData.GetChainID(); |
570 | auto it = mergeMinedChains.find(cID); | |
571 | if (it != mergeMinedChains.end()) | |
572 | { | |
ca8e2295 | 573 | // remove and reinsert target, replace data |
ca8e2295 | 574 | target.SetCompact(it->second.block.nBits); |
575 | for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++) | |
576 | { | |
577 | // make sure we don't just match by target | |
578 | if (removeRange.first->second->GetChainID() == cID) | |
579 | { | |
580 | mergeMinedTargets.erase(removeRange.first); | |
581 | break; | |
582 | } | |
583 | } | |
b2a98c42 | 584 | it->second = blkData; |
ca8e2295 | 585 | target.SetCompact(blkData.block.nBits); |
586 | mergeMinedTargets.insert(make_pair(target, &it->second)); | |
b2a98c42 MT |
587 | } |
588 | else | |
589 | { | |
b2a98c42 MT |
590 | target.SetCompact(blkData.block.nBits); |
591 | mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second))); | |
592 | } | |
2830db29 | 593 | dirty = true; |
b2a98c42 | 594 | } |
bce52b27 | 595 | return true; |
b2a98c42 MT |
596 | } |
597 | ||
598 | bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData) | |
599 | { | |
600 | { | |
601 | LOCK(cs_mergemining); | |
602 | auto chainIt = mergeMinedChains.find(chainID); | |
603 | if (chainIt != mergeMinedChains.end()) | |
604 | { | |
605 | rpcChainData = (CRPCChainData)chainIt->second; | |
606 | return true; | |
607 | } | |
608 | return false; | |
609 | } | |
610 | } | |
611 | ||
612 | // this returns a pointer to the data without copy and assumes the lock is held | |
613 | CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID) | |
614 | { | |
615 | { | |
616 | auto chainIt = mergeMinedChains.find(chainID); | |
617 | if (chainIt != mergeMinedChains.end()) | |
618 | { | |
619 | return &chainIt->second; | |
620 | } | |
621 | return NULL; | |
622 | } | |
623 | } | |
624 | ||
e771a884 | 625 | bool CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh) |
626 | { | |
c1e4f279 | 627 | printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str()); |
2830db29 | 628 | { |
629 | LOCK(cs_mergemining); | |
630 | qualifiedHeaders[UintToArith256(bh.GetHash())] = bh; | |
631 | } | |
e771a884 | 632 | sem_submitthread.post(); |
633 | } | |
634 | ||
f8f61a6d | 635 | // get the latest block header and submit one block at a time, returning after there are no more |
636 | // matching blocks to be found | |
637 | vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks() | |
b2a98c42 MT |
638 | { |
639 | std::set<uint160> inHeader; | |
f8f61a6d | 640 | bool submissionFound; |
641 | CPBaaSMergeMinedChainData chainData; | |
b2a98c42 MT |
642 | vector<pair<string, UniValue>> results; |
643 | ||
f8f61a6d | 644 | CBlockHeader bh; |
645 | arith_uint256 lastHash; | |
b2a98c42 MT |
646 | CPBaaSBlockHeader pbh; |
647 | ||
b2a98c42 | 648 | { |
f8f61a6d | 649 | do |
b2a98c42 | 650 | { |
f8f61a6d | 651 | submissionFound = false; |
b2a98c42 | 652 | { |
f8f61a6d | 653 | LOCK(cs_mergemining); |
2830db29 | 654 | // attempt to submit with the lowest hash answers first to increase the likelihood of submitting |
2fd1f0fb | 655 | // common, merge mined headers for notarization, drop out on any submission |
656 | for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin()) | |
f8f61a6d | 657 | { |
2fd1f0fb | 658 | // add the PBaaS chain ids from this header to a set for search |
2830db29 | 659 | for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++) |
f8f61a6d | 660 | { |
2830db29 | 661 | inHeader.insert(pbh.chainID); |
662 | } | |
b2a98c42 | 663 | |
2fd1f0fb | 664 | // now look through all targets that are equal to or above the hash of this header |
2830db29 | 665 | for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++) |
666 | { | |
667 | uint160 chainID = chainIt->second->GetChainID(); | |
668 | if (inHeader.count(chainID)) | |
f8f61a6d | 669 | { |
2830db29 | 670 | // first, check that the winning header matches the block that is there |
671 | CPBaaSPreHeader preHeader(chainIt->second->block); | |
672 | preHeader.SetBlockData(headerIt->second); | |
b2a98c42 | 673 | |
2830db29 | 674 | // check if the block header matches the block's specific data, only then can we create a submission from this block |
675 | if (headerIt->second.CheckNonCanonicalData(chainID)) | |
676 | { | |
677 | // save block as is, remove the block from merged headers, replace header, and submit | |
678 | chainData = *chainIt->second; | |
679 | ||
680 | *(CBlockHeader *)&chainData.block = headerIt->second; | |
b2a98c42 | 681 | |
2fd1f0fb | 682 | // once it is going to be submitted, remove block from this chain until a new one is added again |
2830db29 | 683 | RemoveMergedBlock(chainID); |
b2a98c42 | 684 | |
2830db29 | 685 | submissionFound = true; |
686 | } | |
c1e4f279 | 687 | //else // not an error condition. code was here for debugging |
688 | //{ | |
689 | // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str()); | |
690 | //} | |
f8f61a6d | 691 | } |
692 | } | |
2830db29 | 693 | |
2fd1f0fb | 694 | // if this header matched no block, discard and move to the next, otherwise, we'll drop through |
2830db29 | 695 | if (!submissionFound) |
696 | { | |
697 | qualifiedHeaders.erase(headerIt); | |
698 | } | |
b2a98c42 | 699 | } |
f8f61a6d | 700 | } |
701 | if (submissionFound) | |
702 | { | |
703 | // submit one block and loop again. this approach allows multiple threads | |
704 | // to collectively empty the submission queue, mitigating the impact of | |
705 | // any one stalled daemon | |
b2a98c42 | 706 | UniValue submitParams(UniValue::VARR); |
f8f61a6d | 707 | submitParams.push_back(EncodeHexBlk(chainData.block)); |
be17c611 | 708 | UniValue result, error; |
9a891caf | 709 | try |
710 | { | |
711 | result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost); | |
be17c611 | 712 | result = find_value(result, "result"); |
713 | error = find_value(result, "error"); | |
9a891caf | 714 | } |
715 | catch (exception e) | |
716 | { | |
717 | result = UniValue(e.what()); | |
718 | } | |
f8f61a6d | 719 | results.push_back(make_pair(chainData.chainDefinition.name, result)); |
be17c611 | 720 | if (result.isStr() || !error.isNull()) |
f8f61a6d | 721 | { |
be17c611 | 722 | printf("Error submitting block to %s chain: %s\n", chainData.chainDefinition.name.c_str(), result.isStr() ? result.get_str().c_str() : error.get_str().c_str()); |
f8f61a6d | 723 | } |
724 | else | |
725 | { | |
726 | printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str()); | |
f8f61a6d | 727 | } |
b2a98c42 | 728 | } |
f8f61a6d | 729 | } while (submissionFound); |
b2a98c42 MT |
730 | } |
731 | return results; | |
732 | } | |
733 | ||
f8f61a6d | 734 | // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header |
b2a98c42 MT |
735 | uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh) |
736 | { | |
737 | vector<uint160> inHeader; | |
738 | vector<UniValue> toCombine; | |
739 | arith_uint256 blkHash = UintToArith256(bh.GetHash()); | |
f8f61a6d | 740 | arith_uint256 target(0); |
b2a98c42 MT |
741 | |
742 | CPBaaSBlockHeader pbh; | |
743 | ||
b2a98c42 | 744 | { |
f8f61a6d | 745 | LOCK(cs_mergemining); |
b2a98c42 | 746 | |
f8f61a6d | 747 | CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution); |
748 | ||
749 | for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++) | |
b2a98c42 | 750 | { |
f8f61a6d | 751 | if (bh.GetPBaaSHeader(pbh, i)) |
752 | { | |
753 | inHeader.push_back(pbh.chainID); | |
754 | } | |
755 | } | |
756 | ||
757 | // loop through the existing PBaaS chain ids in the header | |
2fd1f0fb | 758 | // remove any that are not either this Chain ID or in our local collection and then add all that are present |
f8f61a6d | 759 | for (uint32_t i = 0; i < inHeader.size(); i++) |
760 | { | |
761 | auto it = mergeMinedChains.find(inHeader[i]); | |
762 | if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end())) | |
763 | { | |
764 | bh.DeletePBaaSHeader(i); | |
765 | } | |
b2a98c42 | 766 | } |
b2a98c42 | 767 | |
b2a98c42 MT |
768 | for (auto chain : mergeMinedChains) |
769 | { | |
770 | // get the native PBaaS header for each chain and put it into the | |
771 | // header we are given | |
772 | uint160 cid = chain.second.GetChainID(); | |
3a2a1777 | 773 | if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1) |
b2a98c42 MT |
774 | { |
775 | if (!bh.AddUpdatePBaaSHeader(pbh)) | |
776 | { | |
777 | LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str()); | |
f8f61a6d | 778 | break; |
779 | } | |
780 | else | |
781 | { | |
782 | arith_uint256 t; | |
783 | t.SetCompact(chain.second.block.nBits); | |
784 | if (t > target) | |
785 | { | |
786 | target = t; | |
787 | } | |
b2a98c42 MT |
788 | } |
789 | } | |
790 | } | |
2830db29 | 791 | dirty = false; |
b2a98c42 | 792 | } |
f8f61a6d | 793 | return target.GetCompact(); |
b2a98c42 MT |
794 | } |
795 | ||
ccac80a3 | 796 | bool CConnectedChains::IsVerusPBaaSAvailable() |
797 | { | |
798 | return notaryChainVersion > "0.6"; | |
799 | } | |
800 | ||
f8f61a6d | 801 | extern string PBAAS_HOST, PBAAS_USERPASS; |
802 | extern int32_t PBAAS_PORT; | |
6bb9fdbc | 803 | bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni) |
9f0c14b2 | 804 | { |
6bb9fdbc | 805 | if (chainInfoUni.isObject() && chainDefUni.isObject()) |
9f0c14b2 | 806 | { |
6bb9fdbc | 807 | UniValue uniVer = find_value(chainInfoUni, "VRSCversion"); |
f8f61a6d | 808 | if (uniVer.isStr()) |
809 | { | |
2156d3d0 | 810 | LOCK(cs_mergemining); |
f8f61a6d | 811 | notaryChainVersion = uni_get_str(uniVer); |
6bb9fdbc | 812 | notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks")); |
813 | CPBaaSChainDefinition chainDef(chainDefUni); | |
f8f61a6d | 814 | notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS); |
815 | } | |
9f0c14b2 | 816 | } |
ccac80a3 | 817 | return IsVerusPBaaSAvailable(); |
9f0c14b2 MT |
818 | } |
819 | ||
2299bd95 | 820 | bool CConnectedChains::CheckVerusPBaaSAvailable() |
9f0c14b2 MT |
821 | { |
822 | if (IsVerusActive()) | |
823 | { | |
ccac80a3 | 824 | notaryChainVersion = ""; |
9f0c14b2 MT |
825 | } |
826 | else | |
827 | { | |
828 | // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number | |
2156d3d0 | 829 | // tolerate only 15 second timeout |
9a891caf | 830 | UniValue chainInfo, chainDef; |
831 | try | |
832 | { | |
833 | UniValue params(UniValue::VARR); | |
2fd1f0fb | 834 | chainInfo = find_value(RPCCallRoot("getinfo", params), "result"); |
9a891caf | 835 | if (!chainInfo.isNull()) |
836 | { | |
837 | params.push_back(VERUS_CHAINNAME); | |
2fd1f0fb | 838 | chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result"); |
9a891caf | 839 | |
840 | if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef)) | |
841 | { | |
842 | return true; | |
843 | } | |
844 | } | |
845 | } catch (exception e) | |
4fa3b13d | 846 | { |
4fa3b13d | 847 | } |
9f0c14b2 | 848 | } |
9a891caf | 849 | notaryChainVersion = ""; |
4fa3b13d | 850 | return false; |
9f0c14b2 MT |
851 | } |
852 | ||
b2a98c42 MT |
853 | void CConnectedChains::SubmissionThread() |
854 | { | |
855 | try | |
856 | { | |
857 | arith_uint256 lastHash; | |
858 | ||
a82942e4 | 859 | // wait for something to check on, then submit blocks that should be submitted |
b2a98c42 MT |
860 | while (true) |
861 | { | |
9f0c14b2 | 862 | if (IsVerusActive()) |
b2a98c42 | 863 | { |
a82942e4 | 864 | // blocks get discarded after no refresh for 5 minutes by default |
865 | ConnectedChains.PruneOldChains(GetAdjustedTime() - 300); | |
2fd1f0fb | 866 | bool submit = false; |
b2a98c42 | 867 | { |
2fd1f0fb | 868 | LOCK(cs_mergemining); |
869 | submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0; | |
870 | } | |
871 | if (submit) | |
872 | { | |
873 | SubmitQualifiedBlocks(); | |
b2a98c42 | 874 | } |
9f0c14b2 MT |
875 | else |
876 | { | |
2fd1f0fb | 877 | sem_submitthread.wait(); |
9f0c14b2 | 878 | } |
b2a98c42 MT |
879 | } |
880 | else | |
881 | { | |
9f0c14b2 MT |
882 | UniValue result; |
883 | ||
884 | // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number | |
9a891caf | 885 | CheckVerusPBaaSAvailable(); |
2830db29 | 886 | for (int i = 0; i < 10; i++) |
887 | { | |
888 | boost::this_thread::interruption_point(); | |
889 | sleep(1); | |
890 | } | |
b2a98c42 | 891 | } |
9f0c14b2 | 892 | |
b2a98c42 MT |
893 | boost::this_thread::interruption_point(); |
894 | } | |
895 | } | |
896 | catch (const boost::thread_interrupted&) | |
897 | { | |
898 | LogPrintf("Verus merge mining thread terminated\n"); | |
899 | } | |
900 | } | |
9f0c14b2 | 901 | |
b2a98c42 MT |
902 | void CConnectedChains::SubmissionThreadStub() |
903 | { | |
904 | ConnectedChains.SubmissionThread(); | |
905 | } | |
906 | ||
68b309c0 MT |
907 | bool IsChainDefinitionInput(const CScript &scriptSig) |
908 | { | |
909 | uint32_t ecode; | |
910 | return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION; | |
911 | } |