]> Git Repo - VerusCoin.git/blame - src/pbaas/pbaas.cpp
Create import transactions with all outputs
[VerusCoin.git] / src / pbaas / pbaas.cpp
CommitLineData
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"
0d5cfbf5 19#include "main.h"
c3250dcd 20#include "transaction_builder.h"
b2a98c42
MT
21
22using namespace std;
23
24CConnectedChains ConnectedChains;
25
26bool IsVerusActive()
27{
28 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
29}
30
31// this adds an opret to a mutable transaction and returns the voutnum if it could be added
32int32_t AddOpRetOutput(CMutableTransaction &mtx, const CScript &opRetScript)
33{
34 if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY)
35 {
36 CTxOut vOut = CTxOut();
37 vOut.scriptPubKey = opRetScript;
38 vOut.nValue = 0;
39 mtx.vout.push_back(vOut);
40 return mtx.vout.size() - 1;
41 }
42 else
43 {
44 return -1;
45 }
46}
47
48// returns a pointer to a base chain object, which can be cast to the
49// object type indicated in its objType member
50uint256 GetChainObjectHash(const CBaseChainObject &bo)
51{
52 union {
53 const CChainObject<CBlockHeader> *pNewHeader;
54 const CChainObject<CTransaction> *pNewTx;
55 const CChainObject<CMerkleBranch> *pNewProof;
56 const CChainObject<CHeaderRef> *pNewHeaderRef;
1fa4454d 57 const CChainObject<CPriorBlocksCommitment> *pPriors;
989b1de1 58 const CChainObject<CReserveTransfer> *pExport;
b2a98c42
MT
59 const CBaseChainObject *retPtr;
60 };
61
62 retPtr = &bo;
63
64 switch(bo.objectType)
65 {
66 case CHAINOBJ_HEADER:
67 return pNewHeader->GetHash();
68
69 case CHAINOBJ_TRANSACTION:
70 return pNewTx->GetHash();
71
72 case CHAINOBJ_PROOF:
73 return pNewProof->GetHash();
74
75 case CHAINOBJ_HEADER_REF:
76 return pNewHeaderRef->GetHash();
77
1fa4454d
MT
78 case CHAINOBJ_PRIORBLOCKS:
79 return pPriors->GetHash();
989b1de1
MT
80
81 case CHAINOBJ_RESERVETRANSFER:
82 return pExport->GetHash();
b2a98c42
MT
83 }
84 return uint256();
85}
86
87// used to export coins from one chain to another, if they are not native, they are represented on the other
88// chain as tokens
89bool ValidateChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
90{
91
92}
93
94// used to validate import of coins from one chain to another. if they are not native and are supported,
95// they are represented o the chain as tokens
96bool ValidateChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
97{
98
99}
100
101// used to validate a specific service reward based on the spending transaction
102bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
103{
13ed2980
MT
104 // for each type of service reward, we need to check and see if the spender is
105 // correctly formatted to be a valid spend of the service reward. for notarization
106 // we ensure that the notarization and its outputs are valid and that the spend
107 // applies to the correct billing period
108 return true;
b2a98c42
MT
109}
110
111// used as a proxy token output for a reserve currency on its fractional reserve chain
112bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
113{
114
115}
116
117// used to convert a fractional reserve currency into its reserve and back
118bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
119{
120
121}
122
123// used for distribution of premine
124bool ValidatePremineOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
125{
126
127}
128
129/*
130 * Verifies that the input objects match the hashes and returns the transaction.
131 *
132 * If the opRetTx has the op ret, this calculates based on the actual transaction and
133 * validates the hashes. If the opRetTx does not have the opRet itself, this validates
134 * by ensuring that all objects are present on this chain, composing the opRet, and
135 * ensuring that the transaction then hashes to the correct txid.
136 *
137 */
138bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
139{
140 // enumerate through the objects and validate that they are objects of the expected type that hash
141 // to the value expected. return true if so
142
143}
144
1fa4454d 145int8_t ObjTypeCode(const CBlockHeader &obj)
b2a98c42
MT
146{
147 return CHAINOBJ_HEADER;
148}
149
1fa4454d 150int8_t ObjTypeCode(const CMerkleBranch &obj)
b2a98c42
MT
151{
152 return CHAINOBJ_PROOF;
153}
154
1fa4454d 155int8_t ObjTypeCode(const CTransaction &obj)
b2a98c42
MT
156{
157 return CHAINOBJ_TRANSACTION;
158}
159
1fa4454d 160int8_t ObjTypeCode(const CHeaderRef &obj)
b2a98c42
MT
161{
162 return CHAINOBJ_HEADER_REF;
163}
164
1fa4454d 165int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
b2a98c42 166{
1fa4454d 167 return CHAINOBJ_PRIORBLOCKS;
b2a98c42
MT
168}
169
989b1de1
MT
170int8_t ObjTypeCode(const CReserveTransfer &obj)
171{
172 return CHAINOBJ_RESERVETRANSFER;
173}
174
b2a98c42 175// this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
f8a22472 176CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs)
b2a98c42
MT
177{
178 CScript vData;
179 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
0b806be9 180 s << (int32_t)OPRETTYPE_OBJECTARR;
b2a98c42
MT
181 bool error = false;
182
183 for (auto pobj : objPtrs)
184 {
d48d49b6
MT
185 try
186 {
187 if (!DehydrateChainObject(s, pobj))
188 {
189 error = true;
190 break;
191 }
192 }
193 catch(const std::exception& e)
b2a98c42 194 {
d48d49b6 195 std::cerr << e.what() << '\n';
b2a98c42
MT
196 error = true;
197 break;
198 }
199 }
200
05578402
MT
201 //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
202 //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
6278cf9c 203
b2a98c42 204 std::vector<unsigned char> vch(s.begin(), s.end());
d48d49b6 205 return error ? CScript() : CScript() << OP_RETURN << vch;
b2a98c42
MT
206}
207
a8c2cb11
MT
208void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
209{
210 for (auto pobj : ora)
211 {
212 switch(pobj->objectType)
213 {
214 case CHAINOBJ_HEADER:
215 {
216 delete (CChainObject<CBlockHeader> *)pobj;
217 break;
218 }
219
220 case CHAINOBJ_TRANSACTION:
221 {
222 delete (CChainObject<CTransaction> *)pobj;
223 break;
224 }
225
226 case CHAINOBJ_PROOF:
227 {
228 delete (CChainObject<CMerkleBranch> *)pobj;
229 break;
230 }
231
232 case CHAINOBJ_HEADER_REF:
233 {
234 delete (CChainObject<CHeaderRef> *)pobj;
235 break;
236 }
237
238 case CHAINOBJ_PRIORBLOCKS:
239 {
240 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
241 break;
242 }
243
989b1de1
MT
244 case CHAINOBJ_RESERVETRANSFER:
245 {
246 delete (CChainObject<CReserveTransfer> *)pobj;
247 break;
248 }
249
a8c2cb11
MT
250 default:
251 delete pobj;
252 }
253 }
254}
255
b2a98c42
MT
256std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
257{
258 std::vector<unsigned char> vch;
259 std::vector<CBaseChainObject *> vRet;
260 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
261 {
262 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
263
1fa4454d
MT
264 int32_t opRetType;
265
266 try
267 {
268 s >> opRetType;
269 if (opRetType == OPRETTYPE_OBJECTARR)
270 {
271 CBaseChainObject *pobj;
272 while (!s.empty() && (pobj = RehydrateChainObject(s)))
273 {
274 vRet.push_back(pobj);
275 }
aeca4678
MT
276 if (!s.empty())
277 {
2d8cedc7 278 printf("failed to load all objects in opret");
ba256d3a 279 DeleteOpRetObjects(vRet);
2d8cedc7 280 vRet.clear();
aeca4678 281 }
1fa4454d
MT
282 }
283 }
284 catch(const std::exception& e)
b2a98c42 285 {
1fa4454d 286 std::cerr << e.what() << '\n';
ba256d3a 287 DeleteOpRetObjects(vRet);
1fa4454d 288 vRet.clear();
b2a98c42
MT
289 }
290 }
291 return vRet;
292}
293
9f0c14b2
MT
294CNodeData::CNodeData(UniValue &obj)
295{
4fa3b13d 296 networkAddress = uni_get_str(find_value(obj, "networkaddress"));
ebee7b5b
MT
297 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
298 ba.GetKeyID(paymentAddress);
9f0c14b2
MT
299}
300
301UniValue CNodeData::ToUniValue() const
302{
303 UniValue obj(UniValue::VOBJ);
304 obj.push_back(Pair("networkaddress", networkAddress));
ebee7b5b 305 obj.push_back(Pair("paymentaddress", CBitcoinAddress(paymentAddress).ToString()));
9f0c14b2
MT
306 return obj;
307}
308
f8f61a6d 309CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj)
9f0c14b2 310{
f8f61a6d 311 nVersion = PBAAS_VERSION;
f39e2bdf
MT
312 name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
313
2ebe5d52
MT
314 string invalidChars = "\\/:?\"<>|";
315 for (int i = 0; i < name.size(); i++)
316 {
317 if (invalidChars.find(name[i]) != string::npos)
318 {
319 name[i] = '_';
320 }
321 }
f39e2bdf 322
ebee7b5b
MT
323 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
324 ba.GetKeyID(address);
4fa3b13d 325 premine = uni_get_int64(find_value(obj, "premine"));
e7e14f44 326 initialcontribution = uni_get_int64(find_value(obj, "initialcontribution"));
f8f61a6d 327 conversion = uni_get_int64(find_value(obj, "conversion"));
989b1de1 328 maxpreconvert = uni_get_int64(find_value(obj, "maxpreconvert"));
e7e14f44 329 preconverted = uni_get_int64(find_value(obj, "preconverted"));
f8f61a6d 330 launchFee = uni_get_int64(find_value(obj, "launchfee"));
4fa3b13d
MT
331 startBlock = uni_get_int(find_value(obj, "startblock"));
332 endBlock = uni_get_int(find_value(obj, "endblock"));
9f0c14b2 333
4fa3b13d 334 auto vEras = uni_getValues(find_value(obj, "eras"));
f8f61a6d 335 if (vEras.size() > ASSETCHAINS_MAX_ERAS)
336 {
337 vEras.resize(ASSETCHAINS_MAX_ERAS);
338 }
339 eras = !vEras.size() ? 1 : vEras.size();
9f0c14b2
MT
340
341 for (auto era : vEras)
342 {
4fa3b13d
MT
343 rewards.push_back(uni_get_int64(find_value(era, "reward")));
344 rewardsDecay.push_back(uni_get_int64(find_value(era, "decay")));
345 halving.push_back(uni_get_int64(find_value(era, "halving")));
346 eraEnd.push_back(uni_get_int64(find_value(era, "eraend")));
347 eraOptions.push_back(uni_get_int64(find_value(era, "eraoptions")));
9f0c14b2
MT
348 }
349
4fa3b13d
MT
350 billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
351 notarizationReward = uni_get_int64(find_value(obj, "notarizationreward"));
9f0c14b2 352
4fa3b13d 353 auto nodeVec = uni_getValues(find_value(obj, "nodes"));
9f0c14b2
MT
354 for (auto node : nodeVec)
355 {
4fa3b13d 356 nodes.push_back(CNodeData(node));
9f0c14b2
MT
357 }
358}
359
b2a98c42
MT
360CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool validate)
361{
362 bool definitionFound = false;
363 nVersion = PBAAS_VERSION_INVALID;
364 for (auto out : tx.vout)
365 {
13ed2980
MT
366 COptCCParams p;
367 if (IsPayToCryptoCondition(out.scriptPubKey, p))
b2a98c42 368 {
13ed2980 369 if (p.evalCode == EVAL_PBAASDEFINITION)
b2a98c42
MT
370 {
371 if (definitionFound)
372 {
373 nVersion = PBAAS_VERSION_INVALID;
374 }
375 else
376 {
13ed2980 377 FromVector(p.vData[0], *this);
f39e2bdf
MT
378
379 // TODO - remove this after validation is finished, check now in case some larger strings got into the chain
2ebe5d52
MT
380 name = std::string(name, 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
381 string invalidChars = "\\/:?\"<>|";
382 for (int i = 0; i < name.size(); i++)
383 {
384 if (invalidChars.find(name[i]) != string::npos)
385 {
386 name[i] = '_';
387 }
388 }
f39e2bdf 389
b2a98c42 390 definitionFound = true;
b2a98c42
MT
391 }
392 }
393 }
394 }
395
396 if (validate)
397 {
398
399 }
400}
401
13ed2980
MT
402CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
403{
404 nVersion = PBAAS_VERSION_INVALID;
405 for (auto out : tx.vout)
406 {
407 COptCCParams p;
408 if (IsPayToCryptoCondition(out.scriptPubKey, p))
409 {
410 // always take the first for now
411 if (p.evalCode == EVAL_SERVICEREWARD)
412 {
413 FromVector(p.vData[0], *this);
414 break;
415 }
416 }
417 }
418
419 if (validate)
420 {
421
422 }
423}
424
41f170fd
MT
425CCrossChainImport::CCrossChainImport(const CTransaction &tx)
426{
c3250dcd 427 if (tx.vout.size())
41f170fd
MT
428 {
429 COptCCParams p;
c3250dcd 430 if (IsPayToCryptoCondition(tx.vout[0].scriptPubKey, p))
41f170fd
MT
431 {
432 // always take the first for now
433 if (p.evalCode == EVAL_CROSSCHAIN_IMPORT)
434 {
435 FromVector(p.vData[0], *this);
41f170fd
MT
436 }
437 }
438 }
439}
440
441CCrossChainExport::CCrossChainExport(const CTransaction &tx)
442{
443 for (auto out : tx.vout)
444 {
445 COptCCParams p;
446 if (IsPayToCryptoCondition(out.scriptPubKey, p))
447 {
448 // always take the first for now
449 if (p.evalCode == EVAL_CROSSCHAIN_EXPORT)
450 {
451 FromVector(p.vData[0], *this);
452 break;
453 }
454 }
455 }
456}
457
b2a98c42
MT
458uint160 CPBaaSChainDefinition::GetChainID(std::string name)
459{
460 const char *chainName = name.c_str();
461 uint256 chainHash = Hash(chainName, chainName + strlen(chainName));
462 return Hash160(chainHash.begin(), chainHash.end());
463}
464
c3250dcd 465uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition) const
b2a98c42 466{
57055854 467 return CCrossChainRPCData::GetConditionID(name, condition);
b2a98c42
MT
468}
469
9f0c14b2
MT
470UniValue CPBaaSChainDefinition::ToUniValue() const
471{
472 UniValue obj(UniValue::VOBJ);
473 obj.push_back(Pair("version", (int64_t)nVersion));
474 obj.push_back(Pair("name", name));
ebee7b5b 475 obj.push_back(Pair("paymentaddress", CBitcoinAddress(CTxDestination(address)).ToString()));
9f0c14b2 476 obj.push_back(Pair("premine", (int64_t)premine));
e7e14f44 477 obj.push_back(Pair("initialcontribution", (int64_t)initialcontribution));
f8f61a6d 478 obj.push_back(Pair("conversion", (int64_t)conversion));
989b1de1 479 obj.push_back(Pair("maxpreconvert", (int64_t)maxpreconvert));
e7e14f44 480 obj.push_back(Pair("preconverted", (int64_t)preconverted));
f8f61a6d 481 obj.push_back(Pair("launchfee", (int64_t)launchFee));
482 obj.push_back(Pair("conversionpercent", (double)conversion / 100000000));
483 obj.push_back(Pair("launchfeepercent", ((double)launchFee / 100000000) * 100));
4fa3b13d
MT
484 obj.push_back(Pair("startblock", (int32_t)startBlock));
485 obj.push_back(Pair("endblock", (int32_t)endBlock));
9f0c14b2
MT
486
487 UniValue eraArr(UniValue::VARR);
488 for (int i = 0; i < eras; i++)
489 {
490 UniValue era(UniValue::VOBJ);
4fa3b13d
MT
491 era.push_back(Pair("reward", rewards.size() > i ? rewards[i] : (int64_t)0));
492 era.push_back(Pair("decay", rewardsDecay.size() > i ? rewardsDecay[i] : (int64_t)0));
493 era.push_back(Pair("halving", halving.size() > i ? (int32_t)halving[i] : (int32_t)0));
494 era.push_back(Pair("eraend", eraEnd.size() > i ? (int32_t)eraEnd[i] : (int32_t)0));
495 era.push_back(Pair("eraoptions", eraOptions.size() > i ? (int32_t)eraOptions[i] : (int32_t)0));
9f0c14b2
MT
496 eraArr.push_back(era);
497 }
498 obj.push_back(Pair("eras", eraArr));
499
4fa3b13d
MT
500 obj.push_back(Pair("billingperiod", billingPeriod));
501 obj.push_back(Pair("notarizationreward", notarizationReward));
9f0c14b2
MT
502
503 UniValue nodeArr(UniValue::VARR);
504 for (auto node : nodes)
505 {
506 nodeArr.push_back(node.ToUniValue());
507 }
508 obj.push_back(Pair("nodes", nodeArr));
509
510 return obj;
511}
512
5d5737c1
MT
513int CPBaaSChainDefinition::GetDefinedPort() const
514{
515 int port;
516 string host;
8d2c835d 517 for (auto node : nodes)
5d5737c1
MT
518 {
519 SplitHostPort(node.networkAddress, port, host);
520 if (port)
521 {
522 return port;
523 }
524 }
525 return 0;
526}
527
f8f61a6d 528#define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
344a051d 529extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
f8f61a6d 530extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3];
344a051d 531extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
f8f61a6d 532extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
f2d873d0 533extern std::string VERUS_CHAINNAME;
f8f61a6d 534
ccac80a3 535// adds the chain definition for this chain and nodes as well
536// this also sets up the notarization chain, if there is one
f8f61a6d 537bool SetThisChain(UniValue &chainDefinition)
9f0c14b2 538{
ccac80a3 539 ConnectedChains.ThisChain() = CPBaaSChainDefinition(chainDefinition);
f8f61a6d 540
541 if (ConnectedChains.ThisChain().IsValid())
9f0c14b2 542 {
94a210aa
MT
543 memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
544 strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
545
f8f61a6d 546 // set all command line parameters into mapArgs from chain definition
547 vector<string> nodeStrs;
548 for (auto node : ConnectedChains.ThisChain().nodes)
549 {
550 nodeStrs.push_back(node.networkAddress);
551 }
552 if (nodeStrs.size())
553 {
554 mapMultiArgs["-seednode"] = nodeStrs;
555 }
8d2c835d 556 if (int port = ConnectedChains.ThisChain().GetDefinedPort())
5d5737c1
MT
557 {
558 mapArgs["-port"] = to_string(port);
559 }
f8f61a6d 560
561 ASSETCHAINS_SUPPLY = ConnectedChains.ThisChain().premine;
f81da2f3 562 mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
f8f61a6d 563 ASSETCHAINS_ALGO = ASSETCHAINS_VERUSHASH;
564 ASSETCHAINS_LWMAPOS = 50;
565
566 ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
567 ASSETCHAINS_TIMEUNLOCKFROM = 0;
568 ASSETCHAINS_TIMEUNLOCKTO = 0;
569
570 auto numEras = ConnectedChains.ThisChain().eras;
571 ASSETCHAINS_LASTERA = numEras - 1;
572 mapArgs["-ac_eras"] = to_string(numEras);
573
574 mapArgs["-ac_end"] = "";
575 mapArgs["-ac_reward"] = "";
576 mapArgs["-ac_halving"] = "";
577 mapArgs["-ac_decay"] = "";
578
579 for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
580 {
581 if (j > ASSETCHAINS_LASTERA)
582 {
583 ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1];
584 ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1];
585 ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1];
586 ASSETCHAINS_ENDSUBSIDY[j] = 0;
587 }
588 else
589 {
590 ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
591 ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
592 ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
593 ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
594 if (j == 0)
595 {
596 mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
597 mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
598 mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
599 mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
600 }
601 else
602 {
603 mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
604 mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
605 mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
606 mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
607 }
608 }
609 }
610
611 PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
612 mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
613 PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
614 mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
615
616 return true;
617 }
618 else
619 {
620 return false;
9f0c14b2
MT
621 }
622}
623
b2a98c42
MT
624// ensures that the chain definition is valid and that there are no other definitions of the same name
625// that have been confirmed.
626bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
627{
628 // the chain definition output can be spent when the chain is at the end of its life and only then
629 // TODO
630 return false;
631}
632
633// ensures that the chain definition is valid and that there are no other definitions of the same name
634// that have been confirmed.
635bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
636{
637 // checked before a chain definition output script is accepted as a valid transaction
638
639 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
640 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
641 // 2) notarization output with conformant values
642 // 3) finalization output
643 // 3) notarization funding
644 //
645
646 // get the source transaction
647 uint256 blkHash;
648 CTransaction thisTx;
6c5d1151 649 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
b2a98c42
MT
650 {
651 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
652 return false;
653 }
654
655 CPBaaSChainDefinition chainDef(thisTx, true);
656 CPBaaSNotarization notarization(thisTx, true);
657 CNotarizationFinalization finalization(thisTx, true);
658
659 if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
660 {
661 LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
662 return false;
663 }
664
665 CPBaaSChainDefinition prior;
666 // this ensures that there is no other definition of the same name already on the blockchain
667 if (!GetChainDefinition(chainDef.name, prior))
668 {
669 LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
670 return false;
671 }
672
673 return true;
674}
675
989b1de1
MT
676CAmount CCrossChainExport::CalculateExportFee() const
677{
678 if (numInputs > MAX_EXPORT_INPUTS)
679 {
680 return 0;
681 }
682 static const arith_uint256 satoshis(100000000);
683
684 int64_t ratio = 50000000 + ((25000000 / MAX_EXPORT_INPUTS) * numInputs);
685
686 return (((arith_uint256(totalFees) * arith_uint256(ratio))) / satoshis).GetLow64();
687}
688
b2a98c42
MT
689bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
690{
2fd1f0fb 691 bool retval = false;
b2a98c42 692 LOCK(cs_mergemining);
23d61f0a 693
3016973b 694 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
23d61f0a 695
b2a98c42
MT
696 auto chainIt = mergeMinedChains.find(chainID);
697 if (chainIt != mergeMinedChains.end())
698 {
699 arith_uint256 target;
700 target.SetCompact(chainIt->second.block.nBits);
701 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
702 {
703 // make sure we don't just match by target
704 if (removeRange.first->second->GetChainID() == chainID)
705 {
706 mergeMinedTargets.erase(removeRange.first);
707 break;
708 }
709 }
710 mergeMinedChains.erase(chainID);
2fd1f0fb 711 dirty = retval = true;
b2a98c42
MT
712
713 // if we get to 0, give the thread a kick to stop waiting for mining
2fd1f0fb 714 //if (!mergeMinedChains.size())
715 //{
716 // sem_submitthread.post();
717 //}
b2a98c42 718 }
2fd1f0fb 719 return retval;
b2a98c42
MT
720}
721
722// remove merge mined chains added and not updated since a specific time
723uint32_t CConnectedChains::PruneOldChains(uint32_t pruneBefore)
724{
725 vector<uint160> toRemove;
726
727 LOCK(cs_mergemining);
728 for (auto blkData : mergeMinedChains)
729 {
730 if (blkData.second.block.nTime < pruneBefore)
731 {
732 toRemove.push_back(blkData.first);
733 }
734 }
735
736 for (auto id : toRemove)
737 {
3016973b 738 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
b2a98c42
MT
739 RemoveMergedBlock(id);
740 }
741}
742
743// adds or updates merge mined blocks
744// returns false if failed to add
745bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
746{
b2a98c42
MT
747 // determine if we should replace one or add to the merge mine vector
748 {
749 LOCK(cs_mergemining);
750
2fd1f0fb 751 arith_uint256 target;
b2a98c42
MT
752 uint160 cID = blkData.GetChainID();
753 auto it = mergeMinedChains.find(cID);
754 if (it != mergeMinedChains.end())
755 {
1fa4454d 756 RemoveMergedBlock(cID); // remove it if already there
b2a98c42 757 }
1fa4454d 758 target.SetCompact(blkData.block.nBits);
23d61f0a 759
3016973b 760 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
23d61f0a 761
1fa4454d 762 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
2830db29 763 dirty = true;
b2a98c42 764 }
bce52b27 765 return true;
b2a98c42
MT
766}
767
768bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
769{
770 {
771 LOCK(cs_mergemining);
772 auto chainIt = mergeMinedChains.find(chainID);
773 if (chainIt != mergeMinedChains.end())
774 {
775 rpcChainData = (CRPCChainData)chainIt->second;
776 return true;
777 }
778 return false;
779 }
780}
781
782// this returns a pointer to the data without copy and assumes the lock is held
783CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
784{
785 {
786 auto chainIt = mergeMinedChains.find(chainID);
787 if (chainIt != mergeMinedChains.end())
788 {
789 return &chainIt->second;
790 }
791 return NULL;
792 }
793}
794
e771a884 795bool CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
796{
3016973b 797 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
2830db29 798 {
799 LOCK(cs_mergemining);
cff3c5ad 800
2830db29 801 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
802 }
e771a884 803 sem_submitthread.post();
804}
805
f8f61a6d 806// get the latest block header and submit one block at a time, returning after there are no more
807// matching blocks to be found
808vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
b2a98c42
MT
809{
810 std::set<uint160> inHeader;
f8f61a6d 811 bool submissionFound;
812 CPBaaSMergeMinedChainData chainData;
b2a98c42
MT
813 vector<pair<string, UniValue>> results;
814
f8f61a6d 815 CBlockHeader bh;
816 arith_uint256 lastHash;
b2a98c42
MT
817 CPBaaSBlockHeader pbh;
818
02560af7 819 do
b2a98c42 820 {
02560af7 821 submissionFound = false;
b2a98c42 822 {
02560af7 823 LOCK(cs_mergemining);
824 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
825 // common, merge mined headers for notarization, drop out on any submission
826 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
b2a98c42 827 {
02560af7 828 // add the PBaaS chain ids from this header to a set for search
829 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
f8f61a6d 830 {
02560af7 831 inHeader.insert(pbh.chainID);
832 }
b2a98c42 833
33d6f38a 834 uint160 chainID;
02560af7 835 // now look through all targets that are equal to or above the hash of this header
836 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
837 {
33d6f38a 838 chainID = chainIt->second->GetChainID();
02560af7 839 if (inHeader.count(chainID))
2830db29 840 {
02560af7 841 // first, check that the winning header matches the block that is there
842 CPBaaSPreHeader preHeader(chainIt->second->block);
843 preHeader.SetBlockData(headerIt->second);
844
845 // check if the block header matches the block's specific data, only then can we create a submission from this block
846 if (headerIt->second.CheckNonCanonicalData(chainID))
f8f61a6d 847 {
02560af7 848 // save block as is, remove the block from merged headers, replace header, and submit
849 chainData = *chainIt->second;
850
851 *(CBlockHeader *)&chainData.block = headerIt->second;
852
02560af7 853 submissionFound = true;
f8f61a6d 854 }
3016973b
MT
855 //else // not an error condition. code is here for debugging
856 //{
857 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
858 //}
cff3c5ad 859 }
3016973b
MT
860 //else // not an error condition. code is here for debugging
861 //{
862 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
863 //}
02560af7 864 }
2830db29 865
02560af7 866 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
33d6f38a
MT
867 if (submissionFound)
868 {
869 // once it is going to be submitted, remove block from this chain until a new one is added again
870 RemoveMergedBlock(chainID);
871 break;
872 }
873 else
02560af7 874 {
875 qualifiedHeaders.erase(headerIt);
b2a98c42 876 }
f8f61a6d 877 }
02560af7 878 }
879 if (submissionFound)
880 {
881 // submit one block and loop again. this approach allows multiple threads
882 // to collectively empty the submission queue, mitigating the impact of
883 // any one stalled daemon
884 UniValue submitParams(UniValue::VARR);
885 submitParams.push_back(EncodeHexBlk(chainData.block));
886 UniValue result, error;
887 try
f8f61a6d 888 {
02560af7 889 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
890 result = find_value(result, "result");
891 error = find_value(result, "error");
b2a98c42 892 }
02560af7 893 catch (exception e)
894 {
895 result = UniValue(e.what());
896 }
897 results.push_back(make_pair(chainData.chainDefinition.name, result));
898 if (result.isStr() || !error.isNull())
899 {
900 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());
901 }
902 else
903 {
904 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
905 }
906 }
907 } while (submissionFound);
b2a98c42
MT
908 return results;
909}
910
f8f61a6d 911// add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
b2a98c42
MT
912uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
913{
914 vector<uint160> inHeader;
915 vector<UniValue> toCombine;
916 arith_uint256 blkHash = UintToArith256(bh.GetHash());
f8f61a6d 917 arith_uint256 target(0);
b2a98c42
MT
918
919 CPBaaSBlockHeader pbh;
920
b2a98c42 921 {
f8f61a6d 922 LOCK(cs_mergemining);
b2a98c42 923
f8f61a6d 924 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
925
926 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
b2a98c42 927 {
f8f61a6d 928 if (bh.GetPBaaSHeader(pbh, i))
929 {
930 inHeader.push_back(pbh.chainID);
931 }
932 }
933
934 // loop through the existing PBaaS chain ids in the header
2fd1f0fb 935 // remove any that are not either this Chain ID or in our local collection and then add all that are present
f8f61a6d 936 for (uint32_t i = 0; i < inHeader.size(); i++)
937 {
938 auto it = mergeMinedChains.find(inHeader[i]);
939 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
940 {
941 bh.DeletePBaaSHeader(i);
942 }
b2a98c42 943 }
b2a98c42 944
b2a98c42
MT
945 for (auto chain : mergeMinedChains)
946 {
947 // get the native PBaaS header for each chain and put it into the
948 // header we are given
326f5f88 949 // it must have itself in as a PBaaS header
b2a98c42 950 uint160 cid = chain.second.GetChainID();
3a2a1777 951 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
b2a98c42
MT
952 {
953 if (!bh.AddUpdatePBaaSHeader(pbh))
954 {
955 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
f8f61a6d 956 break;
957 }
958 else
959 {
960 arith_uint256 t;
961 t.SetCompact(chain.second.block.nBits);
962 if (t > target)
963 {
964 target = t;
965 }
b2a98c42
MT
966 }
967 }
326f5f88
MT
968 else
969 {
970 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
971 }
972
b2a98c42 973 }
2830db29 974 dirty = false;
b2a98c42 975 }
326f5f88 976
f8f61a6d 977 return target.GetCompact();
b2a98c42
MT
978}
979
ccac80a3 980bool CConnectedChains::IsVerusPBaaSAvailable()
981{
982 return notaryChainVersion > "0.6";
983}
984
f8f61a6d 985extern string PBAAS_HOST, PBAAS_USERPASS;
986extern int32_t PBAAS_PORT;
6bb9fdbc 987bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
9f0c14b2 988{
6bb9fdbc 989 if (chainInfoUni.isObject() && chainDefUni.isObject())
9f0c14b2 990 {
6bb9fdbc 991 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
f8f61a6d 992 if (uniVer.isStr())
993 {
2156d3d0 994 LOCK(cs_mergemining);
f8f61a6d 995 notaryChainVersion = uni_get_str(uniVer);
6bb9fdbc 996 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
997 CPBaaSChainDefinition chainDef(chainDefUni);
f8f61a6d 998 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
999 }
9f0c14b2 1000 }
ccac80a3 1001 return IsVerusPBaaSAvailable();
9f0c14b2
MT
1002}
1003
2299bd95 1004bool CConnectedChains::CheckVerusPBaaSAvailable()
9f0c14b2
MT
1005{
1006 if (IsVerusActive())
1007 {
ccac80a3 1008 notaryChainVersion = "";
9f0c14b2
MT
1009 }
1010 else
1011 {
1012 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2156d3d0 1013 // tolerate only 15 second timeout
9a891caf 1014 UniValue chainInfo, chainDef;
1015 try
1016 {
1017 UniValue params(UniValue::VARR);
2fd1f0fb 1018 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
9a891caf 1019 if (!chainInfo.isNull())
1020 {
1021 params.push_back(VERUS_CHAINNAME);
2fd1f0fb 1022 chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result");
9a891caf 1023
1024 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
1025 {
1026 return true;
1027 }
1028 }
1029 } catch (exception e)
4fa3b13d 1030 {
4fa3b13d 1031 }
9f0c14b2 1032 }
9a891caf 1033 notaryChainVersion = "";
4fa3b13d 1034 return false;
9f0c14b2
MT
1035}
1036
41f170fd
MT
1037CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1038{
1039 CCoinbaseCurrencyState currencyState;
1040 CBlock block;
1041 LOCK(cs_main);
1042 if (!IsVerusActive() &&
1043 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::SOLUTION_VERUSV3 &&
1044 height != 0 &&
1045 height <= chainActive.Height() &&
1046 chainActive[height] &&
1047 ReadBlockFromDisk(block, chainActive[height], false))
1048 {
1049 currencyState = CCoinbaseCurrencyState(block.vtx[0]);
1050 }
1051 return currencyState;
1052}
1053
c3250dcd
MT
1054bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> minerOutputs, CTxDestination &firstDestinationOut)
1055{
1056 LOCK(cs_mergemining);
1057 {
1058 txnouttype outType;
1059 std::vector<std::vector<unsigned char>> vSolutions;
1060
1061 if (!minerOutputs.size() || !Solver(minerOutputs[0].second, outType, vSolutions))
1062 {
1063 return false;
1064 }
1065
1066 if (outType == TX_PUBKEY)
1067 {
1068 CPubKey pubKey(vSolutions[0]);
1069 if (!pubKey.IsValid())
1070 {
1071 return false;
1072 }
1073 firstDestinationOut = CTxDestination(pubKey);
1074 }
1075 else if (outType == TX_PUBKEYHASH)
1076 {
1077 firstDestinationOut = CTxDestination(CKeyID(uint160(vSolutions[0])));
1078 }
1079 else
1080 {
1081 return false;
1082 }
1083 }
1084 latestMiningOutputs = minerOutputs;
1085 latestDestination = firstDestinationOut;
1086}
1087
1088void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1089{
1090 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1091 {
1092 if (!nHeight)
1093 {
1094 return;
1095 }
1096
1097 multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1098
1099 CKeyID exportKeyID(CCrossChainRPCData::GetConditionID(ThisChain().GetChainID(), EVAL_CROSSCHAIN_EXPORT));
1100
1101 // get all available transfer outputs to aggregate into export transactions
1102 if (GetUnspentChainTransfers(transferOutputs))
1103 {
1104 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1105 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1106
1107 // we need unspent export outputs to export
1108 if (GetUnspentChainExports(exportOutputs))
1109 {
1110 uint160 lastChain;
1111
1112 // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
1113 // export of that type, or there are MIN_INPUTS or more outputs to aggregate
1114 for (auto it = transferOutputs.begin(); it != transferOutputs.end(); it++)
1115 {
1116 // get chain target and see if it is the same
1117 if (lastChain.IsNull() || it->first == lastChain)
1118 {
1119 txInputs.push_back(it->second);
1120 lastChain = it->first;
1121 }
1122 else
1123 {
1124 auto recentExportIt = exportOutputs.find(lastChain);
1125 // TODO: we cannot have a duplicate here, check for it
1126
1127 if (recentExportIt != exportOutputs.end() &&
1128 ((nHeight - recentExportIt->second.first) >= CCrossChainExport::MIN_BLOCKS) ||
1129 (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
1130 {
1131 boost::optional<CTransaction> oneExport;
1132
1133 // make one or more transactions that spends the last export and all possible cross chain transfers
1134 while (txInputs.size())
1135 {
1136 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1137
1138 int numInputs = (txInputs.size() < CCrossChainExport::MAX_EXPORT_INPUTS) ? txInputs.size() : CCrossChainExport::MAX_EXPORT_INPUTS;
1139
1140 int inputsLeft = txInputs.size() - numInputs;
1141
1142 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1143 {
1144 inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1145 numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1146 assert(numInputs > 0);
1147 }
1148
1149 // each time through, we make one export transaction with the remainder or a subset of the
1150 // reserve transfer inputs. inputs can be:
1151 // 1. transfers of reserve for fractional reserve chains
1152 // 2. pre-conversions for pre-launch participation in the premine
1153 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1154 //
1155 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1156 // or reserve token inputs.
1157 //
1158 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1159 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1160 // as part of the import process
1161 //
1162 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1163 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
1164 // remaining Verus reserve coin considered burned.
1165 //
1166 CAmount totalTxFees = 0;
1167 CAmount totalAmount = 0;
1168 std::vector<CBaseChainObject *> chainObjects;
1169
1170 // first, we must add the export output from the current export thread to this chain
1171 if (oneExport.has_value())
1172 {
1173 // spend the last export transaction output
1174 CTransaction &tx = oneExport.get();
1175 COptCCParams p;
1176 int j;
1177 for (j = 0; j < tx.vout.size(); j++)
1178 {
1179 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1180 {
1181 break;
1182 }
1183 }
1184
1185 // had to be found and valid if we made the tx
1186 assert(j < tx.vout.size() && p.IsValid());
1187
1188 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1189 }
1190 else
1191 {
1192 // spend the recentExportIt output
1193 tb.AddTransparentInput(recentExportIt->second.second.txIn.prevout,
1194 recentExportIt->second.second.scriptPubKey,
1195 recentExportIt->second.second.nValue);
1196 }
1197
1198 for (int j = 0; j < numInputs; j++)
1199 {
1200 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1201 totalTxFees += txInputs[j].second.nFees;
1202 totalAmount += txInputs[j].second.nValue;
1203 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1204 }
1205
1206 CCrossChainExport ccx(lastChain, numInputs, totalAmount, totalTxFees);
1207 CAmount exportFees = ccx.CalculateExportFee();
1208 CReserveTransfer feeOut = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::SEND_BACK + CReserveTransfer::FEE_OUTPUT,
1209 exportFees, 0, GetDestinationID(feeOutput));
1210 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1211
1212 CScript opRet = StoreOpRetArray(chainObjects);
1213 DeleteOpRetObjects(chainObjects);
1214
1215 CCcontract_info CC;
1216 CCcontract_info *cp;
1217 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1218
1219 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1220
1221 // send zero to a cross chain export output of the specific chain
1222 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_CROSSCHAIN_EXPORT))});
1223
1224 CTxOut exportOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT,
1225 0,
1226 pk,
1227 dests,
1228 ccx);
1229
1230 tb.AddTransparentOutput(exportOut.scriptPubKey, 0);
1231
1232 // if we are on Verus chain, send all native funds, less fees to reserve deposit CC, which is equivalent to the reserve account
1233 // on a PBaaS reserve chain, input is burned
1234 if (IsVerusActive())
1235 {
1236 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1237 pk = CPubKey(ParseHex(CC.CChexstr));
1238
1239 // send the entire amount to a reserve transfer output of the specific chain
1240 // we receive our fee on the other chain or when it comes back
1241 dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_RESERVE_DEPOSIT))});
1242
1243 CReserveOutput ro(CReserveOutput::VALID, totalAmount - ccx.CalculateExportFee());
1244
1245 CTxOut outToReserve = MakeCC1of1Vout(EVAL_RESERVE_DEPOSIT,
1246 ro.nValue,
1247 pk,
1248 dests,
1249 ro);
1250
1251 tb.AddTransparentOutput(outToReserve.scriptPubKey, ro.nValue);
1252 }
1253
1254 tb.AddOpRet(opRet);
1255
1256 boost::optional<CTransaction> newExport = tb.Build();
1257
1258 if (newExport.has_value())
1259 {
1260 // replace the last one only if we have a valid new one
1261 oneExport = newExport;
1262 CTransaction &tx = oneExport.get();
1263
1264 LOCK2(cs_main, mempool.cs);
1265 static int lastHeight = 0;
1266 // remove conflicts, so that we are the now
1267 std::list<CTransaction> removed;
1268 mempool.removeConflicts(tx, removed);
1269
1270 // add to mem pool and relay
1271 if (myAddtomempool(tx))
1272 {
1273 RelayTransaction(tx);
1274 }
1275 }
1276 // erase the inputs we've attempted to spend
1277 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1278 }
1279 }
1280 }
1281 }
1282 }
1283 }
1284 }
1285}
1286
1287// send new imports from this chain to the specified chain, which generally will be the notary chain
1288void CConnectedChains::SendNewImports(const uint160 &chainID,
1289 const CPBaaSNotarization &notarization,
1290 const uint256 &lastExportTx,
1291 const CTransaction &lastCrossImport,
1292 const CTransaction &lastExport)
1293{
1294 // currently only support sending imports to
1295}
1296
b2a98c42
MT
1297void CConnectedChains::SubmissionThread()
1298{
1299 try
1300 {
1301 arith_uint256 lastHash;
1302
a82942e4 1303 // wait for something to check on, then submit blocks that should be submitted
b2a98c42
MT
1304 while (true)
1305 {
c3250dcd
MT
1306 boost::this_thread::interruption_point();
1307
9f0c14b2 1308 if (IsVerusActive())
b2a98c42 1309 {
23d61f0a 1310 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
9014248c 1311 //printf("SubmissionThread: pruning\n");
c3250dcd 1312 PruneOldChains(GetAdjustedTime() - 300);
2fd1f0fb 1313 bool submit = false;
b2a98c42 1314 {
2fd1f0fb 1315 LOCK(cs_mergemining);
bea3e6a2
MT
1316 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1317 {
326f5f88 1318 qualifiedHeaders.clear();
bea3e6a2 1319 }
2fd1f0fb 1320 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
23d61f0a 1321
9014248c 1322 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2fd1f0fb 1323 }
1324 if (submit)
1325 {
9014248c 1326 //printf("SubmissionThread: calling submit qualified blocks\n");
2fd1f0fb 1327 SubmitQualifiedBlocks();
b2a98c42 1328 }
9f0c14b2
MT
1329 else
1330 {
9014248c 1331 //printf("SubmissionThread: waiting on sem\n");
2fd1f0fb 1332 sem_submitthread.wait();
9f0c14b2 1333 }
b2a98c42
MT
1334 }
1335 else
1336 {
9f0c14b2 1337 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
9a891caf 1338 CheckVerusPBaaSAvailable();
572c763f
MT
1339
1340 // check to see if we have recently earned a block with an earned notarization that qualifies for
1341 // submitting an accepted notarization
94a210aa 1342 if (earnedNotarizationHeight)
2830db29 1343 {
572c763f 1344 CBlock blk;
d6bc5de8 1345 int32_t txIndex = -1, height;
572c763f
MT
1346 {
1347 LOCK(cs_mergemining);
ba256d3a 1348 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
572c763f 1349 {
94a210aa
MT
1350 blk = earnedNotarizationBlock;
1351 earnedNotarizationBlock = CBlock();
1352 txIndex = earnedNotarizationIndex;
572c763f 1353 height = earnedNotarizationHeight;
94a210aa 1354 earnedNotarizationHeight = 0;
572c763f
MT
1355 }
1356 }
1357
d6bc5de8 1358 if (txIndex != -1)
572c763f 1359 {
f39e2bdf 1360 //printf("SubmissionThread: testing notarization\n");
833b3007 1361
7620ccae 1362 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
d6bc5de8
MT
1363
1364 if (!txId.IsNull())
1365 {
1366 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1367 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1368 }
572c763f 1369 }
2830db29 1370 }
572c763f 1371 sleep(1);
b2a98c42 1372 }
b2a98c42
MT
1373 boost::this_thread::interruption_point();
1374 }
1375 }
1376 catch (const boost::thread_interrupted&)
1377 {
1378 LogPrintf("Verus merge mining thread terminated\n");
1379 }
1380}
9f0c14b2 1381
b2a98c42
MT
1382void CConnectedChains::SubmissionThreadStub()
1383{
1384 ConnectedChains.SubmissionThread();
1385}
1386
572c763f
MT
1387void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1388{
1389 // called after winning a block that contains an earned notarization
1390 // the earned notarization and its height are queued for processing by the submission thread
1391 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1392 // kept
1393 LOCK(cs_mergemining);
1394
94a210aa 1395 // we only care about the last
572c763f 1396 earnedNotarizationHeight = height;
94a210aa
MT
1397 earnedNotarizationBlock = blk;
1398 earnedNotarizationIndex = txIndex;
572c763f
MT
1399}
1400
68b309c0
MT
1401bool IsChainDefinitionInput(const CScript &scriptSig)
1402{
1403 uint32_t ecode;
1404 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;
1405}
13ed2980
MT
1406
1407bool IsServiceRewardInput(const CScript &scriptSig)
1408{
1409 // this is an output check, and is incorrect. need to change to input
1410 uint32_t ecode;
1411 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_SERVICEREWARD;
1412}
This page took 0.263819 seconds and 4 git commands to generate.